/* * Copyright (C) 2002 Daniel Heck * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #include "video.hh" #include "menus.hh" #include "editor.hh" #include "enigma.hh" #include "options.hh" #include "px/px.hh" #include "sound.hh" #include "config.h" using namespace enigma; using namespace gui; using namespace px; namespace { class ImageAlloc { public: px::Surface *acquire(const std::string &name) { return px::LoadImage(name.c_str()); } void release(px::Surface *s) { delete s; } }; typedef cache::Cache ImageCache; class LevelWidget : public Widget { public: LevelWidget(LevelPack *lp, int w, int h); bool manage (); int selected_level() const { return iselected; } // Widget interface. void draw(px::GC &gc, const px::Rect &r); void set_listener(ActionListener *al) { listener = al; } void trigger_action() { cache.clear(); if (listener) listener->on_action(this); } void page_up() { set_selected (ifirst - width*height, iselected - width*height); } void page_down() { set_selected (ifirst + width*height, iselected + width*height); } void start() {set_selected (0,0);} void end() { set_selected (ifirst, level_pack.levels.size()-1); } bool on_event(const SDL_Event &e); private: // Private functions. void scroll_up(int lines); void scroll_down(int lines); void set_selected (int newfirst, int newsel); // Event handling. bool handle_keydown (const SDL_Event *e); bool handle_mousedown (const SDL_Event *e); // Variables. ImageCache cache; LevelPack &level_pack; // The level pack int ifirst; // Index of "upper left" level int iselected; // Index of selected level int width, height; vector areas; // Screen areas occupied by level previews bool show_filenames_p; ActionListener *listener; }; class LevelMenu : public Menu { public: LevelMenu(LevelPack *lp, unsigned ilevel_pack); private: // Menu interface. void draw_background(px::GC &gc); // Widget interface. bool on_event (const SDL_Event &e); // ActionListener interface. void on_action(Widget *w); // Variables. Widget *pgup, *pgdown, *start, *end; Widget *newgame; Widget *back; LevelWidget *levelwidget; LevelPack *level_pack; }; class LevelPackMenu : public Menu { public: LevelPackMenu(); void on_action(Widget *w); void draw_background(px::GC &gc); private: vector buttons; }; class MainMenu : public Menu { public: MainMenu(); private: // Menu interface void draw_background(px::GC &gc); // ActionListener interface. void on_action(Widget *w); // Private methods. void show_credits(); // Variables. Widget *newgame; Widget *playenigma; Widget *playoxyd; Widget *leveled; Widget *options; Widget *credits; Widget *quit; Widget *lpack; }; } //---------------------------------------------------------------------- // LevelWidget implementation //---------------------------------------------------------------------- LevelWidget::LevelWidget(LevelPack *lp, int w, int h) : level_pack(*lp), ifirst(0), iselected(0), width(w), height(h), areas(), show_filenames_p(false), listener(0) {} void LevelWidget::scroll_up(int nlines) { for (; nlines; --nlines) { if (ifirst+width*height >= (int)level_pack.levels.size()) break; ifirst += width; if (iselected < ifirst) iselected += width; } redraw(); } void LevelWidget::scroll_down(int nlines) { for (; nlines; --nlines) { if (ifirst < width) break; ifirst -= width; if (iselected >= ifirst+width*height) iselected -= width; } redraw(); } void LevelWidget::draw (px::GC &gc, const px::Rect &r) { areas.clear(); blit(gc, 0,0, enigma::GetImage("menu_bg")); // set_color(gc, 0,0,0); // box(gc, r); const int imgw = 128; //238; const int imgh = 83; //104; const int hgap = 20, vgap=30; const int x0 = get_x()+(get_w() - width*(imgw+hgap)+hgap)/2; const int y0 = get_y()+(get_h() - height*(imgh+vgap))/2; //get_y(); //30; unsigned i=ifirst; vector &levels = level_pack.levels; Font *smallfnt = enigma::GetFont("levelmenu"); for (int y=0; y= levels.size()) goto done_painting; int xpos = x0 + x*(imgw + hgap); int ypos = y0 + y*(imgh + vgap); areas.push_back(Rect(xpos-hgap/2, ypos-hgap/2, imgw+hgap, imgh+hgap+vgap/2)); string fname = string("levels/") + levels[i].filename + ".png"; if (Surface *img = cache.get(enigma::FindDataFile(fname))) blit (gc, xpos, ypos, img); options::LevelStatus *ls; if ((ls=options::GetLevelStatus(level_pack.name, levels[i].filename))) { if (ls->finished != 0) blit (gc, xpos, ypos, enigma::GetImage("completed")); } char str[10]; sprintf(str, "#%d ",i+1); string tstr(str); if (show_filenames_p) tstr += levels[i].filename; else tstr += levels[i].name; smallfnt->render (gc, xpos+(imgw-smallfnt->get_width(tstr.c_str()))/2, ypos+imgh+1, tstr.c_str()); } } done_painting: set_color(gc, 255,0,0); Rect a=areas[iselected-ifirst]; frame(gc, a); frame(gc, smaller(a,1)); frame(gc, smaller(a,2)); } void LevelWidget::set_selected (int newfirst, int newsel) { vector &levels = level_pack.levels; if (newfirst!=ifirst && newfirst>=0 && newfirst<(int)levels.size()) { sound::PlaySound("menumove"); ifirst = newfirst; iselected = Max(Min(newsel,(int)levels.size()-1), 0); redraw(); } else if (newsel!=iselected && newsel>=0 && newsel<(int)levels.size()) { sound::PlaySound("menumove"); while (newsel < ifirst) ifirst -= width; while (newsel >= ifirst+width*height) ifirst += width; iselected = newsel; redraw(); } } bool LevelWidget::on_event(const SDL_Event &e) { bool h=false; switch (e.type) { case SDL_MOUSEMOTION: { int newsel=iselected; for (unsigned i=0; ibutton.button) { case SDL_BUTTON_LEFT: for (unsigned i=0; ibutton.x, e->button.y)) { sound::PlaySound("menuok"); iselected = ifirst+i; trigger_action(); return true; } break; case SDL_BUTTON_RIGHT: break; case 4: scroll_down(1); return true; case 5: scroll_up(1); return true; } return false; } bool LevelWidget::handle_keydown (const SDL_Event *e) { switch (e->key.keysym.sym) { case SDLK_t: // Toggle level name/file name display show_filenames_p = !show_filenames_p; redraw(); break; case SDLK_l: // Reload level index level_pack.init(); redraw(); break; case SDLK_o: // Show/hide "todo" levels break; case SDLK_LEFT: set_selected (ifirst, iselected-1); break; case SDLK_RIGHT: set_selected (ifirst, iselected+1); break; case SDLK_DOWN: set_selected (ifirst, iselected+width); break; case SDLK_UP: set_selected (ifirst, iselected-width); break; case SDLK_PAGEDOWN: page_down(); break; case SDLK_PAGEUP: page_up(); break; case SDLK_HOME: start(); break; case SDLK_END: end(); break; case SDLK_RETURN: trigger_action(); break; default: return false; // key not handled } return true; } //---------------------------------------------------------------------- // Various buttons for the options menu //---------------------------------------------------------------------- namespace { class FullscreenButton : public TextButton { void update() { if (options::FullScreen > 0) set_text("Window"); else set_text("Fullscreen"); } // ActionListener interface. void on_action(Widget *) { options::FullScreen = !options::FullScreen; video::ToggleFullscreen(); update(); } public: FullscreenButton() : TextButton("", this) { update(); } }; class MouseSpeedButton: public TextButton { void update() { char msg[50]; sprintf(msg, "Mouse speed: %d", int(options::MouseSpeed)); set_text(msg); } // ActionListener interface. void on_action(Widget *) { double &ms = options::MouseSpeed; ms = int(ms) + 1; if (ms > 10) ms = 1; update(); } public: MouseSpeedButton() : TextButton("", this) { update(); } }; class SoundVolumeButton : public TextButton { int get_vol() const { return int(0.5+options::SoundVolume*10.0); } void set_vol(int v) { options::SoundVolume = v/10.0; } void update() { if (options::SoundVolume == 0) set_text("Sound off"); else { char msg[50]; sprintf(msg, "Sound vol: %d", get_vol()); set_text(msg); } } // ActionListener interface. void on_action(Widget *) { int sv = 1 + get_vol(); if (sv > 10) sv = 0; set_vol(sv); sound::UpdateVolume(); update(); } public: SoundVolumeButton() : TextButton("", this) { update(); } }; class MusicVolumeButton : public TextButton { int get_vol() const { return int(0.5+options::MusicVolume*10.0); } void set_vol(int v) { options::MusicVolume = v/10.0; } void update() { if (options::MusicVolume == 0) set_text("Music off"); else { char msg[50]; sprintf(msg, "Music vol: %d", get_vol()); set_text(msg); } } // ActionListener interface. void on_action(Widget *) { int sv = 1 + get_vol(); if (sv > 10) sv = 0; set_vol(sv); sound::UpdateVolume(); update(); } public: MusicVolumeButton() : TextButton("", this) { update(); } }; class AlphaShadowButton : public TextButton { void update() { // if (lua::get_num("AlphaShadows") > 0) // set_text("Blended Shadows"); // else // set_text("Stippled Shadows"); } // ActionListener interface. void on_action(Widget *) { // video::ToggleFullscreen(); update(); } public: AlphaShadowButton() : TextButton("") { update(); } }; } //---------------------------------------------------------------------- // Helper classes //---------------------------------------------------------------------- namespace { class BuildVList { Rect r; Menu *container; int skip; public: BuildVList(Menu *cc, const px::Rect &rr, int s) : r(rr), container(cc), skip(s) {} Widget * add(Widget *w) { container->add(w, r); r.y += r.h+skip; return w; } }; class BuildHList { Rect r; Menu *container; int skip; public: BuildHList(Menu *cc, const px::Rect &rr, int s) : r(rr), container(cc), skip(s) {} Widget * add(Widget *w) { container->add(w, r); r.x += r.w+skip; return w; } }; } //---------------------------------------------------------------------- // Options menu //---------------------------------------------------------------------- OptionsMenu::OptionsMenu() : back(new TextButton("Back", this)) { BuildVList vlist(this, Rect(0,0,150,40), 5); vlist.add(new FullscreenButton); vlist.add (new MouseSpeedButton); vlist.add (new SoundVolumeButton); vlist.add (new MusicVolumeButton); // add(new AlphaShadowButton, Rect(0,50,150,40)); vlist.add(back); } bool OptionsMenu::on_event (const SDL_Event &e) { if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_RIGHT) { Menu::quit(); return true; } return false; } void OptionsMenu::on_action(Widget *w) { if (w == back) Menu::quit(); } void OptionsMenu::draw_background(px::GC &gc) { video::SetCaption("Enigma - Options Menu"); blit(gc, 0,0, enigma::GetImage("menu_bg")); } //---------------------------------------------------------------------- // Level menu //---------------------------------------------------------------------- LevelMenu::LevelMenu(LevelPack *lp, unsigned ilevel_pack) : newgame(new TextButton("Start Game", this)), back(new TextButton("Back", this)), levelwidget(new LevelWidget(lp, 3, 4)), level_pack(lp) { { BuildVList hlist(this, Rect(510,20,100,28), 5); start = hlist.add(new ImageButton("ic-top", "ic-top1")); pgup = hlist.add(new ImageButton("ic-up", "ic-up1")); pgdown = hlist.add(new ImageButton("ic-down", "ic-down1")); end = hlist.add(new ImageButton("ic-bottom", "ic-bottom1")); pgup->set_listener(this); pgdown->set_listener(this); start->set_listener(this); end->set_listener(this); } { BuildVList vlist(this, Rect(510,400,100,28), 5); // vlist.add(new TextButton("Timer on")); vlist.add(newgame); vlist.add(back); } // Add levelwidget at the center add(levelwidget, Rect(10,10,500,480)); levelwidget->set_listener(this); } bool LevelMenu::on_event (const SDL_Event &e) { bool handled=levelwidget->on_event(e); // Pass all events to the level widget first if (!handled) { if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_RIGHT) { Menu::quit(); handled=true; } } return handled; } void LevelMenu::on_action(Widget *w) { if (w == newgame || w==levelwidget) { int ilevel=levelwidget->selected_level(); StartGame(level_pack, ilevel); invalidate_all(); } else if (w == back) { Menu::quit(); } else if (w == pgup) { levelwidget->page_up(); } else if (w == pgdown) { levelwidget->page_down(); } else if (w == start) { levelwidget->start(); } else if (w == end) { levelwidget->end(); } } void LevelMenu::draw_background(px::GC &gc) { video::SetCaption("Enigma - Level Menu"); sound::PlayMusic("sound/menu.s3m"); blit(gc, 0,0, enigma::GetImage("menu_bg")); } //---------------------------------------------------------------------- // Level pack menu //---------------------------------------------------------------------- LevelPackMenu::LevelPackMenu() { BuildVList b(this, Rect(0,0,150,40), 5); for (unsigned i=0; iname, this))); } } void LevelPackMenu::on_action(Widget *w) { for (unsigned i=0; iwidth())/2; int y0=40; blit(gc, x0, y0, logo); f->render (gc, 5, 460, "v" VERSION); } void MainMenu::on_action(Widget *w) { if (w == newgame) { LevelPackMenu m; m.center(video::GetScreen()); m.manage(video::GetScreen()); } else if (w == playenigma) { LevelMenu m(enigma::LevelPacks[0], 0); m.manage(video::GetScreen()); invalidate_all(); } else if (w == playoxyd) { LevelMenu m(enigma::LevelPacks[1], 0); m.manage(video::GetScreen()); invalidate_all(); } else if (w == credits) { show_credits (); } else if (w == options) { GUI_OptionsMenu(); } else if (w == leveled) { editor::Run(); } else if (w == quit) { Menu::quit(); } else return; invalidate_all(); } void MainMenu::show_credits () { static char *credits[] = { "Main developer:", " DANIEL HECK", "", "Additional Programming:", " SIEGFRIED FENNIG, MARTIN HAWLISCH, NAT PRYCE", "", "Level Design:", " SIEGFRIED FENNIG, MARTIN HAWLISCH, DANIEL HECK, NAT PRYCE,", " PETR MACHATA, ANDRZEJ SZOMBIERSKI", "", "Graphics:", " SIEGFRIED FENNIG, JOHANNES FORTMANN, MARTIN HAWLISCH,", " DANIEL HECK", "", "Music:", " ANDREW \"NECROS\" SEGA", "", "", "Enigma is free software and may be distributed under the", "terms of the GNU General Public License, version 2. See", "the accompanying COPYING.GPL for details.", 0 }; Screen *scr = video::GetScreen (); GC gc (scr->get_drawable()); blit (gc, 0,0, enigma::GetImage("menu_bg")); Font *f = enigma::GetFont("menufont"); for (int i=0; credits[i]; ++i) { f->render (scr, 40, 20+i*f->get_height(), credits[i]); } scr->update_all (); scr->flush_updates(); SDL_Event e; for (;;) { SDL_WaitEvent(&e); if (e.type == SDL_KEYDOWN || e.type == SDL_MOUSEBUTTONDOWN) break; } } //---------------------------------------------------------------------- // Functions //---------------------------------------------------------------------- void enigma::GUI_MainMenu(LevelPack *lp, unsigned ilevel_pack) { Screen *scr = video::GetScreen(); MainMenu m; m.manage(scr); } void enigma::GUI_OptionsMenu() { Screen *scr = video::GetScreen(); OptionsMenu m; m.center(scr); m.manage(scr); }