mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-11-27 14:37:16 +00:00
4428 lines
137 KiB
C++
4428 lines
137 KiB
C++
// Hyperbolic Rogue
|
|
|
|
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
|
|
|
|
// land generation routines
|
|
|
|
vector<cell*> buggycells;
|
|
|
|
cell *pathTowards(cell *pf, cell *pt) {
|
|
|
|
while(celldist(pt) > celldist(pf)) {
|
|
if(isNeighbor(pf, pt)) return pt;
|
|
cell *pn = NULL;
|
|
forCellEx(pn2, pt) if(celldist(pn2) < celldist(pt)) pn = pn2;
|
|
pt = pn;
|
|
}
|
|
|
|
if(isNeighbor(pf, pt)) return pt;
|
|
forCellEx(pn2, pt) if(celldist(pn2) < celldist(pt)) return pn2;
|
|
return NULL;
|
|
}
|
|
|
|
bool buildBarrierNowall(cell *c, eLand l2, bool force = false);
|
|
|
|
bool safety = false;
|
|
|
|
eLand firstland = laIce, euclidland = laIce;
|
|
|
|
bool timerghost;
|
|
eLand lastland;
|
|
|
|
int lastexplore;
|
|
|
|
bool randomPatternsMode = false;
|
|
int randompattern[landtypes];
|
|
|
|
bool chaosmode = false;
|
|
bool chaosUnlocked = false;
|
|
bool chaosAchieved = false;
|
|
|
|
eItem treasureType(eLand l) {
|
|
switch(l) {
|
|
case laIce: return itDiamond;
|
|
case laJungle: return itRuby;
|
|
case laCaves: return itGold;
|
|
case laDesert: return itSpice;
|
|
|
|
case laAlchemist: return itElixir;
|
|
case laMirror: 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 laNone: return itNone;
|
|
case laBarrier: return itNone;
|
|
case laOceanWall: return itNone;
|
|
case laCanvas: 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 itEdge;
|
|
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 laClearing: return itMutant2;
|
|
case laHaunted: case laHauntedWall: case laHauntedBorder: return itLotus;
|
|
case laWhirlwind: return itWindstone;
|
|
|
|
case laRose: return itRose;
|
|
case laGridCoast: case laGridSea: return itCoral;
|
|
|
|
case laDragon: return itDragon;
|
|
case laEndorian: return itApple;
|
|
case laTortoise: return itBabyTortoise;
|
|
}
|
|
return itNone;
|
|
}
|
|
|
|
#define ORBLINES 46
|
|
|
|
struct orbinfo {
|
|
eLand l;
|
|
int lchance;
|
|
int gchance;
|
|
eItem orb;
|
|
};
|
|
|
|
orbinfo orbinfos[ORBLINES] = {
|
|
{laGraveyard, 200, 200,itGreenStone}, // must be first so that it does not reduce
|
|
// chance of other orbs
|
|
{laJungle, 1200, 1500,itOrbLightning},
|
|
{laIce, 2000, 1500,itOrbFlash},
|
|
{laCaves, 1800, 2000,itOrbLife},
|
|
{laAlchemist, 800, 800,itOrbSpeed},
|
|
{laDesert, 2500, 1500,itOrbShield},
|
|
{laHell, 2000, 1000,itOrbYendor},
|
|
{laRlyeh, 1500, 1500,itOrbTeleport},
|
|
{laMotion, 2000, 700, itOrbSafety},
|
|
{laIce, 1500, 0, itOrbWinter},
|
|
{laDragon, 2500, 0, itOrbWinter},
|
|
{laDryForest, 2500, 0, itOrbWinter},
|
|
{laCocytus, 1500, 1500, itOrbWinter},
|
|
{laCaves, 1200, 0, itOrbDigging},
|
|
{laDryForest, 500, 2500, itOrbThorns},
|
|
{laDeadCaves, 1800, 0, itGreenStone},
|
|
{laDeadCaves, 1800, 1500, itOrbDigging},
|
|
{laEmerald, 1500, 3500, itOrbPsi},
|
|
{laWineyard, 900, 1200, itOrbGhost},
|
|
{laHive, 800, 1200, itOrbInvis},
|
|
{laPower, 0, 3000, itOrbFire},
|
|
{laMinefield, 0, 3500, itOrbFriend},
|
|
{laTemple, 0, 3000, itOrbDragon},
|
|
{laCaribbean, 0, 3500, itOrbPreserve},
|
|
{laRedRock, 0, 2500, itOrbTelekinesis},
|
|
{laCamelot, 1000, 1500, itOrbIllusion},
|
|
{laOcean, 0, 3000, itOrbEmpathy},
|
|
{laOcean, 0, 0, itOrbAir},
|
|
{laPalace, 0, 4000, itOrbDiscord},
|
|
{laZebra, 500, 1500, itOrbFrog},
|
|
{laLivefjord, 0, 1800, itOrbFish},
|
|
{laPrincessQuest, 0, 200, itOrbLove},
|
|
{laIvoryTower, 500, 4000, itOrbMatter},
|
|
{laElementalWall, 1500, 4000, itOrbSummon},
|
|
{laStorms, 1000, 2500, itOrbStunning},
|
|
{laOvergrown, 1000, 800, itOrbLuck},
|
|
{laWhirlwind, 1250, 3000, itOrbAir},
|
|
{laHaunted, 1000, 5000, itOrbUndeath},
|
|
{laClearing, 5000, 5000, itOrbFreedom},
|
|
{laRose, 2000, 8000, itOrbSkunk},
|
|
{laGridCoast, 2000, 8000, itOrb37},
|
|
{laDragon, 500, 5000, itOrbDomination},
|
|
{laTortoise, 2500, 1500, itOrbShell},
|
|
{laEndorian, 150, 2500, itOrbEnergy},
|
|
{laEndorian, 450, 0, itOrbTeleport},
|
|
{laWhirlpool, 0, 2000, itOrbWater}, // must be last because it generates a boat
|
|
};
|
|
|
|
bool isElemental(eLand l);
|
|
|
|
eItem orbType(eLand l) {
|
|
if(isElemental(l)) l = laElementalWall;
|
|
if(l == laMirror) return itShard;
|
|
for(int i=0; i<ORBLINES; i++)
|
|
if(orbinfos[i].l == l && orbinfos[i].gchance)
|
|
return orbinfos[i].orb;
|
|
return itNone;
|
|
}
|
|
|
|
enum eOrbLandRelation {
|
|
olrForbidden, // never appears: forbidden
|
|
olrDangerous, // never appears: would be dangerous
|
|
olrUseless, // never appears: useless here
|
|
olrNoPrizes, // no prizes in this land
|
|
olrNoPrizeOrb,// orb not allowed as a prize
|
|
olrPrize25, // prize for collecting 25
|
|
olrPrize3, // prize for collecting 3
|
|
olrNative, // native orb in this land
|
|
olrNative1, // native orb in this land (1)
|
|
olrGuest, // extra orb in this land
|
|
olrPNative, // Land of Power: native
|
|
olrPBasic, // Land of Power: basic orbs
|
|
olrPPrized, // Land of Power: prized orbs
|
|
olrPNever, // Land of Power: foreign orbs
|
|
olrHub, // hub lands
|
|
olrMonster, // available from a monster
|
|
olrAlways // always available
|
|
};
|
|
|
|
string olrDescriptions[] = {
|
|
"forbidden to find in %the1",
|
|
"too dangerous to use in %the1",
|
|
"useless in %the1",
|
|
"only native Orbs allowed in %the1",
|
|
"this Orb is never unlocked globally (only hubs)",
|
|
"collect 25 %2 to unlock it in %the1",
|
|
"collect 3 %2 to unlock it in %the1",
|
|
"native in %the1 (collect 10 %2)",
|
|
"native in %the1 (collect 1 %2)",
|
|
"secondary in %the1 (collect 10 %3, or 25 %2)",
|
|
"the native Orb of %the1",
|
|
"this Orb appears on floors and is used by witches",
|
|
"a prized Orb, it appears only in cabinets",
|
|
"this Orb never appears in %the1",
|
|
"Hub Land: orbs appear here if unlocked in their native land",
|
|
"kill a monster, or collect 25 %2",
|
|
"always available"
|
|
};
|
|
|
|
eOrbLandRelation getOLR(eItem it, eLand l) {
|
|
|
|
if(l == laPower) {
|
|
if(it == itOrbFire) return olrPNative;
|
|
|
|
if(
|
|
it == itOrbFlash || it == itOrbSpeed || it == itOrbWinter || it == itOrbGhost ||
|
|
it == itOrbLife) return olrPBasic;
|
|
|
|
if(
|
|
it == itOrbLightning || it == itOrbThorns || it == itOrbInvis ||
|
|
it == itOrbShield || it == itOrbTeleport || it == itOrbPsi ||
|
|
it == itOrbDragon || it == itOrbIllusion || it == itOrbPreserve)
|
|
return olrPPrized;
|
|
|
|
return olrPNever;
|
|
}
|
|
|
|
// if(it == itOrbYendor && l == laWhirlpool) return olrUseless;
|
|
if(it == itOrbYendor && l == laWhirlwind) return olrUseless;
|
|
|
|
if(it == itOrbAir && l == laAlchemist) return olrUseless;
|
|
// if(it == itOrbShield && l == laMotion) return olrUseless;
|
|
|
|
if(it == itOrbIllusion && l == laCamelot) return olrNative1;
|
|
if(it == itOrbLove) return olrNoPrizeOrb;
|
|
if(orbType(l) == it) return olrNative;
|
|
if(it == itOrbWinter && (l == laIce || l == laDryForest))
|
|
return olrGuest;
|
|
if(it == itOrbLuck && l == laIvoryTower)
|
|
return olrUseless;
|
|
if(it == itOrbLuck && l == laEndorian)
|
|
return olrUseless;
|
|
if(it == itOrbLuck && l == laTortoise)
|
|
return olrUseless;
|
|
if(it == itOrbDigging && l == laCaves)
|
|
return olrGuest;
|
|
if(it == itOrbFrog && (l == laPalace || l == laPrincessQuest))
|
|
return olrGuest;
|
|
if(it == itOrbDragon && l == laRlyeh)
|
|
return olrMonster;
|
|
|
|
if(it == itOrbSafety && l == laWhirlpool)
|
|
return olrAlways;
|
|
if(it == itOrbWater && l == laLivefjord)
|
|
return olrMonster;
|
|
if(isCrossroads(l) || l == laOcean)
|
|
return olrHub;
|
|
|
|
if(l == laCocytus)
|
|
if(it == itOrbDragon || it == itOrbFire || it == itOrbFlash || it == itOrbLightning)
|
|
return olrDangerous;
|
|
|
|
if(it == itOrbSafety)
|
|
if(l == laCaves || l == laLivefjord || l == laRedRock || l == laCocytus || l == laHell ||
|
|
l == laDesert || l == laAlchemist || l == laDeadCaves || l == laMinefield || isHaunted(l) ||
|
|
l == laDragon)
|
|
return olrDangerous;
|
|
|
|
if(it == itOrbMatter)
|
|
if(l == laCaves || l == laEmerald || l == laAlchemist || l == laCaribbean ||
|
|
l == laMinefield || l == laCocytus) return olrUseless;
|
|
|
|
if(l == laPrincessQuest)
|
|
if(it == itOrbGhost || it == itOrbFlash || it == itOrbTeleport || it == itOrbSummon || it == itOrbFreedom)
|
|
return olrForbidden;
|
|
|
|
if(l == laTemple)
|
|
return olrNoPrizes;
|
|
|
|
if(it == itOrbDigging) {
|
|
if(l == laCaves || l == laOcean || l == laLivefjord || l == laEmerald ||
|
|
l == laDesert || l == laDeadCaves || l == laRedRock || l == laCaribbean || l == laGraveyard)
|
|
return olrPrize25;
|
|
return olrUseless;
|
|
}
|
|
|
|
if(it == itShard) {
|
|
if(l == laDesert || l == laIce || l == laJungle || l == laGraveyard ||
|
|
l == laRlyeh || l == laHell || l == laDryForest || l == laWineyard ||
|
|
l == laHive || l == laCamelot || l == laRedRock || l == laPalace ||
|
|
l == laLivefjord || l == laZebra || isElemental(l) || l == laPrincessQuest)
|
|
return olrPrize25;
|
|
return olrForbidden;
|
|
}
|
|
|
|
if(it == itOrbWater)
|
|
if(l == laMotion || l == laZebra || l == laIvoryTower || l == laEndorian)
|
|
return olrUseless;
|
|
|
|
if(it == itOrbWinter && l != laRlyeh && l != laTemple)
|
|
return olrUseless;
|
|
|
|
if(it == itOrbLife && l == laMotion)
|
|
return olrUseless;
|
|
|
|
if(it == itOrbFish && l != laOcean && l != laLivefjord && l != laWhirlpool && l != laCamelot &&
|
|
l != laTortoise)
|
|
return olrUseless;
|
|
|
|
if(it == itOrbDragon && l != laOcean && l != laRedRock && l != laDesert &&
|
|
l != laRlyeh && l != laDragon)
|
|
return olrUseless;
|
|
|
|
if(it == itOrbIllusion) return olrPrize3;
|
|
|
|
if(l == laTortoise)
|
|
if(it == itOrbFlash || it == itOrbLightning || it == itOrbFreedom ||
|
|
it == itOrbPsi || it == itOrbFriend || it == itOrbDragon)
|
|
return olrForbidden;
|
|
|
|
if(l == laEndorian)
|
|
if(it == itOrbDragon || it == itOrbFire)
|
|
return olrDangerous;
|
|
|
|
return olrPrize25;
|
|
}
|
|
|
|
int landMultiplier(eLand l) {
|
|
if(l == laCamelot || l == laPrincessQuest) return 10;
|
|
return 1;
|
|
}
|
|
|
|
// 2 = always available, 1 = highscore required, 0 = never available
|
|
int isRandland(eLand l) {
|
|
if(l == laIce || l == laDesert || l == laCaves || l == laWildWest)
|
|
return 2;
|
|
for(int i=0; i<RANDLANDS; i++) if(randlands[i] == l) return 1;
|
|
return 0;
|
|
}
|
|
|
|
bool landUnlocked(eLand l) {
|
|
if(randomPatternsMode) {
|
|
int i = isRandland(l);
|
|
if(i == 2) return true;
|
|
if(i == 1) return hiitemsMax(treasureType(l)) >= 10;
|
|
return false;
|
|
}
|
|
|
|
switch(l) {
|
|
case laOvergrown:
|
|
return gold() >= 60 && items[itRuby] >= 10;
|
|
|
|
case laStorms: case laWhirlwind:
|
|
return gold() >= 60;
|
|
|
|
case laWildWest:
|
|
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:
|
|
return gold() >= 30;
|
|
|
|
case laCaribbean: case laWhirlpool:
|
|
return exploreland[0][laOcean] || items[itCoast] || items[itStatue];
|
|
|
|
case laRlyeh: case laDryForest: case laWineyard: case laCrossroads2:
|
|
return gold() >= 60;
|
|
|
|
case laDeadCaves:
|
|
return gold() >= 60 && items[itGold] >= 10;
|
|
|
|
case laGraveyard:
|
|
return tkills() >= 100;
|
|
|
|
case laHive:
|
|
return tkills() >= 100 && gold() >= 60;
|
|
|
|
case laRedRock:
|
|
return gold() >= 60 && items[itSpice] >= 10;
|
|
|
|
case laEmerald:
|
|
return (items[itFernFlower] >= 5 && items[itGold] >= 5) || kills[moVizier];
|
|
|
|
case laCamelot:
|
|
return items[itEmerald] >= 5;
|
|
|
|
case laHell: case laCrossroads3:
|
|
return hellUnlocked();
|
|
|
|
case laPower:
|
|
return items[itHell] >= 10;
|
|
|
|
case laCocytus:
|
|
return items[itHell] >= 10 && items[itDiamond] >= 10;
|
|
|
|
case laTemple:
|
|
return items[itStatue] >= 5;
|
|
|
|
case laClearing:
|
|
return items[itMutant] >= 5;
|
|
|
|
case laIvoryTower: return gold() >= 30 && items[itElixir] >= 10;
|
|
case laZebra: return gold() >= 30 && items[itFeather] >= 10;
|
|
|
|
case laEAir: case laEEarth: case laEWater: case laEFire: case laElementalWall:
|
|
return elementalUnlocked();
|
|
|
|
case laBarrier: case laNone: case laOceanWall: case laCanvas:
|
|
return false;
|
|
|
|
case laHaunted: case laHauntedWall: case laHauntedBorder:
|
|
return items[itBone] >= 10;
|
|
|
|
case laPrincessQuest: return kills[moVizier];
|
|
|
|
case laRose:
|
|
return gold() >= 60;
|
|
|
|
case laGridCoast: case laGridSea:
|
|
return gold() >= 30;
|
|
|
|
case laCrossroads4:
|
|
return gold() >= 200;
|
|
|
|
case laEndorian:
|
|
return items[itEdge] >= 10;
|
|
|
|
case laTortoise:
|
|
return tortoise::seek();
|
|
|
|
case laDragon:
|
|
return killtypes() >= 20;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int orbsUnlocked() {
|
|
int i = 0;
|
|
for(int t=0; t<ittypes; t++)
|
|
if(itemclass(eItem(t)) == IC_TREASURE && items[t] >= 10)
|
|
i++;
|
|
return i;
|
|
}
|
|
|
|
bool hellUnlocked() {
|
|
return orbsUnlocked() >= 9;
|
|
}
|
|
|
|
void countHyperstoneQuest(int& i1, int& i2) {
|
|
i1 = 0; i2 = 0;
|
|
for(int t=1; t<ittypes; t++)
|
|
if(t != itHyperstone && t != itBounty && itemclass(eItem(t)) == IC_TREASURE) {
|
|
i2++; if(items[t] >= 10) i1++;
|
|
}
|
|
}
|
|
|
|
bool hyperstonesUnlocked() {
|
|
int i1, i2;
|
|
if(tactic::on && isCrossroads(tactic::lasttactic) && !tactic::trailer) return true;
|
|
countHyperstoneQuest(i1, i2);
|
|
return i1 == i2;
|
|
}
|
|
|
|
// reduce c->mpdist to d; also generate the landscape
|
|
|
|
bool buggyGeneration = false;
|
|
|
|
bool checkBarriersBack(cellwalker bb, int q=5, bool cross = false);
|
|
|
|
bool checkBarriersFront(cellwalker bb, int q=5, bool cross = false) {
|
|
|
|
if(bb.c->type == 6)
|
|
return false;
|
|
|
|
if(bb.c->mpdist < BARLEV) return false;
|
|
if(bb.c->mpdist == BUGLEV) return false;
|
|
if(bb.c->bardir != NODIR) return false;
|
|
if(bb.spin == (purehepta ? 3 : 0)) {q--; if(!q) return true; }
|
|
|
|
if(!cross) for(int i=0; i<7; i++) {
|
|
cellwalker bb2 = bb;
|
|
cwspin(bb2, i); cwstep(bb2);
|
|
if(bb2.c->bardir != NODIR) return false;
|
|
if(!purehepta) {
|
|
cwspin(bb2, 4); cwstep(bb2);
|
|
if(bb2.c->bardir != NODIR) return false;
|
|
}
|
|
}
|
|
|
|
cwstep(bb);
|
|
if(!purehepta) { cwspin(bb, 3); cwstep(bb); cwspin(bb, 3); cwstep(bb); }
|
|
return checkBarriersBack(bb, q);
|
|
}
|
|
|
|
bool checkBarriersBack(cellwalker bb, int q, bool cross) {
|
|
// printf("back, %p, s%d\n", bb.c, bb.spin);
|
|
|
|
// if(mark) { printf("mpdist = %d [%d] bardir = %d spin=%d q=%d cross=%d\n", bb.c->mpdist, BARLEV, bb.c->bardir, bb.spin, q, cross); }
|
|
|
|
if(bb.c->mpdist < BARLEV) return false;
|
|
if(bb.c->mpdist == BUGLEV) return false;
|
|
if(bb.c->bardir != NODIR) return false;
|
|
|
|
// if(bb.spin == 0 && bb.c->mpdist == INFD) return true;
|
|
|
|
if(!cross) for(int i=0; i<7; i++) {
|
|
cellwalker bb2 = bb;
|
|
cwspin(bb2, i); cwstep(bb2);
|
|
if(bb2.c->bardir != NODIR) return false;
|
|
if(!purehepta) {
|
|
cwspin(bb2, 4); cwstep(bb2);
|
|
if(bb2.c->bardir != NODIR) return false;
|
|
}
|
|
}
|
|
|
|
cwspin(bb, 3); cwstep(bb); cwspin(bb, purehepta ? 5 : 4);
|
|
// bool create = cwstepcreates(bb);
|
|
cwstep(bb); cwspin(bb, 3);
|
|
// if(create && bb.spin == 0) return true;
|
|
return checkBarriersFront(bb, q);
|
|
}
|
|
|
|
const eLand NOWALLSEP = laNone;
|
|
const eLand NOWALLSEP_USED = laWhirlpool;
|
|
|
|
void setland(cell *c, eLand l);
|
|
|
|
bool checkBarriersNowall(cellwalker bb, int q, int dir, eLand l1=laNone, eLand l2=laNone) {
|
|
if(bb.c->mpdist < BARLEV && l1 == laNone) return false;
|
|
if(bb.c->bardir != NODIR && l1 == laNone) return false;
|
|
|
|
if(l1 != laNone) {
|
|
bb.c->bardir = bb.spin; bb.c->barright = l2; bb.c->barleft = NOWALLSEP;
|
|
setland(bb.c, l1);
|
|
}
|
|
if(q > 10) return true;
|
|
|
|
if(l1 == laNone) for(int i=0; i<bb.c->type; i++) {
|
|
cell *c1 = bb.c->mov[i];
|
|
if(!c1) continue;
|
|
for(int j=0; j<c1->type; j++) {
|
|
cell *c2 = c1->mov[j];
|
|
if(!c2) continue;
|
|
if(c2 && c2->bardir == NOBARRIERS)
|
|
return false;
|
|
if(c2 && c2->bardir != NODIR && c2->barleft != NOWALLSEP)
|
|
return false;
|
|
// note: "far crashes" between NOWALL lines are allowed
|
|
}
|
|
}
|
|
|
|
if(purehepta) {
|
|
cwspin(bb, 3*dir);
|
|
cwstep(bb);
|
|
cwspin(bb, -3*dir);
|
|
}
|
|
else {
|
|
cwstep(bb);
|
|
cwspin(bb, 2*dir);
|
|
cwstep(bb);
|
|
cwspin(bb, dir);
|
|
}
|
|
return checkBarriersNowall(bb, q+1, -dir, l2, l1);
|
|
}
|
|
|
|
bool isSealand(eLand l) {
|
|
return l == laOcean || l == laCaribbean || l == laWhirlpool || l == laLivefjord ||
|
|
l == laOceanWall || l == laGridSea;
|
|
}
|
|
|
|
bool isElemental(eLand l) {
|
|
return l == laEAir || l == laEWater || l == laEEarth || l == laEFire ||
|
|
l == laElementalWall;
|
|
}
|
|
|
|
bool isHaunted(eLand l) {
|
|
return l == laHaunted || l == laHauntedBorder || l == laHauntedWall;
|
|
}
|
|
|
|
eWall getElementalWall(eLand l) {
|
|
if(l == laEAir) return waChasm;
|
|
if(l == laEEarth) return waStone;
|
|
if(l == laEFire) return waEternalFire;
|
|
if(l == laEWater) return waSea;
|
|
return waNone;
|
|
}
|
|
|
|
void setbarrier(cell *c) {
|
|
if(isSealand(c->barleft) && isSealand(c->barright)) {
|
|
c->wall = c->type == 7 ? waBarrier : waSea;
|
|
c->land = laOceanWall;
|
|
}
|
|
else if(isElemental(c->barleft) && isElemental(c->barright)) {
|
|
c->land = laElementalWall;
|
|
c->wall = getElementalWall(c->barleft);
|
|
}
|
|
else if(c->barleft == laHaunted || c->barright == laHaunted) {
|
|
c->land = laHauntedWall;
|
|
}
|
|
else {
|
|
c->wall = waBarrier;
|
|
c->land = laBarrier;
|
|
}
|
|
/*if(isHive(c->barleft) && isHive(c->barright))
|
|
c->wall = waWaxWall, c->land = c->barleft; */
|
|
}
|
|
|
|
int buildIvy(cell *c, int children, int minleaf) {
|
|
if(c->monst) return 0;
|
|
c->mondir = NODIR;
|
|
c->monst = moIvyRoot;
|
|
|
|
cell *child = NULL;
|
|
|
|
int leaf = 0;
|
|
int leafchild = 0;
|
|
for(int i=0; i<c->type; i++) {
|
|
createMov(c, i);
|
|
if(passable(c->mov[i], c, 0) && c->mov[i]->land == c->land) {
|
|
if(children && !child)
|
|
child = c->mov[i], leafchild = buildIvy(c->mov[i], children-1, 5);
|
|
else
|
|
c->mov[i]->monst = (leaf++) ? moIvyWait : moIvyHead,
|
|
c->mov[i]->mondir = c->spn[i];
|
|
}
|
|
}
|
|
|
|
leaf += leafchild;
|
|
if(leaf < minleaf) {
|
|
if(child) killIvy(child);
|
|
killIvy(c);
|
|
return 0;
|
|
}
|
|
else return leaf;
|
|
}
|
|
|
|
bool grailWasFound(cell *c) {
|
|
if(euclid) return items[itHolyGrail];
|
|
return c->master->alt->alt->emeraldval & GRAIL_FOUND;
|
|
}
|
|
|
|
int euclidAlt(short x, short y) {
|
|
if(euclidland == laTemple || euclidland == laClearing) {
|
|
return max(int(x), x+y);
|
|
}
|
|
else if(euclidland == laCaribbean || euclidland == laWhirlpool) {
|
|
return
|
|
min(
|
|
min(max(int(-x), -x-y) + 3,
|
|
max(int(x+y), int(y)) + 3),
|
|
max(int(x), int(-y)) + 3
|
|
);
|
|
}
|
|
else if(euclidland == laPrincessQuest)
|
|
return eudist(x-EPX, y-EPY);
|
|
else return eudist(x-20, y-10);
|
|
}
|
|
|
|
bool isCrossroads(eLand l) {
|
|
return l == laCrossroads || l == laCrossroads2 || l == laCrossroads3 ||
|
|
l == laCrossroads4;
|
|
}
|
|
|
|
bool bearsCamelot(eLand l) {
|
|
return isCrossroads(l) && l != laCrossroads2;
|
|
}
|
|
|
|
void buildPrizeMirror(cell *c) {
|
|
if(c->type == 7 && !purehepta) return;
|
|
if(items[itShard] < 25) return;
|
|
c->wall = hrand(2) ? waCloud : waMirror;
|
|
}
|
|
|
|
void placePrizeOrb(cell *c) {
|
|
eLand l = c->land;
|
|
if(isElemental(l)) l = laElementalWall;
|
|
|
|
// these two lands would have too much orbs according to normal rules
|
|
if(l == laPalace && hrand(100) >= 20) return;
|
|
if(l == laGraveyard && hrand(100) >= 15) 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<ORBLINES; i++) {
|
|
orbinfo& oi(orbinfos[i]);
|
|
eOrbLandRelation olr = getOLR(oi.orb, l);
|
|
if(olr != olrPrize25 && olr != olrPrize3) continue;
|
|
if(olr == olrPrize25 || olr == olrGuest || olr == olrMonster || olr == olrAlways) {
|
|
if(items[treasureType(oi.l)] < 25) continue;
|
|
}
|
|
else if(olr == olrPrize3) { if(items[treasureType(oi.l)] < 3) continue; }
|
|
else continue;
|
|
|
|
if(!oi.gchance) continue;
|
|
if(hrand(oi.gchance) >= 60) continue;
|
|
if(oi.orb == itOrbWater && c->land != laOcean) {
|
|
if(cellHalfvine(c)) continue;
|
|
c->item = oi.orb;
|
|
c->wall = waStrandedBoat;
|
|
return;
|
|
}
|
|
c->item = oi.orb;
|
|
}
|
|
|
|
// printf("land: %s orb: %s\n", dnameof(l), dnameof(c->item));
|
|
}
|
|
|
|
void placeLocalOrbs(cell *c) {
|
|
eLand l = c->land;
|
|
if(l == laZebra && c->wall == waTrapdoor) return;
|
|
if(isGravityLand(l) && cellEdgeUnstable(c)) return;
|
|
if(isElemental(l)) l = laElementalWall;
|
|
|
|
for(int i=0; i<ORBLINES; i++) {
|
|
orbinfo& oi(orbinfos[i]);
|
|
if(oi.l != l) continue;
|
|
if(yendor::on && (oi.orb == itOrbSafety || oi.orb == itOrbYendor))
|
|
continue;
|
|
if(!oi.lchance) continue;
|
|
int ch = hrand(oi.lchance);
|
|
if(tactic::trailer && ch < 5) ch = 0;
|
|
if(ch == 0 && items[treasureType(oi.l)] * landMultiplier(oi.l) >= 10) {
|
|
// printf("local orb\n");
|
|
c->item = oi.orb;
|
|
if(oi.orb == itOrbWater && c->land != laOcean) c->wall = waStrandedBoat;
|
|
return;
|
|
}
|
|
else if(oi.gchance && ch == 1 && getOLR(itShard, l) == olrPrize25 && l != laRedRock && l != laWhirlwind)
|
|
buildPrizeMirror(c);
|
|
else if(oi.gchance && (ch >= 2 && ch < 2+PRIZEMUL))
|
|
placePrizeOrb(c);
|
|
}
|
|
}
|
|
|
|
void placeCrossroadOrbs(cell *c) {
|
|
for(int i=0; i<ORBLINES; i++) {
|
|
orbinfo& oi(orbinfos[i]);
|
|
if(!oi.gchance) continue;
|
|
if(tactic::on && isCrossroads(tactic::lasttactic)) {
|
|
if(oi.orb == itOrbYendor || oi.orb == itOrbSummon || oi.orb == itOrbFish || oi.orb == itOrbDigging || oi.orb == itOrbLove)
|
|
continue;
|
|
}
|
|
else {
|
|
if(items[treasureType(oi.l)] * landMultiplier(oi.l) < 10) continue;
|
|
}
|
|
if(hrand(oi.gchance)) continue;
|
|
if(hrand(50+items[itHyperstone]) >= 50) continue;
|
|
c->item = oi.orb;
|
|
if(oi.orb == itOrbWater && c->land != laOcean) c->wall = waStrandedBoat;
|
|
}
|
|
}
|
|
|
|
void placeOceanOrbs(cell *c) {
|
|
for(int i=0; i<ORBLINES; i++) {
|
|
orbinfo& oi(orbinfos[i]);
|
|
if(items[treasureType(oi.l)] * landMultiplier(oi.l) < 10) continue;
|
|
if(!oi.gchance) continue;
|
|
if(hrand(oi.gchance) >= 20) continue;
|
|
c->item = oi.orb;
|
|
}
|
|
}
|
|
|
|
bool errorReported = false;
|
|
|
|
void describeCell(cell *c) {
|
|
if(!c) { printf("NULL\n"); return; }
|
|
printf("describe %p: ", c);
|
|
printf("%-15s", linf[c->land].name);
|
|
printf("%-15s", winf[c->wall].name);
|
|
printf("%-15s", iinf[c->item].name);
|
|
printf("%-15s", minf[c->monst].name);
|
|
printf("%3d", c->landparam);
|
|
printf("%3d", c->mpdist);
|
|
printf("%3d", c->mondir);
|
|
printf("\n");
|
|
}
|
|
|
|
void raiseBuggyGeneration(cell *c, const char *s) {
|
|
|
|
printf("procgen error in: %s\n", s);
|
|
|
|
if(!errorReported) {
|
|
addMessage(string("something strange happened in: ") + s);
|
|
errorReported = true;
|
|
}
|
|
// return;
|
|
|
|
#ifdef LOCAL
|
|
|
|
describeCell(c);
|
|
for(int i=0; i<c->type; i++) describeCell(c->mov[i]);
|
|
|
|
buggyGeneration = true; buggycells.push_back(c);
|
|
|
|
#else
|
|
c->item = itBuggy;
|
|
#endif
|
|
}
|
|
|
|
void setland(cell *c, eLand l) {
|
|
if(c->land != l)
|
|
c->landparam = 0;
|
|
if(l == laNone) {
|
|
printf("setland\n"); // NONEDEBUG
|
|
}
|
|
c->land = l;
|
|
}
|
|
|
|
void extendBarrierFront(cell *c) {
|
|
int ht = c->landparam;
|
|
|
|
cellwalker bb(c, c->bardir); setbarrier(bb.c);
|
|
cwstep(bb);
|
|
|
|
if(!purehepta) {
|
|
bb.c->barleft = c->barleft;
|
|
bb.c->barright = c->barright;
|
|
bb.c->landparam = (ht-4);
|
|
//printf("[A heat %d]\n", ht-4);
|
|
setbarrier(bb.c);
|
|
cwspin(bb, 2); cwstep(bb); setland(bb.c, c->barleft); cwstep(bb);
|
|
cwspin(bb, 2); cwstep(bb); setland(bb.c, c->barright); cwstep(bb);
|
|
cwspin(bb, 2);
|
|
|
|
cwspin(bb, 3); cwstep(bb);
|
|
bb.c->landparam = (ht-4)^2;
|
|
//printf("[B heat %d]\n", (ht-4)^2);
|
|
bb.c->barleft = c->barright;
|
|
bb.c->barright = c->barleft;
|
|
setbarrier(bb.c);
|
|
cwspin(bb, 3); cwstep(bb);
|
|
|
|
bb.c->landparam = ht ^ 2;
|
|
}
|
|
|
|
//printf("[C heat %d]\n", (ht)^2);
|
|
bb.c->bardir = bb.spin;
|
|
bb.c->barleft = c->barright;
|
|
bb.c->barright = c->barleft;
|
|
// printf("#1\n");
|
|
extendBarrier(bb.c);
|
|
|
|
for(int a=-3; a<=3; a++) if(a) {
|
|
bb.c = c; bb.spin = c->bardir; cwspin(bb, purehepta?-a:a); cwstep(bb);
|
|
setland(bb.c, a > 0 ? c->barright : c->barleft);
|
|
}
|
|
}
|
|
|
|
void extendBarrierBack(cell *c) {
|
|
int ht = c->landparam;
|
|
|
|
cellwalker bb(c, c->bardir); setbarrier(bb.c);
|
|
cwspin(bb, 3); cwstep(bb); cwspin(bb, purehepta?5:4); setland(bb.c, purehepta ? c->barleft : c->barright); cwstep(bb); cwspin(bb, 3);
|
|
bb.c->bardir = bb.spin;
|
|
bb.c->barleft = c->barright;
|
|
bb.c->barright = c->barleft;
|
|
bb.c->landparam = ht ^ 11;
|
|
//printf("[D heat %d]\n", (ht^11));
|
|
|
|
// needed for CR2 to work
|
|
if(!purehepta) {
|
|
cwstep(bb);
|
|
bb.c->barleft = c->barright;
|
|
bb.c->barright = c->barleft;
|
|
bb.c->landparam = (ht^11)-4;
|
|
cwstep(bb);
|
|
}
|
|
//printf("[E heat %d]\n", (ht^11));
|
|
|
|
// printf("#2\n");
|
|
extendBarrier(bb.c);
|
|
}
|
|
|
|
eLand oppositeElement(eLand l) {
|
|
if(l == laEFire) return laEWater;
|
|
if(l == laEWater) return laEFire;
|
|
if(l == laEAir) return laEEarth;
|
|
if(l == laEEarth) return laEAir;
|
|
return l;
|
|
}
|
|
|
|
void extendNowall(cell *c) {
|
|
c->barleft = NOWALLSEP_USED;
|
|
cellwalker cw(c, c->bardir);
|
|
|
|
if(!purehepta) {
|
|
cwstep(cw);
|
|
setland(cw.c, c->barright);
|
|
}
|
|
|
|
for(int i=-1; i<2; i+=2) {
|
|
if(purehepta) {
|
|
cwspin(cw, 3*i);
|
|
cwstep(cw);
|
|
cwspin(cw, -3*i);
|
|
}
|
|
else {
|
|
cwspin(cw, 2*i);
|
|
cwstep(cw);
|
|
}
|
|
setland(cw.c, c->barright);
|
|
if(cw.c->barleft != NOWALLSEP_USED) {
|
|
cw.c->barleft = NOWALLSEP;
|
|
cw.c->barright = c->land;
|
|
if(c->barright == laNone) {
|
|
printf("barright\n");
|
|
}// NONEDEBUG
|
|
cw.c->land = c->barright;
|
|
if(!purehepta) cwspin(cw, i);
|
|
cw.c->bardir = cw.spin;
|
|
if(!purehepta) cwspin(cw, -i);
|
|
extendBarrier(cw.c);
|
|
}
|
|
if(purehepta) {
|
|
cwspin(cw, 3*i);
|
|
cwstep(cw);
|
|
cwspin(cw, -3*i);
|
|
}
|
|
else {
|
|
cwstep(cw);
|
|
cwspin(cw, -2*i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void extendBarrier(cell *c) {
|
|
if(buggyGeneration) return;
|
|
|
|
if(c->barleft == NOWALLSEP_USED) return;
|
|
|
|
// printf("build barrier at %p", c);
|
|
if(c->wall == waBarrier || c->land == laElementalWall || c->land == laHauntedWall) {
|
|
// printf("-> ready\n");
|
|
return;
|
|
}
|
|
// if(c->wall == waWaxWall) return;
|
|
if(c->mpdist > BARLEV) {
|
|
// printf("-> too far\n");
|
|
return; // == INFD) return;
|
|
}
|
|
|
|
if(c->barleft == NOWALLSEP) {
|
|
extendNowall(c);
|
|
return;
|
|
}
|
|
|
|
if(((c->barleft == laCrossroads3 || c->barright == laCrossroads3) && hrand(100) < 66) ||
|
|
(isElemental(c->barleft) && isElemental(c->barright) && hrand(100) < 25)) {
|
|
|
|
cellwalker cw(c, c->bardir);
|
|
if(purehepta) {
|
|
cwstep(cw); if(cw.c->wall == waBarrier || cw.c->land == laElementalWall) {
|
|
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);
|
|
if(b) return;
|
|
}
|
|
else {
|
|
bool b = buildBarrier4(c, c->bardir, 1, c->barleft, c->barright);
|
|
if(b) return;
|
|
}
|
|
}
|
|
else {
|
|
cwspin(cw, 3); cwstep(cw);
|
|
cell *cp = cwpeek(cw, 4);
|
|
if(cp->wall != waBarrier && cp->land != laElementalWall) {
|
|
cwspin(cw, 2); cwstep(cw);
|
|
bool b = buildBarrier4(cw.c, cw.spin, 2, oppositeElement(c->barleft), c->barright);
|
|
if(b) return;
|
|
}
|
|
else {
|
|
bool b = buildBarrier4(c, c->bardir, 1, c->barleft, c->barright);
|
|
if(b) return;
|
|
}
|
|
}
|
|
}
|
|
|
|
extendBarrierFront(c);
|
|
extendBarrierBack(c);
|
|
}
|
|
|
|
void chasmify(cell *c) {
|
|
c->wall = waChasm; c->item = itNone;
|
|
int q = 0;
|
|
cell *c2[10];
|
|
for(int i=0; i<c->type; i++) if(c->mov[i] && c->mov[i]->mpdist > c->mpdist && cellUnstable(c->mov[i]))
|
|
c2[q++] = c->mov[i];
|
|
if(q) {
|
|
cell *c3 = c2[hrand(q)];
|
|
c3->wall = waChasmD;
|
|
}
|
|
}
|
|
|
|
void chasmifyEarth(cell *c) {
|
|
int q = 0;
|
|
int d2[10];
|
|
for(int i=2; i<=c->type-2; i++) {
|
|
int j = (i+c->mondir)%c->type;
|
|
cell *c2 = c->mov[j];
|
|
if(c2 && c2->mpdist > c->mpdist && (
|
|
c2->wall == waDeadfloor || c2->wall == waDeadwall ||
|
|
c2->wall == waDeadfloor2))
|
|
d2[q++] = j;
|
|
}
|
|
if(!q) printf("no further move!\n");
|
|
if(q) {
|
|
int d = d2[hrand(q)];
|
|
cell *c3 = c->mov[d];
|
|
c3->wall = waEarthD;
|
|
for(int i=0; i<c3->type; i++) {
|
|
cell *c4 = createMov(c3, i);
|
|
earthFloor(c4);
|
|
}
|
|
c3->mondir = c->spn[d];
|
|
}
|
|
earthWall(c); c->item = itNone;
|
|
}
|
|
|
|
void chasmifyElemental(cell *c) {
|
|
int q = 0;
|
|
int d2[10];
|
|
for(int i=2; i<=c->type-2; i++) {
|
|
int j = (i+c->mondir)%c->type;
|
|
cell *c2 = c->mov[j];
|
|
if(c2 && c2->mpdist > c->mpdist && c2->land == c->land)
|
|
d2[q++] = j;
|
|
}
|
|
if(q) {
|
|
int d = d2[hrand(q)];
|
|
cell *c3 = c->mov[d];
|
|
if(!c3->monst) {
|
|
c3->wall = waElementalD;
|
|
for(int i=0; i<c3->type; i++) {
|
|
cell *c4 = createMov(c3, i);
|
|
if(c4->wall != waBarrier) c4->wall = waNone;
|
|
}
|
|
c3->mondir = c->spn[d];
|
|
}
|
|
}
|
|
c->wall = getElementalWall(c->land);
|
|
c->wparam = 100; c->item = itNone;
|
|
}
|
|
|
|
bool incompatible1(eLand l1, eLand l2) {
|
|
if(isCrossroads(l1) && isCrossroads(l2)) return true;
|
|
if(l1 == laJungle && l2 == laMotion) return true;
|
|
if(l1 == laMirror && l2 == laMotion) return true;
|
|
if(l1 == laPower && l2 == laWineyard) return true;
|
|
if(l1 == laPower && l2 == laDryForest) 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 == 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(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];
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
#define HAUNTED_RADIUS (purehepta?5:7)
|
|
extern bool generatingEquidistant;
|
|
|
|
bool rlyehComplete() {
|
|
if(chaosmode) return items[itStatue] >= 1;
|
|
return items[itStatue] >= 10 || items[itGrimoire] >= 10;
|
|
}
|
|
|
|
bool lchance(eLand l) {
|
|
if(tactic::on || yendor::on) 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(RANDLANDS)];
|
|
if(incompatible(n, old)) continue;
|
|
if(isRandland(n) == 2) return n;
|
|
if(hiitemsMax(treasureType(n)) < 10)
|
|
continue;
|
|
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++)
|
|
|
|
template<class T> T pick(T x, T y) { return hrand(2) ? x : y; }
|
|
template<class T> T pick(T x, T y, T z) { switch(hrand(3)) { case 0: return x; case 1: return y; case 2: return z; } return x; }
|
|
template<class T> T pick(T x, T y, T z, T v) { switch(hrand(4)) { case 0: return x; case 1: return y; case 2: return z; case 3: return v; } return x; }
|
|
|
|
bool noChaos(eLand l) {
|
|
if(l == laOcean || l == laTemple) return false;
|
|
return
|
|
isCrossroads(l) || isCyclic(l) || isHaunted(l) ||
|
|
l == laCaribbean || isGravityLand(l) || l == laPrincessQuest;
|
|
}
|
|
|
|
eLand getNewSealand(eLand old) {
|
|
while(true) {
|
|
eLand p = pick(laOcean, pick(laCaribbean, laLivefjord, laGridSea));
|
|
if(p == old) continue;
|
|
if(chaosmode && noChaos(p)) continue;
|
|
return p;
|
|
}
|
|
}
|
|
|
|
bool doEndorian = false;
|
|
|
|
eLand getNewLand(eLand old) {
|
|
|
|
#ifdef LOCAL
|
|
extern bool doAutoplay;
|
|
if(doAutoplay)
|
|
return pick(laOcean, laLivefjord, laGridSea, laGridCoast);
|
|
#endif
|
|
|
|
if(items[itEdge] == 12345) return laEndorian;
|
|
|
|
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(noChaos(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(tactic::on && !(tactic::trailer && old == firstland)) return firstland;
|
|
|
|
if(yendor::on && (yendor::clev().flags & YF_WALLS)) {
|
|
if(old != yendor::clev().l) return yendor::clev().l;
|
|
}
|
|
|
|
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 ^ laGridCoast ^ laGridSea);
|
|
|
|
if(old == laGridSea || old == laCaribbean ||
|
|
(old == laLivefjord && hrand(2)) ||
|
|
(old == laOcean && (chaosmode ? hrand(2) : !generatingEquidistant)))
|
|
return getNewSealand(old);
|
|
|
|
if(old == laGraveyard && generatingEquidistant)
|
|
return laHaunted;
|
|
|
|
if(old == laOcean && gold() >= 60 && hrand(100) < 75 && !rlyehComplete())
|
|
return laRlyeh;
|
|
|
|
if(old == laRlyeh && !rlyehComplete())
|
|
return laOcean;
|
|
|
|
eLand tab[256];
|
|
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;
|
|
tab[cnt++] = laAlchemist;
|
|
if(old != laDeadCaves) tab[cnt++] = laCaves;
|
|
|
|
// the intermediate lands
|
|
if(gold() >= 30) {
|
|
tab[cnt++] = laCrossroads;
|
|
tab[cnt++] = laMirror;
|
|
tab[cnt++] = laOcean;
|
|
tab[cnt++] = laLivefjord;
|
|
tab[cnt++] = laMinefield;
|
|
tab[cnt++] = laPalace;
|
|
if(kills[moVizier]) tab[cnt++] = laEmerald;
|
|
if(items[itFeather] >= 10) tab[cnt++] = laZebra;
|
|
tab[cnt++] = laGridCoast;
|
|
if(euclid) tab[cnt++] = laGridSea;
|
|
// Ivory Tower tends to crash while generating equidistant
|
|
if(items[itElixir] >= 10 && !generatingEquidistant) tab[cnt++] = laIvoryTower;
|
|
if(items[itEdge] >= 10 && !generatingEquidistant) tab[cnt++] = laEndorian;
|
|
}
|
|
|
|
// the advanced lands
|
|
if(gold() >= 60) {
|
|
tab[cnt++] = laStorms;
|
|
tab[cnt++] = laWhirlwind;
|
|
tab[cnt++] = laCrossroads;
|
|
if(!generatingEquidistant) tab[cnt++] = laCrossroads2;
|
|
if(items[itRuby] >= 10) {
|
|
tab[cnt++] = laOvergrown;
|
|
if(old == laJungle) LIKELY tab[cnt++] = laOvergrown;
|
|
}
|
|
if(rlyehComplete()) tab[cnt++] = laRlyeh;
|
|
else if(chaosmode && (old == laGridCoast || old == laLivefjord || old == laOcean))
|
|
tab[cnt++] = laRlyeh;
|
|
if(items[itStatue] >= 5 && chaosmode)
|
|
tab[cnt++] = laTemple;
|
|
if(old == laCrossroads || old == laCrossroads2) tab[cnt++] = laOcean;
|
|
if(old == laOcean) tab[cnt++] = laCrossroads;
|
|
if(items[itGold] >= 5 && items[itFernFlower] >= 5 && !kills[moVizier])
|
|
tab[cnt++] = laEmerald;
|
|
tab[cnt++] = laDryForest;
|
|
tab[cnt++] = laWineyard;
|
|
if(items[itGold] >= 10) tab[cnt++] = laDeadCaves;
|
|
// tab[cnt++] = laCaribbean;
|
|
if(items[itSpice] >= 10) {
|
|
tab[cnt++] = laRedRock;
|
|
if(old == laDesert) LIKELY tab[cnt++] = laRedRock;
|
|
}
|
|
if(old == laRedRock) LIKELY tab[cnt++] = laDesert;
|
|
if(old == laOvergrown) LIKELY tab[cnt++] = laJungle;
|
|
tab[cnt++] = laRose;
|
|
}
|
|
|
|
if(tkills() >= 100) {
|
|
tab[cnt++] = laGraveyard;
|
|
if(gold() >= 60) tab[cnt++] = laHive;
|
|
}
|
|
|
|
if(killtypes() >= 20)
|
|
tab[cnt++] = laDragon;
|
|
|
|
if(elementalUnlocked())
|
|
tab[cnt++] = randomElementalLandWeighted();
|
|
|
|
if(hellUnlocked()) {
|
|
if(!generatingEquidistant) tab[cnt++] = laCrossroads3;
|
|
tab[cnt++] = laHell;
|
|
}
|
|
|
|
if(items[itHell] >= 10) {
|
|
if(items[itDiamond] >= 10) {
|
|
tab[cnt++] = laCocytus;
|
|
if(old == laHell || old == laIce) 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) || (chaosmode && noChaos(n))) n = tab[hrand(cnt)];
|
|
|
|
return n;
|
|
}
|
|
|
|
bool notDippingFor(eItem i) {
|
|
int v = items[i] - currentLocalTreasure;
|
|
if(v <= 10) return true;
|
|
if(v >= 20) return false;
|
|
return v >= hrand(10) + 10;
|
|
}
|
|
|
|
bool notDippingForExtra(eItem i, eItem x) {
|
|
int v = items[i] - min(items[x], currentLocalTreasure);
|
|
if(v <= 10) return true;
|
|
if(v >= 20) return false;
|
|
return v >= hrand(10) + 10;
|
|
}
|
|
|
|
eLand euland[65536];
|
|
|
|
eLand switchable(eLand nearland, eLand farland, eucoord c) {
|
|
if(euclidland == laCrossroads4) {
|
|
if(hrand(15) == 0)
|
|
return getNewLand(nearland);
|
|
return nearland;
|
|
}
|
|
else if(nearland == laCrossroads) {
|
|
if(hrand(4) == 0 && (short(c)%3==0))
|
|
return laBarrier;
|
|
return laCrossroads;
|
|
}
|
|
else if(nearland == laBarrier) {
|
|
return getNewLand(farland);
|
|
}
|
|
else {
|
|
if(hrand(20) == 0 && (short(c)%3==0))
|
|
return laBarrier;
|
|
return nearland;
|
|
}
|
|
}
|
|
|
|
eLand getEuclidLand(eucoord c) {
|
|
if(euland[c]) return euland[c];
|
|
if(c == 0 || c == eucoord(-1) || c == 1)
|
|
return euland[c] = euclidland;
|
|
if(euland[eucoord(c-2)] && ! euland[eucoord(c-1)]) getEuclidLand(c-1);
|
|
if(euland[eucoord(c+2)] && ! euland[eucoord(c+1)]) getEuclidLand(c+1);
|
|
if(euland[eucoord(c-1)]) return
|
|
euland[c] = switchable(euland[c-1], euland[eucoord(c-2)], c);
|
|
if(euland[eucoord(c+1)]) return
|
|
euland[c] = switchable(euland[c+1], euland[eucoord(c+2)], c);
|
|
return euland[c] = laCrossroads;
|
|
}
|
|
|
|
int newRoundTableRadius() {
|
|
return 28 + 2 * items[itHolyGrail];
|
|
}
|
|
|
|
int getAnthraxData(cell *c, bool b) {
|
|
int d = celldistAlt(c);
|
|
int rad = 28 + 3 * anthraxBonus;
|
|
while(d < -rad) {
|
|
d += rad + 12;
|
|
rad += 3;
|
|
}
|
|
while(d >= 12) {
|
|
if(rad > 5) rad -= 3;
|
|
else if(rad) rad--;
|
|
d -= rad + 12;
|
|
}
|
|
if(b) return rad;
|
|
return d;
|
|
}
|
|
|
|
int roundTableRadius(cell *c) {
|
|
if(euclid) return 28;
|
|
if(tactic::on) return getAnthraxData(c, true);
|
|
return c->master->alt->alt->emeraldval & GRAIL_RADIUS_MASK;
|
|
}
|
|
|
|
int celldistAltRelative(cell *c) {
|
|
if(tactic::on) return getAnthraxData(c, false);
|
|
return celldistAlt(c) - roundTableRadius(c);
|
|
}
|
|
|
|
void buildCamelotWall(cell *c) {
|
|
c->wall = waCamelot;
|
|
for(int i=0; i<c->type; i++) {
|
|
cell *c2 = createMov(c, i);
|
|
if(c2->wall == waNone && (euclid || (c2->master->alt && c->master->alt)) && celldistAlt(c2) > celldistAlt(c) && c2->monst == moNone)
|
|
c2->wall = waCamelotMoat;
|
|
}
|
|
}
|
|
|
|
eLand showlist[10] = {
|
|
laHell, laRlyeh, laAlchemist, laGraveyard, laCaves, laDesert, laIce, laJungle, laMotion, laMirror
|
|
};
|
|
|
|
void buildBarrier(cell *c, int d, eLand l) {
|
|
d %= 7;
|
|
cellwalker bb(c, d);
|
|
|
|
if(checkBarriersFront(bb) && checkBarriersBack(bb)) {
|
|
c->bardir = d;
|
|
eLand oldland = c->land;
|
|
eLand newland = l ? l : getNewLand(oldland);
|
|
if(showoff) newland = showlist[(showid++) % 10];
|
|
landcount[newland]++;
|
|
|
|
if(d == 5) c->barleft = oldland, c->barright = newland;
|
|
else c->barleft = newland, c->barright = oldland;
|
|
c->landparam = 40;
|
|
}
|
|
}
|
|
|
|
bool buildBarrier4(cell *c, int d, int mode, eLand ll, eLand lr) {
|
|
d %= 7;
|
|
cellwalker b1(c, d);
|
|
|
|
cellwalker b2(c, d);
|
|
if(purehepta) cwstep(b2);
|
|
else { cwstep(b2); cwspin(b2, 3); cwstep(b2); cwspin(b2, 3); cwstep(b2); }
|
|
|
|
cellwalker b3(c, d);
|
|
if(purehepta) {
|
|
cwspin(b3, -1); cwstep(b3); cwspin(b3, 3);
|
|
}
|
|
else {
|
|
cwstep(b3); cwspin(b3, 4); cwstep(b3); cwspin(b3, 4);
|
|
}
|
|
|
|
cellwalker b4(c, d);
|
|
if(purehepta) {
|
|
cwspin(b4, 1); cwstep(b4); cwspin(b4, -3);
|
|
}
|
|
else {
|
|
cwstep(b4); cwspin(b4, -4); cwstep(b4); cwspin(b4, -4);
|
|
}
|
|
|
|
if(mode == 0) {
|
|
if(!((checkBarriersBack(b1) && checkBarriersBack(b2)))) return false;
|
|
if(!((checkBarriersFront(b3) && checkBarriersFront(b4)))) return false;
|
|
}
|
|
|
|
if(mode == 1) {
|
|
if(!(checkBarriersFront(b3, 5, true) && checkBarriersFront(b4, 5, true)))
|
|
return false;
|
|
}
|
|
|
|
if(mode == 2) {
|
|
if(!((checkBarriersBack(b1, 5, true) && checkBarriersBack(b2, 5, true))))
|
|
return false;
|
|
}
|
|
|
|
eLand xl = oppositeElement(ll);
|
|
eLand xr = oppositeElement(lr);
|
|
|
|
c->bardir = d, c->barleft = ll, c->barright = lr; extendBarrierBack(c);
|
|
|
|
c= b2.c; d=b2.spin;
|
|
c->bardir = d, c->barleft = xl, c->barright = xr; extendBarrierBack(c);
|
|
|
|
c= b3.c; d=b3.spin;
|
|
c->bardir = d, c->barleft = xl, c->barright = lr; extendBarrierFront(c);
|
|
|
|
c= b4.c; d=b4.spin;
|
|
c->bardir = d, c->barleft = ll, c->barright = xr; extendBarrierFront(c);
|
|
|
|
if(!purehepta) for(int a=-3; a<=3; a++) if(a) {
|
|
setland(cwpeek(b1, a), a > 0 ? lr : ll);
|
|
setland(cwpeek(b2, a), a > 0 ? xr : xl);
|
|
setland(cwpeek(b3, a), a > 0 ? lr : xl);
|
|
setland(cwpeek(b4, a), a > 0 ? xr : ll);
|
|
}
|
|
|
|
if(purehepta) setbarrier(b1.c), setbarrier(b2.c), setbarrier(b3.c), setbarrier(b4.c);
|
|
|
|
if(!purehepta) {
|
|
cell *cp;
|
|
cp = cwpeek(b1, 0);
|
|
cp->barleft = ll; cp->barright = lr; setbarrier(cp);
|
|
cp = cwpeek(b2, 0);
|
|
cp->barleft = xl; cp->barright = xr; setbarrier(cp);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void buildBarrierStrong(cell *c, int d, bool oldleft) {
|
|
d %= 7;
|
|
cellwalker bb(c, d);
|
|
|
|
c->bardir = d;
|
|
eLand oldland = c->land;
|
|
eLand newland = getNewLand(oldland);
|
|
if(showoff) newland = showlist[(showid++) % 10];
|
|
landcount[newland]++;
|
|
|
|
if(oldleft) c->barleft = oldland, c->barright = newland;
|
|
else c->barleft = newland, c->barright = oldland;
|
|
}
|
|
|
|
void buildCrossroads2(cell *c) {
|
|
|
|
if(buggyGeneration) return;
|
|
|
|
if(!c) return;
|
|
|
|
for(int i=0; i<c->type; i++)
|
|
if(c->mov[i] && !c->mov[i]->landparam && c->mov[i]->mpdist < c->mpdist)
|
|
buildCrossroads2(c->mov[i]);
|
|
|
|
if(c->bardir != NODIR && c->bardir != NOBARRIERS)
|
|
extendBarrier(c);
|
|
|
|
if(c->land != laCrossroads2) return;
|
|
|
|
if(!c->landparam) {
|
|
for(int i=0; i<c->type; i++) {
|
|
cell *c2 = createMov(c, i);
|
|
if(c2 && c2->landparam && (c2->land == laCrossroads2 || c2->land == laBarrier)) {
|
|
for(int j=0; j<c2->type; j++) {
|
|
createMov(c2, j);
|
|
cell *c3 = c2->mov[j];
|
|
if(c3 && c3->landparam && (c3->land == laCrossroads2 || c3->land == laBarrier)) {
|
|
int h2 = c2->landparam;
|
|
int h3 = c3->landparam;
|
|
|
|
// ambiguous
|
|
if(h2/4 == 1 && h3/4 == 3) continue;
|
|
if(h2/4 == 3 && h3/4 == 1) continue;
|
|
|
|
for(int d=0; d<c2->type; d++)
|
|
if(emeraldtable[h2][d] == h3) {
|
|
int nh = emeraldtable[h2][(42+d + c->spn[i] - j) % c2->type];
|
|
if(c->landparam>0 && c->landparam != nh) {
|
|
printf("CONFLICT\n");
|
|
raiseBuggyGeneration(c, "CONFLICT");
|
|
}
|
|
c->landparam = nh;
|
|
}
|
|
|
|
if(c->landparam == 0)
|
|
printf("H2 = %d H3=%d\n", c2->landparam, c3->landparam);
|
|
}
|
|
}
|
|
if(c->landparam == 0) {
|
|
printf("H2 = %d\n", c2->landparam);
|
|
describeCell(c2);
|
|
for(int i=0; i<c2->type; i++) describeCell(c2->mov[i]);
|
|
// halted = true;
|
|
// c->heat = -1;
|
|
raiseBuggyGeneration(c, "buildCrossroads2x");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(c->landparam) {
|
|
// for(int i=0; i<c->type; i++) {
|
|
// cell *c2 = c->mov[i];
|
|
// buildCrossroads2(c2);
|
|
// }
|
|
}
|
|
else {
|
|
raiseBuggyGeneration(c, "buildCrossroads2");
|
|
return;
|
|
}
|
|
}
|
|
|
|
int h = c->landparam;
|
|
|
|
if(h/4 >= 8 && h/4 <= 11) {
|
|
for(int i=0; i<c->type; i++) {
|
|
createMov(c, i)->land = laCrossroads2;
|
|
if(!c->mov[i]->landparam) buildCrossroads2(c->mov[i]);
|
|
}
|
|
if(h/4 == 8 || h/4 == 10)
|
|
for(int i=0; i<c->type; i++) {
|
|
if(c->mov[i] && c->mov[i]->landparam == h-4) {
|
|
bool oldleft = true;
|
|
for(int j=1; j<=3; j++)
|
|
if(c->mov[(i+j)%7] && c->mov[(i+j)%7]->mpdist < c->mpdist)
|
|
oldleft = false;
|
|
|
|
buildBarrierStrong(c, i, oldleft);
|
|
c->landparam = h;
|
|
extendBarrier(c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern bool bugtrack;
|
|
|
|
bool generatingEquidistant = false;
|
|
|
|
void buildAnotherEquidistant(cell *c) {
|
|
//printf("building another coast\n");
|
|
|
|
if(yendor::on) return;
|
|
|
|
int gdir = -1;
|
|
for(int i=0; i<c->type; i++) {
|
|
if(c->mov[i] && c->mov[i]->mpdist < c->mpdist) gdir = i;
|
|
}
|
|
if(gdir == -1) return;
|
|
|
|
generatingEquidistant = true;
|
|
|
|
int radius = c->land == laOcean ? 30 : HAUNTED_RADIUS + 5;
|
|
|
|
cellwalker cw(c, (gdir+3) % c->type);
|
|
vector<cell*> coastpath;
|
|
while(size(coastpath) < radius || cw.c->type != 7) {
|
|
// this leads to bugs for some reason!
|
|
if(cw.c->land == laCrossroads2) {
|
|
#ifdef AUTOPLAY
|
|
if(doAutoplay) printf("avoiding the Crossroads II\n"); // todo
|
|
#endif
|
|
generatingEquidistant = false;
|
|
return;
|
|
}
|
|
coastpath.push_back(cw.c);
|
|
if(cw.c->land == laNone && cw.c->mpdist <= 7) {
|
|
raiseBuggyGeneration(cw.c, "landNone 1");
|
|
for(int i=0; i<size(coastpath); i++) coastpath[i]->item = itPirate;
|
|
return;
|
|
}
|
|
cwstep(cw); cwspin(cw, 3);
|
|
if(cw.c->type == 7 && hrand(2) == 0) cwspin(cw, 1);
|
|
}
|
|
int mpd[10];
|
|
for(int i=0; i<10; i++) mpd[i] = coastpath[i]->mpdist;
|
|
coastpath.push_back(cw.c);
|
|
// printf("setdists\n");
|
|
for(int i=1; i<size(coastpath) - 1; i++) {
|
|
if(coastpath[i-1]->land == laNone) {
|
|
raiseBuggyGeneration(cwt.c, "landNone 3");
|
|
for(int i=0; i<10; i++) printf("%d ", mpd[i]); printf("\n");
|
|
for(int i=0; i<size(coastpath); i++) coastpath[i]->item = itPirate;
|
|
return;
|
|
}
|
|
setdist(coastpath[i], BARLEV, coastpath[i-1]);
|
|
setdist(coastpath[i], BARLEV-1, coastpath[i-1]);
|
|
}
|
|
//printf("building barrier\n");
|
|
cell *c2 = coastpath[coastpath.size() - 1];
|
|
int bd = 2 + (hrand(2)) * 3;
|
|
|
|
bool nowall = false;
|
|
|
|
if(c2->land == laNone) {
|
|
raiseBuggyGeneration(c2, "landNone 2");
|
|
for(int i=0; i<size(coastpath); i++) coastpath[i]->item = itPirate;
|
|
return;
|
|
}
|
|
|
|
// else if(c->type == 7 && hrand(10000) < 20 && !isCrossroads(c->land) && gold() >= 200)
|
|
if(c2->type == 7 && gold() >= 200 && hrand(10) < 2 && buildBarrierNowall(c2, laCrossroads4, true)) {
|
|
nowall = true;
|
|
// raiseBuggyGeneration(c2, "check");
|
|
// return;
|
|
}
|
|
else buildBarrier(c2, bd);
|
|
//printf("building barrier II\n");
|
|
if(c2->bardir != NODIR && c2->bardir != NOBARRIERS)
|
|
extendBarrier(c2);
|
|
|
|
for(int i=size(coastpath)-(nowall?1:2); i>=0; i--) {
|
|
for(int j=BARLEV; j>=6; j--)
|
|
setdist(coastpath[i], j, NULL);
|
|
}
|
|
|
|
generatingEquidistant = false;
|
|
}
|
|
|
|
bool bugtrack = false, bugfound = false;
|
|
|
|
#define UNKNOWN 65535
|
|
|
|
int coastval(cell *c, eLand base) {
|
|
if(!c) return UNKNOWN;
|
|
if(c->land == laNone) return UNKNOWN;
|
|
if(base == laGraveyard) {
|
|
if(c->land == laHaunted || c->land == laHauntedWall)
|
|
return 0;
|
|
if(c->land != laGraveyard && c->land != laHauntedBorder) return 30;
|
|
}
|
|
else {
|
|
if(c->land == laOceanWall || c->land == laCaribbean || c->land == laWhirlpool ||
|
|
c->land == laLivefjord || c->land == laGridSea)
|
|
return 30;
|
|
if(c->land != laOcean && !isGravityLand(c->land) && c->land != laHaunted) {
|
|
return 0;
|
|
}
|
|
}
|
|
if(!c->landparam) return UNKNOWN;
|
|
return c->landparam;
|
|
}
|
|
|
|
eMonster crossroadsMonster() {
|
|
|
|
static eMonster weak[9] = {
|
|
moYeti, moGoblin, moRanger, moOrangeDog, moRunDog, moMonkey, moZombie,
|
|
moDesertman, moCultist
|
|
};
|
|
|
|
if(hrand(10) == 0) return weak[hrand(9)];
|
|
|
|
static eMonster m[24] = {
|
|
moWorm, moTentacle,
|
|
moTroll, moEagle,
|
|
moLesser, moGreater, moPyroCultist, moGhost,
|
|
moFireFairy, moIvyRoot, moHedge,
|
|
moLancer, moFlailer, moVineBeast,
|
|
moBomberbird, moAlbatross, moRedTroll,
|
|
moWaterElemental, moAirElemental, moFireElemental,
|
|
moFatGuard, moMiner, moPalace, moVizier
|
|
};
|
|
return m[hrand(24)];
|
|
}
|
|
|
|
eMonster wanderingCrossroadsMonster() {
|
|
while(true) {
|
|
eMonster m = crossroadsMonster();
|
|
if(!isIvy(m) && m != moTentacle) return m;
|
|
}
|
|
}
|
|
|
|
bool checkInTree(cell *c, int maxv) {
|
|
if(c->landparam <= 3) return false;
|
|
if(!maxv) return false;
|
|
if(c->landflags) return true;
|
|
for(int t=0; t<c->type; t++)
|
|
if(c->mov[t] && c->mov[t]->landparam < c->landparam && checkInTree(c->mov[t], maxv-1))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void buildEquidistant(cell *c) {
|
|
if(!c) return;
|
|
if(c->landparam) return;
|
|
if(chaosmode) return;
|
|
eLand b = c->land;
|
|
if(!b) {
|
|
printf("land missing at %p\n", c);
|
|
describeCell(c);
|
|
for(int i=0; i<c->type; i++) if(c->mov[i])
|
|
describeCell(c->mov[i]);
|
|
// buggycells.push_back(c);
|
|
}
|
|
if(b == laHauntedBorder) b = laGraveyard;
|
|
int mcv = UNKNOWN;
|
|
|
|
// find the lowest coastval
|
|
for(int i=0; i<c->type; i++) {
|
|
int cv = coastval(createMov(c,i), b);
|
|
if(cv < mcv) mcv = cv;
|
|
}
|
|
|
|
int mcv2 = 0;
|
|
|
|
if(bugfound) { mcv = 1; c->landparam = 10; }
|
|
|
|
else if(mcv == 0) {
|
|
// if(generatingEquidistant) printf("mcv=0\n");
|
|
c->landparam = 1;
|
|
}
|
|
else {
|
|
// if it appears twice, increase it
|
|
int qcv = 0;
|
|
int sid = 0;
|
|
for(int i=0; i<c->type; 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;
|
|
else {
|
|
// if(qcv != 1) { printf("qcv = %d\n", qcv); exit(1); }
|
|
cell *c2 = c->mov[sid];
|
|
int bsid = c->spn[sid];
|
|
for(int j=0; j<7; j++) {
|
|
int q = (bsid+j+42) % c2->type;
|
|
cell *c3 = c2->mov[q];
|
|
if(coastval(c3, b) < mcv) {
|
|
cell *c4 = createMovR(c2, bsid+1);
|
|
if(c4->land == laNone && c2->mpdist <= BARLEV) setdist(c4, BARLEV, c2);
|
|
buildEquidistant(c4);
|
|
mcv2 = coastval(c4, b);
|
|
break;
|
|
}
|
|
q = (bsid-j+42) % c2->type;
|
|
c3 = c2->mov[q];
|
|
if(coastval(c3, b) < mcv) {
|
|
cell *c4 = createMovR(c2, bsid-1);
|
|
if(c4->land == laNone && c2->mpdist <= BARLEV) setdist(c4, BARLEV, c2);
|
|
buildEquidistant(c4);
|
|
mcv2 = coastval(c4, b);
|
|
break;
|
|
}
|
|
}
|
|
if(bugfound) c->item = itSapphire;
|
|
if(mcv2 > mcv) mcv2 = mcv;
|
|
if(mcv2 == 0) mcv2 = mcv;
|
|
c->landparam = mcv2+1;
|
|
|
|
/* if(c->heat < 3)
|
|
raiseBuggyGeneration(c, "low heat"); */
|
|
|
|
}
|
|
}
|
|
|
|
if(!c->landparam) {
|
|
// int z = int(c->heat);
|
|
if(c->item || c->monst)
|
|
printf("building coast over %s/%s, mpdist = %d\n", iinf[c->item].name, minf[c->monst].name,
|
|
c->mpdist);
|
|
if(c->land == laOcean) c->wall = waSea;
|
|
}
|
|
|
|
if(c->land == laEndorian) {
|
|
if(c->landparam == 1 && c->type == 7) {
|
|
for(int i=0; i<7; i++) {
|
|
int i1 = (i+1) % 7;
|
|
if(c->mov[i] && c->mov[i]->land != laEndorian && c->mov[i]->land != laNone)
|
|
if(c->mov[i1] && c->mov[i1]->land != laEndorian && c->mov[i1]->land != laNone) {
|
|
c->landflags = 2;
|
|
c->wall = waTrunk;
|
|
}
|
|
}
|
|
}
|
|
else if(c->landparam == 1 && c->type == 6) {
|
|
for(int i=0; i<6; i++) {
|
|
int i1 = (i+1) % 6;
|
|
int i2 = (i+2) % 6;
|
|
int i4 = (i+4) % 6;
|
|
int i5 = (i+5) % 6;
|
|
if(c->mov[i] && c->mov[i]->land == laBarrier && c->mov[i]->type == 7)
|
|
if(c->mov[i1] && c->mov[i1]->land != laBarrier)
|
|
if(c->mov[i2] && c->mov[i2]->land != laBarrier)
|
|
if(c->mov[i4] && c->mov[i4]->land != laBarrier)
|
|
if(c->mov[i5] && c->mov[i5]->land != laBarrier) {
|
|
c->landflags = 2;
|
|
c->wall = waTrunk;
|
|
}
|
|
|
|
if(c->mov[i] && c->mov[i]->land != laEndorian && c->mov[i]->land != laNone && c->mov[i]->type == 7)
|
|
if(c->mov[i1] && c->mov[i1]->land != laEndorian && c->mov[i1]->land != laNone)
|
|
if(c->mov[i5] && c->mov[i5]->land != laEndorian && c->mov[i5]->land != laNone) {
|
|
c->landflags = 3;
|
|
c->wall = waTrunk;
|
|
}
|
|
}
|
|
}
|
|
else if(c->landparam > 1) {
|
|
for(int i=0; i<c->type; i++) {
|
|
cell *c2 = c->mov[i];
|
|
if(c2 && c2->landparam < c->landparam && c2->landflags) {
|
|
bool ok = false;
|
|
if(c2->landflags == 3)
|
|
ok = true;
|
|
else if(c2->landflags == 2) {
|
|
ok = c->mov[(i+1)%c->type]->landparam != c->landparam-1
|
|
&& c->mov[(i+c->type-1)%c->type]->landparam != c->landparam-1;
|
|
}
|
|
else for(int j=0; j<c2->type; j++) {
|
|
cell *c3 = c2->mov[j];
|
|
if(c3 && c3->landparam < c2->landparam && c3->landflags)
|
|
if(c->spn[i] == (j+3)%c2->type || c->spn[i] == (j+c2->type-3)%c2->type)
|
|
ok = true;
|
|
}
|
|
if(ok) {
|
|
c->wall = waTrunk;
|
|
c->landflags = 1;
|
|
}
|
|
}
|
|
if(c2 && c2->landparam < c->landparam && c2->landflags == 1 && c->type == 7) {
|
|
cell *c3 = c->mov[(i+1)%7];
|
|
if(c3 && c3->landparam < c->landparam && c3->landflags == 1) {
|
|
c->wall = waTrunk;
|
|
c->landflags = 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!c->landflags && checkInTree(c, 5)) {
|
|
int lev = hrand(100);
|
|
if(lev < 10) c->wall = waSolidBranch;
|
|
else if(lev < 20) c->wall = waWeakBranch;
|
|
else c->wall = waCanopy;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(c->landparam > 30 && b == laOcean && !generatingEquidistant && hrand(10) < 5)
|
|
buildAnotherEquidistant(c);
|
|
|
|
if(c->landparam > HAUNTED_RADIUS+5 && b == laGraveyard && !generatingEquidistant && hrand(100) < (purehepta?25:5) && items[itBone] >= 10)
|
|
buildAnotherEquidistant(c);
|
|
}
|
|
|
|
void beCIsland(cell *c) {
|
|
int i = hrand(3);
|
|
if(i == 0) c->wall = waCIsland;
|
|
if(i == 1) c->wall = waCIsland2;
|
|
if(i == 2) c->wall = waCTree;
|
|
return;
|
|
}
|
|
|
|
void generateTreasureIsland(cell *c) {
|
|
if(!euclid) generateAlts(c->master);
|
|
if(isOnCIsland(c)) return;
|
|
|
|
bool src = hrand(100) < 10;
|
|
if(src) {
|
|
beCIsland(c);
|
|
if(c->wall == waCTree) return;
|
|
}
|
|
cell* ctab[7];
|
|
int qc = 0, qlo, qhi;
|
|
for(int i=0; i<c->type; i++) {
|
|
cell *c2 = createMov(c, i);
|
|
if(!euclid) generateAlts(c2->master);
|
|
if((euclid || (c->master->alt && c2->master->alt)) && celldistAlt(c2) < celldistAlt(c)) {
|
|
ctab[qc++] = c2;
|
|
qlo = i; qhi = i;
|
|
while(true) {
|
|
qlo--;
|
|
c2 = createMovR(c, qlo);
|
|
if(!euclid && !c2->master->alt) break;
|
|
if(celldistAlt(c2) >= celldistAlt(c)) break;
|
|
ctab[qc++] = c2;
|
|
}
|
|
while(true) {
|
|
qhi++;
|
|
c2 = createMovR(c, qhi);
|
|
if(!euclid && !c2->master->alt) break;
|
|
if(celldistAlt(c2) >= celldistAlt(c)) break;
|
|
ctab[qc++] = c2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(!qc) {
|
|
printf("NO QC\n"); c->wall = waSea;
|
|
for(int i=0; i<c->type; i++) printf("%d ", celldistAlt(c->mov[i]));
|
|
printf("vs %d\n", celldistAlt(c));
|
|
return;
|
|
}
|
|
cell* c2 = createMovR(c, (qlo+qhi)/2);
|
|
generateTreasureIsland(c2);
|
|
if(!src) {
|
|
c->wall = c2->wall;
|
|
if(c->wall != waCTree && hrand(100) < 15)
|
|
c->wall = (c->wall == waCIsland ? waCIsland2 : waCIsland);
|
|
}
|
|
if(src && c2->wall == waCTree && (euclid||c->master->alt) && celldistAlt(c) <= -10) {
|
|
bool end = true;
|
|
for(int i=0; i<qc; i++) {
|
|
generateTreasureIsland(ctab[i]);
|
|
if(ctab[i]->wall != waCTree)
|
|
end = false;
|
|
}
|
|
// printf("%p: end=%d, qc=%d, dist=%d\n", c, end, qc, celldistAlt(c));
|
|
if(end) c->item = itPirate;
|
|
else c->item = itCompass;
|
|
}
|
|
}
|
|
|
|
void buildRedWall(cell *c, int gemchance) {
|
|
if(c->monst) return;
|
|
int ki = PT(kills[moHexSnake] + kills[moRedTroll], 50);
|
|
c->wall = waRed3;
|
|
if(hrand(100+ki) < gemchance + ki)
|
|
c->item = itRedGem;
|
|
if(items[itRedGem] >= 10 && hrand(18000) < gemchance)
|
|
c->item = itOrbTelekinesis;
|
|
}
|
|
|
|
int palaceHP() {
|
|
if(tactic::on && isCrossroads(firstland))
|
|
return 4;
|
|
if(items[itPalace] < 3) // 1+2
|
|
return 2;
|
|
else if(items[itPalace] < 10) // 1+2+3+4
|
|
return 3;
|
|
else if(items[itPalace] < 21) // 1+2+3+4+5+6
|
|
return 4;
|
|
else if(items[itPalace] < 35)
|
|
return 5;
|
|
else if(items[itPalace] < 50)
|
|
return 6;
|
|
else return 7;
|
|
}
|
|
|
|
cell *randomDown(cell *c) {
|
|
cell *tab[7];
|
|
int q=0;
|
|
for(int i=0; i<c->type; i++)
|
|
if(c->mov[i] && coastval(c->mov[i], laIvoryTower) < coastval(c, laIvoryTower))
|
|
tab[q++] = c->mov[i];
|
|
if(!q) return NULL;
|
|
if(q==1) return tab[0];
|
|
return tab[hrand(q)];
|
|
}
|
|
|
|
// which=1 => right, which=-1 => left
|
|
// set which=1,bonus=1 to get right neighbor on level
|
|
|
|
typedef int cellfunction(cell*);
|
|
|
|
cell *chosenDown(cell *c, int which, int bonus, cellfunction* cf) {
|
|
int d = (*cf)(c)-1;
|
|
for(int i=0; i<c->type; i++) {
|
|
if(!c->mov[i]) createMov(c, i);
|
|
if(c->mov[i]->mpdist > BARLEV && cf == coastvalEdge) setdist(c->mov[i], BARLEV, c);
|
|
|
|
if((*cf)(c->mov[i]) == d) {
|
|
int i2 = (i+which+42)%c->type;
|
|
createMov(c, i2);
|
|
if((*cf)(c->mov[i2]) == d)
|
|
return createMovR(c, i2+bonus);
|
|
else return createMovR(c, i+bonus);
|
|
}
|
|
}
|
|
// printf("nothing down here\n");
|
|
return NULL;
|
|
}
|
|
|
|
int edgeDepth(cell *c) {
|
|
if(c->land == laIvoryTower || c->land == laEndorian) return coastvalEdge(c);
|
|
else if(c->land != laBarrier) {
|
|
for(int i=0; i<c->type; i++) if(c->mov[i] && c->mov[i]->land == laBarrier)
|
|
return -20+c->cpdist;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int getHauntedDepth(cell *c) {
|
|
if((tactic::on || euclid) && c->land == laHaunted) return celldist(c);
|
|
if(c->land == laHaunted) return c->landparam;
|
|
if(c->land == laHauntedWall) return 0;
|
|
if(c->land == laHauntedBorder || c->land == laGraveyard) return -c->landparam;
|
|
return -100;
|
|
}
|
|
|
|
int compassDist(cell *c) {
|
|
if(c->master->alt) return celldistAlt(c);
|
|
if(isHaunted(c->land) || c->land == laGraveyard) return getHauntedDepth(c);
|
|
return 500;
|
|
}
|
|
|
|
int towerval(cell *c, cellfunction* cf) {
|
|
// if(c->land != laEdge) return 0;
|
|
cell *cp1 = chosenDown(c, 1, 1, cf);
|
|
if(!cp1) return 0;
|
|
/* cell *cp2 = chosenDown(cp1, 1, 1);
|
|
if(!cp2) return 0;
|
|
cell *cm1 = chosenDown(c, -1, -1);
|
|
if(!cm1) return 0;
|
|
cell *cm2 = chosenDown(cm1, -1, -1);
|
|
if(!cm2) return 0; */
|
|
|
|
/* return
|
|
(c->type-6)
|
|
+ 2*(cp1->type-6) + 4*(cp2->type-6)
|
|
+ 8*(cm1->type-6) +16*(cm2->type-6); */
|
|
|
|
int under = 0;
|
|
int cfc = (*cf)(c);
|
|
for(int i=0; i<c->type; i++) {
|
|
if(c->mov[i] && (*cf)(c->mov[i]) < cfc)
|
|
under++;
|
|
}
|
|
return (c->type-6) + 2*(cp1->type-6) + 4*under;
|
|
}
|
|
|
|
int hivehard() {
|
|
return items[itRoyalJelly] + yendor::hardness() * (yendor::hardness() * 3/5 - 2);
|
|
// 0, 5, 40, 135
|
|
}
|
|
|
|
eMonster randomHyperbug() {
|
|
int h = hivehard();
|
|
if(hrand(200) < h)
|
|
return moBug2;
|
|
return eMonster(moBug0 + hrand(BUGCOLORS));
|
|
// 50: 25/25/50
|
|
// 100:
|
|
}
|
|
|
|
#define RANDPATC(c) (randpattern(c,randompattern[c->land]))
|
|
#define RANDPAT (randpattern(c,randompattern[c->land]))
|
|
#define RANDPAT3(i) (randpatternMajority(c,i,RANDITER))
|
|
#define RANDPATV(x) (randpattern(c,randompattern[x]))
|
|
|
|
bool buildBarrierNowall(cell *c, eLand l2, bool force) {
|
|
|
|
int dtab[3] = {0,1,6};
|
|
|
|
if(c->land == laNone) {
|
|
raiseBuggyGeneration(c, "barrier nowall!");
|
|
return false;
|
|
}
|
|
|
|
for(int i=0; i<3; i++) {
|
|
int d = dtab[i];
|
|
if(force) d=1;
|
|
cellwalker cw(c, d);
|
|
|
|
if(force || (checkBarriersNowall(cw, 0, -1) && checkBarriersNowall(cw, 0, 1))) {
|
|
eLand l1 = c->land;
|
|
checkBarriersNowall(cw, 0, -1, l1, l2);
|
|
checkBarriersNowall(cw, 0, 1, l1, l2);
|
|
extendBarrier(c);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void setLandEuclid(cell *c) {
|
|
c->land = euclidland;
|
|
if(euclidland == laCrossroads) {
|
|
eucoord x, y;
|
|
decodeMaster(c->master, x, y);
|
|
c->land = getEuclidLand(y+2*x);
|
|
}
|
|
if(euclidland == laCrossroads4) {
|
|
eucoord x, y;
|
|
decodeMaster(c->master, x, y);
|
|
c->land = getEuclidLand(y);
|
|
}
|
|
if(euclidland == laWhirlpool) {
|
|
c->land = laOcean;
|
|
c->landparam = 99;
|
|
}
|
|
if(euclidland == laPrincessQuest)
|
|
c->land = laPalace;
|
|
if(euclidland == laOcean) {
|
|
eucoord x, y;
|
|
decodeMaster(c->master, x, y);
|
|
int y0 = y; if(y>50000) y0 -= 65536; y0 += 10;
|
|
if(y0 == 0)
|
|
{c->land = laBarrier; if(ishept(c)) c->land = laRlyeh; }
|
|
else if(y0<0) c->land = laRlyeh;
|
|
else c->landparam = y0;
|
|
}
|
|
if(euclidland == laIvoryTower) {
|
|
eucoord x, y;
|
|
decodeMaster(c->master, x, y);
|
|
int y0 = y; if(y>50000) y0 -= 65536; y0 = -y0; y0 -= 5;
|
|
if(y0 == 0)
|
|
{c->land = laBarrier; if(ishept(c)) c->land = laAlchemist; }
|
|
else if(y0<0) c->land = laAlchemist;
|
|
else {
|
|
c->landparam = y0;
|
|
}
|
|
}
|
|
if(euclidland == laElementalWall) {
|
|
eucoord x, y;
|
|
decodeMaster(c->master, x, y);
|
|
int y0 = y; if(y>32768) y0 -= 65536;
|
|
int x0 = x +y/2;
|
|
|
|
int id = 0;
|
|
if(y0&16) id += 2;
|
|
if(x0&16) id += 1;
|
|
|
|
x0 += 8; y0 += 8;
|
|
|
|
y0--; x0++;
|
|
int id2 = 0;
|
|
if(y0&16) id2 += 2;
|
|
if(x0&16) id2 += 1;
|
|
|
|
c->land = eLand(laEFire + id);
|
|
|
|
if(((y0&15) == 15 && (x0&1)) || ((x0&15) == 0 && ((y0+1)&1))) {
|
|
if(c->land == laEFire) c->wall = waEternalFire;
|
|
if(c->land == laEWater) c->wall = waSea;
|
|
if(c->land == laEAir) c->wall = waChasm;
|
|
if(c->land == laEEarth) c->wall = waStone;
|
|
c->barright = c->land;
|
|
c->barleft = eLand(laEFire+id2);
|
|
c->land = laElementalWall;
|
|
}
|
|
}
|
|
if(euclidland == laCrossroads3) {
|
|
eucoord x, y;
|
|
decodeMaster(c->master, x, y);
|
|
int y0 = y; if(y>32768) y0 -= 65536;
|
|
int x0 = x +y/2;
|
|
|
|
x0 += 24; y0 += 8;
|
|
|
|
int id = 0;
|
|
if(y0&16) id ^= 1;
|
|
if(x0&16) id ^= 1;
|
|
|
|
c->land = id ? laCrossroads3 : laDesert;
|
|
|
|
if(((y0&15) == 15 && (x0&1)) || ((x0&15) == 0 && ((y0+1)&1))) {
|
|
c->wall = waBarrier;
|
|
c->land = laBarrier;
|
|
}
|
|
}
|
|
if(euclidland == laGridCoast) {
|
|
eucoord x, y;
|
|
decodeMaster(c->master, x, y);
|
|
|
|
int zz = 2*short(x)+short(y) + 10;
|
|
zz %= 30; if(zz<0) zz += 30;
|
|
if(zz >= 15)
|
|
c->land = laGridSea;
|
|
else
|
|
c->land = laGridCoast;
|
|
}
|
|
}
|
|
|
|
void buildBigStuff(cell *c, cell *from) {
|
|
bool deepOcean = false;
|
|
|
|
if(c->land == laOcean) {
|
|
if(!from) deepOcean = true;
|
|
else for(int i=0; i<from->type; i++) {
|
|
cell *c2 = from->mov[i];
|
|
if(c2 && c2->landparam > 30)
|
|
deepOcean = true;
|
|
}
|
|
}
|
|
|
|
if(c->land == laGraveyard) {
|
|
if(!from) deepOcean = true;
|
|
else for(int i=0; i<from->type; i++) {
|
|
cell *c2 = from->mov[i];
|
|
if(c2 && c2->landparam > HAUNTED_RADIUS+5)
|
|
deepOcean = true;
|
|
}
|
|
}
|
|
|
|
if(generatingEquidistant) deepOcean = false;
|
|
|
|
// buildgreatwalls
|
|
|
|
if(chaosmode) {
|
|
if(c->type == 7 && hrand(10000) < 9000 && buildBarrierNowall(c, getNewLand(c->land)))
|
|
;
|
|
}
|
|
|
|
else if(c->type == 7 && isWarped(c->land) && hrand(10000) < 3000 &&
|
|
buildBarrierNowall(c, eLand(c->land ^ laGridSea ^ laGridCoast))) ;
|
|
|
|
else if(c->type == 7 && c->land == laCrossroads4 && hrand(10000) < 7000 &&
|
|
buildBarrierNowall(c, getNewLand(laCrossroads4))) ;
|
|
|
|
else if(c->type == 7 && hrand(10000) < 20 && !generatingEquidistant && !yendor::on && !tactic::on && !isCrossroads(c->land) && gold() >= 200 &&
|
|
!isSealand(c->land) && !isHaunted(c->land) && !isGravityLand(c->land) &&
|
|
c->land != laTortoise &&
|
|
!(c->land == laGraveyard && !deepOcean)
|
|
)
|
|
buildBarrierNowall(c, laCrossroads4) ;
|
|
|
|
else if(c->land == laCrossroads2 && !purehepta)
|
|
buildCrossroads2(c);
|
|
|
|
else if(c->type == 7 && hrand(10000) < (
|
|
showoff ? (cwt.c->mpdist > 7 ? 0 : 10000) :
|
|
isGravityLand(c->land) ? 0 :
|
|
generatingEquidistant ? 0 :
|
|
(yendor::on && yendor::nexttostart) ? 10000 :
|
|
princess::challenge ? 0 :
|
|
isElemental(c->land) ? 4000 :
|
|
(yendor::on && (yendor::generating || !(yendor::clev().flags & YF_WALLS))) ? 0 :
|
|
c->land == laCrossroads3 ? 10000 :
|
|
c->land == laCrossroads ? 5000 :
|
|
c->land == laCrossroads2 ? 10000 :
|
|
c->land == laCrossroads4 ? 0 :
|
|
(tactic::on && !tactic::trailer) ? 0 :
|
|
c->land == laCaribbean ? 500 :
|
|
(c->land == laGridSea || c->land == laGridCoast) ? 500 :
|
|
c->land == laStorms ? 250 :
|
|
c->land == laCanvas ? 0 :
|
|
c->land == laHaunted ? 0 :
|
|
(c->land == laGraveyard && !deepOcean) ? 0 :
|
|
(c->land == laGraveyard && items[itBone] >= 10) ? 120 :
|
|
c->land == laOcean ? (deepOcean ? (purehepta ? 250 : 2000) : 0) :
|
|
c->land == laDragon ? 120 :
|
|
50))
|
|
{
|
|
|
|
int bd = 2 + hrand(2) * 3;
|
|
|
|
buildBarrier(c, bd);
|
|
|
|
/* int bd = 2;
|
|
buildBarrier4(c, bd, 0, getNewLand(c->land), c->land); */
|
|
}
|
|
|
|
if((!chaosmode) && bearsCamelot(c->land) && c->type == 7 && hrand(2000) < 200 && items[itEmerald] >= 5 && !tactic::on) {
|
|
int rtr = newRoundTableRadius();
|
|
heptagon *alt = createAlternateMap(c, rtr+14, hsOrigin);
|
|
if(alt) {
|
|
alt->emeraldval = rtr;
|
|
alt->fiftyval = c->land;
|
|
}
|
|
}
|
|
|
|
if(!chaosmode) {
|
|
|
|
// buildbigstuff
|
|
|
|
if(c->land == laRlyeh && c->type == 7 && hrand(2000) < 100 &&
|
|
items[itStatue] >= 5 && !randomPatternsMode &&
|
|
!tactic::on && !yendor::on)
|
|
createAlternateMap(c, 2, hsA);
|
|
|
|
if(c->land == laOvergrown && c->type == 7 && hrand(2000) < 25 &&
|
|
!randomPatternsMode && items[itMutant] >= 5 &&
|
|
!tactic::on && !yendor::on) {
|
|
heptagon *h = createAlternateMap(c, 2, hsA);
|
|
if(h) clearing::bpdata[h].root = NULL;
|
|
}
|
|
|
|
if(c->land == laStorms && c->type == 7 && hrand(2000) < 1000 && !randomPatternsMode) {
|
|
heptagon *h = createAlternateMap(c, 2, hsA);
|
|
if(h) h->alt->emeraldval = hrand(2);
|
|
}
|
|
|
|
if(c->land == laOcean && c->type == 7 && hrand(2000) < (purehepta ? 500 : 1000) && deepOcean && !tactic::on && !yendor::on && !generatingEquidistant)
|
|
createAlternateMap(c, 2, hsA);
|
|
|
|
if(c->land == laCaribbean && c->type == 7)
|
|
createAlternateMap(c, 2, hsA);
|
|
|
|
if(c->land == laPalace && c->type == 7 && !princess::generating && !shmup::on &&
|
|
(princess::forceMouse ? (from && from->pathdist != INF) : (hrand(2000) < 20)) &&
|
|
!c->master->alt &&
|
|
(princess::challenge || kills[moVizier]) && !tactic::on && !yendor::on)
|
|
createAlternateMap(c, 141, hsOrigin, waPalace);
|
|
}
|
|
|
|
if(c->bardir != NODIR && c->bardir != NOBARRIERS)
|
|
extendBarrier(c);
|
|
}
|
|
|
|
void buildIvoryTower(cell *c) {
|
|
/* if(int(c->landparam) % 5 == 0)
|
|
c->wall = waCamelot;
|
|
*/
|
|
|
|
if(euclid) {
|
|
eucoord x, y;
|
|
decodeMaster(c->master, x, y);
|
|
string tab[] = {
|
|
".####...",
|
|
"L...L...",
|
|
".L..L...",
|
|
"L...L...",
|
|
"........",
|
|
"........"
|
|
};
|
|
int y0 = y; if(y>32768) y0 -= 65536;
|
|
|
|
y0 += 5; y0 %= 12; if(y0<0) y0+=12;
|
|
|
|
if(y0 >= 6) { y0 -= 6; x += 4; }
|
|
|
|
char ch = tab[y0][(x+(y+1)/2)&7];
|
|
|
|
if(ch == '#')
|
|
c->wall = waPlatform;
|
|
else if(ch == 'L')
|
|
c->wall = waLadder;
|
|
}
|
|
|
|
else if(true) {
|
|
|
|
cell *c2 = c;
|
|
cell *c3 = c;
|
|
|
|
bool rdepths[5];
|
|
for(int i=0; i<5; i++) {
|
|
if(coastvalEdge(c2) == 0) {
|
|
rdepths[i] = false;
|
|
}
|
|
else {
|
|
cell *c4 = c2;
|
|
if(c2 != c3 && !isNeighbor(c2, c3)) {
|
|
for(int i=0; i<c2->type; i++) if(c2->mov[i] && isNeighbor(c2->mov[i], c3))
|
|
c4 = c2->mov[i];
|
|
}
|
|
rdepths[i] = c2 && c3 && c4 && (c2->landflags == 3 || c3->landflags == 3 || c4->landflags == 3);
|
|
c2 = chosenDown(c2, 1, 0); // if(!c2) break;
|
|
c3 = chosenDown(c3, -1, 0);
|
|
if(!c2) { raiseBuggyGeneration(c, "ivory c2"); return; }
|
|
if(!c3) { raiseBuggyGeneration(c, "ivory c3"); return; }
|
|
}
|
|
}
|
|
|
|
if(rdepths[3]) {
|
|
c->wall = waPlatform;
|
|
// if(!c4->item) c4->item = itPalace;
|
|
}
|
|
else if(!rdepths[2] && !rdepths[4] && !rdepths[1]) {
|
|
c2 = c;
|
|
c3 = c;
|
|
cell *c4 = chosenDown(c, 1, 1);
|
|
cell *c5 = chosenDown(c, -1, -1);
|
|
for(int i=0; i<3; i++) {
|
|
if(coastvalEdge(c2) == 0) break;
|
|
if(c2 && c4 && c4->landflags == 3 && c2->landflags != 3 && c4 == chosenDown(c2, 1, 1))
|
|
c->wall = waLadder;
|
|
if(c3 && c5 && c5->landflags == 3 && c3->landflags != 3 && c5 == chosenDown(c3, -1, -1))
|
|
c->wall = waLadder;
|
|
buildEquidistant(c4); buildEquidistant(c5);
|
|
if(c2) c2 = chosenDown(c2, 1, 0);
|
|
if(c3) c3 = chosenDown(c3, -1, 0);
|
|
if(c4) c4 = chosenDown(c4, 1, 0);
|
|
if(c5) c5 = chosenDown(c5, -1, 0);
|
|
}
|
|
}
|
|
}
|
|
else c->wall = waCIsland;
|
|
}
|
|
|
|
// This function generates all lands. Warning: it's very long!
|
|
void setdist(cell *c, int d, cell *from) {
|
|
|
|
if(signed(c->mpdist) <= d) return;
|
|
if(c->mpdist > d+1 && d != BARLEV) setdist(c, d+1, from);
|
|
c->mpdist = d;
|
|
|
|
if(d <= 3) lastexplore = shmup::on ? shmup::curtime : turncount;
|
|
|
|
if(buggyGeneration) {
|
|
if(d < BARLEV) for(int i=0; i<c->type; i++) {
|
|
setdist(createMov(c, i), d+(purehepta?2:1), c);
|
|
}
|
|
if(d >= BARLEV) c->item = itBuggy2;
|
|
return;
|
|
}
|
|
|
|
if(d >= BARLEV) {
|
|
|
|
if(!c->land && from->land != laElementalWall && from->land != laHauntedWall) c->land = from->land;
|
|
if(c->land == laTemple && !tactic::on && !chaosmode) c->land = laRlyeh;
|
|
if(c->land == laClearing && !tactic::on) c->land = laOvergrown;
|
|
if(c->land == laWhirlpool && !tactic::on && !yendor::on) c->land = laOcean;
|
|
if(c->land == laCamelot && !tactic::on) c->land = laCrossroads;
|
|
|
|
if(euclid) setLandEuclid(c);
|
|
|
|
// if(chaosmode) c->land = getCLand(c);
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
if(d == BARLEV && c->land == laCanvas) {
|
|
c->landparam = mapeditor::generateCanvas(c);
|
|
}
|
|
#endif
|
|
|
|
int hard = yendor::hardness();
|
|
|
|
if(d == BARLEV && !euclid && c != cwt.c)
|
|
buildBigStuff(c, from);
|
|
|
|
if(d < 10) {
|
|
explore[d]++;
|
|
exploreland[d][c->land]++;
|
|
|
|
if(d < BARLEV) for(int i=0; i<c->type; i++) {
|
|
setdist(createMov(c, i), d+(purehepta && d>=3 && d<7?2:1), c);
|
|
if(buggyGeneration) return;
|
|
}
|
|
|
|
if(d == BARLEV-2 && c->land == laOcean)
|
|
buildEquidistant(c);
|
|
|
|
if(d == BARLEV-2 && (c->land == laGraveyard || c->land == laHauntedBorder || c->land == laHaunted))
|
|
buildEquidistant(c);
|
|
|
|
if(d <= 7 && (c->land == laGraveyard || c->land == laHauntedBorder)) {
|
|
c->land = (c->landparam >= 1 && c->landparam <= HAUNTED_RADIUS) ? laHauntedBorder : laGraveyard;
|
|
}
|
|
|
|
if(d == 8 && isGravityLand(c->land)) {
|
|
buildEquidistant(c);
|
|
}
|
|
|
|
if(d == 8 && c->land == laIvoryTower && !euclid) {
|
|
|
|
if(hrand(1000) < 75 && // chosenDown(c, 1, 0)->landflags != 3 && chosenDown(c,-1,0)->landflags != 3 &&
|
|
(c->landparam&1) ) {
|
|
c->landflags = 3;
|
|
}
|
|
else c->landflags = 0;
|
|
}
|
|
|
|
if(d == 7 && c->land == laIvoryTower) buildIvoryTower(c);
|
|
|
|
if(d == 9 && c->land == laPalace) {
|
|
if(cdist50(c) == 3 && polarb50(c) == 1)
|
|
c->wall = waPalace;
|
|
}
|
|
|
|
if(d == 8 && c->land == laPalace) {
|
|
|
|
// note: Princess Challenge brings back the normal Palace generation
|
|
bool lookingForPrincess = !euclid && c->master->alt && !princess::challenge;
|
|
|
|
bool pgate = false;
|
|
if(purehepta) {
|
|
int i = fiftyval049(c);
|
|
if(i >= 8 && i <= 14 && !polarb50(c)) pgate = true;
|
|
}
|
|
|
|
if(pgate) {
|
|
switch(princess::generating ? 0 : hrand(2)) {
|
|
case 0:
|
|
c->wall = waClosedGate;
|
|
break;
|
|
case 1:
|
|
c->wall = waOpenGate;
|
|
break;
|
|
}
|
|
for(int s=0; s<7; s++) if(c->mov[s] &&
|
|
(c->mov[s]->wall == waClosedGate || c->mov[s]->wall == waOpenGate))
|
|
c->wall = c->mov[s]->wall;
|
|
}
|
|
else if(cdist50(c) == 3 && polarb50(c) == 1) {
|
|
int q = 0, s = 0;
|
|
if(!ishept(c)) for(int i=0; i<6; i++)
|
|
if(cdist50(c->mov[i]) == 3 && polarb50(c->mov[i]) == 1 && !ishept(c->mov[i]))
|
|
q++, s += i;
|
|
if(q == 1 && c->mov[s]->land == laPalace) {
|
|
switch(princess::generating ? 0 : hrand(2)) {
|
|
case 0:
|
|
c->wall = waClosedGate;
|
|
c->mov[s]->wall = waClosedGate;
|
|
break;
|
|
case 1:
|
|
c->wall = waOpenGate;
|
|
c->mov[s]->wall = waOpenGate;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if((hrand(100) < (lookingForPrincess ? (purehepta ? 11 : 7) : 5) && cdist50(c)) ||
|
|
(cdist50(c) == 0 && polarb50(c) && hrand(100) < 60)) {
|
|
c->wall = hrand(100) < (lookingForPrincess ? (purehepta ? 25 : 30):50) ? waClosePlate : waOpenPlate;
|
|
}
|
|
else if(hrand(100) < (lookingForPrincess ? 3 : 5))
|
|
c->wall = waTrapdoor;
|
|
|
|
if(cdist50(c) == 0 && yendor::path) {
|
|
cell *c2 = c->mov[hrand(c->type)];
|
|
if(c2->wall == waNone) c2->wall = waTrapdoor;
|
|
}
|
|
}
|
|
|
|
if(d==8 && c->land == laEmerald) {
|
|
if(randomPatternsMode)
|
|
c->wall = RANDPAT3(0) ? waCavewall : waCavefloor;
|
|
else if(euclid) {
|
|
eucoord x, y;
|
|
decodeMaster(c->master, x, y);
|
|
if(((y-2)&7) < 4) c->wall = waCavewall;
|
|
else c->wall = waCavefloor;
|
|
}
|
|
else if(purehepta) {
|
|
if(polarb50(c))
|
|
c->wall = waCavewall;
|
|
else c->wall = waCavefloor;
|
|
}
|
|
else {
|
|
int v = emeraldval(c);
|
|
if((v&3) >= 2)
|
|
c->wall = waCavewall;
|
|
else c->wall = waCavefloor;
|
|
}
|
|
}
|
|
|
|
if(d == 8 && isIcyLand(c)) c->landparam = 0;
|
|
if(d == 8 && c->land == laDryForest) c->landparam = 0;
|
|
|
|
if(d==8 && c->land == laPower) {
|
|
int v;
|
|
if(randomPatternsMode)
|
|
v = RANDPAT ? 24 : 0;
|
|
else if(euclid) {
|
|
eucoord x, y;
|
|
decodeMaster(c->master, x, y);
|
|
int y0 = ((short)y) % 6;
|
|
if(y0<0) y0+=6;
|
|
if(y0 == 3 || y0 == 4) v=24; else v=0;
|
|
}
|
|
else v = emeraldval(c);
|
|
v &= ~3;
|
|
if((v == 24 || v == 32 || v == 56))
|
|
c->wall = waEternalFire;
|
|
else if(hrand(100) < 10) {
|
|
c->wall = waGlass;
|
|
eItem protectedItems[18] = {
|
|
itPower, itPower, itPower, itPower, itPower, itPower,
|
|
itOrbLightning, itOrbLightning, itOrbThorns, itOrbThorns,
|
|
itOrbInvis, itOrbInvis,
|
|
itOrbShield, itOrbTeleport, itOrbPsi,
|
|
itOrbDragon, itOrbIllusion, itOrbPreserve
|
|
};
|
|
c->item = protectedItems[hrand(18)];
|
|
}
|
|
}
|
|
|
|
if(d==8 && c->land == laZebra) {
|
|
if(euclid) {
|
|
eucoord x, y;
|
|
decodeMaster(c->master, x, y);
|
|
if(y&1) c->wall = waTrapdoor;
|
|
else c->wall = waNone;
|
|
}
|
|
else
|
|
c->wall = (randomPatternsMode ? RANDPAT : (zebra40(c)&2)) ? waTrapdoor : waNone;
|
|
}
|
|
|
|
if(d==8 && isElemental(c->land)) {
|
|
if(hrand(c->land == laEAir ? 6 : 25) == 0) {
|
|
if(c->land == laEFire) c->wall = waEternalFire;
|
|
else if(c->land == laEWater) c->wall = waSea;
|
|
else if(c->land == laEAir) c->wall = waChasm;
|
|
else if(c->land == laEEarth) c->wall = waStone;
|
|
}
|
|
}
|
|
|
|
if(d==8 && c->land == laWineyard) {
|
|
if(euclid) {
|
|
eucoord x, y;
|
|
decodeMaster(c->master, x, y);
|
|
int dy = ((short)y)%3; if(dy<0) dy += 3;
|
|
if(dy == 1) c->wall = waVinePlant;
|
|
}
|
|
else {
|
|
int v = emeraldval(c);
|
|
int w = v / 4;
|
|
if(randomPatternsMode) c->wall = RANDPAT ? waVinePlant : waNone;
|
|
else if(w == 9 || w == 10 || w == 7 || w == 8) {
|
|
c->wall = waVinePlant;
|
|
}
|
|
else if(v == 24 || v == 58 || v == 26 || v == 56)
|
|
c->wall = waVineHalfA;
|
|
else if(v == 25 || v == 59 || v == 27 || v == 57)
|
|
c->wall = waVineHalfB;
|
|
else c->wall = waNone;
|
|
}
|
|
}
|
|
|
|
if(d == 7 && cellHalfvine(c)) {
|
|
int i = -1;
|
|
for(int k=0; k<c->type; k++) if(c->mov[k] && c->mov[k]->wall == c->wall)
|
|
i = 0;
|
|
if(i == -1) c->wall = waNone;
|
|
}
|
|
|
|
// 24-58
|
|
// 26-56
|
|
|
|
if(d == 9) {
|
|
|
|
// mapgen9
|
|
|
|
if(isWarped(c->land) && randomPatternsMode)
|
|
c->land = RANDPAT ? laGridCoast : laGridSea;
|
|
|
|
// if(c->land == laIce && ((celldist(c) % 10) + 10) % 10 == 5)
|
|
// c->wall = waColumn;
|
|
|
|
if(c->land == laIce) {
|
|
if(randomPatternsMode) c->wall = RANDPAT ? waIcewall : waNone;
|
|
else if(hrand(100) < 5 && c->wall != waBarrier) {
|
|
c->wall = waIcewall;
|
|
for(int i=0; i<c->type; i++) if(hrand(100) < 50) {
|
|
cell *c2 = createMov(c, i);
|
|
setdist(c2, d+1, c);
|
|
if(c2->wall == waBarrier || c2->land != laIce) continue;
|
|
c2->wall = waIcewall;
|
|
for(int j=0; j<c2->type; j++) if(hrand(100) < 20) {
|
|
cell *c3 = createMov(c2, j);
|
|
setdist(c->mov[i], d+2, c);
|
|
if(c3->wall == waBarrier || c3->land != laIce) continue;
|
|
c3->wall = waIcewall;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(c->land == laIce || c->land == laCocytus) if(c->wall == waIcewall && items[itDiamond] >= 5 && hrand(200) == 1)
|
|
c->wall = waBonfireOff;
|
|
|
|
if(c->land == laDragon) {
|
|
int cd = getCdata(c, 3);
|
|
cd &= 31;
|
|
if(cd >= 6 && cd<12) c->wall = waChasm;
|
|
else if(hrand(1000) == 0) c->wall = waEternalFire;
|
|
}
|
|
|
|
if(c->land == laCaves)
|
|
c->wall = (randomPatternsMode ? RANDPAT3(1) : hrand(100) < 55) ? waCavewall : waCavefloor;
|
|
|
|
if(c->land == laLivefjord) {
|
|
int die = (randomPatternsMode ? (RANDPAT3(2)?100:0) : hrand(100));
|
|
if(die < 50)
|
|
c->wall = waSea;
|
|
else
|
|
c->wall = waNone;
|
|
}
|
|
|
|
if(c->land == laDeadCaves) {
|
|
int die = (randomPatternsMode ? (RANDPAT?100:0) : hrand(100));
|
|
if(die<50) c->wall = waDeadwall;
|
|
else if(die<55) c->wall = waDeadfloor2;
|
|
else c->wall = waDeadfloor;
|
|
}
|
|
|
|
if(c->land == laAlchemist)
|
|
c->wall = (randomPatternsMode ? RANDPAT : hrand(2)) ? waFloorA : waFloorB;
|
|
|
|
if(c->land == laDryForest) {
|
|
if(randomPatternsMode)
|
|
c->wall = RANDPAT ? waNone : RANDPATV(laHell) ? waBigTree : waSmallTree;
|
|
else
|
|
c->wall =
|
|
(hrand(100) < 50) ? (hrand(100) < 50 ? waBigTree : waSmallTree) : waNone;
|
|
}
|
|
|
|
if(c->land == laOvergrown) {
|
|
// 0: 60%
|
|
// 10: 50%
|
|
if(randomPatternsMode)
|
|
c->wall = RANDPAT ? waNone : RANDPATV(laWineyard) ? waBigTree : waSmallTree;
|
|
else
|
|
c->wall =
|
|
(hrand(50+items[itMutant]/2+hard) < 30) ? (hrand(100) < 50 ? waBigTree : waSmallTree) : waNone;
|
|
}
|
|
|
|
if(c->land == laWildWest) {
|
|
if(randomPatternsMode)
|
|
c->wall = RANDPAT ? waNone : waSaloon;
|
|
else if(cdist50(c) <= 2) c->wall = waSaloon;
|
|
|
|
/*if(i == 3 && polarb50(c) == 1) {
|
|
for(int j=0; j<c->type; j++) if(c->mov[j] && polarb50(c->mov[j]) == 0)
|
|
c->wall = waFloorA;
|
|
} */
|
|
}
|
|
|
|
if(c->land == laWhirlwind) {
|
|
if(!euclid && zebra3(c) == 0) c->wall = waFan;
|
|
else if(pseudohept(c) && hrand(2000) < 150)
|
|
c->wall = waChasm;
|
|
else if(d == 7 && hrand(1000) == 0)
|
|
buildPrizeMirror(c);
|
|
else
|
|
whirlwind::switchTreasure(c);
|
|
|
|
/* bool edge = false;
|
|
for(int i=0; i<c->type; i++)
|
|
if(zebra3(c->mov[i]) != z)
|
|
edge = true; */
|
|
// if(edge && hrand(100) < 5)
|
|
// c->wall = waSaloon;
|
|
}
|
|
|
|
if(c->land == laStorms) {
|
|
|
|
if(euclid) {
|
|
eucoord x, y;
|
|
decodeMaster(c->master, x, y);
|
|
if(short(x+1)%3 == 0 && short(y)%3 == 0) {
|
|
if(hrand(100) < 50)
|
|
c->wall = hrand(2) ? waCharged : waGrounded;
|
|
}
|
|
else if(ishept(c)) {
|
|
bool sand = false;
|
|
for(int i=0; i<c->type; i++) {
|
|
createMov(c, i);
|
|
decodeMaster(c->mov[i]->master, x, y);
|
|
if(short(x+1)%3 == 0 && short(y)%3 == 0) sand = true;
|
|
}
|
|
if(sand && hrand(100) < 20)
|
|
c->wall = waSandstone;
|
|
}
|
|
}
|
|
else if(purehepta) {
|
|
int i = zebra40(c);
|
|
if((i == 5 || i == 8) && hrand(100) < 20) c->wall = hrand(2) ? waCharged : waGrounded;
|
|
else if(i == 15) c->wall = waSandstone;
|
|
}
|
|
else {
|
|
int i = zebra40(c);
|
|
if(i >= 40 && hrand(100) < 50)
|
|
c->wall = hrand(2) ? waCharged : waGrounded;
|
|
else if(ishept(c) && hrand(100) < 20)
|
|
c->wall = waSandstone;
|
|
}
|
|
|
|
for(int i=0; i<c->type; i++)
|
|
if(c->mov[i] && c->mov[i]->land != laStorms && c->mov[i]->land != laNone)
|
|
c->wall = waNone;
|
|
}
|
|
|
|
if(c->land == laGraveyard) {
|
|
if(randomPatternsMode)
|
|
c->wall = RANDPAT ? ((RANDPATV(laCrossroads) || RANDPATV(laCrossroads2)) ? waAncientGrave : waFreshGrave) : waNone;
|
|
else if(pseudohept(c))
|
|
c->wall = hrand(5) ? waAncientGrave : waFreshGrave;
|
|
}
|
|
|
|
if(c->land == laRlyeh) {
|
|
if(randomPatternsMode) {
|
|
c->wall = RANDPAT ? waColumn : waNone;
|
|
}
|
|
else {
|
|
if(hrand(500) < 5) {
|
|
for(int i=0; i<c->type; i++) {
|
|
cell *c2 = createMov(c, i);
|
|
setdist(c2, d+1, c);
|
|
if(c2 && c2->land == laRlyeh)
|
|
c2->wall = waColumn;
|
|
}
|
|
|
|
for(int j=0; j<2; j++) {
|
|
int i = hrand(c->type);
|
|
if(c->mov[i] && c->mov[i]->land == laRlyeh)
|
|
c->mov[i]->wall = waNone;
|
|
}
|
|
}
|
|
if(pseudohept(c) && hrand(2)) c->wall = waColumn;
|
|
}
|
|
}
|
|
|
|
if(c->land == laHell) {
|
|
if(hrand(1000) < 36 && celldist(c) >= 3) {
|
|
for(int i=0; i<c->type; i++) {
|
|
cell *c2 = createMov(c, i);
|
|
setdist(c2, d+1, c);
|
|
if(c2 && c2->land == laHell)
|
|
if(c2->wall != waSulphurC)
|
|
c2->wall = waSulphur;
|
|
}
|
|
|
|
c->wall = waSulphurC;
|
|
}
|
|
}
|
|
|
|
if(c->land == laCocytus) {
|
|
if(c->wall == waNone) c->wall = waFrozenLake;
|
|
if(hrand(100) < 5 && !safety && celldist(c) >= 3) {
|
|
for(int i=0; i<c->type; i++) {
|
|
cell *c2 = createMov(c, i);
|
|
setdist(c2, d+1, c);
|
|
if(c2 && c2->land == laCocytus)
|
|
c2->wall = waLake;
|
|
}
|
|
|
|
c->wall = waLake;
|
|
|
|
if(hrand(500) < 100 + 2 * (items[itSapphire] + hard))
|
|
c->monst = moShark;
|
|
}
|
|
}
|
|
|
|
if(isHive(c->land) && hrand(2000) < (chaosmode ? 1000 : purehepta?200:2) && !safety)
|
|
hive::createBugArmy(c);
|
|
|
|
// landbigstuff
|
|
|
|
if(c->land == laPalace && !euclid && c->master->alt) {
|
|
int d = celldistAlt(c);
|
|
if(d <= 150) generateAlts(c->master);
|
|
}
|
|
|
|
if((bearsCamelot(c->land) && !euclid) || c->land == laCamelot)
|
|
if(euclid || c->master->alt) {
|
|
int d = celldistAltRelative(c);
|
|
if(tactic::on || (d <= 14 && roundTableRadius(c) > 20)) {
|
|
if(!euclid) generateAlts(c->master);
|
|
c->bardir = NOBARRIERS;
|
|
if(d == 10) {
|
|
if(pseudohept(c)) buildCamelotWall(c);
|
|
else {
|
|
if(!euclid) for(int i=0; i<7; i++) generateAlts(c->master->move[i]);
|
|
int q = 0;
|
|
for(int t=0; t<6; t++) {
|
|
createMov(c, t);
|
|
if(celldistAltRelative(c->mov[t]) == 10 && !pseudohept(c->mov[t])) q++;
|
|
}
|
|
if(q == 1) buildCamelotWall(c);
|
|
// towers of Camelot
|
|
if(q == 0 && !purehepta) {
|
|
c->monst = moKnight;
|
|
for(int i=0; i<6; i++)
|
|
buildCamelotWall(c->mov[i]);
|
|
for(int i=0; i<c->type; i++) if(celldistAltRelative(c->mov[i]) < d)
|
|
c->mondir = i;
|
|
}
|
|
}
|
|
}
|
|
if(d == 0) c->wall = waRoundTable;
|
|
if(celldistAlt(c) == 0 && !tactic::on) c->item = itHolyGrail;
|
|
if(d < 0 && hrand(7000) <= 10 + items[itHolyGrail] * 5) {
|
|
eMonster m[3] = { moHedge, moLancer, moFlailer };
|
|
c->monst = m[hrand(3)];
|
|
}
|
|
if(d == 1) {
|
|
// roughly as many knights as table cells
|
|
if(hrand(purehepta ? 2618 : 1720) < 1000)
|
|
c->monst = moKnight;
|
|
if(!euclid) for(int i=0; i<7; i++) generateAlts(c->master->move[i]);
|
|
for(int i=0; i<c->type; i++)
|
|
if(c->mov[i] && celldistAltRelative(c->mov[i]) < d)
|
|
c->mondir = (i+3) % 6;
|
|
}
|
|
if(tactic::on && d >= 2 && d <= 8 && hrand(1000) < 10)
|
|
c->item = itOrbSafety;
|
|
if(d == 5 && tactic::on)
|
|
c->item = itGreenStone;
|
|
if(d <= 10) c->land = laCamelot;
|
|
if(d > 10 && !euclid && !tactic::on) {
|
|
c->land = eLand(c->master->alt->alt->fiftyval);
|
|
if(c->land == laNone) printf("Camelot\n"); // NONEDEBUG
|
|
}
|
|
}
|
|
}
|
|
|
|
if((c->land == laStorms && !euclid)) {
|
|
if(c->master->alt && c->master->alt->distance <= 2) {
|
|
generateAlts(c->master);
|
|
c->bardir = NOBARRIERS;
|
|
int d = celldistAlt(c);
|
|
if(d <= -2) {
|
|
c->wall = (c->master->alt->alt->emeraldval & 1) ? waCharged : waGrounded;
|
|
c->item = itNone;
|
|
c->monst = moNone;
|
|
}
|
|
else if(d <= -1)
|
|
c->wall = (hrand(100) < 20) ? waSandstone : waNone;
|
|
else if(d <= 0)
|
|
c->wall = waNone;
|
|
}
|
|
}
|
|
|
|
if(chaosmode && c->land == laTemple) {
|
|
for(int i=0; i<c->type; i++)
|
|
if(pseudohept(c) && c->mov[i] && c->mov[i]->land != laTemple)
|
|
c->wall = waColumn;
|
|
}
|
|
|
|
else if((c->land == laRlyeh && !euclid) || c->land == laTemple) {
|
|
if(euclid || (c->master->alt && (tactic::on || c->master->alt->distance <= 2))) {
|
|
if(!euclid && !chaosmode) generateAlts(c->master);
|
|
c->bardir = NOBARRIERS;
|
|
int d = celldistAlt(c);
|
|
if(d <= 0) {
|
|
c->land = laTemple, c->wall = waNone, c->monst = moNone, c->item = itNone;
|
|
}
|
|
if(d % TEMPLE_EACH==0) {
|
|
if(pseudohept(c))
|
|
c->wall = waColumn;
|
|
else {
|
|
if(!euclid) for(int i=0; i<7; i++) generateAlts(c->master->move[i]);
|
|
int q = 0;
|
|
for(int t=0; t<6; t++) {
|
|
createMov(c, t);
|
|
if(celldistAlt(c->mov[t]) % TEMPLE_EACH == 0 && !ishept(c->mov[t])) q++;
|
|
}
|
|
if(q == 2) c->wall = waColumn;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if((c->land == laOvergrown && !euclid) || c->land == laClearing) {
|
|
if(euclid || (c->master->alt && (tactic::on || c->master->alt->distance <= 2))) {
|
|
if(!euclid) generateAlts(c->master);
|
|
c->bardir = NOBARRIERS;
|
|
int d = celldistAlt(c);
|
|
if(d <= 0) {
|
|
c->land = laClearing, c->wall = waNone; // , c->monst = moNone, c->item = itNone;
|
|
}
|
|
else if(d == 1 && !tactic::on)
|
|
c->wall = waSmallTree, c->monst = moNone, c->item = itNone;
|
|
}
|
|
}
|
|
|
|
if(c->land == laOcean || c->land == laWhirlpool) {
|
|
bool fullwhirlpool = false;
|
|
if(tactic::on && tactic::lasttactic == laWhirlpool)
|
|
fullwhirlpool = true;
|
|
if(yendor::on && yendor::clev().l == laWhirlpool)
|
|
fullwhirlpool = true;
|
|
if(euclid || (c->master->alt && (fullwhirlpool || c->master->alt->distance <= 2))) {
|
|
if(!euclid) generateAlts(c->master);
|
|
c->bardir = NOBARRIERS;
|
|
int dd = celldistAlt(c);
|
|
if(dd <= 0 || fullwhirlpool) {
|
|
c->land = laWhirlpool, c->wall = waSea, c->monst = moNone, c->item = itNone;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(c->land == laCaribbean) {
|
|
if(!euclid) {
|
|
if(c->master->alt && c->master->alt->distance <= 2) {
|
|
if(!euclid) generateAlts(c->master);
|
|
c->bardir = NOBARRIERS;
|
|
int d = celldistAlt(c);
|
|
if(d <= 0)
|
|
// c->wall = waChasm;
|
|
generateTreasureIsland(c);
|
|
else
|
|
c->wall = waSea;
|
|
}
|
|
else c->wall = waSea;
|
|
}
|
|
else {
|
|
int d = celldistAlt(c);
|
|
if(d <= 0)
|
|
generateTreasureIsland(c);
|
|
else
|
|
c->wall = waSea;
|
|
}
|
|
}
|
|
|
|
if(c->land == laTortoise) {
|
|
using namespace tortoise;
|
|
int f = hrand(30);
|
|
if(f < 21 && ((getBits(c)>>f) & 1)) {
|
|
tflag tf = tflag(f);
|
|
// long neck and saddleback shell help with big trees
|
|
#define PSH(x,y) if(tf == x || tf == y) tf = pseudohept(c) ? x : y;
|
|
PSH(tfLongNeck, tfShell)
|
|
if(tf == tfLongNeck) c->wall = waBigTree;
|
|
if(tf == tfShell) c->wall = waBigTree;
|
|
// brown body elements help with (small) trees
|
|
PSH(tfShellHue, tfScuteHue)
|
|
if(tf == tfShellHue) c->wall = waSmallTree;
|
|
if(tf == tfScuteHue) c->wall = waSmallTree;
|
|
if(tf == tfSkinHue) c->wall = waSmallTree;
|
|
// long legs help with rocks
|
|
PSH(tfFront, tfRear)
|
|
if(tf == tfFront) c->wall = waStone;
|
|
if(tf == tfRear) c->wall = waStone;
|
|
// broad tail and desaturated shell helps with water
|
|
PSH(tfTail, tfShellSat)
|
|
if(tf == tfTail) c->wall = waSea;
|
|
if(tf == tfShellSat) c->wall = waSea;
|
|
#undef PSH
|
|
}
|
|
}
|
|
|
|
if(isHive(c->land) && hrand(2000) < 100 && !c->wall && !c->item && !c->monst) {
|
|
int nww = 0;
|
|
for(int i=0; i<c->type; i++) if(c->mov[i] && c->mov[i]->wall == waWaxWall)
|
|
nww++;
|
|
if(nww == 0) {
|
|
c->wall = waWaxWall;
|
|
c->monst = moNone;
|
|
c->landparam = hrand(0xFFFFFF + 1);
|
|
}
|
|
/* for(int i=0; i<c->type; i++) {
|
|
if(hrand(6) < 5) {
|
|
createMov(c,i);
|
|
cell *c2 = c->mov[i];
|
|
c2->wall = waWaxWall;
|
|
c2->monst = moNone;
|
|
}
|
|
} */
|
|
}
|
|
|
|
if(c->land == laDesert) {
|
|
if(randomPatternsMode)
|
|
c->wall = RANDPAT ? waDune : waNone;
|
|
else {
|
|
if(hrand(100) < 5) {
|
|
for(int i=0; i<c->type; i++) {
|
|
cell *c2 = createMov(c, i);
|
|
setdist(c2, d+1, c);
|
|
if(c2 && c2->land == laDesert)
|
|
c2->wall = waDune;
|
|
}
|
|
|
|
for(int j=0; j<2; j++) {
|
|
int i = hrand(c->type);
|
|
if(c->mov[i] && c->mov[i]->land == laDesert)
|
|
c->mov[i]->wall = waNone;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(hrand(300) == 1 && items[itSpice] >= 5) c->wall = waThumperOff;
|
|
}
|
|
|
|
if(c->land == laRedRock) {
|
|
if(randomPatternsMode) {
|
|
c->wall = waNone;
|
|
if(!ishept(c)) { if(RANDPAT) buildRedWall(c, 20); }
|
|
else {
|
|
int k = 0;
|
|
for(int i=0; i<20; i++)
|
|
if(RANDPATC(c->mov[i%7]) && !RANDPATC(c->mov[(i+1)%7]))
|
|
k++;
|
|
if(k>=4) buildRedWall(c, 20);
|
|
}
|
|
}
|
|
else if(ishept(c) && hrand(100) < 8 && !c->monst) {
|
|
buildRedWall(c, 80);
|
|
int i = hrand(7);
|
|
buildRedWall(createMovR(c, i), 33);
|
|
if(hrand(2) == 0)
|
|
buildRedWall(createMovR(createMovR(c, i), c->spn[i]+(hrand(2)?2:4)), 20);
|
|
i += 3 + hrand(2);
|
|
if(hrand(6) < 4)
|
|
buildRedWall(createMovR(c, i), 33);
|
|
if(hrand(2) == 0)
|
|
buildRedWall(createMovR(createMovR(c, i), c->spn[i]+(hrand(2)?2:4)), 20);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(d == 8) {
|
|
if(c->land == laGridCoast) {
|
|
if(hrand(1000) < 150 && celldist(c) >= 3 && !pseudohept(c))
|
|
c->wall = waSmallTree;
|
|
int q = 0;
|
|
if(!purehepta && !chaosmode) for(int i=0; i<c->type; i++)
|
|
if(c->mov[i] && !isWarped(c->mov[i]->land) && c->mov[i]->land != laCrossroads4)
|
|
q++;
|
|
if(q == 1) c->wall = waWarpGate;
|
|
}
|
|
|
|
if(c->land == laGridSea) {
|
|
c->wall = waSea;
|
|
int q = 0;
|
|
if(!purehepta && !chaosmode) for(int i=0; i<c->type; i++)
|
|
if(c->mov[i] && !isWarped(c->mov[i]->land)) q++;
|
|
if(q == 1) c->wall = waWarpGate;
|
|
}
|
|
}
|
|
|
|
if(d == 8 && c->land == laOvergrown) {
|
|
if(hrand(15000) < 20 + (2 * items[itMutant] + hard) && !safety) {
|
|
c->item = itMutant;
|
|
c->landparam = items[itMutant] + 5 + hrand(11);
|
|
c->wall = waNone;
|
|
for(int i=0; i<c->type; i++)
|
|
if(c->mov[i] && (c->mov[i]->wall == waBigTree || c->mov[i]->wall == waSmallTree))
|
|
c->mov[i]->wall = waNone;
|
|
}
|
|
else if(hrand(15000) < 20 + (2 * items[itMutant] + hard) && !safety) {
|
|
// for the Yendor Challenge, use only Mutants
|
|
if(!(yendor::on && yendor::clev().l == laMirror)) {
|
|
c->monst = moForestTroll;
|
|
c->wall = waNone;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(d == 7 && c->item == itMutant) {
|
|
if(c->monst != moMutant && isMultitile(c->monst))
|
|
c->item = itNone;
|
|
else {
|
|
c->stuntime = mutantphase;
|
|
c->monst = moMutant;
|
|
c->mondir = NODIR;
|
|
}
|
|
}
|
|
|
|
if(d == 8 && c->land == laCaribbean && !euclid) {
|
|
int mindist = 9;
|
|
for(int i=0; i<c->type; i++) {
|
|
cell *c2 = c->mov[i];
|
|
if((euclid || c2->master->alt) && celldistAlt(c2) < mindist)
|
|
mindist = celldistAlt(c2);
|
|
}
|
|
if(mindist == 0) beCIsland(c);
|
|
}
|
|
|
|
if(d == 7 && isHaunted(c->land)) {
|
|
c->wall = waNone;
|
|
|
|
c->item = itNone; c->monst = moNone;
|
|
|
|
if(hrand(100) < 25)
|
|
c->wall = hrand(2) ? waBigTree : waSmallTree;
|
|
|
|
else if(hrand(500) < 2 && ishept(c))
|
|
c->wall = hrand(10) < 3 ? waFreshGrave : waAncientGrave;
|
|
|
|
else {
|
|
if(hrand(5000) < 30)
|
|
c->item = itGreenStone;
|
|
|
|
if(hrand(4000) < 10 + items[itLotus] && !safety)
|
|
c->monst = moGhost;
|
|
|
|
int depth = getHauntedDepth(c);
|
|
|
|
if(hrand(500 + depth) < depth - items[itLotus] && !safety)
|
|
c->item = itLotus;
|
|
}
|
|
}
|
|
|
|
if(d == 7 && c->land == laRedRock && c->wall == waNone && hrand(1000) == 0)
|
|
buildPrizeMirror(c);
|
|
|
|
if(d == 8 && c->land == laRose && hrand(2000) < 100 && !c->wall && !c->item && !c->monst) {
|
|
int nww = 0;
|
|
for(int i=0; i<c->type; i++) {
|
|
cell *c2 = c->mov[i];
|
|
if(c2 && c2->wall == waRose) nww++;
|
|
for(int j=0; j<c2->type; j++) {
|
|
cell *c3 = c2->mov[j];
|
|
// note: c3->land is required for Android --
|
|
// not strictly equivalent since another land there might be not yet generated
|
|
if(c3 && c3->land != laRose && c3->land) nww++;
|
|
}
|
|
};
|
|
|
|
if(nww == 0) {
|
|
c->wall = waRose;
|
|
c->monst = moNone;
|
|
}
|
|
}
|
|
|
|
if(d == 7 && c->land == laCaribbean && c->wall == waSea) {
|
|
bool coast = false;
|
|
for(int i=0; i<c->type; i++) {
|
|
cell *c2 = c->mov[i];
|
|
if(passable(c2, c, 0) && c2->wall != waBoat) coast = true;
|
|
}
|
|
if(coast && hrand(10) < 5) {
|
|
c->wall = waBoat;
|
|
if(items[itPirate] >= 10 && hrand(100) < 2 && !safety)
|
|
c->item = itOrbPreserve;
|
|
else if(hrand(100) < 2*PRIZEMUL && !safety)
|
|
placePrizeOrb(c);
|
|
}
|
|
}
|
|
|
|
if(d == 7 && c->land == laCaves && c->wall == waCavewall && hrand(5000) < items[itGold] + hard && !safety)
|
|
c->monst = moSeep;
|
|
|
|
if(d == 7 && c->land == laLivefjord && c->wall == waSea && hrand(5000) < 15 + items[itFjord] + hard && !safety) {
|
|
if(items[itFjord] >= 5 && hrand(100) < 20)
|
|
c->monst = moWaterElemental;
|
|
else {
|
|
c->monst = moViking;
|
|
c->wall = waBoat;
|
|
}
|
|
}
|
|
|
|
else if(d == 7 && c->land == laLivefjord && yendor::path && c->wall == waSea && hrand(5000) < 20) {
|
|
c->monst = moViking;
|
|
c->wall = waBoat;
|
|
}
|
|
|
|
if(d == 7 && c->land == laLivefjord && hrand(2000) < PT(50 + kills[moViking], 100) && notDippingFor(itFjord) && !safety) {
|
|
c->item = itFjord;
|
|
}
|
|
|
|
if(d == 7 && c->land == laLivefjord && items[itFjord] >= 10 && hrand(2000) < 2)
|
|
c->item = itOrbFish;
|
|
|
|
if(d == 7 && c->land == laLivefjord && hrand(2000) < 2*PRIZEMUL)
|
|
placePrizeOrb(c);
|
|
|
|
if(d == 7 && c->land == laLivefjord && hrand(2000) < 2)
|
|
buildPrizeMirror(c);
|
|
|
|
if(d == 7 && c->land == laEmerald && c->wall == waCavewall && hrand(5000) < items[itEmerald] + hard && !safety)
|
|
c->monst = moSeep;
|
|
|
|
if(d == 7 && c->land == laDeadCaves && c->wall == waDeadwall && hrand(1000) < items[itSilver] + hard && !safety)
|
|
c->monst = moSeep;
|
|
|
|
if(d == 7 && c->wall == waVinePlant && hrand(100) < (randomPatternsMode ? 2 : 10))
|
|
c->monst = moVineSpirit;
|
|
|
|
if(d == 7 && c->land == laOcean && !safety) {
|
|
if((c->landparam >= 1 && c->landparam <= 25) || chaosmode) {
|
|
if(hrand(1000) < 5)
|
|
c->wall = waBoat;
|
|
if(hrand(1000) < PT(50 + kills[moAlbatross]/2, 150))
|
|
c->item = itCoast;
|
|
if(hrand(15000) < 10 + 2 * items[itCoast] + 2 * hard)
|
|
c->monst = moAlbatross;
|
|
if(items[itCoast] >= 10 && hrand(10000) < 5)
|
|
c->item = itOrbAir;
|
|
else if(items[itCoast] >= 10 && hrand(10000) < 6)
|
|
c->item = itOrbEmpathy;
|
|
if(hrand(10000) < 5*PRIZEMUL)
|
|
placePrizeOrb(c);
|
|
if(hrand(10000) < 5)
|
|
buildPrizeMirror(c);
|
|
}
|
|
else if(c->landparam > 25) {
|
|
int amberbonus = items[itCoast] - 50;
|
|
// 50 => 10
|
|
if(hrand(15000) < 10 + amberbonus + 2 * hard)
|
|
c->monst = moAlbatross;
|
|
if(hrand(30000) < 20 + 2 * hard) {
|
|
c->wall = waBoat;
|
|
c->monst = moPirate;
|
|
// orbs are possible!
|
|
placeOceanOrbs(c);
|
|
}
|
|
else if(hrand(30000) < 10 + 2 * hard)
|
|
c->monst = moCShark;
|
|
}
|
|
}
|
|
|
|
if(d == 7 && c->land == laGridSea && c->wall == waSea && !safety) {
|
|
if(hrand(12000) < 30 + 2 * items[itCoral] + hard) {
|
|
c->wall = waBoat;
|
|
c->monst = moRatling;
|
|
}
|
|
else if(hrand(6000) < PT(120 + min(kills[moRatling], 250), 240) && notDippingFor(itCoral)) {
|
|
c->wall = waBoat;
|
|
c->item = itCoral;
|
|
}
|
|
}
|
|
|
|
if(d == 7 && c->land == laCaribbean && c->wall == waSea && hrand(10000) < 20 + items[itPirate] + 2 * hard && !safety)
|
|
c->monst = moCShark;
|
|
|
|
if(d == 7 && c->wall == waCTree && hrand(5000) < 100 + items[itPirate] + hard)
|
|
c->monst = moParrot;
|
|
|
|
// repair the buggy walls flowing in from another land, like ice walls flowing into the Caves
|
|
if(d == 7 && c->land == laCaves && c->wall != waCavewall && c->wall != waCavefloor)
|
|
c->wall = waCavefloor;
|
|
|
|
if(d == 7 && c->land == laDeadCaves && c->wall != waDeadwall &&
|
|
c->wall != waDeadfloor && c->wall != waDeadfloor2 && c->wall != waEarthD)
|
|
c->wall = waDeadfloor2;
|
|
|
|
if(d == 7 && c->land == laCocytus && c->wall != waFrozenLake && c->wall != waLake && c->wall != waIcewall)
|
|
c->wall = waFrozenLake;
|
|
|
|
if(d == 7 && c->land == laAlchemist && c->wall != waFloorA && c->wall != waFloorB)
|
|
c->wall = waFloorA;
|
|
|
|
if(d == 3 && c->land == laMinefield && safety && (c->wall == waMineMine || c->wall == waMineUnknown))
|
|
c->wall = waMineOpen;
|
|
|
|
if(c->land == laCamelot && tactic::on && d == 0) {
|
|
int r = roundTableRadius(c);
|
|
if(r > 28 + 3 * items[itHolyGrail])
|
|
items[itHolyGrail]++;
|
|
}
|
|
|
|
if(d == 7 && c->land == laMinefield) {
|
|
c->wall = waMineUnknown;
|
|
// 250: rare mines
|
|
// 1250: at 25
|
|
int minefreq = 0;
|
|
int treas = items[itBombEgg];
|
|
if(treas <= 10) minefreq = 250 + 30 * treas;
|
|
if(treas <= 110) minefreq = 550 + 10 * (treas-10);
|
|
else minefreq = 1550 + (treas - 110);
|
|
|
|
// experimentation says that 600 is slightly too hard to find the Orb of Yendor
|
|
if(yendor::generating || yendor::path)
|
|
if(minefreq < 550)
|
|
minefreq = 550;
|
|
|
|
int tfreq =
|
|
treas < 10 ? 50 + 5 * treas :
|
|
treas < 25 ? 100 + (treas-10) * 2:
|
|
treas < 50 ? 150 + (treas-25) :
|
|
175;
|
|
|
|
if(hrand(5000) < minefreq)
|
|
c->wall = waMineMine;
|
|
else if(hrand(5000) < tfreq && !safety) {
|
|
c->item = itBombEgg;
|
|
c->landparam = items[itBombEgg] + 5 + hrand(11);
|
|
}
|
|
else if(hrand(5000) < treas - 20 + yendor::hardness() && !safety)
|
|
c->monst = moBomberbird;
|
|
else if(treas >= 10 && hrand(5000) < 10 && !safety)
|
|
c->item = itOrbFriend;
|
|
else if(hrand(5000) < 10*PRIZEMUL && !safety)
|
|
placePrizeOrb(c);
|
|
}
|
|
|
|
if(d == 7 && c->wall == waIcewall && c->land != laIce && c->land != laCocytus)
|
|
c->wall = waNone;
|
|
|
|
if(d == 7 && c->wall == waRed3 && c->land != laRedRock)
|
|
c->wall = waNone;
|
|
|
|
if(d == 7 && c->item == itRedGem && c->land != laRedRock)
|
|
c->item = itNone;
|
|
|
|
if(d == 7 && c->item == itRose && c->land != laRose)
|
|
c->item = itNone;
|
|
|
|
if(d == 7 && c->wall == waChasmD) {
|
|
chasmify(c);
|
|
}
|
|
|
|
if(d == 7 && c->wall == waEarthD) {
|
|
chasmifyEarth(c);
|
|
}
|
|
|
|
if(d == 7 && c->wall == waElementalD) {
|
|
chasmifyElemental(c);
|
|
}
|
|
|
|
// seal entrances to the Land of Power.
|
|
if(d == 7 && c->land == laPower && c->type == 7) {
|
|
bool onwall = false;
|
|
for(int i=0; i<7; i++) if(c->mov[i] && c->mov[i]->land == laBarrier)
|
|
onwall = true;
|
|
if(!onwall) for(int i=0; i<7; i++) {
|
|
cell *c2 = c->mov[i];
|
|
if(!c2) continue;
|
|
cell *c3 = c2->mov[(c->spn[i] + 3) % 6];
|
|
if(c3->land != laPower && c3->land != laBarrier)
|
|
if(c2->wall != waFire && c2->wall != waGlass) {
|
|
if(isFire(c)) c->monst = moWitchWinter;
|
|
else if(c->wall == waGlass) c->monst = moWitchGhost;
|
|
else c->monst = moEvilGolem;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(d == 7 && passable(c, NULL, 0)) {
|
|
if(c->land == laBarrier) c->wall = waBarrier;
|
|
if(c->land == laOceanWall)
|
|
c->wall = c->type == 7 ? waBarrier : waSea;
|
|
}
|
|
|
|
if(d == 7 && c->land == laWhirlpool)
|
|
whirlpool::generate(c);
|
|
|
|
if(c->land == laPalace && princess::generating) {
|
|
// no Opening Plates nearby
|
|
if(d <= 7 && c->wall == waOpenPlate && !purehepta)
|
|
c->wall = waNone;
|
|
if(d <= 7 && c->wall == waClosePlate && purehepta)
|
|
c->wall = waOpenPlate;
|
|
// no monsters nearby
|
|
if(d>0) c->monst = moNone;
|
|
// no Plates or Trapdoors in the Princess cell
|
|
if(d < 3 && (c->wall == waClosePlate || c->wall == waOpenPlate || c->wall == waTrapdoor))
|
|
c->wall = waNone;
|
|
if(d > 1) c->item = itNone;
|
|
// the Princess herself
|
|
if(d == 0) {
|
|
c->monst = moPrincess;
|
|
c->hitpoints = palaceHP();
|
|
c->wall = waGiantRug;
|
|
cell *c2 = NULL;
|
|
for(int i=0; i<c->type; i++) {
|
|
cellwalker cw(c, i);
|
|
cwstep(cw);
|
|
cwspin(cw, 4);
|
|
cwstep(cw);
|
|
cwspin(cw, 2);
|
|
cwstep(cw);
|
|
cwspin(cw, 4);
|
|
cwstep(cw);
|
|
cwspin(cw, 2 + hrand(3));
|
|
cwstep(cw);
|
|
if(!c2) c2 = cw.c;
|
|
else if(celldist(cw.c) > celldist(c2)) c2 = cw.c;
|
|
cw.c->monst = moMouse;
|
|
}
|
|
c2->wall = waOpenPlate;
|
|
}
|
|
|
|
}
|
|
|
|
if(d == 7 && passable(c, NULL, 0) && !safety) {
|
|
|
|
if(c->land == laIvoryTower) {
|
|
if(hrand(20000) < 20 + items[itEdge] + hard) {
|
|
if(cellEdgeUnstable(c))
|
|
c->monst = moGargoyle;
|
|
else
|
|
c->monst = moEdgeMonkey;
|
|
}
|
|
else if(c->landparam >= 14 && hrand(2000) < PT(50+kills[moGargoyle]+kills[moEdgeMonkey], 150) && !cellEdgeUnstable(c) ) {
|
|
c->item = itEdge;
|
|
}
|
|
}
|
|
|
|
if(c->land == laEndorian) {
|
|
if(c->wall == waNone && coastval(c, laEndorian) >= 10 && hrand(5000) < 10 + 2 * (items[itApple] + hard))
|
|
c->monst = moKestrel;
|
|
else if(c->wall != waNone && hrand(5000) < 10 + 2 * (items[itApple] + hard))
|
|
c->monst = moLemur;
|
|
else if(c->wall == waCanopy && !checkInTree(c, 3) && hrand(5000) < PT(300 + 5 * (kills[moKestrel] + kills[moLemur]), 750))
|
|
c->item = itApple;
|
|
}
|
|
|
|
if(c->land == laIce) {
|
|
if(hrand(5000) < PT(100 + 2 * (kills[moYeti] + kills[moWolf]), 200) && notDippingFor(itDiamond))
|
|
c->item = itDiamond;
|
|
if(hrand(8000) < 2 * (items[itDiamond] + hard))
|
|
c->monst = hrand(2) ? moYeti : moWolf;
|
|
}
|
|
|
|
if(c->land == laTortoise) {
|
|
if(hrand(4000) < 50 + items[itBabyTortoise]*2 && !safety) {
|
|
c->monst = moTortoise;
|
|
c->hitpoints = 3;
|
|
}
|
|
if((tactic::on || euclid) && hrand(4000) < 50 + items[itBabyTortoise]*2 && !safety) {
|
|
c->item = itBabyTortoise;
|
|
tortoise::babymap[c] = getBits(c) ^ tortoise::getRandomBits();
|
|
}
|
|
}
|
|
|
|
if(c->land == laPalace) {
|
|
bool lookingForPrincess0 = !euclid && c->master->alt;
|
|
bool lookingForPrincess = lookingForPrincess0 && !princess::challenge;
|
|
int hardness = lookingForPrincess ? 5 : items[itPalace] + hard;
|
|
|
|
if(hrand(5000) < PT(100 + 2 * (kills[moPalace] + kills[moFatGuard] + kills[moVizier] + kills[moSkeleton]), 200) && notDippingFor(itPalace) &&
|
|
c->wall != waOpenGate && !lookingForPrincess0)
|
|
c->item = itPalace;
|
|
if(items[itPalace] >= 10 && hrand(5000) < 16 && c->wall != waOpenGate)
|
|
c->item = hrand(100) < 80 ? itOrbFrog : itOrbDiscord;
|
|
if(hrand(5000) < 20*PRIZEMUL && c->wall != waOpenGate)
|
|
placePrizeOrb(c);
|
|
if(hrand(5000) < 20 && c->wall == waNone)
|
|
buildPrizeMirror(c);
|
|
if(c->land == laPalace && (euclid || c->master->alt) && celldistAlt(c) <= 150 && !havemouse && !princess::generating &&
|
|
princess::getPrisonInfo(c) &&
|
|
(euclid || (princess::getPrisonInfo(c)->bestdist < 6 && princess::getPrisonInfo(c)->princess))) {
|
|
c->monst = moMouse;
|
|
addMessage(XLAT("You hear a distant squeak!"));
|
|
drawBigFlash(c);
|
|
/* {
|
|
cell *c2= c;
|
|
z:
|
|
c2->item = itPirate;
|
|
printf("AT %p\n", c2);
|
|
for(int i=0; i<c2->type; i++) if(c2->mov[i] && c2->mov[i]->mpdist < c2->mpdist) {
|
|
c2 = c2->mov[i];
|
|
goto z;
|
|
}
|
|
} */
|
|
havemouse = true;
|
|
}
|
|
else if(hrand(15000) < 10 + hardness) {
|
|
c->monst = moPalace;
|
|
c->hitpoints = palaceHP();
|
|
if(hrand(10 + items[itPalace] + yendor::hardness()) >= 14 && !lookingForPrincess)
|
|
c->monst = moSkeleton;
|
|
}
|
|
else if(hrand(20000) < hardness) {
|
|
c->monst = moFatGuard;
|
|
c->hitpoints = palaceHP();
|
|
}
|
|
else if(hrand(20000) < hardness - 7) {
|
|
c->monst = moVizier;
|
|
c->hitpoints = palaceHP();
|
|
}
|
|
else if(princess::forceVizier && from->pathdist != INF) {
|
|
c->monst = moVizier;
|
|
c->hitpoints = palaceHP();
|
|
princess::forceVizier = false;
|
|
}
|
|
}
|
|
if(c->land == laCaves) {
|
|
if(hrand(5000) < PT(100 + 2 * min(kills[moTroll] + kills[moGoblin], 150), 200) && notDippingFor(itGold))
|
|
c->item = itGold;
|
|
if(hrand(8000) < 10 + 2 * (items[itGold] + hard))
|
|
c->monst = hrand(2) ? moTroll : moGoblin;
|
|
}
|
|
if(c->land == laLivefjord) {
|
|
if(hrand(16000) < 10 + 2 * (items[itFjord] + hard) + (yendor::path ? 90:0))
|
|
c->monst = moFjordTroll;
|
|
}
|
|
if(c->land == laDeadCaves) {
|
|
if(hrand(5000) < PT(100 + 2 * (kills[moDarkTroll] + kills[moEarthElemental]), 200) && notDippingFor(itSilver))
|
|
c->item = itSilver;
|
|
if(hrand(16000) < (items[itSilver] + hard)) {
|
|
c->monst = moEarthElemental;
|
|
for(int i=0; i<c->type; i++) {
|
|
cell *c2 = c->mov[i];
|
|
earthFloor(c2);
|
|
}
|
|
for(int i=0; i<c->type; i++) if(c->mov[i]->mpdist < c->mpdist) c->mondir = i;
|
|
chasmifyEarth(c); c->wall = waDeadfloor2;
|
|
}
|
|
else if(hrand(8000) < 60 + 8 * (items[itSilver] + hard)) {
|
|
if(hrand(100) < 25) {
|
|
}
|
|
else c->monst = hrand(2) ? moDarkTroll : moGoblin;
|
|
}
|
|
}
|
|
if(c->land == laDesert) {
|
|
if(hrand(5000) < PT(100 + 2 * (kills[moWorm] + kills[moDesertman]), 200) && notDippingFor(itSpice))
|
|
c->item = itSpice;
|
|
if(hrand(8000) < 10 + 2 * (items[itSpice] + hard) && !c->monst)
|
|
c->monst = hrand(2) ? moWorm : moDesertman,
|
|
c->mondir = NODIR;
|
|
}
|
|
if(c->land == laRedRock) {
|
|
if(hrand(16000) < 30+items[itRedGem]+hard && !pseudohept(c) && !c->monst && !c->wall) {
|
|
int i = -1;
|
|
for(int t=0; t<6; t++) if(c->mov[t]->mpdist > c->mpdist && !pseudohept(c->mov[t]))
|
|
i = t;
|
|
if(i != -1) {
|
|
c->monst = moHexSnake;
|
|
c->bardir = NOBARRIERS;
|
|
int len = purehepta ? 2 : ROCKSNAKELENGTH;
|
|
cell *c2 = c;
|
|
vector<cell*> rocksnake;
|
|
while(--len) {
|
|
rocksnake.push_back(c2);
|
|
c2->bardir = NOBARRIERS;
|
|
c2->mondir = i;
|
|
createMov(c2, i);
|
|
int j = c2->spn[i];
|
|
cell *c3 = c2->mov[i];
|
|
if(c3->monst || c3->bardir != NODIR || c3->wall) break;
|
|
c2 = c3;
|
|
c2->monst = moHexSnakeTail;
|
|
i = (j + (len%2 ? 2 : 4)) % 6;
|
|
}
|
|
if(size(rocksnake) < ROCKSNAKELENGTH/2 && !purehepta) {
|
|
for(int i=0; i<size(rocksnake); i++)
|
|
rocksnake[i]->monst = moNone;
|
|
}
|
|
else c2->mondir = NODIR;
|
|
}
|
|
}
|
|
else if(hrand(16000) < 50+items[itRedGem]+hard && (purehepta?hrand(10)<3:!ishept(c)) && !c->monst)
|
|
c->monst = moRedTroll,
|
|
c->mondir = NODIR;
|
|
}
|
|
if(c->land == laDragon) {
|
|
// 40 is the usual rate of dragon generation
|
|
int dchance = 40;
|
|
// but it grows to 400 if no Dragons in sight, to make it faster
|
|
if(cwt.c->land == laDragon && !havedragon)
|
|
dchance = 400;
|
|
// also, don't generate additional Dragons for newbies
|
|
else if(havedragon && items[itDragon] < 10)
|
|
dchance = 5;
|
|
|
|
if(hrand(150000) < dchance && !c->monst && (!c->wall || c->wall == waChasm)) {
|
|
havedragon = true;
|
|
// printf("dragon generated with dchance = %d\n", dchance);
|
|
vector<int> possi;
|
|
for(int t=0; t<6; t++) if(c->mov[t]->mpdist > c->mpdist) possi.push_back(t);
|
|
if(size(possi)) {
|
|
int i = possi[hrand(size(possi))];
|
|
int dragonlength = 6 + items[itDragon] / 2;
|
|
c->monst = moDragonHead; c->hitpoints = 1;
|
|
c->bardir = NOBARRIERS;
|
|
cell *c2 = c;
|
|
int len = dragonlength;
|
|
vector<cell*> dragon;
|
|
while(--len) {
|
|
dragon.push_back(c2);
|
|
c2->bardir = NOBARRIERS;
|
|
c2->mondir = i;
|
|
createMov(c2, i);
|
|
int j = c2->spn[i];
|
|
cell *c3 = c2->mov[i];
|
|
if(c3->monst || c3->bardir != NODIR || c3->wall || c3->mpdist <= 7) break;
|
|
c2 = c3;
|
|
c2->monst = moDragonTail;
|
|
c2->hitpoints = 1;
|
|
i = j + 2 + hrand(c2->type-3);
|
|
i %= c2->type;
|
|
}
|
|
if(size(dragon) < 5 || size(dragon) < dragonlength / 2) {
|
|
for(int i=0; i<size(dragon); i++)
|
|
dragon[i]->monst = moNone;
|
|
}
|
|
else c2->mondir = NODIR;
|
|
}
|
|
}
|
|
if(!c->monst && !tactic::on && !yendor::on && !euclid && hrand(4000) < 10 && !safety) {
|
|
c->item = itBabyTortoise;
|
|
tortoise::babymap[c] = getBits(c) ^ tortoise::getRandomBits();
|
|
}
|
|
}
|
|
if(c->land == laWineyard) {
|
|
if(hrand(5000) < PT(100 + 2 * (kills[moVineBeast] + kills[moVineSpirit]), 200) && notDippingFor(itWine))
|
|
c->item = itWine;
|
|
if(hrand(8000) < 12 * (items[itWine] + hard))
|
|
c->monst = moVineBeast;
|
|
}
|
|
if(c->land == laZebra) {
|
|
if(c->wall == waNone && hrand(2500) < PT(100 + 2 * (kills[moOrangeDog]), 300) && notDippingFor(itZebra))
|
|
c->item = itZebra;
|
|
if(hrand(10000) < 40 + 2*(items[itZebra] + hard))
|
|
c->monst = moOrangeDog;
|
|
}
|
|
if(isElemental(c->land) && c->land != laElementalWall) {
|
|
eItem localshard = eItem(itFireShard + (c->land - laEFire));
|
|
int danger = 5 * items[localshard] * items[localshard];
|
|
eMonster elof = elementalOf(c->land);
|
|
int elkills = PT(kills[elof], 25);
|
|
|
|
if(hrand(8000) < 12 + (items[itElemental] + danger + hard)) {
|
|
c->monst = elof;
|
|
if(c->land != laEAir) chasmifyElemental(c);
|
|
c->wall = waNone;
|
|
if(c->land == laEWater) c->wall = waSea;
|
|
}
|
|
else if(hrand(5000) < 100 + elkills*3 && notDippingFor(itElemental))
|
|
c->item = localshard;
|
|
else if(hrand(5000) < 10 && items[itElemental] >= 10)
|
|
c->item = itOrbSummon;
|
|
else if(hrand(5000) < 10*PRIZEMUL)
|
|
placePrizeOrb(c);
|
|
}
|
|
if(c->land == laEmerald) {
|
|
if(hrand(purehepta?400:1000) < PT(100 + 2 * (kills[moMiner] + kills[moLancer] + kills[moFlailer]), 200) && notDippingFor(itEmerald)) {
|
|
// do not destroy walls!
|
|
bool ok = true;
|
|
for(int i=0; i<c->type; i++) if(c->mov[i]->wall == waCavewall) ok = false;
|
|
if(ok) c->item = itEmerald;
|
|
}
|
|
if(hrand(8000) < 50 + 10 * (items[itEmerald] + hard)) {
|
|
static eMonster emeraldmonsters[4] = { moHedge, moLancer, moFlailer, moMiner };
|
|
c->monst = emeraldmonsters[hrand(4)];
|
|
}
|
|
}
|
|
if(c->land == laJungle) {
|
|
if(hrand(5000) < PT(25 + 2 * (kills[moIvyRoot] + kills[moMonkey]), 40) && notDippingFor(itRuby))
|
|
c->item = itRuby;
|
|
if(hrand(15000) < 5 + 1 * (items[itRuby] + hard))
|
|
c->monst = moMonkey;
|
|
else if(hrand(80000) < 5 + items[itRuby] + hard)
|
|
c->monst = moEagle;
|
|
else if(ishept(c) && c != origin.c7 && hrand(4000) < 300 + items[itRuby] && !c->monst) {
|
|
int hardchance = items[itRuby] + hard;
|
|
if(hardchance > 25) hardchance = 25;
|
|
bool hardivy = hrand(100) < hardchance;
|
|
if(hardivy ? buildIvy(c, 1, 9) : buildIvy(c, 0, c->type))
|
|
c->item = itRuby;
|
|
}
|
|
}
|
|
if(c->land == laWhirlwind) {
|
|
if(hrand(4500) < items[itWindstone] + hard)
|
|
c->monst = moWindCrow;
|
|
if(hrand(30000) < items[itWindstone] + hard - 5)
|
|
c->monst = moAirElemental;
|
|
}
|
|
|
|
if(c->land == laStorms) {
|
|
if(hrand(7500) < 25 + (items[itFulgurite] + hard))
|
|
c->monst = (hrand(5) ? moMetalBeast : moMetalBeast2),
|
|
c->hitpoints = 3;
|
|
if(hrand(10000) < 20 + (items[itFulgurite] + hard) && !ishept(c)) {
|
|
c->monst = moStormTroll;
|
|
}
|
|
}
|
|
if(c->land == laWildWest) {
|
|
if(hrand(25000) < 2 + (2 * items[itBounty] + hard) + (items[itRevolver] ? 150:0))
|
|
c->monst = moOutlaw;
|
|
if(hrand(1000) < PT(20 + kills[moOutlaw], 40))
|
|
c->item = itRevolver;
|
|
}
|
|
if(c->land == laAlchemist) {
|
|
if(hrand(5000) < PT(25 + min(kills[moSlime], 200), 100) && notDippingFor(itElixir))
|
|
c->item = itElixir;
|
|
else if(hrand(3500) < 10 + items[itElixir] + hard)
|
|
c->monst = moSlime;
|
|
}
|
|
if(c->land == laRose) {
|
|
if(hrand(2000) < PT(25 + min(kills[moFalsePrincess] + kills[moRoseBeauty] + kills[moRoseLady], 200), 100) && notDippingFor(itRose)) {
|
|
for(int t=0; t<c->type; t++) if(c->mov[t] && c->mov[t]->wall == waRose)
|
|
c->item = itRose;
|
|
}
|
|
else {
|
|
int p = hrand(10000);
|
|
if(p >= 10 + items[itRose] + hard) ;
|
|
else if(p < 10) c->monst = moFalsePrincess;
|
|
else if(p < 13) c->monst = moRoseBeauty;
|
|
else if(p < 18) c->monst = moRoseLady;
|
|
else if(p < 20) c->monst = moRoseBeauty;
|
|
else if(p < 30) c->monst = moFalsePrincess;
|
|
else if(p < 35) c->monst = moRoseLady;
|
|
else if(p < 60) c->monst = moFalsePrincess;
|
|
else if(p < 65) c->monst = moRoseBeauty;
|
|
else c->monst = moFalsePrincess;
|
|
}
|
|
}
|
|
if(c->land == laPower) {
|
|
if(hrand(5000+50*items[itPower]) < 1200) {
|
|
eItem powerorbs[6] = {
|
|
itOrbFlash, itOrbSpeed, itOrbFire, itOrbWinter, itOrbGhost, itOrbLife};
|
|
c->item = powerorbs[hrand(6)];
|
|
}
|
|
else if(c->type == 6 && hrand(5000) < 10)
|
|
c->wall = hrand(2) ? waMirror : waCloud;
|
|
else if(hrand(1000) < 10 + (items[itPower] ? 10:0) + (items[itPower] + hard))
|
|
c->monst = eMonster(moWitch + hrand(NUMWITCH));
|
|
}
|
|
if(isCrossroads(c->land)) {
|
|
if(c->type == 6 && hrand(8000) < 120 && (tactic::on && (isCrossroads(tactic::lasttactic) || items[itShard] >= 10)))
|
|
c->wall = hrand(2) ? waMirror : waCloud;
|
|
else if(c->land == laCrossroads4 && hrand(24000) < 10 && tactic::on)
|
|
c->wall = waRose;
|
|
else {
|
|
if(hyperstonesUnlocked() && hrand(25000) < PT(tkills(), 2000) && notDippingFor(itHyperstone))
|
|
c->item = itHyperstone;
|
|
if(hrand(4000) < items[itHyperstone] + 2 * items[itHolyGrail] && !c->monst) {
|
|
// only interesting monsters here!
|
|
eMonster cm = crossroadsMonster();
|
|
if(cm == moIvyRoot) buildIvy(c, 0, c->type);
|
|
else c->monst = cm;
|
|
if(cm == moWorm || cm == moTentacle)
|
|
c->mondir = NODIR;
|
|
c->hitpoints = palaceHP();
|
|
}
|
|
}
|
|
}
|
|
if(c->land == laMirror) {
|
|
if((purehepta?pseudohept(c):!ishept(c)) && hrand(5000) < 120 && notDippingFor(itShard))
|
|
c->wall = hrand(2) ? waMirror : waCloud;
|
|
else if(ishept(c) && hrand(5000) < 10 * PRIZEMUL)
|
|
placePrizeOrb(c);
|
|
else if(hrand(12000) < 8 + items[itShard] + hard)
|
|
c->monst = moRanger;
|
|
else if(hrand(60000) < 8 + items[itShard] + hard)
|
|
c->monst = moEagle;
|
|
}
|
|
if(c->land == laGraveyard) {
|
|
if(hrand(5000) < PT(30 + 2 * (kills[moZombie] + kills[moGhost] + kills[moNecromancer]), 120) && notDippingFor(itBone))
|
|
c->item = itBone;
|
|
if(hrand(20000) < 10 + items[itBone] + hard) {
|
|
eMonster grm[6] = { moZombie, moZombie, moZombie, moGhost, moGhost, moNecromancer};
|
|
c->monst = grm[hrand(6)];
|
|
}
|
|
}
|
|
if(c->land == laRlyeh) {
|
|
if(hrand(5000) < PT(30 + 2 * (kills[moCultist] + kills[moTentacle] + kills[moPyroCultist]), 100) && notDippingFor(itStatue))
|
|
c->item = itStatue;
|
|
if(hrand(8000) < 5 + items[itStatue] + hard && !c->monst)
|
|
c->monst = moTentacle, c->item = itStatue, c->mondir = NODIR;
|
|
else if(hrand(12000) < 5 + items[itStatue] + hard)
|
|
c->monst = hrand(3) ? ((hrand(40) < items[itStatue]-25) ? moCultistLeader : moCultist) : moPyroCultist;
|
|
else if(hrand(8000) < 5 + items[itStatue] + hard && c->type == 6 && !(yendor::on && (yendor::clev().flags & YF_NEAR_TENT)) && celldist(c)>=3) {
|
|
for(int t=0; t<c->type; t++) {
|
|
if(c->mov[t] && c->mov[t]->monst == moNone && (c->wall == waNone || c->wall == waColumn))
|
|
c->mov[t]->wall = ishept(c->mov[t]) ? waColumn : waNone;
|
|
if(c->mov[t]->wall == waColumn)
|
|
c->mov[t]->item = itNone;
|
|
}
|
|
if(buildIvy(c, 0, 3)) c->item = itStatue;
|
|
}
|
|
}
|
|
if(c->land == laTemple) {
|
|
// depth!
|
|
int d =
|
|
chaosmode ? -15 * items[itGrimoire]:
|
|
(euclid || c->master->alt) ? celldistAlt(c) : 10;
|
|
// remember: d is negative
|
|
if(chaosmode ? hrand(100) < 25 : d % TEMPLE_EACH == 0) {
|
|
if(hrand(5000) < 20 - 2*d && !c->monst)
|
|
c->monst = moTentacle, c->mondir = NODIR;
|
|
}
|
|
else {
|
|
// int d0 = d % TEMPLE_EACH;
|
|
// if(d0<0) d0=-d0;
|
|
if(hrand(100) < 30) // && d0 != 1 && d0 != TEMPLE_EACH-1)
|
|
c->wall = waBigStatue;
|
|
else if(hrand(20000) < -d)
|
|
c->monst = hrand(3) ? moCultist : moPyroCultist;
|
|
else if(hrand(100000) < -d)
|
|
c->monst = moCultistLeader;
|
|
else if(hrand(5000) < 250)
|
|
c->item = itGrimoire;
|
|
else if(hrand(5000) < 10 && -d > TEMPLE_EACH * 10)
|
|
c->item = itOrbDragon;
|
|
}
|
|
}
|
|
if(c->land == laClearing) {
|
|
clearing::generate(c);
|
|
if(pseudohept(c)) {
|
|
int d = -celldistAlt(c);
|
|
if(hrand(2500) < items[itMutant2] + hard - 10)
|
|
c->monst = moRedFox;
|
|
else if(hrand(100 + d) < d)
|
|
c->item = itMutant2;
|
|
}
|
|
}
|
|
|
|
if(c->land == laDryForest) {
|
|
if(hrand(5000) < PT(100 + 2 * (kills[moFireFairy]*2 + kills[moHedge]), 160) && notDippingFor(itFernFlower))
|
|
c->item = itFernFlower;
|
|
if(hrand(4000) < 40 + items[itFernFlower] + hard)
|
|
c->monst = moHedge;
|
|
else if(hrand(8000) < 2 * items[itFernFlower] + hard)
|
|
c->monst = moFireFairy;
|
|
}
|
|
if(c->land == laHell) {
|
|
if(hrand(6000) < PT(120 + (kills[moLesser]), 240) && notDippingFor(itHell))
|
|
c->item = itHell;
|
|
if(hrand(8000) < 40 + items[itHell] * (chaosmode?4:1) + hard)
|
|
c->monst = moLesser;
|
|
else if(hrand(24000) < 40 + items[itHell] * (chaosmode?4:1) + hard)
|
|
c->monst = moGreater;
|
|
}
|
|
if(c->land == laGridCoast) {
|
|
if(hrand(12000) < 20 + 2 * items[itCoral] + hard) {
|
|
c->monst = moRatling;
|
|
c->stuntime = hrand(2);
|
|
}
|
|
}
|
|
if(c->land == laCocytus) {
|
|
if(hrand(5000) < PT(100 + 2 * (kills[moShark] + kills[moGreaterShark] + kills[moCrystalSage]), 200) && notDippingFor(itSapphire))
|
|
c->item = itSapphire;
|
|
if(hrand(5000) < 2 * (items[itSapphire] + hard)) {
|
|
eMonster ms[3] = { moYeti, moGreaterShark, moCrystalSage };
|
|
c->monst = ms[hrand(3)];
|
|
if(c->monst == moGreaterShark) c->wall = waLake;
|
|
}
|
|
}
|
|
if(c->land == laMotion) {
|
|
if(hrand(1500) < PT(30 + kills[moRunDog], 100) && notDippingFor(itFeather))
|
|
c->item = itFeather;
|
|
if(hrand(20000) < 25 + items[itFeather] + hard) {
|
|
c->monst = moRunDog;
|
|
// preset the movement direction
|
|
// this will make the dog go in the direction of the center,
|
|
// if the player is unreachable/invisible
|
|
for(int d=0; d<c->type; d++) if(c->mov[d] == from) {
|
|
c->mondir = (d+3) % c->type;
|
|
}
|
|
chasmify(c);
|
|
c->wall = shmup::on ? waNone : waChasm;
|
|
}
|
|
}
|
|
if(c->land == laHive) {
|
|
if(isHive(c->land) && hrand(6000) < (hivehard() - 15))
|
|
c->monst = randomHyperbug();
|
|
|
|
/* if(hrand(1500) < 30 + (kills[moBug0] + kills[moBug1] + kills[moBug2]) && notDippingFor(itRoyalJelly))
|
|
c->item = itRoyalJelly; */
|
|
/* if(hrand(2000) < 2)
|
|
c->monst = eMonster(moBug0 + hrand(3)); */
|
|
}
|
|
if(c->land == laCaribbean) {
|
|
// if(hrand(1500) < 60 && celldistAlt(c) <= -5)
|
|
// c->item = itCompass;
|
|
if(hrand(16000) < 40 + (items[itPirate] + hard))
|
|
c->monst = moPirate;
|
|
}
|
|
if(!c->item && c->wall != waCloud && c->wall != waMirror) {
|
|
if(isCrossroads(c->land))
|
|
placeCrossroadOrbs(c);
|
|
else
|
|
placeLocalOrbs(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
if(d >= 7 && mapeditor::whichPattern)
|
|
mapeditor::applyModelcell(c);
|
|
#endif
|
|
}
|
|
|
|
bool wchance(int a, int of) {
|
|
of *= 10;
|
|
a += yendor::hardness() + items[itHolyGrail] + 1;
|
|
if(isCrossroads(cwt.c->land))
|
|
a+= items[itHyperstone] * 10;
|
|
|
|
//if(cwt.c->land == laWhirlwind && !nowhirl) a += items[itWindstone] * 3;
|
|
|
|
for(int i=0; i<ittypes; i++) if(itemclass(eItem(i)) == IC_TREASURE)
|
|
a = max(a, (items[i]-10) / 10);
|
|
return hrand(a+of) < a;
|
|
}
|
|
|
|
void wanderingZebra(cell *start) {
|
|
cell *c = start, *c2 = start;
|
|
for(int it=0; it<100; it++) {
|
|
if(c->cpdist == getDistLimit()) {
|
|
c->monst = moOrangeDog;
|
|
c->stuntime = 0;
|
|
return;
|
|
}
|
|
int q = 0;
|
|
cell *ctab[8];
|
|
for(int i=0; i<c->type; i++) {
|
|
cell *c3 = c->mov[i];
|
|
if(c3 && c3 != c2 && c3->land == laZebra && c3->wall == waNone)
|
|
ctab[q++] = c3;
|
|
}
|
|
if(!q) break;
|
|
c2 = c; c = ctab[hrand(q)];
|
|
}
|
|
}
|
|
|
|
int getGhostTimer() {
|
|
return shmup::on ? (shmup::curtime - lastexplore) / 350 : turncount - lastexplore;
|
|
}
|
|
|
|
int getGhostcount() {
|
|
int t = getGhostTimer();
|
|
int ghostcount = 0;
|
|
if(t > 80) ghostcount = (t-80 + hrand(20)) / 20;
|
|
return ghostcount;
|
|
}
|
|
|
|
int getSeepcount() {
|
|
int t = getGhostTimer();
|
|
int seepcount = 0;
|
|
if(t > 40) seepcount = (t-40 + hrand(20)) / 20;
|
|
return seepcount;
|
|
}
|
|
|
|
bool canReachPlayer(cell *cf, eMonster m) {
|
|
vector<cell*> v;
|
|
sval++;
|
|
v.push_back(cf); cf->aitmp = sval;
|
|
for(int i=0; i<size(v); i++) {
|
|
cell *c = v[i];
|
|
for(int j=0; j<c->type; j++) {
|
|
cell *c2 = c->mov[j];
|
|
if(!c2) continue;
|
|
if(eq(c2->aitmp, sval)) continue;
|
|
if(!passable_for(m, c2, c, P_MONSTER | P_ONPLAYER | P_CHAIN)) continue;
|
|
if(isPlayerOn(c2)) return true;
|
|
c2->aitmp = sval; v.push_back(c2);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void wandering() {
|
|
if(!canmove) return;
|
|
int seepcount = getSeepcount();
|
|
int ghostcount = getGhostcount();
|
|
|
|
if(cwt.c->land == laZebra && cwt.c->wall == waNone && wchance(items[itZebra], 20))
|
|
wanderingZebra(cwt.c);
|
|
|
|
while(first7 < size(dcal)) {
|
|
int i = first7 + hrand(size(dcal) - first7);
|
|
cell *c = dcal[i];
|
|
if(!c->monst) c->stuntime = 0;
|
|
|
|
if(timerghost) {
|
|
// wandering seeps & ghosts
|
|
if(seepcount && c->wall == waCavewall && !c->monst && canReachPlayer(c, moSlime)) {
|
|
c->monst = moSeep;
|
|
seepcount--;
|
|
continue;
|
|
}
|
|
|
|
if(ghostcount && !c->monst) {
|
|
c->monst = moGhost;
|
|
ghostcount--;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if((c->wall == waCavewall || c->wall == waDeadwall) && !c->monst &&
|
|
wchance(items[treasureType(c->land)], 10) && canReachPlayer(c, moSlime)) {
|
|
c->monst = moSeep;
|
|
continue;
|
|
}
|
|
|
|
else if(c->wall == waCTree && !c->monst && wchance(items[itPirate], 15) && canReachPlayer(c, moSlime)) {
|
|
c->monst = moParrot;
|
|
continue;
|
|
}
|
|
|
|
else if(c->land == laEndorian && c->wall == waNone && wchance(items[itApple], 50)) {
|
|
c->monst = moKestrel;
|
|
continue;
|
|
}
|
|
|
|
else if(c->wall == waSea && !c->monst) {
|
|
if(c->land == laCaribbean && wchance(items[itPirate], 15) && canReachPlayer(c, moPirate)) {
|
|
c->monst = moCShark;
|
|
continue;
|
|
}
|
|
if(c->land == laGridSea && avengers && canReachPlayer(c, moPirate)) {
|
|
c->monst = moRatlingAvenger;
|
|
c->wall = waBoat;
|
|
avengers--;
|
|
if(cheater) printf("avenger comes\n");
|
|
continue;
|
|
}
|
|
if(c->land == laGridSea && wchance(items[itCoral], 25) && canReachPlayer(c, moPirate)) {
|
|
c->monst = moRatling;
|
|
c->wall = waBoat;
|
|
continue;
|
|
}
|
|
if(c->land == laOcean && (items[itCoast] > 50 || (cwt.c->landparam < 25 && c->landparam < 25)) && wchance(items[itCoast], 25) && canReachPlayer(c, moEagle)) {
|
|
c->monst = moAlbatross;
|
|
continue;
|
|
}
|
|
if(c->land == laLivefjord && wchance(items[itFjord], 80) && items[itFjord] >= 10 && canReachPlayer(c, moWaterElemental)) {
|
|
c->monst = moWaterElemental;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
else if(c->monst || c->pathdist == INFD) break;
|
|
|
|
else if(c->land == laClearing && wchance(items[itMutant2], 150) && items[itMutant2] >= 15 && !c->monst && c->type == 7)
|
|
c->monst = moRedFox;
|
|
|
|
else if(hrand(50) < statuecount * statuecount)
|
|
c->monst = moCultistLeader;
|
|
|
|
else if(c->land == laIce && wchance(items[itDiamond], 10))
|
|
c->monst = hrand(2) ? moWolf : moYeti;
|
|
|
|
else if(c->land == laDesert && wchance(items[itSpice], 10))
|
|
c->monst = hrand(10) ? moDesertman : moWorm;
|
|
|
|
else if(c->land == laDragon && (items[itDragon] >= 8 || items[itOrbYendor]) && wchance(items[itDragon], 20))
|
|
c->monst = moFireElemental;
|
|
|
|
else if(c->land == laRedRock && wchance(items[itRedGem], 10))
|
|
c->monst = hrand(10) ? moRedTroll : moHexSnake;
|
|
|
|
else if(c->land == laCaves && wchance(items[itGold], 5))
|
|
c->monst = hrand(3) ? moTroll : moGoblin;
|
|
|
|
else if(c->land == laHive && wchance(hivehard(), 25))
|
|
c->monst = randomHyperbug();
|
|
|
|
else if(c->land == laDeadCaves && wchance(items[itSilver], 5))
|
|
c->monst = hrand(20) ? (hrand(3) ? moDarkTroll : moGoblin) : moEarthElemental;
|
|
|
|
else if(c->land == laJungle && wchance(items[itRuby], 40))
|
|
c->monst = hrand(10) ? moMonkey : moEagle;
|
|
|
|
else if(c->land == laMirror && wchance(items[itShard], 15))
|
|
c->monst = hrand(10) ? moRanger : moEagle;
|
|
|
|
else if(c->land == laGridCoast && wchance(items[itCoral], 40))
|
|
c->monst = moRatling;
|
|
|
|
else if(c->land == laRose && wchance(items[itRose], 25))
|
|
c->monst = moFalsePrincess;
|
|
|
|
else if(c->land == laHell && wchance(items[itHell], 20))
|
|
c->monst = hrand(3) ? moLesser : moGreater;
|
|
|
|
else if(c->land == laStorms && wchance(items[itFulgurite], 30)) {
|
|
c->monst = hrand(3) ? moMetalBeast : moStormTroll;
|
|
c->hitpoints = 3;
|
|
}
|
|
|
|
else if(c->land == laWhirlwind && wchance(items[itWindstone], 30))
|
|
c->monst = hrand(5) ? moWindCrow : moAirElemental;
|
|
|
|
else if(c->land == laWildWest && wchance(items[itBounty], 30))
|
|
c->monst = moOutlaw;
|
|
|
|
else if(c->land == laEndorian && c->wall == waTrunk && wchance(items[itApple], 30))
|
|
c->monst = moLemur;
|
|
|
|
else if(c->land == laOvergrown && wchance(items[itMutant], 50))
|
|
c->monst = moForestTroll;
|
|
|
|
else if(c->land == laCaribbean && wchance(items[itPirate], 30))
|
|
c->monst = moPirate;
|
|
|
|
else if(c->land == laRlyeh && wchance(items[itStatue], 15))
|
|
c->monst = hrand(3) ? moPyroCultist :
|
|
(hrand(40) < items[itStatue]-25) ? moCultistLeader : moCultist;
|
|
|
|
else if(c->land == laGraveyard && wchance(items[itBone], 15))
|
|
c->monst = hrand(5) ? moGhost : moNecromancer;
|
|
|
|
else if(isHaunted(c->land) && wchance(items[itLotus], 15))
|
|
c->monst = moGhost;
|
|
|
|
else if(c->land == laDryForest && wchance(items[itFernFlower], 5))
|
|
c->monst = hrand(5) ? moHedge : moFireFairy;
|
|
|
|
else if(c->land == laCocytus && wchance(items[itSapphire], 45))
|
|
c->monst = moCrystalSage;
|
|
|
|
else if(c->land == laAlchemist && wchance(items[itElixir], 3) && canReachPlayer(c, moSlime) && c->item == itNone)
|
|
c->monst = moSlime; // ?
|
|
|
|
else if(isElemental(c->land) && wchance(items[itElemental], 20))
|
|
c->monst = elementalOf(c->land);
|
|
|
|
else if(c->land == laIvoryTower && wchance(items[itEdge], 20))
|
|
c->monst = moGargoyle;
|
|
|
|
else if(c->land == laIvoryTower && wchance(items[itApple], 20))
|
|
c->monst = c->wall == waTrunk ? moLemur : moGargoyle;
|
|
|
|
else if(c->land == laMinefield && wchance(items[itBombEgg]-20, 400))
|
|
c->monst = moBomberbird;
|
|
|
|
else if(c->land == laEmerald && wchance(items[itEmerald], 5)) {
|
|
static eMonster m[4] = {moHedge, moLancer, moMiner, moFlailer};
|
|
c->monst = m[hrand(4)];
|
|
}
|
|
|
|
else if(c->land == laWineyard && wchance(items[itWine], 10)) {
|
|
c->monst = moVineBeast;
|
|
}
|
|
|
|
else if(c->land == laPalace && wchance(items[itPalace], 50)) {
|
|
if(princess::dist(c) < OUT_OF_PRISON && !princess::challenge) break;
|
|
|
|
if(items[itPalace] >= 15 && hrand(100) < 10)
|
|
c->monst = moVizier;
|
|
else if(items[itPalace] >= 5 && hrand(100) < 50)
|
|
c->monst = hrand(2) ? moFatGuard : moSkeleton;
|
|
else c->monst = moPalace;
|
|
c->hitpoints = palaceHP();
|
|
}
|
|
|
|
else if(c->land == laLivefjord && wchance(items[itFjord], 10)) {
|
|
c->monst = moViking;
|
|
}
|
|
|
|
else if(c->land == laOcean && wchance(items[itCoast], 100)) {
|
|
c->monst = moAlbatross;
|
|
}
|
|
|
|
else if(c->land == laPower && wchance(items[itPower], 10)) {
|
|
c->monst = eMonster(moWitch + hrand(NUMWITCH));
|
|
}
|
|
|
|
else if(c->land == laCamelot && hrand(30) == 0 && (euclid || c->master->alt) && celldistAltRelative(c) < 0) {
|
|
eMonster m[3] = { moHedge, moLancer, moFlailer };
|
|
c->monst = m[hrand(3)];
|
|
}
|
|
|
|
else if(isCrossroads(c->land) && items[itHyperstone] && wchance(items[itHyperstone], 20)) {
|
|
c->monst = wanderingCrossroadsMonster();
|
|
c->hitpoints = palaceHP();
|
|
}
|
|
|
|
else break;
|
|
|
|
if(c->monst == moWorm || c->monst == moHexSnake) c->mondir = NODIR;
|
|
|
|
// laMotion -> no respawn!
|
|
}
|
|
}
|
|
|
|
void generateAlts(heptagon *h) {
|
|
if(!h->alt) return;
|
|
h->c7->bardir = NOBARRIERS;
|
|
for(int i=0; i<7; i++) if(h->c7->mov[i])
|
|
h->c7->mov[i]->bardir = NOBARRIERS;
|
|
for(int i=0; i<7; i++)
|
|
createStep(h->alt, i)->alt = h->alt->alt;
|
|
int relspin = -4; // for horocycles it must go the other way
|
|
for(int i=0; i<7; i++) for(int j=0; j<7; j++) {
|
|
createStep(h, i);
|
|
if(h->move[i]->alt == h->alt->move[j])
|
|
relspin = (i-j+7) % 7;
|
|
}
|
|
if(relspin == -4) {
|
|
if(h->alt != h->alt->alt) {
|
|
printf("relspin {%p:%p}\n", h->alt, h->alt->alt);
|
|
for(int i=0; i<7; i++) printf("%p ", h->alt->move[i]); printf(" ALT\n");
|
|
for(int i=0; i<7; i++) printf("%p ", h->move[i]); printf(" REAL\n");
|
|
for(int i=0; i<7; i++) printf("%p ", h->move[i]->alt); printf(" REAL ALT\n");
|
|
}
|
|
relspin = 3;
|
|
}
|
|
// h[relspin] matches alt[0]
|
|
//printf("{%d~%d}\n", h->distance, h->alt->distance);
|
|
for(int i=0; i<7; i++) {
|
|
int ir = (7+i-relspin)%7;
|
|
heptagon *hm = h->alt->move[ir];
|
|
heptagon *ho = createStep(h, i);
|
|
// printf("[%p:%d ~ %p:%d] %p ~ %p\n",
|
|
// h, i, h->alt, ir,
|
|
// ho, hm);
|
|
if(ho->alt && ho->alt != hm) {
|
|
if(ho->alt->alt == hm->alt) {
|
|
printf("ERROR: alt cross! [%d -> %d]\n", ho->alt->distance, hm->distance);
|
|
// exit(1);
|
|
}
|
|
continue;
|
|
}
|
|
ho->alt = hm;
|
|
}
|
|
}
|
|
|
|
heptagon *createAlternateMap(cell *c, int rad, hstate firststate, int special) {
|
|
|
|
// check for direction
|
|
int gdir = -1;
|
|
for(int i=0; i<c->type; i++) {
|
|
if(c->mov[i] && c->mov[i]->mpdist < c->mpdist) gdir = i;
|
|
}
|
|
if(gdir < 0) return NULL;
|
|
|
|
// check for non-crossing
|
|
int bd = 2;
|
|
cellwalker bb(c, bd);
|
|
if(!(checkBarriersFront(bb) && checkBarriersBack(bb))) {
|
|
return NULL;
|
|
}
|
|
|
|
// okay, let's go then!
|
|
cellwalker bf(c, gdir);
|
|
cell *cx[rad+1];
|
|
for(int i=0; i<rad; i++) {
|
|
cx[i] = bf.c;
|
|
if(bf.c->type == 6)
|
|
cwspin(bf, 3);
|
|
else
|
|
cwspin(bf, 3 + hrand(2));
|
|
cwstep(bf);
|
|
}
|
|
cx[rad] = bf.c;
|
|
heptagon *h = bf.c->master;
|
|
|
|
if(special == waPalace) {
|
|
|
|
// type 7 is ensured
|
|
cell *c = bf.c;
|
|
|
|
if(cdist50(c) != 0) return NULL;
|
|
if(polarb50(c) != 1) return NULL;
|
|
}
|
|
|
|
heptagon *alt = new heptagon;
|
|
allAlts.push_back(alt);
|
|
//printf("new alt {%p}\n", alt);
|
|
alt->s = firststate;
|
|
alt->emeraldval = 0;
|
|
alt->zebraval = 0;
|
|
for(int i=0; i<7; i++) alt->move[i] = NULL;
|
|
alt->distance = 0;
|
|
alt->c7 = NULL;
|
|
alt->alt = alt;
|
|
h->alt = alt;
|
|
|
|
for(int d=rad; d>=0; d--) {
|
|
generateAlts(cx[d]->master);
|
|
cx[d]->bardir = NOBARRIERS;
|
|
}
|
|
|
|
if(special == waPalace) {
|
|
|
|
cell *c = bf.c;
|
|
|
|
princess::generating = true;
|
|
c->land = laPalace;
|
|
for(int j=BARLEV; j>=0; j--)
|
|
setdist(c, j, NULL);
|
|
princess::generating = false;
|
|
|
|
princess::newInfo(c);
|
|
princess::forceMouse = false;
|
|
|
|
if(princess::gotoPrincess && cheater) princess::gotoPrincess=false, cwt.c = c;
|
|
}
|
|
|
|
return alt;
|
|
//for(int d=rad; d>=0; d--) printf("%3d. %p {%d}\n", d, cx[d]->master, cx[d]->master->alt->distance);
|
|
}
|
|
|