From ce5650a81f4ddff54171c0d44a478fa4b9ffa0d7 Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Sun, 16 Jul 2017 23:00:55 +0200 Subject: [PATCH] 10.0 --- achievement.cpp | 31 ++- basegraph.cpp | 8 +- cell.cpp | 2 +- classes.cpp | 33 ++- complex.cpp | 222 ++++++++++++++++--- config.cpp | 560 +++++++++++++++++++++++++++--------------------- conformal.cpp | 36 ++-- control.cpp | 16 +- dialogs.cpp | 10 +- flags.cpp | 1 + game.cpp | 248 +++++---------------- graph.cpp | 122 +++++++++-- help.cpp | 84 +++++--- hyper.cpp | 8 + hyper.h | 65 +++++- hyper.rc | 8 +- hypgraph.cpp | 7 +- init.cpp | 11 +- landgen.cpp | 356 ++++++++++++++++++++++++++---- langen.cpp | 2 +- language-pl.cpp | 251 +++++++++++++++++++++- mapeditor.cpp | 43 ++-- menus.cpp | 2 +- orbs.cpp | 77 +++++++ polygons.cpp | 58 ++++- quit.cpp | 31 ++- rug.cpp | 2 +- scores.cpp | 4 +- shmup.cpp | 233 +++++++++++--------- system.cpp | 148 +++++++++---- util.cpp | 7 + yendor.cpp | 15 +- 32 files changed, 1861 insertions(+), 840 deletions(-) diff --git a/achievement.cpp b/achievement.cpp index b616b5fc..465a8761 100644 --- a/achievement.cpp +++ b/achievement.cpp @@ -1,7 +1,7 @@ // Hyperbolic Rogue -- achievements // Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details -#define NUMLEADER 69 +#define NUMLEADER 70 #define SCORE_UNKNOWN (-1) #define NO_SCORE_YET (-2) @@ -14,7 +14,7 @@ int currentscore[NUMLEADER]; const char* leadernames[NUMLEADER] = { "Score", "Diamonds", "Gold", "Spice", "Rubies", "Elixirs", - "Shards", "Totems", "Daisies", "Statues", "Feathers", "Sapphires", + "Shards100", "Totems", "Daisies", "Statues", "Feathers", "Sapphires", "Hyperstones", "Time to Win-71", "Turns to Win-71", "Time to 10 Hyperstones-94", "Turns to 10 Hyperstones-94", "Orbs of Yendor", "Fern Flowers", @@ -58,7 +58,8 @@ const char* leadernames[NUMLEADER] = { "Slime Molds", // 65 "Dodecahedra", // 66 "Green Grass", // 67 - "Spinel" // 68 + "Spinel", // 68 + "Orb Strategy Score", // 69 }; #define LB_STATISTICS 62 @@ -212,7 +213,7 @@ void achievement_collection(eItem it, int prevgold, int newgold) { if(q == 8) achievement_gain("GRAIL4"); } - if(q == 10) { + if(q == U10) { if(it == itDiamond) achievement_gain("DIAMOND2"); if(it == itRuby) achievement_gain("RUBY2"); if(it == itHyperstone) achievement_gain("HYPER2"); @@ -268,7 +269,7 @@ void achievement_collection(eItem it, int prevgold, int newgold) { if(it == itBull) achievement_gain("BULL2"); } - if(q == 25) { + if(q == R10) { if(it == itDiamond) achievement_gain("DIAMOND3"); if(it == itRuby) achievement_gain("RUBY3"); if(it == itHyperstone) achievement_gain("HYPER3"); @@ -327,7 +328,7 @@ void achievement_collection(eItem it, int prevgold, int newgold) { if(it == itBull) achievement_gain("BULL3"); } - if(q == 50) { + if(q == 50 && !inv::on) { if(it == itDiamond) achievement_gain("DIAMOND4"); if(it == itRuby) achievement_gain("RUBY4"); if(it == itHyperstone) achievement_gain("HYPER4"); @@ -553,18 +554,21 @@ void achievement_final(bool really_final) { if(shmup::on) specials++; if(chaosmode) specials++; if(purehepta) specials++; + if(inv::on) specials++; if(specials > 1) return; if(numplayers() > 1 && chaosmode) return; if(numplayers() > 1 && purehepta) return; + if(numplayers() > 1 && inv::on) return; int total_improved = 0; specific_improved = 0; specific_what = 0; - if(!shmup::on && !chaosmode && !purehepta && numplayers() == 1) improveItemScores(); + if(!shmup::on && !chaosmode && !purehepta && numplayers() == 1 && !inv::on) improveItemScores(); int sid = purehepta ? 57 : chaosmode ? 53 : shmup::on ? (numplayers() > 1 ? 44 : 28) : + inv::on ? 69 : (numplayers() > 1 ? 61 : 0); int tg = gold(); @@ -601,6 +605,19 @@ void achievement_final(bool really_final) { #endif } +bool hadtotalvictory; + +void check_total_victory() { + if(!inv::on) return; + if(hadtotalvictory) return; + if(!items[itOrbYendor]) return; + if(!items[itHolyGrail]) return; + if(items[itHyperstone] < 50) return; + if(!princess::saved) return; + hadtotalvictory = true; + achievement_gain("TOTALVICTORY"); + } + void achievement_victory(bool hyper) { DEBB(DF_STEAM, (debugfile,"achievement_victory\n")) if(offlineMode) return; diff --git a/basegraph.cpp b/basegraph.cpp index 94e7f541..90922f38 100644 --- a/basegraph.cpp +++ b/basegraph.cpp @@ -88,8 +88,6 @@ int textwidth(int siz, const string &str) { #endif -int gradient(int c0, int c1, ld v0, ld v, ld v1); - int darkenedby(int c, int lev) { for(int i=0; i> 1); @@ -100,6 +98,10 @@ int darkened(int c) { #ifdef EXTRA_FADEOUT c = gradient(backcolor, c, 0, extra::fadeout, 1); #endif + if(inmirrorcount&1) + c = gradient(c, winf[waMirror].color, 0, 0.5, 1); + else if(inmirrorcount) + c = gradient(c, winf[waCloud].color, 0, 0.5, 1); for(int i=0; i> 1) + ((backcolor & 0xFEFEFE) >> 1); return c; @@ -1008,8 +1010,10 @@ void setvideomode() { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 16); glEnable(GL_MULTISAMPLE); } +#ifndef MAC else glDisable(GL_MULTISAMPLE); +#endif } #endif diff --git a/cell.cpp b/cell.cpp index cea000b1..e1f4931c 100644 --- a/cell.cpp +++ b/cell.cpp @@ -648,7 +648,7 @@ struct cellwalker { cell *c; int spin; bool mirrored; - cellwalker(cell *c, int spin) : c(c), spin(spin) { mirrored = false; } + cellwalker(cell *c, int spin, bool m=false) : c(c), spin(spin), mirrored(m) { } cellwalker() { mirrored = false; } }; diff --git a/classes.cpp b/classes.cpp index bc34b292..80e71576 100644 --- a/classes.cpp +++ b/classes.cpp @@ -1143,17 +1143,23 @@ itemtype iinf[ittypes] = { "Target a cell on the other side to use it." }, { '$', 0x80FF80, "Green Grass", prairiedesc }, - { 'o', 0x8080FF, "Orb of Horns", + { 'o', 0xC09090, "Orb of Horns", "After you move while having this Orb, you immediately attack the next cell in the straight line " "(or two cells, when moving on a heptagon). This attack is slightly stronger than your normal " "attack: it can stun some of the monsters which cannot be killed or stunned normally." }, - { 'o', 0x8080FF, "Orb of the Bull", + { 'o', 0xA05020, "Orb of the Bull", "You get the powers of Shield, Horns, and Thorns after you move two moves in a straight line " "with this Orb." }, { '$', 0xC060C0, "Spinel", bulldashdesc }, - { 'o', 0xC0C0FF, "Orb of the Mirror", NODESCYET }, - { 'O', 0xF0F0F0, "your orbs", NODESC}, + { 'o', 0xC0C0FF, "Orb of the Mirror", + "Use Orb of the Mirror to gain copies of one of your Orbs; " + "mirroring weaker Orbs usually yields more copies. " + "It can only be used once per Orb type, " + "and only when you are next to a mirror.", + }, + { 'O', 0xF0F0F0, "your orbs", + "Click this to see your orbs."}, }; enum eItem { itNone, itDiamond, itGold, itSpice, itRuby, itElixir, itShard, itBone, itHell, itStatue, @@ -1192,7 +1198,7 @@ enum eItem { itNone, itDiamond, itGold, itSpice, itRuby, itElixir, itShard, itBo // --- wall types --- -const int walltypes = 97; +const int walltypes = 98; struct walltype { char glyph; @@ -1372,6 +1378,7 @@ walltype winf[walltypes] = { { '.', 0xFFFF00, "Reptile floor", reptiledesc}, { '.', 0xFFFF00, "Reptile bridge", reptiledesc}, { '.', 0xFFFF00, "invisible floor", NODESCYET}, + { '#', 0xC0C0FF, "mirror wall", NODESCYET}, }; enum eWall { waNone, waIcewall, waBarrier, waFloorA, waFloorB, waCavewall, waCavefloor, waDeadTroll, waDune, @@ -1400,12 +1407,13 @@ enum eWall { waNone, waIcewall, waBarrier, waFloorA, waFloorB, waCavewall, waCav waPetrified, waTower, waBigBush, waSmallBush, waReptile, waReptileBridge, - waInvisibleFloor + waInvisibleFloor, + waMirrorWall }; // --- land types --- -const int landtypes = 67; +const int landtypes = 71; struct landtype { int color; @@ -1437,7 +1445,7 @@ const landtype linf[landtypes] = { "A land filled with huge ivy plants and dangerous animals." }, { 0x900090, "Alchemist Lab", slimehelp}, - { 0x704070, "Mirror Land", + { 0x704070, "Hall of Mirrors", "A strange land which contains mirrors and mirages, protected by Mirror Rangers."}, { 0x404070, "Graveyard", "All the monsters you kill are carried to this strange land, and buried. " @@ -1578,7 +1586,11 @@ const landtype linf[landtypes] = { { 0x0000D0, "Prairie", prairiedesc}, { 0x800080, "Bull Dash", bulldashdesc}, { 0xC000C0, "Crossroads V", "Extremely narrow Crossroads layout.\n"}, - { 0xC0C0C0, "Cellular Automaton", cadesc} + { 0xC0C0C0, "Cellular Automaton", cadesc}, + { 0xC0C0FF, "Mirror Wall", NODESCYET}, + { 0xC8C8FF, "Reflection", NODESCYET}, + { 0xC0C0FF, "Reflection", NODESCYET}, + { 0xC8C8FF, "Reflection", NODESCYET}, }; enum eLand { laNone, laBarrier, laCrossroads, laDesert, laIce, laCaves, laJungle, laAlchemist, laMirror, laGraveyard, @@ -1595,7 +1607,8 @@ enum eLand { laNone, laBarrier, laCrossroads, laDesert, laIce, laCaves, laJungle laEndorian, laTortoise, laDragon, laKraken, laBurial, laTrollheim, laHalloween, laDungeon, laMountain, laReptile, - laPrairie, laBull, laCrossroads5, laCA + laPrairie, laBull, laCrossroads5, laCA, + laMirrorWall, laMirrored, laMirrorWall2, laMirrored2 }; // cell information for the game diff --git a/complex.cpp b/complex.cpp index 709572d7..a180b352 100644 --- a/complex.cpp +++ b/complex.cpp @@ -466,6 +466,8 @@ namespace princess { #define OUT_OF_PRISON 200 #define OUT_OF_PALACE 250 +#define PRADIUS0 (141) +#define PRADIUS1 (150) bool generating = false; bool challenge = false; @@ -475,6 +477,8 @@ namespace princess { bool forceVizier = false; bool forceMouse = false; bool gotoPrincess = false; + bool nodungeon = false; + bool squeaked = false; int saveHP = 0, saveArmedHP = 0; @@ -496,7 +500,7 @@ namespace princess { if(i->alt) i->alt->emeraldval = i->id; } - void newInfo(cell *c) { + int newInfo(cell *c) { info *i = new info; i->prison = c; i->princess = c; @@ -506,6 +510,7 @@ namespace princess { i->bestnear = OUT_OF_PRISON; infos.push_back(i); assign(i); + return i->id; } void newFakeInfo(cell *c) { @@ -514,7 +519,7 @@ namespace princess { i->princess = c; i->alt = NULL; i->id = size(infos); - i->bestdist = OUT_OF_PALACE; + i->bestdist = items[itSavedPrincess] ? OUT_OF_PALACE : OUT_OF_PRISON; i->bestnear = 0; infos.push_back(i); assign(i); @@ -524,7 +529,7 @@ namespace princess { if(euclid) return NULL; if(c->land != laPalace) return NULL; if(!c->master->alt) return NULL; - int ev = c->master->alt->emeraldval; // NEWYEARFIX + int ev = c->master->alt->alt->emeraldval; // NEWYEARFIX if(ev < 0 || ev >= size(infos)) return NULL; if(infos[ev]->alt != c->master->alt->alt) return NULL; return infos[ev]; @@ -535,6 +540,7 @@ namespace princess { while(i) { infos[i]->id = i-1; assign(infos[i]); infos[i-1]->id = i; assign(infos[i-1]); + swap(infos[i], infos[i-1]); i--; } return infos[i]; @@ -543,7 +549,7 @@ namespace princess { } int dist(cell *c) { - if(c->land != laPalace) return OUT_OF_PALACE; + if(c->land != laPalace && c->land != laDungeon) return OUT_OF_PALACE; else if(quotient || sphere || torus) return OUT_OF_PRISON; else if(euclid) return celldistAlt(c); else if(!c->master->alt) return OUT_OF_PRISON; @@ -565,10 +571,10 @@ namespace princess { playSound(c, princessgender() ? "heal-princess" : "heal-prince"); info *inf = NULL; - for(int i=0; iprincess && infos[i]->bestdist == OUT_OF_PALACE) + for(int i=0; iprincess && infos[i]->bestdist == OUT_OF_PALACE && isPrincess(infos[i]->princess->monst)) inf = infos[i]; - + } if(inf) { inf->princess->monst = moNone; inf->princess = c; } else newFakeInfo(c); return true; @@ -586,6 +592,9 @@ namespace princess { // printf("Improved dist to %d\n", newdist); if(newdist == OUT_OF_PALACE) { if(!princess::saved) +#ifdef INV + if(!inv::on || !inv::usedForbidden) +#endif achievement_gain("PRINCESS1"); princess::saved = true; princess::everSaved = true; @@ -600,25 +609,27 @@ namespace princess { showMissionScreen(); } } + if(i->princess->land == laDungeon && !saved && !nodungeon) { + addMessage(XLAT("%The1 says, \"not this place, it looks even worse...\"", moPrincess)); + nodungeon = true; + } } void save(cell *princess) { if(euclid) return; princess::info *i = princess::getPrincessInfo(princess); if(!i || i->bestdist <= 3) princess->monst = moNone; - else if(i) setdist(i, OUT_OF_PALACE); + else if(i) setdist(i, OUT_OF_PRISON); } void move(cell *ct, cell *cf) { if(euclid) return; princess::info *i = princess::getPrincessInfo(cf); if(!i) { - static bool warn = true; // note: OK if mapediting or loading - if(warn) printf("Warning: unknown princess\n"); - if(warn && !cheater) + printf("Warning: unknown princess\n"); + if(!cheater) addMessage("Warning: unknown princess (that's a bug, please report)"); - warn = false; } else { i->princess = ct; @@ -661,31 +672,33 @@ namespace princess { retry: if(msgid >= 32) msgid = 0; - if(msgid == 0 && d < 20 && c->land == laPalace) { + bool inpalace = c->land == laPalace || c->land == laDungeon; + + if(msgid == 0 && d < 20 && inpalace) { addMessage(XLAT("%The1 kisses you, and begs you to bring %him1 away from here.", m)); } - else if(msgid == 1 && d >= 20 && c->land == laPalace) { + else if(msgid == 1 && d >= 20 && inpalace) { if(m == moPrincess) addMessage(XLAT("\"I want my revenge. Stun a guard and leave him for me!\"", m)); else addMessage(XLAT("\"That felt great. Thanks!\"", m)); } - else if(msgid == 2 && d >= 70 && c->land == laPalace) { + else if(msgid == 2 && d >= 70 && inpalace) { addMessage(XLAT("\"Bring me out of here please!\"", m)); } - else if(msgid == 3 && c->land != laPalace) { + else if(msgid == 3 && !inpalace) { addMessage(XLAT("%The1 kisses you, and thanks you for saving %him1.", m)); } - else if(msgid == 4 && c->land != laPalace && m == moPrincess) { + else if(msgid == 4 && !inpalace && m == moPrincess) { addMessage(XLAT("\"I have been trained to fight with a Hypersian scimitar, you know?\"", m)); } - else if(msgid == 5 && c->land != laPalace) { + else if(msgid == 5 && !inpalace) { addMessage(XLAT("\"I would love to come to your world with you!\"", m)); } - else if(msgid == 6 && c->land != laPalace) { + else if(msgid == 6 && !inpalace) { addMessage(XLAT("\"Straight lines stay close to each other forever, this is so romantic!\"", m)); } - else if(msgid == 7 && c->land != laPalace) { + else if(msgid == 7 && !inpalace) { addMessage(XLAT("\"Maps... Just like the world, but smaller... how is that even possible?!\"", m)); } else { @@ -1005,23 +1018,31 @@ namespace mirror { c->wall == waFrozenLake || c->wall == waDeadfloor || c->wall == waDeadfloor2 || c->wall == waGiantRug || c->wall == waCIsland || c->wall == waCIsland2 || c->wall == waGargoyleFloor || c->wall == waRubble || - c->wall == waGargoyleBridge || c->wall == waTempFloor || c->wall == waTempBridge; - } - - void createMM(cellwalker& cw, eMonster type) { - if(type == moLightningBolt) - castLightningBolt(cw); - else if(cw.c->monst == moNone && cellMirrorable(cw.c) && !isPlayerOn(cw.c)) { - cw.c->monst = type; - cw.c->mondir = cw.spin; - cw.c->hitpoints = multi::cpid; - } + c->wall == waGargoyleBridge || c->wall == waTempFloor || c->wall == waTempBridge || + c->wall == waMirrorWall; } inline eMonster switchtype(eMonster m) { return (m == moMirror) ? moMirage : moMirror; } + void createMM(cellwalker& cw, eMonster type) { + if(type == moLightningBolt) { + castLightningBolt(cw); + return; + } + if(inmirror(cw)) { + bool b = cw.mirrored; + cw = reflect(cw); + if(cw.mirrored != b) type = switchtype(type); + } + if(cw.c->monst == moNone && cellMirrorable(cw.c) && !isPlayerOn(cw.c)) { + cw.c->monst = type; + cw.c->mondir = cw.spin; + cw.c->hitpoints = multi::cpid; + } + } + inline eMonster switchtypeif(eMonster m, bool b) { if(!b) return m; return (m == moMirror) ? moMirage : moMirror; @@ -1113,7 +1134,7 @@ namespace mirror { } } } - + void go(bool fwd) { int tk = tkills(); int nummirage = 0; @@ -1123,9 +1144,24 @@ namespace mirror { if(c->hitpoints != multi::cpid) continue; eMonster m = c->monst; if(isMimic(m)) { - if(m == moMirage) nummirage++; + int dir = c->mondir; + if(m == moMirage) nummirage++; cell *c2 = c->mov[dir]; + + if(c2 && inmirror(c2)) { + if(c->land == laMirror) { + // c->mondir = (dir+3) % 6; + c->monst = switchtype(m); + continue; + } + cellwalker cw(c2, c->spin(dir), false); + cw = reflect(cw); + dir = c->mondir = cw.c->spin(cw.spin); + if(cw.mirrored) m = c->monst = switchtype(m); + c2 = c->mov[dir]; + } + if(c2 && !isMimic(c2) && canAttack(c,m,c2,c2->monst, 0)) attackMonster(c2, AF_MSG | AF_ORSTUN, m); if(c2->wall == waBigTree) @@ -1135,7 +1171,7 @@ namespace mirror { if(!fwd) continue; c->monst = moNone; if(!c2) continue; - if(!passable(c2, c, P_MONSTER | P_MIRROR)) continue; + if(!passable(c2, c, P_MONSTER | P_MIRROR | P_MIRRORWALL)) continue; if(isWorm(c2)) continue; if(c2->monst == moGreater) { c2->monst = moLesser; continue; @@ -1192,7 +1228,123 @@ namespace mirror { if(multi::players > 1) spin(d); go(fwd); } + + int mirrordir(cell *c) { + if(c->type == 7) return c->bardir; + int icount = 0, isum = 0; + for(int i=0; i<6; i+=2) { + if(createMov(c, i)->bardir == c->spin(i)) + icount++, isum+=i; + } + if(icount > 1) return -1; + return isum; + } + + pair traceback(vector& v, cellwalker cw) { + bool goout = false; + for(int i=size(v)-1; i>=0; i--) { + if(v[i]) cwspin(cw, -v[i]); + else { + cwstep(cw); + if(cw.c->land == laMirrorWall || cw.c->land == laMirror) goout = true; + } + } + return make_pair(goout, cw); + } + + cellwalker reflect(cellwalker cw, bool debug) { + int stepcount = 0; + cellwalker cwcopy = cw; + static vector v; + v.clear(); + while(true) { + if(!inmirror(cw)) break; + stepcount++; if(stepcount > 10000) { + if(debug) cw.c->wall = waBoat; + if(debug) printf("fail\n"); return cw; + } + cell *c0 = cwpeek(cw, 0); + int go = 0; + if(!inmirror(c0)) go = 2; + else if(c0->landparam && c0->landparam < cw.c->landparam) go = 1; + if(go) { + v.push_back(0); + cwstep(cw); + if(debug) queuemarkerat(gmatrix[cw.c], 0x00FF0020); + if(go == 2) break; + } + else { + v.push_back(1); + cwspin(cw, 1); + } + } + if(cw.c->land == laMirrorWall || cw.c->land == laMirrorWall2) { + if(cw.c->type == 7) { + while(cw.spin != cw.c->bardir) { + cwspin(cw, 1); + v.push_back(1); + stepcount++; if(stepcount > 10000) { printf("failhep\n"); return cw; } + } + if(purehepta && cwpeek(cw,0) == cwcopy.c) + v.pop_back(); + if(purehepta && cwpeek(cw,3)->land == laMirrored && cwpeek(cw,2)->land == laMirrorWall) { + cw.mirrored = !cw.mirrored; + auto p = traceback(v, cw); + if(p.first) return p.second; + cwspin(cw, 2); + v.push_back(2); + cwstep(cw); + if(debug) queuemarkerat(gmatrix[cw.c], 0xC9C90080); + v.push_back(0); + cwspin(cw, 3); + v.push_back(3); + } + } + else { + while(cwpeek(cw,0)->type != 7) { + cwspin(cw, 1); + v.push_back(1); + } + int icount = 0; + for(int i=0; i<3; i++) { + if(cwpeek(cw, 0)->bardir == cw.c->spin(cw.spin)) + icount++; + cwspin(cw, 2); + } + if(icount >= 2) { + cellwalker cwcopy = cw; + for(int a=0; a<3; a++) for(int m=0; m<2; m++) { + cellwalker cw = cwcopy; + if(m) cw.mirrored = !cw.mirrored; + cwspin(cw, a*2); + auto p = traceback(v,cw); + if(p.first) return p.second; + } + printf("icount >= 2 but failed\n"); + return cw; + } + while(cwpeek(cw, 0)->bardir != cw.c->spin(cw.spin)) { + stepcount++; if(stepcount > 10000) { printf("fail2\n"); return cw; } + cwspin(cw, 2); + v.push_back(1); + v.push_back(1); + } + } + } + else v.pop_back(); + cw.mirrored = !cw.mirrored; + cw = traceback(v,cw).second; + return cw; + } + + void debug() { + if(!mouseover) return; + queuemarkerat(gmatrix[mouseover], 0xFF0000FF); + cellwalker mw(mouseover, (SDL_GetTicks()/1000) % mouseover->type, (SDL_GetTicks()/500) % 2); + mw = mirror::reflect(mw, true); + queuemarkerat(gmatrix[mw.c], 0x800000FF); + } } namespace hive { @@ -2765,7 +2917,7 @@ namespace ca { } auto ccm = addHook(clearmemory, 0, [] () { - offscreen.clear(); + offscreen.clear(); princess::clear(); mirrors.clear(); clearing::bpdata.clear(); diff --git a/config.cpp b/config.cpp index 30fd1fc9..7c557e3f 100644 --- a/config.cpp +++ b/config.cpp @@ -45,43 +45,91 @@ int lang() { bool autojoy = true; -/*struct saver { +struct supersaver { string name; - virtual string save(); - virtual void load(string& s); + virtual string save() = 0; + virtual void load(const string& s) = 0; + virtual bool dosave() = 0; }; -struct intsaver : saver { - int& val; - int dft; - void intsaver() { val = dft; } - string save() { return its(val); } - void load(string& s) { val = atoi(s.c_str()); } +vector> savers; + +template struct dsaver : supersaver { + T& val; + T dft; + bool dosave() { return val != dft; } + dsaver(T& val) : val(val) { } }; -struct hexsaver : saver { - int& val; - int dft; - void intsaver() { val = dft; } - string save() { return itsh(val); } - void load(string& s) { val = strtol(s.c_str(), 16); } - }; +template struct saver : dsaver {}; -struct ldsaver : saver { - ld& val; - ld dft; - void intsaver() { val = dft; } - string save() { return ffts(val); } - void load(string& s) { val = atof(s.c_str()); } - }; - -vector> savers; - -void initConfig0() { - savers.push_back(make_shared ({"cs.charid", cs.charid, 0})); +template void addsaver(T& i, U name, V dft) { + auto s = make_shared> (i); + s->dft = i = dft; + s->name = name; + savers.push_back(s); } -*/ +template void addsaver(T& i, string name) { + addsaver(i, name, i); + } + +template struct saverenum : supersaver { + T& val; + T dft; + bool dosave() { return val != dft; } + saverenum(T& v) : val(v) { } + string save() { return its(val); } + void load(const string& s) { val = (T) atoi(s.c_str()); } + }; + +template void addsaverenum(T& i, U name) { + auto s = make_shared> (i); + s->dft = i; + s->name = name; + savers.push_back(s); + } + +template<> struct saver : dsaver { + saver(int& val) : dsaver(val) { } + string save() { return its(val); } + void load(const string& s) { val = atoi(s.c_str()); } + }; + +template<> struct saver : dsaver { + saver(char& val) : dsaver(val) { } + string save() { return its(val); } + void load(const string& s) { val = atoi(s.c_str()); } + }; + +template<> struct saver : dsaver { + saver(bool& val) : dsaver(val) { } + string save() { return val ? "yes" : "no"; } + void load(const string& s) { val = size(s) && s[0] == 'y'; } + }; + +template<> struct saver : dsaver { + saver(unsigned& val) : dsaver(val) { } + string save() { return itsh(val); } + void load(const string& s) { val = strtol(s.c_str(), NULL, 16); } + }; + +template<> struct saver : dsaver { + saver(ld& val) : dsaver(val) { } + string save() { return ftssmart(val); } + void load(const string& s) { val = atof(s.c_str()); } + }; + +void addsaver(charstyle& cs, string s) { + addsaver(cs.charid, s + ".charid"); + addsaver(cs.skincolor, s + ".skincolor"); + addsaver(cs.haircolor, s + ".haircolor"); + addsaver(cs.dresscolor, s + ".dresscolor"); + addsaver(cs.swordcolor, s + ".swordcolor"); + addsaver(cs.dresscolor2, s + ".dresscolor2"); + addsaver(cs.uicolor, s + ".uicolor"); + } + // R:239, G:208, B:207 unsigned int skincolors[] = { 7, 0xD0D0D0FF, 0xEFD0C9FF, 0xC77A58FF, 0xA58869FF, 0x602010FF, 0xFFDCB1FF, 0xEDE4C8FF }; @@ -127,69 +175,109 @@ void loadcs(FILE *f, charstyle& cs, int xvernum) { } void initConfig() { - vid.usingGL = true; - vid.antialias = AA_NOGL | AA_FONT | AA_LINES | AA_LINEWIDTH | AA_VERSION; - vid.linewidth = 1; - vid.flashtime = 8; - vid.scale = 1; - vid.alpha = 1; - vid.sspeed = 0; - vid.mspeed = 1; - vid.eye = 0; - vid.full = false; - vid.ballangle = 20; - vid.yshift = 0; - vid.camera_angle = 0; - vid.ballproj = 1; - vid.aurastr = 128; - vid.aurasmoothen = 5; - vid.graphglyph = 1; - -#ifdef ANDROID - vid.monmode = 2; - vid.wallmode = 3; -#else -#ifdef PANDORA - vid.monmode = 2; - vid.wallmode = 3; -#else - vid.monmode = 4; - vid.wallmode = 5; -#endif -#endif - - vid.particles = 1; - vid.mobilecompasssize = 30; - - vid.joyvalue = 4800; - vid.joyvalue2 = 5600; - vid.joypanthreshold = 2500; -#ifdef PANDORA - vid.joypanspeed = 0.0001; -#else - vid.joypanspeed = 0; -#endif - - vid.framelimit = 75; - vid.axes = 1; - vid.shifttarget = 2; - vid.steamscore = 1; - initcs(vid.cs); + // basic config + addsaver(vid.flashtime, "flashtime", 8); + addsaver(vid.mobilecompasssize, "mobile compass size", 30); + addsaver(vid.axes, "movement help", 1); + addsaver(vid.shifttarget, "shift-targetting", 2); + addsaver(vid.steamscore, "scores to Steam", 1); + initcs(vid.cs); addsaver(vid.cs, "single"); + addsaver(vid.samegender, "princess choice", false); + addsaver(vid.language, "language", -1); + addsaver(vid.drawmousecircle, "mouse circle", ISMOBILE || ISPANDORA); + addsaver(vid.revcontrol, "reverse control", false); + addsaver(musicvolume, "music volume"); + addsaver(effvolume, "sound effect volume"); + addsaverenum(glyphsortorder, "glyph sort order"); + // basic graphics + + addsaver(vid.usingGL, "usingGL", true); + addsaver(vid.antialias, "antialias", AA_NOGL | AA_FONT | AA_LINES | AA_LINEWIDTH | AA_VERSION); + addsaver(vid.linewidth, "linewidth", 1); + addsaver(vid.scale, "scale", 1); + addsaver(vid.alpha, "projection", 1); + addsaver(vid.sspeed, "scrollingspeed", 0); + addsaver(vid.mspeed, "movement speed", 1); + addsaver(vid.full, "fullscreen", false); + addsaver(vid.aurastr, "aura strength", 128); + addsaver(vid.aurasmoothen, "aura smoothen", 5); + addsaver(vid.graphglyph, "graphical items/kills", 1); + addsaver(vid.particles, "extra effects", 1); + addsaver(vid.framelimit, "frame limit", 75); + addsaver(vid.xres, "xres"); + addsaver(vid.yres, "yres"); + addsaver(vid.fsize, "font size"); + addsaver(vid.darkhepta, "mark heptagons", false); + + // special graphics + + addsaver(vid.eye, "eye distance", 0); + addsaver(vid.ballangle, "ball angle", 20); + addsaver(vid.yshift, "Y shift", 0); + addsaver(vid.camera_angle, "camera angle", 0); + addsaver(vid.ballproj, "ballproj", 1); + addsaver(vid.monmode, "monster display mode", (ISANDROID || ISPANDORA) ? 2 : 4); + addsaver(vid.wallmode, "wall display mode", (ISANDROID || ISPANDORA) ? 3 : 5); + + addsaver(geom3::depth, "3D depth"); + addsaver(geom3::camera, "3D camera level"); + addsaver(geom3::wall_height, "3D wall height"); + addsaver(geom3::rock_wall_ratio, "3D rock-wall ratio"); + addsaver(geom3::human_wall_ratio, "3D human-wall ratio"); + addsaver(geom3::lake_top, "3D lake top"); + addsaver(geom3::lake_bottom, "3D lake bottom"); + addsaver(geom3::tc_depth, "3D TC depth"); + addsaver(geom3::tc_camera, "3D TC camera"); + addsaver(geom3::tc_alpha, "3D TC alpha"); + addsaver(geom3::highdetail, "3D highdetail"); + addsaver(geom3::middetail, "3D middetail"); + + addsaver(rug::renderonce, "rug-renderonce"); + addsaver(rug::rendernogl, "rug-rendernogl"); + addsaver(rug::texturesize, "rug-texturesize"); + addsaver(rug::scale, "rug-scale"); + + addsaverenum(pmodel, "used model"); + addsaver(polygonal::SI, "polygon sides"); + addsaver(polygonal::STAR, "polygon star factor"); + addsaver(polygonal::deg, "polygonal degree"); + addsaver(conformal::includeHistory, "include history"); // check! + addsaver(conformal::lvspeed, "lineview speed"); + + addsaver(polygonal::maxcoef, "polynomial degree"); + for(int i=0; i tc_camera) pt_alpha++; if(tc_alpha < tc_camera) pt_camera++; - - fprintf(f, "%f %f %f %f %f %f %f %d %d %d %f %f %d\n", - float(geom3::depth), float(geom3::camera), float(geom3::wall_height), - float(geom3::rock_wall_ratio), - float(geom3::human_wall_ratio), - float(geom3::lake_top), - float(geom3::lake_bottom), - pt_depth, pt_camera, pt_alpha, - float(geom3::highdetail), float(geom3::middetail), - glyphsortorder); - - fprintf(f, "%f %f %f %f\n", - float(vid.yshift), float(vid.camera_angle), - float(vid.ballangle), float(vid.ballproj) - ); - - fprintf(f, "%d %d %d %d %f\n", vid.mobilecompasssize, vid.aurastr, vid.aurasmoothen, vid.graphglyph, float(vid.linewidth)); + tc_alpha = pt_alpha; + tc_camera = pt_camera; + tc_depth = pt_depth; } - - fprintf(f, "\n\nThis is a configuration file for HyperRogue (version " VER ")\n"); - fprintf(f, "\n\nThe numbers are:\n"); - fprintf(f, "screen width & height, fullscreen mode (0=windowed, 1=fullscreen), font size\n"); - fprintf(f, "scale, eye distance, parameter, scrolling speed\n"); - fprintf(f, "wallmode, monster mode, cross mode, music volume, framerate limit, usingGL, antialiasing flags\n"); - fprintf(f, "calibrate first joystick (threshold A, threshold B), calibrate second joystick (pan threshold, pan speed), joy mode\n"); - fprintf(f, "gender (1=female, 16=same gender prince), language, skin color, hair color, sword color, dress color\n"); - fprintf(f, "darken hepta, shift target\n"); - fprintf(f, "euclid, euclid land, shmup, hardcore\n"); - fprintf(f, "version number, shmup players, alwaysuse, shmup keyboard/joystick config\n"); - fprintf(f, "hypersian rug config: renderonce, rendernogl, texturesize; purehepta; rug scale; share score; chaosmode\n"); - fprintf(f, "conformal: model, sides, star, degree, includeHistory, speed\n"); - fprintf(f, "conformal: bandwidth, segment, rotation, autoband, autohistory, dospiral\n"); - fprintf(f, "conformal: degree, (degree+1) times {real, imag}\n"); - fprintf(f, "vid.revcontrol, drawmousecircle, sight range, movement animation speed, sound effect volume, particle effects\n"); - fprintf(f, "3D parameters, sort order\n"); - fprintf(f, "yhsift, camera angle, ball angle, ball projection\n"); - fprintf(f, "compass size, aura strength, aura smoothen factor, graphical glyphs\n"); + + for(auto s: savers) if(s->dosave()) + fprintf(f, "%s=%s\n", s->name.c_str(), s->save().c_str()); fclose(f); #ifndef MOBILE @@ -294,110 +322,150 @@ void readf(FILE *f, ld& x) { x = fl; } +void loadOldConfig(FILE *f) { + int gl=1, aa=1, bb=1, cc, dd, ee; + int err; + float a, b, c, d; + err=fscanf(f, "%f%f%f%f\n", &a, &b, &c, &d); + if(err == 4) { + vid.scale = a; vid.eye = b; vid.alpha = c; vid.sspeed = d; + } + err=fscanf(f, "%d%d%d%d%d%d%d", &vid.wallmode, &vid.monmode, &vid.axes, &musicvolume, &vid.framelimit, &gl, &vid.antialias); + vid.usingGL = gl; + if(vid.antialias == 0) vid.antialias = AA_VERSION | AA_LINES | AA_LINEWIDTH; + if(vid.antialias == 1) vid.antialias = AA_NOGL | AA_VERSION | AA_LINES | AA_LINEWIDTH | AA_FONT; + double jps = vid.joypanspeed; + err=fscanf(f, "%d%d%d%lf%d%d", &vid.joyvalue, &vid.joyvalue2, &vid.joypanthreshold, &jps, &aa, &vid.flashtime); + vid.joypanspeed = jps; + autojoy = aa; aa = 0; + + loadcs(f, vid.cs, 0); + + aa=0; bb=0; + err=fscanf(f, "%d%d", &aa, &bb); + vid.darkhepta = aa; vid.shifttarget = bb; + + aa = geometry; bb = euclidland; cc = shmup::on; dd = hardcore; + err=fscanf(f, "%d%d%d%d", &aa, &bb, &cc, &dd); + geometry = eGeometry(aa); euclidland = eLand(bb); shmup::on = cc; hardcore = dd; + + shmup::loadConfig(f); + + aa = rug::renderonce; bb = rug::rendernogl; cc = purehepta; dd = chaosmode; ee = vid.steamscore; + double rs = rug::scale; + err=fscanf(f, "%d%d%d%d%lf%d%d", &aa, &bb, &rug::texturesize, &cc, &rs, &ee, &dd); + rug::renderonce = aa; rug::rendernogl = bb; purehepta = cc; chaosmode = dd; vid.steamscore = ee; + rug::scale = rs; + + aa=conformal::includeHistory; + double ps = polygonal::STAR, lv = conformal::lvspeed; + int pmb = pmodel; + err=fscanf(f, "%d%d%lf%d%d%lf", + &pmb, &polygonal::SI, &ps, &polygonal::deg, + &aa, &lv); + pmodel = eModel(pmb); + conformal::includeHistory = aa; polygonal::STAR = ps; conformal::lvspeed = lv; + + aa=conformal::autoband; bb=conformal::autobandhistory; cc=conformal::dospiral; + err=fscanf(f, "%d%d%d%d%d%d", + &conformal::bandhalf, &conformal::bandsegment, &conformal::rotation, + &aa, &bb, &cc); + conformal::autoband = aa; conformal::autobandhistory = bb; conformal::dospiral = cc; + + err=fscanf(f, "%d", &polygonal::maxcoef); + if(polygonal::maxcoef < 0) polygonal::maxcoef = 0; + if(polygonal::maxcoef > polygonal::MSI) polygonal::maxcoef = polygonal::MSI; + for(int i=0; i<=polygonal::maxcoef; i++) { + double re=0, im=0; + err=fscanf(f, "%lf%lf", &re, &im); + polygonal::coefr[i] = re; + polygonal::coefi[i] = im; + } + + aa=vid.revcontrol; bb=vid.drawmousecircle; + d = vid.mspeed; + err=fscanf(f, "%d%d%d%f%d%d", &aa, &bb, &sightrange, &d, &effvolume, &vid.particles); + vid.mspeed = d; + if(sightrange < 4) sightrange = 4; + if(sightrange > 7) sightrange = 7; + vid.revcontrol = aa; vid.drawmousecircle = bb; + + readf(f, geom3::depth); readf(f, geom3::camera); readf(f, geom3::wall_height); + readf(f, geom3::rock_wall_ratio); readf(f, geom3::human_wall_ratio); + readf(f, geom3::lake_top); readf(f, geom3::lake_bottom); + + err=fscanf(f, "%d %d %d", &geom3::tc_depth, &geom3::tc_camera, &geom3::tc_alpha); + + readf(f, geom3::highdetail); + geom3::middetail = 200; readf(f, geom3::middetail); + if(geom3::middetail == 200) { + if(ISMOBILE) + geom3::highdetail = 0, geom3::middetail = 3; + else + geom3::highdetail = geom3::middetail = 5; + } + + int gso = glyphsortorder; err=fscanf(f, "%d", &gso); glyphsortorder = eGlyphsortorder(gso); + + readf(f, vid.yshift); readf(f, vid.camera_angle); readf(f, vid.ballangle); readf(f, vid.ballproj); + + jps = vid.linewidth; + err=fscanf(f, "%d%d%d%d%lf\n", &vid.mobilecompasssize, &vid.aurastr, &vid.aurasmoothen, &vid.graphglyph, &jps); + vid.linewidth = jps; + } + +map > allconfigs; + +void parseline(const string& str) { + for(int i=0; i +typedef complex cld; + namespace polygonal { typedef complex cld; @@ -13,10 +16,16 @@ namespace polygonal { ld matrix[MSI][MSI]; ld ans[MSI]; - cld coef[MSI]; + cld coef[MSI]; + ld coefr[MSI], coefi[MSI]; int maxcoef, coefid; void solve() { + if(pmodel == mdPolynomial) { + for(int i=0; i= 'A' && uni <= 'D') { int x = (mousex - vid.xres/4) * 510 / vid.xres; @@ -478,7 +478,7 @@ namespace dialog { keyhandler = handleKeyColor; } - void openColorDialog(int& col, unsigned int *pal) { + void openColorDialog(unsigned int& col, unsigned int *pal) { colorPointer = &col; palette = pal; pushScreen(drawColorDialog); } @@ -593,7 +593,7 @@ namespace dialog { if(ne.intval == &polygonal::SI) polygonal::solve(); if(ne.editwhat == &polygonal::STAR) polygonal::solve(); - conformal::applyIB(); + polygonal::solve(); if(ne.editwhat == &geom3::highdetail && geom3::highdetail > geom3::middetail) geom3::middetail = geom3::highdetail; @@ -626,7 +626,7 @@ namespace dialog { addBreak(100); - if(cmode && sm::A3) ne.help = explain3D(ne.editwhat); + if(cmode & sm::A3) ne.help = explain3D(ne.editwhat); if(ne.help != "") { addHelp(ne.help); diff --git a/flags.cpp b/flags.cpp index 0885e630..e28707e3 100644 --- a/flags.cpp +++ b/flags.cpp @@ -637,6 +637,7 @@ bool highwall(cell *c) { return false; // if(wmspatial && isTree(c)) return false; if(isGrave(c->wall)) return true; + if(c->wall == waMirrorWall) return false; return winf[c->wall].glyph == '#' || c->wall == waClosedGate; } diff --git a/game.cpp b/game.cpp index 0a0701ce..826c4563 100644 --- a/game.cpp +++ b/game.cpp @@ -124,9 +124,9 @@ cellwalker cwt; // single player character position inline cell*& singlepos() { return cwt.c; } inline bool singleused() { return !(shmup::on || multi::players > 1); } -#include "mtrand.cpp" +#include -MTRand_int32 r; +mt19937 r; void shrand(int i) { r.seed(i); @@ -510,6 +510,9 @@ bool passable(cell *w, cell *from, flagtype flags) { if(w->wall == waMirror || w->wall == waCloud) return F(P_MIRROR | P_AETHER); + if(w->wall == waMirrorWall) + return F(P_MIRRORWALL); + if(F(P_BULLET)) { if(isFire(w) || w->wall == waBonfireOff || cellHalfvine(w) || w->wall == waAncientGrave || w->wall == waFreshGrave || w->wall == waRoundTable) @@ -632,10 +635,12 @@ bool ghostmove(eMonster m, cell* to, cell* from) { if((m == moWitchGhost || m == moWitchWinter) && to->land != laPower) return false; if(isGhost(m)) - for(int i=0; itype; i++) + for(int i=0; itype; i++) { + if(inmirror(to->mov[i])) return false; if(to->mov[i] && to->mov[i] != from && isGhost(to->mov[i]->monst) && (to->mov[i]->monst == moFriendlyGhost) == (m== moFriendlyGhost)) return false; + } if(isGhost(m) || m == moWitchGhost) return true; if(m == moGreaterShark) return isWatery(to); if(m == moWitchWinter) @@ -1728,6 +1733,10 @@ void killMonster(cell *c, eMonster who, flagtype deathflags) { i->princess = NULL; if(i->bestdist == OUT_OF_PALACE) { items[itSavedPrincess]--; + if(items[itSavedPrincess] < 0) { + printf("princess below 0\n"); + items[itSavedPrincess] = 0; + } if(items[itSavedPrincess] == 0 && !inv::on) { items[itOrbLove] = 0; princess::reviveAt = gold(NO_LOVE) + 20; @@ -5399,6 +5408,11 @@ bool hasSafeOrb(cell *c) { } void checkmove() { + +#ifdef INV + if(inv::on) inv::compute(); +#endif + if(multi::players > 1 && !multi::checkonly) return; if(hardcore) return; msgscroll = 0; @@ -5423,6 +5437,20 @@ void checkmove() { canmove = legalmoves[cwt.spin] = true; if(kills[moPlayer]) canmove = false; +#ifdef INV + if(!canmove && !inv::incheck) { + if(inv::remaining[itOrbSafety]) + canmove = true; + else { + inv::check(1); + checkmove(); + inv::check(-1); + } + if(canmove) + pushScreen(inv::show); + } +#endif + if(!canmove) { achievement_final(true); if(cmode & sm::NORMAL) showMissionScreen(); @@ -5435,11 +5463,7 @@ void checkmove() { items[itWarning]-=2; for(int i=0; i 5) items[itOrbLife] = 5; + if(items[itOrbLife] > 5 && !shmup::on) items[itOrbLife] = 5; } void collectMessage(cell *c2, eItem which) { @@ -5686,19 +5710,29 @@ bool collectItem(cell *c2, bool telekinesis) { if(isRevivalOrb(c2->item) && multi::revive_queue.size()) { multiRevival(cwt.c, c2); } - else if(c2->item == itOrbSpeed) { - items[c2->item] += 31; - playSound(c2, "pickup-speed"); + else if(isShmupLifeOrb(c2->item) && shmup::on) { + playSound(c2, "pickup-orb"); // TODO summon + gainLife(); + } + else if(orbcharges(c2->item)) { + eItem it = c2->item; + if(it == itOrbRecall) saveRecall(c2); + if(it == itOrbFire) playSound(c2, "fire"); + else if(it == itOrbFire) playSound(c2, "fire"); + else if(it == itOrbWinter) playSound(c2, "pickup-winter"); + else if(it == itOrbSpeed) playSound(c2, "pickup-speed"); + else if(it == itRevolver) playSound(c2, "pickup-key"); + else playSound(c2, "pickup-orb"); + if(!items[it]) items[it]++; + items[it] += orbcharges(it); } else if(c2->item == itOrbLife) { playSound(c2, "pickup-orb"); // TODO summon - if(shmup::on) gainLife(); - else placeGolem(cwt.c, c2, moGolem); + placeGolem(cwt.c, c2, moGolem); } else if(c2->item == itOrbFriend) { playSound(c2, "pickup-orb"); // TODO summon - if(shmup::on) gainLife(); - else placeGolem(cwt.c, c2, moTameBomberbird); + placeGolem(cwt.c, c2, moTameBomberbird); } #ifdef TOUR else if(tour::on && (c2->item == itOrbSafety || c2->item == itOrbRecall)) { @@ -5715,145 +5749,6 @@ bool collectItem(cell *c2, bool telekinesis) { activateSafety(c2->land); return true; } - else if(c2->item == itOrbLightning) { - playSound(c2, "pickup-orb"); - items[c2->item] += 78; - } - else if(c2->item == itOrbStone) { - playSound(c2, "pickup-orb"); - items[c2->item] += 61; - } - else if(c2->item == itOrbNature) { - playSound(c2, "pickup-orb"); - if(shmup::on) gainLife(); - else items[c2->item] += 61; - } - else if(c2->item == itOrbRecall) { - saveRecall(c2); - playSound(c2, "pickup-orb"); - items[c2->item] += 61; - } - else if(c2->item == itOrbTime) { - playSound(c2, "pickup-orb"); - items[c2->item] += 78; - } - else if(c2->item == itOrbLove) { - playSound(c2, "pickup-orb"); - items[c2->item] += 31; - } - else if(c2->item == itOrbSpace) { - playSound(c2, "pickup-orb"); - items[c2->item] += 78; - } - else if(c2->item == itOrbThorns) { - playSound(c2, "pickup-orb"); - items[c2->item] += 78; - } - else if(c2->item == itOrbSword) { - playSound(c2, "pickup-orb"); - if(!items[itOrbSword] && !items[itOrbSword2]) - sword::shuffle(); - items[c2->item] += 61 + 30 * multi::activePlayers(); - } - else if(c2->item == itOrbSword2) { - playSound(c2, "pickup-orb"); - if(!items[itOrbSword] && !items[itOrbSword2]) - sword::shuffle(); - items[c2->item] += 41 + 20 * multi::activePlayers(); - } - else if(c2->item == itOrbFlash) { - playSound(c2, "pickup-orb"); - items[c2->item] += 78; - } - else if(c2->item == itOrbShield) { - playSound(c2, "pickup-orb"); // TODO Shield - items[c2->item] += 16; - orbused[itOrbShield] = false; - } - else if(c2->item == itOrbWater) { - playSound(c2, "pickup-orb"); - items[c2->item] += 31; - } - else if(c2->item == itOrbAir) { - playSound(c2, "pickup-orb"); - items[c2->item] += 67; - } - else if(c2->item == itOrbFrog) { - playSound(c2, "pickup-orb"); - items[c2->item] += 45; - } - else if(c2->item == itOrbDash) { - playSound(c2, "pickup-orb"); - items[c2->item] += 45; - } - else if(c2->item == itOrbDiscord) { - playSound(c2, "pickup-orb"); - // make it seem to be 23 - if(!items[c2->item]) items[c2->item]++; - items[c2->item] += 23; - } - else if(c2->item == itOrbSummon) { - playSound(c2, "pickup-orb"); - items[c2->item] += 121; - } - else if(c2->item == itOrbMatter) { - playSound(c2, "pickup-orb"); - items[c2->item] += 67; - } - else if(c2->item == itOrbHorns) { - playSound(c2, "pickup-orb"); - items[c2->item] += 67; - } - else if(c2->item == itOrbBull) { - playSound(c2, "pickup-orb"); - items[c2->item] += 67; - } - else if(c2->item == itOrbFish) { - playSound(c2, "pickup-orb"); - items[c2->item] += 21 + 10 * multi::activePlayers(); - } - else if(c2->item == itOrbWinter) { - playSound(c2, "pickup-winter"); - items[c2->item] += 31; - } - else if(c2->item == itRevolver) { - playSound(c2, "pickup-key"); - items[c2->item] = 6; - } - else if(c2->item == itOrbStunning) { - playSound(c2, "pickup-orb"); - items[c2->item] += 61; - } - else if(c2->item == itOrbLuck) { - playSound(c2, "pickup-orb"); - items[c2->item] += 61; - } - else if(c2->item == itOrbUndeath) { - playSound(c2, "pickup-orb"); - if(shmup::on) gainLife(); - else items[c2->item] += 31; - } - else if(c2->item == itOrbFreedom) { - playSound(c2, "pickup-orb"); - items[c2->item] += 31; - } - else if(c2->item == itOrb37) { - playSound(c2, "pickup-orb"); - items[c2->item] += 51; - } - else if(c2->item == itOrbEnergy) { - playSound(c2, "pickup-orb"); - items[c2->item] += 51; - } - else if(c2->item == itOrbDomination) { - playSound(c2, "pickup-orb"); - if(shmup::on) gainLife(); - else items[c2->item] += 91; - } - else if(c2->item == itOrbShell) { - playSound(c2, "pickup-orb"); // TOOD shield - items[c2->item] += 67; - } else if(c2->item == itBabyTortoise) { using namespace tortoise; int bnew = babymap[c2]; @@ -5869,47 +5764,6 @@ bool collectItem(cell *c2, bool telekinesis) { } else items[itBabyTortoise]++; } - else if(c2->item == itOrbBeauty) { - playSound(c2, "pickup-orb"); - items[c2->item] += 41; - } - else if(c2->item == itOrbEmpathy) { - playSound(c2, "pickup-orb"); - if(shmup::on) gainLife(); - else items[c2->item] += 41; - } - else if(c2->item == itOrbFire) { - playSound(c2, "fire"); - items[c2->item] += (sphere ? 3 : 31); - } - else if(c2->item == itOrbDragon) { - playSound(c2, "pickup-orb"); - items[c2->item] += sphere ? 10 : 78; - } - else if(c2->item == itOrbIllusion) { - playSound(c2, "pickup-orb"); - items[c2->item] += 78; - } - else if(c2->item == itOrbPsi) { - playSound(c2, "pickup-orb"); - items[c2->item] += 78; - } - else if(c2->item == itOrbInvis) { - playSound(c2, "pickup-orb"); - items[c2->item] += 31; - } - else if(c2->item == itOrbAether) { - playSound(c2, "pickup-orb"); - items[c2->item] += 31; - } - else if(c2->item == itOrbDigging) { - playSound(c2, "pickup-orb"); - items[c2->item] += 78; - } - else if(c2->item == itOrbTeleport) { - playSound(c2, "pickup-orb"); - items[c2->item] += 78; - } else if(c2->item == itOrbYendor && peace::on) { if(!items[itDodeca]) { addMessage(XLAT("Collect as many Dodecahedra as you can, then return here!")); @@ -7003,6 +6857,8 @@ bool movepcto(int d, int subdir, bool checkonly) { if(multi::players == 1) monstersTurn(); + check_total_victory(); + if(items[itWhirlpool] && cwt.c->land != laWhirlpool && !whirlpool::escaped) { whirlpool::escaped = true; achievement_gain("WHIRL1"); diff --git a/graph.cpp b/graph.cpp index 9bd4cf56..cb21b1e6 100644 --- a/graph.cpp +++ b/graph.cpp @@ -4,6 +4,8 @@ // basic graphics: +int inmirrorcount = 0; + bool wmspatial, wmescher, wmplain, wmblack, wmascii; bool mmspatial, mmhigh, mmmon, mmitem; int maxreclevel, reclevel; @@ -614,6 +616,7 @@ bool drawItemType(eItem it, cell *c, const transmatrix& V, int icol, int ticks, if(it == itOrbUndeath) icol = minf[moFriendlyGhost].color; if(it == itOrbRecall) icol = 0x101010; hpcshape& sh = + it == itOrbLove ? shLoveRing : isRangedOrb(it) ? shTargetRing : isOffensiveOrb(it) ? shSawRing : isFriendOrb(it) ? shPeaceRing : @@ -1023,9 +1026,12 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou queuepoly(VBIRD * Mirror, shGadflyEye, darkena(col, 2, 0xFF)); } else if(m == moVampire || m == moBat) { - if(m == moBat) ShadowV(V, shBatWings); // but vampires have no shadow - queuepoly(VBIRD, shBatWings, darkena(0x303030, 0, 0xFF)); - queuepoly(VBIRD, shBatBody, darkena(0x606060, 0, 0xFF)); + // vampires have no shadow and no mirror images + if(m == moBat) ShadowV(V, shBatWings); + if(m == moBat || !inmirrorcount) { + queuepoly(VBIRD, shBatWings, darkena(0x303030, 0, 0xFF)); + queuepoly(VBIRD, shBatBody, darkena(0x606060, 0, 0xFF)); + } /* queuepoly(V, shBatMouth, darkena(0xC00000, 0, 0xFF)); queuepoly(V, shBatFang, darkena(0xFFC0C0, 0, 0xFF)); queuepoly(V*Mirror, shBatFang, darkena(0xFFC0C0, 0, 0xFF)); @@ -1444,7 +1450,7 @@ bool applyAnimation(cell *c, transmatrix& V, double& footphase, int layer) { } double chainAngle(cell *c, transmatrix& V, cell *c2, double dft) { - if(!gmatrix0.count(c2)) return dft; + if(inmirrorcount || !gmatrix0.count(c2)) return dft; hyperpoint h = C0; if(animations[LAYER_BIG].count(c2)) h = animations[LAYER_BIG][c2].wherenow * h; h = inverse(V) * gmatrix0[c2] * h; @@ -1453,7 +1459,7 @@ double chainAngle(cell *c, transmatrix& V, cell *c2, double dft) { // equivalent to V = V * spin(-chainAngle(c,V,c2,dft)); bool chainAnimation(cell *c, transmatrix& V, cell *c2, int i, int b) { - if(!gmatrix0.count(c2)) { + if(inmirrorcount || !gmatrix0.count(c2)) { V = V * ddspin(c,i,b); return false; } @@ -1502,7 +1508,8 @@ bool drawMonster(const transmatrix& Vparam, int ct, cell *c, int col) { double footphaseb = 0, footphase = 0; transmatrix Vs = Vparam; nospins = applyAnimation(c, Vs, footphase, LAYER_SMALL); - transmatrix Vb = Vparam; nospinb = applyAnimation(c, Vb, footphaseb, LAYER_BIG); + transmatrix Vb = Vparam; + if(!inmirrorcount) nospinb = applyAnimation(c, Vb, footphaseb, LAYER_BIG); // nospin = true; eMonster m = c->monst; @@ -1638,6 +1645,8 @@ bool drawMonster(const transmatrix& Vparam, int ct, cell *c, int col) { if(!nospins) Vs = Vs * ddspin(c, c->mondir, flipplayer ? S42 : 0); + + if(inmirrorcount&1) col ^= minf[moMirror].color ^ minf[moMirage].color; if(c->monst == moMirror) Vs = Vs * Mirror; @@ -2240,7 +2249,8 @@ void setcolors(cell *c, int& wcol, int &fcol) { if(c->wall == waPlatform) wcol = 0xF0F0A0; } if(c->land == laWineyard) fcol = 0x006000; - if(c->land == laMirror) fcol = 0x808080; + if(c->land == laMirror || c->land == laMirrorWall) + fcol = 0x808080; if(c->land == laMotion) fcol = 0xF0F000; if(c->land == laGraveyard) fcol = 0x107010; if(c->land == laDryForest) fcol = gradient(0x008000, 0x800000, 0, c->landparam, 10); @@ -2509,7 +2519,7 @@ void setcolors(cell *c, int& wcol, int &fcol) { if(c->wall == waAncientGrave || c->wall == waFreshGrave || c->wall == waThumperOn || c->wall == waThumperOff || c->wall == waBonfireOff) fcol = wcol; - if(c->land == laMinefield && c->wall == waMineMine && ((cmode && sm::MAP) || !canmove)) + if(c->land == laMinefield && c->wall == waMineMine && ((cmode & sm::MAP) || !canmove)) fcol = wcol = 0xFF4040; if(mightBeMine(c) && mineMarkedSafe(c)) @@ -2792,14 +2802,17 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { qfi.shape = NULL; qfi.special = false; ivoryz = isGravityLand(c->land); - transmatrix& gm = gmatrix[c]; - bool orig = - gm[2][2] == 0 ? true : - torus ? hypot(gm[0][2], gm[1][2]) >= hypot(V[0][2], V[1][2]) : - (sphere && vid.alphax >= 1.001) ? fabs(gm[2][2]-1) <= fabs(V[2][2]-1) : - fabs(gm[2][2]-1) >= fabs(V[2][2]-1) - 1e-8; + bool orig = false; + if(!inmirrorcount) { + transmatrix& gm = gmatrix[c]; + orig = + gm[2][2] == 0 ? true : + torus ? hypot(gm[0][2], gm[1][2]) >= hypot(V[0][2], V[1][2]) : + (sphere && vid.alphax >= 1.001) ? fabs(gm[2][2]-1) <= fabs(V[2][2]-1) : + fabs(gm[2][2]-1) >= fabs(V[2][2]-1) - 1e-8; - if(orig) gm = V; + if(orig) gm = V; + } if(behindsphere(V)) return; @@ -2862,7 +2875,8 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { hyperpoint VC0 = tC0(V); - if(intval(mouseh, VC0) < modist) { + if(inmirrorcount) ; + else if(intval(mouseh, VC0) < modist) { modist2 = modist; mouseover2 = mouseover; modist = intval(mouseh, VC0); mouseover = c; @@ -2871,7 +2885,7 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { modist2 = intval(mouseh, VC0); mouseover2 = c; } - + if(!torus) { double dfc = euclid ? intval(VC0, C0) : VC0[2]; @@ -2892,6 +2906,19 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { } } + if(inmirror(c)) { + if(inmirrorcount >= 10) return; + cellwalker cw(c, 0, mirrored); + cw = mirror::reflect(cw); + int cmc = (cw.mirrored == mirrored) ? 2 : 1; + inmirrorcount += cmc; + if(cw.mirrored != mirrored) V = V * Mirror; + if(cw.spin) V = V * spin(2*M_PI*cw.spin/cw.c->type); + drawcell(cw.c, V, 0, cw.mirrored); + inmirrorcount -= cmc; + return; + } + // int col = 0xFFFFFF - 0x20 * c->maxdist - 0x2000 * c->cpdist; if(!buggyGeneration && c->mpdist > 8 && !cheater) return; // not yet generated @@ -2904,8 +2931,20 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { int wcol, fcol, asciicol; setcolors(c, wcol, fcol); + + if(inmirror(c)) { + // for debugging + if(c->land == laMirrored) fcol = 0x008000; + if(c->land == laMirrorWall2) fcol = 0x800000; + if(c->land == laMirrored2) fcol = 0x000080; + } + + for(int k=0; kland != laTortoise) { int d = @@ -2918,6 +2957,11 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { fcol = gradient(fcol, dc, 0, .3, 1); } + if(c->land == laMirrored || c->land == laMirrorWall2 || c->land == laMirrored2) { + string label = its(c->landparam); + queuestr(V, 1 * .2, label, 0xFFFFFFFF, 1); + } + if(viewdists) { int cd = celldistance(c, cwt.c); string label = its(cd); @@ -3113,6 +3157,44 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { } #endif + else if(c->land == laMirrorWall) { + int d = mirror::mirrordir(c); + bool onleft = c->type == 7; + if(c->type == 7 && c->barleft == laMirror) + onleft = !onleft; + if(c->type == 6 && c->mov[d]->barleft == laMirror) + onleft = !onleft; + if(purehepta) onleft = !onleft; + + if(d == -1) { + for(d=0; d<6; d++) + if(c->mov[d] && c->mov[(1+d)%6] && c->mov[d]->land == laMirrorWall && c->mov[(1+d)%6]->land == laMirrorWall) + break; + qfi.spin = ddspin(c, d, 0); + transmatrix V2 = V * qfi.spin; + for(int d=0; d<6; d++) { + inmirrorcount+=d; + qfloor(c, V2 * spin(d*M_PI/3), shHalfFloor[2], darkena(fcol, fd, 0xFF)); + inmirrorcount-=d; + } + const int layers = 2 << detaillevel; + for(int z=1; zland == laWineyard && cellHalfvine(c)) { int i =-1; @@ -3606,6 +3688,8 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { } } + else if(c->wall == waMirrorWall) ; + else if(highwall(c)) { zcol = wcol; int wcol0 = wcol; @@ -4363,6 +4447,7 @@ void drawthemap() { maxreclevel, hsOrigin, ypush(vid.yshift) * sphereflip * View); } + ivoryz = false; linepatterns::drawAll(); @@ -4589,6 +4674,7 @@ void drawfullmap() { } #endif profile_start(2); + drawaura(); drawqueue(); profile_stop(2); diff --git a/help.cpp b/help.cpp index 54abf10d..043d6eff 100644 --- a/help.cpp +++ b/help.cpp @@ -36,6 +36,15 @@ string buildHelpText() { "automatically cancels all moves which result in that.\n\n" ); + if(inv::on) + h += XLAT( + "You are playing in the Orb Strategy Mode. Collecting treasure " + "gives you access to magical Orb powers. In this mode, " + "unlocking requirements are generally higher, and " + "several quests and lands " + "give you extremely powerful Orbs of the Mirror.\n" + ); + else h += XLAT( "There are many lands in HyperRogue. Collect 10 treasure " "in the given land type to complete it; this enables you to " @@ -127,6 +136,7 @@ string helptitle(string s, int col) { } string princessReviveHelp() { + if(inv::on) return ""; string h = "\n\n" + XLAT("Killed %1 can be revived with Orb of the Love, after you collect 20 more $$$.", moPrincess); if(princess::reviveAt) @@ -136,7 +146,8 @@ string princessReviveHelp() { } void describeOrb(string& help, const orbinfo& oi) { - eOrbLandRelation olr = getOLR(oi.orb, cwt.c->land); + if(inv::on) return; + eOrbLandRelation olr = getOLR(oi.orb, getPrizeLand()); eItem tr = treasureType(oi.l); help += "\n\n" + XLAT(olrDescriptions[olr], cwt.c->land, tr, treasureType(cwt.c->land)); int t = items[tr] * landMultiplier(oi.l); @@ -222,6 +233,51 @@ string generateHelpForItem(eItem it) { } } + if(inv::on) { + if(it == itOrbYendor || it == itHell) { + help += XLAT( + "\n\nIn the Orb Strategy Mode, Orbs of Yendor appear in Hell after " + "you collect 25 Demon Daisies in Hell, in Crossroads/Ocean after you collect 50, " + "and everywhere after you collect 100."); + } + + if(it == itBone || it == itGreenStone) { + help += XLAT( + "\n\nIn the Orb Strategy Mode, dead orbs are available once you collect " + "10 Necromancer Totems in the Graveyard." + ); + } + + if(it == itFeather || it == itOrbSafety) { + help += XLAT( + "\n\nIn the Orb Strategy Mode, Orbs of Safety can be gained by " + "collecting Phoenix Feathers in the Land of Eternal Motion. " + "You can also find unlimited Orbs of Safety in the Crossroads " + "and the Ocean (after collecting 25 Phoenix Feathers) " + "and in the Prairie." + ); + } + + if(it == itOrbYendor || it == itHolyGrail) + help += XLAT( + "\n\nCollect %the1 to gain an extra Orb of the Mirror. " + "You can gain further Orbs of the Mirror by collecting 2, 4, 8..." + ); + + if(it == itOrbLuck) + help += XLAT( + "\n\nIn the Orb Strategy Mode, the Orb of Luck also " + "significantly increases the frequency of Great Walls, Crossroads IV, " + "and sub-lands." + ); + + if(it == itBone) + help += XLAT( + "\n\nIn the Orb Strategy Mode, each 25 Necromancer's Totems " + "you are given a random offensive Orb." + ); + } + if(itemclass(it) == IC_ORB || it == itGreenStone || it == itOrbYendor) { for(int i=0; iland) { + else if(oi.l == cwt.c->land || inv::on) { help += XLAT("\n\nSecondary orb: %1", oi.orb); describeOrb(help, oi); } @@ -537,7 +593,7 @@ void describeMouseover() { else if(c->wall != waInvisibleFloor) { out = XLAT1(linf[c->land].name); help = generateHelpForLand(c->land); - + if(c->land == laIce || c->land == laCocytus) out += " (" + fts(heat::celsius(c)) + " °C)"; if(c->land == laDryForest && c->landparam) @@ -561,27 +617,7 @@ void describeMouseover() { if(c->land == laTortoise && tortoise::seek()) out += " " + tortoise::measure(getBits(c)); if(buggyGeneration) { - char buf[20]; sprintf(buf, " H=%d M=%d", c->landparam, c->mpdist); out += buf; - } - - if(false) { - - out += " LP:" + itsh(c->landparam)+"/"+its(turncount); - - out += " CD:" + its(celldist(c)); - - out += " D:" + its(c->mpdist); - - char zz[64]; sprintf(zz, " P%p", c); out += zz; - - if(euclid) { - for(int i=0; i<4; i++) out += " " + its(getEuclidCdata(c->master)->val[i]); - out += " " + itsh(getBits(c)); - } - else { - for(int i=0; i<4; i++) out += " " + its(getHeptagonCdata(c->master)->val[i]); - out += " " + fts(tortoise::getScent(getBits(c))); - } + char buf[80]; sprintf(buf, " %p H=%d M=%d", c, c->landparam, c->mpdist); out += buf; } if(randomPatternsMode) diff --git a/hyper.cpp b/hyper.cpp index 9559d1ac..b194fcbb 100644 --- a/hyper.cpp +++ b/hyper.cpp @@ -209,6 +209,14 @@ else if(args()[0] == '-' && args()[1] == x && args()[2] == '0') { if(curphase == else if(argis("-fix")) { fixseed = true; autocheat = true; } + else if(argis("-fixx")) { + fixseed = true; autocheat = true; + shift(); startseed = argi(); + } + else if(argis("-steplimit")) { + fixseed = true; autocheat = true; + shift(); steplimit = argi(); + } else if(argis("-qpar")) { int p; shift(); sscanf(args(), "%d,%d,%d", diff --git a/hyper.h b/hyper.h index 12f7a5be..5728a65d 100644 --- a/hyper.h +++ b/hyper.h @@ -7,7 +7,7 @@ #define LB_YENDOR_CHALLENGE 40 #define LB_PURE_TACTICS 41 -#define NUMLEADER 69 +#define NUMLEADER 70 #define LB_PURE_TACTICS_SHMUP 49 #define LB_PURE_TACTICS_COOP 50 @@ -73,6 +73,7 @@ eItem treasureType(eLand l); void buildBarrier(cell *c, int d, eLand l = laNone); void extendBarrier(cell *c); bool buildBarrier4(cell *c, int d, int mode, eLand ll, eLand lr); +bool buildBarrier6(struct cellwalker cw, int type); bool makeEmpty(cell *c); bool isCrossroads(eLand l); enum orbAction { roMouse, roKeyboard, roCheck, roMouseForce, roMultiCheck, roMultiGo }; @@ -134,7 +135,8 @@ void activateActiv(cell *c, bool msg); // shmup struct charstyle { - int charid, skincolor, haircolor, dresscolor, swordcolor, dresscolor2, uicolor; + int charid; + unsigned skincolor, haircolor, dresscolor, swordcolor, dresscolor2, uicolor; }; string csname(charstyle& cs); @@ -271,7 +273,7 @@ void drawfullmap(); bool displaystr(int x, int y, int shift, int size, const char *str, int color, int align); bool displaystr(int x, int y, int shift, int size, const string& str, int color, int align); -extern int darken; +extern int darken, inmirrorcount; void calcparam(); #ifdef USE_SDL @@ -395,7 +397,7 @@ namespace rug { extern bool renderonce; extern bool rendernogl; extern int texturesize; - extern double scale; + extern ld scale; void show(); void init(); void close(); @@ -406,8 +408,6 @@ namespace rug { #endif #define HASLINEVIEW -#include -typedef complex cld; namespace conformal { extern bool on; @@ -440,7 +440,7 @@ namespace polygonal { extern int SI; extern ld STAR; extern int deg; - extern complex coef[MSI]; + extern ld coefr[MSI], coefi[MSI]; extern int maxcoef, coefid; void solve(); pair compute(ld x, ld y); @@ -469,6 +469,7 @@ extern bool localKill(shmup::monster *m); #define P_ONPLAYER (1<<6) // always can step on the player #define P_FLYING (1<<7) // is flying #define P_BULLET (1<<8) // bullet can fly through more things +#define P_MIRRORWALL (1<<9) // mirror images go through mirror walls #define P_JUMP1 (1<<10) // first part of a jump #define P_JUMP2 (1<<11) // second part of a jump #define P_TELE (1<<12) // teleport onto @@ -519,7 +520,7 @@ extern bool safety; #define SAGEMELT .1 #define TEMPLE_EACH 6 -#define PT(x, y) ((tactic::on || quotient == 2) ? (y) : (x)) +#define PT(x, y) ((tactic::on || quotient == 2) ? (y) : inv::on ? min(2*(y),x) : (x)) #define ROCKSNAKELENGTH 50 #define WORMLENGTH 15 #define PUREHARDCORE_LEVEL 10 @@ -540,7 +541,7 @@ bool isAlchAny(eWall w); bool isAlchAny(cell *c); #define YDIST 101 -#define MODECODES 254 +#define MODECODES 255 extern cellwalker cwt; // player character position extern int sval; @@ -827,7 +828,7 @@ namespace dialog { void addSelItem(string body, string value, int key); void addBoolItem(string body, bool value, int key); void addColorItem(string body, int value, int key); - void openColorDialog(int& col, unsigned int *pal = palette); + void openColorDialog(unsigned int& col, unsigned int *pal = palette); void addHelp(string body); void addInfo(string body, int color = 0xC0C0C0); void addItem(string body, int key); @@ -1304,8 +1305,14 @@ bool createOnSea(eLand old); namespace inv { extern bool on; + extern bool usedForbidden; extern int remaining[ittypes]; void compute(); + void applyBox(eItem it); + + extern int incheck; + void check(int delta); + void show(); } bool drawItemType(eItem it, cell *c, const transmatrix& V, int icol, int ticks, bool hidden); @@ -1327,6 +1334,9 @@ typedef hookset *purehookset; template int addHook(hookset*& m, int prio, const U& hook) { if(!m) m = new hookset (); + while(m->count(prio)) { + prio++; + } (*m)[prio] = hook; return 0; } @@ -1469,3 +1479,38 @@ namespace leader { void showMenu(); void handleKey(int sym, int uni); } bool needConfirmation(); extern const char* geometrynames_short[gGUARD]; + +namespace mirror { + cellwalker reflect(cellwalker cw, bool debug = false); + } + +bool inmirror(eLand l); +bool inmirror(cell *c); +bool inmirror(const cellwalker& cw); + +void queuemarkerat(const transmatrix& V, int col); + +void check_total_victory(); +void applyBoxNum(int& i, string name = ""); +extern int hinttoshow; + +bool isShmupLifeOrb(eItem it); +int orbcharges(eItem it); + +#ifdef PANDORA +static const bool ISPANDORA = true; +#else +static const bool ISPANDORA = false; +#endif + +int gradient(int c0, int c1, ld v0, ld v, ld v1); + +struct hint { + time_t last; + function usable; + function display; + function action; + }; + +extern hint hints[]; +int counthints(); diff --git a/hyper.rc b/hyper.rc index acdac4a4..0c2fe35e 100644 --- a/hyper.rc +++ b/hyper.rc @@ -1,8 +1,8 @@ id ICON "hr-icon.ico" 1 VERSIONINFO -FILEVERSION 9,4,0,15 -PRODUCTVERSION 9,4,0,15 +FILEVERSION 9,4,0,13 +PRODUCTVERSION 9,4,0,13 BEGIN BLOCK "StringFileInfo" BEGIN @@ -10,12 +10,12 @@ BEGIN BEGIN VALUE "CompanyName", "Zeno Rogue" VALUE "FileDescription", "A roguelike in non-euclidean space" - VALUE "FileVersion", "94n1" + VALUE "FileVersion", "A10.0" VALUE "InternalName", "hyper" VALUE "LegalCopyright", "Zeno Rogue" VALUE "OriginalFilename", "hyper.exe" VALUE "ProductName", "HyperRogue" - VALUE "ProductVersion", "9.4n1" + VALUE "ProductVersion", "10.0" END END diff --git a/hypgraph.cpp b/hypgraph.cpp index 011affe5..f4c9f4db 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -255,6 +255,11 @@ hyperpoint mirrorif(const hyperpoint& V, bool b) { else return V; } +transmatrix mirrorif(const transmatrix& V, bool b) { + if(b) return V*Mirror; + else return V; + } + // -1 if away, 0 if not away int away(const transmatrix& V2) { return intval(C0, V2 * xpush0(1)) > intval(C0, tC0(V2)); @@ -305,7 +310,7 @@ void drawrec(const heptspin& hs, int lev, hstate s, const transmatrix& V) { if(dodrawcell(c)) { reclevel = maxreclevel - lev; - drawcell(c, (hs.spin || purehepta) ? V1 * spin(hs.spin*2*M_PI/S7 + (purehepta ? M_PI:0)) : V1, hs.spin, + drawcell(c, (hs.spin || purehepta) ? V1 * spin(hs.spin*2*M_PI/S7 + (purehepta ? M_PI:0)) : V1, 0, hs.mirrored); } diff --git a/init.cpp b/init.cpp index 22dff82c..40208126 100644 --- a/init.cpp +++ b/init.cpp @@ -1,6 +1,6 @@ -#define VER "9.4n2" -#define VERNUM 9416 -#define VERNUM_HEX 0x9416 +#define VER "10.0" +#define VERNUM 10000 +#define VERNUM_HEX 0xA000 #define GEN_M 0 #define GEN_F 1 @@ -264,7 +264,7 @@ const char *loadlevel = NULL; #include "landgen.cpp" #include "orbs.cpp" #ifdef INV -#include "closed/inventory.cpp" +#include "inventory.cpp" #else bool inv::on; #endif @@ -307,12 +307,13 @@ bool inv::on; #endif bool fixseed = false; +int startseed = 0; void initAll() { ca::init(); arg::read(1); srand(time(NULL)); - shrand(fixseed ? 0 : time(NULL)); + shrand(fixseed ? startseed : time(NULL)); achievement_init(); // not in ANDROID diff --git a/landgen.cpp b/landgen.cpp index ab5c9099..47bb9980 100644 --- a/landgen.cpp +++ b/landgen.cpp @@ -4,6 +4,19 @@ // land generation routines +int steplimit = 0; +int cstep; + +template +void limitgen(T... args) { + if(steplimit) { + cstep++; + printf("%6d ", cstep); + printf(args...); + if(cstep == steplimit) buggyGeneration = true; + } + } + vector buggycells; cell *pathTowards(cell *pf, cell *pt) { @@ -57,7 +70,7 @@ int isNative(eLand l, eMonster m) { case laAlchemist: return (m == moSlime) ? 2 : 0; - case laMirror: + case laMirror: case laMirrored: case laMirrorWall: case laMirrorWall2: case laMirrored2: return (m == moEagle || m == moRanger || m == moMirror || m == moMirage) ? 1 : 0; case laMotion: @@ -217,7 +230,10 @@ eItem treasureType(eLand l) { case laDesert: return itSpice; case laAlchemist: return itElixir; - case laMirror: return itShard; + + case laMirror: case laMirrored: case laMirrorWall: case laMirrorWall2: case laMirrored2: + return itShard; + case laMotion: return itFeather; case laGraveyard: return itBone; @@ -310,7 +326,7 @@ eItem wanderingTreasure(cell *c) { return treasureType(l); } -#define ORBLINES 54 +#define ORBLINES 56 struct orbinfo { eLand l; @@ -350,6 +366,7 @@ const orbinfo orbinfos[ORBLINES] = { {laOcean, 0, 3000, itOrbEmpathy}, {laOcean, 0, 0, itOrbAir}, {laPalace, 0, 4000, itOrbDiscord}, + {laPalace, 0, 0, itOrbFrog}, {laZebra, 500, 2100, itOrbFrog}, {laLivefjord, 0, 1800, itOrbFish}, {laPrincessQuest, 0, 200, itOrbLove}, @@ -374,6 +391,7 @@ const orbinfo orbinfos[ORBLINES] = { {laReptile, 500, 2100, itOrbDash}, {laBull, 720, 3000, itOrbHorns}, {laPrairie, 0, 3500, itOrbBull}, + {laWhirlpool, 0, 0, itOrbSafety}, {laWhirlpool, 0, 2000, itOrbWater}, // must be last because it generates a boat }; @@ -635,7 +653,8 @@ bool landUnlocked(eLand l) { return true; case laMirror: case laMinefield: case laPalace: - case laOcean: case laLivefjord: + case laOcean: case laLivefjord: case laMirrored: case laMirrorWall: case laMirrorWall2: + case laMirrored2: return gold() >= R30; case laCaribbean: case laWhirlpool: @@ -926,6 +945,10 @@ void setbarrier(cell *c) { else if(c->barleft == laHaunted || c->barright == laHaunted) { c->land = laHauntedWall; } + else if(c->barleft == laMirrored2 || c->barright == laMirrored2) + c->land = laMirrorWall2; + else if(c->barleft == laMirrored || c->barright == laMirrored) + c->land = laMirrorWall; else { c->wall = waBarrier; c->land = laBarrier; @@ -1016,24 +1039,36 @@ bool buildPrizeMirror(cell *c, int freq) { return true; } -void placePrizeOrb(cell *c) { +eLand getPrizeLand(cell *c = cwt.c) { eLand l = c->land; if(isElemental(l)) l = laElementalWall; + if(l == laPalace && princess::dist(c) < OUT_OF_PRISON) + l = laPrincessQuest; + return l; + } + +void placePrizeOrb(cell *c) { if(peace::on) return; + + eLand l = getPrizeLand(c); // these two lands would have too much orbs according to normal rules if(l == laPalace && hrand(100) >= 20) return; + if(l == laPrincessQuest && hrand(100) >= 20) return; if(l == laGraveyard && hrand(100) >= 15) return; if(l == laBurial && hrand(100) >= 10) return; if(l == laLivefjord && hrand(100) >= 35) return; if(l == laMinefield && hrand(100) >= 25) return; if(l == laElementalWall && hrand(100) >= 25) return; - if(l == laPalace && princess::dist(c) < OUT_OF_PRISON) - l = laPrincessQuest; for(int i=0; i= 100) ; + else continue; + } + eOrbLandRelation olr = getOLR(oi.orb, l); if(olr != olrPrize25 && olr != olrPrize3) continue; int treas = items[treasureType(oi.l)]; @@ -1069,7 +1104,10 @@ void placeLocalOrbs(cell *c) { for(int i=0; i= 50) ; + else if(oi.orb == itOrbSafety && items[itFeather] >= 25) ; + else continue; + } int treas = items[treasureType(oi.l)] * landMultiplier(oi.l); if(tactic::on && isCrossroads(tactic::lasttactic)) { if(oi.orb == itOrbYendor || oi.orb == itOrbSummon || oi.orb == itOrbFish || oi.orb == itOrbDigging || oi.orb == itOrbLove || oi.orb == itOrbLuck) @@ -1117,7 +1160,13 @@ void placeOceanOrbs(cell *c) { if(peace::on) return; for(int i=0; i= 50) ; + else if(oi.orb == itOrbSafety && items[itFeather] >= 25) ; + else continue; + } + if(items[treasureType(oi.l)] * landMultiplier(oi.l) < 10) continue; if(!oi.gchance) continue; if(oi.orb == itOrbLife) continue; // useless @@ -1193,8 +1242,28 @@ void extendcheck(cell *c) { raiseBuggyGeneration(c, "extend error"); } } + +bool oldmirror; + +bool inmirror(eLand l) { + return l == laMirrored || l == laMirrorWall2 || l == laMirrored2; + } + +bool inmirror(cell *c) { + return inmirror(c->land); + } + +bool inmirror(const cellwalker& cw) { + return inmirror(cw.c->land); + } + +bool mirrorwall(cell *c) { + return c->barleft == laMirrored || c->barright == laMirrored; + } void extendBarrierFront(cell *c) { + limitgen("extend front %p\n", c); + if(buggyGeneration) return; int ht = c->landparam; extendcheck(c); @@ -1205,7 +1274,8 @@ void extendBarrierFront(cell *c) { bb.c->barleft = c->barleft; bb.c->barright = c->barright; setbarrier(bb.c); - bb.c->landparam = (ht-4); + if(!mirrorwall(bb.c)) + bb.c->landparam = (ht-4); //printf("[A heat %d]\n", ht-4); cwspin(bb, 2); cwstep(bb); setland(bb.c, c->barleft); cwstep(bb); @@ -1216,11 +1286,15 @@ void extendBarrierFront(cell *c) { bb.c->barleft = c->barright; bb.c->barright = c->barleft; setbarrier(bb.c); - bb.c->landparam = (ht-4)^2; + if(!mirrorwall(bb.c)) + bb.c->landparam = (ht-4)^2; //printf("[B heat %d]\n", (ht-4)^2); cwspin(bb, 3); cwstep(bb); - bb.c->landparam = ht ^ 2; + bb.c->barleft = c->barleft; + bb.c->barright = c->barright; + if(!mirrorwall(bb.c)) + bb.c->landparam = ht ^ 2; } //printf("[C heat %d]\n", (ht)^2); @@ -1238,6 +1312,8 @@ void extendBarrierFront(cell *c) { } void extendBarrierBack(cell *c) { + limitgen("extend back %p\n", c); + if(buggyGeneration) return; int ht = c->landparam; extendcheck(c); @@ -1248,7 +1324,8 @@ void extendBarrierBack(cell *c) { bb.c->bardir = bb.spin; bb.c->barleft = c->barright; bb.c->barright = c->barleft; - bb.c->landparam = ht ^ 11; + if(!mirrorwall(bb.c)) + bb.c->landparam = ht ^ 11; extendcheck(bb.c); //printf("[D heat %d]\n", (ht^11)); @@ -1257,7 +1334,8 @@ void extendBarrierBack(cell *c) { cwstep(bb); bb.c->barleft = c->barright; bb.c->barright = c->barleft; - bb.c->landparam = (ht^11)-4; + if(!mirrorwall(bb.c)) + bb.c->landparam = (ht^11)-4; cwstep(bb); } //printf("[E heat %d]\n", (ht^11)); @@ -1266,11 +1344,13 @@ void extendBarrierBack(cell *c) { extendBarrier(bb.c); } -eLand oppositeElement(eLand l) { +eLand oppositeElement(eLand l, eLand l2) { if(l == laEFire) return laEWater; if(l == laEWater) return laEFire; if(l == laEAir) return laEEarth; if(l == laEEarth) return laEAir; + if(l == laMirror && l2 == laMirrored) return laMirrored2; + if(l == laMirrored2 && l2 == laMirrored) return laMirror; return l; } @@ -1353,7 +1433,14 @@ void extendCR5(cell *c) { } } +bool isbar4(cell *c) { + return + c->wall == waBarrier || c->land == laElementalWall || + c->land == laMirrorWall || c->land == laMirrorWall2; + } + void extendBarrier(cell *c) { + limitgen("extend barrier %p\n", c); if(buggyGeneration) return; if(c->barleft == NOWALLSEP_USED) return; @@ -1361,7 +1448,8 @@ void extendBarrier(cell *c) { extendcheck(c); // printf("build barrier at %p", c); - if(c->land == laBarrier || c->land == laElementalWall || c->land == laHauntedWall || c->land == laOceanWall) { + if(c->land == laBarrier || c->land == laElementalWall || c->land == laHauntedWall || c->land == laOceanWall || + c->land == laMirrorWall || c->land == laMirrorWall2) { // printf("-> ready\n"); return; } @@ -1376,14 +1464,40 @@ void extendBarrier(cell *c) { return; } + bool firstmirror = + (c->barleft == laMirrored || c->barright == laMirrored) && + c->barleft != laMirrored2 && c->barright != laMirrored2; + + if(firstmirror && c->barleft == laMirror && hrand(100) < 60) { + cellwalker cw(c, c->bardir); + if(!purehepta) cwstep(cw); + if(cw.c->land != laMirrorWall) + if(buildBarrier6(cw, 1)) return; + } + + if(firstmirror && (purehepta?c->barleft == laMirror : c->barright == laMirror) && hrand(100) < 60) { + cellwalker cw(c, c->bardir); + if(purehepta) { + cwspin(cw, -3); cwstep(cw); cwspin(cw, -3); +// cwspin(cw, 3); cwstep(cw); cwspin(cw, -2); cwstep(cw); cwspin(cw, 3); + } + else { + cwstep(cw); cwspin(cw, 3); cwstep(cw); cwspin(cw, -1); // check this + } + if(buildBarrier6(cw, 2)) return; + } + if(((c->barleft == laCrossroads3 || c->barright == laCrossroads3) && hrand(100) < 66) || - (isElemental(c->barleft) && isElemental(c->barright) && hrand(100) < 25)) { + (isElemental(c->barleft) && isElemental(c->barright) && hrand(100) < 75) + || (firstmirror && hrand(100) < 60) + ) { cellwalker cw(c, c->bardir); if(purehepta) { - cwstep(cw); if(cw.c->wall == waBarrier || cw.c->land == laElementalWall) { + cwstep(cw); + if(isbar4(cw.c)) { cwstep(cw); cwspin(cw, 3); cwstep(cw); cwspin(cw, -1); cwstep(cw); - bool b = buildBarrier4(cw.c, cw.spin, 2, oppositeElement(c->barleft), c->barright); + bool b = buildBarrier4(cw.c, cw.spin, 2, oppositeElement(c->barleft, c->barright), c->barright); if(b) return; } else { @@ -1394,9 +1508,9 @@ void extendBarrier(cell *c) { else { cwspin(cw, 3); cwstep(cw); cell *cp = cwpeek(cw, 4); - if(cp->wall != waBarrier && cp->land != laElementalWall) { + if(!isbar4(cp)) { cwspin(cw, 2); cwstep(cw); - bool b = buildBarrier4(cw.c, cw.spin, 2, oppositeElement(c->barleft), c->barright); + bool b = buildBarrier4(cw.c, cw.spin, 2, oppositeElement(c->barleft, c->barright), c->barright); if(b) return; } else { @@ -1602,6 +1716,8 @@ eLand getNewLand(eLand old) { eLand l = callhandlers(laNone, hooks_nextland, old); if(l) return l; + + if(old == laMirror && !oldmirror && hrand(10)) return laMirrored; if(cheatdest != old) if(!isCyclic(cheatdest) && !isTechnicalLand(cheatdest)) return cheatdest; @@ -1927,7 +2043,7 @@ void buildBarrierForce(cell *c, int d, eLand l) { landcount[newland]++; if(d == 4 || d == 5 || d == 6) c->barleft = oldland, c->barright = newland; else c->barleft = newland, c->barright = oldland; - c->landparam = 40; + if(!mirrorwall(c)) c->landparam = 40; extendcheck(c); } @@ -1939,7 +2055,124 @@ void buildBarrier(cell *c, int d, eLand l) { buildBarrierForce(c, d, l); } +bool buildBarrier6(cellwalker cw, int type) { + limitgen("build6 %p/%d (%d)\n", cw.c, cw.spin, type); + + cellwalker b[4]; + for(int i=0; i<4; i++) b[i] = cw; + + if(buggyGeneration) return true; + + if(!purehepta) { + cwstep(b[0]); + cwspin(b[1], 1); cwstep(b[1]); cwspin(b[1], 3); cwstep(b[1]); + cwspin(b[2], 4); cwstep(b[2]); + cwspin(b[3], 3); cwstep(b[3]); cwspin(b[3], 3); cwstep(b[3]); + } + else { + cwspin(b[1], 3); cwstep(b[1]); cwspin(b[1], 3); + cwspin(b[2], -2); cwstep(b[2]); cwspin(b[2], -3); + cwspin(b[3], -3); cwstep(b[3]); cwspin(b[3], 2); cwstep(b[3]); cwspin(b[3],-3); + if(type == 1 && b[3].c->land != laMirrorWall) return false; + if(type == 2 && cwpeek(b[1], 0)->land != laMirrorWall) return false; + // if(type == 2 && b[2].c->land != laMirrorWall) return false; + } + + if(false) { + for(int z=0; z<4; z++) { + printf("%p/%d\n", b[z].c, b[z].spin); + b[z].c->wall = waStrandedBoat; b[z].c->land = laAlchemist; + b[z].c->mondir = b[z].spin; + b[z].c->mpdist = 7; + b[z].c->item = eItem(1+z); + buggyGeneration = true; + } + return true; + } + + if(type == 1) { + if(!(purehepta?checkBarriersFront:checkBarriersBack)(b[1], 6, true)) return false; + if(!(purehepta?checkBarriersFront:checkBarriersBack)(b[2], 6, true)) return false; + } + else { + if(!(purehepta?checkBarriersFront:checkBarriersBack)(b[0], 6, true)) return false; + if(!(purehepta?checkBarriersFront:checkBarriersBack)(b[3], 6, true)) return false; + } + + for(int d=0; d<4; d++) { + b[d].c->bardir = b[d].spin; + + if(purehepta) { + b[0].c->barleft = laMirrored, b[0].c->barright = laMirrored2; + b[1].c->barleft = laMirror, b[1].c->barright = laMirrored; + b[2].c->barleft = laMirrored2, b[2].c->barright = laMirrored; + b[3].c->barleft = laMirrored, b[3].c->barright = laMirror; + } + else { + b[0].c->barleft = laMirror, b[0].c->barright = laMirrored; + b[1].c->barleft = laMirrored, b[1].c->barright = laMirror; + b[2].c->barleft = laMirrored, b[2].c->barright = laMirrored2; + b[3].c->barleft = laMirrored2, b[3].c->barright = laMirrored; + } + + (purehepta?extendBarrierFront:extendBarrierBack)(b[d].c); + } + + if(purehepta && false) { + for(int a=0; a<4; a++) + extendBarrierBack(cwpeek(b[a],0)); + } + + if(!purehepta) { + setland(cwpeek(cw, 1), laMirrorWall); + setland(cwpeek(cw, 2), laMirrored); + setland(cwpeek(cw, 3), laMirrorWall2); + setland(cwpeek(cw, 4), laMirrorWall2); + setland(cwpeek(cw, 5), laMirrored); + setland(cwpeek(cw, 0), laMirrorWall); + setland(cwpeek(b[0], 2), laMirrored); + setland(cwpeek(b[3], 6), laMirrored2); + setland(cwpeek(b[3], 5), laMirrored2); + setland(cwpeek(b[1], -1), laMirrored); + setland(cwpeek(b[2], -2), laMirrored); + setland(cwpeek(b[1], -2), laMirrored); + setland(cwpeek(b[0], -2), laMirror); + cw.c->land = laMirrorWall; + cw.c->wall = waMirrorWall; + cw.c->landparam = 1; + } + else { + setland(cw.c, laMirrorWall2); + setland(cwpeek(cw, 0), laMirrorWall2); + setland(cwpeek(cw, 1), laMirrored); + setland(cwpeek(cw, 2), laMirrored); + setland(cwpeek(cw, 3), laMirrorWall); + setland(cwpeek(cw, 4), laMirrored); + setland(cwpeek(cw, 5), laMirrorWall2); + setland(cwpeek(cw, 6), laMirrored2); + + setland(cwpeek(b[1], 0), laMirrorWall); + setland(cwpeek(b[1], 1), laMirror); + setland(cwpeek(b[1], 2), laMirrorWall); + setland(cwpeek(b[1], 6), laMirrored); + + cellwalker cf = b[0]; + cwstep(cf); + setland(cwpeek(cf, -2), laMirrored); + + cf = b[3]; + cwstep(cf); + setland(cwpeek(cf, -2), laMirrored); + } + + return true; + } + + + bool buildBarrier4(cell *c, int d, int mode, eLand ll, eLand lr) { + limitgen("build4 %p\n", c); + if(buggyGeneration) return true; d %= 7; cellwalker b1(c, d); @@ -1978,8 +2211,8 @@ bool buildBarrier4(cell *c, int d, int mode, eLand ll, eLand lr) { return false; } - eLand xl = oppositeElement(ll); - eLand xr = oppositeElement(lr); + eLand xl = oppositeElement(ll, lr); + eLand xr = oppositeElement(lr, ll); c->bardir = d, c->barleft = ll, c->barright = lr; extendBarrierBack(c); @@ -2235,6 +2468,9 @@ int coastval(cell *c, eLand base) { return 0; if(c->land != laGraveyard && c->land != laHauntedBorder) return 30; } + else if(base == laMirrored) { + if(!inmirror(c)) return 0; + } else { if(c->land == laOceanWall || c->land == laCaribbean || c->land == laWhirlpool || c->land == laLivefjord || c->land == laWarpSea || c->land == laKraken) @@ -2303,6 +2539,7 @@ void buildEquidistant(cell *c) { // buggycells.push_back(c); } if(b == laHauntedBorder) b = laGraveyard; + if(inmirror(b)) b = laMirrored; int mcv = UNKNOWN; // find the lowest coastval @@ -2326,7 +2563,7 @@ void buildEquidistant(cell *c) { for(int i=0; itype; i++) if(coastval(c->mov[i], b) == mcv) qcv++, sid = i; - + // if(generatingEquidistant) printf("qcv=%d mcv=%d\n", qcv, mcv); if(qcv >= 2) c->landparam = mcv+1; // (mcv == UNKNOWN ? UNKNOWN : mcv+1); else { @@ -2827,6 +3064,10 @@ void setLandEuclid(cell *c) { } } +#define INVLUCK (items[itOrbLuck] && inv::on) +#define I2000 (INVLUCK?600:2000) +#define I10000 (INVLUCK?3000:10000) + void buildBigStuff(cell *c, cell *from) { if(sphere || quotient) return; bool deepOcean = false; @@ -2867,7 +3108,7 @@ void buildBigStuff(cell *c, cell *from) { else if(c->type == 7 && c->land == laCrossroads4 && hrand(10000) < 7000 && c->land && buildBarrierNowall(c, getNewLand(laCrossroads4))) ; - else if(c->type == 7 && hrand(10000) < 20 && !generatingEquidistant && !yendor::on && !tactic::on && !isCrossroads(c->land) && gold() >= R200 && + else if(c->type == 7 && hrand(I10000) < 20 && !generatingEquidistant && !yendor::on && !tactic::on && !isCrossroads(c->land) && gold() >= R200 && !isSealand(c->land) && !isHaunted(c->land) && !isGravityLand(c->land) && (c->land != laRlyeh || rlyehComplete()) && c->land != laTortoise && c->land != laPrairie && c->land && @@ -2891,8 +3132,9 @@ void buildBigStuff(cell *c, cell *from) { } } - else if(c->type == 7 && c->land && hrand(10000) < ( + else if(c->type == 7 && c->land && hrand(I10000) < ( showoff ? (cwt.c->mpdist > 7 ? 0 : 10000) : + inmirror(c) ? 0 : isGravityLand(c->land) ? 0 : generatingEquidistant ? 0 : c->land == laPrairie ? 0 : @@ -2915,6 +3157,7 @@ void buildBigStuff(cell *c, cell *from) { (c->land == laGraveyard && items[itBone] >= 10) ? 120 : c->land == laOcean ? (deepOcean ? (purehepta ? 250 : 2000) : 0) : c->land == laDragon ? 120 : + (c->land == laMirror && !oldmirror) ? 6000 : 50)) { @@ -2927,7 +3170,7 @@ void buildBigStuff(cell *c, cell *from) { } if((!chaosmode) && bearsCamelot(c->land) && c->type == 7 && - (quickfind(laCamelot) || peace::on || (hrand(2000) < 200 && + (quickfind(laCamelot) || peace::on || (hrand(I2000) < 200 && items[itEmerald] >= U5 && !tactic::on))) { int rtr = newRoundTableRadius(); heptagon *alt = createAlternateMap(c, rtr+14, hsOrigin); @@ -2942,18 +3185,18 @@ void buildBigStuff(cell *c, cell *from) { // buildbigstuff if(c->land == laRlyeh && c->type == 7 && - (quickfind(laTemple) || peace::on || (hrand(2000) < 100 && + (quickfind(laTemple) || peace::on || (hrand(I2000) < 100 && items[itStatue] >= U5 && !randomPatternsMode && !tactic::on && !yendor::on))) createAlternateMap(c, 2, hsA); if(c->land == laJungle && c->type == 7 && - (quickfind(laMountain) || (hrand(2000) < 100 && + (quickfind(laMountain) || (hrand(I2000) < 100 && !randomPatternsMode && !tactic::on && !yendor::on && landUnlocked(laMountain)))) createAlternateMap(c, 2, hsA); if(c->land == laOvergrown && c->type == 7 && - (quickfind(laClearing) || (hrand(2000) < 25 && + (quickfind(laClearing) || (hrand(I2000) < 25 && !randomPatternsMode && items[itMutant] >= U5 && !tactic::on && !yendor::on))) { heptagon *h = createAlternateMap(c, 2, hsA); @@ -2977,8 +3220,11 @@ void buildBigStuff(cell *c, cell *from) { (princess::forceMouse ? (from && from->pathdist != INF) : (hrand(2000) < (peace::on ? 100 : 20))) && !c->master->alt && - (princess::challenge || kills[moVizier] || peace::on) && !tactic::on && !yendor::on) - createAlternateMap(c, 141, hsOrigin, waPalace); + (princess::challenge || kills[moVizier] || peace::on) && !tactic::on && !yendor::on) { + createAlternateMap(c, PRADIUS0, hsOrigin, waPalace); + celllister cl(c, 5, 1000000, NULL); + for(cell *c: cl.lst) if(c->master->alt) generateAlts(c->master); + } } if(hasbardir(c)) extendBarrier(c); @@ -3265,6 +3511,8 @@ void setdist(cell *c, int d, cell *from) { if(d <= 3) lastexplore = shmup::on ? shmup::curtime : turncount; + oldmirror = euclid || chaosmode || yendor::on || yendor::generating; + if(buggyGeneration) { if(d < BARLEV) for(int i=0; itype; i++) { setdist(createMov(c, i), d+(purehepta?2:1), c); @@ -3310,6 +3558,8 @@ void setdist(cell *c, int d, cell *from) { if(d == BARLEV && !euclid && c != cwt.c) buildBigStuff(c, from); + if(buggyGeneration) return; + if(d < 10) { explore[d]++; exploreland[d][c->land]++; @@ -3322,6 +3572,9 @@ void setdist(cell *c, int d, cell *from) { if(d == BARLEV-2 && c->land == laOcean) buildEquidistant(c); + if(d == BARLEV-2 && inmirror(c)) + buildEquidistant(c); + if(d == BARLEV-2 && (c->land == laGraveyard || c->land == laHauntedBorder || c->land == laHaunted)) buildEquidistant(c); @@ -3908,7 +4161,7 @@ void setdist(cell *c, int d, cell *from) { if(c->land == laPalace && !euclid && c->master->alt) { int d = celldistAlt(c); - if(d <= 150) generateAlts(c->master); + if(d <= PRADIUS1) generateAlts(c->master); } if((bearsCamelot(c->land) && !euclid && !quotient) || c->land == laCamelot) @@ -4734,9 +4987,12 @@ void setdist(cell *c, int d, cell *from) { princess::getPrisonInfo(c) && (euclid || (princess::getPrisonInfo(c)->bestdist < 6 && princess::getPrisonInfo(c)->princess))) { c->monst = moMouse; - addMessage(XLAT("You hear a distant squeak!")); - playSound(c, "mousesqueak"); - drawBigFlash(c); + if(!princess::squeaked) { + addMessage(XLAT("You hear a distant squeak!")); + playSound(c, "mousesqueak"); + drawBigFlash(c); + princess::squeaked = true; + } /* { cell *c2= c; z: @@ -5024,7 +5280,7 @@ void setdist(cell *c, int d, cell *from) { else { if(hyperstonesUnlocked() && hrand(25000) < min(PT(tkills(), 2000), 5000) && notDippingFor(itHyperstone)) c->item = itHyperstone; - if(hrand(4000) < items[itHyperstone] + 2 * items[itHolyGrail] && !c->monst) { + if(hrand(4000) < items[itHyperstone] && !c->monst) { // only interesting monsters here! eMonster cm = crossroadsMonster(); if(cm == moIvyRoot) buildIvy(c, 0, c->type); @@ -5035,14 +5291,19 @@ void setdist(cell *c, int d, cell *from) { } } } + if(c->land == laMirrored || c->land == laMirrorWall || c->land == laMirrorWall2 || + c->land == laMirrored2) + c->wall = waMirrorWall; if(c->land == laMirror) { - if((purehepta?pseudohept(c):!ishept(c)) && hrand(5000) < 120 && notDippingFor(itShard)) + int freqt = oldmirror ? 1 : 4; + int freqm = (oldmirror || cwt.c->land != laMirror) ? 1 : 30; + if((purehepta?pseudohept(c):!ishept(c)) && hrand(5000/freqt) < 120 && notDippingFor(itShard)) c->wall = hrand(2) ? waMirror : waCloud; - else if(ishept(c) && hrand(5000) < 10 * PRIZEMUL) + else if(ishept(c) && hrand(5000/freqt) < 10 * PRIZEMUL) placePrizeOrb(c); - else if(hrand(12000) < 8 + items[itShard] + hard) + else if(hrand(12000/freqt) < 8 + items[itShard] + hard) c->monst = moRanger; - else if(hrand(60000) < 8 + items[itShard] + hard) + else if(hrand(60000/freqm) < 8 + items[itShard] + hard) c->monst = moEagle; } if(c->land == laGraveyard) { @@ -5186,14 +5447,14 @@ void setdist(cell *c, int d, cell *from) { bool wchance(int a, int of) { of *= 10; - a += yendor::hardness() + items[itHolyGrail] + 1; + a += yendor::hardness() + 1; if(isCrossroads(cwt.c->land)) a+= items[itHyperstone] * 10; //if(cwt.c->land == laWhirlwind && !nowhirl) a += items[itWindstone] * 3; for(int i=0; iitem && hrand(5) == 0 && c->land != laHalloween) { if(passable(c, NULL, 0) || euclidland == laKraken) { @@ -5327,7 +5589,7 @@ void wandering() { continue; } - if(ghostcount && !c->monst) { + if(ghostcount && !c->monst && !inmirror(c)) { c->monst = moGhost; playSeenSound(c); ghostcount--; diff --git a/langen.cpp b/langen.cpp index 6e62e895..2859bb49 100644 --- a/langen.cpp +++ b/langen.cpp @@ -7,12 +7,12 @@ #define GEN_N 2 #define GEN_O 3 -using namespace std; #include #include #include #include #include +using namespace std; template int size(T x) { return x.size(); } diff --git a/language-pl.cpp b/language-pl.cpp index 37d3e087..2f5794ea 100644 --- a/language-pl.cpp +++ b/language-pl.cpp @@ -346,7 +346,7 @@ S("Hyperstone Quest: collect at least %3 %1 in %the2", "Misja alternatywna: znaj S("Hyperstone Quest completed!", "Misja alternatywna zakończona!") S("Look for the Orbs of Yendor in Hell or in the Crossroads!", "Szukaj Sfer Yendoru w Piekle albo na Skrzyżowaniu!") S("Unlock the Orb of Yendor!", "Otwórz Sferę Yendoru!") -S("Defeat 100 enemies to access the Graveyard", "Pokonaj 100 przeciwników, by trafić na Cmentarz") +S("Defeat %1 enemies to access the Graveyard", "Pokonaj %1 przeciwników, by trafić na Cmentarz") S("(press ESC during the game to review your quest)", "(naciśnij ESC w trakcie gry, by zobaczyć stan swojej misji)") S("you have cheated %1 times", "liczba oszustw: %1") S("%1 turns (%2)", "kolejek: %1 (%2)") @@ -5440,19 +5440,31 @@ S( "Naciśnij '5' by opuścić podręcznik." ) -#undef Orb - /* // for 10.0 +*/ -S("configure keys/joysticks", "konfiguracja klawiszy/joysticka") -S("Press F5 or 'o' to try again!", "Naciśnij F5 lub 'o' by spróbować jeszcze raz!") -S("peaceful mode", "tryb pokojowy") -S("inventory mode", "tryb inwentarza") -S("inventory", "sfery") +// Orb Strategy mode +S("Orb Strategy mode", "tryb strategii sfer") + +S( + "You are playing in the Orb Strategy Mode. Collecting treasure " + "gives you access to magical Orb powers. In this mode, " + "unlocking requirements are generally higher, and " + "several quests and lands " + "give you extremely powerful Orbs of the Mirror.\n", + + "Grasz w trybie strategii sfer. Zebrane skarby dają Ci " + "dostęp do magicznych mocy. W tym trybie wymagania " + "są generalnie wyższe, i niektóre krainy i misje " + "dają Ci bardzo potężne Sfery Lustra.\n") + +S("The treasure gives your magical powers!", "Skarby dają Ci moce magiczne!") +S("Press 'i' to access your magical powers.", "Naciśnij 'i', by użyć mocy.") +S("inventory", "Twoje sfery") S("mirror what?", "co odbić?") S("Which orb to use?", "Której Sfery użyć?") -S("Unlocked by: %1 in %2", "Odblokwane przez: %1 %abl2") +S("Unlocked by: %1 in %2", "Odblokowane przez: %1 %abl2") S(" (next at %1)", " (kolejny przy %1)") S(" (next at %1 to %2)", " (kolejny przy %1 do %2)") S("Number of uses left: %1", "Pozostało użyć: %1") @@ -5460,4 +5472,223 @@ S("You mirror %the1.", "Odbijasz %a1.") S("You need to stand next to a magic mirror or cloud to use %the1.", "Musisz stać przy magicznym lustrze, by odbić %a1.") S("Each orb type can be mirrored only once.", "Każdy typ sfery może być odbity tylko raz.") -*/ \ No newline at end of file + +S( + "\n\nIn the Orb Strategy Mode, Orbs of Yendor appear in Hell after " + "you collect 25 Demon Daisies in Hell, in Crossroads/Ocean after you collect 50, " + "and everywhere after you collect 100.", + + "\n\nW trybie strategii sfer Sfery Yendoru się pojawiają w Piekle " + "po zebraniu 25 Czarcich Ziel, na Skrzyżowaniu/Oceanie po zebraniu 50, " + "wszędzie po zebraniu 100." + ); + +S( + "\n\nIn the Orb Strategy Mode, dead orbs are available once you collect " + "10 Necromancer Totems in the Graveyard.", + + "\n\nW trybie strategii sfer martwe sfery są dostępne po zdobyciu " + "10 Totemów Nekromanty na Cmentarzu.") + +S( + "\n\nIn the Orb Strategy Mode, Orbs of Safety can be gained by " + "collecting Phoenix Feathers in the Land of Eternal Motion. " + "You can also find unlimited Orbs of Safety in the Crossroads " + "and the Ocean (after collecting 25 Phoenix Feathers) " + "and in the Prairie.", + + "\n\nW trybie strategii sfer Sfery Bezpieczeństwa mogą być zdobyte " + "przez zbieranie Piór Feniksa w Krainie Wiecznego Ruchu. " + "Można też znaleźć nieograniczone Sfery Bezpieczeństwa na " + "Skrzyżowaniach (po zdobyciu 25 Piór Feniksa) i na Prerii." + ) + +S( + "\n\nCollect %the1 to gain an extra Orb of the Mirror. " + "You can gain further Orbs of the Mirror by collecting 2, 4, 8...", + + "\n\nZdobądź %a1 by dostać dodatkową Sferę Lustra. " + "Więcej Sfer Lustra dostaniesz przy 2, 4, 8..." + ) + +S( + "\n\nIn the Orb Strategy Mode, the Orb of Luck also " + "significantly increases the frequency of Great Walls, Crossroads IV, " + "and sub-lands.", + + "\n\nW trybie strategii sfer Sfera Szczęścia dodatkowo " + "znacznie zwiększa częstotliwość wielkich ścian, Skrzyżowań IV, " + "i podkrain.") + +S("\n\nIn the Orb Strategy Mode, each 25 Necromancer's Totems " + "you are given a random offensive Orb.", + + "\n\nW trybie strategii sfer każde 25 Totemów Nekromanty " + "daje dodatkową ofensywną sferę.") + +S( + "Use Orb of the Mirror to gain copies of one of your Orbs; " + "mirroring weaker Orbs usually yields more copies. " + "It can only be used once per Orb type, " + "and only when you are next to a mirror.", + + "Użyj Sfery Lustra by skopiować jedną z Twoich sfer; " + "odbijanie słabszych Sfer zwykle daje więcej kopii. " + "Możesz użyć tylko raz na każdy typ Sfery, " + "i tylko stojąc obok lustra.") + +S("Uses to gain: %1", "Dostaniesz użyć: %1") +S("already mirrored", "już było odbijane") + +N("your orbs", GEN_F, "Twoje Sfery", "Twoje Sfery", "Twoje Sfery", "Twoje Sfery") +S("Click this to see your orbs.", "Kliknij by zobaczyć Twoje sfery.") + +// peaceful mode +S("configure keys/joysticks", "konfiguracja klawiszy/joysticka") +S("peaceful mode", "tryb spokojny") + +// config changes +S("Press F5 or 'o' to try again!", "Naciśnij F5 lub 'o' by spróbować jeszcze raz!") +S("aura brightness", "jasność aury") +S("aura smoothening factor", "wygładzanie aury") +S("graphics configuration", "konfiguracja grafiki") +S("special display modes", "specjalne tryby ekranu") +S("openGL mode", "tryb OpenGL") +S("anti-aliasing", "anti-aliasing") +S("line width", "szerokość linii") +S("configure panning and general keys", "skonfiguruj klawisze ogólne") + +S("\n\nHint: use 'm' to toggle cells quickly", + "\n\nWsk: użyj 'm' by szybko przestawiać pola"); + +// cell pattern names +S("football", "piłka nożna") +S("dark rainbow landscape", "ciemna tęcza") +S("field pattern", "wzór pola") +S("field pattern C", "wzór pola C") +S("field pattern D", "wzór pola D") +S("field pattern N", "wzór pola N") +S("field pattern S", "wzór pola S") +S("four triangles", "cztery trójkąty") +S("big triangles: rings", "duże trójkąty: pierścenie") + +// missing for the Tutorial +S("tutorial", "podręcznik") +S("This Orb is not compatible with the Tutorial.", "Ta Sfera nie jest kompatybilna z podręcznikiem.") + +// local scores +S("turns", "kol") +S("cells", "pola") +S("sort", "sortuj") +S("choose", "wybór") +S("play", "graj") + +// draw editor +S("autochoose", "autowybór") +S("c = choose", "c = wybór") +S("b = switch auto", "b = ustaw auto") + +// mission screen hints + +S( + "If you collect too many treasures in a given land, it will become " + "extremely dangerous. Try other lands once you have enough!", + "Jeśli zbierzesz za dużo skarbów w jednej krainie, stanie się ona " + "bardzo niebezpieczna. Spróbuj pójść do innych krain, gdy masz dość!"); + +S( + "Remember that you can right click mostly anything for more information.", + "Pamiętaj, że prawie wszystko możesz kliknąć prawym przyciskiem, " + "by dowiedzieć się czegoś na dany temat.") + +S("Want to understand the geometry in HyperRogue? Try the Tutorial!", + "Chcesz zrozumieć geometrię w HyperRogue? Obejrzyj Podręcznik!"); + +S( + "Collecting 25 treasures in a given land may be dangerous, " + "but allows magical Orbs of this land to appear in other places!", + "Zebranie 25 skarbów w jednej krainie może być niebezpieczne, " + "ale powoduje, że magiczne Sfery z tej krainy " + "pojawiają się w pozostałych krainach!") + +S( + "Press ESC to view this screen during the game.", + "Naciśnij ESC w czasie gry, by obejrzeć ten ekran.") + +S("The 'world overview' shows all the lands in HyperRogue.", + "'Przegląd krain' pokazuje wszystkie krainy w HyperRogue." + ) + +S("Press 'o' to see all the lands in HyperRogue.", + "Naciśnij 'o', by zobaczyć wszystkie krainy w HyperRogue.") + +S( + "Want another type of game? Want more challenge?\n" + "HyperRogue has many special modes and challenges that " + "significantly change the gameplay. Try them!", + + "Chcesz spróbować innego typu gry? Dodatkowych wyzwań?\n" + "HyperRogue ma dużo specjalnych trybów, istotnie " + "zmieniających styl gry. Wypróbuj je!") + +S( + "Hyperbolic geometry can be shown in many ways.", + "Geometria hiperboliczna może być pokazana na wiele sposobów...") + +S( + "You do not want to lose the game from a single mistake?\n" + "Do you want to use the Orbs strategically?\n" + "Try the Orb Strategy mode!", + + "Nie chcesz ginąć od jednego błędu? Chcesz używać Sfer strategicznie? " + "Wypróbuj tryb strategii sfer!") + +S( + "Do you think you are playing on a ball? " + "This is far from the truth!\n", + + "Myślisz, że grasz na sferze? Jest to dalekie od prawdy!\n") + +S( + "Did you know that the path you take during the game " + "is usually very close to a straight line?\n", + + "Czy wiesz, że droga, którą przebywasz w czasie gry, " + "jest zwykle bardzo zbliżona do linii prostej?") + +S("Show me!", "Pokaż!") + +S( + "You are %1 cells away from the starting point, or " + "the place where you used an Orb of Safety last time. " + "There are %2 such cells.\n", + + "Jesteś %1 kroków od punktu startu, lub miejsca " + "ostatniego użycia Sfery Bezpieczeństwa. " + "Takich pól jest %2.\n") + +S("about ", "około ") +S(" (%1 more digits)", " (jeszcze cyfr: %1)") + +S("see how it ended", "jak się skończyło") + +// other missing/new things +S("\n\nOrb unlocked: %1", "\n\nOdblokowana Sfera: %1") +S("Orb unlocked: %1", "Odblokowana Sfera: %1") +S("\n\nSecondary orb: %1", "\n\nDodatkowa Sfera: %1") +S(" to submerge", " do zanurzenia") +S(" to surface", " do wynurzenia") +S("%The1 says, \"not this place, it looks even worse...\"", + "%The1 mówi, \"nie tu, tu jest jeszcze gorzej...\"") +S("torus", "torus") +S(" (click to use)", " (klik by użyć)") +N("Hall of Mirrors", GEN_F, "Lustrzana Sala", "Lustrzane Sale", "Lustzaną Salę", "w Lustrzanej Sali") +Orb("the Mirror", "Lustra") +N("Reflection", GEN_N, "Odbicie", "Odbicia", "Odbicie", "w Odbiciu") +N("mirror wall", GEN_F, "lustrzana ściana", "lustrzane ściany", "lustrzaną ścianę", "lustrzaną ścianą") + +S("This would only move you deeper into the trap!", + "To tylko przeniesie Cię w głąb pułapki!"); + +#undef Orb + diff --git a/mapeditor.cpp b/mapeditor.cpp index bcd6a284..b1258948 100644 --- a/mapeditor.cpp +++ b/mapeditor.cpp @@ -1150,7 +1150,7 @@ namespace mapeditor { else if(uni == 'p') { painttype = 6; paintwhat_str = "paint"; - dialog::openColorDialog(paintwhat = (painttype ==6 ? paintwhat : 0x808080)); + dialog::openColorDialog((unsigned&)(paintwhat = (painttype ==6 ? paintwhat : 0x808080))); } else if(sym == SDLK_F2) { if(mapstream::saveMap(levelfile.c_str())) @@ -1219,7 +1219,7 @@ namespace mapeditor { int dslayer; bool coloring; - int colortouse = 0xC0C0C0FF; + unsigned int colortouse = 0xC0C0C0FFu; transmatrix drawtrans, drawtransnew; @@ -1280,6 +1280,7 @@ namespace mapeditor { void showDrawEditor() { cmode = sm::DRAW; + gamescreen(0); drawGrid(); if(!mouseout()) getcstat = '-'; @@ -1882,7 +1883,7 @@ namespace mapeditor { } #ifndef NOEDIT - if((cmode == sm::DRAW) && mapeditor::editingShape(group, id)) { + if((cmode & sm::DRAW) && mapeditor::editingShape(group, id)) { /* for(int a=0; amov[lig.spin] == NULL) break; cwstep(lig); + if(inmirror(lig)) lig = mirror::reflect(lig); cell *c = lig.c; @@ -1188,3 +1189,79 @@ eItem targetRangedOrb(cell *c, orbAction a) { return eItem(-1); } +int orbcharges(eItem it) { + switch(it) { + case itRevolver: //pickup-key + return 6; + case itOrbShield: + return inv::on ? 30 : 20; + case itOrbDiscord: + return inv::on ? 46 : 23; + case itOrbLove: + case itOrbUndeath: + case itOrbSpeed: //"pickup-speed"); + case itOrbInvis: + case itOrbAether: + return 30; + case itOrbWinter: // "pickup-winter" + return inv::on ? 45 : 30; + break; + case itOrbBeauty: + case itOrbEmpathy: + case itOrbFreedom: + return 40; + case itOrbFrog: + case itOrbDash: + return 45; + case itOrb37: + case itOrbEnergy: + return 50; + case itOrbRecall: + case itOrbNature: + case itOrbStone: + case itOrbStunning: + case itOrbLuck: + return 60; + case itOrbWater: + case itOrbMatter: + case itOrbHorns: + case itOrbBull: + case itOrbShell: + return 66; + case itOrbTime: + case itOrbSpace: + case itOrbThorns: + case itOrbLightning: + case itOrbFlash: + case itOrbIllusion: + case itOrbPsi: + case itOrbDigging: + case itOrbTeleport: + return 77; + case itOrbDomination: + return 90; + case itOrbSummon: + return 120; + + case itOrbSword: + return 60 + 30 * multi::activePlayers(); + case itOrbSword2: + return 40 + 20 * multi::activePlayers(); + case itOrbFish: + return 20 + 10 * multi::activePlayers(); + case itOrbFire: + return sphere ? 3 : 30; + case itOrbDragon: + return sphere ? 10 : 77; + default: + return 0; + } + } + +bool isShmupLifeOrb(eItem it) { + return + it == itOrbLife || it == itOrbFriend || + it == itOrbNature || it == itOrbEmpathy || + it == itOrbUndeath || it == itOrbLove || + it == itOrbDomination; + } diff --git a/polygons.cpp b/polygons.cpp index 0e3d6751..66001034 100644 --- a/polygons.cpp +++ b/polygons.cpp @@ -630,10 +630,11 @@ hpcshape shBarrowFloor[3], shTriheptaFloor[11], shTriheptaFloor2[2], shTriheptaEuc[3], shCross, shGiantStar[2], shLake, shMirror, + shHalfFloor[3], shHalfMirror[3], shGem[2], shStar, shDisk, shDiskT, shDiskS, shDiskM, shDiskSq, shRing, shEgg, shSpikedRing, shTargetRing, shSawRing, shGearRing, shPeaceRing, shHeptaRing, - shSpearRing, + shSpearRing, shLoveRing, shDaisy, shTriangle, shNecro, shStatue, shKey, shGun, shFigurine, shTreat, @@ -1112,6 +1113,36 @@ void buildpolys() { } hpcpush(ddi(0, crossf * .25) * C0); + /* three nice spikes + bshape(shLoveRing, PPR_ITEM); + for(int i=0; i<=S84; i+=3) + hpcpush(ddi(i, crossf * .25) * C0); + for(int i=S84; i>=0; i--) { + int j = i*3 % S84; + int d = j - S42; + if(d<0) d = -d; + d = 8 - 2 * d; + if(d<0) d = 0; + hpcpush(ddi(i, crossf * (.3 + .02 * d)) * C0); + } + hpcpush(ddi(0, crossf * .25) * C0); + */ + + bshape(shLoveRing, PPR_ITEM); + for(int i=0; i<=S84; i+=3) + hpcpush(ddi(i, crossf * .25) * C0); + for(int i=S84; i>=0; i--) { + int j = i*3 % S84; + double d = j - S42; + d = d / 9; + if(d<0) d = -d; + d = 8 - 2 * d; + if(d<0) d = 0; + if(d >= 6) d -= (d-6)/3; + hpcpush(ddi(i, crossf * (.27 + .02 * d)) * C0); + } + hpcpush(ddi(0, crossf * .25) * C0); + bshape(shSawRing, PPR_ITEM); for(int i=0; i<=S84; i+=3) hpcpush(ddi(i, crossf * .25) * C0); @@ -1242,6 +1273,13 @@ void buildpolys() { bshape(shButterflyFloor[0], PPR_FLOOR, scalef*spzoom6, 325); bshape(shButterflyFloor[1], PPR_FLOOR, scalef*spzoomd7, 326); + bshape(shHalfFloor[0], PPR_FLOOR, scalef*spzoom6, 329); + bshape(shHalfFloor[1], PPR_FLOOR, scalef*spzoom6, 327); + bshape(shHalfFloor[2], PPR_FLOOR, scalef*spzoom6, 331); + bshape(shHalfMirror[0], PPR_WALL, scalef*spzoom6, 330); + bshape(shHalfMirror[1], PPR_WALL, scalef*spzoom6, 328); + bshape(shHalfMirror[2], PPR_WALL, scalef*spzoom6, 332); + bshape(shLeafFloor[0], PPR_FLOOR_DRAGON, 1*spzoom6, 313); bshape(shLeafFloor[1], PPR_FLOOR_DRAGON, 1*spzoomd7, 314); @@ -2009,6 +2047,10 @@ void queuecircle(const transmatrix& V, double size, int col) { queuecircle(xc, yc, int(sqrt(squar(xc-xs)+squar(yc-ys)) * size), col); } +void queuemarkerat(const transmatrix& V, int col) { + queuepolyat(V, shTriangle, col, PPR_LINE); + } + long double polydata[] = { // shStarFloor[0] (6x1) NEWSHAPE, 1,6,1, 0.267355,0.153145, 0.158858,0.062321, 0.357493,-0.060252, @@ -2660,6 +2702,20 @@ NEWSHAPE, 325, 3, 1, 0.003906,-0.017741, -0.005665,-0.023874, -0.001296,-0.05925 // shButterflyFloor[1] NEWSHAPE, 326, 7, 1, -0.199281,-0.117040, -0.202870,-0.110023, -0.247957,-0.128116, -0.298501,0.006170, -0.226086,0.045756, -0.061553,0.006677, -0.059070,0.020733, -0.217691,0.072727, +// halfhepta +NEWSHAPE, 327, 1, 1, 0.335252,0.044112, 0.225849,0.283419, -0.081851,0.347313, -0.325491,0.159424, -0.323584,0.033019, +// hepta mirror +NEWSHAPE, 328, 1, 2, -0.315398,0.010102, 0.568278,0.010645, + +// halfhex +NEWSHAPE, 329, 1, 1, 0.263160,0.022375, 0.265137,0.152727, 0.000228,0.306625, -0.261438,0.151819, -0.263489,0.020161, +// halfhex mirror +NEWSHAPE, 330, 1, 2, 0.262597,0.018558, -0.261563,0.016306, + +NEWSHAPE, 331, 1, 1, 0.148337,0.215535, 0.267624,0.150567, 0.262973,0.019662, 0.033981,0.019835, +// 0 0 1 [000000] +NEWSHAPE, 332, 6, 2, -0.016778,-0.008267, -0.261607,-0.011992, + NEWSHAPE }; diff --git a/quit.cpp b/quit.cpp index 42a6f18c..e3ddcc89 100644 --- a/quit.cpp +++ b/quit.cpp @@ -19,13 +19,6 @@ string timeline() { XLAT("%1 turns (%2)", its(turncount), buf); } -struct hint { - time_t last; - function usable; - function display; - function action; - }; - void noaction() {} function cancel = noaction; @@ -58,7 +51,7 @@ hint hints[] = { }, []() { dialog::addHelp(XLAT( - "If you collect too many treasures in a given land, monsters will be " + "If you collect too many treasures in a given land, it will become " "extremely dangerous. Try other lands once you have enough!")); }, noaction}, @@ -97,7 +90,7 @@ hint hints[] = { []() { return !inv::on; }, []() { dialog::addHelp(XLAT( - "Collecting 25 treasures in a given land is dangerous, " + "Collecting 25 treasures in a given land may be dangerous, " "but allows magical Orbs of this land to appear in other places!" )); }, @@ -136,7 +129,7 @@ hint hints[] = { 0, []() { return !canmove; }, []() { - dialog::addInfo(XLAT( + dialog::addHelp(XLAT( "Want another type of game? Want more challenge?\n" "HyperRogue has many special modes and challenges that " "significantly change the gameplay. Try them!" @@ -174,8 +167,6 @@ hint hints[] = { dialog::addBreak(50); #ifdef INF dialog::addItem(XLAT("Orb Strategy mode"), 'z'); -#else - dialog::addItem(XLAT("(paid versions only)"), 'z'); #endif }, []() { @@ -193,7 +184,7 @@ hint hints[] = { "This is far from the truth!\n" )); dialog::addBreak(50); - dialog::addItem(XLAT("Hypersian Rug mode"), 'z'); + dialog::addItem(XLAT("hypersian rug mode"), 'z'); }, [] () { popScreen(); @@ -217,7 +208,7 @@ hint hints[] = { []() { dialog::addHelp(XLAT( "Did you know that the path you take during the game " - "is very close to a straight line?\n" + "is usually very close to a straight line?\n" )); dialog::addBreak(50); dialog::addItem(XLAT("Show me!"), 'z'); @@ -299,6 +290,10 @@ hint hints[] = { int hinttoshow; +string contstr() { + return XLAT(canmove ? "continue" : "see how it ended"); + } + void showMission() { cmode = sm::DOTOUR | sm::MISSION | sm::CENTER; @@ -439,9 +434,11 @@ void showMission() { #endif } else { - dialog::addItem(XLAT(canmove ? "continue" : "see how it ended"), SDLK_ESCAPE); + dialog::addItem(contstr(), SDLK_ESCAPE); dialog::addItem(XLAT("main menu"), 'v'); dialog::addItem(XLAT("restart"), SDLK_F5); + if(inv::on && items[itInventory]) + dialog::addItem(XLAT("inventory"), 'i'); #ifndef MOBILE dialog::addItem(XLAT(quitsaves() ? "save" : "quit"), SDLK_F10); #endif @@ -469,7 +466,7 @@ void handleKeyQuit(int sym, int uni) { if(sym == SDLK_RETURN || sym == SDLK_KP_ENTER || sym == SDLK_F10) quitmainloop = true; else if(uni == 'r' || sym == SDLK_F5) { - restartGame(), popScreen(); + restartGame(); msgs.clear(); } else if(sym == SDLK_UP || sym == SDLK_KP8 || sym == PSEUDOKEY_WHEELUP) msgscroll++; @@ -524,5 +521,7 @@ void showMissionScreen() { } hints[hinttoshow].last = time(NULL); } + + dialog::highlight_text = contstr(); } diff --git a/rug.cpp b/rug.cpp index fa53a821..e1eafc62 100644 --- a/rug.cpp +++ b/rug.cpp @@ -42,7 +42,7 @@ bool glew = false; bool renderonce = false; bool rendernogl = false; int texturesize = 1024; -double scale = 1; +ld scale = 1; int queueiter, qvalid, dt; double err; diff --git a/scores.cpp b/scores.cpp index 640d505e..161b6586 100644 --- a/scores.cpp +++ b/scores.cpp @@ -39,6 +39,7 @@ bool isHardcore(score *S) { int modediff(score *S) { int diff = 0; eGeometry g = (eGeometry) S->box[116]; + if(S->box[306] != inv::on) diff += 4; if(S->box[238]) g = gSphere; if(S->box[239]) g = gElliptic; if(max(S->box[197], 1) != multi::players) diff += 8; @@ -64,6 +65,7 @@ string modedesc(score *S) { if(S->box[196]) s += "/C"; if(S->box[119]) s += "/s"; if(S->box[197] > 1) s += "/P" + its(S->box[197]); + if(S->box[306]) s += "/i"; if(isHardcore(S)) s += "/h"; return s; } @@ -301,7 +303,7 @@ void load() { for(int i=boxid; i= "4.4") { + if(!verless(sc.ver, "4.4")) { sc.box[0] = sc.box[65]; // the first executable on Steam included a corruption if(sc.box[65] > 1420000000 && sc.box[65] < 1430000000) { diff --git a/shmup.cpp b/shmup.cpp index 8d45f0c5..3f31be8f 100644 --- a/shmup.cpp +++ b/shmup.cpp @@ -60,6 +60,9 @@ namespace multi { movedir whereto[MAXPLAYER]; // player's target cell double mdx[MAXPLAYER], mdy[MAXPLAYER]; // movement vector for the next move + + static const int CMDS = 15; + static const int CMDS_PAN = 11; const char* playercmds_shmup[15] = { "forward", "backward", "turn left", "turn right", @@ -77,13 +80,15 @@ namespace multi { "" }; - const char* pancmds[7] = { + const char* pancmds[11] = { "pan up", "pan right", "pan down", "pan left", - "rotate left", "rotate right", "home" + "rotate left", "rotate right", "home", + "world overview", "review your quest", "inventory", "main menu" }; #define SHMUPAXES_BASE 4 #define SHMUPAXES ((SHMUPAXES_BASE) + 4 * (MAXPLAYER)) +#define SHMUPAXES_CUR ((SHMUPAXES_BASE) + 4 * vid.scfg.players) const char* axemodes[SHMUPAXES] = { "do nothing", @@ -122,44 +127,6 @@ const char* axemodes[SHMUPAXES] = { int centerplayer = -1; -#ifndef NOCONFIG -void saveConfig(FILE *f) { - fprintf(f, "%d %d %d", VERNUM, vid.scfg.players, alwaysuse); - for(int i=0; i<512; i++) fprintf(f, " %d", vid.scfg.keyaction[i]); - for(int i=0; i MAXPLAYER) - vid.scfg.players = 1; - if(err != 2) return; - if(xvernum >= 8990) { int b=alwaysuse; err=fscanf(f, " %d", &b); alwaysuse = b; } - for(int i=0; i<512; i++) scanchar(f, vid.scfg.keyaction[i]); - for(int i=0; i= 9007) - for(int i=0; i 1) - dialog::addItem(XLAT("configure panning"), 'p'); + dialog::addItem(XLAT("configure panning and general keys"), 'p'); else dialog::addBreak(100); if(numsticks > 0) { @@ -442,8 +410,8 @@ help += XLAT("This menu can be also used to configure keys.\n\n"); else { int v = (*axeconfigs[xuni - 'a']); v += (shiftmul>0?1:-1); - v += SHMUPAXES; - v %= SHMUPAXES; + v += SHMUPAXES_CUR; + v %= SHMUPAXES_CUR; (*axeconfigs[xuni - 'a']) = v; } } @@ -494,63 +462,7 @@ bool notremapped(int sym) { return k > multi::players; } -void handleInput(int delta) { -#ifndef NOSDL - double d = delta / 500.; - - Uint8 *keystate = SDL_GetKeyState(NULL); - - for(int i=0; i dz) value -= dz; else if(value < -dz) value += dz; - else value = 0; - axespressed[vid.scfg.axeaction[j][b] % SHMUPAXES] += value; - } - } - - if(keystate[SDLK_LCTRL] || keystate[SDLK_RCTRL]) d /= 5; - - double panx = - actionspressed[49] - actionspressed[51] + axespressed[2] / 32000.0; - double pany = - actionspressed[50] - actionspressed[48] + axespressed[3] / 20000.0; - - double panspin = actionspressed[52] - actionspressed[53] + axespressed[1] / 20000.0; - - if(actionspressed[54]) { centerplayer = -1, playermoved = true; centerpc(100); } - - if(panx || pany || panspin) { - View = xpush(-panx * d) * ypush(-pany * d) * spin(panspin * d) * View; - playermoved = false; - } -#endif - } - -int tableid[7] = {1, 2, 4, 5, 6, 7, 8}; +#ifndef NOCONFIG void initConfig() { vid.scfg.players = 1; @@ -650,8 +562,125 @@ void initConfig() { multi::scs[4].uicolor = 0xC000C0FF; multi::scs[5].uicolor = 0x00C0C0FF; multi::scs[6].uicolor = 0xC0C0C0FF; + + addsaver(vid.scfg.players, "mode-number of players"); + addsaver(alwaysuse, "use configured keys"); + // unfortunately we cannot use key names here because SDL is not yet initialized + for(int i=0; i<512; i++) + addsaver(vid.scfg.keyaction[i], string("key:")+its(i)); + for(int i=0; i MAXPLAYER) + vid.scfg.players = 1; + if(err != 2) return; + if(xvernum >= 8990) { int b=alwaysuse; err=fscanf(f, " %d", &b); alwaysuse = b; } + for(int i=0; i<512; i++) scanchar(f, vid.scfg.keyaction[i]); + for(int i=0; i= 9007) + for(int i=0; i dz) value -= dz; else if(value < -dz) value += dz; + else value = 0; + axespressed[vid.scfg.axeaction[j][b] % SHMUPAXES] += value; + } + } + + if(keystate[SDLK_LCTRL] || keystate[SDLK_RCTRL]) d /= 5; + + double panx = + actionspressed[49] - actionspressed[51] + axespressed[2] / 32000.0; + double pany = + actionspressed[50] - actionspressed[48] + axespressed[3] / 20000.0; + + double panspin = actionspressed[52] - actionspressed[53] + axespressed[1] / 20000.0; + + if(actionspressed[54]) { centerplayer = -1, playermoved = true; centerpc(100); } + + if(actionspressed[55] && !lactionpressed[55]) + pushScreen(showOverview); + + if(actionspressed[56] && !lactionpressed[56]) + showMissionScreen(); + +#ifdef INV + if(actionspressed[57] && !lactionpressed[57]) + pushScreen(inv::show); +#endif + + if(actionspressed[58] && !lactionpressed[58]) + pushScreen(showMainMenu); + + if(panx || pany || panspin) { + View = xpush(-panx * d) * ypush(-pany * d) * spin(panspin * d) * View; + playermoved = false; + } +#endif + } + + int tableid[7] = {1, 2, 4, 5, 6, 7, 8}; + + void leaveGame(int i) { multi::player[i].c = NULL; multi::deaths[i]++; diff --git a/system.cpp b/system.cpp index f057bab3..f60e6c48 100644 --- a/system.cpp +++ b/system.cpp @@ -15,6 +15,13 @@ bool timerstopped; int savecount; bool showoff = false, doCross = false; +bool verless(const string& v, const string& cmp) { + // no checks exists for versions greater than 10.0 yet + if(isdigit(v[0]) && isdigit(v[1])) + return false; + return v < cmp; + } + // initialize the game void initgame() { DEBB(DF_INIT, (debugfile,"initGame\n")); @@ -167,6 +174,8 @@ void initgame() { makeEmpty(cwt.c); } + princess::squeaked = false; + if(!safety) { usedSafety = false; timerstart = time(NULL); turncount = 0; rosewave = 0; rosephase = 0; @@ -254,8 +263,8 @@ void initgame() { bool havesave = true; #ifndef NOSAVE -#define MAXBOX 300 -#define POSSCORE 258 // update this when new boxes are added! +#define MAXBOX 500 +#define POSSCORE 308 // update this when new boxes are added! struct score { string ver; @@ -275,7 +284,7 @@ void applyBox(int& t) { else boxid++; } -void applyBoxNum(int& i, string name = "") { +void applyBoxNum(int& i, string name) { fakebox[boxid] = (name == ""); boxname[boxid] = name; monsbox[boxid] = false; @@ -313,6 +322,31 @@ void applyBoxI(eItem it, bool f = false) { else applyBox(items[it]); } +vector invorb; + +void addinv(eItem it) { + invorb.push_back(it); + } + +void applyBoxOrb(eItem it) { + applyBoxI(it, true); + invorb.push_back(it); + } + +void list_invorb() { + for(eItem it: invorb) { +#ifdef INV + if(true) { + inv::applyBox(it); + continue; + } +#endif + int u = 0; + applyBoxNum(u); + } + invorb.clear(); + } + void applyBoxM(eMonster m, bool f = false) { fakebox[boxid] = f; boxname[boxid] = minf[m].name; @@ -321,6 +355,7 @@ void applyBoxM(eMonster m, bool f = false) { } void applyBoxes() { + invorb.clear(); eLand lostin = laNone; @@ -347,9 +382,9 @@ void applyBoxes() { else if(i == moWormwait) applyBoxM(moFireFairy); else if(i == moTentacleEscaping) applyBoxM(moMiner); else if(i == moGolemMoved) applyBoxM(moIllusion); - else if(i == moTentaclewait) applyBoxI(itOrbThorns, true); - else if(i == moGreater) applyBoxI(itOrbDragon, true); - else if(i == moGreaterM) applyBoxI(itOrbIllusion, true); + else if(i == moTentaclewait) applyBoxOrb(itOrbThorns); + else if(i == moGreater) applyBoxOrb(itOrbDragon); + else if(i == moGreaterM) applyBoxOrb(itOrbIllusion); else applyBoxM(eMonster(i), fake); } @@ -371,17 +406,17 @@ void applyBoxes() { else if(loading) firstland = safetyland = eLand(applyBoxLoad()); else lostin = eLand(savebox[boxid++]); - for(int i=itOrbLightning; i<25; i++) applyBoxI(eItem(i), true); + for(int i=itOrbLightning; i<25; i++) applyBoxOrb(eItem(i)); applyBoxI(itRoyalJelly); applyBoxI(itWine); applyBoxI(itSilver); applyBoxI(itEmerald); applyBoxI(itPower); - applyBoxI(itOrbFire, true); - applyBoxI(itOrbInvis, true); - applyBoxI(itOrbAether, true); - applyBoxI(itOrbPsi, true); + applyBoxOrb(itOrbFire); + applyBoxOrb(itOrbInvis); + applyBoxOrb(itOrbAether); + applyBoxOrb(itOrbPsi); applyBoxM(moBug0); applyBoxM(moBug1); applyBoxM(moBug2); @@ -407,12 +442,12 @@ void applyBoxes() { applyBoxM(moCShark); applyBoxM(moParrot); applyBoxI(itPirate); - applyBoxI(itOrbTime, true); + applyBoxOrb(itOrbTime); applyBoxM(moHexSnake); applyBoxM(moRedTroll); applyBoxI(itRedGem); - applyBoxI(itOrbSpace, true); + applyBoxOrb(itOrbSpace); int geo = geometry; applyBoxNum(geo, ""); geometry = eGeometry(geo); @@ -429,14 +464,14 @@ void applyBoxes() { applyBoxM(moBomberbird); applyBoxM(moTameBomberbird); applyBoxM(moAlbatross); - applyBoxI(itOrbFriend, true); - applyBoxI(itOrbAir, true); - applyBoxI(itOrbWater, true); + applyBoxOrb(itOrbFriend); + applyBoxOrb(itOrbAir); + applyBoxOrb(itOrbWater); applyBoxI(itPalace); applyBoxI(itFjord); - applyBoxI(itOrbFrog, true); - applyBoxI(itOrbDiscord, true); + applyBoxOrb(itOrbFrog); + applyBoxOrb(itOrbDiscord); applyBoxM(moPalace); applyBoxM(moFatGuard); applyBoxM(moSkeleton); @@ -446,7 +481,7 @@ void applyBoxes() { applyBoxM(moWaterElemental); applyBoxI(itSavedPrincess); - applyBoxI(itOrbLove, true); + applyBoxOrb(itOrbLove); applyBoxM(moPrincess); applyBoxM(moPrincessMoved, false); // live Princess for Safety applyBoxM(moPrincessArmedMoved, false); // live Princess for Safety @@ -467,8 +502,8 @@ void applyBoxes() { applyBoxM(moFamiliar); applyBoxM(moGargoyle); applyBoxM(moOrangeDog); - applyBoxI(itOrbSummon, true); - applyBoxI(itOrbMatter, true); + applyBoxOrb(itOrbSummon); + applyBoxOrb(itOrbMatter); applyBoxM(moForestTroll); applyBoxM(moStormTroll); @@ -479,8 +514,8 @@ void applyBoxes() { applyBoxI(itMutant); applyBoxI(itFulgurite); applyBoxI(itBounty); - applyBoxI(itOrbLuck, true); - applyBoxI(itOrbStunning, true); + applyBoxOrb(itOrbLuck); + applyBoxOrb(itOrbStunning); applyBoxBool(tactic::on, ""); applyBoxNum(elec::lightningfast, ""); @@ -488,22 +523,22 @@ void applyBoxes() { // if(savebox[boxid]) printf("lotus = %d (lost = %d)\n", savebox[boxid], isHaunted(lostin)); if(loadingHi && isHaunted(lostin)) boxid++; else applyBoxI(itLotus); - applyBoxI(itOrbUndeath, true); + applyBoxOrb(itOrbUndeath); applyBoxI(itWindstone); - applyBoxI(itOrbEmpathy, true); + applyBoxOrb(itOrbEmpathy); applyBoxM(moWindCrow); - applyBoxI(itMutant2); - applyBoxI(itOrbFreedom, true); + applyBoxOrb(itMutant2); + applyBoxOrb(itOrbFreedom); applyBoxM(moRedFox); applyBoxBool(survivalist); if(loadingHi) applyBoxI(itLotus); else applyBoxNum(truelotus, "lotus/escape"); applyBoxBool(purehepta, "heptagons only"); applyBoxI(itRose); - applyBoxI(itOrbBeauty, true); + applyBoxOrb(itOrbBeauty); applyBoxI(itCoral); - applyBoxI(itOrb37, true); - applyBoxI(itOrbEnergy, true); + applyBoxOrb(itOrb37); + applyBoxOrb(itOrbEnergy); applyBoxM(moRatling); applyBoxM(moFalsePrincess); applyBoxM(moRoseLady); @@ -520,11 +555,11 @@ void applyBoxes() { applyBoxM(moResearcher); applyBoxI(itDragon); applyBoxM(moDragonHead); - applyBoxI(itOrbDomination, true); + applyBoxOrb(itOrbDomination); applyBoxI(itBabyTortoise); applyBoxNum(tortoise::seekbits, ""); applyBoxM(moTortoise); - applyBoxI(itOrbShell, true); + applyBoxOrb(itOrbShell); applyBoxNum(safetyseed); @@ -539,12 +574,12 @@ void applyBoxes() { applyBoxI(itKraken); applyBoxM(moKrakenH); applyBoxM(moKrakenT); - applyBoxI(itOrbSword, true); + applyBoxOrb(itOrbSword); applyBoxI(itBarrow); applyBoxM(moDraugr); - applyBoxI(itOrbSword2, true); + applyBoxOrb(itOrbSword2); applyBoxI(itTrollEgg); - applyBoxI(itOrbStone, true); + applyBoxOrb(itOrbStone); bool sph; sph = false; applyBoxBool(sph, "sphere"); if(sph) geometry = gSphere; @@ -554,22 +589,34 @@ void applyBoxes() { applyBoxI(itDodeca); applyBoxI(itAmethyst); applyBoxI(itSlime); - applyBoxI(itOrbNature, true); - applyBoxI(itOrbDash, true); - // itOrbRecall should not be here + applyBoxOrb(itOrbNature); + applyBoxOrb(itOrbDash); + addinv(itOrbRecall); applyBoxM(moBat); applyBoxM(moReptile); applyBoxM(moFriendlyIvy); applyBoxI(itGreenGrass); applyBoxI(itBull); - applyBoxI(itOrbHorns, true); - applyBoxI(itOrbBull, true); + applyBoxOrb(itOrbHorns); + applyBoxOrb(itOrbBull); applyBoxM(moSleepBull); applyBoxM(moRagingBull); applyBoxM(moHerdBull); applyBoxM(moButterfly); applyBoxM(moGadfly); + + // 10.0: + applyBoxNum(hinttoshow); // 258 + addinv(itOrbMirror); + addinv(itGreenStone); + list_invorb(); + applyBoxBool(inv::on, "inventory"); // 306 + #ifdef INV + applyBoxNum(inv::rseed); + #else + { int u; applyBoxNum(u); } + #endif if(POSSCORE != boxid) printf("ERROR: %d boxes\n", boxid); } @@ -785,6 +832,7 @@ void loadsave() { score sc; bool ok = false; bool tamper = false; + int coh = counthints(); while(!feof(f)) { char buf[120]; if(fgets(buf, 120, f) == NULL) break; @@ -792,7 +840,8 @@ void loadsave() { gamecount++; if(fscanf(f, "%s", buf) <= 0) break; sc.ver = buf; - if(sc.ver < "4.4" || sc.ver == "CHEATER!") { ok = false; continue; } + if(sc.ver[1] != '.') sc.ver = '0' + sc.ver; + if(verless(sc.ver, "4.4") || sc.ver == "CHEATER!") { ok = false; continue; } ok = true; for(int i=0; i= 0 && savebox[258] < coh) { + hints[savebox[258]].last = savebox[1]; + } + loadBoxHigh(); break; @@ -826,7 +879,10 @@ void loadsave() { for(int xc=0; xc 0 && cid < YENDORLEVELS) - if(!(ver < string("8.0f") && oy > 1 && cid == 15)) - if(!(ver < string("9.3b") && oy > 1 && (cid == 27 || cid == 28))) + if(!(verless(ver, "8.0f") && oy > 1 && cid == 15)) + if(!(verless(ver, "9.3b") && oy > 1 && (cid == 27 || cid == 28))) { yendor::bestscore[xc][cid] = max(yendor::bestscore[xc][cid], oy); } @@ -955,6 +1011,7 @@ void restartGame(char switchWhat, bool push) { #endif achievementsReceived.clear(); princess::saved = false; + princess::nodungeon = false; princess::reviveAt = 0; princess::forceVizier = false; princess::forceMouse = false; @@ -1101,7 +1158,8 @@ eItem randomTreasure2(int cv) { bool isTechnicalLand(eLand l) { return l == laNone || l == laOceanWall || l == laBarrier || l == laCanvas || - l == laHauntedWall || l == laHauntedBorder || l == laCA; + l == laHauntedWall || l == laHauntedBorder || l == laCA || + l == laMirrorWall || l == laMirrored; } eLand cheatdest; diff --git a/util.cpp b/util.cpp index 06e40841..c799d9db 100644 --- a/util.cpp +++ b/util.cpp @@ -29,6 +29,13 @@ string its(int i) { char buf[64]; sprintf(buf, "%d", i); return buf; } string fts(float x) { char buf[64]; sprintf(buf, "%4.2f", x); return buf; } string fts3(float x) { char buf[64]; sprintf(buf, "%5.3f", x); return buf; } string fts4(float x) { char buf[64]; sprintf(buf, "%6.4f", x); return buf; } + +string ftssmart(ld x) { + if(x == int(x)) return its(int(x)); + if(abs(x) > 1) return fts4(x); + char buf[64]; sprintf(buf, "%.10le", (double) x); return buf; + } + string cts(char c) { char buf[8]; buf[0] = c; buf[1] = 0; return buf; } string llts(long long i) { // sprintf does not work on Windows IIRC diff --git a/yendor.cpp b/yendor.cpp index 05c15787..19dda395 100644 --- a/yendor.cpp +++ b/yendor.cpp @@ -7,7 +7,7 @@ namespace peace { extern bool on; } -#define MODECODES 254 +#define MODECODES 255 int hiitemsMax(eItem it) { int mx = 0; @@ -211,6 +211,7 @@ namespace yendor { nyi.path[i] = lig.c; cwstep(lig); + if(inmirror(lig)) lig = mirror::reflect(lig); cwspin(lig, 3); if(lig.c->type == 7) { if(in_endorian && endorian_change && i >= YDIST - 20) { @@ -289,6 +290,8 @@ namespace yendor { c2->wall = waPlatform; if(c2->land == laReptile && i >= 0) c2->wall = waChasm; + if(c2->land == laMirrorWall && i == -1) + c2->wall = waNone; } key->item = itKey; @@ -508,14 +511,12 @@ namespace yendor { challenge = uni-'a' + 1; if(levelUnlocked(challenge) || autocheat) { restartGame(yendor::on ? 0 : 'y'); - popScreen(); } else addMessage("Collect 10 treasures in various lands to unlock the challenges there"); } else if(uni == '0') { if(yendor::on) restartGame('y'); - popScreen(); } else if(uni == '1') easy = !easy; else if(uni == '2' || sym == SDLK_F1) gotoHelp(chelp); @@ -744,12 +745,11 @@ namespace tactic { if(uni >= 1000 && uni < 1000 + LAND_TAC) { firstland = euclidland = getLandById(uni - 1000); restartGame(tactic::on ? 0 : 't'); - popScreen(); } else if(uni == '0') { - popScreen(); firstland = laIce; if(tactic::on) restartGame('t'); + else popScreen(); } else if(sym == SDLK_F1) gotoHelp( @@ -827,7 +827,7 @@ int modecodetable[42][6] = { {248,249,250,251,252,253}, // shmup heptagonal elliptic chaosmode }; // unused code: 25 -int newmodecode = 254; +int newmodecode = 255; int modecode() { #ifndef NOSAVE @@ -838,6 +838,7 @@ int modecode() { if(quotient) return 6; #endif if(peace::on) return 6; + if(inv::on) return 254; // no code yet int xcode = 0; if(shmup::on) xcode += 2; @@ -1049,13 +1050,11 @@ namespace peace { else if(uni >= 'a' && uni < 'a' + qty) { whichland = levellist[uni - 'a']; restartGame(peace::on ? 0 : 'P'); - popScreen(); } else if(uni == '2') { hint = !hint; popScreen(); } else if(uni == '0') { firstland = laIce; if(peace::on) restartGame('P'); - popScreen(); } else if(uni == 'h' || sym == SDLK_F1) gotoHelp(chelp); else if(doexiton(sym, uni)) popScreen();