// -- geometry menu -- int eupage = 0; int euperpage = 21; 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. " "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 " "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; // extra information for field quotient extra configuration struct primeinfo { int p; int cells; bool squared; }; struct fgeomextra { eGeometry base; vector primes; int current_prime_id; fgeomextra(eGeometry b, int i) : base(b), current_prime_id(i) {} }; vector fgeomextras = { fgeomextra(gNormal, 3), fgeomextra(gOctagon, 1), fgeomextra(g45, 0), fgeomextra(g46, 3), fgeomextra(g47, 0) }; int current_extra = 0; void nextPrime(fgeomextra& ex) { dynamicval g(geometry, ex.base); int nextprime; if(size(ex.primes)) nextprime = ex.primes.back().p + 1; else nextprime = 2; while(true) { fieldpattern::fpattern fp(0); fp.Prime = nextprime; if(fp.solve() == 0) { fp.build(); ex.primes.emplace_back(primeinfo{nextprime, size(fp.matrices) / S7, fp.wsquare}); break; } nextprime++; } } void nextPrimes(fgeomextra& ex) { while(size(ex.primes) < 4) nextPrime(ex); } void enableFieldChange() { fgeomextra& gxcur = fgeomextras[current_extra]; fieldpattern::quotient_field_changed = true; nextPrimes(gxcur); dynamicval g(geometry, gQuotient2); ginf[geometry].sides = ginf[gxcur.base].sides; ginf[geometry].vertex = ginf[gxcur.base].vertex; ginf[geometry].distlimit = ginf[gxcur.base].distlimit; fieldpattern::current_quotient_field.init(gxcur.primes[gxcur.current_prime_id].p); } void showQuotientConfig() { gamescreen(2); dialog::init(XLAT("advanced configuration")); fgeomextra& gxcur = fgeomextras[current_extra]; for(int i=0; i= 'a' && uni < 'a' + size(fgeomextras)) current_extra = uni - 'a'; else if(uni >= 'A' && uni < 'A' + size(gxcur.primes)) gxcur.current_prime_id = uni - 'A'; else if(uni == 'p') nextPrime(gxcur); else if(uni == 'x' || uni == '\n') { targetgeometry = gxcur.base; restartGame('g'); enableFieldChange(); targetgeometry = gQuotient2; restartGame('g'); } else if(uni == 'c') { targetgeometry = gEuclid; restartGame('g'); fieldpattern::quotient_field_changed = false; targetgeometry = gQuotient2; restartGame('g'); } else if(doexiton(sym, uni)) popScreen(); }; dialog::display(); } void showTorusConfig() { cmode = sm::SIDE | sm::TORUSCONFIG; gamescreen(2); dialog::init(XLAT("advanced configuration")); dialog::addSelItem(XLAT("number of cells (n)"), its(torusconfig::newqty), 'n'); dialog::addSelItem(XLAT("cell bottom-right from 0 (d)"), its(torusconfig::newdy), 'd'); if(torusconfig::newqty % 3) dialog::addInfo("best if n is divisible by 3", 0x808080); if((torusconfig::newdy + 999999) % 3 != 2) dialog::addInfo("best if d+1 is divisible by 3", 0x808080); dialog::addSelItem(XLAT("scale factor"), fts(vid.scale), 'z'); #if CAP_RUG dialog::addBoolItem(XLAT("hypersian rug mode"), (rug::rugged), 'u'); #endif dialog::addItem("activate", 'a'); dialog::addItem("default", 'c'); keyhandler = [] (int sym, int uni) { if(uni == 'n') dialog::editNumber(torusconfig::newqty, 0, 1000, 3, torusconfig::def_qty, XLAT("number of cells (n)"), ""); else if(uni == 'd') dialog::editNumber(torusconfig::newdy, -1000, 1000, 3, -torusconfig::def_dy, XLAT("cell bottom-right from 0 (d)"), ""); else if((uni == 'a' || uni == '\n') && torusconfig::newqty >= 3 && abs(torusconfig::newdy) < torusconfig::newqty ) { targetgeometry = gEuclid; restartGame('g'); torusconfig::qty = torusconfig::newqty; torusconfig::dy = torusconfig::newdy; targetgeometry = gTorus; restartGame('g'); } else if(uni == 'c') { targetgeometry = gEuclid; restartGame('g'); torusconfig::qty = torusconfig::def_qty; torusconfig::dy = torusconfig::def_dy; targetgeometry = gTorus; restartGame('g'); } else if(uni == 'z') editScale(); else if(uni == 'u') rug::select(); else if(doexiton(sym, uni)) popScreen(); }; dialog::display(); } void showEuclideanMenu() { cmode = sm::SIDE; gamescreen(0); if(cheater) for(int i=0; i= 25) landvisited[i] = true; landvisited[laCrossroads] = true; landvisited[laIce] = true; landvisited[laHunting] = true; landvisited[laMirrorOld] = true; landvisited[laPrincessQuest] = cheater || princess::everSaved; landvisited[laWildWest] = true; landvisited[laHalloween] = true; landvisited[laWarpCoast] = true; landvisited[laGraveyard] = true; landvisited[laDual] = true; landvisited[laCA] = true; // for(int i=2; i10, '1'); else dialog::addBoolItem(XLAT("Poincaré/Klein"), vid.alpha>.5, '1'); if(torus || quotient == 2) dialog::addItem(XLAT("advanced parameters"), '4'); dialog::addItem(XLAT("help"), SDLK_F1); dialog::addItem(XLAT("back"), '0'); 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) { restartGame('7'); pushScreen(showEuclideanMenu); } } else if(uni == '2' || sym == SDLK_F1) gotoHelp(euchelp); else if(uni == '3') { viewdists = !viewdists; if(viewdists) popScreenAll(); } 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(uni == '4') { if(torus) torusconfig::newdy = torusconfig::dy, torusconfig::newqty = torusconfig::qty, pushScreen(showTorusConfig); if(quotient==2) pushScreen(showQuotientConfig); } else if(doexiton(sym, uni)) popScreen(); }; } else { dialog::init(XLAT("experiment with geometry")); string truncatenames[2] = {" (t)", " (n)"}; dialog::addSelItem(XLAT("geometry"), XLAT(ginf[geometry].name) + XLAT(truncatenames[nontruncated]), '5'); dialog::addBreak(50); generateLandList(isLandValid); for(int i=0; i= size(landlist)) { dialog::addBreak(100); break; } eLand l = landlist[euperpage * eupage + i]; char ch; if(i < 26) ch = 'a' + i; else ch = 'A' + (i-26); string validclasses[4] = {"", " (½)", "", " (!)"}; string s = XLAT1(linf[l].name); if(landvisited[l]) { dialog::addBoolItem(s, l == specialland, ch); } else { dialog::addSelItem(s, XLAT("(locked)"), ch); } dialog::lastItem().color = linf[l].color; dialog::lastItem().value += validclasses[isLandValid(l)]; } 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'); dialog::display(); 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++; if(eupage * euperpage >= size(landlist)) eupage = 0; } else if(uni == '1') { if(chaosUnlocked) { restartGame('C'); pushScreen(showEuclideanMenu); } } else if(lid >= 0 && lid < size(landlist)) { eLand nland = landlist[lid]; 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() { specialland = getLandForList(cwt.c); pushScreen(showEuclideanMenu); }