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() : under(activeMenu
), time(0), renderBG(true) { 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
;
163 char title
[50]; // FIXME
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
)
316 Menu::Mouse(x
,y
,dx
,dy
,buttons_pressed
, buttons_released
, buttons
);
319 bool KeyPressed(int key
, int mod
)
323 else if (key
==SDLK_RIGHT
)
326 return Menu::KeyPressed(key
, mod
);
330 virtual void Render()
332 const double SPD
= 10;
335 sprintf (title
, _("Help (Page --)"), page_display
+1, page_count
);
337 sprintf (title
, _("Help (Page %d/%d)"), page_display
+1, page_count
);
342 y
= SCREEN_H
/4+int(MAX(0, time
*SPD
)*-page_dir
*SCREEN_H
);
344 y
= SCREEN_H
/4+int(MAX(0, 1-time
*SPD
)*page_dir
*SCREEN_H
);
348 PrintC(false, SCREEN_W
/2, y
-FONT_SPACING
*2, "^");
349 PrintC(false, SCREEN_W
/2, HintMessage::GetOuterWindowRect().y
+
350 HintMessage::GetOuterWindowRect().h
, "_");
353 HintMessage::Render(0,y
);
355 if (time
> 1.0/SPD
&& page_dir
&& state
==0)
358 page
= (page
+page_dir
) & 31;
360 if (hint
[page
]) break;
362 } while (hint
[page
]==0 || !(flags
&(1<<page
)));
365 page_display
= (page_display
+ page_count
+ page_dir
) % page_count
;
370 if (time
>1.0/SPD
&& state
==1)
371 state
=0, page_dir
= 0;
373 virtual void Update(double timedelta
)
375 Menu::Update(timedelta
);
387 #define MAX_GAMESLOT 4
390 OPT_GAMESLOT_LAST
= OPT_GAMESLOT_0
+ MAX_GAMESLOT
- 1,
394 OPT_GOTO_MAP_CONTINUE
,
400 OPT_QUIT_MENU_CONFIRM
,
410 char optionSlotName
[MAX_GAMESLOT
][40] = {0};
411 char currentSlot
[80] = "";
413 char* GetSlotName(int i
, char * t
)
415 sprintf(t
, "save%d.dat", i
+1);
419 char * optionString
[] = {
428 _("Toggle Fullscreen"),
433 _("Return to Title"),
436 _("Yes, really delete it!"),
440 _("View Credits Sequence"), _("View Credits Sequence"),
444 struct OptMenu
: public Menu
453 OptMenu(const char * t
) : select(0), title(t
)
462 r
.x
=(SCREEN_W
-r
.w
)/2;
467 const int SPACE
= int(FONT_SPACING
* 1.5);
469 r2
.h
= SPACE
*num_opt
+ FONT_SPACING
/2;
470 r
.h
= r2
.h
+ (FONT_SPACING
+2*2);
471 r
.y
-= FONT_SPACING
+2;
476 void RenderOption(int o
, const char * s
)
478 int y
= r2
.y
+ FONT_SPACING
/2 + int(FONT_SPACING
* 1.5) * o
;
481 int x
= r
.x
+ SDLPangoTextWidth(" ");
482 int x1
= x
+ SDLPangoTextWidth("> ");
485 //x += int( sin(time*9)*2.5 );
486 //y += int( sin(time*9 + 1.5)*1.5 );
487 Print(x
, y
, "> %s", s
);
491 Print(x1
, y
, "%s", s
);
499 //x += int( sin(time*9)*2.5 );
500 //y += int( sin(time*9 + 1.5)*1.5 );
501 PrintC(false, x
, y
, "> %s <", s
);
505 PrintC(false, x
, y
, "%s", s
);
513 if (select
<0) select
= num_opt
-1;
514 if (select
>=num_opt
) select
= 0;
516 virtual void Mouse(int x
, int y
, int dx
, int dy
, int buttons_pressed
, int buttons_released
, int buttons
)
520 if (x
<r2
.x
|| y
<r2
.y
|| x
>r2
.x
+r2
.w
|| y
>r2
.y
+r2
.h
)
522 if (buttons_pressed
!=4 && buttons_pressed
!=2)
527 select
= (y
-r2
.y
-FONT_SPACING
/3) / int(FONT_SPACING
*1.5);
528 if (select
<0) select
= 0;
529 if (select
>=num_opt
) select
= num_opt
-1;
532 if (buttons_pressed
==1)
534 Menu::Mouse(x
, y
, dx
, dy
, buttons_pressed
, buttons_released
, buttons
);
546 SDL_FillRect(screen
, &r
, SDL_MapRGB(screen
->format
, 60,90,90));
547 SDL_FillRect(screen
, &r2
, SDL_MapRGB(screen
->format
, 20,50,50));
551 int y
= r2
.y
+ FONT_SPACING
/ 2;
553 Print(r2
.x
+SDLPangoTextWidth(" "), r
.y
+4, title
);
555 PrintC(false, r2
.x
+r2
.w
/2, r
.y
+4, title
);
559 for (int i
=0; i
<num_opt
; i
++)
560 RenderOption(i
, optionString
[opt
[i
]]);
568 struct WinLoseScreen
: public OptMenu
571 int score
, par
, best_score
;
572 WinLoseScreen(bool _win
, int _score
=0, int _par
=0, int _prev_score
=0) :
575 best_score(_prev_score
),
577 OptMenu(_win
? _("Level Complete!") : _("Emi can't swim..."))
580 opt
[num_opt
++] = OPT_UNDO
;
582 opt
[num_opt
++] = OPT_RESTART
;
583 opt
[num_opt
++] = win
? OPT_GOTO_MAP_CONTINUE
: OPT_GOTO_MAP
;
589 r
.h
+= FONT_SPACING
* 3 + FONT_SPACING
/2;
590 r2
.h
+= FONT_SPACING
* 3 + FONT_SPACING
/2;
597 OptMenu::RenderTitle();
600 r2
.y
+= FONT_SPACING
* 3 + FONT_SPACING
/2;
602 OptMenu::RenderOptions();
605 r2
.y
-= FONT_SPACING
* 3 + FONT_SPACING
/2;
610 int y
= r2
.y
+ FONT_SPACING
/2;
611 if (score
< best_score
&& score
<= par
)
612 PrintC(true, x
, y
, _("New Best Score: %d Par Score: %d Par Beaten!"), score
, par
);
613 else if (score
< best_score
)
614 PrintC(true, x
, y
, _("New Best Score: %d Par Score: %d"), score
, par
);
615 else if (par
&& best_score
)
616 PrintC(true, x
, y
, _("Score: %d Previous Best: %d Par Score: %d"), score
, best_score
, par
);
618 PrintC(true, x
, y
+FONT_SPACING
/2, _("Well Done! Level Completed!"));
625 HackKeyPress('z', 0);
627 bool KeyPressed(int key
, int mod
)
629 if (key
=='z' || key
=='u' || key
==SDLK_DELETE
|| key
==SDLK_BACKSPACE
)
631 if (key
=='r' && (mod
& KMOD_CTRL
))
634 HackKeyPress(key
, mod
);
637 return OptMenu::KeyPressed(key
, mod
);
639 virtual void Mouse(int x
, int y
, int dx
, int dy
, int buttons_pressed
, int buttons_released
, int buttons
)
641 if (buttons_pressed
==4)
647 OptMenu::Mouse(x
, y
, dx
, dy
, buttons_pressed
, buttons_released
, buttons
);
658 struct OptMenuTitle
: public OptMenu
660 OptMenuTitle(const char * t
) : OptMenu(t
)
668 SDL_Rect a
= {0,0,SCREEN_W
,SCREEN_H
};
669 // SDL_FillRect(screen, &a, SDL_MapRGB(screen->format, 10,25,25));
672 SDL_BlitSurface(titlePage
, &a
, screen
, &a
);
674 OptMenu::RenderTitle();
675 OptMenu::RenderOptions();
684 int x
= SCREEN_W
- r
.x
- r
.w
;// - FONT_SPACING;
685 int y
= SCREEN_H
- r
.y
- r
.h
+ FONT_SPACING
/4;// - FONT_SPACING;
688 r
.w
+=20; r2
.w
+=20; r
.h
+=20; r2
.h
+=20;
690 r
.h
= r2
.h
= SCREEN_H
;
692 r
.y
= r2
.y
- FONT_SPACING
- 2;
696 const char *ending
[] = {
697 _(" Very Well Done! "),
698 "", "", "", "", "", "",
700 "", "", "*15,4", "", "",
702 _("All Levels Cleared!"),
704 "", "", "*5,7", "", "",
706 _("Not a single green hexagon is left unbroken."),
708 _("Truly, you are a master of hexagon hopping!"),
710 "", "", "*9,10", "", "",
712 "", _("Credits"), "", "", "",
713 _("<Design & Direction:"), ">Tom Beaumont", "", "",
714 _("<Programming:"), ">Tom Beaumont", "", "",
715 _("<Graphics:"), ">Tom Beaumont", "", "",
716 _("<Thanks to:"), ">Kris Beaumont", "", "",
717 // "", "<Some useless facts...", "",
718 _("<Tools and libraries used:"), "", ">Photoshop LE", ">Inno Setup", ">Wings 3D", ">MSVC", ">SDL", "",
719 _("<Fonts used:"), "", ">Copperplate gothic bold", ">Verdana", "",
721 "", "", "*12,14", "", "",
723 _("Thanks for playing!")
726 const char *ending2
[] = {
727 _(" Absolutely Amazing! "),
730 "", "", "*15,4", "", "",
732 _("All Levels Mastered!!"),
734 "", "", "*5,7", "", "",
736 _("You crushed every last green hexagon with"),
737 _("breathtaking efficiency!"),
739 _("You truly are a grand master of hexagon hopping!"),
742 const int endingLen
= sizeof(ending
)/sizeof(ending
[0]);
743 const int endingLen2
= sizeof(ending2
)/sizeof(ending2
[0]);
744 const int scrollMax
= SCREEN_H
+ (endingLen
+1) * FONT_SPACING
;
746 struct Ending
: public Menu
749 double x
,y
,xs
,ys
,xa
,ya
;
753 void Update(double td
)
755 if (type
==EMPTY
) return;
762 if (type
==TILE_LASER_HEAD
&& time
<0.3)
763 type
=TILE_FIRE_PARTICLE_1
;
764 if (type
==TILE_FIRE_PARTICLE_1
&& time
<0.1)
765 type
=TILE_FIRE_PARTICLE_2
;
767 // if (type==COLLAPSABLE || type==COLLAPSE_DOOR)
768 // for (int i=int((time)*40); i<int((time+td)*40); i++)
769 // new Particle(TILE_GREEN_FRAGMENT_1, x+32-rand()%63, y+20-rand()%40);
771 if (y
>SCREEN_H
&& type
==TILE_GREEN_FRAGMENT
)
772 xa
=0, ys
=-ys
/2, y
=SCREEN_H
, type
=TILE_GREEN_FRAGMENT_1
, time
+=rand()%100*0.01;
773 if (y
>SCREEN_H
&& type
==TILE_GREEN_FRAGMENT_1
)
774 xa
=0, ys
=-ys
/2, y
=SCREEN_H
, type
=TILE_GREEN_FRAGMENT_2
, time
+=rand()%100*0.01;
775 if (y
>SCREEN_H
&& type
==TILE_GREEN_FRAGMENT_2
)
782 for (int i
=0; i
<40; i
++)
783 new Particle(TILE_LASER_HEAD
, x
, y
);
784 for (int i
=0; i
<40; i
++)
785 new Particle(TILE_GREEN_FRAGMENT
+ rand() % 3, x
+32-rand()%63, y
+20-rand()%40);
787 if (type
==COLLAPSE_DOOR
)
798 if (type
==EMPTY
) return;
800 if (type
<0.05 && type
<15)
801 RenderTile(false, tileSolid
[type
] ? TILE_WHITE_WALL
: TILE_WHITE_TILE
, int(x
)+scrollX
, int(y
)+scrollY
);
803 RenderTile(false, type
, int(x
)+scrollX
, int(y
)+scrollY
);
806 Particle() : type(EMPTY
) {}
807 Particle(int t
, int _x
) : type(t
), x(_x
)
814 //x=rand() % SCREEN_W;
815 //y=rand() % SCREEN_H;
820 Particle(int t
, double _x
, double _y
) : type(t
)
824 double r
= (rand() % 2000) * PI
/ 1000;
825 double d
= rand() % 50 + 250;
829 time
= 1 + (rand() & 255) /255.0;
830 if (t
==TILE_WATER_PARTICLE
|| t
==TILE_GREEN_FRAGMENT
|| t
==TILE_GREEN_FRAGMENT_1
)
832 xa
=-xs
/time
; ya
=-ys
/time
;
833 if (t
==TILE_WATER_PARTICLE
|| t
==TILE_GREEN_FRAGMENT
|| t
==TILE_GREEN_FRAGMENT_1
)
834 ya
+= 500, ya
/=2, xa
/=2, xs
/=2, ys
/=2;
836 ~Particle() { type
= EMPTY
; }
837 void* operator new(size_t sz
);
845 static Ending
* ending
;
847 Ending(bool _goodEnd
) : goodEnding(_goodEnd
)
849 memset(p
, 0, sizeof(p
));
858 SDL_Rect a
= {0,0,SCREEN_W
,SCREEN_H
};
859 SDL_FillRect(screen
, &a
, SDL_MapRGB(screen
->format
, 10,25,25));
861 for (int i
=0; i
<sizeof(p
)/sizeof(p
[0]); i
++)
866 int xr
= SCREEN_W
*4/5;
867 int y
= SCREEN_H
- int(scroll
);
868 for (int i
=0; i
<endingLen
; i
++)
870 if (y
>-FONT_SPACING
*2 && y
<SCREEN_H
+FONT_SPACING
)
872 const char * xx
= (i
<endingLen2
&& goodEnding
) ? ending2
[i
] : ::ending
[i
];
874 Print(xl
, y
+FONT_SPACING
, xx
+1);
879 RenderTile(false, atoi(xx
+1), (xl
+x
)/2+scrollX
, y
+FONT_SPACING
/2+scrollY
);
880 RenderTile(false, atoi(strchr(xx
, ',')+1), (xr
+x
)/2+scrollX
, y
+FONT_SPACING
/2+scrollY
);
883 PrintC(false, x
, y
, xx
);
887 if (scroll
> scrollMax
+ FONT_SPACING
*10)
888 PrintC(true, x
, SCREEN_H
/2-FONT_SPACING
/2, _("The End"));
893 bool KeyPressed(int key
, int mod
)
895 if (key
=='r' && (mod
& KMOD_CTRL
))
898 memset(p
, 0, sizeof(p
));
901 return Menu::KeyPressed(key
, mod
);
905 void Update(double td
)
912 if (keyState
[SDLK_LSHIFT
])
915 t
= MAX(-td
*5, -time
);
918 double s
= scroll
= time
* 50;
919 // if (scroll > scrollMax)
920 // scroll = fmod(scroll, scrollMax);
921 // scroll = 0, time = 0;
923 if (old
>4 && time
> 4)
925 if (scroll
< scrollMax
+ FONT_SPACING
*17)
927 for (int i
=int( old
/2.5); i
<int(time
/2.5); i
++)
929 int xs
= (rand()%SCREEN_W
* 6 + 1) / 8;
930 for (int j
=rand()%3+1; j
; j
--)
931 new Particle(rand()&1 ? COLLAPSABLE
: COLLAPSE_DOOR
, xs
+j
*64);
934 if (scroll
> scrollMax
+ FONT_SPACING
*27)
941 Ending
* Ending::ending
= 0;
942 void* Ending::Particle::operator new(size_t sz
)
944 static int start
= 0;
945 const int max
= sizeof(ending
->p
)/sizeof(ending
->p
[0]);
946 for (int i
=0; i
<max
; i
++)
949 if (start
==max
) start
=0;
950 if (ending
->p
[start
].type
==EMPTY
)
951 return &ending
->p
[start
];
953 return &ending
->p
[rand() % max
];
956 struct TitleMenu
: public OptMenuTitle
958 TitleMenu() : OptMenuTitle("")
964 for (int i
=0; i
<MAX_GAMESLOT
; i
++)
968 FILE* f
= file_open(tmp
, "rb");
971 p
.LoadSave(f
, false);
974 if (p
.general
.completionPercentage
==100 && p
.general
.masteredPercentage
==100)
975 sprintf(optionSlotName
[i
], _("Continue game %d (All Clear!)"), i
+1, p
.general
.completionPercentage
, p
.general
.masteredPercentage
);
976 else if (p
.general
.completionPercentage
==100)
977 sprintf(optionSlotName
[i
], _("Continue game %d (%d%% + %d%%)"), i
+1, p
.general
.completionPercentage
, p
.general
.masteredPercentage
);
979 sprintf(optionSlotName
[i
], _("Continue game %d (%d%% complete)"), i
+1, p
.general
.completionPercentage
);
981 opt
[num_opt
++] = OPT_GAMESLOT_0
+ i
;
985 // sprintf(optionSlotName[i], "Start new game (slot %d)", i+1);
986 // opt[num_opt++] = OPT_GAMESLOT_0 + i;
994 if (num_opt
< MAX_GAMESLOT
)
995 opt
[num_opt
++] = OPT_GAMESLOT_NEW
;
997 opt
[num_opt
++] = OPT_OPTIONS
;
999 opt
[num_opt
++] = OPT_END
;
1000 opt
[num_opt
++] = OPT_END2
;
1002 opt
[num_opt
++] = OPT_QUIT
;
1006 r
.y
-=FONT_SPACING
*2;
1007 r2
.y
-=FONT_SPACING
*2;
1010 r
.y
+=FONT_SPACING
+FONT_SPACING
/2, r2
.y
+=FONT_SPACING
+FONT_SPACING
/2;
1013 bool KeyPressed(int key
, int mod
);
1016 struct QuitConfirmMenu
: public OptMenuTitle
1018 QuitConfirmMenu() : OptMenuTitle(_("Quit: Are you sure?"))
1020 opt
[num_opt
++] = OPT_QUIT_CONFIRM
;
1021 opt
[select
=num_opt
++] = OPT_QUIT_CANCEL
;
1024 r
.y
+= FONT_SPACING
*1;
1025 r2
.y
+= FONT_SPACING
*2;
1030 struct DeleteConfirmMenu
: public OptMenuTitle
1034 DeleteConfirmMenu(int _num
) : OptMenuTitle(&tmp
[0]), num(_num
)
1038 sprintf(tmp
, _("Really delete game %d?"), num
+1);
1039 opt
[num_opt
++] = OPT_DELETE_CONFIRM
;
1040 opt
[select
=num_opt
++] = OPT_DELETE_CANCEL
;
1043 r
.y
+= FONT_SPACING
*1;
1044 r2
.y
+= FONT_SPACING
*2;
1048 if (select
<0 || select
>=num_opt
)
1050 if (opt
[select
] == OPT_DELETE_CONFIRM
)
1052 GetSlotName(num
, tmp
);
1061 bool TitleMenu::KeyPressed(int key
, int mod
)
1063 if (key
==SDLK_DELETE
|| key
==SDLK_BACKSPACE
|| key
==SDLK_F2
)
1065 if (select
<0 || select
>=num_opt
|| opt
[select
]<OPT_GAMESLOT_0
|| opt
[select
]>OPT_GAMESLOT_LAST
)
1067 int i
= opt
[select
] - OPT_GAMESLOT_0
;
1069 new DeleteConfirmMenu(i
);
1073 return OptMenu::KeyPressed(key
, mod
);
1076 struct PauseMenu
: public OptMenu
1078 PauseMenu(bool isMap
, bool allowGotoMap
, int allowEnd
, int allowEnd2
) : OptMenu(_("Paused"))
1080 opt
[num_opt
++] = OPT_RESUME
;
1082 opt
[num_opt
++] = OPT_RESTART
;
1083 opt
[num_opt
++] = OPT_OPTIONS
;
1084 opt
[num_opt
++] = OPT_HELP
;
1085 if (allowEnd
|| allowEnd2
)
1086 opt
[num_opt
++] = allowEnd2
? OPT_END2
: OPT_END
;
1087 opt
[num_opt
++] = (isMap
|| !allowGotoMap
) ? OPT_QUIT_MENU_CONFIRM
: OPT_GOTO_MAP
;
1090 virtual bool KeyPressed(int key
, int mod
)
1092 if (key
=='p' || key
==SDLK_PAUSE
)
1097 return Menu::KeyPressed(key
, mod
);
1102 struct OptionMenu
: public OptMenuTitle
1105 OptionMenu(bool _title
) : OptMenuTitle(_("Options")), title(_title
)
1107 opt
[num_opt
++] = OPT_FULLSCREEN
;
1109 opt
[num_opt
++] = OPT_BACK
;
1113 OptMenuTitle::Init(), renderBG
=false;
1114 r
.y
+= FONT_SPACING
;
1115 r2
.y
+= FONT_SPACING
*2;
1118 OptMenu::Init(), renderBG
=true;
1123 OptMenuTitle::Render();
1129 void RenderFade(double time
, int dir
, int seed
);
1131 struct Fader
: public Menu
1136 Fader(int _dir
, int _result
, double _speed
=1) : dir(_dir
), result(_result
), speed(_speed
)
1138 renderBG
= under
? under
->renderBG
: true;
1145 RenderFade(time
, dir
, (long)this);
1147 void Update(double timedelta
)
1149 Menu::Update(timedelta
* speed
);
1155 else if (time
>= 0.5)
1165 Pop(); // Remove old menu
1166 HackKeyPress(SDLK_ESCAPE
, KMOD_CTRL
| KMOD_SHIFT
); // Reload map combination!
1170 Pop(); // Remove old menu
1171 new Fader(1, 0, speed
);
1173 if (result
==-5 || result
==-7)
1175 new Ending(result
==-7);
1176 new Fader(1, 0, speed
);
1182 void Ending::Cancel()
1184 new Fader(-1, -6, 0.3);
1188 void ToggleFullscreen();
1190 void OptMenu::Select()
1192 if (select
<0 || select
>=num_opt
)
1202 HackKeyPress('r', KMOD_CTRL
);
1206 case OPT_GOTO_MAP_CONTINUE
:
1208 HackKeyPress(SDLK_ESCAPE
, KMOD_CTRL
);
1212 new QuitConfirmMenu();
1215 case OPT_FULLSCREEN
:
1219 case OPT_QUIT_CONFIRM
:
1223 case OPT_QUIT_MENU_CONFIRM
:
1229 new OptionMenu(!activeMenu
->renderBG
);
1236 case OPT_QUIT_CANCEL
:
1242 new Fader(-1, -5, 0.3);
1246 new Fader(-1, -7, 0.3);
1251 HackKeyPress('z', 0);
1255 if (opt
[select
]>=OPT_GAMESLOT_0
&& opt
[select
]<=OPT_GAMESLOT_LAST
1256 || opt
[select
]==OPT_GAMESLOT_NEW
&& freeSlot
>=0)
1258 if (opt
[select
]==OPT_GAMESLOT_NEW
)
1259 GetSlotName(freeSlot
, currentSlot
);
1261 GetSlotName(opt
[select
]-OPT_GAMESLOT_0
, currentSlot
);