Gettextize program so that it can be translated to other languages
[hex-a-hop.git] / menus.h
blobe3c424ba1012486de819e350700a1b2a35efce8f
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
20 struct Menu;
21 Menu* activeMenu = 0;
22 Menu* deadMenu = 0;
24 static void HackKeyPress(int key, int mod)
26 int k = keyState[key];
27 StateMakerBase::current->KeyPressed(key, mod);
28 keyState[key] = k;
31 struct Menu {
32 bool renderBG;
34 Menu() : under(activeMenu), time(0), renderBG(true) { activeMenu = this; }
36 virtual ~Menu() {
37 if(this!=deadMenu) FATAL("this!=deadMenu");
38 deadMenu=under;
41 static void Pop()
43 if (!activeMenu) return;
44 Menu* m = activeMenu;
45 activeMenu = activeMenu->under;
46 m->under = deadMenu;
47 deadMenu = m;
50 virtual bool KeyPressed(int key, int mod)
52 if (key=='w' || key==SDLK_UP || key==SDLK_KP8 || key=='q' || key=='e' || key==SDLK_KP7 || key==SDLK_KP9)
53 Move(-1), noMouse=1;
54 else if (key=='s' || key==SDLK_DOWN || key==SDLK_KP2 || key=='a' || key=='d' || key==SDLK_KP1 || key==SDLK_KP3)
55 Move(1), noMouse=1;
56 else if (key==' ' || key==SDLK_RETURN)
58 Select();
59 noMouse=1;
61 else if (key==SDLK_ESCAPE || key==SDLK_BACKSPACE || key==SDLK_KP_PERIOD || key==SDLK_DELETE)
62 Cancel();
63 else
64 return false;
65 return true;
67 virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons)
69 if (buttons_pressed==4 || buttons_pressed==2)
70 Cancel();
73 virtual void Move(int dir) {}
74 virtual void Select() {}
75 virtual void Cancel() {}
77 virtual void Update(double timedelta) {time+=timedelta;}
78 virtual void Render() = 0;
80 Menu * under;
81 double time;
84 const char * hint[] = {
86 /*EMPTY*/
87 _("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."),
88 /*NORMAL*/
90 /*COLLAPSABLE*/
91 _("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!"),
92 /*COLLAPSE_DOOR*/
93 _("The coloured walls flatten themselves when there are no matching coloured tiles remaining."),
94 /*TRAMPOLINE*/
95 _("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!"),
96 /*SPINNER*/
97 _("A red spinner tile will rotate the pieces around it when you step on it."),
98 /*WALL*/
100 /*COLLAPSABLE2*/
101 _("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..."),
102 /*COLLAPSE_DOOR2*/
104 /*GUN*/
105 _("Yellow laser tiles fire when you step on them. Shooting other laser tiles is more destructive."),
106 /*TRAP*/
107 _("Ice is slippery! Please be careful!!"),
108 /*COLLAPSABLE3*/
110 /*BUILDER*/
111 _("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."),
112 /*SWITCH*/
114 /*FLOATING_BALL*/
115 /* TRANSLATORS: pop means vanish and Emy drowns (you loose) */
116 _("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."),
117 /*LIFT_DOWN*/
118 _("The blue lifts go up or down when you land on them."),
119 /*LIFT_UP*/
122 0,0,0,0,
123 //Item 0 (21)
124 _("The spiky anti-ice pickups turn icy tiles into blue ones. They get used automatically when you land on ice."),
125 //Item 1 (22)
126 /* TRANSLATORS: Normally you jump from one plate to another. The golden jump (a
127 pickup) allows you to jump and land on the *same* plate */
128 _("Collecting the golden jump pickups will allow you to do a big vertical jump. Try it out on different types of tile. Use the space bar or return key to jump. Or click on the tile you're currently on with the mouse."),
130 0,0,
132 // Map (25)
133 _("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."),
135 // Scoring (26)
136 /* TRANSLATORS: Levels are depicted as black balls. Once you passed them, they
137 turn silver. If you reached the par, they turn golden (with a crown), and if
138 you beat the par, they turn their shape/color once more */
139 _("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?"),
141 0,0,0,
143 // End of help (30)
144 _("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!"),
146 // First help page (31)
147 /* TRANSLATORS: This string is copied twice into the POT file to workaround a
148 gettext limitation (no macro expansion). The extracted string "Welcome to "
149 will not be used. */
150 _("Welcome to " GAMENAME "! This is a puzzle game based on hexagonal tiles. There is no time limit and no real-time elements, 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."),
153 struct HintMessage : public Menu
155 static int flags;
157 const char * msg;
158 char title[50];
159 int numLines;
160 int state;
162 static bool FlagTile(int t, bool newStuff=true)
164 if (t==LIFT_UP) t=LIFT_DOWN;
165 if (newStuff && (flags & (1<<t))) return false;
166 if (!newStuff && !(flags & (1<<t))) return false;
167 if (t>31) return false;
168 if (!hint[t]) return false;
170 flags |= 1<<t;
171 new HintMessage(hint[t]);
172 return true;
175 HintMessage(const char * m) { Init(m); }
177 void Init(const char * m)
179 numLines = 1;
180 state = 0; time = 0;
181 memset(title, 0, sizeof(title));
182 char * x = strstr(m, "|");
183 if (!x)
185 msg = m;
186 strcpy(title, _("Info:"));
188 else
190 strncpy(title, m, x-m);
191 msg=x+1;
194 const char * s = m;
195 while ((s=strstr(s, " ")))
196 s+=2, numLines++;
199 virtual void Render()
201 int y = SCREEN_H/4 + int(SCREEN_H*MAX(1-time*5, 0)*3/4);
202 if (state)
203 y = SCREEN_H/4 + int(SCREEN_H*(-time*5)*3/4);
205 Render(0, y);
207 if (!state && time>0.2)
208 Print(SCREEN_W*3/4, SCREEN_H-FONT_SPACING, _("Press any key"));
211 void Render(int x, int y)
213 SDL_Rect r2 = {x+TILE_W1, y, SCREEN_W-TILE_W1*2, numLines*FONT_SPACING+FONT_SPACING};
214 SDL_Rect r = {r2.x-2, r2.y-2-FONT_SPACING, r2.w+4, r2.h+4+FONT_SPACING};
215 SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 60,90,90));
216 SDL_FillRect(screen, &r2, SDL_MapRGB(screen->format, 20,50,50));
217 Print(r.x+FONT_SPACING/4, y-FONT_SPACING, title);
218 PrintC(true, x+SCREEN_W/2, y+FONT_SPACING/2, msg);
221 virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons)
223 if (buttons_pressed && state==0 && time>0.2)
224 state = 1, time=0;
227 bool KeyPressed(int key, int mod)
229 if (state==0 && time>0.2)
230 state = 1, time=0;
231 return true;
234 virtual void Update(double timedelta)
236 Menu::Update(timedelta);
237 if(state && time > 0.2)
238 Pop();
242 int HintMessage::flags = 1<<31 | 1<<30;
244 struct HintReview : public HintMessage
246 int page;
247 int page_dir;
248 int page_count;
249 int page_display;
250 HintReview() : HintMessage(hint[31]), page(31), page_dir(0), page_display(0)
252 page_count=0;
253 for (int i=0; i<32; i++)
254 if (flags & (1<<i))
255 page_count++;
258 void Cancel() { Pop(); }
259 void Select() { Pop(); }
262 virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons)
264 if (buttons_pressed==1)
266 if (y < SCREEN_H/4-FONT_SPACING+2)
267 Move(-1);
268 else if (y > SCREEN_H/4+FONT_SPACING*numLines+FONT_SPACING)
269 Move(1);
270 else
271 Cancel();
273 else
274 Menu::Mouse(x,y,dx,dy,buttons_pressed, buttons_released, buttons);
277 bool KeyPressed(int key, int mod)
279 if (key==SDLK_LEFT)
280 Move(-1);
281 else if (key==SDLK_RIGHT)
282 Move(1);
283 else
284 return Menu::KeyPressed(key, mod);
285 return true;
288 virtual void Render()
290 const double SPD = 10;
292 #ifdef EDIT
293 sprintf (title, _("Help (Page --)"), page_display+1, page_count);
294 #else
295 sprintf (title, _("Help (Page %d/%d)"), page_display+1, page_count);
296 #endif
298 int y=SCREEN_H/4;
299 if (state==0)
300 y = SCREEN_H/4+int(MAX(0, time*SPD)*-page_dir*SCREEN_H);
301 if (state==1)
302 y = SCREEN_H/4+int(MAX(0, 1-time*SPD)*page_dir*SCREEN_H);
304 //if (!noMouse)
306 PrintC(false, SCREEN_W/2, y-FONT_SPACING*2, "^");
307 PrintC(false, SCREEN_W/2, y+FONT_SPACING*(numLines+1)+FONT_SPACING/2-2, "_");
310 HintMessage::Render(0,y);
312 if (time > 1.0/SPD && page_dir && state==0)
314 do {
315 page = (page+page_dir) & 31;
316 #ifdef EDIT
317 if (hint[page]) break;
318 #endif
319 } while (hint[page]==0 || !(flags&(1<<page)));
320 Init(hint[page]);
322 page_display = (page_display + page_count + page_dir) % page_count;
324 time = 0;
325 state=1;
327 if (time>1.0/SPD && state==1)
328 state=0, page_dir = 0;
330 virtual void Update(double timedelta)
332 Menu::Update(timedelta);
334 void Move(int dir)
336 if (page_dir)
337 return;
338 time = 0;
339 page_dir = dir;
340 state = 0;
344 #define MAX_GAMESLOT 4
345 enum option {
346 OPT_GAMESLOT_0,
347 OPT_GAMESLOT_LAST = OPT_GAMESLOT_0 + MAX_GAMESLOT - 1,
348 OPT_RESUME,
349 OPT_RESTART,
350 OPT_GOTO_MAP,
351 OPT_GOTO_MAP_CONTINUE,
352 OPT_FULLSCREEN,
353 OPT_OPTIONS,
354 OPT_QUIT,
355 OPT_QUIT_CONFIRM,
356 OPT_QUIT_CANCEL,
357 OPT_QUIT_MENU_CONFIRM,
358 OPT_HELP,
359 OPT_GAMESLOT_NEW,
360 OPT_DELETE_CONFIRM,
361 OPT_DELETE_CANCEL,
362 OPT_UNDO,
363 OPT_BACK,
364 OPT_END, OPT_END2,
367 char optionSlotName[MAX_GAMESLOT][40] = {0};
368 char currentSlot[80] = "";
369 int freeSlot = -1;
370 char* GetSlotName(int i, char * t)
372 sprintf(t, "save%d.dat", i+1);
373 return t;
376 char * optionString[] = {
377 optionSlotName[0],
378 optionSlotName[1],
379 optionSlotName[2],
380 optionSlotName[3],
381 _("Resume"),
382 _("Restart Level"),
383 _("Return to Map"),
384 _("Continue"),
385 _("Toggle Fullscreen"),
386 _("Options"),
387 _("Quit"),
388 _("Yes"),
389 _("No"),
390 _("Return to Title"),
391 _("Help"),
392 _("Start New Game"),
393 _("Yes, really delete it!"),
394 _("Don't do it!"),
395 _("Undo Last Move"),
396 _("OK"),
397 _("View Credits Sequence"), _("View Credits Sequence"),
401 struct OptMenu : public Menu
403 int select;
404 int num_opt;
405 int opt[10];
406 bool left_align;
407 const char * title;
408 SDL_Rect r, r2;
410 OptMenu(const char * t) : select(0), title(t)
412 left_align = false;
413 num_opt = 0;
416 void Init()
418 r.w=SCREEN_W/2;
419 r.x=(SCREEN_W-r.w)/2;
420 r.y=SCREEN_H/3;
422 r2 = r;
424 const int SPACE = int(FONT_SPACING * 1.5);
426 r2.h = SPACE*num_opt + FONT_SPACING/2;
427 r.h = r2.h + (FONT_SPACING+2*2);
428 r.y -= FONT_SPACING+2;
429 r.w += 2*2;
430 r.x -= 2;
433 void RenderOption(int o, const char * s)
435 int y = r2.y + FONT_SPACING/2 + int(FONT_SPACING * 1.5) * o;
436 if (left_align)
438 int x = r.x + font[' '].w;
439 int x1 = x + (font[' '].w + font['>'].w + FONT_X_SPACING*2) / 2;
440 if (select==o)
442 //x += int( sin(time*9)*2.5 );
443 //y += int( sin(time*9 + 1.5)*1.5 );
444 Print(x, y, "> %s", s);
446 else
448 Print(x1, y, "%s", s);
451 else
453 int x = r.x + r.w/2;
454 if (select==o)
456 //x += int( sin(time*9)*2.5 );
457 //y += int( sin(time*9 + 1.5)*1.5 );
458 PrintC(false, x, y, "> %s <", s);
460 else
462 PrintC(false, x, y, "%s", s);
467 void Move(int dir)
469 select += dir;
470 if (select<0) select = num_opt-1;
471 if (select>=num_opt) select = 0;
473 virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons)
475 if (1)
477 if (x<r2.x || y<r2.y || x>r2.x+r2.w || y>r2.y+r2.h)
479 if (buttons_pressed!=4 && buttons_pressed!=2)
480 return;
482 else
484 select = (y-r2.y-FONT_SPACING/3) / int(FONT_SPACING*1.5);
485 if (select<0) select = 0;
486 if (select>=num_opt) select = num_opt-1;
489 if (buttons_pressed==1)
490 Select();
491 Menu::Mouse(x, y, dx, dy, buttons_pressed, buttons_released, buttons);
493 void Select();
495 void Render()
497 RenderBG();
498 RenderTitle();
499 RenderOptions();
501 void RenderBG()
503 SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 60,90,90));
504 SDL_FillRect(screen, &r2, SDL_MapRGB(screen->format, 20,50,50));
506 void RenderTitle()
508 int y = r2.y + FONT_SPACING / 2;
509 if (left_align)
510 Print(r2.x+font[' '].w, r.y+4, title);
511 else
512 PrintC(false, r2.x+r2.w/2, r.y+4, title);
514 void RenderOptions()
516 for (int i=0; i<num_opt; i++)
517 RenderOption(i, optionString[opt[i]]);
519 void Cancel()
521 Pop();
525 struct WinLoseScreen : public OptMenu
527 bool win;
528 int score, par, best_score;
529 WinLoseScreen(bool _win, int _score=0, int _par=0, int _prev_score=0) :
530 score(_score),
531 par(_par),
532 best_score(_prev_score),
533 win(_win),
534 OptMenu(_win ? _("Level Complete!") : _("Emi can't swim..."))
536 if (!win)
537 opt[num_opt++] = OPT_UNDO;
538 if (!win)
539 opt[num_opt++] = OPT_RESTART;
540 opt[num_opt++] = win ? OPT_GOTO_MAP_CONTINUE : OPT_GOTO_MAP;
542 Init();
544 if (win)
546 r.h += FONT_SPACING * 3 + FONT_SPACING/2;
547 r2.h += FONT_SPACING * 3 + FONT_SPACING/2;
551 void Render()
553 OptMenu::RenderBG();
554 OptMenu::RenderTitle();
556 if (win)
557 r2.y += FONT_SPACING * 3 + FONT_SPACING/2;
559 OptMenu::RenderOptions();
561 if (win)
562 r2.y -= FONT_SPACING * 3 + FONT_SPACING/2;
564 if (win)
566 int x = r.x+r.w/2;
567 int y = r2.y + FONT_SPACING/2;
568 if (score < best_score && score <= par)
569 PrintC(true, x, y, _("New Best Score: %d Par Score: %d Par Beaten!"), score, par);
570 else if (score < best_score)
571 PrintC(true, x, y, _("New Best Score: %d Par Score: %d"), score, par);
572 else if (par && best_score)
573 PrintC(true, x, y, _("Score: %d Previous Best: %d Par Score: %d"), score, best_score, par);
574 else
575 PrintC(true, x, y+FONT_SPACING/2, _("Well Done! Level Completed!"));
579 static void Undo()
581 Pop();
582 HackKeyPress('z', 0);
584 bool KeyPressed(int key, int mod)
586 if (key=='z' || key=='u' || key==SDLK_DELETE || key==SDLK_BACKSPACE)
587 return Undo(), true;
588 if (key=='r' && (mod & KMOD_CTRL))
590 Pop();
591 HackKeyPress(key, mod);
592 return true;
594 return OptMenu::KeyPressed(key, mod);
596 virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons)
598 if (buttons_pressed==4)
600 Undo();
601 return;
604 OptMenu::Mouse(x, y, dx, dy, buttons_pressed, buttons_released, buttons);
606 void Cancel()
608 if (win)
609 select=0, Select();
610 else
611 Undo();
615 struct OptMenuTitle : public OptMenu
617 OptMenuTitle(const char * t) : OptMenu(t)
619 renderBG = false;
620 //left_align = true;
623 void Render()
625 SDL_Rect a = {0,0,SCREEN_W,SCREEN_H};
626 // SDL_FillRect(screen, &a, SDL_MapRGB(screen->format, 10,25,25));
629 SDL_BlitSurface(titlePage, &a, screen, &a);
631 OptMenu::RenderTitle();
632 OptMenu::RenderOptions();
635 void Init()
637 OptMenu::Init();
639 int xw = SCREEN_W/6;
640 r.w += xw; r2.w+=xw;
641 int x = SCREEN_W - r.x - r.w;// - FONT_SPACING;
642 int y = SCREEN_H - r.y - r.h + FONT_SPACING/4;// - FONT_SPACING;
643 r.x += x; r2.x += x;
644 r.y += y; r2.y += y;
645 r.w+=20; r2.w+=20; r.h+=20; r2.h+=20;
647 r.h = r2.h = SCREEN_H;
648 r2.y = SCREEN_H/2;
649 r.y = r2.y - FONT_SPACING - 2;
653 const char *ending[] = {
654 _(" Very Well Done! "),
655 "", "", "", "", "", "",
657 "", "", "*15,4", "", "",
659 _("All Levels Cleared!"),
661 "", "", "*5,7", "", "",
663 _("Not a single green hexagon is left unbroken."),
664 "",
665 _("Truly, you are a master of hexagon hopping!"),
667 "", "", "*9,10", "", "",
669 "", _("Credits"), "", "", "",
670 _("<Design & Direction:"), ">Tom Beaumont", "", "",
671 _("<Programming:"), ">Tom Beaumont", "", "",
672 _("<Graphics:"), ">Tom Beaumont", "", "",
673 _("<Thanks to:"), ">Kris Beaumont", "", "",
674 // "", "<Some useless facts...", "",
675 _("<Tools and libraries used:"), "", ">Photoshop LE", ">Inno Setup", ">Wings 3D", ">MSVC", ">SDL", "",
676 _("<Fonts used:"), "", ">Copperplate gothic bold", ">Verdana", "",
678 "", "", "*12,14", "", "",
680 _("Thanks for playing!")
683 const char *ending2[] = {
684 _(" Absolutely Amazing! "),
685 "", "", "", "", "",
687 "", "", "*15,4", "", "",
689 _("All Levels Mastered!!"),
691 "", "", "*5,7", "", "",
693 _("You crushed every last green hexagon with"),
694 _("breathtaking efficiency!"),
696 _("You truly are a grand master of hexagon hopping!"),
699 const int endingLen = sizeof(ending)/sizeof(ending[0]);
700 const int endingLen2 = sizeof(ending2)/sizeof(ending2[0]);
701 const int scrollMax = SCREEN_H + (endingLen+1) * FONT_SPACING;
703 struct Ending : public Menu
705 struct Particle{
706 double x,y,xs,ys,xa,ya;
707 double time;
708 int type;
710 void Update(double td)
712 if (type==EMPTY) return;
714 time -= td;
715 x += xs*td;
716 y += ys*td;
717 xs += xa*td;
718 ys += ya*td;
719 if (type==TILE_LASER_HEAD && time<0.3)
720 type=TILE_FIRE_PARTICLE_1;
721 if (type==TILE_FIRE_PARTICLE_1 && time<0.1)
722 type=TILE_FIRE_PARTICLE_2;
724 // if (type==COLLAPSABLE || type==COLLAPSE_DOOR)
725 // for (int i=int((time)*40); i<int((time+td)*40); i++)
726 // new Particle(TILE_GREEN_FRAGMENT_1, x+32-rand()%63, y+20-rand()%40);
728 if (y>SCREEN_H && type==TILE_GREEN_FRAGMENT)
729 xa=0, ys=-ys/2, y=SCREEN_H, type=TILE_GREEN_FRAGMENT_1, time+=rand()%100*0.01;
730 if (y>SCREEN_H && type==TILE_GREEN_FRAGMENT_1)
731 xa=0, ys=-ys/2, y=SCREEN_H, type=TILE_GREEN_FRAGMENT_2, time+=rand()%100*0.01;
732 if (y>SCREEN_H && type==TILE_GREEN_FRAGMENT_2)
733 type = EMPTY;
735 if (time<=0)
737 if (type < 10)
739 for (int i=0; i<40; i++)
740 new Particle(TILE_LASER_HEAD, x, y);
741 for (int i=0; i<40; i++)
742 new Particle(TILE_GREEN_FRAGMENT + rand() % 3, x+32-rand()%63, y+20-rand()%40);
744 if (type==COLLAPSE_DOOR)
746 type = COLLAPSABLE;
747 time += 1;
749 else
751 type = EMPTY;
755 if (type==EMPTY) return;
757 if (type<0.05 && type<15)
758 RenderTile(false, tileSolid[type] ? TILE_WHITE_WALL : TILE_WHITE_TILE, int(x)+scrollX, int(y)+scrollY);
759 else
760 RenderTile(false, type, int(x)+scrollX, int(y)+scrollY);
763 Particle() : type(EMPTY) {}
764 Particle(int t, int _x) : type(t), x(_x)
766 xa=ys=xs=0; ya=400;
767 //if (t==1)
769 xs = rand()%100-50;
770 ys=-400-rand()%200;
771 //x=rand() % SCREEN_W;
772 //y=rand() % SCREEN_H;
773 y = SCREEN_H+20;
774 time = ys/-ya;
777 Particle(int t, double _x, double _y) : type(t)
779 x=_x; y=_y;
780 xa=0; ya=-100;
781 double r = (rand() % 2000) * PI / 1000;
782 double d = rand() % 50 + 250;
784 xs = sin(r)*d;
785 ys = cos(r)*d;
786 time = 1 + (rand() & 255) /255.0;
787 if (t==TILE_WATER_PARTICLE || t==TILE_GREEN_FRAGMENT || t==TILE_GREEN_FRAGMENT_1)
788 time = 2;
789 xa=-xs/time; ya=-ys/time;
790 if (t==TILE_WATER_PARTICLE || t==TILE_GREEN_FRAGMENT || t==TILE_GREEN_FRAGMENT_1)
791 ya += 500, ya/=2, xa/=2, xs/=2, ys/=2;
793 ~Particle() { type = EMPTY; }
794 void* operator new(size_t sz);
797 Particle p[1000];
799 double scroll;
800 double t;
801 bool goodEnding;
802 static Ending* ending;
804 Ending(bool _goodEnd) : goodEnding(_goodEnd)
806 memset(p, 0, sizeof(p));
807 ending = this;
808 renderBG = false;
809 scroll = 0;
810 t=0;
813 void Render()
815 SDL_Rect a = {0,0,SCREEN_W,SCREEN_H};
816 SDL_FillRect(screen, &a, SDL_MapRGB(screen->format, 10,25,25));
818 for (int i=0; i<sizeof(p)/sizeof(p[0]); i++)
819 p[i].Update(t);
821 int x = a.x + a.w/2;
822 int xl = SCREEN_W/5;
823 int xr = SCREEN_W*4/5;
824 int y = SCREEN_H - int(scroll);
825 for (int i=0; i<endingLen; i++)
827 if (y>-FONT_SPACING*2 && y<SCREEN_H+FONT_SPACING)
829 const char * xx = (i<endingLen2 && goodEnding) ? ending2[i] : ::ending[i];
830 if (xx[0]=='<')
831 Print(xl, y+FONT_SPACING, xx+1);
832 else if (xx[0]=='>')
833 PrintR(xr, y, xx+1);
834 else if (xx[0]=='*')
836 RenderTile(false, atoi(xx+1), (xl+x)/2+scrollX, y+FONT_SPACING/2+scrollY);
837 RenderTile(false, atoi(strchr(xx, ',')+1), (xr+x)/2+scrollX, y+FONT_SPACING/2+scrollY);
839 else
840 PrintC(false, x, y, xx);
842 y+=FONT_SPACING;
844 if (scroll > scrollMax + FONT_SPACING*10)
845 PrintC(true, x, SCREEN_H/2-FONT_SPACING/2, _("The End"));
848 void Cancel();
850 bool KeyPressed(int key, int mod)
852 if (key=='r' && (mod & KMOD_CTRL))
854 time = 0;
855 memset(p, 0, sizeof(p));
857 else
858 return Menu::KeyPressed(key, mod);
859 return true;
862 void Update(double td)
864 noMouse = 1;
866 double old = time;
868 t = td;
869 if (keyState[SDLK_LSHIFT])
870 t = td*5;
871 if (keyState['0'])
872 t = MAX(-td*5, -time);
874 Menu::Update(t);
875 double s = scroll = time * 50;
876 // if (scroll > scrollMax)
877 // scroll = fmod(scroll, scrollMax);
878 // scroll = 0, time = 0;
880 if (old>4 && time > 4)
882 if (scroll < scrollMax + FONT_SPACING*17)
884 for (int i=int( old/2.5); i<int(time/2.5); i++)
886 int xs = (rand()%SCREEN_W * 6 + 1) / 8;
887 for (int j=rand()%3+1; j; j--)
888 new Particle(rand()&1 ? COLLAPSABLE : COLLAPSE_DOOR, xs+j*64);
891 if (scroll > scrollMax + FONT_SPACING*27)
892 Cancel();
898 Ending* Ending::ending = 0;
899 void* Ending::Particle::operator new(size_t sz)
901 static int start = 0;
902 const int max = sizeof(ending->p)/sizeof(ending->p[0]);
903 for (int i=0; i<max; i++)
905 start++;
906 if (start==max) start=0;
907 if (ending->p[start].type==EMPTY)
908 return &ending->p[start];
910 return &ending->p[rand() % max];
913 struct TitleMenu : public OptMenuTitle
915 TitleMenu() : OptMenuTitle("")
917 //left_align = 1;
919 SaveState p;
920 freeSlot = -1;
921 for (int i=0; i<MAX_GAMESLOT; i++)
923 char tmp[80];
924 GetSlotName(i, tmp);
925 FILE* f = file_open(tmp, "rb");
926 if (f)
928 p.LoadSave(f, false);
929 fclose(f);
931 if (p.general.completionPercentage==100 && p.general.masteredPercentage==100)
932 sprintf(optionSlotName[i], _("Continue game %d (All Clear!)"), i+1, p.general.completionPercentage, p.general.masteredPercentage);
933 else if (p.general.completionPercentage==100)
934 sprintf(optionSlotName[i], _("Continue game %d (%d%% + %d%%)"), i+1, p.general.completionPercentage, p.general.masteredPercentage);
935 else
936 sprintf(optionSlotName[i], _("Continue game %d (%d%% complete)"), i+1, p.general.completionPercentage);
938 opt[num_opt++] = OPT_GAMESLOT_0 + i;
940 else
942 // sprintf(optionSlotName[i], "Start new game (slot %d)", i+1);
943 // opt[num_opt++] = OPT_GAMESLOT_0 + i;
945 if (freeSlot==-1)
946 freeSlot = i;
951 if (num_opt < MAX_GAMESLOT)
952 opt[num_opt++] = OPT_GAMESLOT_NEW;
954 opt[num_opt++] = OPT_OPTIONS;
955 #ifdef EDIT
956 opt[num_opt++] = OPT_END;
957 opt[num_opt++] = OPT_END2;
958 #endif
959 opt[num_opt++] = OPT_QUIT;
961 Init();
962 #ifdef EDIT
963 r.y-=FONT_SPACING*2;
964 r2.y-=FONT_SPACING*2;
965 #else
966 if (num_opt==3)
967 r.y+=FONT_SPACING+FONT_SPACING/2, r2.y+=FONT_SPACING+FONT_SPACING/2;
968 #endif
970 bool KeyPressed(int key, int mod);
973 struct QuitConfirmMenu : public OptMenuTitle
975 QuitConfirmMenu() : OptMenuTitle(_("Quit: Are you sure?"))
977 opt[num_opt++] = OPT_QUIT_CONFIRM;
978 opt[select=num_opt++] = OPT_QUIT_CANCEL;
979 Init();
981 r.y += FONT_SPACING*1;
982 r2.y += FONT_SPACING*2;
987 struct DeleteConfirmMenu : public OptMenuTitle
989 char tmp[80];
990 int num;
991 DeleteConfirmMenu(int _num) : OptMenuTitle(&tmp[0]), num(_num)
993 //left_align = 1;
995 sprintf(tmp, _("Really delete game %d?"), num+1);
996 opt[num_opt++] = OPT_DELETE_CONFIRM;
997 opt[select=num_opt++] = OPT_DELETE_CANCEL;
998 Init();
1000 r.y += FONT_SPACING*1;
1001 r2.y += FONT_SPACING*2;
1003 void Select()
1005 if (select<0 || select>=num_opt)
1006 return;
1007 if (opt[select] == OPT_DELETE_CONFIRM)
1009 GetSlotName(num, tmp);
1010 remove(tmp);
1012 Pop();
1013 Pop();
1014 new TitleMenu();
1018 bool TitleMenu::KeyPressed(int key, int mod)
1020 if (key==SDLK_DELETE || key==SDLK_BACKSPACE || key==SDLK_F2)
1022 if (select<0 || select>=num_opt || opt[select]<OPT_GAMESLOT_0 || opt[select]>OPT_GAMESLOT_LAST)
1023 return true;
1024 int i = opt[select] - OPT_GAMESLOT_0;
1026 new DeleteConfirmMenu(i);
1028 return true;
1030 return OptMenu::KeyPressed(key, mod);
1033 struct PauseMenu : public OptMenu
1035 PauseMenu(bool isMap, bool allowGotoMap, int allowEnd, int allowEnd2) : OptMenu(_("Paused"))
1037 opt[num_opt++] = OPT_RESUME;
1038 if (!isMap)
1039 opt[num_opt++] = OPT_RESTART;
1040 opt[num_opt++] = OPT_OPTIONS;
1041 opt[num_opt++] = OPT_HELP;
1042 if (allowEnd || allowEnd2)
1043 opt[num_opt++] = allowEnd2 ? OPT_END2 : OPT_END;
1044 opt[num_opt++] = (isMap || !allowGotoMap) ? OPT_QUIT_MENU_CONFIRM : OPT_GOTO_MAP;
1045 Init();
1047 virtual bool KeyPressed(int key, int mod)
1049 if (key=='p' || key==SDLK_PAUSE)
1051 Cancel();
1052 return true;
1054 return Menu::KeyPressed(key, mod);
1059 struct OptionMenu : public OptMenuTitle
1061 bool title;
1062 OptionMenu(bool _title) : OptMenuTitle(_("Options")), title(_title)
1064 opt[num_opt++] = OPT_FULLSCREEN;
1066 opt[num_opt++] = OPT_BACK;
1068 if (title)
1070 OptMenuTitle::Init(), renderBG=false;
1071 r.y += FONT_SPACING;
1072 r2.y += FONT_SPACING*2;
1074 else
1075 OptMenu::Init(), renderBG=true;
1077 void Render()
1079 if (title)
1080 OptMenuTitle::Render();
1081 else
1082 OptMenu::Render();
1086 void RenderFade(double time, int dir, int seed);
1088 struct Fader : public Menu
1090 int dir;
1091 double speed;
1092 int result;
1093 Fader(int _dir, int _result, double _speed=1) : dir(_dir), result(_result), speed(_speed)
1095 renderBG = under ? under->renderBG : true;
1097 void Render()
1099 if (under)
1100 under->Render();
1102 RenderFade(time, dir, (long)this);
1104 void Update(double timedelta)
1106 Menu::Update(timedelta * speed);
1107 if (result==-2)
1109 if (time > 0.7)
1110 quitting = 1;
1112 else if (time >= 0.5)
1114 Pop();
1115 if (result==-1)
1117 new TitleMenu();
1118 new Fader(1, -3);
1120 if (result==-4)
1122 Pop(); // Remove old menu
1123 HackKeyPress(SDLK_ESCAPE, KMOD_CTRL | KMOD_SHIFT); // Reload map combination!
1125 if (result==-6)
1127 Pop(); // Remove old menu
1128 new Fader(1, 0, speed);
1130 if (result==-5 || result==-7)
1132 new Ending(result==-7);
1133 new Fader(1, 0, speed);
1139 void Ending::Cancel()
1141 new Fader(-1, -6, 0.3);
1142 // Pop();
1145 void ToggleFullscreen();
1147 void OptMenu::Select()
1149 if (select<0 || select>=num_opt)
1150 return;
1151 switch(opt[select])
1153 case OPT_RESUME:
1154 Cancel();
1155 break;
1157 case OPT_RESTART:
1158 Cancel();
1159 HackKeyPress('r', KMOD_CTRL);
1160 break;
1162 case OPT_GOTO_MAP:
1163 case OPT_GOTO_MAP_CONTINUE:
1164 Pop();
1165 HackKeyPress(SDLK_ESCAPE, KMOD_CTRL);
1166 break;
1168 case OPT_QUIT:
1169 new QuitConfirmMenu();
1170 break;
1172 case OPT_FULLSCREEN:
1173 ToggleFullscreen();
1174 break;
1176 case OPT_QUIT_CONFIRM:
1177 new Fader(-1, -2);
1178 break;
1180 case OPT_QUIT_MENU_CONFIRM:
1181 Pop();
1182 new Fader(-1, -1);
1183 break;
1185 case OPT_OPTIONS:
1186 new OptionMenu(!activeMenu->renderBG);
1187 break;
1189 case OPT_HELP:
1190 new HintReview();
1191 break;
1193 case OPT_QUIT_CANCEL:
1194 case OPT_BACK:
1195 Pop();
1196 break;
1198 case OPT_END:
1199 new Fader(-1, -5, 0.3);
1200 break;
1202 case OPT_END2:
1203 new Fader(-1, -7, 0.3);
1204 break;
1206 case OPT_UNDO:
1207 Pop();
1208 HackKeyPress('z', 0);
1209 break;
1211 default:
1212 if (opt[select]>=OPT_GAMESLOT_0 && opt[select]<=OPT_GAMESLOT_LAST
1213 || opt[select]==OPT_GAMESLOT_NEW && freeSlot>=0)
1215 if (opt[select]==OPT_GAMESLOT_NEW)
1216 GetSlotName(freeSlot, currentSlot);
1217 else
1218 GetSlotName(opt[select]-OPT_GAMESLOT_0, currentSlot);
1219 new Fader(-1, -4);
1221 break;