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
24 static void HackKeyPress(int key
, int mod
)
26 int k
= keyState
[key
];
27 StateMakerBase::current
->KeyPressed(key
, mod
);
34 Menu() : renderBG(true), under(activeMenu
), time(0) { activeMenu
= this; }
37 if(this!=deadMenu
) FATAL("this!=deadMenu");
43 if (!activeMenu
) return;
45 activeMenu
= activeMenu
->under
;
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
)
54 else if (key
=='s' || key
==SDLK_DOWN
|| key
==SDLK_KP2
|| key
=='a' || key
=='d' || key
==SDLK_KP1
|| key
==SDLK_KP3
)
56 else if (key
==' ' || key
==SDLK_RETURN
)
61 else if (key
==SDLK_ESCAPE
|| key
==SDLK_BACKSPACE
|| key
==SDLK_KP_PERIOD
|| key
==SDLK_DELETE
)
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)
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;
84 const char * hint
[] = {
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.",
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!",
93 "The coloured walls flatten themselves when there are no matching coloured tiles remaining.",
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!",
97 "A red spinner tile will rotate the pieces around it when you step on it.",
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...",
105 "Yellow laser tiles fire when you step on them. Shooting other laser tiles is more destructive.",
107 "Ice is slippery! Please be careful!!",
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.",
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.",
117 "The blue lifts go up or down when you land on them.",
123 "The spiky anti-ice pickups turn icy tiles into blue ones. They get used automatically when you land on ice.",
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.",
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.",
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?",
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
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;
162 new HintMessage(hint
[t
]);
166 HintMessage(const char * m
) { Init(m
); }
168 void Init(const char * m
)
172 memset(title
, 0, sizeof(title
));
173 char * x
= strstr(m
, "|");
177 strcpy(title
, "Info:");
181 strncpy(title
, m
, x
-m
);
186 while ((s
=strstr(s
, " ")))
190 virtual void Render()
192 int y
= SCREEN_H
/4 + int(SCREEN_H
*MAX(1-time
*5, 0)*3/4);
194 y
= SCREEN_H
/4 + int(SCREEN_H
*(-time
*5)*3/4);
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)
218 bool KeyPressed(int /*key*/, int /*mod*/)
220 if (state
==0 && time
>0.2)
225 virtual void Update(double timedelta
)
227 Menu::Update(timedelta
);
228 if(state
&& time
> 0.2)
233 int HintMessage::flags
= 1<<31 | 1<<30;
235 struct HintReview
: public HintMessage
241 HintReview() : HintMessage(hint
[31]), page(31), page_dir(0), page_display(0)
244 for (int i
=0; i
<32; i
++)
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)
259 else if (y
> SCREEN_H
/4+FONT_SPACING
*numLines
+FONT_SPACING
)
264 else if (buttons_pressed
==8)
266 else if (buttons_pressed
==16)
269 Menu::Mouse(x
,y
,dx
,dy
,buttons_pressed
, buttons_released
, buttons
);
272 bool KeyPressed(int key
, int mod
)
276 else if (key
==SDLK_RIGHT
)
279 return Menu::KeyPressed(key
, mod
);
283 virtual void Render()
285 const double SPD
= 10;
288 sprintf (title
, "Help (Page --)", page_display
+1, page_count
);
290 sprintf (title
, "Help (Page %d/%d)", page_display
+1, page_count
);
295 y
= SCREEN_H
/4+int(MAX(0, time
*SPD
)*-page_dir
*SCREEN_H
);
297 y
= SCREEN_H
/4+int(MAX(0, 1-time
*SPD
)*page_dir
*SCREEN_H
);
301 PrintC(false, SCREEN_W
/2, y
-FONT_SPACING
*2, "^");
302 PrintC(false, SCREEN_W
/2, y
+FONT_SPACING
*(numLines
+1)+FONT_SPACING
/2-2, "_");
305 HintMessage::Render(0,y
);
307 if (time
> 1.0/SPD
&& page_dir
&& state
==0)
310 page
= (page
+page_dir
) & 31;
312 if (hint
[page
]) break;
314 } while (hint
[page
]==0 || !(flags
&(1<<page
)));
317 page_display
= (page_display
+ page_count
+ page_dir
) % page_count
;
322 if (time
>1.0/SPD
&& state
==1)
323 state
=0, page_dir
= 0;
325 virtual void Update(double timedelta
)
327 Menu::Update(timedelta
);
339 #define MAX_GAMESLOT 4
342 OPT_GAMESLOT_LAST
= OPT_GAMESLOT_0
+ MAX_GAMESLOT
- 1,
346 OPT_GOTO_MAP_CONTINUE
,
352 OPT_QUIT_MENU_CONFIRM
,
362 char optionSlotName
[MAX_GAMESLOT
][40] = { {0} };
363 char currentSlot
[80] = "";
365 char* GetSlotName(int i
, char * t
)
367 sprintf(t
, "save%d.dat", i
+1);
371 char * optionString
[] = {
388 "Yes, really delete it!",
392 "View Credits Sequence", "View Credits Sequence",
396 struct OptMenu
: public Menu
405 OptMenu(const char * t
) : select(0), title(t
)
414 r
.x
=(SCREEN_W
-r
.w
)/2;
419 const int SPACE
= int(FONT_SPACING
* 1.5);
421 r2
.h
= SPACE
*num_opt
+ FONT_SPACING
/2;
422 r
.h
= r2
.h
+ (FONT_SPACING
+2*2);
423 r
.y
-= FONT_SPACING
+2;
428 void RenderOption(int o
, const char * s
)
430 int y
= r2
.y
+ FONT_SPACING
/2 + int(FONT_SPACING
* 1.5) * o
;
433 int x
= r
.x
+ font
[' '].w
;
434 int x1
= x
+ (font
[' '].w
+ font
['>'].w
+ FONT_X_SPACING
*2) / 2;
437 //x += int( sin(time*9)*2.5 );
438 //y += int( sin(time*9 + 1.5)*1.5 );
439 Print(x
, y
, "> %s", s
);
443 Print(x1
, y
, "%s", s
);
451 //x += int( sin(time*9)*2.5 );
452 //y += int( sin(time*9 + 1.5)*1.5 );
453 PrintC(false, x
, y
, "> %s <", s
);
457 PrintC(false, x
, y
, "%s", s
);
465 if (select
<0) select
= num_opt
-1;
466 if (select
>=num_opt
) select
= 0;
468 virtual void Mouse(int x
, int y
, int dx
, int dy
, int buttons_pressed
, int buttons_released
, int buttons
)
472 if (x
<r2
.x
|| y
<r2
.y
|| x
>r2
.x
+r2
.w
|| y
>r2
.y
+r2
.h
)
474 if (buttons_pressed
!=4 && buttons_pressed
!=2)
479 select
= (y
-r2
.y
-FONT_SPACING
/3) / int(FONT_SPACING
*1.5);
480 if (select
<0) select
= 0;
481 if (select
>=num_opt
) select
= num_opt
-1;
484 if (buttons_pressed
==1)
486 Menu::Mouse(x
, y
, dx
, dy
, buttons_pressed
, buttons_released
, buttons
);
498 SDL_FillRect(screen
, &r
, SDL_MapRGB(screen
->format
, 60,90,90));
499 SDL_FillRect(screen
, &r2
, SDL_MapRGB(screen
->format
, 20,50,50));
504 Print(r2
.x
+font
[' '].w
, r
.y
+4, title
);
506 PrintC(false, r2
.x
+r2
.w
/2, r
.y
+4, title
);
510 for (int i
=0; i
<num_opt
; i
++)
511 RenderOption(i
, optionString
[opt
[i
]]);
519 struct WinLoseScreen
: public OptMenu
522 int score
, par
, best_score
;
523 WinLoseScreen(bool _win
, int _score
=0, int _par
=0, int _prev_score
=0) :
524 OptMenu(_win
? _("Level Complete!") : _("Emi can't swim...")),
528 best_score(_prev_score
)
531 opt
[num_opt
++] = OPT_UNDO
;
533 opt
[num_opt
++] = OPT_RESTART
;
534 opt
[num_opt
++] = win
? OPT_GOTO_MAP_CONTINUE
: OPT_GOTO_MAP
;
540 r
.h
+= FONT_SPACING
* 3 + FONT_SPACING
/2;
541 r2
.h
+= FONT_SPACING
* 3 + FONT_SPACING
/2;
548 OptMenu::RenderTitle();
551 r2
.y
+= FONT_SPACING
* 3 + FONT_SPACING
/2;
553 OptMenu::RenderOptions();
556 r2
.y
-= FONT_SPACING
* 3 + FONT_SPACING
/2;
561 int y
= r2
.y
+ FONT_SPACING
/2;
562 if (score
< best_score
&& score
<= par
)
563 PrintC(true, x
, y
, "New Best Score: %d Par Score: %d Par Beaten!", score
, par
);
564 else if (score
< best_score
)
565 PrintC(true, x
, y
, "New Best Score: %d Par Score: %d", score
, par
);
566 else if (par
&& best_score
)
567 PrintC(true, x
, y
, "Score: %d Previous Best: %d Par Score: %d", score
, best_score
, par
);
569 PrintC(true, x
, y
+FONT_SPACING
/2, "Well Done! Level Completed!");
576 HackKeyPress('z', 0);
578 bool KeyPressed(int key
, int mod
)
580 if (key
=='z' || key
=='u' || key
==SDLK_DELETE
|| key
==SDLK_BACKSPACE
)
582 if (key
=='r' && (mod
& KMOD_CTRL
))
585 HackKeyPress(key
, mod
);
588 return OptMenu::KeyPressed(key
, mod
);
590 virtual void Mouse(int x
, int y
, int dx
, int dy
, int buttons_pressed
, int buttons_released
, int buttons
)
592 if (buttons_pressed
==4)
598 OptMenu::Mouse(x
, y
, dx
, dy
, buttons_pressed
, buttons_released
, buttons
);
609 struct OptMenuTitle
: public OptMenu
611 OptMenuTitle(const char * t
) : OptMenu(t
)
619 SDL_Rect a
= {0,0,SCREEN_W
,SCREEN_H
};
620 // SDL_FillRect(screen, &a, SDL_MapRGB(screen->format, 10,25,25));
623 SDL_BlitSurface(titlePage
, &a
, screen
, &a
);
625 OptMenu::RenderTitle();
626 OptMenu::RenderOptions();
635 int x
= SCREEN_W
- r
.x
- r
.w
;// - FONT_SPACING;
636 int y
= SCREEN_H
- r
.y
- r
.h
+ FONT_SPACING
/4;// - FONT_SPACING;
639 r
.w
+=20; r2
.w
+=20; r
.h
+=20; r2
.h
+=20;
641 r
.h
= r2
.h
= SCREEN_H
;
643 r
.y
= r2
.y
- FONT_SPACING
- 2;
647 const char *ending
[] = {
649 "", "", "", "", "", "",
651 "", "", "*15,4", "", "",
653 "All Levels Cleared!",
655 "", "", "*5,7", "", "",
657 "Not a single green hexagon is left unbroken.",
659 "Truly, you are a master of hexagon hopping!",
661 "", "", "*9,10", "", "",
663 "", "Credits", "", "", "",
664 "<Design & Direction:", ">Tom Beaumont", "", "",
665 "<Programming:", ">Tom Beaumont", "", "",
666 "<Graphics:", ">Tom Beaumont", "", "",
667 "<Thanks to:", ">Kris Beaumont", "", "",
668 // "", "<Some useless facts...", "",
669 "<Tools and libraries used:", "", ">Photoshop LE", ">Inno Setup", ">Wings 3D", ">MSVC", ">SDL", "",
670 "<Fonts used:", "", ">Copperplate gothic bold", ">Verdana", "",
672 "", "", "*12,14", "", "",
674 "Thanks for playing!"
677 const char *ending2
[] = {
678 " Absolutely Amazing! ",
681 "", "", "*15,4", "", "",
683 "All Levels Mastered!!",
685 "", "", "*5,7", "", "",
687 "You crushed every last green hexagon with",
688 "breathtaking efficiency!",
690 "You truly are a grand master of hexagon hopping!",
693 const int endingLen
= sizeof(ending
)/sizeof(ending
[0]);
694 const int endingLen2
= sizeof(ending2
)/sizeof(ending2
[0]);
695 const int scrollMax
= SCREEN_H
+ (endingLen
+1) * FONT_SPACING
;
697 struct Ending
: public Menu
700 double x
,y
,xs
,ys
,xa
,ya
;
704 void Update(double td
)
706 if (type
==EMPTY
) return;
713 if (type
==TILE_LASER_HEAD
&& time
<0.3)
714 type
=TILE_FIRE_PARTICLE_1
;
715 if (type
==TILE_FIRE_PARTICLE_1
&& time
<0.1)
716 type
=TILE_FIRE_PARTICLE_2
;
718 // if (type==COLLAPSABLE || type==COLLAPSE_DOOR)
719 // for (int i=int((time)*40); i<int((time+td)*40); i++)
720 // new Particle(TILE_GREEN_FRAGMENT_1, x+32-rand()%63, y+20-rand()%40);
722 if (y
>SCREEN_H
&& type
==TILE_GREEN_FRAGMENT
)
723 xa
=0, ys
=-ys
/2, y
=SCREEN_H
, type
=TILE_GREEN_FRAGMENT_1
, time
+=rand()%100*0.01;
724 if (y
>SCREEN_H
&& type
==TILE_GREEN_FRAGMENT_1
)
725 xa
=0, ys
=-ys
/2, y
=SCREEN_H
, type
=TILE_GREEN_FRAGMENT_2
, time
+=rand()%100*0.01;
726 if (y
>SCREEN_H
&& type
==TILE_GREEN_FRAGMENT_2
)
733 for (int i
=0; i
<40; i
++)
734 new Particle(TILE_LASER_HEAD
, x
, y
);
735 for (int i
=0; i
<40; i
++)
736 new Particle(TILE_GREEN_FRAGMENT
+ rand() % 3, x
+32-rand()%63, y
+20-rand()%40);
738 if (type
==COLLAPSE_DOOR
)
749 if (type
==EMPTY
) return;
751 if (type
<0.05 && type
<15)
752 RenderTile(false, tileSolid
[type
] ? TILE_WHITE_WALL
: TILE_WHITE_TILE
, int(x
)+scrollX
, int(y
)+scrollY
);
754 RenderTile(false, type
, int(x
)+scrollX
, int(y
)+scrollY
);
757 Particle() : type(EMPTY
) {}
758 Particle(int t
, int _x
) : x(_x
), type(t
)
765 //x=rand() % SCREEN_W;
766 //y=rand() % SCREEN_H;
771 Particle(int t
, double _x
, double _y
) : type(t
)
775 double r
= (rand() % 2000) * PI
/ 1000;
776 double d
= rand() % 50 + 250;
780 time
= 1 + (rand() & 255) /255.0;
781 if (t
==TILE_WATER_PARTICLE
|| t
==TILE_GREEN_FRAGMENT
|| t
==TILE_GREEN_FRAGMENT_1
)
783 xa
=-xs
/time
; ya
=-ys
/time
;
784 if (t
==TILE_WATER_PARTICLE
|| t
==TILE_GREEN_FRAGMENT
|| t
==TILE_GREEN_FRAGMENT_1
)
785 ya
+= 500, ya
/=2, xa
/=2, xs
/=2, ys
/=2;
787 ~Particle() { type
= EMPTY
; }
788 void* operator new(size_t sz
);
796 static Ending
* ending
;
798 Ending(bool _goodEnd
) : goodEnding(_goodEnd
)
800 memset(p
, 0, sizeof(p
));
809 SDL_Rect a
= {0,0,SCREEN_W
,SCREEN_H
};
810 SDL_FillRect(screen
, &a
, SDL_MapRGB(screen
->format
, 10,25,25));
812 for (unsigned int i
=0; i
<sizeof(p
)/sizeof(p
[0]); i
++)
817 int xr
= SCREEN_W
*4/5;
818 int y
= SCREEN_H
- int(scroll
);
819 for (int i
=0; i
<endingLen
; i
++)
821 if (y
>-FONT_SPACING
*2 && y
<SCREEN_H
+FONT_SPACING
)
823 const char * xx
= (i
<endingLen2
&& goodEnding
) ? ending2
[i
] : ::ending
[i
];
825 Print(xl
, y
+FONT_SPACING
, xx
+1);
830 RenderTile(false, atoi(xx
+1), (xl
+x
)/2+scrollX
, y
+FONT_SPACING
/2+scrollY
);
831 RenderTile(false, atoi(strchr(xx
, ',')+1), (xr
+x
)/2+scrollX
, y
+FONT_SPACING
/2+scrollY
);
834 PrintC(false, x
, y
, xx
);
838 if (scroll
> scrollMax
+ FONT_SPACING
*10)
839 PrintC(true, x
, SCREEN_H
/2-FONT_SPACING
/2, "The End");
844 bool KeyPressed(int key
, int mod
)
846 if (key
=='r' && (mod
& KMOD_CTRL
))
849 memset(p
, 0, sizeof(p
));
852 return Menu::KeyPressed(key
, mod
);
856 void Update(double td
)
863 if (keyState
[SDLK_LSHIFT
])
865 if (keyState
[SDLK_0
])
866 t
= MAX(-td
*5, -time
);
870 // if (scroll > scrollMax)
871 // scroll = fmod(scroll, scrollMax);
872 // scroll = 0, time = 0;
874 if (old
>4 && time
> 4)
876 if (scroll
< scrollMax
+ FONT_SPACING
*17)
878 for (int i
=int( old
/2.5); i
<int(time
/2.5); i
++)
880 int xs
= (rand()%SCREEN_W
* 6 + 1) / 8;
881 for (int j
=rand()%3+1; j
; j
--)
882 new Particle(rand()&1 ? COLLAPSABLE
: COLLAPSE_DOOR
, xs
+j
*64);
885 if (scroll
> scrollMax
+ FONT_SPACING
*27)
892 Ending
* Ending::ending
= 0;
893 void* Ending::Particle::operator new(size_t /*sz*/)
895 static int start
= 0;
896 const int max
= sizeof(ending
->p
)/sizeof(ending
->p
[0]);
897 for (int i
=0; i
<max
; i
++)
900 if (start
==max
) start
=0;
901 if (ending
->p
[start
].type
==EMPTY
)
902 return &ending
->p
[start
];
904 return &ending
->p
[rand() % max
];
907 struct TitleMenu
: public OptMenuTitle
909 TitleMenu() : OptMenuTitle("")
915 for (int i
=0; i
<MAX_GAMESLOT
; i
++)
919 FILE* f
= file_open(tmp
, "rb");
922 p
.LoadSave(f
, false);
925 if (p
.general
.completionPercentage
==100 && p
.general
.masteredPercentage
==100)
926 sprintf(optionSlotName
[i
], _("Continue game %d (All Clear!)"), i
+1);
927 else if (p
.general
.completionPercentage
==100)
928 sprintf(optionSlotName
[i
], "Continue game %d (%d%% + %d%%)", i
+1, p
.general
.completionPercentage
, p
.general
.masteredPercentage
);
930 sprintf(optionSlotName
[i
], "Continue game %d (%d%% complete)", i
+1, p
.general
.completionPercentage
);
932 opt
[num_opt
++] = OPT_GAMESLOT_0
+ i
;
936 // sprintf(optionSlotName[i], "Start new game (slot %d)", i+1);
937 // opt[num_opt++] = OPT_GAMESLOT_0 + i;
945 if (num_opt
< MAX_GAMESLOT
)
946 opt
[num_opt
++] = OPT_GAMESLOT_NEW
;
948 opt
[num_opt
++] = OPT_OPTIONS
;
950 opt
[num_opt
++] = OPT_END
;
951 opt
[num_opt
++] = OPT_END2
;
953 opt
[num_opt
++] = OPT_QUIT
;
958 r2
.y
-=FONT_SPACING
*2;
961 r
.y
+=FONT_SPACING
+FONT_SPACING
/2, r2
.y
+=FONT_SPACING
+FONT_SPACING
/2;
964 bool KeyPressed(int key
, int mod
);
967 struct QuitConfirmMenu
: public OptMenuTitle
969 QuitConfirmMenu() : OptMenuTitle("Quit: Are you sure?")
971 opt
[num_opt
++] = OPT_QUIT_CONFIRM
;
972 opt
[select
=num_opt
++] = OPT_QUIT_CANCEL
;
975 r
.y
+= FONT_SPACING
*1;
976 r2
.y
+= FONT_SPACING
*2;
981 struct DeleteConfirmMenu
: public OptMenuTitle
985 DeleteConfirmMenu(int _num
) : OptMenuTitle(&tmp
[0]), num(_num
)
989 sprintf(tmp
, "Really delete game %d?", num
+1);
990 opt
[num_opt
++] = OPT_DELETE_CONFIRM
;
991 opt
[select
=num_opt
++] = OPT_DELETE_CANCEL
;
994 r
.y
+= FONT_SPACING
*1;
995 r2
.y
+= FONT_SPACING
*2;
999 if (select
<0 || select
>=num_opt
)
1001 if (opt
[select
] == OPT_DELETE_CONFIRM
)
1003 GetSlotName(num
, tmp
);
1012 bool TitleMenu::KeyPressed(int key
, int mod
)
1014 if (key
==SDLK_DELETE
|| key
==SDLK_BACKSPACE
|| key
==SDLK_F2
)
1016 if (select
<0 || select
>=num_opt
|| opt
[select
]<OPT_GAMESLOT_0
|| opt
[select
]>OPT_GAMESLOT_LAST
)
1018 int i
= opt
[select
] - OPT_GAMESLOT_0
;
1020 new DeleteConfirmMenu(i
);
1024 return OptMenu::KeyPressed(key
, mod
);
1027 struct PauseMenu
: public OptMenu
1029 PauseMenu(bool isMap
, bool allowGotoMap
, int allowEnd
, int allowEnd2
) : OptMenu("Paused")
1031 opt
[num_opt
++] = OPT_RESUME
;
1033 opt
[num_opt
++] = OPT_RESTART
;
1034 opt
[num_opt
++] = OPT_OPTIONS
;
1035 opt
[num_opt
++] = OPT_HELP
;
1036 if (allowEnd
|| allowEnd2
)
1037 opt
[num_opt
++] = allowEnd2
? OPT_END2
: OPT_END
;
1038 opt
[num_opt
++] = (isMap
|| !allowGotoMap
) ? OPT_QUIT_MENU_CONFIRM
: OPT_GOTO_MAP
;
1041 virtual bool KeyPressed(int key
, int mod
)
1043 if (key
=='p' || key
==SDLK_PAUSE
)
1048 return Menu::KeyPressed(key
, mod
);
1053 struct OptionMenu
: public OptMenuTitle
1056 OptionMenu(bool _title
) : OptMenuTitle("Options"), title(_title
)
1058 opt
[num_opt
++] = OPT_FULLSCREEN
;
1060 opt
[num_opt
++] = OPT_BACK
;
1064 OptMenuTitle::Init(), renderBG
=false;
1065 r
.y
+= FONT_SPACING
;
1066 r2
.y
+= FONT_SPACING
*2;
1069 OptMenu::Init(), renderBG
=true;
1074 OptMenuTitle::Render();
1080 void RenderFade(double time
, int dir
, int seed
);
1082 struct Fader
: public Menu
1087 Fader(int _dir
, int _result
, double _speed
=1) : dir(_dir
), speed(_speed
), result(_result
)
1089 renderBG
= under
? under
->renderBG
: true;
1096 RenderFade(time
, dir
, (long)this);
1098 void Update(double timedelta
)
1100 Menu::Update(timedelta
* speed
);
1106 else if (time
>= 0.5)
1116 Pop(); // Remove old menu
1117 HackKeyPress(SDLK_ESCAPE
, KMOD_CTRL
| KMOD_SHIFT
); // Reload map combination!
1121 Pop(); // Remove old menu
1122 new Fader(1, 0, speed
);
1124 if (result
==-5 || result
==-7)
1126 new Ending(result
==-7);
1127 new Fader(1, 0, speed
);
1133 void Ending::Cancel()
1135 new Fader(-1, -6, 0.3);
1139 void ToggleFullscreen();
1141 void OptMenu::Select()
1143 if (select
<0 || select
>=num_opt
)
1153 HackKeyPress('r', KMOD_CTRL
);
1157 case OPT_GOTO_MAP_CONTINUE
:
1159 HackKeyPress(SDLK_ESCAPE
, KMOD_CTRL
);
1163 new QuitConfirmMenu();
1166 case OPT_FULLSCREEN
:
1170 case OPT_QUIT_CONFIRM
:
1174 case OPT_QUIT_MENU_CONFIRM
:
1180 new OptionMenu(!activeMenu
->renderBG
);
1187 case OPT_QUIT_CANCEL
:
1193 new Fader(-1, -5, 0.3);
1197 new Fader(-1, -7, 0.3);
1202 HackKeyPress('z', 0);
1206 if (opt
[select
]>=OPT_GAMESLOT_0
&& opt
[select
]<=OPT_GAMESLOT_LAST
1207 || opt
[select
]==OPT_GAMESLOT_NEW
&& freeSlot
>=0)
1209 if (opt
[select
]==OPT_GAMESLOT_NEW
)
1210 GetSlotName(freeSlot
, currentSlot
);
1212 GetSlotName(opt
[select
]-OPT_GAMESLOT_0
, currentSlot
);