Reduce CPU usage
[hex-a-hop.git] / menus.h
blob837f08fab22b81e86e705edd0c86f87358fdfca2
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 "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.",
116 /*LIFT_DOWN*/
117 "The blue lifts go up or down when you land on them.",
118 /*LIFT_UP*/
121 0,0,0,0,
122 //Item 0 (21)
123 "The spiky anti-ice pickups turn icy tiles into blue ones. They get used automatically when you land on ice.",
124 //Item 1 (22)
125 "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.",
127 0,0,
129 // Map (25)
130 "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.",
132 // Scoring (26)
133 "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?",
135 0,0,0,
137 // End of help (30)
138 "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!",
140 // First help page (31)
141 "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.",
144 struct HintMessage : public Menu
146 static int flags;
148 const char * msg;
149 char title[50];
150 int numLines;
151 int state;
153 static bool FlagTile(int t, bool newStuff=true)
155 if (t==LIFT_UP) t=LIFT_DOWN;
156 if (newStuff && (flags & (1<<t))) return false;
157 if (!newStuff && !(flags & (1<<t))) return false;
158 if (t>31) return false;
159 if (!hint[t]) return false;
161 flags |= 1<<t;
162 new HintMessage(hint[t]);
163 return true;
166 HintMessage(const char * m) { Init(m); }
168 void Init(const char * m)
170 numLines = 1;
171 state = 0; time = 0;
172 memset(title, 0, sizeof(title));
173 char * x = strstr(m, "|");
174 if (!x)
176 msg = m;
177 strcpy(title, "Info:");
179 else
181 strncpy(title, m, x-m);
182 msg=x+1;
185 const char * s = m;
186 while ((s=strstr(s, " ")))
187 s+=2, numLines++;
190 virtual void Render()
192 int y = SCREEN_H/4 + int(SCREEN_H*MAX(1-time*5, 0)*3/4);
193 if (state)
194 y = SCREEN_H/4 + int(SCREEN_H*(-time*5)*3/4);
196 Render(0, y);
198 if (!state && time>0.2)
199 Print(SCREEN_W*3/4, SCREEN_H-FONT_SPACING, "Press any key");
202 void Render(int x, int y)
204 SDL_Rect r2 = {x+TILE_W1, y, SCREEN_W-TILE_W1*2, numLines*FONT_SPACING+FONT_SPACING};
205 SDL_Rect r = {r2.x-2, r2.y-2-FONT_SPACING, r2.w+4, r2.h+4+FONT_SPACING};
206 SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 60,90,90));
207 SDL_FillRect(screen, &r2, SDL_MapRGB(screen->format, 20,50,50));
208 Print(r.x+FONT_SPACING/4, y-FONT_SPACING, title);
209 PrintC(true, x+SCREEN_W/2, y+FONT_SPACING/2, msg);
212 virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons)
214 if (buttons_pressed && state==0 && time>0.2)
215 state = 1, time=0;
218 bool KeyPressed(int key, int mod)
220 if (state==0 && time>0.2)
221 state = 1, time=0;
222 return true;
225 virtual void Update(double timedelta)
227 Menu::Update(timedelta);
228 if(state && time > 0.2)
229 Pop();
233 int HintMessage::flags = 1<<31 | 1<<30;
235 struct HintReview : public HintMessage
237 int page;
238 int page_dir;
239 int page_count;
240 int page_display;
241 HintReview() : HintMessage(hint[31]), page(31), page_dir(0), page_display(0)
243 page_count=0;
244 for (int i=0; i<32; i++)
245 if (flags & (1<<i))
246 page_count++;
249 void Cancel() { Pop(); }
250 void Select() { Pop(); }
253 virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons)
255 if (buttons_pressed==1)
257 if (y < SCREEN_H/4-FONT_SPACING+2)
258 Move(-1);
259 else if (y > SCREEN_H/4+FONT_SPACING*numLines+FONT_SPACING)
260 Move(1);
261 else
262 Cancel();
264 else
265 Menu::Mouse(x,y,dx,dy,buttons_pressed, buttons_released, buttons);
268 bool KeyPressed(int key, int mod)
270 if (key==SDLK_LEFT)
271 Move(-1);
272 else if (key==SDLK_RIGHT)
273 Move(1);
274 else
275 return Menu::KeyPressed(key, mod);
276 return true;
279 virtual void Render()
281 const double SPD = 10;
283 #ifdef EDIT
284 sprintf (title, "Help (Page --)", page_display+1, page_count);
285 #else
286 sprintf (title, "Help (Page %d/%d)", page_display+1, page_count);
287 #endif
289 int y=SCREEN_H/4;
290 if (state==0)
291 y = SCREEN_H/4+int(MAX(0, time*SPD)*-page_dir*SCREEN_H);
292 if (state==1)
293 y = SCREEN_H/4+int(MAX(0, 1-time*SPD)*page_dir*SCREEN_H);
295 //if (!noMouse)
297 PrintC(false, SCREEN_W/2, y-FONT_SPACING*2, "^");
298 PrintC(false, SCREEN_W/2, y+FONT_SPACING*(numLines+1)+FONT_SPACING/2-2, "_");
301 HintMessage::Render(0,y);
303 if (time > 1.0/SPD && page_dir && state==0)
305 do {
306 page = (page+page_dir) & 31;
307 #ifdef EDIT
308 if (hint[page]) break;
309 #endif
310 } while (hint[page]==0 || !(flags&(1<<page)));
311 Init(hint[page]);
313 page_display = (page_display + page_count + page_dir) % page_count;
315 time = 0;
316 state=1;
318 if (time>1.0/SPD && state==1)
319 state=0, page_dir = 0;
321 virtual void Update(double timedelta)
323 Menu::Update(timedelta);
325 void Move(int dir)
327 if (page_dir)
328 return;
329 time = 0;
330 page_dir = dir;
331 state = 0;
335 #define MAX_GAMESLOT 4
336 enum option {
337 OPT_GAMESLOT_0,
338 OPT_GAMESLOT_LAST = OPT_GAMESLOT_0 + MAX_GAMESLOT - 1,
339 OPT_RESUME,
340 OPT_RESTART,
341 OPT_GOTO_MAP,
342 OPT_GOTO_MAP_CONTINUE,
343 OPT_FULLSCREEN,
344 OPT_OPTIONS,
345 OPT_QUIT,
346 OPT_QUIT_CONFIRM,
347 OPT_QUIT_CANCEL,
348 OPT_QUIT_MENU_CONFIRM,
349 OPT_HELP,
350 OPT_GAMESLOT_NEW,
351 OPT_DELETE_CONFIRM,
352 OPT_DELETE_CANCEL,
353 OPT_UNDO,
354 OPT_BACK,
355 OPT_END, OPT_END2,
358 char optionSlotName[MAX_GAMESLOT][40] = {0};
359 char currentSlot[80] = "";
360 int freeSlot = -1;
361 char* GetSlotName(int i, char * t)
363 sprintf(t, "save%d.dat", i+1);
364 return t;
367 char * optionString[] = {
368 optionSlotName[0],
369 optionSlotName[1],
370 optionSlotName[2],
371 optionSlotName[3],
372 "Resume",
373 "Restart Level",
374 "Return to Map",
375 "Continue",
376 "Toggle Fullscreen",
377 "Options",
378 "Quit",
379 "Yes",
380 "No",
381 "Return to Title",
382 "Help",
383 "Start New Game",
384 "Yes, really delete it!",
385 "Don't do it!",
386 "Undo Last Move",
387 "OK",
388 "View Credits Sequence", "View Credits Sequence",
392 struct OptMenu : public Menu
394 int select;
395 int num_opt;
396 int opt[10];
397 bool left_align;
398 const char * title;
399 SDL_Rect r, r2;
401 OptMenu(const char * t) : select(0), title(t)
403 left_align = false;
404 num_opt = 0;
407 void Init()
409 r.w=SCREEN_W/2;
410 r.x=(SCREEN_W-r.w)/2;
411 r.y=SCREEN_H/3;
413 r2 = r;
415 const int SPACE = int(FONT_SPACING * 1.5);
417 r2.h = SPACE*num_opt + FONT_SPACING/2;
418 r.h = r2.h + (FONT_SPACING+2*2);
419 r.y -= FONT_SPACING+2;
420 r.w += 2*2;
421 r.x -= 2;
424 void RenderOption(int o, const char * s)
426 int y = r2.y + FONT_SPACING/2 + int(FONT_SPACING * 1.5) * o;
427 if (left_align)
429 int x = r.x + font[' '].w;
430 int x1 = x + (font[' '].w + font['>'].w + FONT_X_SPACING*2) / 2;
431 if (select==o)
433 //x += int( sin(time*9)*2.5 );
434 //y += int( sin(time*9 + 1.5)*1.5 );
435 Print(x, y, "> %s", s);
437 else
439 Print(x1, y, "%s", s);
442 else
444 int x = r.x + r.w/2;
445 if (select==o)
447 //x += int( sin(time*9)*2.5 );
448 //y += int( sin(time*9 + 1.5)*1.5 );
449 PrintC(false, x, y, "> %s <", s);
451 else
453 PrintC(false, x, y, "%s", s);
458 void Move(int dir)
460 select += dir;
461 if (select<0) select = num_opt-1;
462 if (select>=num_opt) select = 0;
464 virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons)
466 if (1)
468 if (x<r2.x || y<r2.y || x>r2.x+r2.w || y>r2.y+r2.h)
470 if (buttons_pressed!=4 && buttons_pressed!=2)
471 return;
473 else
475 select = (y-r2.y-FONT_SPACING/3) / int(FONT_SPACING*1.5);
476 if (select<0) select = 0;
477 if (select>=num_opt) select = num_opt-1;
480 if (buttons_pressed==1)
481 Select();
482 Menu::Mouse(x, y, dx, dy, buttons_pressed, buttons_released, buttons);
484 void Select();
486 void Render()
488 RenderBG();
489 RenderTitle();
490 RenderOptions();
492 void RenderBG()
494 SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 60,90,90));
495 SDL_FillRect(screen, &r2, SDL_MapRGB(screen->format, 20,50,50));
497 void RenderTitle()
499 int y = r2.y + FONT_SPACING / 2;
500 if (left_align)
501 Print(r2.x+font[' '].w, r.y+4, title);
502 else
503 PrintC(false, r2.x+r2.w/2, r.y+4, title);
505 void RenderOptions()
507 for (int i=0; i<num_opt; i++)
508 RenderOption(i, optionString[opt[i]]);
510 void Cancel()
512 Pop();
516 struct WinLoseScreen : public OptMenu
518 bool win;
519 int score, par, best_score;
520 WinLoseScreen(bool _win, int _score=0, int _par=0, int _prev_score=0) :
521 score(_score),
522 par(_par),
523 best_score(_prev_score),
524 win(_win),
525 OptMenu(_win ? "Level Complete!" : "Emi can't swim...")
527 if (!win)
528 opt[num_opt++] = OPT_UNDO;
529 if (!win)
530 opt[num_opt++] = OPT_RESTART;
531 opt[num_opt++] = win ? OPT_GOTO_MAP_CONTINUE : OPT_GOTO_MAP;
533 Init();
535 if (win)
537 r.h += FONT_SPACING * 3 + FONT_SPACING/2;
538 r2.h += FONT_SPACING * 3 + FONT_SPACING/2;
542 void Render()
544 OptMenu::RenderBG();
545 OptMenu::RenderTitle();
547 if (win)
548 r2.y += FONT_SPACING * 3 + FONT_SPACING/2;
550 OptMenu::RenderOptions();
552 if (win)
553 r2.y -= FONT_SPACING * 3 + FONT_SPACING/2;
555 if (win)
557 int x = r.x+r.w/2;
558 int y = r2.y + FONT_SPACING/2;
559 if (score < best_score && score <= par)
560 PrintC(true, x, y, "New Best Score: %d Par Score: %d Par Beaten!", score, par);
561 else if (score < best_score)
562 PrintC(true, x, y, "New Best Score: %d Par Score: %d", score, par);
563 else if (par && best_score)
564 PrintC(true, x, y, "Score: %d Previous Best: %d Par Score: %d", score, best_score, par);
565 else
566 PrintC(true, x, y+FONT_SPACING/2, "Well Done! Level Completed!");
570 static void Undo()
572 Pop();
573 HackKeyPress('z', 0);
575 bool KeyPressed(int key, int mod)
577 if (key=='z' || key=='u' || key==SDLK_DELETE || key==SDLK_BACKSPACE)
578 return Undo(), true;
579 if (key=='r' && (mod & KMOD_CTRL))
581 Pop();
582 HackKeyPress(key, mod);
583 return true;
585 return OptMenu::KeyPressed(key, mod);
587 virtual void Mouse(int x, int y, int dx, int dy, int buttons_pressed, int buttons_released, int buttons)
589 if (buttons_pressed==4)
591 Undo();
592 return;
595 OptMenu::Mouse(x, y, dx, dy, buttons_pressed, buttons_released, buttons);
597 void Cancel()
599 if (win)
600 select=0, Select();
601 else
602 Undo();
606 struct OptMenuTitle : public OptMenu
608 OptMenuTitle(const char * t) : OptMenu(t)
610 renderBG = false;
611 //left_align = true;
614 void Render()
616 SDL_Rect a = {0,0,SCREEN_W,SCREEN_H};
617 // SDL_FillRect(screen, &a, SDL_MapRGB(screen->format, 10,25,25));
620 SDL_BlitSurface(titlePage, &a, screen, &a);
622 OptMenu::RenderTitle();
623 OptMenu::RenderOptions();
626 void Init()
628 OptMenu::Init();
630 int xw = SCREEN_W/6;
631 r.w += xw; r2.w+=xw;
632 int x = SCREEN_W - r.x - r.w;// - FONT_SPACING;
633 int y = SCREEN_H - r.y - r.h + FONT_SPACING/4;// - FONT_SPACING;
634 r.x += x; r2.x += x;
635 r.y += y; r2.y += y;
636 r.w+=20; r2.w+=20; r.h+=20; r2.h+=20;
638 r.h = r2.h = SCREEN_H;
639 r2.y = SCREEN_H/2;
640 r.y = r2.y - FONT_SPACING - 2;
644 const char *ending[] = {
645 " Very Well Done! ",
646 "", "", "", "", "", "",
648 "", "", "*15,4", "", "",
650 "All Levels Cleared!",
652 "", "", "*5,7", "", "",
654 "Not a single green hexagon is left unbroken.",
655 "",
656 "Truly, you are a master of hexagon hopping!",
658 "", "", "*9,10", "", "",
660 "", "Credits", "", "", "",
661 "<Design & Direction:", ">Tom Beaumont", "", "",
662 "<Programming:", ">Tom Beaumont", "", "",
663 "<Graphics:", ">Tom Beaumont", "", "",
664 "<Thanks to:", ">Kris Beaumont", "", "",
665 // "", "<Some useless facts...", "",
666 "<Tools and libraries used:", "", ">Photoshop LE", ">Inno Setup", ">Wings 3D", ">MSVC", ">SDL", "",
667 "<Fonts used:", "", ">Copperplate gothic bold", ">Verdana", "",
669 "", "", "*12,14", "", "",
671 "Thanks for playing!"
674 const char *ending2[] = {
675 " Absolutely Amazing! ",
676 "", "", "", "", "",
678 "", "", "*15,4", "", "",
680 "All Levels Mastered!!",
682 "", "", "*5,7", "", "",
684 "You crushed every last green hexagon with",
685 "breathtaking efficiency!",
687 "You truly are a grand master of hexagon hopping!",
690 const int endingLen = sizeof(ending)/sizeof(ending[0]);
691 const int endingLen2 = sizeof(ending2)/sizeof(ending2[0]);
692 const int scrollMax = SCREEN_H + (endingLen+1) * FONT_SPACING;
694 struct Ending : public Menu
696 struct Particle{
697 double x,y,xs,ys,xa,ya;
698 double time;
699 int type;
701 void Update(double td)
703 if (type==EMPTY) return;
705 time -= td;
706 x += xs*td;
707 y += ys*td;
708 xs += xa*td;
709 ys += ya*td;
710 if (type==TILE_LASER_HEAD && time<0.3)
711 type=TILE_FIRE_PARTICLE_1;
712 if (type==TILE_FIRE_PARTICLE_1 && time<0.1)
713 type=TILE_FIRE_PARTICLE_2;
715 // if (type==COLLAPSABLE || type==COLLAPSE_DOOR)
716 // for (int i=int((time)*40); i<int((time+td)*40); i++)
717 // new Particle(TILE_GREEN_FRAGMENT_1, x+32-rand()%63, y+20-rand()%40);
719 if (y>SCREEN_H && type==TILE_GREEN_FRAGMENT)
720 xa=0, ys=-ys/2, y=SCREEN_H, type=TILE_GREEN_FRAGMENT_1, time+=rand()%100*0.01;
721 if (y>SCREEN_H && type==TILE_GREEN_FRAGMENT_1)
722 xa=0, ys=-ys/2, y=SCREEN_H, type=TILE_GREEN_FRAGMENT_2, time+=rand()%100*0.01;
723 if (y>SCREEN_H && type==TILE_GREEN_FRAGMENT_2)
724 type = EMPTY;
726 if (time<=0)
728 if (type < 10)
730 for (int i=0; i<40; i++)
731 new Particle(TILE_LASER_HEAD, x, y);
732 for (int i=0; i<40; i++)
733 new Particle(TILE_GREEN_FRAGMENT + rand() % 3, x+32-rand()%63, y+20-rand()%40);
735 if (type==COLLAPSE_DOOR)
737 type = COLLAPSABLE;
738 time += 1;
740 else
742 type = EMPTY;
746 if (type==EMPTY) return;
748 if (type<0.05 && type<15)
749 RenderTile(false, tileSolid[type] ? TILE_WHITE_WALL : TILE_WHITE_TILE, int(x)+scrollX, int(y)+scrollY);
750 else
751 RenderTile(false, type, int(x)+scrollX, int(y)+scrollY);
754 Particle() : type(EMPTY) {}
755 Particle(int t, int _x) : type(t), x(_x)
757 xa=ys=xs=0; ya=400;
758 //if (t==1)
760 xs = rand()%100-50;
761 ys=-400-rand()%200;
762 //x=rand() % SCREEN_W;
763 //y=rand() % SCREEN_H;
764 y = SCREEN_H+20;
765 time = ys/-ya;
768 Particle(int t, double _x, double _y) : type(t)
770 x=_x; y=_y;
771 xa=0; ya=-100;
772 double r = (rand() % 2000) * PI / 1000;
773 double d = rand() % 50 + 250;
775 xs = sin(r)*d;
776 ys = cos(r)*d;
777 time = 1 + (rand() & 255) /255.0;
778 if (t==TILE_WATER_PARTICLE || t==TILE_GREEN_FRAGMENT || t==TILE_GREEN_FRAGMENT_1)
779 time = 2;
780 xa=-xs/time; ya=-ys/time;
781 if (t==TILE_WATER_PARTICLE || t==TILE_GREEN_FRAGMENT || t==TILE_GREEN_FRAGMENT_1)
782 ya += 500, ya/=2, xa/=2, xs/=2, ys/=2;
784 ~Particle() { type = EMPTY; }
785 void* operator new(size_t sz);
788 Particle p[1000];
790 double scroll;
791 double t;
792 bool goodEnding;
793 static Ending* ending;
795 Ending(bool _goodEnd) : goodEnding(_goodEnd)
797 memset(p, 0, sizeof(p));
798 ending = this;
799 renderBG = false;
800 scroll = 0;
801 t=0;
804 void Render()
806 SDL_Rect a = {0,0,SCREEN_W,SCREEN_H};
807 SDL_FillRect(screen, &a, SDL_MapRGB(screen->format, 10,25,25));
809 for (int i=0; i<sizeof(p)/sizeof(p[0]); i++)
810 p[i].Update(t);
812 int x = a.x + a.w/2;
813 int xl = SCREEN_W/5;
814 int xr = SCREEN_W*4/5;
815 int y = SCREEN_H - int(scroll);
816 for (int i=0; i<endingLen; i++)
818 if (y>-FONT_SPACING*2 && y<SCREEN_H+FONT_SPACING)
820 const char * xx = (i<endingLen2 && goodEnding) ? ending2[i] : ::ending[i];
821 if (xx[0]=='<')
822 Print(xl, y+FONT_SPACING, xx+1);
823 else if (xx[0]=='>')
824 PrintR(xr, y, xx+1);
825 else if (xx[0]=='*')
827 RenderTile(false, atoi(xx+1), (xl+x)/2+scrollX, y+FONT_SPACING/2+scrollY);
828 RenderTile(false, atoi(strchr(xx, ',')+1), (xr+x)/2+scrollX, y+FONT_SPACING/2+scrollY);
830 else
831 PrintC(false, x, y, xx);
833 y+=FONT_SPACING;
835 if (scroll > scrollMax + FONT_SPACING*10)
836 PrintC(true, x, SCREEN_H/2-FONT_SPACING/2, "The End");
839 void Cancel();
841 bool KeyPressed(int key, int mod)
843 if (key=='r' && (mod & KMOD_CTRL))
845 time = 0;
846 memset(p, 0, sizeof(p));
848 else
849 return Menu::KeyPressed(key, mod);
850 return true;
853 void Update(double td)
855 noMouse = 1;
857 double old = time;
859 t = td;
860 if (keyState[SDLK_LSHIFT])
861 t = td*5;
862 if (keyState['0'])
863 t = MAX(-td*5, -time);
865 Menu::Update(t);
866 double s = scroll = time * 50;
867 // if (scroll > scrollMax)
868 // scroll = fmod(scroll, scrollMax);
869 // scroll = 0, time = 0;
871 if (old>4 && time > 4)
873 if (scroll < scrollMax + FONT_SPACING*17)
875 for (int i=int( old/2.5); i<int(time/2.5); i++)
877 int xs = (rand()%SCREEN_W * 6 + 1) / 8;
878 for (int j=rand()%3+1; j; j--)
879 new Particle(rand()&1 ? COLLAPSABLE : COLLAPSE_DOOR, xs+j*64);
882 if (scroll > scrollMax + FONT_SPACING*27)
883 Cancel();
889 Ending* Ending::ending = 0;
890 void* Ending::Particle::operator new(size_t sz)
892 static int start = 0;
893 const int max = sizeof(ending->p)/sizeof(ending->p[0]);
894 for (int i=0; i<max; i++)
896 start++;
897 if (start==max) start=0;
898 if (ending->p[start].type==EMPTY)
899 return &ending->p[start];
901 return &ending->p[rand() % max];
904 struct TitleMenu : public OptMenuTitle
906 TitleMenu() : OptMenuTitle("")
908 //left_align = 1;
910 SaveState p;
911 freeSlot = -1;
912 for (int i=0; i<MAX_GAMESLOT; i++)
914 char tmp[80];
915 GetSlotName(i, tmp);
916 FILE* f = file_open(tmp, "rb");
917 if (f)
919 p.LoadSave(f, false);
920 fclose(f);
922 if (p.general.completionPercentage==100 && p.general.masteredPercentage==100)
923 sprintf(optionSlotName[i], "Continue game %d (All Clear!)", i+1, p.general.completionPercentage, p.general.masteredPercentage);
924 else if (p.general.completionPercentage==100)
925 sprintf(optionSlotName[i], "Continue game %d (%d%% + %d%%)", i+1, p.general.completionPercentage, p.general.masteredPercentage);
926 else
927 sprintf(optionSlotName[i], "Continue game %d (%d%% complete)", i+1, p.general.completionPercentage);
929 opt[num_opt++] = OPT_GAMESLOT_0 + i;
931 else
933 // sprintf(optionSlotName[i], "Start new game (slot %d)", i+1);
934 // opt[num_opt++] = OPT_GAMESLOT_0 + i;
936 if (freeSlot==-1)
937 freeSlot = i;
942 if (num_opt < MAX_GAMESLOT)
943 opt[num_opt++] = OPT_GAMESLOT_NEW;
945 opt[num_opt++] = OPT_OPTIONS;
946 #ifdef EDIT
947 opt[num_opt++] = OPT_END;
948 opt[num_opt++] = OPT_END2;
949 #endif
950 opt[num_opt++] = OPT_QUIT;
952 Init();
953 #ifdef EDIT
954 r.y-=FONT_SPACING*2;
955 r2.y-=FONT_SPACING*2;
956 #else
957 if (num_opt==3)
958 r.y+=FONT_SPACING+FONT_SPACING/2, r2.y+=FONT_SPACING+FONT_SPACING/2;
959 #endif
961 bool KeyPressed(int key, int mod);
964 struct QuitConfirmMenu : public OptMenuTitle
966 QuitConfirmMenu() : OptMenuTitle("Quit: Are you sure?")
968 opt[num_opt++] = OPT_QUIT_CONFIRM;
969 opt[select=num_opt++] = OPT_QUIT_CANCEL;
970 Init();
972 r.y += FONT_SPACING*1;
973 r2.y += FONT_SPACING*2;
978 struct DeleteConfirmMenu : public OptMenuTitle
980 char tmp[80];
981 int num;
982 DeleteConfirmMenu(int _num) : OptMenuTitle(&tmp[0]), num(_num)
984 //left_align = 1;
986 sprintf(tmp, "Really delete game %d?", num+1);
987 opt[num_opt++] = OPT_DELETE_CONFIRM;
988 opt[select=num_opt++] = OPT_DELETE_CANCEL;
989 Init();
991 r.y += FONT_SPACING*1;
992 r2.y += FONT_SPACING*2;
994 void Select()
996 if (select<0 || select>=num_opt)
997 return;
998 if (opt[select] == OPT_DELETE_CONFIRM)
1000 GetSlotName(num, tmp);
1001 remove(tmp);
1003 Pop();
1004 Pop();
1005 new TitleMenu();
1009 bool TitleMenu::KeyPressed(int key, int mod)
1011 if (key==SDLK_DELETE || key==SDLK_BACKSPACE || key==SDLK_F2)
1013 if (select<0 || select>=num_opt || opt[select]<OPT_GAMESLOT_0 || opt[select]>OPT_GAMESLOT_LAST)
1014 return true;
1015 int i = opt[select] - OPT_GAMESLOT_0;
1017 new DeleteConfirmMenu(i);
1019 return true;
1021 return OptMenu::KeyPressed(key, mod);
1024 struct PauseMenu : public OptMenu
1026 PauseMenu(bool isMap, bool allowGotoMap, int allowEnd, int allowEnd2) : OptMenu("Paused")
1028 opt[num_opt++] = OPT_RESUME;
1029 if (!isMap)
1030 opt[num_opt++] = OPT_RESTART;
1031 opt[num_opt++] = OPT_OPTIONS;
1032 opt[num_opt++] = OPT_HELP;
1033 if (allowEnd || allowEnd2)
1034 opt[num_opt++] = allowEnd2 ? OPT_END2 : OPT_END;
1035 opt[num_opt++] = (isMap || !allowGotoMap) ? OPT_QUIT_MENU_CONFIRM : OPT_GOTO_MAP;
1036 Init();
1038 virtual bool KeyPressed(int key, int mod)
1040 if (key=='p' || key==SDLK_PAUSE)
1042 Cancel();
1043 return true;
1045 return Menu::KeyPressed(key, mod);
1050 struct OptionMenu : public OptMenuTitle
1052 bool title;
1053 OptionMenu(bool _title) : OptMenuTitle("Options"), title(_title)
1055 opt[num_opt++] = OPT_FULLSCREEN;
1057 opt[num_opt++] = OPT_BACK;
1059 if (title)
1061 OptMenuTitle::Init(), renderBG=false;
1062 r.y += FONT_SPACING;
1063 r2.y += FONT_SPACING*2;
1065 else
1066 OptMenu::Init(), renderBG=true;
1068 void Render()
1070 if (title)
1071 OptMenuTitle::Render();
1072 else
1073 OptMenu::Render();
1077 void RenderFade(double time, int dir, int seed);
1079 struct Fader : public Menu
1081 int dir;
1082 double speed;
1083 int result;
1084 Fader(int _dir, int _result, double _speed=1) : dir(_dir), result(_result), speed(_speed)
1086 renderBG = under ? under->renderBG : true;
1088 void Render()
1090 if (under)
1091 under->Render();
1093 RenderFade(time, dir, (long)this);
1095 void Update(double timedelta)
1097 Menu::Update(timedelta * speed);
1098 if (result==-2)
1100 if (time > 0.7)
1101 quitting = 1;
1103 else if (time >= 0.5)
1105 Pop();
1106 if (result==-1)
1108 new TitleMenu();
1109 new Fader(1, -3);
1111 if (result==-4)
1113 Pop(); // Remove old menu
1114 HackKeyPress(SDLK_ESCAPE, KMOD_CTRL | KMOD_SHIFT); // Reload map combination!
1116 if (result==-6)
1118 Pop(); // Remove old menu
1119 new Fader(1, 0, speed);
1121 if (result==-5 || result==-7)
1123 new Ending(result==-7);
1124 new Fader(1, 0, speed);
1130 void Ending::Cancel()
1132 new Fader(-1, -6, 0.3);
1133 // Pop();
1136 void ToggleFullscreen();
1138 void OptMenu::Select()
1140 if (select<0 || select>=num_opt)
1141 return;
1142 switch(opt[select])
1144 case OPT_RESUME:
1145 Cancel();
1146 break;
1148 case OPT_RESTART:
1149 Cancel();
1150 HackKeyPress('r', KMOD_CTRL);
1151 break;
1153 case OPT_GOTO_MAP:
1154 case OPT_GOTO_MAP_CONTINUE:
1155 Pop();
1156 HackKeyPress(SDLK_ESCAPE, KMOD_CTRL);
1157 break;
1159 case OPT_QUIT:
1160 new QuitConfirmMenu();
1161 break;
1163 case OPT_FULLSCREEN:
1164 ToggleFullscreen();
1165 break;
1167 case OPT_QUIT_CONFIRM:
1168 new Fader(-1, -2);
1169 break;
1171 case OPT_QUIT_MENU_CONFIRM:
1172 Pop();
1173 new Fader(-1, -1);
1174 break;
1176 case OPT_OPTIONS:
1177 new OptionMenu(!activeMenu->renderBG);
1178 break;
1180 case OPT_HELP:
1181 new HintReview();
1182 break;
1184 case OPT_QUIT_CANCEL:
1185 case OPT_BACK:
1186 Pop();
1187 break;
1189 case OPT_END:
1190 new Fader(-1, -5, 0.3);
1191 break;
1193 case OPT_END2:
1194 new Fader(-1, -7, 0.3);
1195 break;
1197 case OPT_UNDO:
1198 Pop();
1199 HackKeyPress('z', 0);
1200 break;
1202 default:
1203 if (opt[select]>=OPT_GAMESLOT_0 && opt[select]<=OPT_GAMESLOT_LAST
1204 || opt[select]==OPT_GAMESLOT_NEW && freeSlot>=0)
1206 if (opt[select]==OPT_GAMESLOT_NEW)
1207 GetSlotName(freeSlot, currentSlot);
1208 else
1209 GetSlotName(opt[select]-OPT_GAMESLOT_0, currentSlot);
1210 new Fader(-1, -4);
1212 break;