1 // Hyperbolic Rogue -- the mission screen
2 // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details
3
4 /** \file quit.cpp
5 * \brief the mission screen, and routines related to it
6 */
7
8 #include "hyper.h"
9 namespace hr {
10
quitsaves()11 EX bool quitsaves() {
12 if(casual) return false;
13 return (items[itOrbSafety] && CAP_SAVE && !arcm::in());
14 }
15
needConfirmationEvenIfSaved()16 EX bool needConfirmationEvenIfSaved() {
17 return canmove && (gold() >= 30 || tkills() >= 50) && !cheater;
18 }
19
needConfirmation()20 EX bool needConfirmation() {
21 if(casual) return needConfirmationEvenIfSaved() && turncount > save_turns + 10;
22 else return needConfirmationEvenIfSaved() && !quitsaves();
23 }
24
getgametime()25 EX int getgametime() {
26 return (int) (savetime + (timerstopped ? 0 : (time(NULL) - timerstart)));
27 }
28
getgametime_s(int timespent IS (getgametime ()))29 EX string getgametime_s(int timespent IS(getgametime())) {
30 char buf[20];
31 sprintf(buf, "%d:%02d", timespent/60, timespent % 60);
32 return buf;
33 }
34
35 EX bool display_yasc_codes;
36
timeline()37 string timeline() {
38 string s;
39 if(shmup::on)
40 s = XLAT("%1 knives (%2)", its(turncount), getgametime_s());
41 else {
42 s = XLAT("%1 turns (%2)", its(turncount), getgametime_s());
43 if(display_yasc_codes)
44 s+= XLAT(" YASC code: ") + its(yasc_code);
45 }
46 return s;
47 }
48
noaction()49 EX void noaction() {}
50
51 EX function<void()> cancel = noaction;
52
53 #if HDR
54 struct hint {
55 time_t last;
56 function<bool()> usable;
57 function<void()> display;
58 function<void()> action;
59 };
60 #endif
61
62 EX hint hints[] = {
63
64 {
65 0,
__anonde946d200102() 66 []() {
67 return !inv::on && items[localTreasureType()] >= 18 && in_full_game();
68 },
__anonde946d200202() 69 []() {
70 dialog::addHelp(XLAT(
71 "If you collect too many treasures in a given land, it will become "
72 "extremely dangerous. Try other lands once you have enough!"));
73 },
74 noaction},
75
76 {
77 0,
__anonde946d200302() 78 []() {
79 return !ISMOBILE;
80 },
__anonde946d200402() 81 []() {
82 dialog::addHelp(XLAT(
83 "Remember that you can right click almost anything for more information."));
84 #if ISMAC
85 dialog::addHelp(XLAT(
86 "(You can also use right Shift)\n\n"));
87 #endif
88 },
89 noaction},
90
91 {
92 0,
__anonde946d200502() 93 []() { return !canmove; },
__anonde946d200602() 94 []() {
95 dialog::addHelp(XLAT(
96 "Want to understand the geometry in HyperRogue? Try the Guided Tour!"
97 ));
98 dialog::addBreak(50);
99 dialog::addItem(XLAT("guided tour"), 'z');
100 },
__anonde946d200702() 101 []() {
102 #if CAP_TOUR
103 tour::start();
104 #else
105 addMessage("Not in this version");
106 #endif
107 }},
108
109 {
110 0,
__anonde946d200802() 111 []() { return !inv::on && in_full_game(); },
__anonde946d200902() 112 []() {
113 dialog::addHelp(XLAT(
114 "Collecting 25 treasures in a given land may be dangerous, "
115 "but allows magical Orbs of this land to appear in other places!"
116 ));
117 },
118 noaction},
119
120 {
121 0,
__anonde946d200a02() 122 []() { return !canmove; },
__anonde946d200b02() 123 []() {
124 dialog::addInfo(XLAT(
125 "Press ESC to view this screen during the game."
126 ));
127 },
128 noaction
129 },
130 {
131 0,
__anonde946d200c02() 132 []() { return in_full_game(); },
__anonde946d200d02() 133 []() {
134 dialog::addInfo(
135 #if ISMOBILE
136 XLAT("The 'world overview' shows all the lands in HyperRogue.")
137 #else
138 XLAT("Press 'o' to see all the lands in HyperRogue.")
139 #endif
140 );
141 dialog::addBreak(50);
142 dialog::addItem(XLAT("world overview"), 'z');
143 },
__anonde946d200e02() 144 []() {
145 pushScreen(showOverview);
146 }},
147 {
148 0,
__anonde946d200f02() 149 []() { return !canmove; },
__anonde946d201002() 150 []() {
151 dialog::addHelp(XLAT(
152 "Want another type of game? Want more challenge?\n"
153 "HyperRogue has many special modes and challenges that "
154 "significantly change the gameplay. Try them!"
155 ));
156 dialog::addBreak(50);
157 dialog::addItem(XLAT("special game modes"), 'z');
158 },
__anonde946d201102() 159 []() {
160 pushScreen(showChangeMode);
161 }},
162
163 {
164 0,
__anonde946d201202() 165 []() { return true; },
__anonde946d201302() 166 []() {
167 dialog::addInfo(XLAT(
168 "Hyperbolic geometry can be shown in many ways."
169 ));
170 dialog::addBreak(50);
171 dialog::addItem(XLAT("special display modes"), 'z');
172 },
__anonde946d201402() 173 []() {
174 pushScreen(models::model_menu);
175 }},
176
177 {
178 0,
__anonde946d201502() 179 []() { return !canmove && !inv::on; },
__anonde946d201602() 180 []() {
181 dialog::addHelp(XLAT(
182 "You do not want to lose the game from a single mistake?\n"
183 "Do you want to use the Orbs strategically?\n"
184 "Try the Orb Strategy mode!")
185 );
186 dialog::addBreak(50);
187 dialog::addItem(XLAT("Orb Strategy mode"), 'z');
188 },
__anonde946d201702() 189 []() {
190 #if CAP_INV
191 restart_game(rg::inv);
192 #endif
193 }
194 },
195 {
196 0,
__anonde946d201802() 197 []() { return CAP_RUG && geometry == gNormal; },
__anonde946d201902() 198 []() {
199 dialog::addHelp(XLAT(
200 "Do you think you are playing on a ball? "
201 "This is far from the truth!\n"
202 ));
203 dialog::addBreak(50);
204 dialog::addItem(XLAT("hypersian rug mode"), 'z');
205 },
__anonde946d201a02() 206 [] () {
207 #if CAP_RUG
208 popScreen();
209 int wm, mm;
210 rug::init();
211 wm = vid.wallmode;
212 mm = vid.monmode;
213 vid.wallmode = 3;
214 vid.monmode = 2;
215 cancel = [wm, mm] () {
216 rug::close();
217 vid.wallmode = wm;
218 vid.monmode = mm;
219 };
220 #endif
221 }
222 },
223
224 {
225 0,
__anonde946d201c02() 226 []() { return !canmove && geometry == gNormal && celldist(cwt.at) >= 50; },
__anonde946d201d02() 227 []() {
228 dialog::addHelp(XLAT(
229 "Did you know that the path you take during the game "
230 "is usually very close to a straight line?\n"
231 ));
232 dialog::addBreak(50);
233 dialog::addItem(XLAT("Show me!"), 'z');
234 },
__anonde946d201e02() 235 [] () {
236 popScreen();
237 auto m = pmodel;
238 pmodel = mdBand;
239 int r = models::rotation;
240 bool h = history::includeHistory;
241 models::rotation = 0;
242 history::includeHistory = true;
243 history::create_playerpath();
244 cancel = [m,r,h] () {
245 history::clear(); pmodel = m;
246 models::rotation = r;
247 history::includeHistory = h;
248 fullcenter(); };
249 }
250 },
251
252 {
253 0,
__anonde946d202002() 254 []() { return !canmove && sizes_known() && celldist(cwt.at) >= 50; },
__anonde946d202102() 255 []() {
256 int d = celldist(cwt.at);
257 string s = expansion.approximate_descendants(d, 10000);
258 dialog::addHelp(XLAT(
259 "You are %1 cells away from the starting point, or "
260 "the place where you used an Orb of Safety last time. "
261 "There are %2 such cells.\n",
262 its(d), s
263 ));
264 dialog::addBreak(50);
265 dialog::addItem(XLAT("expansion"), 'z');
266 },
__anonde946d202202() 267 [] () {
268 viewdists = !viewdists;
269 popScreen();
270 cancel = [] () { viewdists = false; };
271 }
272 },
273
274 {
275 0,
__anonde946d202402() 276 []() { return !canmove && showHalloween(); },
__anonde946d202502() 277 []() {
278 dialog::addItem(XLAT("Halloween mini-game"), 'z');
279 },
__anonde946d202602() 280 [] () {
281 if(!sphere) {
282 resetModes();
283 stop_game();
284 specialland = laHalloween;
285 set_geometry(gSphere);
286 start_game();
287 pconf.alpha = 999;
288 pconf.scale = 998;
289 }
290 else {
291 resetModes();
292 pconf.alpha = 1;
293 pconf.scale = 1;
294 }
295 }
296 },
297
__anonde946d202702() 298 {-1, []() { return false; }, noaction, noaction}
299 };
300
301 EX int hinttoshow;
302
contstr()303 string contstr() {
304 return canmove ? XLAT("continue") : XLAT("see how it ended");
305 }
306
nextHyperstone()307 eLand nextHyperstone() {
308 generateLandList(isLandIngame);
309 for(eLand l: landlist)
310 if(items[treasureType(l)] < R10 && !isCrossroads(l) && l != laPrincessQuest && l != laCamelot)
311 return l;
312 if(items[itHyperstone] >= 10) return laNone;
313 return laCrossroads;
314 }
315
showMission()316 EX void showMission() {
317
318 cmode = sm::DOTOUR | sm::MISSION | sm::CENTER;
319 gamescreen(1); drawStats();
320
321 dialog::init(
322 #if CAP_TOUR
323 tour::on ? (canmove ? XLAT("guided tour") : XLAT("GAME OVER")) :
324 #endif
325 (cheater && !autocheat)? XLAT("It is a shame to cheat!") :
326 racing::on ? "racing mode" :
327 (canmove && princess::challenge) ? XLAT("%1 Challenge", moPrincess) :
328 canmove ? XLAT("Quest status") :
329 XLAT("GAME OVER"),
330 0xC00000, 200, 100
331 );
332 keyhandler = handleKeyQuit;
333
334 #if CAP_COMPLEX2
335 bool sweeper = mine::in_minesweeper();
336 #else
337 const bool sweeper = false;
338 #endif
339
340 if(!peace::on && !racing::on && !sweeper && !in_lovasz())
341 dialog::addInfo(XLAT("Your score: %1", its(gold())));
342 if(!peace::on && !racing::on && !sweeper && !in_lovasz())
343 dialog::addInfo(XLAT("Enemies killed: %1", its(tkills())));
344
345 #if CAP_TOUR
346 if(tour::on) ; else
347 #endif
348 if(items[itOrbYendor]) {
349 dialog::addInfo(XLAT("Orbs of Yendor found: %1", its(items[itOrbYendor])), iinf[itOrbYendor].color);
350 dialog::addInfo(XLAT("CONGRATULATIONS!"), iinf[itOrbYendor].color);
351 }
352 #if CAP_COMPLEX2
353 else if(mine::in_minesweeper()) {
354 int to_uncover = kills[moBomberbird];
355 if(to_uncover) {
356 dialog::addInfo(XLAT("Uncover all cells which do not contain mines"));
357 dialog::addInfo(XLAT("Cells to uncover: %1", its(to_uncover)));
358 }
359 else {
360 dialog::addInfo(XLAT("CONGRATULATIONS!"), iinf[itOrbYendor].color);
361 dialog::addInfo(XLAT("You won in %1", getgametime_s(mine::victory_time)));
362 }
363 }
364 #endif
365 else if(in_lovasz()) {
366 int score = 0, all = 0;
367 for(cell *c: currentmap->allcells()) {
368 if(c->wall == waChasm || c->item == itOrbInvis)
369 score++;
370 all++;
371 }
372 dialog::addInfo(XLAT("Dropped floors: %1/%2", its(score), its(all)));
373 if(score == all) dialog::addInfo(XLAT("CONGRATULATIONS!"), iinf[itOrbYendor].color);
374 if(score == all && geometry == gKleinQuartic && variation == eVariation::untruncated && gp::param == gp::loc(1,1))
375 achievement_gain_once("LOVASZ", rg::special_geometry);
376 }
377 else {
378 if(0)
379 ;
380 #if CAP_TOUR
381 else if(tour::on)
382 ;
383 #endif
384 else if(racing::on) ;
385 else if(princess::challenge)
386 dialog::addInfo(XLAT("Follow the Mouse and escape with %the1!", moPrincess));
387 else if(!in_full_game()) ;
388 else if(casual && savecount == 0) {
389 dialog::addInfo(XLAT("Find an Orb of Safety to save your game"));
390 }
391 else if(gold() < R30)
392 dialog::addInfo(XLAT("Collect %1 $$$ to access more worlds", its(R30)));
393 else if(gold() < R60)
394 dialog::addInfo(XLAT("Collect %1 $$$ to access even more lands", its(R60)));
395 else if(!landUnlocked(laHell))
396 dialog::addInfo(XLAT("Collect at least %1 treasures in each of %2 types to access Hell", its(R10), its(lands_for_hell())));
397 else if(items[itHell] < R10)
398 dialog::addInfo(XLAT("Collect at least %1 Demon Daisies to find the Orbs of Yendor", its(R10)));
399 else if(isize(yendor::yi) == 0)
400 dialog::addInfo(XLAT("Look for the Orbs of Yendor in Hell or in the Crossroads!"));
401 else
402 dialog::addInfo(XLAT("Unlock the Orb of Yendor!"));
403 }
404
405 if(!timerstopped && !canmove) {
406 savetime += time(NULL) - timerstart;
407 timerstopped = true;
408 }
409 if(canmove && !timerstart)
410 timerstart = time(NULL);
411
412 if(princess::challenge) ;
413 #if CAP_TOUR
414 else if(tour::on) ;
415 #endif
416 else if(peace::on) ;
417 else if(racing::on) ;
418 else if(!in_full_game()) ;
419 else if(tkills() < R100)
420 dialog::addInfo(XLAT("Defeat %1 enemies to access the Graveyard", its(R100)));
421 else if(kills[moVizier] == 0 && (items[itFernFlower] < U5 || items[itGold] < U5))
422 dialog::addInfo(XLAT("Kill a Vizier in the Palace to access Emerald Mine"));
423 else if(items[itEmerald] < U5)
424 dialog::addInfo(XLAT("Collect 5 Emeralds to access Camelot"));
425 else if(landUnlocked(laHell) && ls::any_order()) {
426 eLand l = nextHyperstone();
427 if(l)
428 dialog::addInfo(
429 l ? XLAT("Hyperstone Quest: collect at least %3 points in %the2", treasureType(l), l, its(R10))
430 : XLAT("Hyperstone Quest: collect at least %3 %1 in %the2", treasureType(l), l, its(R10))
431 );
432 else
433 dialog::addInfo(XLAT("Hyperstone Quest completed!"), iinf[itHyperstone].color);
434 }
435 else dialog::addInfo(XLAT("Some lands unlock at specific treasures or kills"));
436 if(cheater && !autocheat) {
437 dialog::addInfo(XLAT("you have cheated %1 times", its(cheater)), 0xFF2020);
438 }
439 else if(!racing::on) {
440 dialog::addInfo(timeline(), dialog::dialogcolor);
441 }
442
443 dialog::addBreak(100);
444
445 #if CAP_TOUR
446 if(!tour::on) {
447 hints[hinttoshow].display();
448 dialog::addBreak(100);
449 }
450 #endif
451
452 bool intour = false;
453
454 #if CAP_TOUR
455 intour = tour::on;
456 #endif
457
458 if(intour) {
459 #if CAP_TOUR
460 if(canmove) {
461 if(sphere) {
462 dialog::addItem(XLAT("return to your game"), '1');
463 dialog::addItem(pconf.alpha < 2 ? XLAT("orthogonal projection") : XLAT("stereographic projection"), '3');
464 }
465 else if(euclid) {
466 dialog::addItem(XLAT("return to your game"), '2');
467 dialog::addBreak(100);
468 }
469 else {
470 dialog::addItem(XLAT("spherical geometry"), '1');
471 dialog::addItem(XLAT("Euclidean geometry"), '2');
472 }
473 // dialog::addItem(XLAT("more curved hyperbolic geometry"), '3');
474 }
475 if(!items[itOrbTeleport])
476 dialog::addItem(XLAT("teleport away"), '4');
477 else if(!items[itOrbAether])
478 dialog::addItem(XLAT("move through walls"), '4');
479 else
480 dialog::addItem(XLAT("flash"), '4');
481 if(canmove) {
482 if(tour::slidecommand != "")
483 dialog::addItem(tour::slidecommand, '5');
484 dialog::addItem(XLAT("static mode"), '6');
485 dialog::addItem(XLAT("enable/disable texts"), '7');
486 dialog::addItem(XLAT("next slide"), SDLK_RETURN);
487 dialog::addItem(XLAT("previous slide"), SDLK_BACKSPACE);
488 dialog::addItem(XLAT("list of slides"), '9');
489 dialog::addItem(XLAT("leave the tour mode"), '0');
490 }
491 else
492 dialog::addBreak(200);
493 dialog::addItem(XLAT("main menu"), 'v');
494 dialog::addItem("continue", SDLK_ESCAPE);
495 #endif
496 }
497 else {
498 dialog::addItem(contstr(), SDLK_ESCAPE);
499 dialog::addItem(XLAT("main menu"), 'v');
500 dialog::addItem(XLAT("restart"), SDLK_F5);
501 if(inv::on && items[itInventory])
502 dialog::addItem(XLAT("inventory"), 'i');
503 if(racing::on)
504 dialog::addItem(XLAT("racing menu"), 'o');
505 #if !ISMOBILE
506 dialog::addItem(quitsaves() ? XLAT("save") : XLAT("quit"), SDLK_F10);
507 #endif
508 if(casual || ISMOBILE) {
509 if(savecount)
510 dialog::addItem(XLAT("load (%1 turns passed)", its(turncount - save_turns)), SDLK_F9);
511 else
512 dialog::addItem(XLAT("how to find an Orb of Safety?"), SDLK_F9);
513 }
514 #if CAP_ANDROIDSHARE
515 dialog::addItem(XLAT("SHARE"), 's'-96);
516 #endif
517 }
518 dialog::addItem(XLAT("message log"), 'l');
519
520 dialog::display();
521 }
522
safety_help()523 EX string safety_help() {
524 return XLAT(
525 "To save the game you need an Orb of Safety.\n\n"
526 "Orbs of Safety appear:\n\n"
527 "* in the Crossroads and the Land of Eternal Motion, after you collect %1 Phoenix Feathers in the Land of Eternal Motion\n\n"
528 "* in the Ocean after you unlock it (%2 treasures)\n\n"
529 "* in the Prairie after you unlock it (%3 treasures)\n\n",
530 its(inv::on ? 25 : 10),
531 its(R30), its(R90)
532 );
533 }
534
535
handleKeyQuit(int sym,int uni)536 EX void handleKeyQuit(int sym, int uni) {
537 dialog::handleNavigation(sym, uni);
538 // ignore the camera movement keys
539
540 #if CAP_RUG
541 if(rug::rugged && (sym == SDLK_UP || sym == SDLK_DOWN || sym == SDLK_PAGEUP || sym == SDLK_PAGEDOWN ||
542 sym == SDLK_RIGHT || sym == SDLK_LEFT))
543 sym = 0;
544 #endif
545
546 if(sym == SDLK_RETURN || sym == SDLK_KP_ENTER || sym == SDLK_F10) {
547 if(needConfirmation()) pushScreen([] {
548 dialog::confirm_dialog(
549 XLAT("This will exit HyperRogue without saving your current game. Are you sure?") + "\n\n" +
550 safety_help(),
551 [] {
552 quitmainloop = true;
553 });
554 });
555 else quitmainloop = true;
556 }
557 else if(uni == 'r' || sym == SDLK_F5) dialog::do_if_confirmed([] {
558 restart_game(rg::nothing);
559 msgs.clear();
560 });
561 else if(uni == 'v') popScreenAll(), pushScreen(showMainMenu);
562 else if(uni == 'l') popScreenAll(), pushScreen(showMessageLog), messagelogpos = isize(gamelog);
563 else if(uni == 'z') hints[hinttoshow].action();
564 #if CAP_SAVE
565 else if(sym == SDLK_F9) {
566 if(casual && savecount) {
567 stop_game();
568 load_last_save();
569 start_game();
570 }
571 else
572 gotoHelp(safety_help());
573 }
574 #endif
575 else if(sym == SDLK_F3 || (sym == ' ' || sym == SDLK_HOME))
576 fullcenter();
577 else if(uni == 'o') get_o_key().second();
578 #if CAP_INV
579 else if(uni == 'i' && inv::on)
580 pushScreen(inv::show);
581 #endif
582 #if CAP_SAVE
583 else if(uni == 't') {
584 msgs.clear();
585 scores::load();
586 }
587 #endif
588
589 else if(doexiton(sym, uni) && !didsomething) {
590 popScreen();
591 msgs.clear();
592 if(!canmove) {
593 addMessage(XLAT("GAME OVER"));
594 addMessage(timeline());
595 }
596 }
597 }
598
counthints()599 EX int counthints() {
600 for(int h=0;; h++) if(hints[h].last < 0) return h;
601 }
602
showMissionScreen()603 EX void showMissionScreen() {
604 cancel(); cancel = noaction;
605 popScreenAll();
606 achievement_final(false);
607
608 if(daily::on) {
609 #if CAP_DAILY
610 pushScreen(daily::showMenu);
611 #endif
612 }
613 else
614 pushScreen(showMission);
615
616 #if CAP_TOUR
617 if(!tour::on)
618 #endif
619 {
620 int ch = counthints();
621 hinttoshow = ch;
622 int h;
623 for(h=0; h < ch; h++) {
624 if(!hints[h].usable()) continue;
625 if(hinttoshow == ch || hints[h].last < hints[hinttoshow].last) hinttoshow = h;
626 }
627 hints[hinttoshow].last = time(NULL);
628 }
629
630 dialog::highlight_text = contstr();
631 }
632
633 }
634