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
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
);
37 Menu() : renderBG(true), under(activeMenu
), time(0) { activeMenu
= this; }
40 if(this!=deadMenu
) FATAL("this!=deadMenu");
46 if (!activeMenu
) return;
48 activeMenu
= activeMenu
->under
;
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
)
57 else if (key
=='s' || key
==SDLK_DOWN
|| key
==SDLK_KP2
|| key
=='a' || key
=='d' || key
==SDLK_KP1
|| key
==SDLK_KP3
)
59 else if (key
==' ' || key
==SDLK_RETURN
)
64 else if (key
==SDLK_ESCAPE
|| key
==SDLK_BACKSPACE
|| key
==SDLK_KP_PERIOD
|| key
==SDLK_DELETE
)
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)
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;
87 const char * hint
[] = {
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."),
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!"),
96 _("The coloured walls flatten themselves when there are no matching coloured tiles remaining."),
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!"),
100 _("A red spinner tile will rotate the pieces around it when you step on it."),
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..."),
108 _("Yellow laser tiles fire when you step on them. Shooting other laser tiles is more destructive."),
110 _("Ice is slippery! Please be careful!!"),
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."),
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."),
121 _("The blue lifts go up or down when you land on them."),
127 _("The spiky anti-ice pickups turn icy tiles into blue ones. They get used automatically when you land on ice."),
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."),
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."),
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?"),
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 "
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
160 SDL_Rect InnerTextWindowRect
;
161 SDL_Rect OuterTextWindowRect
;
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;
185 new HintMessage(hint
[t
]);
189 HintMessage(const char * m
) { Init(m
); }
191 void Init(const char * m
)
194 memset(title
, 0, sizeof(title
));
195 char * x
= strstr(m
, "|");
199 strcpy(title
, _("Info:"));
203 strncpy(title
, m
, x
-m
);
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);
226 y
= SCREEN_H
/4 + int(SCREEN_H
*(-time
*5)*3/4);
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
)
237 // std::cout << "Error in Render: " << x << " " << y << "\n"; // CHECKME
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);
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)
268 bool KeyPressed(int /*key*/, int /*mod*/)
270 if (state
==0 && time
>0.2)
275 virtual void Update(double timedelta
)
277 Menu::Update(timedelta
);
278 if(state
&& time
> 0.2)
283 int HintMessage::flags
= 1<<31 | 1<<30;
285 struct HintReview
: public HintMessage
291 HintReview() : HintMessage(hint
[31]), page(31), page_dir(0), page_display(0)
294 for (int i
=0; i
<32; i
++)
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)
309 else if (y
> HintMessage::GetOuterWindowRect().y
+
310 HintMessage::GetOuterWindowRect().h
)
315 else if (buttons_pressed
==8)
317 else if (buttons_pressed
==16)
320 Menu::Mouse(x
,y
,dx
,dy
,buttons_pressed
, buttons_released
, buttons
);
323 bool KeyPressed(int key
, int mod
)
327 else if (key
==SDLK_RIGHT
)
330 return Menu::KeyPressed(key
, mod
);
334 virtual void Render()
336 const double SPD
= 10;
339 sprintf (title
, _("Help (Page --)"), page_display
+1, page_count
);
341 sprintf (title
, _("Help (Page %d/%d)"), page_display
+1, page_count
);
346 y
= SCREEN_H
/4+int(MAX(0, time
*SPD
)*-page_dir
*SCREEN_H
);
348 y
= SCREEN_H
/4+int(MAX(0, 1-time
*SPD
)*page_dir
*SCREEN_H
);
352 PrintC(false, SCREEN_W
/2, y
-FONT_SPACING
*2, "^");
353 PrintC(false, SCREEN_W
/2, HintMessage::GetOuterWindowRect().y
+
354 HintMessage::GetOuterWindowRect().h
, "_");
357 HintMessage::Render(0,y
);
359 if (time
> 1.0/SPD
&& page_dir
&& state
==0)
362 page
= (page
+page_dir
) & 31;
364 if (hint
[page
]) break;
366 } while (hint
[page
]==0 || !(flags
&(1<<page
)));
369 page_display
= (page_display
+ page_count
+ page_dir
) % page_count
;
374 if (time
>1.0/SPD
&& state
==1)
375 state
=0, page_dir
= 0;
377 virtual void Update(double timedelta
)
379 Menu::Update(timedelta
);
391 #define MAX_GAMESLOT 4
394 OPT_GAMESLOT_LAST
= OPT_GAMESLOT_0
+ MAX_GAMESLOT
- 1,
398 OPT_GOTO_MAP_CONTINUE
,
404 OPT_QUIT_MENU_CONFIRM
,
414 char optionSlotName
[MAX_GAMESLOT
][400] = { {0} };
415 char currentSlot
[800] = "";
417 char* GetSlotName(int i
, char * t
)
419 sprintf(t
, "save%d.dat", i
+1);
423 char * optionString
[] = {
432 _("Toggle Fullscreen"),
437 _("Return to Title"),
440 _("Yes, really delete it!"),
444 _("View Credits Sequence"), _("View Credits Sequence"),
448 struct OptMenu
: public Menu
457 OptMenu(const char * t
) : select(0), title(t
)
466 r
.x
=(SCREEN_W
-r
.w
)/2;
471 const int SPACE
= int(FONT_SPACING
* 1.5);
473 r2
.h
= SPACE
*num_opt
+ FONT_SPACING
/2;
474 r
.h
= r2
.h
+ (FONT_SPACING
+2*2);
475 r
.y
-= FONT_SPACING
+2;
480 void RenderOption(int o
, const char * s
)
482 int y
= r2
.y
+ FONT_SPACING
/2 + int(FONT_SPACING
* 1.5) * o
;
485 int x
= r
.x
+ SDLPangoTextWidth(" ");
486 int x1
= x
+ SDLPangoTextWidth("> ");
489 //x += int( sin(time*9)*2.5 );
490 //y += int( sin(time*9 + 1.5)*1.5 );
491 Print(x
, y
, "> %s", s
);
495 Print(x1
, y
, "%s", s
);
503 //x += int( sin(time*9)*2.5 );
504 //y += int( sin(time*9 + 1.5)*1.5 );
505 PrintC(false, x
, y
, "> %s <", s
);
509 PrintC(false, x
, y
, "%s", s
);
517 if (select
<0) select
= num_opt
-1;
518 if (select
>=num_opt
) select
= 0;
520 virtual void Mouse(int x
, int y
, int dx
, int dy
, int buttons_pressed
, int buttons_released
, int buttons
)
524 if (x
<r2
.x
|| y
<r2
.y
|| x
>r2
.x
+r2
.w
|| y
>r2
.y
+r2
.h
)
526 if (buttons_pressed
!=4 && buttons_pressed
!=2)
531 select
= (y
-r2
.y
-FONT_SPACING
/3) / int(FONT_SPACING
*1.5);
532 if (select
<0) select
= 0;
533 if (select
>=num_opt
) select
= num_opt
-1;
536 if (buttons_pressed
==1)
538 Menu::Mouse(x
, y
, dx
, dy
, buttons_pressed
, buttons_released
, buttons
);
550 SDL_FillRect(screen
, &r
, SDL_MapRGB(screen
->format
, 60,90,90));
551 SDL_FillRect(screen
, &r2
, SDL_MapRGB(screen
->format
, 20,50,50));
556 Print(r2
.x
+SDLPangoTextWidth(" "), r
.y
+4, title
);
558 PrintC(false, r2
.x
+r2
.w
/2, r
.y
+4, title
);
562 for (int i
=0; i
<num_opt
; i
++)
563 RenderOption(i
, optionString
[opt
[i
]]);
571 struct WinLoseScreen
: public OptMenu
574 int score
, par
, best_score
;
575 WinLoseScreen(bool _win
, int _score
=0, int _par
=0, int _prev_score
=0) :
576 OptMenu(_win
? _("Level Complete!") : _("Emi can't swim...")),
580 best_score(_prev_score
)
583 opt
[num_opt
++] = OPT_UNDO
;
585 opt
[num_opt
++] = OPT_RESTART
;
586 opt
[num_opt
++] = win
? OPT_GOTO_MAP_CONTINUE
: OPT_GOTO_MAP
;
592 r
.h
+= FONT_SPACING
* 3 + FONT_SPACING
/2;
593 r2
.h
+= FONT_SPACING
* 3 + FONT_SPACING
/2;
600 OptMenu::RenderTitle();
603 r2
.y
+= FONT_SPACING
* 3 + FONT_SPACING
/2;
605 OptMenu::RenderOptions();
608 r2
.y
-= FONT_SPACING
* 3 + FONT_SPACING
/2;
613 int y
= r2
.y
+ FONT_SPACING
/2;
614 if (score
< best_score
&& score
<= par
)
615 PrintC(true, x
, y
, _("New Best Score: %d Par Score: %d Par Beaten!"), score
, par
);
616 else if (score
< best_score
)
617 PrintC(true, x
, y
, _("New Best Score: %d Par Score: %d"), score
, par
);
618 else if (par
&& best_score
)
619 PrintC(true, x
, y
, _("Score: %d Previous Best: %d Par Score: %d"), score
, best_score
, par
);
621 PrintC(true, x
, y
+FONT_SPACING
/2, _("Well Done! Level Completed!"));
628 HackKeyPress('z', 0);
630 bool KeyPressed(int key
, int mod
)
632 if (key
=='z' || key
=='u' || key
==SDLK_DELETE
|| key
==SDLK_BACKSPACE
)
634 if (key
=='r' && (mod
& KMOD_CTRL
))
637 HackKeyPress(key
, mod
);
640 return OptMenu::KeyPressed(key
, mod
);
642 virtual void Mouse(int x
, int y
, int dx
, int dy
, int buttons_pressed
, int buttons_released
, int buttons
)
644 if (buttons_pressed
==4)
650 OptMenu::Mouse(x
, y
, dx
, dy
, buttons_pressed
, buttons_released
, buttons
);
661 struct OptMenuTitle
: public OptMenu
663 OptMenuTitle(const char * t
) : OptMenu(t
)
671 SDL_Rect a
= {0,0,SCREEN_W
,SCREEN_H
};
672 // SDL_FillRect(screen, &a, SDL_MapRGB(screen->format, 10,25,25));
675 SDL_BlitSurface(titlePage
, &a
, screen
, &a
);
677 OptMenu::RenderTitle();
678 OptMenu::RenderOptions();
687 int x
= SCREEN_W
- r
.x
- r
.w
;// - FONT_SPACING;
688 int y
= SCREEN_H
- r
.y
- r
.h
+ FONT_SPACING
/4;// - FONT_SPACING;
691 r
.w
+=20; r2
.w
+=20; r
.h
+=20; r2
.h
+=20;
693 r
.h
= r2
.h
= SCREEN_H
;
695 r
.y
= r2
.y
- FONT_SPACING
- 2;
699 const char *ending
[] = {
700 _(" Very Well Done! "),
701 "", "", "", "", "", "",
703 "", "", "*15,4", "", "",
705 _("All Levels Cleared!"),
707 "", "", "*5,7", "", "",
709 _("Not a single green hexagon is left unbroken."),
711 _("Truly, you are a master of hexagon hopping!"),
713 "", "", "*9,10", "", "",
715 "", _("Credits"), "", "", "",
716 _("<Design & Direction:"), ">Tom Beaumont", "", "",
717 _("<Programming:"), ">Tom Beaumont", "", "",
718 _("<Graphics:"), ">Tom Beaumont", "", "",
719 _("<Thanks to:"), ">Kris Beaumont", "", "",
720 // "", "<Some useless facts...", "",
721 _("<Tools and libraries used:"), "", ">Photoshop LE", ">Inno Setup", ">Wings 3D", ">MSVC", ">SDL", "",
722 _("<Fonts used:"), "", ">Copperplate gothic bold", ">Verdana", "",
724 "", "", "*12,14", "", "",
726 _("Thanks for playing!")
729 const char *ending2
[] = {
730 _(" Absolutely Amazing! "),
733 "", "", "*15,4", "", "",
735 _("All Levels Mastered!!"),
737 "", "", "*5,7", "", "",
739 _("You crushed every last green hexagon with"),
740 _("breathtaking efficiency!"),
742 _("You truly are a grand master of hexagon hopping!"),
745 const int endingLen
= sizeof(ending
)/sizeof(ending
[0]);
746 const int endingLen2
= sizeof(ending2
)/sizeof(ending2
[0]);
747 const int scrollMax
= SCREEN_H
+ (endingLen
+1) * FONT_SPACING
;
749 struct Ending
: public Menu
752 double x
,y
,xs
,ys
,xa
,ya
;
756 void Update(double td
)
758 if (type
==EMPTY
) return;
765 if (type
==TILE_LASER_HEAD
&& time
<0.3)
766 type
=TILE_FIRE_PARTICLE_1
;
767 if (type
==TILE_FIRE_PARTICLE_1
&& time
<0.1)
768 type
=TILE_FIRE_PARTICLE_2
;
770 // if (type==COLLAPSABLE || type==COLLAPSE_DOOR)
771 // for (int i=int((time)*40); i<int((time+td)*40); i++)
772 // new Particle(TILE_GREEN_FRAGMENT_1, x+32-rand()%63, y+20-rand()%40);
774 if (y
>SCREEN_H
&& type
==TILE_GREEN_FRAGMENT
)
775 xa
=0, ys
=-ys
/2, y
=SCREEN_H
, type
=TILE_GREEN_FRAGMENT_1
, time
+=rand()%100*0.01;
776 if (y
>SCREEN_H
&& type
==TILE_GREEN_FRAGMENT_1
)
777 xa
=0, ys
=-ys
/2, y
=SCREEN_H
, type
=TILE_GREEN_FRAGMENT_2
, time
+=rand()%100*0.01;
778 if (y
>SCREEN_H
&& type
==TILE_GREEN_FRAGMENT_2
)
785 for (int i
=0; i
<40; i
++)
786 new Particle(TILE_LASER_HEAD
, x
, y
);
787 for (int i
=0; i
<40; i
++)
788 new Particle(TILE_GREEN_FRAGMENT
+ rand() % 3, x
+32-rand()%63, y
+20-rand()%40);
790 if (type
==COLLAPSE_DOOR
)
801 if (type
==EMPTY
) return;
803 if (type
<0.05 && type
<15)
804 RenderTile(false, tileSolid
[type
] ? TILE_WHITE_WALL
: TILE_WHITE_TILE
, int(x
)+scrollX
, int(y
)+scrollY
);
806 RenderTile(false, type
, int(x
)+scrollX
, int(y
)+scrollY
);
809 Particle() : type(EMPTY
) {}
810 Particle(int t
, int _x
) : x(_x
), type(t
)
817 //x=rand() % SCREEN_W;
818 //y=rand() % SCREEN_H;
823 Particle(int t
, double _x
, double _y
) : type(t
)
827 double r
= (rand() % 2000) * PI
/ 1000;
828 double d
= rand() % 50 + 250;
832 time
= 1 + (rand() & 255) /255.0;
833 if (t
==TILE_WATER_PARTICLE
|| t
==TILE_GREEN_FRAGMENT
|| t
==TILE_GREEN_FRAGMENT_1
)
835 xa
=-xs
/time
; ya
=-ys
/time
;
836 if (t
==TILE_WATER_PARTICLE
|| t
==TILE_GREEN_FRAGMENT
|| t
==TILE_GREEN_FRAGMENT_1
)
837 ya
+= 500, ya
/=2, xa
/=2, xs
/=2, ys
/=2;
839 ~Particle() { type
= EMPTY
; }
840 void* operator new(size_t sz
);
848 static Ending
* ending
;
850 Ending(bool _goodEnd
) : goodEnding(_goodEnd
)
852 memset(p
, 0, sizeof(p
));
861 SDL_Rect a
= {0,0,SCREEN_W
,SCREEN_H
};
862 SDL_FillRect(screen
, &a
, SDL_MapRGB(screen
->format
, 10,25,25));
864 for (unsigned int i
=0; i
<sizeof(p
)/sizeof(p
[0]); i
++)
869 int xr
= SCREEN_W
*4/5;
870 int y
= SCREEN_H
- int(scroll
);
871 for (int i
=0; i
<endingLen
; i
++)
873 if (y
>-FONT_SPACING
*2 && y
<SCREEN_H
+FONT_SPACING
)
875 const char * xx
= (i
<endingLen2
&& goodEnding
) ? ending2
[i
] : ::ending
[i
];
877 Print(xl
, y
+FONT_SPACING
, xx
+1);
882 RenderTile(false, atoi(xx
+1), (xl
+x
)/2+scrollX
, y
+FONT_SPACING
/2+scrollY
);
883 RenderTile(false, atoi(strchr(xx
, ',')+1), (xr
+x
)/2+scrollX
, y
+FONT_SPACING
/2+scrollY
);
886 PrintC(false, x
, y
, xx
);
890 if (scroll
> scrollMax
+ FONT_SPACING
*10)
891 PrintC(true, x
, SCREEN_H
/2-FONT_SPACING
/2, _("The End"));
896 bool KeyPressed(int key
, int mod
)
898 if (key
=='r' && (mod
& KMOD_CTRL
))
901 memset(p
, 0, sizeof(p
));
904 return Menu::KeyPressed(key
, mod
);
908 void Update(double td
)
915 if (keyState
[SDLK_LSHIFT
])
917 if (keyState
[SDLK_0
])
918 t
= MAX(-td
*5, -time
);
922 // if (scroll > scrollMax)
923 // scroll = fmod(scroll, scrollMax);
924 // scroll = 0, time = 0;
926 if (old
>4 && time
> 4)
928 if (scroll
< scrollMax
+ FONT_SPACING
*17)
930 for (int i
=int( old
/2.5); i
<int(time
/2.5); i
++)
932 int xs
= (rand()%SCREEN_W
* 6 + 1) / 8;
933 for (int j
=rand()%3+1; j
; j
--)
934 new Particle(rand()&1 ? COLLAPSABLE
: COLLAPSE_DOOR
, xs
+j
*64);
937 if (scroll
> scrollMax
+ FONT_SPACING
*27)
944 Ending
* Ending::ending
= 0;
945 void* Ending::Particle::operator new(size_t /*sz*/)
947 static int start
= 0;
948 const int max
= sizeof(ending
->p
)/sizeof(ending
->p
[0]);
949 for (int i
=0; i
<max
; i
++)
952 if (start
==max
) start
=0;
953 if (ending
->p
[start
].type
==EMPTY
)
954 return &ending
->p
[start
];
956 return &ending
->p
[rand() % max
];
959 struct TitleMenu
: public OptMenuTitle
961 TitleMenu() : OptMenuTitle("")
967 for (int i
=0; i
<MAX_GAMESLOT
; i
++)
971 FILE* f
= file_open(tmp
, "rb");
974 p
.LoadSave(f
, false);
977 if (p
.general
.completionPercentage
==100 && p
.general
.masteredPercentage
==100)
978 sprintf(optionSlotName
[i
], _("Continue game %d (All Clear!)"), i
+1);
979 else if (p
.general
.completionPercentage
==100)
980 sprintf(optionSlotName
[i
], _("Continue game %d (%d%% + %d%%)"), i
+1, p
.general
.completionPercentage
, p
.general
.masteredPercentage
);
982 sprintf(optionSlotName
[i
], _("Continue game %d (%d%% complete)"), i
+1, p
.general
.completionPercentage
);
984 opt
[num_opt
++] = OPT_GAMESLOT_0
+ i
;
988 // sprintf(optionSlotName[i], "Start new game (slot %d)", i+1);
989 // opt[num_opt++] = OPT_GAMESLOT_0 + i;
997 if (num_opt
< MAX_GAMESLOT
)
998 opt
[num_opt
++] = OPT_GAMESLOT_NEW
;
1000 opt
[num_opt
++] = OPT_OPTIONS
;
1002 opt
[num_opt
++] = OPT_END
;
1003 opt
[num_opt
++] = OPT_END2
;
1005 opt
[num_opt
++] = OPT_QUIT
;
1009 r
.y
-=FONT_SPACING
*2;
1010 r2
.y
-=FONT_SPACING
*2;
1013 r
.y
+=FONT_SPACING
+FONT_SPACING
/2, r2
.y
+=FONT_SPACING
+FONT_SPACING
/2;
1016 bool KeyPressed(int key
, int mod
);
1019 struct QuitConfirmMenu
: public OptMenuTitle
1021 QuitConfirmMenu() : OptMenuTitle(_("Quit: Are you sure?"))
1023 opt
[num_opt
++] = OPT_QUIT_CONFIRM
;
1024 opt
[select
=num_opt
++] = OPT_QUIT_CANCEL
;
1027 r
.y
+= FONT_SPACING
*1;
1028 r2
.y
+= FONT_SPACING
*2;
1033 struct DeleteConfirmMenu
: public OptMenuTitle
1037 DeleteConfirmMenu(int _num
) : OptMenuTitle(&tmp
[0]), num(_num
)
1041 sprintf(tmp
, _("Really delete game %d?"), num
+1);
1042 opt
[num_opt
++] = OPT_DELETE_CONFIRM
;
1043 opt
[select
=num_opt
++] = OPT_DELETE_CANCEL
;
1046 r
.y
+= FONT_SPACING
*1;
1047 r2
.y
+= FONT_SPACING
*2;
1051 if (select
<0 || select
>=num_opt
)
1053 if (opt
[select
] == OPT_DELETE_CONFIRM
)
1055 GetSlotName(num
, tmp
);
1064 bool TitleMenu::KeyPressed(int key
, int mod
)
1066 if (key
==SDLK_DELETE
|| key
==SDLK_BACKSPACE
|| key
==SDLK_F2
)
1068 if (select
<0 || select
>=num_opt
|| opt
[select
]<OPT_GAMESLOT_0
|| opt
[select
]>OPT_GAMESLOT_LAST
)
1070 int i
= opt
[select
] - OPT_GAMESLOT_0
;
1072 new DeleteConfirmMenu(i
);
1076 return OptMenu::KeyPressed(key
, mod
);
1079 struct PauseMenu
: public OptMenu
1081 PauseMenu(bool isMap
, bool allowGotoMap
, int allowEnd
, int allowEnd2
) : OptMenu(_("Paused"))
1083 opt
[num_opt
++] = OPT_RESUME
;
1085 opt
[num_opt
++] = OPT_RESTART
;
1086 opt
[num_opt
++] = OPT_OPTIONS
;
1087 opt
[num_opt
++] = OPT_HELP
;
1088 if (allowEnd
|| allowEnd2
)
1089 opt
[num_opt
++] = allowEnd2
? OPT_END2
: OPT_END
;
1090 opt
[num_opt
++] = (isMap
|| !allowGotoMap
) ? OPT_QUIT_MENU_CONFIRM
: OPT_GOTO_MAP
;
1093 virtual bool KeyPressed(int key
, int mod
)
1095 if (key
=='p' || key
==SDLK_PAUSE
)
1100 return Menu::KeyPressed(key
, mod
);
1105 struct OptionMenu
: public OptMenuTitle
1108 OptionMenu(bool _title
) : OptMenuTitle(_("Options")), title(_title
)
1110 opt
[num_opt
++] = OPT_FULLSCREEN
;
1112 opt
[num_opt
++] = OPT_BACK
;
1116 OptMenuTitle::Init(), renderBG
=false;
1117 r
.y
+= FONT_SPACING
;
1118 r2
.y
+= FONT_SPACING
*2;
1121 OptMenu::Init(), renderBG
=true;
1126 OptMenuTitle::Render();
1132 void RenderFade(double time
, int dir
, int seed
);
1134 struct Fader
: public Menu
1139 Fader(int _dir
, int _result
, double _speed
=1) : dir(_dir
), speed(_speed
), result(_result
)
1141 renderBG
= under
? under
->renderBG
: true;
1148 RenderFade(time
, dir
, (long)this);
1150 void Update(double timedelta
)
1152 Menu::Update(timedelta
* speed
);
1158 else if (time
>= 0.5)
1168 Pop(); // Remove old menu
1169 HackKeyPress(SDLK_ESCAPE
, KMOD_CTRL
| KMOD_SHIFT
); // Reload map combination!
1173 Pop(); // Remove old menu
1174 new Fader(1, 0, speed
);
1176 if (result
==-5 || result
==-7)
1178 new Ending(result
==-7);
1179 new Fader(1, 0, speed
);
1185 void Ending::Cancel()
1187 new Fader(-1, -6, 0.3);
1191 void ToggleFullscreen();
1193 void OptMenu::Select()
1195 if (select
<0 || select
>=num_opt
)
1205 HackKeyPress('r', KMOD_CTRL
);
1209 case OPT_GOTO_MAP_CONTINUE
:
1211 HackKeyPress(SDLK_ESCAPE
, KMOD_CTRL
);
1215 new QuitConfirmMenu();
1218 case OPT_FULLSCREEN
:
1222 case OPT_QUIT_CONFIRM
:
1226 case OPT_QUIT_MENU_CONFIRM
:
1232 new OptionMenu(!activeMenu
->renderBG
);
1239 case OPT_QUIT_CANCEL
:
1245 new Fader(-1, -5, 0.3);
1249 new Fader(-1, -7, 0.3);
1254 HackKeyPress('z', 0);
1258 if (opt
[select
]>=OPT_GAMESLOT_0
&& opt
[select
]<=OPT_GAMESLOT_LAST
1259 || opt
[select
]==OPT_GAMESLOT_NEW
&& freeSlot
>=0)
1261 if (opt
[select
]==OPT_GAMESLOT_NEW
)
1262 GetSlotName(freeSlot
, currentSlot
);
1264 GetSlotName(opt
[select
]-OPT_GAMESLOT_0
, currentSlot
);