Added files with translations to several languages:
[hex-a-hop.git] / menus.h
blob9365dd8cd5e6b08c2afd0202fcb4d09b0afcc0fc
1 /*
2 Copyright (C) 2005-2007 Tom Beaumont
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include <string>
21 struct Menu;
22 Menu* activeMenu = 0;
23 Menu* deadMenu = 0;
25 int SDLPangoTextHeight(const std::string &text_utf8, int width);
27 static void HackKeyPress(int key, int mod)
29 int k = keyState[key];
30 StateMakerBase::current->KeyPressed(key, mod);
31 keyState[key] = k;
34 struct Menu {
35 bool renderBG;
37 Menu() : under(activeMenu), time(0), renderBG(true) { activeMenu = this; }
39 virtual ~Menu() {
40 if(this!=deadMenu) FATAL("this!=deadMenu");
41 deadMenu=under;
44 static void Pop()
46 if (!activeMenu) return;
47 Menu* m = activeMenu;
48 activeMenu = activeMenu->under;
49 m->under = deadMenu;
50 deadMenu = m;
53 virtual bool KeyPressed(int key, int mod)
55 if (key=='w' || key==SDLK_UP || key==SDLK_KP8 || key=='q' || key=='e' || key==SDLK_KP7 || key==SDLK_KP9)
56 Move(-1), noMouse=1;
57 else if (key=='s' || key==SDLK_DOWN || key==SDLK_KP2 || key=='a' || key=='d' || key==SDLK_KP1 || key==SDLK_KP3)
58 Move(1), noMouse=1;
59 else if (key==' ' || key==SDLK_RETURN)
61 Select();
62 noMouse=1;
64 else if (key==SDLK_ESCAPE || key==SDLK_BACKSPACE || key==SDLK_KP_PERIOD || key==SDLK_DELETE)
65 Cancel();
66 else
67 return false;
68 return true;
70 virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons)
72 if (buttons_pressed==4 || buttons_pressed==2)
73 Cancel();
76 virtual void Move(int dir) {}
77 virtual void Select() {}
78 virtual void Cancel() {}
80 virtual void Update(double timedelta) {time+=timedelta;}
81 virtual void Render() = 0;
83 Menu * under;
84 double time;
87 const char * hint[] = {
89 /*EMPTY*/
90 _("Basic controls:|Move around with the keys Q,W,E,A,S,D or the numeric keypad. Alternatively, you can use the mouse and click on the tile you'd like to move to. Use 'U', backspace or the right mouse button to undo mistakes. The 'Esc' key (or middle mouse button) brings up a menu from which you can restart if you get stuck."),
91 /*NORMAL*/
93 /*COLLAPSABLE*/
94 _("Objective:|Your goal is to break all the green tiles. You mainly do this by jumping on them. They will crack when you land on them, and only disintegrate when you jump off. Try not to trap yourself!"),
95 /*COLLAPSE_DOOR*/
96 _("The coloured walls flatten themselves when there are no matching coloured tiles remaining."),
97 /*TRAMPOLINE*/
98 _("You can bounce on the purple trampoline tiles to get around. But try not to fall in the water. If you do, remember you can undo with 'U', backspace or the right mouse button!"),
99 /*SPINNER*/
100 _("A red spinner tile will rotate the pieces around it when you step on it."),
101 /*WALL*/
103 /*COLLAPSABLE2*/
104 _("You don't need to destroy blue tiles to complete the level. But they'll turn green when you step off them, and you know what you have to do to green tiles..."),
105 /*COLLAPSE_DOOR2*/
107 /*GUN*/
108 _("Yellow laser tiles fire when you step on them. Shooting other laser tiles is more destructive."),
109 /*TRAP*/
110 _("Ice is slippery! Please be careful!!"),
111 /*COLLAPSABLE3*/
113 /*BUILDER*/
114 _("The dark grey tiles with arrows on are builders. Landing on one creates green tiles in any adjacent empty tile, and turns green tiles into walls."),
115 /*SWITCH*/
117 /*FLOATING_BALL*/
118 /* TRANSLATORS: pop means vanish and Emy drowns (you loose) */
119 _("You can ride on the pink floating boats to get across water. They'll pop if you try and float off the edge of the screen though, so look where you're going."),
120 /*LIFT_DOWN*/
121 _("The blue lifts go up or down when you land on them."),
122 /*LIFT_UP*/
125 0,0,0,0,
126 //Item 0 (21)
127 _("The spiky anti-ice pickups turn icy tiles into blue ones. They get used automatically when you land on ice."),
128 //Item 1 (22)
129 /* TRANSLATORS: Normally you jump from one plate to another. The golden jump (a
130 pickup) allows you to jump and land on the *same* plate */
131 _("Collecting the golden jump pickups will allow you to do a big vertical jump. Try it out on different types of tiles. Use the space bar or return key to jump. Or click on the tile you're currently on with the mouse."),
133 0,0,
135 // Map (25)
136 _("Map Screen:|You can choose which level to attempt next from the map screen. Silver levels are ones you've cleared. Black levels are ones you haven't completed yet, but are available to play."),
138 // Scoring (26)
139 /* TRANSLATORS: Levels are depicted as black balls. Once you passed them, they
140 turn silver. If you reached the par, they turn golden (with a crown), and if
141 you beat the par, they turn their shape/color once more */
142 _("New feature unlocked!|Each level has an efficiency target for you to try and beat. Every move you make and each non-green tile you destroy counts against you. Why not try replaying some levels and going for gold?"),
144 0,0,0,
146 // End of help (30)
147 _("Thanks for playing this little game. I hope you enjoy it! -- -- All content is Copyright 2005 Tom Beaumont email: tombeaumont@yahoo.com Any constructive criticism gratefully received!"),
149 // First help page (31)
150 /* TRANSLATORS: This string is copied twice into the POT file to workaround a
151 gettext limitation (no macro expansion). The extracted string "Welcome to "
152 will not be used. */
153 _("Welcome to " GAMENAME "! This is a puzzle game based on hexagonal tiles. There is no time limit and no real-time element, so take as long as you like. Use the cursor keys or click on the arrows to scroll through the help pages. More pages will be added as you progress through the game."),
156 struct HintMessage : public Menu
158 static int flags;
160 SDL_Rect InnerTextWindowRect;
161 SDL_Rect OuterTextWindowRect;
162 const char * msg;
163 char title[500];
164 int state;
166 const SDL_Rect &GetInnerWindowRect() const
168 return InnerTextWindowRect;
171 const SDL_Rect &GetOuterWindowRect() const
173 return OuterTextWindowRect;
176 static bool FlagTile(int t, bool newStuff=true)
178 if (t==LIFT_UP) t=LIFT_DOWN;
179 if (newStuff && (flags & (1<<t))) return false;
180 if (!newStuff && !(flags & (1<<t))) return false;
181 if (t>31) return false;
182 if (!hint[t]) return false;
184 flags |= 1<<t;
185 new HintMessage(hint[t]);
186 return true;
189 HintMessage(const char * m) { Init(m); }
191 void Init(const char * m)
193 state = 0; time = 0;
194 memset(title, 0, sizeof(title));
195 char * x = strstr(m, "|");
196 if (!x)
198 msg = m;
199 strcpy(title, _("Info:"));
201 else
203 strncpy(title, m, x-m);
204 msg=x+1;
207 char msg_utf8[5000];
208 ConvertToUTF8(msg, msg_utf8, sizeof(msg_utf8)/sizeof(char));
210 std::string text(msg_utf8);
211 while (text.find(" ") != std::string::npos)
212 text.replace(text.find(" "), 2, "\n");
214 InnerTextWindowRect.w = SCREEN_W-TILE_W1*2;
215 InnerTextWindowRect.h = SDLPangoTextHeight(text, InnerTextWindowRect.w - 2*FONT_SPACING);
216 InnerTextWindowRect.h += FONT_SPACING;
217 OuterTextWindowRect = InnerTextWindowRect;
218 OuterTextWindowRect.w += 4;
219 OuterTextWindowRect.h += FONT_SPACING+4;
222 virtual void Render()
224 int y = SCREEN_H/4 + int(SCREEN_H*MAX(1-time*5, 0)*3/4);
225 if (state)
226 y = SCREEN_H/4 + int(SCREEN_H*(-time*5)*3/4);
228 Render(0, y);
230 if (!state && time>0.2)
231 PrintR(SCREEN_W-FONT_SPACING, SCREEN_H-FONT_SPACING, _("Press any key"));
234 void Render(int x, int y)
236 //if (y<0) {
237 // std::cout << "Error in Render: " << x << " " << y << "\n"; // CHECKME
238 // y = 0;
240 InnerTextWindowRect.x = x+TILE_W1;
241 InnerTextWindowRect.y = y;
242 OuterTextWindowRect.x = InnerTextWindowRect.x-2;
243 OuterTextWindowRect.y = InnerTextWindowRect.y-2-FONT_SPACING;
244 // Height is reduced in SDL_FillRect!!? Why? ==> Use a copy:
245 SDL_Rect r2 = InnerTextWindowRect;
246 SDL_Rect r = OuterTextWindowRect;
247 SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 60,90,90));
248 SDL_FillRect(screen, &r2, SDL_MapRGB(screen->format, 20,50,50));
249 Print(OuterTextWindowRect.x+FONT_SPACING/4, y-FONT_SPACING, "%s", title);
250 /* TRANSLATORS: This specifies how the text in the help dialog should
251 be aligned. Do *not* translate the text itself but use one of "left",
252 "center" or "right" (untranslated!). The default is "center". */
253 std::string alignment = _("text alignment");
254 if (alignment == "right")
255 Print_Aligned(true, InnerTextWindowRect.x + InnerTextWindowRect.w - FONT_SPACING, y+FONT_SPACING/2, InnerTextWindowRect.w - 2*FONT_SPACING, msg, 2);
256 else if (alignment == "left")
257 Print_Aligned(true, InnerTextWindowRect.x + FONT_SPACING, y+FONT_SPACING/2, InnerTextWindowRect.w - 2*FONT_SPACING, msg, 0);
258 else
259 Print_Aligned(true, x+SCREEN_W/2, y+FONT_SPACING/2, InnerTextWindowRect.w - 2*FONT_SPACING, msg, 1);
262 virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons)
264 if (buttons_pressed && state==0 && time>0.2)
265 state = 1, time=0;
268 bool KeyPressed(int key, int mod)
270 if (state==0 && time>0.2)
271 state = 1, time=0;
272 return true;
275 virtual void Update(double timedelta)
277 Menu::Update(timedelta);
278 if(state && time > 0.2)
279 Pop();
283 int HintMessage::flags = 1<<31 | 1<<30;
285 struct HintReview : public HintMessage
287 int page;
288 int page_dir;
289 int page_count;
290 int page_display;
291 HintReview() : HintMessage(hint[31]), page(31), page_dir(0), page_display(0)
293 page_count=0;
294 for (int i=0; i<32; i++)
295 if (flags & (1<<i))
296 page_count++;
299 void Cancel() { Pop(); }
300 void Select() { Pop(); }
303 virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons)
305 if (buttons_pressed==1)
307 if (y < SCREEN_H/4-FONT_SPACING+2)
308 Move(-1);
309 else if (y > HintMessage::GetOuterWindowRect().y+
310 HintMessage::GetOuterWindowRect().h)
311 Move(1);
312 else
313 Cancel();
315 else
316 Menu::Mouse(x,y,dx,dy,buttons_pressed, buttons_released, buttons);
319 bool KeyPressed(int key, int mod)
321 if (key==SDLK_LEFT)
322 Move(-1);
323 else if (key==SDLK_RIGHT)
324 Move(1);
325 else
326 return Menu::KeyPressed(key, mod);
327 return true;
330 virtual void Render()
332 const double SPD = 10;
334 #ifdef EDIT
335 sprintf (title, _("Help (Page --)"), page_display+1, page_count);
336 #else
337 sprintf (title, _("Help (Page %d/%d)"), page_display+1, page_count);
338 #endif
340 int y=SCREEN_H/4;
341 if (state==0)
342 y = SCREEN_H/4+int(MAX(0, time*SPD)*-page_dir*SCREEN_H);
343 if (state==1)
344 y = SCREEN_H/4+int(MAX(0, 1-time*SPD)*page_dir*SCREEN_H);
346 //if (!noMouse)
348 PrintC(false, SCREEN_W/2, y-FONT_SPACING*2, "^");
349 PrintC(false, SCREEN_W/2, HintMessage::GetOuterWindowRect().y+
350 HintMessage::GetOuterWindowRect().h, "_");
353 HintMessage::Render(0,y);
355 if (time > 1.0/SPD && page_dir && state==0)
357 do {
358 page = (page+page_dir) & 31;
359 #ifdef EDIT
360 if (hint[page]) break;
361 #endif
362 } while (hint[page]==0 || !(flags&(1<<page)));
363 Init(hint[page]);
365 page_display = (page_display + page_count + page_dir) % page_count;
367 time = 0;
368 state=1;
370 if (time>1.0/SPD && state==1)
371 state=0, page_dir = 0;
373 virtual void Update(double timedelta)
375 Menu::Update(timedelta);
377 void Move(int dir)
379 if (page_dir)
380 return;
381 time = 0;
382 page_dir = dir;
383 state = 0;
387 #define MAX_GAMESLOT 4
388 enum option {
389 OPT_GAMESLOT_0,
390 OPT_GAMESLOT_LAST = OPT_GAMESLOT_0 + MAX_GAMESLOT - 1,
391 OPT_RESUME,
392 OPT_RESTART,
393 OPT_GOTO_MAP,
394 OPT_GOTO_MAP_CONTINUE,
395 OPT_FULLSCREEN,
396 OPT_OPTIONS,
397 OPT_QUIT,
398 OPT_QUIT_CONFIRM,
399 OPT_QUIT_CANCEL,
400 OPT_QUIT_MENU_CONFIRM,
401 OPT_HELP,
402 OPT_GAMESLOT_NEW,
403 OPT_DELETE_CONFIRM,
404 OPT_DELETE_CANCEL,
405 OPT_UNDO,
406 OPT_BACK,
407 OPT_END, OPT_END2,
410 char optionSlotName[MAX_GAMESLOT][400] = { {0} };
411 char currentSlot[800] = "";
412 int freeSlot = -1;
413 char* GetSlotName(int i, char * t)
415 sprintf(t, "save%d.dat", i+1);
416 return t;
419 char * optionString[] = {
420 optionSlotName[0],
421 optionSlotName[1],
422 optionSlotName[2],
423 optionSlotName[3],
424 _("Resume"),
425 _("Restart Level"),
426 _("Return to Map"),
427 _("Continue"),
428 _("Toggle Fullscreen"),
429 _("Options"),
430 _("Quit"),
431 _("Yes"),
432 _("No"),
433 _("Return to Title"),
434 _("Help"),
435 _("Start New Game"),
436 _("Yes, really delete it!"),
437 _("Don't do it!"),
438 _("Undo Last Move"),
439 _("OK"),
440 _("View Credits Sequence"), _("View Credits Sequence"),
444 struct OptMenu : public Menu
446 int select;
447 int num_opt;
448 int opt[10];
449 bool left_align;
450 const char * title;
451 SDL_Rect r, r2;
453 OptMenu(const char * t) : select(0), title(t)
455 left_align = false;
456 num_opt = 0;
459 void Init()
461 r.w=SCREEN_W/2;
462 r.x=(SCREEN_W-r.w)/2;
463 r.y=SCREEN_H/3;
465 r2 = r;
467 const int SPACE = int(FONT_SPACING * 1.5);
469 r2.h = SPACE*num_opt + FONT_SPACING/2;
470 r.h = r2.h + (FONT_SPACING+2*2);
471 r.y -= FONT_SPACING+2;
472 r.w += 2*2;
473 r.x -= 2;
476 void RenderOption(int o, const char * s)
478 int y = r2.y + FONT_SPACING/2 + int(FONT_SPACING * 1.5) * o;
479 if (left_align)
481 int x = r.x + SDLPangoTextWidth(" ");
482 int x1 = x + SDLPangoTextWidth("> ");
483 if (select==o)
485 //x += int( sin(time*9)*2.5 );
486 //y += int( sin(time*9 + 1.5)*1.5 );
487 Print(x, y, "> %s", s);
489 else
491 Print(x1, y, "%s", s);
494 else
496 int x = r.x + r.w/2;
497 if (select==o)
499 //x += int( sin(time*9)*2.5 );
500 //y += int( sin(time*9 + 1.5)*1.5 );
501 PrintC(false, x, y, "> %s <", s);
503 else
505 PrintC(false, x, y, "%s", s);
510 void Move(int dir)
512 select += dir;
513 if (select<0) select = num_opt-1;
514 if (select>=num_opt) select = 0;
516 virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons)
518 if (1)
520 if (x<r2.x || y<r2.y || x>r2.x+r2.w || y>r2.y+r2.h)
522 if (buttons_pressed!=4 && buttons_pressed!=2)
523 return;
525 else
527 select = (y-r2.y-FONT_SPACING/3) / int(FONT_SPACING*1.5);
528 if (select<0) select = 0;
529 if (select>=num_opt) select = num_opt-1;
532 if (buttons_pressed==1)
533 Select();
534 Menu::Mouse(x, y, dx, dy, buttons_pressed, buttons_released, buttons);
536 void Select();
538 void Render()
540 RenderBG();
541 RenderTitle();
542 RenderOptions();
544 void RenderBG()
546 SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 60,90,90));
547 SDL_FillRect(screen, &r2, SDL_MapRGB(screen->format, 20,50,50));
549 void RenderTitle()
551 int y = r2.y + FONT_SPACING / 2;
552 if (left_align)
553 Print(r2.x+SDLPangoTextWidth(" "), r.y+4, title);
554 else
555 PrintC(false, r2.x+r2.w/2, r.y+4, title);
557 void RenderOptions()
559 for (int i=0; i<num_opt; i++)
560 RenderOption(i, optionString[opt[i]]);
562 void Cancel()
564 Pop();
568 struct WinLoseScreen : public OptMenu
570 bool win;
571 int score, par, best_score;
572 WinLoseScreen(bool _win, int _score=0, int _par=0, int _prev_score=0) :
573 score(_score),
574 par(_par),
575 best_score(_prev_score),
576 win(_win),
577 OptMenu(_win ? _("Level Complete!") : _("Emi can't swim..."))
579 if (!win)
580 opt[num_opt++] = OPT_UNDO;
581 if (!win)
582 opt[num_opt++] = OPT_RESTART;
583 opt[num_opt++] = win ? OPT_GOTO_MAP_CONTINUE : OPT_GOTO_MAP;
585 Init();
587 if (win)
589 r.h += FONT_SPACING * 3 + FONT_SPACING/2;
590 r2.h += FONT_SPACING * 3 + FONT_SPACING/2;
594 void Render()
596 OptMenu::RenderBG();
597 OptMenu::RenderTitle();
599 if (win)
600 r2.y += FONT_SPACING * 3 + FONT_SPACING/2;
602 OptMenu::RenderOptions();
604 if (win)
605 r2.y -= FONT_SPACING * 3 + FONT_SPACING/2;
607 if (win)
609 int x = r.x+r.w/2;
610 int y = r2.y + FONT_SPACING/2;
611 if (score < best_score && score <= par)
612 PrintC(true, x, y, _("New Best Score: %d Par Score: %d Par Beaten!"), score, par);
613 else if (score < best_score)
614 PrintC(true, x, y, _("New Best Score: %d Par Score: %d"), score, par);
615 else if (par && best_score)
616 PrintC(true, x, y, _("Score: %d Previous Best: %d Par Score: %d"), score, best_score, par);
617 else
618 PrintC(true, x, y+FONT_SPACING/2, _("Well Done! Level Completed!"));
622 static void Undo()
624 Pop();
625 HackKeyPress('z', 0);
627 bool KeyPressed(int key, int mod)
629 if (key=='z' || key=='u' || key==SDLK_DELETE || key==SDLK_BACKSPACE)
630 return Undo(), true;
631 if (key=='r' && (mod & KMOD_CTRL))
633 Pop();
634 HackKeyPress(key, mod);
635 return true;
637 return OptMenu::KeyPressed(key, mod);
639 virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons)
641 if (buttons_pressed==4)
643 Undo();
644 return;
647 OptMenu::Mouse(x, y, dx, dy, buttons_pressed, buttons_released, buttons);
649 void Cancel()
651 if (win)
652 select=0, Select();
653 else
654 Undo();
658 struct OptMenuTitle : public OptMenu
660 OptMenuTitle(const char * t) : OptMenu(t)
662 renderBG = false;
663 //left_align = true;
666 void Render()
668 SDL_Rect a = {0,0,SCREEN_W,SCREEN_H};
669 // SDL_FillRect(screen, &a, SDL_MapRGB(screen->format, 10,25,25));
672 SDL_BlitSurface(titlePage, &a, screen, &a);
674 OptMenu::RenderTitle();
675 OptMenu::RenderOptions();
678 void Init()
680 OptMenu::Init();
682 int xw = SCREEN_W/6;
683 r.w += xw; r2.w+=xw;
684 int x = SCREEN_W - r.x - r.w;// - FONT_SPACING;
685 int y = SCREEN_H - r.y - r.h + FONT_SPACING/4;// - FONT_SPACING;
686 r.x += x; r2.x += x;
687 r.y += y; r2.y += y;
688 r.w+=20; r2.w+=20; r.h+=20; r2.h+=20;
690 r.h = r2.h = SCREEN_H;
691 r2.y = SCREEN_H/2;
692 r.y = r2.y - FONT_SPACING - 2;
696 const char *ending[] = {
697 _(" Very Well Done! "),
698 "", "", "", "", "", "",
700 "", "", "*15,4", "", "",
702 _("All Levels Cleared!"),
704 "", "", "*5,7", "", "",
706 _("Not a single green hexagon is left unbroken."),
707 "",
708 _("Truly, you are a master of hexagon hopping!"),
710 "", "", "*9,10", "", "",
712 "", _("Credits"), "", "", "",
713 _("<Design & Direction:"), ">Tom Beaumont", "", "",
714 _("<Programming:"), ">Tom Beaumont", "", "",
715 _("<Graphics:"), ">Tom Beaumont", "", "",
716 _("<Thanks to:"), ">Kris Beaumont", "", "",
717 // "", "<Some useless facts...", "",
718 _("<Tools and libraries used:"), "", ">Photoshop LE", ">Inno Setup", ">Wings 3D", ">MSVC", ">SDL", "",
719 _("<Fonts used:"), "", ">Copperplate gothic bold", ">Verdana", "",
721 "", "", "*12,14", "", "",
723 _("Thanks for playing!")
726 const char *ending2[] = {
727 _(" Absolutely Amazing! "),
728 "", "", "", "", "",
730 "", "", "*15,4", "", "",
732 _("All Levels Mastered!!"),
734 "", "", "*5,7", "", "",
736 _("You crushed every last green hexagon with"),
737 _("breathtaking efficiency!"),
739 _("You truly are a grand master of hexagon hopping!"),
742 const int endingLen = sizeof(ending)/sizeof(ending[0]);
743 const int endingLen2 = sizeof(ending2)/sizeof(ending2[0]);
744 const int scrollMax = SCREEN_H + (endingLen+1) * FONT_SPACING;
746 struct Ending : public Menu
748 struct Particle{
749 double x,y,xs,ys,xa,ya;
750 double time;
751 int type;
753 void Update(double td)
755 if (type==EMPTY) return;
757 time -= td;
758 x += xs*td;
759 y += ys*td;
760 xs += xa*td;
761 ys += ya*td;
762 if (type==TILE_LASER_HEAD && time<0.3)
763 type=TILE_FIRE_PARTICLE_1;
764 if (type==TILE_FIRE_PARTICLE_1 && time<0.1)
765 type=TILE_FIRE_PARTICLE_2;
767 // if (type==COLLAPSABLE || type==COLLAPSE_DOOR)
768 // for (int i=int((time)*40); i<int((time+td)*40); i++)
769 // new Particle(TILE_GREEN_FRAGMENT_1, x+32-rand()%63, y+20-rand()%40);
771 if (y>SCREEN_H && type==TILE_GREEN_FRAGMENT)
772 xa=0, ys=-ys/2, y=SCREEN_H, type=TILE_GREEN_FRAGMENT_1, time+=rand()%100*0.01;
773 if (y>SCREEN_H && type==TILE_GREEN_FRAGMENT_1)
774 xa=0, ys=-ys/2, y=SCREEN_H, type=TILE_GREEN_FRAGMENT_2, time+=rand()%100*0.01;
775 if (y>SCREEN_H && type==TILE_GREEN_FRAGMENT_2)
776 type = EMPTY;
778 if (time<=0)
780 if (type < 10)
782 for (int i=0; i<40; i++)
783 new Particle(TILE_LASER_HEAD, x, y);
784 for (int i=0; i<40; i++)
785 new Particle(TILE_GREEN_FRAGMENT + rand() % 3, x+32-rand()%63, y+20-rand()%40);
787 if (type==COLLAPSE_DOOR)
789 type = COLLAPSABLE;
790 time += 1;
792 else
794 type = EMPTY;
798 if (type==EMPTY) return;
800 if (type<0.05 && type<15)
801 RenderTile(false, tileSolid[type] ? TILE_WHITE_WALL : TILE_WHITE_TILE, int(x)+scrollX, int(y)+scrollY);
802 else
803 RenderTile(false, type, int(x)+scrollX, int(y)+scrollY);
806 Particle() : type(EMPTY) {}
807 Particle(int t, int _x) : type(t), x(_x)
809 xa=ys=xs=0; ya=400;
810 //if (t==1)
812 xs = rand()%100-50;
813 ys=-400-rand()%200;
814 //x=rand() % SCREEN_W;
815 //y=rand() % SCREEN_H;
816 y = SCREEN_H+20;
817 time = ys/-ya;
820 Particle(int t, double _x, double _y) : type(t)
822 x=_x; y=_y;
823 xa=0; ya=-100;
824 double r = (rand() % 2000) * PI / 1000;
825 double d = rand() % 50 + 250;
827 xs = sin(r)*d;
828 ys = cos(r)*d;
829 time = 1 + (rand() & 255) /255.0;
830 if (t==TILE_WATER_PARTICLE || t==TILE_GREEN_FRAGMENT || t==TILE_GREEN_FRAGMENT_1)
831 time = 2;
832 xa=-xs/time; ya=-ys/time;
833 if (t==TILE_WATER_PARTICLE || t==TILE_GREEN_FRAGMENT || t==TILE_GREEN_FRAGMENT_1)
834 ya += 500, ya/=2, xa/=2, xs/=2, ys/=2;
836 ~Particle() { type = EMPTY; }
837 void* operator new(size_t sz);
840 Particle p[1000];
842 double scroll;
843 double t;
844 bool goodEnding;
845 static Ending* ending;
847 Ending(bool _goodEnd) : goodEnding(_goodEnd)
849 memset(p, 0, sizeof(p));
850 ending = this;
851 renderBG = false;
852 scroll = 0;
853 t=0;
856 void Render()
858 SDL_Rect a = {0,0,SCREEN_W,SCREEN_H};
859 SDL_FillRect(screen, &a, SDL_MapRGB(screen->format, 10,25,25));
861 for (int i=0; i<sizeof(p)/sizeof(p[0]); i++)
862 p[i].Update(t);
864 int x = a.x + a.w/2;
865 int xl = SCREEN_W/5;
866 int xr = SCREEN_W*4/5;
867 int y = SCREEN_H - int(scroll);
868 for (int i=0; i<endingLen; i++)
870 if (y>-FONT_SPACING*2 && y<SCREEN_H+FONT_SPACING)
872 const char * xx = (i<endingLen2 && goodEnding) ? ending2[i] : ::ending[i];
873 if (xx[0]=='<')
874 Print(xl, y+FONT_SPACING, xx+1);
875 else if (xx[0]=='>')
876 PrintR(xr, y, xx+1);
877 else if (xx[0]=='*')
879 RenderTile(false, atoi(xx+1), (xl+x)/2+scrollX, y+FONT_SPACING/2+scrollY);
880 RenderTile(false, atoi(strchr(xx, ',')+1), (xr+x)/2+scrollX, y+FONT_SPACING/2+scrollY);
882 else
883 PrintC(false, x, y, xx);
885 y+=FONT_SPACING;
887 if (scroll > scrollMax + FONT_SPACING*10)
888 PrintC(true, x, SCREEN_H/2-FONT_SPACING/2, _("The End"));
891 void Cancel();
893 bool KeyPressed(int key, int mod)
895 if (key=='r' && (mod & KMOD_CTRL))
897 time = 0;
898 memset(p, 0, sizeof(p));
900 else
901 return Menu::KeyPressed(key, mod);
902 return true;
905 void Update(double td)
907 noMouse = 1;
909 double old = time;
911 t = td;
912 if (keyState[SDLK_LSHIFT])
913 t = td*5;
914 if (keyState['0'])
915 t = MAX(-td*5, -time);
917 Menu::Update(t);
918 double s = scroll = time * 50;
919 // if (scroll > scrollMax)
920 // scroll = fmod(scroll, scrollMax);
921 // scroll = 0, time = 0;
923 if (old>4 && time > 4)
925 if (scroll < scrollMax + FONT_SPACING*17)
927 for (int i=int( old/2.5); i<int(time/2.5); i++)
929 int xs = (rand()%SCREEN_W * 6 + 1) / 8;
930 for (int j=rand()%3+1; j; j--)
931 new Particle(rand()&1 ? COLLAPSABLE : COLLAPSE_DOOR, xs+j*64);
934 if (scroll > scrollMax + FONT_SPACING*27)
935 Cancel();
941 Ending* Ending::ending = 0;
942 void* Ending::Particle::operator new(size_t sz)
944 static int start = 0;
945 const int max = sizeof(ending->p)/sizeof(ending->p[0]);
946 for (int i=0; i<max; i++)
948 start++;
949 if (start==max) start=0;
950 if (ending->p[start].type==EMPTY)
951 return &ending->p[start];
953 return &ending->p[rand() % max];
956 struct TitleMenu : public OptMenuTitle
958 TitleMenu() : OptMenuTitle("")
960 //left_align = 1;
962 SaveState p;
963 freeSlot = -1;
964 for (int i=0; i<MAX_GAMESLOT; i++)
966 char tmp[80];
967 GetSlotName(i, tmp);
968 FILE* f = file_open(tmp, "rb");
969 if (f)
971 p.LoadSave(f, false);
972 fclose(f);
974 if (p.general.completionPercentage==100 && p.general.masteredPercentage==100)
975 sprintf(optionSlotName[i], _("Continue game %d (All Clear!)"), i+1, p.general.completionPercentage, p.general.masteredPercentage);
976 else if (p.general.completionPercentage==100)
977 sprintf(optionSlotName[i], _("Continue game %d (%d%% + %d%%)"), i+1, p.general.completionPercentage, p.general.masteredPercentage);
978 else
979 sprintf(optionSlotName[i], _("Continue game %d (%d%% complete)"), i+1, p.general.completionPercentage);
981 opt[num_opt++] = OPT_GAMESLOT_0 + i;
983 else
985 // sprintf(optionSlotName[i], "Start new game (slot %d)", i+1);
986 // opt[num_opt++] = OPT_GAMESLOT_0 + i;
988 if (freeSlot==-1)
989 freeSlot = i;
994 if (num_opt < MAX_GAMESLOT)
995 opt[num_opt++] = OPT_GAMESLOT_NEW;
997 opt[num_opt++] = OPT_OPTIONS;
998 #ifdef EDIT
999 opt[num_opt++] = OPT_END;
1000 opt[num_opt++] = OPT_END2;
1001 #endif
1002 opt[num_opt++] = OPT_QUIT;
1004 Init();
1005 #ifdef EDIT
1006 r.y-=FONT_SPACING*2;
1007 r2.y-=FONT_SPACING*2;
1008 #else
1009 if (num_opt==3)
1010 r.y+=FONT_SPACING+FONT_SPACING/2, r2.y+=FONT_SPACING+FONT_SPACING/2;
1011 #endif
1013 bool KeyPressed(int key, int mod);
1016 struct QuitConfirmMenu : public OptMenuTitle
1018 QuitConfirmMenu() : OptMenuTitle(_("Quit: Are you sure?"))
1020 opt[num_opt++] = OPT_QUIT_CONFIRM;
1021 opt[select=num_opt++] = OPT_QUIT_CANCEL;
1022 Init();
1024 r.y += FONT_SPACING*1;
1025 r2.y += FONT_SPACING*2;
1030 struct DeleteConfirmMenu : public OptMenuTitle
1032 char tmp[800];
1033 int num;
1034 DeleteConfirmMenu(int _num) : OptMenuTitle(&tmp[0]), num(_num)
1036 //left_align = 1;
1038 sprintf(tmp, _("Really delete game %d?"), num+1);
1039 opt[num_opt++] = OPT_DELETE_CONFIRM;
1040 opt[select=num_opt++] = OPT_DELETE_CANCEL;
1041 Init();
1043 r.y += FONT_SPACING*1;
1044 r2.y += FONT_SPACING*2;
1046 void Select()
1048 if (select<0 || select>=num_opt)
1049 return;
1050 if (opt[select] == OPT_DELETE_CONFIRM)
1052 GetSlotName(num, tmp);
1053 remove(tmp);
1055 Pop();
1056 Pop();
1057 new TitleMenu();
1061 bool TitleMenu::KeyPressed(int key, int mod)
1063 if (key==SDLK_DELETE || key==SDLK_BACKSPACE || key==SDLK_F2)
1065 if (select<0 || select>=num_opt || opt[select]<OPT_GAMESLOT_0 || opt[select]>OPT_GAMESLOT_LAST)
1066 return true;
1067 int i = opt[select] - OPT_GAMESLOT_0;
1069 new DeleteConfirmMenu(i);
1071 return true;
1073 return OptMenu::KeyPressed(key, mod);
1076 struct PauseMenu : public OptMenu
1078 PauseMenu(bool isMap, bool allowGotoMap, int allowEnd, int allowEnd2) : OptMenu(_("Paused"))
1080 opt[num_opt++] = OPT_RESUME;
1081 if (!isMap)
1082 opt[num_opt++] = OPT_RESTART;
1083 opt[num_opt++] = OPT_OPTIONS;
1084 opt[num_opt++] = OPT_HELP;
1085 if (allowEnd || allowEnd2)
1086 opt[num_opt++] = allowEnd2 ? OPT_END2 : OPT_END;
1087 opt[num_opt++] = (isMap || !allowGotoMap) ? OPT_QUIT_MENU_CONFIRM : OPT_GOTO_MAP;
1088 Init();
1090 virtual bool KeyPressed(int key, int mod)
1092 if (key=='p' || key==SDLK_PAUSE)
1094 Cancel();
1095 return true;
1097 return Menu::KeyPressed(key, mod);
1102 struct OptionMenu : public OptMenuTitle
1104 bool title;
1105 OptionMenu(bool _title) : OptMenuTitle(_("Options")), title(_title)
1107 opt[num_opt++] = OPT_FULLSCREEN;
1109 opt[num_opt++] = OPT_BACK;
1111 if (title)
1113 OptMenuTitle::Init(), renderBG=false;
1114 r.y += FONT_SPACING;
1115 r2.y += FONT_SPACING*2;
1117 else
1118 OptMenu::Init(), renderBG=true;
1120 void Render()
1122 if (title)
1123 OptMenuTitle::Render();
1124 else
1125 OptMenu::Render();
1129 void RenderFade(double time, int dir, int seed);
1131 struct Fader : public Menu
1133 int dir;
1134 double speed;
1135 int result;
1136 Fader(int _dir, int _result, double _speed=1) : dir(_dir), result(_result), speed(_speed)
1138 renderBG = under ? under->renderBG : true;
1140 void Render()
1142 if (under)
1143 under->Render();
1145 RenderFade(time, dir, (long)this);
1147 void Update(double timedelta)
1149 Menu::Update(timedelta * speed);
1150 if (result==-2)
1152 if (time > 0.7)
1153 quitting = 1;
1155 else if (time >= 0.5)
1157 Pop();
1158 if (result==-1)
1160 new TitleMenu();
1161 new Fader(1, -3);
1163 if (result==-4)
1165 Pop(); // Remove old menu
1166 HackKeyPress(SDLK_ESCAPE, KMOD_CTRL | KMOD_SHIFT); // Reload map combination!
1168 if (result==-6)
1170 Pop(); // Remove old menu
1171 new Fader(1, 0, speed);
1173 if (result==-5 || result==-7)
1175 new Ending(result==-7);
1176 new Fader(1, 0, speed);
1182 void Ending::Cancel()
1184 new Fader(-1, -6, 0.3);
1185 // Pop();
1188 void ToggleFullscreen();
1190 void OptMenu::Select()
1192 if (select<0 || select>=num_opt)
1193 return;
1194 switch(opt[select])
1196 case OPT_RESUME:
1197 Cancel();
1198 break;
1200 case OPT_RESTART:
1201 Cancel();
1202 HackKeyPress('r', KMOD_CTRL);
1203 break;
1205 case OPT_GOTO_MAP:
1206 case OPT_GOTO_MAP_CONTINUE:
1207 Pop();
1208 HackKeyPress(SDLK_ESCAPE, KMOD_CTRL);
1209 break;
1211 case OPT_QUIT:
1212 new QuitConfirmMenu();
1213 break;
1215 case OPT_FULLSCREEN:
1216 ToggleFullscreen();
1217 break;
1219 case OPT_QUIT_CONFIRM:
1220 new Fader(-1, -2);
1221 break;
1223 case OPT_QUIT_MENU_CONFIRM:
1224 Pop();
1225 new Fader(-1, -1);
1226 break;
1228 case OPT_OPTIONS:
1229 new OptionMenu(!activeMenu->renderBG);
1230 break;
1232 case OPT_HELP:
1233 new HintReview();
1234 break;
1236 case OPT_QUIT_CANCEL:
1237 case OPT_BACK:
1238 Pop();
1239 break;
1241 case OPT_END:
1242 new Fader(-1, -5, 0.3);
1243 break;
1245 case OPT_END2:
1246 new Fader(-1, -7, 0.3);
1247 break;
1249 case OPT_UNDO:
1250 Pop();
1251 HackKeyPress('z', 0);
1252 break;
1254 default:
1255 if (opt[select]>=OPT_GAMESLOT_0 && opt[select]<=OPT_GAMESLOT_LAST
1256 || opt[select]==OPT_GAMESLOT_NEW && freeSlot>=0)
1258 if (opt[select]==OPT_GAMESLOT_NEW)
1259 GetSlotName(freeSlot, currentSlot);
1260 else
1261 GetSlotName(opt[select]-OPT_GAMESLOT_0, currentSlot);
1262 new Fader(-1, -4);
1264 break;