// Hyperbolic Rogue // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details // land statistics and flags namespace hr { bool nodisplay(eMonster m) { return m == moIvyDead || m == moDragonTail || m == moWolfMoved || m == moIvyNext || m == moIvyDead; } // returns: 2 = treasure increaser, 1 = just appears, 0 = does not appear int isNative(eLand l, eMonster m) { switch(l) { case laIce: return (m == moWolf || m == moWolfMoved || m == moYeti) ? 2 : 0; case laJungle: return (m == moIvyRoot || m == moMonkey) ? 2 : (isIvy(m) || m == moEagle || m == moMonkey) ? 1 : 0; case laCaves: return (m == moGoblin || m == moTroll) ? 2 : m == moSeep ? 1 : 0; case laDesert: return (m == moDesertman || m == moWorm) ? 2 : 0; case laAlchemist: return (m == moSlime) ? 2 : 0; case laMirror: case laMirrored: case laMirrorWall: case laMirrorWall2: case laMirrored2: return (m == moMirrorSpirit || m == moNarciss || m == moMimic) ? 1 : 0; case laMirrorOld: return (m == moEagle || m == moRanger || m == moMimic) ? 1 : 0; case laMotion: return (m == moRunDog) ? 2 : 0; case laGraveyard: return (m == moZombie || m == moNecromancer) ? 2 : m == moGhost ? 1 : 0; case laRlyeh: return (m == moCultist || m == moTentacle || m == moPyroCultist) ? 2 : (m == moCultistLeader || isIvy(m)) ? 1 : 0; case laDryForest: return (m == moHedge || m == moFireFairy) ? 2 : 0; case laHell: return (m == moLesser || m == moGreater) ? 2 : 0; case laCocytus: return (m == moShark || m == moGreaterShark || m == moCrystalSage) ? 2 : m == moYeti ? 1 : 0; case laCrossroads: case laCrossroads2: case laCrossroads3: case laCrossroads4: case laCrossroads5: case laNone: case laBarrier: case laOceanWall: case laCanvas: case laMemory: return 0; case laEmerald: return (m == moFlailer || m == moLancer || m == moMiner) ? 2 : m == moHedge ? 1 : 0; case laWineyard: return (m == moVineSpirit || m == moVineBeast) ? 2 : 0; case laHive: return isBug(m) ? 1 : 0; case laDeadCaves: return (m == moEarthElemental || m == moDarkTroll) ? 2 : (m == moGoblin || m == moSeep) ? 1 : 0; case laPower: return (isWitch(m) || m == moEvilGolem) ? 1 : 0; case laCamelot: return (m == moKnight || m == moHedge || m == moFlailer || m == moLancer) ? 1 : 0; case laTemple: return (m == moTentacle || m == moCultist || m == moPyroCultist || m == moCultistLeader) ? 1 : 0; case laCaribbean: return (m == moPirate || m == moParrot || m == moCShark) ? 1 : 0; case laRedRock: return (m == moRedTroll || m == moHexSnake) ? 2 : 0; case laMinefield: return (m == moBomberbird || m == moTameBomberbird) ? 1 : 0; case laOcean: return (m == moAlbatross) ? 2 : (m == moPirate || m == moCShark) ? 1 : 0; case laWhirlpool: return (m == moPirate || m == moCShark) ? 1 : 0; case laPalace: case laPrincessQuest: return (m == moPalace || m == moFatGuard || m == moSkeleton || m == moVizier) ? 2 : m == moSkeleton ? 1 : 0; case laLivefjord: return m == moViking ? 2 : (m == moFjordTroll || m == moWaterElemental) ? 1 : 0; case laIvoryTower: return (m == moFamiliar || m == moGargoyle) ? 2 : 0; case laZebra: return (m == moOrangeDog) ? 2 : 0; case laEAir: case laEEarth: case laEWater: case laEFire: case laElementalWall: if(m == elementalOf(l) && m) return 2; return (m == moAirElemental || m == moEarthElemental || m == moWaterElemental || m == moFireElemental) ? 1 : 0; case laStorms: return (m == moMetalBeast || m == moMetalBeast2 || m == moStormTroll) ? 1 : 0; case laOvergrown: return (m == moMutant || m == moForestTroll) ? 1 : 0; case laWildWest: return (m == moOutlaw) ? 2 : 0; case laHalloween: return (m == moGhost || m == moZombie || m == moWitch || m == moLesser || m == moGreater || m == moVampire || m == moDraugr || m == moSkeleton || m == moWitchFire || m == moFlailer || m == moPyroCultist || m == moFatGuard || m == moHedge || m == moLancer || m == moFireFairy || m == moBomberbird || m == moRatlingAvenger || m == moVineBeast || m == moDragonHead || m == moDragonTail) ? 1 : 0; case laClearing: return (m == moMutant || m == moRedFox) ? 1 : 0; case laHaunted: case laHauntedWall: case laHauntedBorder: return (m == moGhost || m == moFriendlyGhost) ? 1 : 0; case laWhirlwind: return (m == moAirElemental || m == moWindCrow) ? 2 : 0; case laRose: return (m == moFalsePrincess || m == moRoseBeauty || m == moRoseLady) ? 2 : 0; case laWarpCoast: case laWarpSea: return m == moRatling ? 2 : m == moRatlingAvenger ? 1 : 0; case laDragon: return (isDragon(m) || m == moFireElemental) ? 1 : 0; case laEndorian: return (m == moResearcher || m == moSparrowhawk) ? 2 : 0; case laTortoise: return m == moTortoise ? 1 : 0; case laTrollheim: return isTroll(m) ? 1 : 0; case laKraken: return m == moKrakenH ? 2 : (m == moViking || m == moKrakenT) ? 1 : 0; case laBurial: return m == moDraugr ? 1 : 0; case laDungeon: return m == moBat ? 2 : m == moSkeleton || m == moGhost ? 1 : 0; case laMountain: return m == moEagle || m == moMonkey || isIvy(m) || m == moFriendlyIvy ? 1 : 0; case laReptile: return m == moReptile ? 1 : 0; case laBull: return (m == moSleepBull || m == moRagingBull || m == moButterfly || m == moGadfly) ? 1 : 0; case laPrairie: return (m == moRagingBull || m == moHerdBull || m == moGadfly) ? 1 : 0; case laVolcano: return (m == moLavaWolf || m == moSalamander) ? 2 : 0; case laTerracotta: case laMercuryRiver: return m == moJiangshi ? 2 : m == moTerraWarrior ? 1 : 0; case laBlizzard: return (m == moVoidBeast || m == moIceGolem) ? 2 : 0; case laHunting: return m == moHunterDog ? 1 : 0; case laDual: return m == moRatling ? 2 : 0; case laSnakeNest: return m == moHexSnake ? 2 : 0; case laCA: return 0; case laDocks: return among(m, moRatling, moPirate, moCShark, moAlbatross, moFireFairy) ? 2 : 0; case laSwitch: return m == moSwitch1 || m == moSwitch2 ? 2 : 0; case laRuins: return among(m, moPair, moHexDemon, moAltDemon, moMonk, moCrusher) ? 2 : m == moSkeleton ? 1 : 0; case laVariant: return m == moVariantWarrior ? 2 : among(m, moMonk, moCrusher, moSkeleton, moHedge, moLancer, moFlailer, moCultist, moPyroCultist, moNecromancer, moGhost, moZombie, moRatling) ? 1 : isIvy(m) ? 1 : 0; case laWestWall: return among(m, moWestHawk, moFallingDog) ? 2 : 0; case laBrownian: return among(m, moBrownBug, moAcidBird) ? 2 : 0; case laMagnetic: return isMagneticPole(m) ? 2 : 0; } return false; } eItem treasureType(eLand l) { switch(l) { case laBrownian: return itBrownian; case laIce: return itDiamond; case laJungle: return itRuby; case laCaves: return itGold; case laDesert: return itSpice; case laAlchemist: return itElixir; case laMirror: case laMirrored: case laMirrorWall: case laMirrorWall2: case laMirrored2: case laMirrorOld: return itShard; case laMotion: return itFeather; case laGraveyard: return itBone; case laRlyeh: return itStatue; case laDryForest: return itFernFlower; case laHell: return itHell; case laCocytus: return itSapphire; case laCrossroads: return itHyperstone; case laCrossroads2: return itHyperstone; case laCrossroads3: return itHyperstone; case laCrossroads4: return itHyperstone; case laCrossroads5: return itHyperstone; case laNone: return itNone; case laBarrier: return itNone; case laOceanWall: return itNone; case laCanvas: return itNone; case laMemory: return itNone; case laEmerald: return itEmerald; case laWineyard: return itWine; case laHive: return itRoyalJelly; case laDeadCaves: return itSilver; case laPower: return itPower; case laCamelot: return itHolyGrail; case laTemple: return itGrimoire; case laCaribbean: return itPirate; case laRedRock: return itRedGem; case laMinefield: return itBombEgg; case laOcean: return itCoast; case laWhirlpool: return itWhirlpool; case laPalace: return itPalace; case laLivefjord: return itFjord; case laIvoryTower: return itIvory; case laZebra: return itZebra; case laEAir: case laEEarth: case laEWater: case laEFire: case laElementalWall: return itElemental; case laPrincessQuest: return itSavedPrincess; case laStorms: return itFulgurite; case laOvergrown: return itMutant; case laWildWest: return itBounty; case laHalloween: return itTreat; case laClearing: return itMutant2; case laHaunted: case laHauntedWall: case laHauntedBorder: return itLotus; case laWhirlwind: return itWindstone; case laRose: return itRose; case laWarpCoast: case laWarpSea: return itCoral; case laDragon: return itDragon; case laEndorian: return itApple; case laTortoise: return itBabyTortoise; case laTrollheim: return itTrollEgg; case laKraken: return itKraken; case laBurial: return itBarrow; case laDungeon: return itSlime; case laMountain: return itAmethyst; case laReptile: return itDodeca; case laBull: return itBull; case laPrairie: return itGreenGrass; case laVolcano: return itLavaLily; case laTerracotta: case laMercuryRiver: return itTerra; case laBlizzard: return itBlizzard; case laHunting: return itHunting; case laDual: return itGlowCrystal; case laSnakeNest: return itSnake; case laDocks: return itDock; case laRuins: return itRuins; case laSwitch: return itSwitch; case laMagnetic: return itMagnet; case laVariant: return itVarTreasure; case laWestWall: return itWest; case laCA: return itNone; } return itNone; } eItem treasureTypeUnlock(eLand l, eItem u) { if(u != itOrbLove && l == laPrincessQuest) return itPalace; return treasureType(l); } eLand landof(eItem it) { for(int i=0; iland); } bool inmirror(const cellwalker& cw) { return inmirror(cw.at->land); } 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; } // land unlocking eLand firstland = laIce, specialland = laIce; bool chaosmode = false; bool landUnlockedRPM(eLand n) { if(isRandland(n) == 2) return true; if(isRandland(n) == 1) return (autocheat || cheater || hiitemsMax(treasureType(n)) >= 10); return false; } int variant_unlock() { return items[itRuins] + items[itEmerald] + items[itBone]; } int variant_unlock_value() { return inv::on ? 75 : 30; } bool landUnlocked(eLand l) { if(randomPatternsMode) { return landUnlockedRPM(l); } switch(l) { case laOvergrown: return gold() >= R60 && items[itRuby] >= U10; case laStorms: case laWhirlwind: return gold() >= R60; case laWildWest: case laHalloween: return false; case laIce: case laJungle: case laCaves: case laDesert: case laMotion: case laCrossroads: case laAlchemist: return true; case laMirror: case laMinefield: case laPalace: case laOcean: case laLivefjord: case laMirrored: case laMirrorWall: case laMirrorWall2: case laDocks: case laMirrored2: return gold() >= R30; case laCaribbean: case laWhirlpool: return gold() >= R30; case laRlyeh: case laDryForest: case laWineyard: case laCrossroads2: return gold() >= R60; case laDeadCaves: return gold() >= R60 && items[itGold] >= U10; case laGraveyard: return tkills() >= R100; case laHive: return tkills() >= R100 && gold() >= R60; case laRedRock: return gold() >= R60 && items[itSpice] >= U10; case laEmerald: return (items[itFernFlower] >= U5 && items[itGold] >= U5) || kills[moVizier]; case laCamelot: return items[itEmerald] >= U5; case laHell: case laCrossroads3: return hellUnlocked(); case laPower: return items[itHell] >= U10; case laCocytus: return items[itHell] >= U10 && items[itDiamond] >= U10; case laTemple: return items[itStatue] >= U5; case laClearing: return items[itMutant] >= U5; case laIvoryTower: return gold() >= R30; case laZebra: return gold() >= R30 && items[itFeather] >= U10; case laEAir: case laEEarth: case laEWater: case laEFire: case laElementalWall: return elementalUnlocked(); case laBarrier: case laNone: case laOceanWall: case laCanvas: case laCA: case laMemory: return false; case laMirrorOld: return false; case laHaunted: case laHauntedWall: case laHauntedBorder: return items[itBone] >= U10; case laPrincessQuest: return kills[moVizier] && !shmup::on && multi::players == 1; case laRose: return gold() >= R90; case laWarpCoast: case laWarpSea: return gold() >= R30; case laCrossroads4: return gold() >= R200; case laEndorian: return items[itIvory] >= U10; case laTortoise: return tortoise::seek(); case laDragon: return killtypes() >= R20; case laKraken: return items[itFjord] >= U10; case laBurial: return items[itKraken] >= U10; case laTrollheim: return trollUnlocked(); case laDungeon: return items[itPalace] >= U5 && items[itIvory] >= U5; case laMountain: return items[itRuby] >= U5 && items[itIvory] >= U5; case laReptile: return gold() >= R30 && items[itElixir] >= U10; case laPrairie: case laBull: return gold() >= R90; case laVolcano: return gold() >= R60 && items[itElixir] >= U10; case laHunting: return true; case laTerracotta: case laMercuryRiver: return gold() >= R90; case laBlizzard: return items[itDiamond] >= U5 && items[itWindstone] >= U5; case laCrossroads5: return gold() >= R300; case laDual: case laSnakeNest: return gold() >= R90; case laSwitch: return gold() >= R30 && items[itElixir] >= U10; case laRuins: return kills[moSkeleton]; case laBrownian: return gold() >= R30; case laWestWall: return items[itFeather] >= 5 && items[itIvory] >= 5; case laVariant: return variant_unlock() >= variant_unlock_value(); case laMagnetic: return false; // not implemented } return false; } void countHyperstoneQuest(int& i1, int& i2) { i1 = 0; i2 = 0; generateLandList(isLandIngame); for(eLand l: landlist) if(l != laCamelot && l != laPrincessQuest) { eItem ttype = treasureType(l); if(ttype != itHyperstone) { i2++; if(items[ttype] >= R10) i1++; } } } bool hyperstonesUnlocked() { int i1, i2; if(tactic::on && isCrossroads(specialland) && !tactic::trailer) return true; countHyperstoneQuest(i1, i2); return i1 == i2; } // 2 = always available, 1 = highscore required, 0 = never available int isRandland(eLand l) { if(l == laIce || l == laDesert || l == laCaves || l == laWildWest || l == laDocks) return 2; for(eLand ll: randlands) if(l == ll) return 1; return 0; } bool incompatible1(eLand l1, eLand l2) { if(isCrossroads(l1) && isCrossroads(l2)) return true; if(l1 == laJungle && l2 == laMotion) return true; if(l1 == laMirrorOld && l2 == laMotion) return true; if(l1 == laPower && l2 == laWineyard) return true; if(l1 == laPower && l2 == laDryForest) return true; if(l1 == laVolcano && l2 == laDryForest) return true; if(l1 == laVolcano && l2 == laWineyard) return true; if(l1 == laDragon && l2 == laDryForest) return true; if(l1 == laEFire && l2 == laWineyard) return true; if(l1 == laEFire && l2 == laDryForest) return true; if(l1 == laGraveyard && l2 == laDryForest) return true; if(l1 == laGraveyard && l2 == laRuins) return true; if(l1 == laGraveyard && l2 == laRedRock) return true; if(l1 == laGraveyard && l2 == laEmerald) return true; if(l1 == laDeadCaves && l2 == laEmerald) return true; if(l1 == laDeadCaves && l2 == laCaves) return true; if(l1 == laWarpSea && l2 == laKraken) return true; if(l1 == laPrairie && l2 == laCrossroads3) return true; if(isElemental(l1) && isElemental(l2)) return true; return false; } eLand randomElementalLand() { int i = hrand(4); eLand t[4] = { laEFire, laEWater, laEAir, laEEarth }; return t[i]; } int elementalKills() { return kills[moAirElemental] + kills[moWaterElemental] + kills[moEarthElemental] + kills[moFireElemental]; } bool elementalUnlocked() { return kills[moAirElemental] && kills[moWaterElemental] && kills[moEarthElemental] && kills[moFireElemental]; } bool trollUnlocked() { return kills[moTroll] && kills[moDarkTroll] && kills[moRedTroll] && kills[moStormTroll] && kills[moForestTroll] && kills[moFjordTroll]; } eLand randomElementalLandWeighted() { int i = hrand(elementalKills()); i -= kills[moAirElemental]; if(i<0) return laEAir; i -= kills[moWaterElemental]; if(i<0) return laEWater; i -= kills[moEarthElemental]; if(i<0) return laEEarth; i -= kills[moFireElemental]; if(i<0) return laEFire; printf("elemental bug\n"); return laElementalWall; } bool incompatible(eLand nw, eLand old) { return (nw == old) || incompatible1(nw, old) || incompatible1(old, nw); } bool rlyehComplete() { if(chaosmode) return items[itStatue] >= 1; return items[itStatue] >= 10 || items[itGrimoire] >= 10; } bool lchance(eLand l) { if(tactic::on || yendor::on || racing::on || ((geometry || GOLDBERG) && specialland == laElementalWall)) return true; if(chaosmode) return hrand(100) < 25; return hrand(100) >= 40 * kills[elementalOf(l)] / (elementalKills()+1); } eLand pickLandRPM(eLand old) { while(true) { eLand n = randlands[hrand(isize(randlands))]; if(incompatible(n, old)) continue; if(landUnlockedRPM(n)) return n; } } eLand pickluck(eLand l1, eLand l2) { int t1 = items[treasureType(l1)]; int t2 = items[treasureType(l2)]; if(t1 < t2) return l1; if(t2 < t1) return l2; if(isCrossroads(l1)) return l1; return l2; } #define LIKELY for(int u=0; u<5; u++) #define LIKELY2 for(int u=0; u<2; u++) /* bool noChaos(eLand l) { if(l == laOcean || l == laTemple) return false; return isCrossroads(l) || isCyclic(l) || isHaunted(l) || l == laCaribbean || isGravityLand(l) || l == laPrincessQuest || l == laPrairie || l == laHalloween; } */ eLand getNewSealand(eLand old) { while(true) { eLand p = pick(laOcean, pick(laCaribbean, laLivefjord, laWarpSea, laKraken, laDocks)); if(p == laKraken && !landUnlocked(p)) continue; if(p == laKraken && peace::on) continue; if(incompatible(old, p)) continue; if(p == old) continue; if(!isLandIngame(p)) continue; return p; } } bool createOnSea(eLand old) { return old == laWarpSea || old == laCaribbean || old == laKraken || (old == laLivefjord && hrand(2)) || (old == laDocks && hrand(2)) || (old == laOcean && (chaosmode ? hrand(2) : !generatingEquidistant)); } hookset *hooks_nextland; eLand getNewLand(eLand old) { if(old == laMirror && !chaosmode && hrand(10) >= ((tactic::on || racing::on) ? 0 : markOrb(itOrbLuck) ? 5 : 2)) return laMirrored; if(old == laTerracotta && !chaosmode && hrand(5) >= ((tactic::on || racing::on) ? 0 : markOrb(itOrbLuck) ? 2 : 1) && !weirdhyperbolic) return laTerracotta; eLand l = callhandlers(laNone, hooks_nextland, old); if(l) return l; if(cheatdest != old) if(!isCyclic(cheatdest) && !isTechnicalLand(cheatdest)) return cheatdest; if(old == laTortoise) return laDragon; if(yendor::on && chaosmode) { while(true) { eLand n = eLand(hrand(landtypes)); if(n == old) continue; if(incompatible(n,old)) continue; if(!isLandIngame(n)) continue; if(n == laElementalWall || isTechnicalLand(n)) continue; if(n == laWildWest) continue; if(isElemental(n) && hrand(100) >= 25) continue; return n; } } if(markOrb(itOrbLuck)) { int i = items[itOrbLuck]; items[itOrbLuck] = 0; eLand l1 = getNewLand(old); for(int i=1; i<3; i++) l1 = pickluck(l1, getNewLand(old)); items[itOrbLuck] = i; return l1; } if(randomPatternsMode) return pickLandRPM(old); if(old == laEEarth && lchance(old)) return hrand(2) ? laEWater : laEFire; if(old == laEAir && lchance(old)) return hrand(2) ? laEWater : laEFire; if(old == laEWater && lchance(old)) return hrand(2) ? laEEarth : laEAir; if(old == laEFire && lchance(old)) return hrand(2) ? laEEarth : laEAir; #if CAP_RACING if(racing::on && old != laElementalWall) { eLand l = old; using racing::race_lands; while(l == old) l = race_lands[hrand(isize(race_lands))]; if(l == laElementalWall) l = randomElementalLand(); if(l == laMirror) l = laCrossroads; return l; } #endif if(tactic::on && !(tactic::trailer && old == specialland)) return specialland; if(weirdhyperbolic && specialland != old && specialland != laCrossroads4 && !chaosmode && old != laBarrier) return specialland; if(yendor::on && (yendor::clev().flags & YF_WALLS)) { if(old != yendor::clev().l) return yendor::clev().l; else if(old == laOcean) return pick(laLivefjord, laCaribbean); } if(yendor::on && yendor::nexttostart) { eLand l = yendor::nexttostart; if(!(yendor::clev().flags & YF_REPEAT)) yendor::nexttostart = laNone; return l; } if(old == laDragon && tortoise::seek() && hrand(100) < 50) return laTortoise; if(isWarped(old) && (hrand(100) < 25) && chaosmode) return eLand(old ^ laWarpCoast ^ laWarpSea); if(createOnSea(old)) return getNewSealand(old); if(old == laGraveyard && generatingEquidistant) return laHaunted; if(old == laOcean && gold() >= R60 && hrand(100) < 75 && !rlyehComplete()) return laRlyeh; if(old == laRlyeh && !rlyehComplete()) return laOcean; eLand tab[1024]; int cnt = 0; /* if(isHive(old) && hrand(100) < 90) { eLand n = old; while(n == old) n = eLand(laHive0 + hrand(3)); return n; } */ // return (hrand(2)) ? laMotion : laJungle; // the basic lands, always available tab[cnt++] = laCrossroads; tab[cnt++] = laIce; tab[cnt++] = laDesert; tab[cnt++] = laJungle; tab[cnt++] = laMotion; if(old == laZebra) LIKELY2 { tab[cnt++] = laMotion; tab[cnt++] = laHunting; } tab[cnt++] = laHunting; tab[cnt++] = laAlchemist; if(old != laDeadCaves) tab[cnt++] = laCaves; // the intermediate lands if(gold() >= R30) { tab[cnt++] = laCrossroads; tab[cnt++] = geometry ? laMirrorOld : laMirror; tab[cnt++] = laOcean; tab[cnt++] = laLivefjord; tab[cnt++] = laMinefield; tab[cnt++] = laPalace; if(old == laDragon && items[itElixir] >= U10) LIKELY tab[cnt++] = laReptile; if(kills[moVizier]) tab[cnt++] = laEmerald; if(kills[moSkeleton]) { tab[cnt++] = laRuins; if(old == laVariant) LIKELY tab[cnt++] = laRuins; } if(items[itFeather] >= U10) { tab[cnt++] = laZebra; if(old == laMotion || old == laHunting) LIKELY2 tab[cnt++] = laZebra; } tab[cnt++] = laWarpCoast; if(euclid) tab[cnt++] = laWarpSea; tab[cnt++] = laDocks; // Ivory Tower tends to crash while generating equidistant if(!generatingEquidistant) tab[cnt++] = laIvoryTower; if(items[itElixir] >= U10) tab[cnt++] = laReptile; if(items[itElixir] >= U10) tab[cnt++] = laSwitch; if(items[itIvory] >= U10 && !generatingEquidistant) tab[cnt++] = laEndorian; if(items[itIvory] >= U5 && !generatingEquidistant && items[itFeather] >= U5) tab[cnt++] = laWestWall; if(items[itKraken] >= U10) tab[cnt++] = laBurial; } if(landUnlocked(laDungeon)) { tab[cnt++] = laDungeon; if(old == laPalace) LIKELY tab[cnt++] = laDungeon; } // the advanced lands if(gold() >= R60) { tab[cnt++] = laStorms; if(!weirdhyperbolic) tab[cnt++] = laWhirlwind; tab[cnt++] = laCrossroads; if(!generatingEquidistant) tab[cnt++] = laCrossroads2; if(items[itRuby] >= U10) { tab[cnt++] = laOvergrown; if(old == laJungle) LIKELY tab[cnt++] = laOvergrown; } if(rlyehComplete()) tab[cnt++] = laRlyeh; else if(chaosmode && (old == laWarpCoast || old == laLivefjord || old == laOcean)) tab[cnt++] = laRlyeh; if(items[itStatue] >= U5 && chaosmode) tab[cnt++] = laTemple; if(old == laCrossroads || old == laCrossroads2) tab[cnt++] = laOcean; if(old == laOcean) tab[cnt++] = laCrossroads; if(items[itGold] >= U5 && items[itFernFlower] >= U5 && !kills[moVizier]) tab[cnt++] = laEmerald; if(old == laVariant) LIKELY tab[cnt++] = laEmerald; if(items[itWindstone] >= U5 && items[itDiamond] >= U5) { tab[cnt++] = laBlizzard; if(old == laIce || old == laCocytus || old == laWhirlwind) LIKELY tab[cnt++] = laBlizzard; if(old == laBlizzard) LIKELY tab[cnt++] = laIce; if(old == laBlizzard) LIKELY tab[cnt++] = laWhirlwind; } tab[cnt++] = laDryForest; tab[cnt++] = laWineyard; if(items[itElixir] >= U10) { tab[cnt++] = laVolcano; if(old == laAlchemist) LIKELY2 tab[cnt++] = laVolcano; if(old == laVolcano) LIKELY2 tab[cnt++] = laAlchemist; } if(items[itGold] >= U10) tab[cnt++] = laDeadCaves; // tab[cnt++] = laCaribbean; if(items[itSpice] >= U10) { tab[cnt++] = laRedRock; if(old == laDesert) LIKELY tab[cnt++] = laRedRock; } if(old == laRedRock) LIKELY tab[cnt++] = laDesert; if(old == laOvergrown) LIKELY tab[cnt++] = laJungle; if(items[itIvory] >= U5 && !generatingEquidistant && items[itFeather] >= U5) tab[cnt++] = laWestWall; if(variant_unlock() >= variant_unlock_value()) { tab[cnt++] = laVariant; if(old == laRuins) LIKELY tab[cnt++] = laVariant; if(old == laGraveyard) LIKELY tab[cnt++] = laVariant; if(old == laEmerald) LIKELY tab[cnt++] = laVariant; } } if(gold() >= R90) { if(!chaosmode) tab[cnt++] = laPrairie; if(old == laPrairie) LIKELY tab[cnt++] = laBull; tab[cnt++] = laBull; if(old == laBull && !chaosmode) LIKELY tab[cnt++] = laPrairie; tab[cnt++] = laTerracotta; tab[cnt++] = laRose; if(chaosmode && geometry) tab[cnt++] = laDual; if(chaosmode && geosupport_threecolor()) tab[cnt++] = laSnakeNest; } if(gold() >= R300) tab[cnt++] = laCrossroads5; if(tkills() >= R100) { tab[cnt++] = laGraveyard; if(gold() >= R60) tab[cnt++] = laHive; if(old == laVariant) LIKELY tab[cnt++] = laGraveyard; } if(killtypes() >= R20) { tab[cnt++] = laDragon; if(old == laReptile) LIKELY tab[cnt++] = laDragon; } if(trollUnlocked()) { tab[cnt++] = laTrollheim; if(isTrollLand(old)) LIKELY tab[cnt++] = laTrollheim; if(old == laTrollheim) for(int i=0; i= U10) { if(items[itDiamond] >= U10) { tab[cnt++] = laCocytus; if(old == laHell || old == laIce || old == laBlizzard) LIKELY tab[cnt++] = laCocytus; } if(old == laCocytus) LIKELY { tab[cnt++] = laIce; tab[cnt++] = laHell; } tab[cnt++] = laPower; if(old == laCrossroads || old == laCrossroads2) tab[cnt++] = laOcean; if(old == laOcean) tab[cnt++] = laCrossroads2; } // for(int i=0; i<20; i++) tab[cnt++] = laRedRock; // for(int i=0; i<20; i++) tab[cnt++] = laCaribbean; // for(int i=0; i<20; i++) tab[cnt++] = laCocytus; // for(int i=0; i<20; i++) tab[cnt++] = laCrossroads; eLand n = old; while(incompatible(n, old) || !isLandIngame(n)) { n = tab[hrand(cnt)]; if(weirdhyperbolic && specialland == laCrossroads4 && isCrossroads(n)) n = laCrossroads4; } return n; } vector land_over = { laIce, laCaves, laDesert, laHunting, laMotion, laJungle, laAlchemist, laCrossroads, laMirror, laMirrorOld, laMinefield, laPalace, laPrincessQuest, laZebra, laSwitch, laReptile, laOcean, laDocks, laWarpCoast, laLivefjord, laKraken, laCaribbean, laWhirlpool, laRlyeh, laTemple, laIvoryTower, laEndorian, laDungeon, laMountain, laCrossroads2, laDryForest, laWineyard, laDeadCaves, laGraveyard, laHaunted, laHive, laRedRock, laVolcano, laDragon, laTortoise, laOvergrown, laClearing, laStorms, laBurial, laWhirlwind, laBlizzard, laRuins, laEmerald, laCamelot, laPrairie, laBull, laTerracotta, laRose, laElementalWall, laTrollheim, laHell, laCrossroads3, laCocytus, laPower, laCrossroads4, laCrossroads5, // EXTRA laWildWest, laHalloween, laDual, laSnakeNest, laMagnetic, laCA }; vector landlist; template void generateLandList(T t) { landlist.clear(); for(auto l: land_over) if(t(l)) landlist.push_back(l); } eLand getLandForList(cell *c) { eLand l = c->land; if(isElemental(l)) return laElementalWall; if(l == laWarpSea) return laWarpCoast; if(l == laMercuryRiver) return laTerracotta; if(l == laBarrier) return laCrossroads; if(l == laOceanWall) return laOcean; if(l == laPalace && princess::dist(cwt.at) < OUT_OF_PRISON) l = laPrincessQuest; // princess? return l; } bool isLandIngame(eLand l) { if(isElemental(l)) l = laElementalWall; return land_validity(l).flags & lv::appears_in_full; } namespace lv { flagtype q0 = lv::display_error_message | lv::display_in_help | lv::appears_in_geom_exp; flagtype q1 = lv::display_error_message | lv::appears_in_geom_exp | lv::appears_in_full | lv::display_in_help; flagtype q2 = lv::appears_in_geom_exp | lv::appears_in_full | lv::display_in_help | lv::appears_in_ptm; flagtype q3 = lv::appears_in_geom_exp | lv::appears_in_full | lv::display_in_help | lv::appears_in_ptm; flagtype qm2= q2 | lv::display_error_message; flagtype qm3= q3 | lv::display_error_message; land_validity_t hedgehogs = { 1, qm2 &~ lv::appears_in_full, "Cannot kill Hedgehog Warriors in this geometry."}; land_validity_t no_randpattern_version = { 0, q0, "No random pattern version."}; land_validity_t no_great_walls = { 0, q0, "Great Walls not implemented."}; land_validity_t pattern_incompatibility = { 0, q0, "Pattern incompatible."}; land_validity_t pattern_not_implemented_random = { 1, q1 | one_and_half, "Pattern not implemented -- using random."}; land_validity_t pattern_not_implemented_weird = { 1, q1, "Pattern not implemented."}; land_validity_t pattern_not_implemented_exclude = { 1, q1 & ~ lv::appears_in_full, "Pattern not implemented."}; land_validity_t not_enough_space = { 0, q0, "Not enough space."}; land_validity_t dont_work = { 0, q0, "Does not work in this geometry."}; land_validity_t bounded_only = { 0, q0, "This land is designed for bounded worlds."}; land_validity_t unbounded_only = { 0, q0, "This land is designed for infinite worlds."}; land_validity_t unbounded_only_except_bigsphere = { 0, q0, "This land is designed for infinite worlds or big spheres."}; land_validity_t out_of_theme = { 3, qm2 &~ lv::appears_in_full, "Out of theme for the full game."}; land_validity_t no_game = { 2, q2 &~ lv::appears_in_full, "No game here."}; land_validity_t not_in_chaos = { 0, q0, "Does not work in chaos mode."}; land_validity_t not_in_full_game = {2, qm2 &~ lv::appears_in_full, "Not in the full game."}; land_validity_t not_in_full_game3 = {3, qm2 &~ lv::appears_in_full, "Not in the full game."}; land_validity_t special_chaos = { 2, qm2, "Special construction in the Chaos mode." }; land_validity_t special_euclidean = { 2, qm2, "Special construction in the Euclidean mode." }; land_validity_t special_geo = { 2, qm2, "Special construction in this geometry." }; land_validity_t special_geo3 = { 3, qm2, "Special construction in this geometry." }; land_validity_t not_implemented = {0, q0, "Not implemented."}; land_validity_t partially_implemented = {1, q1 | one_and_half, "Partially implemented."}; land_validity_t ok = {2, q2 &~ lv::display_in_help, "No comments."}; land_validity_t not_in_ptm = {0, q0, "Does not work in pure tactics mode."}; land_validity_t technical = {0, q0 &~ lv::appears_in_geom_exp, "Technical."}; land_validity_t full_game = {3, q3, "Full game."}; land_validity_t inaccurate = {1, q1, "Somewhat inaccurate."}; land_validity_t great_walls_missing = {1, q1 | one_and_half, "Mercury rivers not implemented (or could not work) in this geometry."}; land_validity_t pattern_compatibility = {3, qm3, "Patterns compatible."}; land_validity_t pattern_defined = {3, qm3, "Pattern defined."}; land_validity_t pattern_compatibility_sole = {3, qm3 &~ lv::appears_in_full, "Patterns compatible."}; land_validity_t pattern_compatibility_notrec = {2, qm2 &~ lv::appears_in_full, "Patterns compatible."}; land_validity_t specially_designed = {3, qm3, "This land is specially designed for this geometry."}; land_validity_t needs_threecolor = {0, q0, "Three-colorability required."}; land_validity_t land_not_implemented = {0, q0 &~ lv::appears_in_geom_exp, "Land not implemented."}; land_validity_t interesting = {3, q3, "Special interest."}; land_validity_t better_version_exists = {0, q0, "Better version exists."}; land_validity_t dont_work_but_ingame = {1, q0 | lv::appears_in_full, "Does not work in this geometry."}; land_validity_t ugly_version = {1, q1 | lv::appears_in_full, "Grid does not work in this geometry."}; land_validity_t bad_graphics = {1, q1, "Graphics not implemented in this geometry."}; land_validity_t some0 = {0, q0, "This land does not work in the current settings. Reason not available."}; land_validity_t some1 = {1, q1, "This land does not work well in the current settings. Reason not available."}; land_validity_t known_buggy = {1, q1, "This combination is known to be buggy at the moment."}; land_validity_t sloppy_pattern = {1, q1 | one_and_half, "Somewhat sloppy pattern."}; land_validity_t no_fractal_landscapes = {1, q1 | one_and_half, "Fractal landscapes not implemented in this geometry."}; land_validity_t simplified_walls = { 1, q1, "Only simplified walls implemented."}; } // old Daily Challenges should keep their validity forever // set this number for historical values of land_validity int old_daily_id = 1000000; // check if the given land should appear in lists land_validity_t& land_validity(eLand l) { using namespace lv; if(binarytiling) { if(among(l, laMountain, laTemple)) return lv::pattern_compatibility_sole; if(among(l, laDungeon, laIvoryTower, laOcean, laEndorian)) return lv::pattern_compatibility; if(among(l, laCaribbean, laCamelot)) return lv::pattern_compatibility_notrec; // Clearing -- does not generate /* laCamelot, laCaribbean -> they are OK but not recommended */ } if(archimedean) { if(among(l, laPower, laZebra, laWineyard) && arcm::current.have_line) return lv::pattern_defined; // horocycles not implemented if(isCyclic(l)) return not_implemented; } if(geometry == gCrystal) { if(l == laCamelot) return interesting; if(isCrossroads(l)) return full_game; } // Random Pattern allowed only in some specific lands if(randomPatternsMode && !isRandland(l)) return no_randpattern_version; if(isElemental(l)) { if(l != laElementalWall) return technical; // not good in Field quotient if(geometry == gZebraQuotient) return special_geo3; if(quotient) return no_great_walls; if(weirdhyperbolic) return simplified_walls; // works nice on a big non-tetrahedron-based sphere if(sphere && S3 != 3 && GOLDBERG) return special_geo3; } // not enough space if(l == laStorms && (old_daily_id < 35 ? !BITRUNCATED : PURE) && elliptic) return not_enough_space; if(l == laStorms && S7 == 3) return not_enough_space; // does not agree with the pattern if(l == laStorms && quotient && geometry != gZebraQuotient) return pattern_not_implemented_random; // pattern not implemented if(l == laStorms && S7 == 8) return pattern_not_implemented_random; // mirrors do not work in gp if(among(l, laMirror, laMirrorOld) && (GOLDBERG && old_daily_id < 33)) return dont_work; if(binarytiling && among(l, laMirror, laMirrorOld)) return dont_work; if(l == laWhirlwind && hyperbolic_not37) return pattern_incompatibility; // available only in non-standard geometries if(l == laMirrorOld && !geometry && STDVAR) return better_version_exists; // available only in standard geometry if(l == laMirror && (geometry || NONSTDVAR)) return not_implemented; // Halloween needs bounded world (can be big bounded) if(l == laHalloween && !bounded) return bounded_only; // Crystal World is designed for nice_dual geometries if(l == laDual && !has_nice_dual()) return dont_work; if(l == laHaunted && chaosmode) return not_in_chaos; // standard, non-PTM specific if(l == laCrossroads5 && tactic::on) return not_in_ptm; // standard non-PTM non-chaos specific if((l == laCrossroads5 || l == laCrossroads2) && (geometry || chaosmode)) return some0; // special construction in the Chaos mode if(chaosmode && (l == laTemple || l == laHive || l == laOcean || l == laHaunted)) return special_chaos; if(l == laWhirlpool && a4) return dont_work; if(isWarped(l) && a4 && GOLDBERG) return dont_work; if((isWarped(l) || l == laDual) && IRREGULAR && !irr::bitruncations_performed) return dont_work; if(IRREGULAR && among(l, laPrairie, laMirror, laMirrorOld)) return dont_work; if(archimedean && l == laPrairie) return dont_work; if((IRREGULAR || archimedean) && among(l, laBlizzard, laVolcano) && !sphere) return dont_work; if(archimedean && DUAL && l == laCrossroads4) return not_implemented; // equidistant-based lands if(isEquidLand(l)) { // no equidistants supported in chaos mode if(chaosmode) return not_in_chaos; // the algorithm fails in Archimedean DUAL if(archimedean && DUAL) return not_implemented; // no equidistants supported in these geometries (big sphere is OK though) if(bounded && !bigsphere) return unbounded_only_except_bigsphere; // Yendorian only implemented in standard if(l == laEndorian && geometry) return not_implemented; // special Euclidean implementations if(euclid && (l == laIvoryTower || l == laMountain || l == laOcean || l == laMountain)) return special_geo; // in other geometries, it works if(geometry) return ok; } if(l == laPrincessQuest && chaosmode) return not_in_chaos; if(l == laPrincessQuest && tactic::on) return not_in_ptm; if(l == laPrincessQuest && (!stdeuc || NONSTDVAR)) return not_implemented; // works correctly only in some geometries if(l == laClearing && chaosmode) return not_in_chaos; if(l == laClearing) if(!(stdeuc || a38 || (a45 && BITRUNCATED) || (a47 && BITRUNCATED)) || NONSTDVAR) if(!bounded) return not_implemented; // does not work in non-bitrunc a4 if(l == laOvergrown && a4 && !BITRUNCATED) return some0; // does not work in bounded either if(l == laOvergrown && bounded) return some0; // horocycle-based lands, not available in bounded geometries nor in Chaos mode if(l == laWhirlpool || l == laCamelot || l == laCaribbean || l == laTemple || l == laHive) { if(chaosmode) { if(l == laTemple || l == laHive) return special_chaos; return not_in_chaos; } if(archimedean) return not_implemented; if(bounded) return unbounded_only; } if(chaosmode && isCrossroads(l)) return not_in_chaos; // this pattern does not work on elliptic and small spheres if((l == laBlizzard || l == laVolcano) && elliptic && S7 < 5 && !archimedean) return not_enough_space; // ... and it works in gp only partially if((l == laBlizzard || l == laVolcano) && GOLDBERG && (old_daily_id < 33 || !sphere)) return partially_implemented; // Kraken does not really work on odd-sided cells; // a nice football pattern will solve the problem by forbidding the Kraken to go there // (but we do have to allow it in non-bitrunc standard) if(l == laKraken && (S7&1) && !has_nice_dual()) { return dont_work_but_ingame; } // works in most spheres, Zebra quotient, and stdeuc if(l == laWhirlwind) { if(geometry == gZebraQuotient) return pattern_compatibility; if(stdeuc) ; else if(S7 == 4 && BITRUNCATED) return special_geo; else if(bigsphere && !BITRUNCATED && !elliptic) return special_geo; else return dont_work; } // needs standard/Euclidean (needs fractal landscape) if(l == laTortoise && !stdeuc) return not_implemented; // technical lands do not count if(l != laCA && isTechnicalLand(l)) return technical; // only in bounded geometry, and not in PTM if(l == laCA && !bounded) return bounded_only; if(l == laCA && tactic::on) return not_in_ptm; if(l == laCA) return no_game; // Dragon Chasm requires unbounded space [partial] if(l == laDragon && smallbounded) return unbounded_only; // Graveyard pattern does not work on non-bitrunc weird geometries if((l == laGraveyard && !randomPatternsMode) || l == laRuins || l == laRedRock) switch(geosupport_football()) { case 0: return dont_work; case 1: return sloppy_pattern; default: ; } // Warped Coast does not work on non-bitrunc S3s (except standard heptagonal where we have to keep it) if(l == laWarpCoast && (S3==3) && geosupport_football() != 2 && !(old_daily_id >= 33 && geosupport_chessboard())) { return ugly_version; } if(l == laWarpCoast && quotient && geometry != gZebraQuotient && !randomPatternsMode) return pattern_incompatibility; if(among(l, laEmerald, laCamelot, laDryForest) && VALENCE != 3 && old_daily_id >= 65) return hedgehogs; // laPower and laEmerald and laPalace -> [partial] in quotients and hyperbolic_non37 if((l == laPower || l == laEmerald || l == laPalace || l == laWildWest) && !randomPatternsMode) { if(euclid || bigsphere) ; else if(old_daily_id <= 65 && a45) ; else if(!hyperbolic_37) return l == laWildWest ? some0 : pattern_not_implemented_random; else if(quotient) return pattern_incompatibility; } if(among(l, laEmerald, laCamelot, laDryForest) && VALENCE != 3) return hedgehogs; // ... wineyard pattern is GOOD only in the standard geometry or Euclidean if(l == laWineyard && (NONSTDVAR || sphere) && !randomPatternsMode) return pattern_not_implemented_random; if(l == laTrollheim && quotient == qFIELD) return not_enough_space; if(l == laStorms && hyperbolic_not37) return pattern_not_implemented_random; if(l == laTrollheim && !stdeuc && !bounded) return some1; if(l == laReptile) { if(old_daily_id <= 64) { if(l == laReptile && (a38 || a4 || sphere || !BITRUNCATED || (quotient && !euwrap && geometry != gZebraQuotient))) return bad_graphics; } else { bool reptile_good = false; if(hyperbolic_37 && BITRUNCATED) reptile_good = true; if(euclid6) reptile_good = true; if(quotient && geometry != gZebraQuotient && !euwrap) reptile_good = false; if(!reptile_good) return bad_graphics; } } if((l == laDragon || l == laReptile) && !stdeuc && !smallbounded && !randomPatternsMode) return no_fractal_landscapes; if(l == laCrossroads && smallsphere) return not_enough_space; if(l == laCrossroads3 && !stdeuc && !bigsphere) return not_enough_space; if(among(l, laCrossroads, laCrossroads2, laCrossroads3, laCrossroads5) && weirdhyperbolic) return no_great_walls; // Crossroads IV is great in weird hyperbolic if(l == laCrossroads4 && weirdhyperbolic && !quotient) return full_game; // OK in small bounded worlds, and in Euclidean if(l == laCrossroads4 && quotient) return some0; if(l == laZebra && quotient && geometry != gZebraQuotient && !randomPatternsMode) return pattern_incompatibility; if(l == laZebra && !(stdeuc || (a4 && !BITRUNCATED) || a46) && !randomPatternsMode) return pattern_not_implemented_weird; if(l == laCrossroads3 && euclid) return inaccurate; // because it is not accurate if(l == laPrairie) { if(GOLDBERG) return not_implemented; else if(stdeuc || (bigsphere && BITRUNCATED && !elliptic) || (geometry == gFieldQuotient)) ; else if(!bounded) return not_implemented; else return unbounded_only; } if(l == laTerracotta && !stdeuc && !(bigsphere)) return great_walls_missing; // highlight Crossroads on Euclidean if(euclid && !euwrap && (l == laCrossroads || l == laCrossroads4)) return full_game; if(euclid && euwrap && !fulltorus && l == laCrossroads && torusconfig::sdy == -2 * torusconfig::sdx) return full_game; if(euclid && euwrap && !fulltorus && l == laCrossroads4 && torusconfig::sdy == 0) return full_game; // highlight Zebra-based lands on Zebra Quotient! if((l == laZebra || l == laWhirlwind || l == laStorms || l == laWarpCoast || l == laWarpSea) && geometry == gZebraQuotient) return pattern_compatibility; // highlight FP-based lands on Field Quotient! if((l == laPrairie || l == laVolcano || l == laBlizzard) && geometry == gFieldQuotient) return pattern_compatibility; // highlight Docks-based lands on Bolza and Bolza x2! if(l == laDocks && among(geometry, gBolza, gBolza2)) return pattern_compatibility; // these are highlighted whenever allowed if(l == laHalloween) return specially_designed; if(l == laDual && geosupport_threecolor() == 2) return specially_designed; if(l == laDual && !geometry && !GOLDBERG) return hyperbolic_37 ? not_in_full_game3 : not_in_full_game; if(l == laSnakeNest) { if(geosupport_threecolor() < 2) return needs_threecolor; else return specially_designed; } if(l == laDocks && !randomPatternsMode) { if(a38 && !GOLDBERG) return specially_designed; if(a38) return pattern_not_implemented_weird; return pattern_not_implemented_exclude; } if(l == laStorms && fulltorus) return interesting; if(l == laMagnetic || l == laBrownian) return land_not_implemented; if(shmup::on && among(l, laMirror, laMirrorOld) && among(geometry, gElliptic, gZebraQuotient, gKleinQuartic, gBolza, gBolza2, gMinimal)) return known_buggy; // these don't appear in normal game, but do appear in special modes if(l == laWildWest && !randomPatternsMode) return out_of_theme; if(l == laIce && STDVAR && hyperbolic_37 && !quotient && !archimedean && !binarytiling) return full_game; return ok; } /* int checkLands() { for(int i=0; i