1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-11-24 21:37:18 +00:00
hyperrogue/menus.cpp

1001 lines
30 KiB
C++
Raw Normal View History

2016-08-26 09:58:03 +00:00
// HyperRogue menus
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
2017-03-23 10:53:57 +00:00
#include "dialogs.cpp"
2017-07-12 16:03:53 +00:00
// -- overview --
2017-03-23 10:53:57 +00:00
#define BLACKISH 0x404040
#define REDDISH 0x400000
ld whatever = 0;
2017-07-10 18:47:38 +00:00
int PREC(ld x) {
ld sh = shiftmul;
if(sh > -.2 && sh < .2) x = 10.01;
return int(shiftmul * x);
2017-07-04 13:38:33 +00:00
}
2017-07-10 18:47:38 +00:00
2016-08-26 09:58:03 +00:00
void showOverview() {
cmode = sm::ZOOMABLE | sm::OVERVIEW;
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"show overview\n"));
if(mapeditor::infix != "")
mouseovers = mapeditor::infix;
else {
mouseovers = XLAT("world overview");
mouseovers += " ";
mouseovers += XLAT(" kills: %1/%2", its(tkills()), its(killtypes()));
mouseovers += XLAT(" $$$: %1", its(gold()));
if(hellUnlocked()) {
int i1, i2; countHyperstoneQuest(i1, i2);
mouseovers += XLAT(" Hyperstone: %1/%2", its(i1), its(i2));
}
else
mouseovers += XLAT(" Hell: %1/9", its(orbsUnlocked()));
2016-08-26 09:58:03 +00:00
}
2017-03-23 10:53:57 +00:00
bool pages;
2016-08-26 09:58:03 +00:00
2017-11-03 18:31:42 +00:00
generateLandList(isLandValid);
if(mapeditor::infix != "") {
2017-11-03 18:31:42 +00:00
vector<eLand> filtered;
for(eLand l: landlist) {
string s = dnameof(l);
s += "@";
s += dnameof(treasureType(l));
s += "@";
s += dnameof(nativeOrbType(l));
if(mapeditor::hasInfix(s))
2017-11-03 18:31:42 +00:00
filtered.push_back(l);
}
2017-11-03 18:31:42 +00:00
if(filtered.size())
landlist = filtered;
}
2017-11-03 18:31:42 +00:00
int nl = size(landlist), nlm;
int lstart = 0;
if(nl > 30) {
2017-03-23 10:53:57 +00:00
pages = true;
2017-11-03 18:31:42 +00:00
lstart += dialog::handlePage(nl, nlm, (nl+1)/2);
2017-03-23 10:53:57 +00:00
}
else nlm = nl;
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
int vf = min((vid.yres-64-vid.fsize*2) / nlm, vid.xres/40);
2017-11-03 18:31:42 +00:00
eLand curland = getLandForList(cwt.c);
2016-08-26 09:58:03 +00:00
getcstat = '0';
for(int i=0; i<nl; i++) {
2017-11-03 18:31:42 +00:00
eLand l = landlist[lstart + i];
2016-08-26 09:58:03 +00:00
int xr = vid.xres / 64;
2017-03-23 10:53:57 +00:00
int i0 = 56 + vid.fsize + i * vf;
2016-08-26 09:58:03 +00:00
int col;
2017-03-23 10:53:57 +00:00
if(landUnlocked(l)) col = linf[l].color; else col = 0x404040;
if(chaosmode && noChaos(l)) col = 0x400000;
2016-08-26 09:58:03 +00:00
if(l == curland)
2017-07-04 13:38:33 +00:00
displayfrZ(1, i0, 1, vf-4, "*", forecolor, 0);
2017-03-23 10:53:57 +00:00
if(displayfrZ(xr*1, i0, 1, vf-4, XLAT1(linf[l].name), col, 0))
2016-08-26 09:58:03 +00:00
getcstat = 1000 + l;
eItem it = treasureType(l);
int lv = items[it] * landMultiplier(l);
if(lv >= 25) col = 0xFFD500;
else if(lv >= 10) col = 0x00D500;
else if(items[it]) col = 0xC0C0C0;
2017-03-23 10:53:57 +00:00
else col = BLACKISH;
if(chaosmode && noChaos(l)) col = REDDISH;
int c8 = (vf+2)/3;
if(displayfrZ(xr*24-c8*6, i0, 1, vf-4, its(items[it]), col, 16))
2016-08-26 09:58:03 +00:00
getcstat = 2000+it;
if(!cheater)
2017-03-23 10:53:57 +00:00
if(displayfrZ(xr*24, i0, 1, vf-4, its(hiitems[modecode()][it]), col, 16))
2016-08-26 09:58:03 +00:00
getcstat = 2000+it;
2017-03-23 10:53:57 +00:00
if(items[it]) col = iinf[it].color; else col = BLACKISH;
if(chaosmode && noChaos(l)) col = REDDISH;
if(displayfrZ(xr*24+c8*4, i0, 1, vf-4, s0 + iinf[it].glyph, col, 16))
2016-08-26 09:58:03 +00:00
getcstat = 2000+it;
2017-03-23 10:53:57 +00:00
if(displayfrZ(xr*24+c8*5, i0, 1, vf-4, XLAT1(iinf[it].name), col, 0))
2016-08-26 09:58:03 +00:00
getcstat = 2000+it;
eItem io = nativeOrbType(l);
2016-08-26 09:58:03 +00:00
if(io == itShard) {
2017-03-23 10:53:57 +00:00
if(items[it] >= 10) col = winf[waMirror].color; else col = BLACKISH;
if(chaosmode && noChaos(l)) col = REDDISH;
if(displayfrZ(xr*46, i0, 1, vf-4, XLAT1(winf[waMirror].name), col, 0))
2016-08-26 09:58:03 +00:00
getcstat = 3000+waMirror;
if(getcstat == 3000+waMirror)
mouseovers = XLAT(
olrDescriptions[getOLR(io, cwt.c->land)], cwt.c->land, it, treasureType(cwt.c->land));
}
else if(io) {
if(lv >= 25) col = 0xFFD500;
else if(lv >= 10) col = 0xC0C0C0;
2017-03-23 10:53:57 +00:00
else col = BLACKISH;
if(chaosmode && noChaos(l)) col = REDDISH;
if(displayfrZ(xr*46-c8*4, i0, 1, vf-4, its(items[io]), col, 16))
2016-08-26 09:58:03 +00:00
getcstat = 2000+io;
2017-03-23 10:53:57 +00:00
if(lv >= 10) col = iinf[io].color; else col = BLACKISH;
if(chaosmode && noChaos(l)) col = REDDISH;
if(displayfrZ(xr*46-c8, i0, 1, vf-4, s0 + iinf[io].glyph, col, 16))
2016-08-26 09:58:03 +00:00
getcstat = 2000+io;
2017-03-23 10:53:57 +00:00
if(displayfrZ(xr*46, i0, 1, vf-4, XLAT1(iinf[io].name), col, 0))
2016-08-26 09:58:03 +00:00
getcstat = 2000+io;
if(getcstat == 2000+io)
mouseovers = XLAT(
olrDescriptions[getOLR(io, curland)], curland, it, treasureType(curland));
}
}
2017-03-23 10:53:57 +00:00
dialog::displayPageButtons(3, pages);
2017-07-10 18:47:38 +00:00
keyhandler = [] (int sym, int uni) {
int umod = uni % 1000;
int udiv = uni / 1000;
if(udiv == 1 && umod < landtypes) {
const eLand l = eLand(umod);
gotoHelp(""); gotoHelpFor(l);
2017-10-08 22:21:39 +00:00
if(cheater) {
help_extension hex;
hex.key = 't';
hex.text = XLAT("teleport");
hex.action = [l] () {
2017-10-08 22:21:39 +00:00
cheater++;
bool princ = (l == laPrincessQuest);
if(princ) {
if(kills[moVizier] == 0) kills[moVizier] = 1;
princess::forceMouse = true;
princess::gotoPrincess = true;
cheatMoveTo(laPalace);
}
else cheatMoveTo(l);
canmove = true;
if(princ) fullcenter();
popScreen();
popScreen();
};
help_extensions.push_back(hex);
2016-08-26 09:58:03 +00:00
}
}
2017-07-10 18:47:38 +00:00
else if(udiv == 2 && umod < ittypes) {
2017-10-08 22:21:39 +00:00
gotoHelp(generateHelpForItem(eItem(umod)));
if(cheater) {
dialog::helpToEdit(items[umod], 0, 200, 10, 10);
dialog::reaction = [] () {
if(hardcore) canmove = true;
else checkmove();
cheater++;
};
2017-07-10 18:47:38 +00:00
}
2016-08-26 09:58:03 +00:00
}
2017-07-10 18:47:38 +00:00
else if(udiv == 3 && umod < walltypes) gotoHelp(generateHelpForWall(eWall(umod)));
else if(uni == SDLK_F1) gotoHelp(
"This displays all lands available in the game. "
"Bonus lands (available only in separate challenges) "
"are not included. Lands written in dark have to be "
"unlocked, and lands in dark red are unavailable "
"because of using special options. Click on any "
"land or item to get information about it. Hover over "
"an Orb to know its relation to the current land. "
"Cheaters can click to move between lands, and use the "
"mousewheel to gain or lose treasures and orbs quickly (Ctrl = precise, Shift = reverse)."
);
else if(dialog::handlePageButtons(uni)) ;
else if(mapeditor::editInfix(uni)) ;
2017-07-10 18:47:38 +00:00
else if(doexiton(sym, uni)) popScreen();
};
2016-08-26 09:58:03 +00:00
}
2017-07-12 16:03:53 +00:00
// -- main menu --
2016-08-26 09:58:03 +00:00
void showMainMenu() {
2017-07-10 18:47:38 +00:00
gamescreen(2);
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
getcstat = ' ';
dialog::init(XLAT("HyperRogue %1", VER), 0xC00000, 200, 100);
dialog::addItem(XLAT("basic configuration"), 'b');
2017-07-04 13:38:33 +00:00
dialog::addItem(XLAT("graphics configuration"), 'g');
dialog::addItem(XLAT("special display modes"), 'd');
dialog::addItem(XLAT("special game modes"), 'm');
2017-07-22 23:33:27 +00:00
#if CAP_SAVE
2017-03-23 10:53:57 +00:00
dialog::addItem(XLAT("local highscores"), 't');
2016-08-26 09:58:03 +00:00
#endif
2017-03-23 10:53:57 +00:00
dialog::addItem(XLAT("help"), 'h'); dialog::lastItem().keycaption += " / F1";
2016-08-26 09:58:03 +00:00
if(cheater)
2017-03-23 10:53:57 +00:00
dialog::addItem(XLAT("cheats"), 'c');
else dialog::addBreak(100);
dialog::addItem(XLAT("restart game"), 'r'); dialog::lastItem().keycaption += " / F5";
2016-08-26 09:58:03 +00:00
string q;
2017-07-22 23:33:27 +00:00
#if ISMOBILE==1
2017-03-23 10:53:57 +00:00
dialog::addItem(XLAT("visit the website"), 'q');
#else
q = quitsaves() ? "save" : "quit";
2016-08-26 09:58:03 +00:00
q = q + " the game";
2017-03-23 10:53:57 +00:00
dialog::addItem(XLAT(q), 'q'); dialog::lastItem().keycaption += " / F10";
#endif
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(canmove)
2016-08-26 09:58:03 +00:00
q = "review your quest";
else
2017-07-10 18:47:38 +00:00
q = "game over screen";
2017-03-23 10:53:57 +00:00
dialog::addItem(XLAT(q), SDLK_ESCAPE);
dialog::addItem(XLAT("world overview"), 'o');
2017-07-04 13:38:33 +00:00
if(inv::on)
dialog::addItem(XLAT("inventory"), 'i');
2017-07-22 23:33:27 +00:00
#if CAP_ROGUEVIZ
2017-07-04 13:38:33 +00:00
dialog::addItem(XLAT("rogueviz menu"), 'u');
2017-03-23 10:53:57 +00:00
#endif
2016-08-26 09:58:03 +00:00
2017-07-22 23:33:27 +00:00
#if ISMOBILE==1
#if CAP_ACHIEVE
2017-03-23 10:53:57 +00:00
dialog::addItem(XLAT("leaderboards/achievements"), '3');
#endif
2017-03-31 19:41:09 +00:00
#endif
2016-08-26 09:58:03 +00:00
2017-07-22 23:33:27 +00:00
#if CAP_ANDROIDSHARE
2017-03-23 10:53:57 +00:00
dialog::addItem("SHARE", 's'-96);
#endif
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
if(!canmove) q = "review the scene";
2016-08-26 09:58:03 +00:00
else if(turncount > 0) q = "continue game";
else q = "play the game!";
2017-03-23 10:53:57 +00:00
dialog::addItem(XLAT(q), ' ');
dialog::display();
2017-07-10 18:47:38 +00:00
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(sym == SDLK_F1 || sym == 'h') gotoHelp(help);
else if(sym == 'c' && cheater) pushScreen(showCheatMenu);
else if(sym == 'b') pushScreen(showBasicConfig);
else if(sym == 'g') pushScreen(showGraphConfig);
else if(sym == 'd') pushScreen(showDisplayMode);
else if(sym == 'm') pushScreen(showChangeMode);
2017-07-22 23:33:27 +00:00
#if CAP_SAVE
2017-07-12 16:03:53 +00:00
else if(sym == 't') scores::load();
2017-07-10 18:47:38 +00:00
#endif
else if(sym == 'r' || sym == SDLK_F5) {
restartGame();
}
2017-07-22 23:33:27 +00:00
else if(sym == 'q' || sym == SDLK_F10) {
#if ISMOBILE
extern void openURL();
openURL();
#else
2017-07-10 18:47:38 +00:00
quitmainloop = true;
2017-07-22 23:33:27 +00:00
#endif
}
2017-07-10 18:47:38 +00:00
else if(sym == 'o') {
clearMessages();
2017-07-22 23:33:27 +00:00
setAppropriateOverview();
2017-07-10 18:47:38 +00:00
}
2017-07-22 23:33:27 +00:00
#if CAP_INV
2017-07-10 18:47:38 +00:00
else if(sym == 'i') {
clearMessages();
2017-07-12 16:03:53 +00:00
pushScreen(inv::show);
2017-03-23 10:53:57 +00:00
}
2016-08-26 09:58:03 +00:00
#endif
2017-07-10 18:47:38 +00:00
else if(sym == SDLK_ESCAPE)
showMissionScreen();
2017-07-22 23:33:27 +00:00
#if ISMOBILE==1
2017-07-10 18:47:38 +00:00
#ifdef HAVE_ACHIEVEMENTS
else if(sym == '3') {
achievement_final(false);
pushScreen(leader::showMenu);
}
2017-07-04 13:38:33 +00:00
#endif
#endif
2017-07-22 23:33:27 +00:00
#if CAP_ROGUEVIZ
2017-07-10 18:47:38 +00:00
else if(uni == 'u') pushScreen(rogueviz::showMenu);
2017-07-04 13:38:33 +00:00
#endif
2017-07-10 18:47:38 +00:00
else if(doexiton(sym, uni)) {
popScreenAll();
msgs.clear();
2017-07-04 13:38:33 +00:00
}
2017-07-10 18:47:38 +00:00
};
2016-08-26 09:58:03 +00:00
}
2017-07-12 16:03:53 +00:00
// -- display modes --
2017-07-04 13:38:33 +00:00
void showDisplayMode() {
2017-07-12 17:50:39 +00:00
cmode = sm::SIDE;
gamescreen(0);
2016-08-26 09:58:03 +00:00
2017-07-04 13:38:33 +00:00
dialog::init(XLAT("special display modes"));
2016-08-26 09:58:03 +00:00
2017-07-04 13:38:33 +00:00
const char *wdmodes[6] = {"ASCII", "black", "plain", "Escher", "plain/3D", "Escher/3D"};
const char *mdmodes[6] = {"ASCII", "items only", "items and monsters", "high contrast",
"3D", "high contrast/3D"};
2017-03-23 10:53:57 +00:00
2017-07-04 13:38:33 +00:00
dialog::addBoolItem(XLAT("orthogonal projection"), vid.alpha >= 500, '1');
dialog::addBoolItem(XLAT("small Poincaré model"), vid.alpha == 1 && vid.scale < 1, '2');
dialog::addBoolItem(XLAT("big Poincaré model"), vid.alpha == 1 && vid.scale >= 1, '3');
dialog::addBoolItem(XLAT("Klein-Beltrami model"), vid.alpha == 0, '4');
dialog::addSelItem(XLAT("wall display mode"), XLAT(wdmodes[vid.wallmode]), '5');
2017-07-12 16:03:53 +00:00
if(getcstat == '5')
mouseovers = XLAT("also hold Alt during the game to toggle high contrast");
2017-07-04 13:38:33 +00:00
dialog::addBoolItem(XLAT("draw the grid"), (vid.grid), '6');
dialog::addBoolItem(XLAT("mark heptagons"), (vid.darkhepta), '7');
dialog::addSelItem(XLAT("3D configuration"), "", '9');
2017-03-23 10:53:57 +00:00
2017-07-04 13:38:33 +00:00
dialog::addSelItem(XLAT("scale factor"), fts(vid.scale), 'z');
dialog::addSelItem(XLAT("monster display mode"), XLAT(mdmodes[vid.monmode]), 'm');
2017-03-23 10:53:57 +00:00
dialog::addBreak(50);
2017-07-04 13:38:33 +00:00
2017-07-22 23:33:27 +00:00
#if CAP_EDIT
2017-07-04 13:38:33 +00:00
dialog::addBoolItem(XLAT("vector graphics editor"), (false), 'g');
2017-03-23 10:53:57 +00:00
#endif
2017-07-04 13:38:33 +00:00
// display modes
2017-07-22 23:33:27 +00:00
#if CAP_RUG
2017-07-04 13:38:33 +00:00
dialog::addBoolItem(XLAT("hypersian rug mode"), (rug::rugged), 'u');
#endif
2017-07-22 23:33:27 +00:00
#if CAP_MODEL
2017-07-04 13:38:33 +00:00
dialog::addBoolItem(XLAT("paper model creator"), (false), 'n');
#endif
dialog::addBoolItem(XLAT("conformal/history mode"), (conformal::on), 'a');
// dialog::addBoolItem(XLAT("expansion"), viewdists, 'x');
2017-07-04 13:38:33 +00:00
showAllConfig();
2017-03-23 10:53:57 +00:00
dialog::display();
2017-07-10 18:47:38 +00:00
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
char xuni = uni;
if((xuni >= 'A' && xuni <= 'Z') || (xuni >= 1 && xuni <= 26)) xuni |= 32;
if(xuni == 'p') projectionDialog();
if(xuni == 'z') {
dialog::editNumber(vid.scale, .001, 1000, .1, 1, XLAT("scale factor"),
XLAT("Scale the displayed model."));
dialog::scaleLog();
}
if(xuni == 'm') { vid.monmode += 60 + (shiftmul > 0 ? 1 : -1); vid.monmode %= 6; }
if(xuni == '9') pushScreen(show3D);
2017-07-22 23:33:27 +00:00
#if CAP_EDIT
2017-07-10 18:47:38 +00:00
else if(xuni == 'g') {
pushScreen(mapeditor::showDrawEditor);
mapeditor::initdraw(cwt.c);
}
#endif
else if(xuni == 'x') {
viewdists = !viewdists;
}
2017-07-22 23:33:27 +00:00
#if CAP_RUG
2017-07-10 18:47:38 +00:00
else if(xuni == 'u') {
if(sphere) projectionDialog();
else rug::select();
}
#endif
else if(uni == 'a')
pushScreen(conformal::show);
2017-07-22 23:33:27 +00:00
#if CAP_MODEL
else if(xuni == 'n')
netgen::run();
2017-07-10 18:47:38 +00:00
#endif
else gmodekeys(sym, uni);
handleAllConfig(sym, xuni);
};
2016-08-26 09:58:03 +00:00
}
2017-07-12 16:03:53 +00:00
// -- game modes --
2017-07-04 13:38:33 +00:00
void switchHardcore() {
if(hardcore && !canmove) {
restartGame();
hardcore = false;
}
else if(hardcore && canmove) { hardcore = false; }
else { hardcore = true; canmove = true; hardcoreAt = turncount; }
if(hardcore)
addMessage("One wrong move, and it is game over!");
else
addMessage("Not so hardcore?");
2017-07-22 23:33:27 +00:00
if(pureHardcore()) popScreenAll();
2016-08-26 09:58:03 +00:00
}
void showChangeMode() {
2017-07-10 18:47:38 +00:00
gamescreen(3);
2017-03-23 10:53:57 +00:00
dialog::init(XLAT("special game modes"));
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
// gameplay modes
2017-07-22 23:33:27 +00:00
#if CAP_TOUR
2017-05-31 16:33:50 +00:00
dialog::addBoolItem(XLAT("Tutorial"), tour::on, 'T');
2017-04-08 15:18:29 +00:00
#endif
dialog::addBoolItem(XLAT("experiment with geometry"), geometry || nontruncated || viewdists, 'e');
2017-03-23 10:53:57 +00:00
dialog::addBoolItem(XLAT(SHMUPTITLE), (shmup::on || multi::players > 1), 's');
if(!shmup::on) dialog::addSelItem(XLAT("hardcore mode"),
2016-08-26 09:58:03 +00:00
hardcore && !pureHardcore() ? XLAT("PARTIAL") : ONOFF(hardcore), 'h');
2017-07-12 16:03:53 +00:00
if(getcstat == 'h')
mouseovers = XLAT("One wrong move and it is game over!");
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
multi::cpid = 0;
dialog::addBoolItem(XLAT("Chaos mode"), (chaosmode), 'C');
2017-07-22 23:33:27 +00:00
dialog::addBoolItem(XLAT("peaceful mode"), peace::on, 'p');
2017-07-16 21:00:55 +00:00
dialog::addBoolItem(XLAT("Orb Strategy mode"), (inv::on), 'i');
2017-07-12 16:03:53 +00:00
dialog::addBoolItem(XLAT("pure tactics mode"), (tactic::on), 't');
dialog::addBoolItem(XLAT("Yendor Challenge"), (yendor::on), 'y');
dialog::addBoolItem(XLAT("%1 Challenge", moPrincess), (princess::challenge), 'P');
dialog::addBoolItem(XLAT("random pattern mode"), (randomPatternsMode), 'r');
2017-03-23 10:53:57 +00:00
dialog::addBreak(50);
// cheating and map editor
dialog::addBoolItem(XLAT("cheat mode"), (cheater), 'c');
2017-07-22 23:33:27 +00:00
#if CAP_EDIT
2017-03-23 10:53:57 +00:00
dialog::addBoolItem(XLAT("map editor"), (false), 'm');
2016-08-26 09:58:03 +00:00
#endif
2017-03-23 10:53:57 +00:00
dialog::addBreak(50);
dialog::addItem(XLAT("return to the game"), 'v');
dialog::display();
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
char xuni = uni;
if(xuni == 'v' || sym == SDLK_ESCAPE) popScreen();
else if(uni == 'c') {
if(tactic::on && gold()) {
addMessage(XLAT("Not available in the pure tactics mode!"));
}
else if(!cheater) {
cheater++;
addMessage(XLAT("You activate your demonic powers!"));
2017-07-22 23:33:27 +00:00
#if ISMOBILE==0
2017-07-10 18:47:38 +00:00
addMessage(XLAT("Shift+F, Shift+O, Shift+T, Shift+L, Shift+U, etc."));
#endif
popScreen();
}
else {
popScreen();
firstland = princess::challenge ? laPalace : laIce;
restartGame();
}
2016-08-26 09:58:03 +00:00
}
2017-07-10 18:47:38 +00:00
2017-10-30 11:05:36 +00:00
else if(xuni == 'e')
runGeometryExperiments();
2017-07-10 18:47:38 +00:00
else if(xuni == 't') {
clearMessages();
pushScreen(tactic::showMenu);
}
else if(xuni == 'y') {
clearMessages();
if(yendor::everwon || autocheat)
pushScreen(yendor::showMenu);
else gotoHelp(yendor::chelp);
2016-08-26 09:58:03 +00:00
}
2017-07-12 16:03:53 +00:00
else if(xuni == 'p')
2017-07-10 18:47:38 +00:00
pushScreen(peace::showMenu);
else if(xuni == 'i') {
restartGame('i');
2016-08-26 09:58:03 +00:00
}
2017-07-22 23:33:27 +00:00
#if CAP_TOUR
2017-07-10 18:47:38 +00:00
else if(uni == 'T') {
tour::start();
2016-08-26 09:58:03 +00:00
}
2017-07-10 18:47:38 +00:00
#endif
else if(uni == 'C') {
if(chaosUnlocked) restartGame('C');
if(chaosmode) gotoHelp(
"In the Chaos mode, lands change very often, and "
"there are no walls between them. "
"Some lands are incompatible with this."
"\n\nYou need to reach Crossroads IV to unlock the Chaos mode."
);
2016-08-26 09:58:03 +00:00
}
2017-09-01 20:17:22 +00:00
else if(xuni == 'P') {
2017-07-10 18:47:38 +00:00
if(!princess::everSaved)
addMessage(XLAT("Save %the1 first to unlock this challenge!", moPrincess));
else
2017-09-17 10:35:32 +00:00
restartGame('p');
2017-07-10 18:47:38 +00:00
}
2017-07-22 23:33:27 +00:00
#if CAP_EDIT
2017-07-10 18:47:38 +00:00
else if(xuni == 'm') {
if(tactic::on)
addMessage(XLAT("Not available in the pure tactics mode!"));
else {
cheater++;
pushScreen(mapeditor::showMapEditor);
lastexplore = turncount;
addMessage(XLAT("You activate your terraforming powers!"));
}
}
#endif
else if(xuni == 's') {
2017-07-22 23:33:27 +00:00
#if ISMOBILE==1
2017-07-10 18:47:38 +00:00
restartGame('s');
#else
multi::shmupcfg = shmup::on;
pushScreen(shmup::showShmupConfig);
#endif
}
else if(xuni == 'h' && !shmup::on)
switchHardcore();
else if(xuni == 'r') {
firstland = laIce;
restartGame('r');
}
else if(doexiton(sym, uni))
popScreen();
};
2016-08-26 09:58:03 +00:00
}
2017-07-12 16:03:53 +00:00
// -- geometry menu --
2016-08-26 09:58:03 +00:00
int eupage = 0;
int euperpage = 21;
2017-10-28 23:57:34 +00:00
const char *curvenames[8] = {
"0", "1", "2", "extremely hyperbolic", "strongly hyperbolic", "strongly hyperbolic", "moderately hyperbolic", "weakly hyperbolic"
};
string euchelp =
"If you want to know how much the gameplay is affected by the "
"hyperbolic geometry in HyperRogue, this mode is for you!\n\n"
"You can try many different geometries here. We start by gluing "
"n-gons in such a way that k of them meet in every vertex. "
"Depending on n and k, this either folds into a sphere, unfolds into a plane, "
"or requires a hyperbolic space. The result may be then 'truncated' by "
"replacing each vertex by a 2k-gon. Furthermore, you can play "
"with quotient geometries. For example, the elliptic geometry is "
"obtained from the sphere by making the antipodes be the same point, "
"so you return to the same spot (but as a mirror image) after going there. "
2017-10-30 11:05:36 +00:00
"Have fun experimenting! "
"Achievements and leaderboards do not work in geometry experiments, "
"except some specific ones.\n\n"
"In standard geometry (truncated or not), you can play the full game, but in other geometries "
"you select a particular land. Lands are unlocked by visiting them in this "
"session, or permanently by collecting 25 treasure. Try Crossroads in Euclidean "
2017-10-30 11:05:36 +00:00
"or chaos mode in non-standard non-quotient hyperbolic to visit many lands. "
"Highlights:\n"
"* Crystal World and Warped Coast can be understood as extra geometries.\n"
"* Halloween is specially designed for spherical geometry.\n"
"* To see the difference, try Hunting Grounds in Euclidean -- it is impossible.\n";
int ewhichscreen = 2;
2017-07-12 17:50:39 +00:00
2016-08-26 09:58:03 +00:00
void showEuclideanMenu() {
2017-10-30 11:05:36 +00:00
cmode = sm::SIDE;
gamescreen(0);
2016-08-26 09:58:03 +00:00
int s = vid.fsize;
if(cheater) for(int i=0; i<landtypes; i++) landvisited[i] = true;
for(int i=0; i<landtypes; i++)
if(hiitemsMax(treasureType(eLand(i))) >= 25) landvisited[i] = true;
landvisited[laCrossroads] = true;
landvisited[laIce] = true;
2017-10-29 22:55:35 +00:00
landvisited[laHunting] = true;
2017-07-22 23:33:27 +00:00
landvisited[laMirrorOld] = true;
2017-03-23 10:53:57 +00:00
landvisited[laPrincessQuest] = cheater || princess::everSaved;
2016-08-26 09:58:03 +00:00
landvisited[laWildWest] = true;
2017-03-23 10:53:57 +00:00
landvisited[laHalloween] = true;
2017-10-28 23:57:34 +00:00
landvisited[laWarpCoast] = true;
landvisited[laGraveyard] = true;
2017-10-29 22:54:26 +00:00
landvisited[laDual] = true;
2017-03-23 10:53:57 +00:00
landvisited[laCA] = true;
2016-08-26 09:58:03 +00:00
// for(int i=2; i<lt; i++) landvisited[i] = true;
2017-10-28 23:57:34 +00:00
2017-10-30 11:05:36 +00:00
if(geometry == gNormal || ewhichscreen == 2) {
dialog::init(XLAT("experiment with geometry"));
int ts = ginf[geometry].sides;
int tv = ginf[geometry].vertex;
int tq = ginf[geometry].quotientstyle;
int nom = (nontruncated ? tv : tv+ts) * ((tq & qELLIP) ? 2 : 4);
2017-10-30 11:05:36 +00:00
int denom = (2*ts + 2*tv - ts * tv);
dialog::addSelItem(XLAT("land"), XLAT1(linf[specialland].name), '5');
dialog::addBreak(50);
2017-03-23 10:53:57 +00:00
2017-10-30 11:05:36 +00:00
for(int i=0; i<gGUARD; i++)
dialog::addBoolItem(XLAT(ginf[i].name), geometry == i, 'a'+i);
dialog::addBreak(50);
2017-10-28 23:57:34 +00:00
2017-10-30 11:05:36 +00:00
if(ts == 6 && tv == 3)
dialog::addSelItem(XLAT("truncated"), XLAT("does not matter"), 't');
2017-10-30 11:05:36 +00:00
else
dialog::addBoolItem(XLAT("truncated"), !nontruncated, 't');
2017-10-30 11:05:36 +00:00
dialog::addBreak(50);
int worldsize = denom ? nom/denom : 0;
if(tq & qTORUS) worldsize = torusconfig::qty;
if(tq & qZEBRA) worldsize = nontruncated ? 12 : 40;
2017-10-30 11:05:36 +00:00
if(tq & qFIELD) {
worldsize = size(currfp.matrices) / ts;
if(!nontruncated) worldsize = ((ts+tv)*worldsize) / tv;
2016-08-26 09:58:03 +00:00
}
2017-03-23 10:53:57 +00:00
dialog::addSelItem(XLAT("sides per face"), its(ts), 0);
dialog::addSelItem(XLAT("faces per vertex"), its(tv), 0);
2017-10-30 11:05:36 +00:00
string qstring = "none";
if(tq & qZEBRA) qstring = "zebra";
else if(tq & qFIELD) qstring = "field";
else if(tq & qELLIP) qstring = "torus";
else if(tq & qTORUS) qstring = "torus";
dialog::addSelItem(XLAT("quotient space"), XLAT(qstring), 0);
2017-10-30 11:05:36 +00:00
dialog::addSelItem(XLAT("size of the world"),
worldsize < 0 ? "exp(∞)*" + (nom%denom ? its(nom)+"/"+its(-denom) : its(-worldsize)):
worldsize == 0 ? "" :
its(worldsize),
'3');
2017-03-23 10:53:57 +00:00
2017-10-30 11:05:36 +00:00
switch(ginf[geometry].cclass) {
case 0:
dialog::addSelItem(XLAT("Curvature"), XLAT("hyperbolic"), 0);
2017-10-30 11:05:36 +00:00
break;
case 1:
dialog::addSelItem(XLAT("Curvature"), XLAT("flat"), 0);
2017-10-30 11:05:36 +00:00
break;
case 2:
dialog::addSelItem(XLAT("Curvature"), XLAT("spherical"), 0);
2017-10-30 11:05:36 +00:00
break;
}
2017-03-23 10:53:57 +00:00
2017-10-30 11:05:36 +00:00
if(sphere)
dialog::addBoolItem(XLAT("stereographic/orthogonal"), vid.alpha>10, '1');
else
dialog::addBoolItem(XLAT("Poincaré/Klein"), vid.alpha>.5, '1');
dialog::addItem(XLAT("help"), SDLK_F1);
dialog::addItem(XLAT("back"), '0');
2017-10-30 11:05:36 +00:00
dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni >= 'a' && uni < 'a'+gGUARD) {
targetgeometry = eGeometry(uni - 'a');
restartGame(geometry == targetgeometry ? 0 : 'g');
pushScreen(showEuclideanMenu);
}
else if(uni == 't') {
if(!euclid) {
2017-10-28 23:57:34 +00:00
restartGame('7');
2017-10-30 11:05:36 +00:00
pushScreen(showEuclideanMenu);
}
}
else if(uni == '2' || sym == SDLK_F1) gotoHelp(euchelp);
else if(uni == '3') { viewdists = !viewdists; if(viewdists) popScreenAll(); }
2017-10-30 11:05:36 +00:00
else if(uni == '1' && !euclid) {
if(sphere) {
if(vid.alpha < 10) { vid.alpha = 999; vid.scale = 998; }
else {vid.alpha = 1; vid.scale = .4; }
}
else {
if(vid.alpha > .5) { vid.alpha = 0; vid.scale = 1; }
else {vid.alpha = 1; vid.scale = 1; }
}
}
else if(uni == '5')
ewhichscreen ^= 3;
else if(doexiton(sym, uni))
popScreen();
};
}
else {
dialog::init(XLAT("experiment with geometry"));
2017-10-30 11:05:36 +00:00
string truncatenames[2] = {" (t)", " (n)"};
dialog::addSelItem(XLAT("geometry"), XLAT(ginf[geometry].name) + XLAT(truncatenames[nontruncated]), '5');
2017-10-30 11:05:36 +00:00
dialog::addBreak(50);
2017-11-03 18:31:42 +00:00
generateLandList(isLandValid);
2017-10-30 11:05:36 +00:00
for(int i=0; i<euperpage; i++) {
2017-11-03 18:31:42 +00:00
if(euperpage * eupage + i >= size(landlist)) { dialog::addBreak(100); break; }
eLand l = landlist[euperpage * eupage + i];
2017-10-30 11:05:36 +00:00
char ch;
if(i < 26) ch = 'a' + i;
else ch = 'A' + (i-26);
2017-11-03 18:31:42 +00:00
string validclasses[4] = {"", " (½)", "", " (!)"};
string s = XLAT1(linf[l].name);
2017-11-03 18:31:42 +00:00
2017-10-30 11:05:36 +00:00
if(landvisited[l]) {
2017-11-03 18:31:42 +00:00
dialog::addBoolItem(s, l == specialland, ch);
2017-10-30 11:05:36 +00:00
}
else {
2017-11-03 18:31:42 +00:00
dialog::addSelItem(s, XLAT("(locked)"), ch);
2017-07-10 18:47:38 +00:00
}
dialog::lastItem().color = linf[l].color;
dialog::lastItem().value += validclasses[isLandValid(l)];
2017-03-23 10:53:57 +00:00
}
2017-10-30 11:05:36 +00:00
dialog::addBreak(50);
if(chaosUnlocked && !quotient && !euclid && !sphere)
dialog::addItem(XLAT("Chaos mode"), '1');
dialog::addItem(XLAT("next page"), '-');
dialog::addItem(XLAT("help"), SDLK_F1);
dialog::addItem(XLAT("back"), '0');
2017-10-30 11:05:36 +00:00
dialog::display();
vid.fsize = s;
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
int lid;
if(uni >= 'a' && uni <= 'z') lid = uni - 'a';
else if(uni >= 'A' && uni <= 'Z') lid = 26 + uni - 'A';
else lid = -1;
if(lid >= 0) lid += euperpage * eupage;
if(uni == '5')
ewhichscreen ^= 3;
else if(uni == '-' || uni == PSEUDOKEY_WHEELUP || uni == PSEUDOKEY_WHEELDOWN) {
eupage++;
2017-11-03 18:31:42 +00:00
if(eupage * euperpage >= size(landlist)) eupage = 0;
2017-10-30 11:05:36 +00:00
}
else if(uni == '1') {
if(chaosUnlocked) {
restartGame('C');
pushScreen(showEuclideanMenu);
}
}
2017-11-03 18:31:42 +00:00
else if(lid >= 0 && lid < size(landlist)) {
eLand nland = landlist[lid];
2017-10-30 11:05:36 +00:00
if(landvisited[nland]) {
specialland = nland;
restartGame(tactic::on ? 't' : 0);
pushScreen(showEuclideanMenu);
}
}
else if(uni == '2' || sym == SDLK_F1) gotoHelp(euchelp);
else if(doexiton(sym, uni)) popScreen();
};
}
}
void runGeometryExperiments() {
2017-11-03 18:31:42 +00:00
specialland = getLandForList(cwt.c);
2017-10-30 11:05:36 +00:00
pushScreen(showEuclideanMenu);
2017-03-23 10:53:57 +00:00
}
2017-08-06 12:50:16 +00:00
bool showstartmenu;
bool showHalloween() {
time_t t = time(NULL);
struct tm tm = *localtime(&t);
int month = tm.tm_mon + 1;
int day = tm.tm_mday;
if(month == 10 && day >= 24) return true;
if(month == 11 && day <= 7) return true;
return false;
}
2017-08-06 12:50:16 +00:00
void showStartMenu() {
gamescreen(2);
getcstat = ' ';
dialog::init();
dialog::addInfo(XLAT("Welcome to HyperRogue!"));
dialog::addBreak(100);
dialog::addBigItem(XLAT("HyperRogue classic"), 'c');
dialog::addInfo(XLAT("explore the world, collect treasures"));
dialog::addInfo(XLAT("do not get checkmated"));
#if CAP_INV
dialog::addBreak(100);
dialog::addBigItem(XLAT("Orb Strategy mode"), 'i');
dialog::addInfo(XLAT("use your Orbs in tough situations"));
#endif
2017-08-06 14:17:13 +00:00
#if CAP_SHMUP_GOOD
2017-08-06 12:50:16 +00:00
dialog::addBreak(100);
dialog::addBigItem(XLAT("shoot'em up mode"), 's');
dialog::addInfo(XLAT("continuous spacetime"));
#if CAP_ACHIEVE
2017-08-06 12:50:16 +00:00
dialog::addInfo(XLAT("(most achievements are not available)"));
#endif
#endif
#if CAP_TOUR
dialog::addBreak(100);
dialog::addBigItem(XLAT("tutorial"), 't');
dialog::addInfo(XLAT("learn about hyperbolic geometry!"));
#endif
#if CAP_ROGUEVIZ && CAP_TOUR
dialog::addBreak(100);
dialog::addBigItem(XLAT("RogueViz"), 'r');
dialog::addInfo(XLAT("see the visualizations"));
#endif
if(showHalloween()) {
dialog::addBreak(100);
dialog::addBigItem(XLAT1("Halloween"), 'z');
dialog::addInfo(XLAT("Halloween mini-game"));
}
2017-08-06 12:50:16 +00:00
dialog::addBreak(100);
dialog::addBigItem(XLAT("main menu"), 'm');
dialog::addInfo(XLAT("more options"));
dialog::display();
clearMessages();
timerstart = time(NULL);
2017-08-06 12:50:16 +00:00
/*
initquickqueue();
int siz = min(vid.xres, vid.yres) / 8;
drawMonsterType(moPrincess, NULL, atscreenpos(siz,siz,siz) * spin(-M_PI/4), 0, 0);
drawMonsterType(moKnight, NULL, atscreenpos(vid.xres-siz,siz,siz) * spin(-3*M_PI/4), 0, 0);
drawItemType(itOrbYendor, NULL, atscreenpos(siz,vid.yres-siz,siz) * spin(M_PI/4), iinf[itOrbYendor].color, 0, false);
drawItemType(itKey, NULL, atscreenpos(siz,vid.yres-siz,siz) * spin(M_PI/4), iinf[itKey].color, 0, false);
drawItemType(itHyperstone, NULL, atscreenpos(vid.xres-siz,vid.yres-siz,siz) * spin(3*M_PI/4), iinf[itHyperstone].color, 0, false);
quickqueue();
*/
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni == 'o') uni = 'i';
if(uni == 'c' || uni == 'i' || uni == 's') {
popScreenAll();
if(inv::on != (uni == 'i')) restartGame('i');
if(shmup::on != (uni == 's')) restartGame('s');
2017-08-06 12:50:16 +00:00
clearMessages();
welcomeMessage();
stampbase = ticks;
}
else if(uni == 'z') {
popScreenAll();
resetModes();
stampbase = ticks;
if(!sphere) {
specialland = laHalloween;
targetgeometry = gSphere;
restartGame('g');
vid.alpha = 999;
vid.scale = 998;
}
2017-08-06 12:50:16 +00:00
}
#if CAP_TOUR
else if(uni == 't') {
popScreenAll();
tour::start();
}
#endif
#if CAP_ROGUEVIZ && CAP_TOUR
else if(uni == 'r') {
tour::slides = rogueviz::rvtour::rvslides;
2017-08-06 12:50:16 +00:00
popScreenAll();
tour::start();
}
#endif
else if(sym == 'm') {
popScreen();
pushScreen(showMainMenu);
}
else if(sym == SDLK_F10)
quitmainloop = true;
else if(sym == SDLK_F1)
gotoHelp(help);
else if(sym == SDLK_ESCAPE || sym == ' ') {
2017-08-06 12:50:16 +00:00
popScreen();
timerstart = time(NULL);
stampbase = ticks;
clearMessages();
welcomeMessage();
}
2017-08-06 12:50:16 +00:00
};
}
2017-07-12 16:03:53 +00:00
// -- overview --
2017-07-10 18:47:38 +00:00
void setAppropriateOverview() {
clearMessages();
if(viewdists)
runGeometryExperiments();
else if(tactic::on)
2017-07-10 18:47:38 +00:00
pushScreen(tactic::showMenu);
else if(yendor::on)
pushScreen(yendor::showMenu);
else if(peace::on)
pushScreen(peace::showMenu);
else if(geometry != gNormal && !chaosmode && !(geometry == gEuclid && isCrossroads(specialland))) {
2017-10-30 11:05:36 +00:00
runGeometryExperiments();
2017-10-28 23:57:34 +00:00
}
else {
mapeditor::infix = "";
2017-07-10 18:47:38 +00:00
pushScreen(showOverview);
}
2017-07-10 18:47:38 +00:00
}
int messagelogpos;
int timeformat;
int stampbase;
string gettimestamp(msginfo& m) {
char buf[128];
switch(timeformat) {
case 0:
return its(m.turnstamp);
case 1:
strftime(buf, 128, "%H:%M:%S", localtime(&m.rtstamp));
return buf;
case 2:
snprintf(buf, 128, "%2d:%02d", m.gtstamp/60, m.gtstamp % 60);
return buf;
case 3:
{ int t = m.stamp - stampbase;
bool sign = t < 0;
if(sign) t = -t;
snprintf(buf, 128, "%2d:%02d.%03d", t/60000, (t/1000) % 60, t % 1000);
string s = buf;
if(sign) s = "-"+s;
return s;
}
}
return "";
}
void showMessageLog() {
DEBB(DF_GRAPH, (debugfile,"show message log\n"));
int lines = vid.yres / vid.fsize - 2;
int maxpos = size(gamelog) - lines;
messagelogpos = min(messagelogpos, maxpos);
messagelogpos = max(messagelogpos, 0);
for(int y=0; y<lines && messagelogpos+y < size(gamelog); y++) {
msginfo& m = gamelog[messagelogpos+y];
displaystr(vid.fsize*8, vid.fsize*(y+1), 0, vid.fsize, fullmsg(m), 0xC0C0C0, 0);
displaystr(vid.fsize*7, vid.fsize*(y+1), 0, vid.fsize, gettimestamp(m), 0xC0C0C0, 16);
}
int i0 = vid.yres - vid.fsize;
int xr = vid.xres / 80;
string timeformats[5] = {"turns", "real time", "game time", "precise", "no time"};
displayButton(xr*70, i0, IFM("0 - ") + XLAT("back"), '0', 8);
displayButton(xr*10, i0, IFM("c - ") + XLAT("clear"), '0', 8);
displayButton(xr*40, i0, IFM("t - ") + XLAT(timeformats[timeformat]), '0', 8);
keyhandler = [lines, maxpos] (int sym, int uni) {
if(uni == PSEUDOKEY_WHEELDOWN) messagelogpos++;
else if(uni == PSEUDOKEY_WHEELUP) messagelogpos--;
else if(uni == SDLK_DOWN || uni == SDLK_KP2) messagelogpos++;
else if(uni == SDLK_UP || uni == SDLK_KP8) messagelogpos--;
else if(uni == SDLK_PAGEUP || uni == SDLK_KP9) messagelogpos -= lines;
else if(uni == SDLK_PAGEDOWN || uni == SDLK_KP3) messagelogpos -= lines;
else if(uni == 'c') gamelog.clear();
else if(uni == 't') { timeformat++; timeformat %= 5; }
else if(doexiton(sym, uni)) popScreen();
};
}