mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-10-31 19:36:16 +00:00
62629f3e70
Since we require C++11, most of these consts can be constexpr. Two `static const ld` remain non-compile-time-evaluable because they depend on the runtime `log` function. One `static const cld` remains non-compile-time because `std::complex<T>` doesn't become constexpr until C++14.
634 lines
23 KiB
C++
634 lines
23 KiB
C++
// Hyperbolic Rogue -- orb generation routines
|
|
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
|
|
|
|
/** \file orbgen.cpp
|
|
* \brief Orb generation: which orbs in which lands, functions generating prize/local orbs, etc.
|
|
*/
|
|
|
|
#include "hyper.h"
|
|
namespace hr {
|
|
// orbgen flags
|
|
|
|
#if HDR
|
|
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
|
|
olrBurns // burns
|
|
};
|
|
#endif
|
|
|
|
#if HDR
|
|
namespace orbgenflags {
|
|
// generates in the given land from 10 treasures, in the classic mode
|
|
static constexpr int LOCAL10 = 1;
|
|
// generates in the Crossroads from 10 treasures, in the classic mode
|
|
static constexpr int CROSS10 = 2;
|
|
// generates in other places from 25 treasures, in the classic mode
|
|
static constexpr int GLOBAL25 = 4;
|
|
// in OSM you get it once at 10 treasures
|
|
static constexpr int OSM_AT10 = 8;
|
|
|
|
|
|
// 'native' functions return this
|
|
static constexpr int NATIVE = 64;
|
|
// 'local' orb will be also placed in OSM (at 25 treasures); needs LOCAL10
|
|
static constexpr int OSM_LOCAL25 = 128;
|
|
// 'crossroads' orb will be also placed in OSM (at 50 treasures)
|
|
static constexpr int OSM_CROSS50 = 256;
|
|
// 'crossroads' orb will be also placed in OSM (at 25 treasures)
|
|
static constexpr int OSM_CROSS25 = 512;
|
|
// 'global' orb will be also placed in OSM (at 100 treasures)
|
|
static constexpr int OSM_GLOBAL100 = 1024;
|
|
// do not create in the Crossroads in the tactics mode
|
|
static constexpr int NO_TACTIC = (1<<11);
|
|
// guest Orb
|
|
static constexpr int GUEST = (1<<12);
|
|
|
|
// typical combinations
|
|
static constexpr int S_NATIVE = LOCAL10 | CROSS10 | GLOBAL25 | NATIVE;
|
|
static constexpr int S_GUEST = LOCAL10 | OSM_AT10 | GUEST;
|
|
static constexpr int S_YENDOR = S_NATIVE | OSM_LOCAL25 | OSM_CROSS50 | OSM_GLOBAL100 | NO_TACTIC;
|
|
static constexpr int S_NAT_NT = S_NATIVE | NO_TACTIC;
|
|
static constexpr int S_NA_O25 = S_NATIVE | OSM_CROSS25;
|
|
}
|
|
|
|
struct orbinfo {
|
|
int flags;
|
|
eLand l;
|
|
int lchance;
|
|
int gchance;
|
|
eItem orb;
|
|
bool is_native() const { using namespace orbgenflags; return flags & NATIVE; }
|
|
};
|
|
#endif
|
|
|
|
EX vector<orbinfo> orbinfos = {
|
|
{orbgenflags::S_NATIVE, laGraveyard, 200, 200,itGreenStone}, // must be first so that it does not reduce
|
|
{orbgenflags::S_NATIVE, laJungle, 1200, 1500,itOrbLightning},
|
|
{orbgenflags::S_NATIVE, laIce, 2000, 1500,itOrbFlash},
|
|
{orbgenflags::S_NATIVE, laCaves, 1800, 2000,itOrbLife},
|
|
{orbgenflags::S_NATIVE, laAlchemist, 800, 800,itOrbSpeed},
|
|
{orbgenflags::S_NATIVE, laDesert, 2500, 1500,itOrbShield},
|
|
{orbgenflags::S_YENDOR, laHell, 2000, 1000,itOrbYendor},
|
|
{orbgenflags::S_NATIVE, laRlyeh, 1500, 1500,itOrbTeleport},
|
|
{orbgenflags::S_NA_O25, laMotion, 2000, 700, itOrbSafety},
|
|
{orbgenflags::S_GUEST, laIce, 1500, 0, itOrbWinter},
|
|
{orbgenflags::S_GUEST, laDragon, 2500, 0, itOrbWinter},
|
|
{orbgenflags::S_GUEST, laDryForest, 2500, 0, itOrbWinter},
|
|
{orbgenflags::S_NATIVE, laCocytus, 1500, 1500, itOrbMorph},
|
|
{orbgenflags::S_GUEST, laCocytus, 1500, 0, itOrbWinter},
|
|
{orbgenflags::S_GUEST, laCaves, 1200, 0, itOrbDigging},
|
|
{orbgenflags::S_NATIVE, laDryForest, 500, 4500, itOrbThorns},
|
|
{orbgenflags::S_GUEST, laDeadCaves, 1800, 0, itGreenStone},
|
|
{orbgenflags::S_NAT_NT, laDeadCaves, 1800, 1500, itOrbDigging},
|
|
{orbgenflags::S_NATIVE, laEmerald, 1500, 3500, itOrbPsi},
|
|
{orbgenflags::S_NATIVE, laWineyard, 900, 1200, itOrbAether},
|
|
{orbgenflags::S_NATIVE, laHive, 800, 1200, itOrbInvis},
|
|
{orbgenflags::S_NATIVE, laPower, 0, 3000, itOrbFire},
|
|
{orbgenflags::S_NATIVE, laMinefield, 0, 3500, itOrbFriend},
|
|
{orbgenflags::S_NATIVE, laTemple, 0, 3000, itOrbDragon},
|
|
{orbgenflags::S_NATIVE, laCaribbean, 0, 3500, itOrbTime},
|
|
{orbgenflags::S_NATIVE, laRedRock, 0, 2500, itOrbSpace},
|
|
{orbgenflags::S_NATIVE, laCamelot, 1000, 1500, itOrbIllusion},
|
|
{orbgenflags::S_NATIVE, laOcean, 0, 1500, itOrbEmpathy},
|
|
{orbgenflags::S_GUEST, laOcean, 0, 0, itOrbAir},
|
|
{orbgenflags::S_NATIVE, laPalace, 0, 4000, itOrbDiscord},
|
|
{orbgenflags::S_GUEST, laPalace, 0, 0, itOrbFrog},
|
|
{orbgenflags::S_NATIVE, laZebra, 500, 2100, itOrbFrog},
|
|
{orbgenflags::S_NAT_NT, laLivefjord, 0, 1800, itOrbFish},
|
|
{orbgenflags::S_NAT_NT, laPrincessQuest, 0, 200, itOrbLove},
|
|
{orbgenflags::S_NATIVE, laIvoryTower, 500, 4000, itOrbMatter},
|
|
{orbgenflags::S_NAT_NT, laElementalWall, 1500, 4000, itOrbSummon},
|
|
{orbgenflags::S_NATIVE, laStorms, 1000, 2500, itOrbStunning},
|
|
{orbgenflags::S_NATIVE, laOvergrown, 1000, 800, itOrbWoods},
|
|
{orbgenflags::S_GUEST, laOvergrown, 1000, 0, itOrbLuck},
|
|
{orbgenflags::S_NATIVE, laWhirlwind, 1250, 3000, itOrbAir},
|
|
{orbgenflags::S_NATIVE, laHaunted, 1000, 5000, itOrbUndeath},
|
|
{orbgenflags::S_NATIVE, laClearing, 5000, 5000, itOrbFreedom},
|
|
{orbgenflags::S_NATIVE, laRose, 2000, 8000, itOrbBeauty},
|
|
{orbgenflags::S_NATIVE, laWarpCoast, 2000, 8000, itOrb37},
|
|
{orbgenflags::S_NATIVE, laDragon, 500, 5000, itOrbDomination},
|
|
{orbgenflags::S_NATIVE, laTortoise, 2500, 1500, itOrbShell},
|
|
{orbgenflags::S_NATIVE, laEndorian, 150, 2500, itOrbEnergy},
|
|
{orbgenflags::S_GUEST, laEndorian, 450, 0, itOrbTeleport},
|
|
{orbgenflags::S_NATIVE, laKraken, 500, 2500, itOrbSword},
|
|
{orbgenflags::S_NATIVE, laBurial, 500, 2500, itOrbSword2},
|
|
{orbgenflags::S_NATIVE, laTrollheim, 750, 1800, itOrbStone},
|
|
{orbgenflags::S_NATIVE, laMountain, 400, 3500, itOrbNature},
|
|
{orbgenflags::S_NATIVE, laDungeon, 120, 2500, itOrbRecall},
|
|
{orbgenflags::S_NATIVE, laReptile, 500, 2100, itOrbDash},
|
|
{orbgenflags::S_NATIVE, laBull, 720, 3000, itOrbHorns},
|
|
{orbgenflags::S_NATIVE, laPrairie, 0, 3500, itOrbBull},
|
|
{orbgenflags::S_GUEST, laWhirlpool, 0, 0, itOrbSafety},
|
|
{orbgenflags::S_NATIVE, laVolcano, 0, 7000, itOrbLava},
|
|
{orbgenflags::S_NATIVE, laHunting, 0, 2500, itOrbSide3},
|
|
{orbgenflags::S_NATIVE, laBlizzard, 0, 2000, itOrbWinter},
|
|
{orbgenflags::S_NATIVE, laTerracotta, 800, 2500, itOrbSide1},
|
|
{orbgenflags::S_NATIVE, laDual, 600, 2500, itOrbSide2},
|
|
{orbgenflags::S_GUEST, laSnakeNest, 2000, 0, itOrbDomination},
|
|
{orbgenflags::S_GUEST, laDocks, 3000, 0, itOrbFish},
|
|
{orbgenflags::S_GUEST, laDocks, 3000, 0, itOrbDragon},
|
|
{orbgenflags::S_GUEST, laDocks, 3000, 0, itOrbDash},
|
|
{orbgenflags::S_GUEST, laSwitch, 2000, 0, itOrbSpace},
|
|
{orbgenflags::S_NATIVE, laSwitch, 2000, 3000, itOrbPhasing},
|
|
{orbgenflags::S_NATIVE, laMagnetic, 2000, 3000, itOrbMagnetism},
|
|
{orbgenflags::S_NATIVE, laRuins, 1200, 2500, itOrbSlaying},
|
|
{orbgenflags::S_NATIVE, laWestWall, 2000, 4200, itOrbGravity},
|
|
{orbgenflags::S_NATIVE, laVariant, 900, 4200, itOrbIntensity},
|
|
{orbgenflags::S_GUEST, laVariant, 4000, 0, itOrbFire},
|
|
{orbgenflags::S_GUEST, laVariant, 4000, 0, itOrbWinter},
|
|
{orbgenflags::S_GUEST, laVariant, 4000, 0, itOrbPhasing},
|
|
{orbgenflags::S_GUEST, laVariant, 4000, 0, itOrbDash},
|
|
{orbgenflags::S_NATIVE, laBrownian, 900, 4200, itOrbChoice},
|
|
{orbgenflags::S_GUEST, laBrownian, 1000, 0, itOrbAir},
|
|
{orbgenflags::S_GUEST, laBrownian, 1000, 0, itOrbDash},
|
|
{orbgenflags::S_GUEST, laBrownian, 1000, 0, itOrbSafety},
|
|
{orbgenflags::S_NATIVE, laFrog, 1000, 1500, itOrbImpact},
|
|
{orbgenflags::S_NATIVE, laEclectic, 1000, 1000, itOrbChaos},
|
|
{orbgenflags::S_GUEST, laEclectic, 4000, 0, itOrbWinter},
|
|
{orbgenflags::S_GUEST, laEclectic, 2000, 0, itOrbLightning},
|
|
{orbgenflags::S_NATIVE, laWet, 800, 2500, itOrbPlague},
|
|
{orbgenflags::S_GUEST, laWet, 1200, 0, itOrbFish},
|
|
{orbgenflags::S_GUEST, laWet, 1200, 0, itOrbAether},
|
|
{orbgenflags::S_GUEST, laWet, 1200, 0, itOrbFrog},
|
|
{orbgenflags::S_NATIVE, laCursed, 400, 1500, itOrbPurity},
|
|
{orbgenflags::S_NAT_NT, laDice, 500, 800, itOrbLuck},
|
|
{orbgenflags::S_GUEST, laDice, 750, 0, itOrbAir},
|
|
{orbgenflags::S_NATIVE, laWhirlpool, 0, 2000, itOrbWater}, // needs to be last
|
|
};
|
|
|
|
EX eItem nativeOrbType(eLand l) {
|
|
if(isElemental(l)) l = laElementalWall;
|
|
if(inv::on && (l == laMirror || l == laMirrorOld || isCrossroads(l)))
|
|
return itOrbMirror;
|
|
if(l == laMirror || l == laMirrorOld) return itShard;
|
|
for(auto& oi: orbinfos)
|
|
if(oi.l == l && oi.is_native())
|
|
return oi.orb;
|
|
return itNone;
|
|
}
|
|
|
|
const orbinfo& getNativityOrbInfo(eItem orb) {
|
|
for(auto& oi: orbinfos)
|
|
if(oi.orb == orb && oi.is_native())
|
|
return oi;
|
|
static orbinfo oi;
|
|
oi.l = laMirror;
|
|
return oi;
|
|
}
|
|
|
|
EX string olrDescriptions[18] = {
|
|
"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 to %the1 (collect 10 %2)",
|
|
"native to %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",
|
|
"would be destroyed in %the1"
|
|
};
|
|
|
|
EX eOrbLandRelation getOLR(eItem it, eLand l) {
|
|
|
|
if(l == laMotion && it == itOrbChoice && !inv::on) return olrUseless;
|
|
|
|
if(l == laPower) {
|
|
if(it == itOrbFire) return olrPNative;
|
|
|
|
if(
|
|
it == itOrbFlash || it == itOrbSpeed || it == itOrbWinter || it == itOrbAether ||
|
|
it == itOrbLife) return olrPBasic;
|
|
|
|
if(
|
|
it == itOrbLightning || it == itOrbThorns || it == itOrbInvis ||
|
|
it == itOrbShield || it == itOrbTeleport || it == itOrbPsi ||
|
|
it == itOrbDragon || it == itOrbIllusion || it == itOrbTime)
|
|
return olrPPrized;
|
|
|
|
return olrPNever;
|
|
}
|
|
|
|
if(it == itOrbWater && l == laWestWall) return olrUseless;
|
|
if(it == itOrbSafety && l == laCrossroads5) return olrDangerous;
|
|
if(it == itOrbFire && l == laKraken) return olrUseless;
|
|
if(it == itOrbDragon && l == laKraken) return olrUseless;
|
|
if(it == itOrbDigging && l == laKraken) return olrUseless;
|
|
if(it == itOrbIllusion && l == laKraken) return olrUseless;
|
|
|
|
if(it == itOrbYendor && among(l, laWhirlwind, laWestWall)) return olrUseless;
|
|
|
|
if(it == itOrbLife && (l == laKraken)) return olrUseless;
|
|
|
|
if(l == laVolcano && it == itOrbSafety) return olrDangerous;
|
|
if(l == laVariant && it == itOrbSafety) return olrDangerous;
|
|
if(l == laVolcano && itemBurns(it)) return olrBurns;
|
|
|
|
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(nativeOrbType(l) == it) return olrNative;
|
|
|
|
for(const orbinfo& oi: orbinfos)
|
|
if((oi.flags & orbgenflags::GUEST) && oi.l == l && oi.orb == it)
|
|
return olrGuest;
|
|
|
|
if(it == itOrbLuck && l == laIvoryTower)
|
|
return olrUseless;
|
|
if(it == itOrbLuck && l == laEndorian)
|
|
return olrUseless;
|
|
if(it == itOrbLuck && l == laDungeon)
|
|
return olrUseless;
|
|
if(it == itOrbWater && l == laRedRock)
|
|
return olrUseless;
|
|
if(it == itOrbLuck && l == laTortoise)
|
|
return olrUseless;
|
|
if(it == itOrbLuck && l == laMountain)
|
|
return olrUseless;
|
|
if(it == itOrbLuck && l == laWestWall)
|
|
return olrUseless;
|
|
if(it == itOrbLuck && l == laCamelot)
|
|
return olrUseless;
|
|
if(it == itOrbLuck && l == laHaunted)
|
|
return olrUseless;
|
|
if(it == itOrbNature && l == laWineyard)
|
|
return olrDangerous;
|
|
if((it == itOrbFrog || it == itOrbDiscord) && l == laPrincessQuest)
|
|
return olrGuest;
|
|
if(it == itOrbDragon && l == laRlyeh)
|
|
return olrMonster;
|
|
|
|
if(it == itOrbSafety && l == laWhirlpool)
|
|
return olrAlways;
|
|
if(it == itOrbSafety && l == laPrairie)
|
|
return olrAlways;
|
|
if(it == itGreenStone && isHaunted(l))
|
|
return olrAlways;
|
|
if(it == itOrbWater && l == laLivefjord)
|
|
return olrMonster;
|
|
if(isCrossroads(l) || l == laOcean)
|
|
return olrHub;
|
|
|
|
if(it == itOrbSlaying && !among(l,
|
|
laMirror, laHell, laEmerald, laDryForest, laCamelot, laPalace, laStorms, laRose, laTortoise, laBurial, laDungeon, laReptile,
|
|
laPrairie, laBull, laVolcano, laTerracotta, laRuins, laVariant, laEclectic, laBrownian, laCursed))
|
|
return olrUseless;
|
|
|
|
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 || l == laWet || l == laCursed)
|
|
return olrDangerous;
|
|
|
|
if(it == itOrbMatter)
|
|
if(among(l, laCaves, laEmerald, laAlchemist, laCaribbean, laMinefield, laCocytus, laWestWall))
|
|
return olrUseless;
|
|
|
|
if(l == laPrincessQuest)
|
|
if(among(it, itOrbAether, itOrbFlash, itOrbTeleport, itOrbSummon, itOrbFreedom, itOrbFriend, itOrbPhasing, itOrbChaos))
|
|
return olrForbidden;
|
|
|
|
if(l == laTemple)
|
|
return olrNoPrizes;
|
|
|
|
if(it == itOrbDigging) {
|
|
if(among(l, laCaves, laOcean, laLivefjord, laEmerald, laDesert, laDeadCaves, laRedRock, laCaribbean, laGraveyard,
|
|
laMountain, laHunting, laWarpSea, laWarpCoast, laCursed, laWet))
|
|
return olrPrize25;
|
|
return olrUseless;
|
|
}
|
|
|
|
if(it == itOrbWoods && (l == laWarpSea || l == laWarpCoast))
|
|
return olrDangerous;
|
|
|
|
if(it == itOrbWoods && !among(l,
|
|
laDryForest, laWineyard, laCaribbean, laOvergrown, laHaunted, laHauntedWall, laHauntedBorder, laTortoise, laFrog, laEclectic,
|
|
laVariant, laBull))
|
|
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 ||
|
|
l == laDragon || l == laTortoise || l == laBurial || l == laTrollheim ||
|
|
l == laOcean || l == laHaunted || l == laWarpCoast || l == laRose)
|
|
return olrPrize25;
|
|
return olrForbidden;
|
|
}
|
|
|
|
if(it == itOrbWater)
|
|
if(l == laMotion || l == laZebra || l == laIvoryTower || l == laEndorian ||
|
|
l == laMountain || l == laReptile || l == laDungeon)
|
|
return olrUseless;
|
|
|
|
if(it == itOrbWinter && l == laMinefield)
|
|
return olrForbidden;
|
|
|
|
if(it == itOrbWinter && !among(l, laRlyeh, laTemple, laVolcano, laCursed))
|
|
return olrUseless;
|
|
|
|
if(it == itOrbLife && l == laMotion)
|
|
return olrUseless;
|
|
|
|
if(it == itOrbFish && l == laKraken)
|
|
return olrAlways;
|
|
|
|
if(it == itOrbSword && l == laBurial)
|
|
return olrAlways;
|
|
|
|
if(it == itOrbFish && !among(l, laOcean, laLivefjord, laWhirlpool, laCamelot, laTortoise, laWarpCoast, laWarpSea, laCocytus, laBrownian, laVariant, laWet, laFrog))
|
|
return olrUseless;
|
|
|
|
if(it == itOrbDomination && 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 || it == itOrbLightning || it == itOrbLava)
|
|
return olrDangerous;
|
|
|
|
if(isHaunted(l) && it == itOrbLava)
|
|
return olrUseless;
|
|
|
|
if(l == laGraveyard && it == itOrbLava)
|
|
return olrUseless;
|
|
|
|
if(l == laDryForest)
|
|
if(it == itOrbFire || it == itOrbLightning || it == itOrbLava)
|
|
return olrDangerous;
|
|
|
|
if(l == laWet && among(it, itOrbDragon, itOrbLava, itOrbFire))
|
|
return olrUseless;
|
|
|
|
if(l == laDungeon) {
|
|
if(it == itOrbSafety || it == itOrbFrog ||
|
|
it == itOrbTeleport || it == itOrbMatter || it == itOrbNature ||
|
|
it == itOrbAether || it == itOrbSummon || it == itOrbStone || it == itOrbChaos)
|
|
return olrForbidden;
|
|
}
|
|
|
|
if(l == laTerracotta && it == itOrbFriend)
|
|
return olrDangerous;
|
|
|
|
return olrPrize25;
|
|
}
|
|
|
|
EX int orbsUnlocked() {
|
|
int i = 0;
|
|
for(int t=0; t<ittypes; t++)
|
|
if(itemclass(eItem(t)) == IC_TREASURE && items[t] >= R10)
|
|
i++;
|
|
return i;
|
|
}
|
|
|
|
EX ld orbprizefun(int tr) {
|
|
if(tactic::on) return 1;
|
|
if(tr < 10) return 0;
|
|
return .6 + .4 * log(tr/25.) / log(2);
|
|
}
|
|
|
|
EX ld orbcrossfun(int tr) {
|
|
if(tactic::on || (ls::single() && isCrossroads(specialland))) return 1;
|
|
if(tr < 10) return 0;
|
|
if(tr > 25) return 1;
|
|
return (tr*2 + 50) / 100.;
|
|
}
|
|
|
|
EX bool buildPrizeMirror(cell *c, int freq) {
|
|
if(inv::on) return false;
|
|
if(items[itShard] < 25) return false;
|
|
if(freq && hrand(freq * 100 / orbprizefun(items[itShard])) >= 100)
|
|
return false;
|
|
return mirror::build(c);
|
|
}
|
|
|
|
#if HDR
|
|
extern cellwalker cwt;
|
|
#endif
|
|
|
|
EX eLand getPrizeLand(cell *c IS(cwt.at)) {
|
|
eLand l = c->land;
|
|
if(isElemental(l)) l = laElementalWall;
|
|
if(isHaunted(l)) l = laHaunted;
|
|
if(l == laMercuryRiver) l = laTerracotta;
|
|
if(l == laWarpSea) l = laWarpCoast;
|
|
if(l == laPalace && princess::dist(c) < OUT_OF_PRISON)
|
|
l = laPrincessQuest;
|
|
return l;
|
|
}
|
|
|
|
EX void placePrizeOrb(cell *c) {
|
|
if(peace::on) return;
|
|
if(daily::on) return;
|
|
|
|
eLand l = getPrizeLand(c);
|
|
|
|
// these two lands would have too much orbs according to normal rules
|
|
if(l == laPalace && hrand(100) >= 20) return;
|
|
if(l == laPrincessQuest && hrand(100) >= 20) return;
|
|
if(l == laGraveyard && hrand(100) >= 15) return;
|
|
if(l == laBurial && hrand(100) >= 10) return;
|
|
if(l == laLivefjord && hrand(100) >= 35) return;
|
|
if(l == laMinefield && hrand(100) >= 25) return;
|
|
if(l == laElementalWall && hrand(100) >= 25) return;
|
|
|
|
for(auto& oi: orbinfos) {
|
|
if(!(oi.flags & orbgenflags::GLOBAL25)) continue;
|
|
|
|
int mintreas = 25;
|
|
if(inv::on) {
|
|
if(oi.flags & orbgenflags::OSM_GLOBAL100) mintreas = 100;
|
|
else continue;
|
|
}
|
|
|
|
eOrbLandRelation olr = getOLR(oi.orb, l);
|
|
if(olr != olrPrize25 && olr != olrPrize3) continue;
|
|
int treas = items[treasureType(oi.l)];
|
|
if(olr == olrPrize3) treas *= 10;
|
|
if(olr == olrPrize25 || olr == olrPrize3 || olr == olrGuest || olr == olrMonster || olr == olrAlways) {
|
|
if(treas < mintreas) continue;
|
|
}
|
|
else continue;
|
|
|
|
int gch = oi.gchance;
|
|
if(!gch) continue;
|
|
gch = int(gch / orbprizefun(treas));
|
|
if(hrand(gch) >= 60) continue;
|
|
if(oi.orb == itOrbWater && c->land != laOcean && c->land != laKraken) {
|
|
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));
|
|
}
|
|
|
|
// 10 not in chaos, less in chaos
|
|
EX int treasureForLocal() {
|
|
return (ls::any_chaos() ? 1+hrand(10) : 10);
|
|
}
|
|
|
|
EX bool extra_safety_for_memory(cell *c) {
|
|
if(hyperbolic && (arcm::in() || S3 > 3) && !quotient && !tactic::on && in_full_game()) {
|
|
if(hrand(1000) < 1) {
|
|
c->item = itOrbSafety;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
EX void placeLocalOrbs(cell *c) {
|
|
eLand l = c->land;
|
|
if(l == laZebra && c->wall == waTrapdoor) return;
|
|
if(isGravityLand(l) && l != laWestWall && cellEdgeUnstable(c)) return;
|
|
if(isElemental(l)) l = laElementalWall;
|
|
if(daily::on) return;
|
|
|
|
if(extra_safety_for_memory(c)) return;
|
|
|
|
if(peace::on) return;
|
|
|
|
for(auto& oi: orbinfos) {
|
|
if(!(oi.flags & orbgenflags::LOCAL10)) continue;
|
|
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(ch == 1 && ls::any_chaos() && hrand(2) == 0 && items[treasureType(oi.l)] * landMultiplier(oi.l) >= (11+hrand(15)))
|
|
ch = 0;
|
|
int tc = items[treasureType(oi.l)] * landMultiplier(oi.l);
|
|
int tcmin = treasureForLocal();
|
|
if(inv::on) {
|
|
if(!(oi.flags & orbgenflags::OSM_LOCAL25))
|
|
tc = 0;
|
|
else
|
|
tcmin = 25;
|
|
}
|
|
if(ch == 0 && tc >= tcmin) {
|
|
// 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 && ch < 11) && getOLR(itShard, l) == olrPrize25 && l != laRedRock && l != laWhirlwind)
|
|
buildPrizeMirror(c, 10);
|
|
else if(oi.gchance && (ch >= 11 && ch < 11+PRIZEMUL))
|
|
placePrizeOrb(c);
|
|
}
|
|
}
|
|
|
|
EX void placeLocalSpecial(cell *c, int outof, int loc IS(1), int priz IS(1)) {
|
|
if(safety || daily::on || extra_safety_for_memory(c) || peace::on) return;
|
|
int i = hrand(outof);
|
|
if(i < loc && items[treasureType(c->land)] >= treasureForLocal() && !inv::on)
|
|
c->item = nativeOrbType(c->land);
|
|
else if(i >= loc && i < loc + PRIZEMUL * priz)
|
|
placePrizeOrb(c);
|
|
}
|
|
|
|
EX void placeCrossroadOrbs(cell *c) {
|
|
if(racing::on) return;
|
|
if(peace::on) return;
|
|
if(daily::on) return;
|
|
for(auto& oi: orbinfos) {
|
|
if(!(oi.flags & orbgenflags::CROSS10)) continue;
|
|
if(!oi.gchance) continue;
|
|
|
|
int treas = items[treasureType(oi.l)] * landMultiplier(oi.l);
|
|
int mintreas = 10;
|
|
|
|
if(inv::on) {
|
|
if(oi.flags & orbgenflags::OSM_CROSS25)
|
|
mintreas = 25;
|
|
else if(oi.flags & orbgenflags::OSM_CROSS50)
|
|
mintreas = 50;
|
|
else continue;
|
|
}
|
|
|
|
if(ls::single() && isCrossroads(specialland)) {
|
|
if(oi.flags & orbgenflags::NO_TACTIC)
|
|
continue;
|
|
else mintreas = 0;
|
|
}
|
|
if(treas < mintreas) continue;
|
|
|
|
if(oi.orb == itOrbSafety && c->land == laCrossroads5) continue;
|
|
int mul = c->land == laCrossroads5 ? 10 : 1;
|
|
int gch = oi.gchance;
|
|
if(!inv::on) gch /= orbcrossfun(treas); else gch /= 2;
|
|
if(hrand(gch) >= mul) continue;
|
|
if(hrand(50+items[itHyperstone]) >= 50) continue;
|
|
c->item = oi.orb;
|
|
if(oi.orb == itOrbWater && c->land != laOcean) c->wall = waStrandedBoat;
|
|
}
|
|
}
|
|
|
|
EX void placeOceanOrbs(cell *c) {
|
|
if(peace::on) return;
|
|
for(auto& oi: orbinfos) {
|
|
if(!(oi.flags & orbgenflags::CROSS10)) continue;
|
|
|
|
int treas = items[treasureType(oi.l)] * landMultiplier(oi.l);
|
|
int mintreas = 10;
|
|
|
|
if(inv::on) {
|
|
if(oi.flags & orbgenflags::OSM_CROSS25)
|
|
mintreas = 25;
|
|
else if(oi.flags & orbgenflags::OSM_CROSS50)
|
|
mintreas = 50;
|
|
else continue;
|
|
}
|
|
|
|
if(treas < mintreas) continue;
|
|
if(!oi.gchance) continue;
|
|
if(oi.orb == itOrbLife) continue; // useless
|
|
if(hrand(oi.gchance) >= 20) continue;
|
|
c->item = oi.orb;
|
|
}
|
|
}
|
|
|
|
}
|