1 // Hyperbolic Rogue - Flags
2 
3 // Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
4 
5 /** \file flags.cpp
6  *  \brief Implementation of various simple flags for lands, items, monsters, and walls.
7  */
8 
9 #include "hyper.h"
10 namespace hr {
11 
12 #if HDR
classflag(eItem it)13 const inline flagtype& classflag(eItem it) { return iinf[it].flags; }
classflag(eWall w)14 const inline flagtype& classflag(eWall w) { return winf[w].flags; }
classflag(eMonster m)15 const inline flagtype& classflag(eMonster m) { return minf[m].flags; }
classflag(eLand l)16 const inline flagtype& classflag(eLand l) { return linf[l].flags; }
17 
18 #define ANYFLAGCHECK(name, cond, field, enum) inline bool name(enum w) { flagtype flag = classflag(w); return cond; } inline bool name(cell *c) { return name(c->field); }
19 
20 #define MONFLAGCHECK(name, cond) ANYFLAGCHECK(name, cond, monst, eMonster)
21 #define WALLFLAGCHECK(name, cond) ANYFLAGCHECK(name, cond, wall, eWall)
22 #define ITEMFLAGCHECK(name, cond) ANYFLAGCHECK(name, cond, item, eItem)
23 #define LANDFLAGCHECK(name, cond) ANYFLAGCHECK(name, cond, land, eLand)
24 
25 WALLFLAGCHECK(isWatery, flag & WF_WATER)
26 WALLFLAGCHECK(isBoat, flag & WF_BOAT)
27 WALLFLAGCHECK(isChasmy, flag & WF_CHASM)
28 WALLFLAGCHECK(isWateryOrBoat, (flag & WF_WATER) || w == waBoat)
29 WALLFLAGCHECK(isNoFlight, flag & WF_NOFLIGHT)
30 WALLFLAGCHECK(isFire, flag & WF_FIRE)
31 WALLFLAGCHECK(isThumper, flag & WF_THUMPER)
32 WALLFLAGCHECK(isActivable, flag & WF_ACTIVABLE)
33 WALLFLAGCHECK(hasTimeout, flag & WF_TIMEOUT)
34 WALLFLAGCHECK(isOnCIsland, flag & WF_CISLAND)
35 WALLFLAGCHECK(cellHalfvine, flag & WF_HALFVINE)
36 WALLFLAGCHECK(isAlch, flag & WF_ALCHEMY)
37 WALLFLAGCHECK(isAlchAny, flag & WF_ALCHEMY)
38 WALLFLAGCHECK(realred, flag & WF_RED)
39 WALLFLAGCHECK(isWall, flag & WF_WALL)
40 WALLFLAGCHECK(isPushable, flag & WF_PUSHABLE)
41 WALLFLAGCHECK(conegraphtype, flag & WF_CONE)
42 WALLFLAGCHECK(isStandardTree, flag & WF_STDTREE)
43 WALLFLAGCHECK(isGrave, flag & WF_GRAVE)
44 WALLFLAGCHECK(isReptile, flag & WF_REPTILE)
45 WALLFLAGCHECK(useHeatColoring, flag & WF_HEATCOLOR)
46 WALLFLAGCHECK(isThorny, flag & WF_THORNY)
47 
48 LANDFLAGCHECK(generateAll, flag & LF_GENERATE_ALL)
49 LANDFLAGCHECK(isIcyLand, flag & LF_ICY)
50 LANDFLAGCHECK(isGravityLand, flag & LF_GRAVITY)
51 LANDFLAGCHECK(isEquidLand, flag & LF_EQUI)
52 LANDFLAGCHECK(isWarpedType, flag & LF_WARPED)
53 LANDFLAGCHECK(isCyclic, flag & LF_CYCLIC)
54 LANDFLAGCHECK(isTechnicalLand, flag & LF_TECHNICAL)
55 LANDFLAGCHECK(is_mirrorland, flag & LF_MIRROR)
56 LANDFLAGCHECK(isSealand, flag & LF_SEA)
57 LANDFLAGCHECK(isCoastal, flag & LF_COASTAL)
58 LANDFLAGCHECK(isPureSealand, flag & LF_PURESEA)
59 LANDFLAGCHECK(isElemental, flag & LF_ELEMENTAL)
60 LANDFLAGCHECK(isHaunted, flag & LF_HAUNTED)
61 LANDFLAGCHECK(isTrollLand, flag & LF_TROLL)
62 LANDFLAGCHECK(inmirror, flag & LF_INMIRROR)
63 LANDFLAGCHECK(inmirrororwall, flag & LF_INMIRRORORWALL)
64 LANDFLAGCHECK(isElectricLand, flag & LF_ELECTRIC)
65 
66 MONFLAGCHECK(isGhostable, !(flag & CF_NOGHOST))
67 MONFLAGCHECK(isRaider, flag & CF_RAIDER)
68 MONFLAGCHECK(isMimic, flag & CF_MIMIC)
69 MONFLAGCHECK(isPrincess, flag & CF_PRINCESS)
70 MONFLAGCHECK(isGolemOrKnight, flag & CF_GOK)
71 MONFLAGCHECK(isNonliving, flag & CF_NONLIVING)
72 MONFLAGCHECK(isMetalBeast, flag & CF_METAL)
73 MONFLAGCHECK(isStunnable, flag & CF_STUNNABLE)
74 MONFLAGCHECK(hasHitpoints, flag & CF_HP)
75 MONFLAGCHECK(isMountable, flag & CF_MOUNTABLE)
76 MONFLAGCHECK(isFriendlyType, flag & CF_FRIENDLY)
77 MONFLAGCHECK(isFriendlyOrPlayer, flag & (CF_FRIENDLY | CF_PLAYER))
78 MONFLAGCHECK(isBug, flag & CF_BUG)
79 MONFLAGCHECK(isIvy, flag & CF_IVY)
80 MONFLAGCHECK(isMonsterPart, flag & CF_PART)
81 MONFLAGCHECK(isMutantIvy, flag & CF_MUTANTIVY)
82 MONFLAGCHECK(isAnyIvy, flag & CF_ANYIVY)
83 MONFLAGCHECK(isBulletType, flag & CF_BULLET)
84 MONFLAGCHECK(isDemon, flag & CF_DEMON)
85 MONFLAGCHECK(isWorm, flag & CF_WORM)
86 MONFLAGCHECK(isWitch, flag & CF_WITCH)
87 MONFLAGCHECK(isAngryBird, (flag & CF_BIRD) && !(flag & CF_FRIENDLY))
88 MONFLAGCHECK(isBird, flag & CF_BIRD)
89 MONFLAGCHECK(slowMover, flag & CF_SLOWMOVER)
90 MONFLAGCHECK(isMagneticPole, flag & CF_MAGNETIC)
91 MONFLAGCHECK(isSwitch, flag & CF_SWITCH)
92 MONFLAGCHECK(isGhost, flag & CF_GHOST)
93 MONFLAGCHECK(isShark, flag & CF_SHARK)
94 MONFLAGCHECK(isSlimeMover, flag & CF_SLIME)
95 MONFLAGCHECK(isDragon, flag & CF_DRAGON)
96 MONFLAGCHECK(isKraken, flag & CF_KRAKEN)
97 MONFLAGCHECK(isBlowableMonster, !(flag & CF_NOBLOW))
98 MONFLAGCHECK(isMultitile, flag & CF_MULTITILE)
99 MONFLAGCHECK(isLeader, flag & CF_LEADER)
100 MONFLAGCHECK(isFlyingType, flag & CF_FLYING)
101 MONFLAGCHECK(attackThruVine, flag & CF_ATTACK_THRU_VINE)
102 MONFLAGCHECK(attackNonAdjacent, flag & CF_ATTACK_NONADJACENT)
103 MONFLAGCHECK(noHighlight, flag & CF_NOHIGHLIGHT)
104 MONFLAGCHECK(isInactiveEnemyType, flag & CF_INACTIVE)
105 MONFLAGCHECK(isUnarmed, flag & CF_UNARMED)
106 MONFLAGCHECK(ignoresPlatesType, flag & CF_IGNORE_PLATE)
107 MONFLAGCHECK(isBull, flag & CF_BULL)
108 MONFLAGCHECK(isTroll, flag & CF_TROLL)
109 MONFLAGCHECK(ignoresSmellType, flag & CF_IGNORE_SMELL)
110 MONFLAGCHECK(isRatling, flag & CF_RATLING)
111 MONFLAGCHECK(isGhostMover, flag & CF_GHOSTMOVER)
112 MONFLAGCHECK(isPowerMonster, flag & CF_POWER)
113 MONFLAGCHECK(hasFacing, flag & CF_FACING)
114 
115 ITEMFLAGCHECK(isElementalShard, flag & IF_SHARD)
116 ITEMFLAGCHECK(itemBurns, !(flag & IF_FIREPROOF))
117 ITEMFLAGCHECK(isProtectionOrb, flag & IF_PROTECTION)
118 ITEMFLAGCHECK(isEmpathyOrb, flag & IF_EMPATHY)
119 ITEMFLAGCHECK(isRangedOrb, flag & IF_RANGED)
120 ITEMFLAGCHECK(isRevivalOrb, flag & IF_REVIVAL)
121 #endif
122 
123 eMonster movegroup(eMonster m);
124 
125 // watery
126 
boatStrandable(cell * c)127 EX bool boatStrandable(cell *c) {
128   return c->wall == waNone && (c->land == laLivefjord || c->land == laOcean);
129   }
130 
131 // monster/wall types
132 
isFireOrMagma(cell * w)133 EX bool isFireOrMagma(cell *w) {
134   return isFire(w) || w->wall == waMagma;
135   }
136 
mirrorcolor(bool mirrored)137 EX int mirrorcolor(bool mirrored) {
138   return winf[mirrored ? waMirror : waCloud].color;
139   }
140 
isMounted(cell * c)141 EX bool isMounted(cell *c) {
142   if(c && c->monst && c->monst != moTentacleGhost && isMountable(c->monst)) {
143     for(int i: player_indices()) {
144       if(playerpos(i)->monst && sameMonster(c, playerpos(i)))
145         return true;
146       if(lastmountpos[i] && lastmountpos[i]->monst && sameMonster(c, lastmountpos[i]))
147         return true;
148       }
149     }
150   return false;
151   }
152 
itemclass(eItem it)153 EX int itemclass(eItem it) { return iinf[it].itemclass; }
154 
isFriendly(eMonster m)155 EX bool isFriendly(eMonster m) { return isFriendlyType(m); }
156 
isFriendly(cell * c)157 EX bool isFriendly(cell *c) {
158   return isMounted(c) || isFriendly(c->monst);
159   }
160 
get_threat_level(cell * c)161 EX eThreatLevel get_threat_level(cell *c) {
162   if(!c->monst) return tlNoThreat;
163   if(isFriendly(c)) return tlNoThreat;
164   if(classflag(c->monst) & CF_HIGH_THREAT) return tlHighThreat;
165   if(classflag(c->monst) & CF_SPAM) return tlSpam;
166   return tlNormal;
167   }
168 
isFriendlyOrBug(cell * c)169 EX bool isFriendlyOrBug(cell *c) { // or killable discord!
170   // do not attack the stunned Princess
171   if(isPrincess(c->monst) && c->stuntime) return false;
172   return isFriendly(c) || isBug(c) || (c->monst && markOrb(itOrbDiscord) && !c->stuntime);
173   }
174 
cellUnstable(cell * c)175 EX bool cellUnstable(cell *c) {
176   return (c->land == laMotion && c->wall == waNone) || c->wall == waTrapdoor;
177   }
178 
cellUnstableOrChasm(cell * c)179 EX bool cellUnstableOrChasm(cell *c) {
180   return
181     (c->land == laMotion && c->wall == waNone) ||
182     c->wall == waChasm || c->wall == waTrapdoor;
183   }
184 
elementalOf(eLand l)185 EX eMonster elementalOf(eLand l) {
186   if(l == laEFire) return moFireElemental;
187   if(l == laEWater) return moWaterElemental;
188   if(l == laEAir) return moAirElemental;
189   if(l == laEEarth) return moEarthElemental;
190   return moNone;
191   }
192 
localshardof(eLand l)193 EX eItem localshardof(eLand l) {
194   return eItem(itFireShard + (l - laEFire));
195   }
196 
snakelevel(cell * c)197 EX int snakelevel(cell *c) {
198 #if CAP_COMPLEX2
199   if(c->land == laBrownian && among(c->wall, waNone, waMineMine, waFire)) return min(c->landparam / brownian::level, 3);
200 #endif
201   return winf[c->wall].snakelevel;
202   }
203 
204 // from-to
205 
slimegroup(cell * c)206 EX eSlimegroup slimegroup(cell *c) {
207   return winf[c->wall].sg;
208   }
209 
isFlying(eMonster m)210 EX bool isFlying(eMonster m) {
211   return isFlyingType(m) || checkOrb(m, itOrbAether);
212   }
213 
survivesChasm(eMonster m)214 EX bool survivesChasm(eMonster m) {
215   return isFlying(m);
216   }
217 
ignoresPlates(eMonster m)218 EX bool ignoresPlates(eMonster m) {
219   return ignoresPlatesType(m) || isFlying(m);
220   }
221 
isInactiveEnemy(cell * w,eMonster forwho)222 EX bool isInactiveEnemy(cell *w, eMonster forwho) {
223   if(forwho != moPlayer) {
224     if(w->monst == moGreaterM || w->monst == moLesserM) return false;
225     if(w->monst == moGreater || w->monst == moLesser) return true;
226     }
227   if(isInactiveEnemyType(w->monst)) return true;
228   if(w->monst && ((forwho == moPlayer) ? realstuntime(w) : realstuntime(w) > 1) && !isFriendly(w))
229     return true;
230   return false;
231   }
232 
233 // forpc = true (for PC), false (for golems)
isActiveEnemy(cell * w,eMonster forwho)234 EX bool isActiveEnemy(cell *w, eMonster forwho) {
235   if(((forwho == moPlayer) ? realstuntime(w) : realstuntime(w) > 1))
236     return false;
237   if(w->monst == passive_switch) return false;
238   if(w->monst == moNone) return false;
239   if(isFriendly(w)) return false;
240   if(isInactiveEnemy(w, forwho)) return false;
241   return true;
242   }
243 
isArmedEnemy(cell * w,eMonster forwho)244 EX bool isArmedEnemy(cell *w, eMonster forwho) {
245   return w->monst != moCrystalSage && w->monst != moCrusher && isActiveEnemy(w, forwho);
246   }
247 
eternalFire(cell * c)248 EX bool eternalFire(cell *c) {
249   return c->land == laDryForest || (c->land == laPower && !smallbounded) || c->land == laMinefield ||
250     c->land == laEFire || c->land == laElementalWall;
251   }
252 
haveRangedOrb()253 EX bool haveRangedOrb() {
254   return
255     items[itOrbPsi] || items[itOrbDragon] || items[itOrbTeleport] ||
256     items[itOrbIllusion] || items[itOrbSpace] || items[itOrbAir] ||
257     items[itOrbFrog] || items[itOrbSummon] || items[itOrbMatter] ||
258     items[itRevolver] || items[itOrbStunning] || items[itStrongWind] ||
259     items[itOrbDomination] || items[itOrbNature] || items[itOrbDash] ||
260     items[itOrbMorph] || items[itOrbPhasing];
261   }
262 
isFriendlyGhost(eMonster m)263 EX bool isFriendlyGhost(eMonster m) {
264   return m == moFriendlyGhost || (markEmpathy(itOrbAether) && isFriendly(m));
265   }
266 
isGhostAether(eMonster m)267 EX bool isGhostAether(eMonster m) {
268   return isGhost(m) || checkOrb(m, itOrbAether);
269   }
270 
survivesWater(eMonster m)271 EX bool survivesWater(eMonster m) {
272   return
273     m == moShark || m == moGreaterShark || m == moCShark ||
274     isGhostAether(m) || m == moWitchGhost || m == moShadow ||
275     isBird(m) || m == moWaterElemental || m == moAirElemental ||
276     isWorm(m) || isIvy(m) || isDragon(m) || isKraken(m) ||
277     m == moMutant || m == moFriendlyIvy ||
278     checkOrb(m, itOrbFish) ||
279     among(m, moPike, moRusalka) ||
280     m == moTortoise; // Tortoises and Ivies survive, but don't go through water
281   }
282 
283 // survives Mercury or Sulphur or Lava
survivesPoison(eMonster m,eWall p)284 EX bool survivesPoison(eMonster m, eWall p) {
285   return
286     isGhostAether(m) || m == moWitchGhost || m == moShadow ||
287     isBird(m) || m == moAirElemental || isDragon(m) || isWorm(m);
288   }
289 
290 // flying even if stunned
isPermanentFlying(eMonster m)291 EX bool isPermanentFlying(eMonster m) {
292   return m == moAirElemental || isGhostAether(m);
293   }
294 
isLuckyLand(eLand l)295 EX bool isLuckyLand(eLand l) {
296   return among(l, laIce, laDesert, laDeadCaves, laOvergrown, laDice);
297   }
298 
survivesFire(eMonster m)299 EX bool survivesFire(eMonster m) {
300   return
301     isGhostAether(m) || m == moWitchWinter || m == moWitchGhost ||
302     m == moBomberbird || m == moTameBomberbird || m == moTameBomberbirdMoved ||
303     checkOrb(m, itOrbWinter) || m == moFireElemental ||
304     isDragon(m) || m == moShadow;
305   }
306 
survivesWall(eMonster m)307 EX bool survivesWall(eMonster m) {
308   return isGhostAether(m);
309   }
310 
survivesThorns(eMonster m)311 EX bool survivesThorns(eMonster m) {
312   return isGhostAether(m) || m == moSkeleton || m == moDraugr;
313   }
314 
survivesFall(eMonster m)315 EX bool survivesFall(eMonster m) {
316   return isBird(m) || m == moAirElemental || m == moSkeleton || isDragon(m) || m == moShadow || isGhostAether(m);
317   }
318 
checkOrb(eMonster m1,eItem orb)319 EX bool checkOrb(eMonster m1, eItem orb) {
320   if(m1 == moPlayer) return markOrb(orb);
321   if(isFriendly(m1)) return markEmpathy(orb);
322   return false;
323   }
324 
checkOrb2(eMonster m1,eItem orb)325 EX bool checkOrb2(eMonster m1, eItem orb) {
326   if(m1 == moPlayer) return markOrb2(orb);
327   if(isFriendly(m1)) return markEmpathy2(orb);
328   return false;
329   }
330 
ignoresSmell(eMonster m)331 EX bool ignoresSmell(eMonster m) {
332   return ignoresSmellType(m) || checkOrb(m, itOrbBeauty) || checkOrb(m, itOrbAether) || checkOrb(m, itOrbShield);
333   }
334 
highwall(cell * c)335 EX bool highwall(cell *c) {
336   if(c->wall == waGlass) return false;
337   if(wmescher && wmspatial && c->wall == waBarrier && c->land == laOceanWall)
338     return false;
339   // if(wmspatial && isTree(c)) return false;
340   if(isGrave(c->wall)) return true;
341   if(c->wall == waMirrorWall) return false;
342   if(c->wall == waEditStatue) return false;
343   return winf[c->wall].glyph == '#' || c->wall == waClosedGate;
344   }
345 
chasmgraph(cell * c)346 EX int chasmgraph(cell *c) {
347   if(c->wall == waChasm || c->wall == waInvisibleFloor) return 2;
348   if(isChasmy(c)) return 1;
349   if(isWateryOrBoat(c)) return 1;
350   if(c->wall == waShallow) return 1;
351   if(wmescher && c->wall == waBarrier && c->land == laOceanWall) return 1;
352   if(c->wall == waReptileBridge) return 1;
353   return 0;
354   }
355 
conegraph(cell * c)356 EX bool conegraph(cell *c) {
357   return ((wmescher && wmspatial) || wmascii3) && (conegraphtype(c) || (c->wall == waBarrier && c->land == laOceanWall));
358   }
359 
360 /** Determine the power of a frog monster. Also used to determine whether monster is a frog. */
frog_power(eMonster m)361 EX eItem frog_power(eMonster m) {
362   if(m == moFrog) return itOrbFrog;
363   if(m == moPhaser) return itOrbPhasing;
364   if(m == moVaulter) return itOrbDash;
365   return itNone;
366   }
367 
hornStuns(cell * c)368 EX bool hornStuns(cell *c) {
369   eMonster m = c->monst;
370   return
371     m == moRagingBull || m == moSleepBull || m == moHerdBull ||
372     m == moButterfly || m == moGreater || m == moGreaterM || m == moDraugr ||
373     m == moHedge || m == moFlailer || m == moVizier || m == moReptile || m == moSalamander ||
374     m == moPair || m == moAltDemon || m == moHexDemon || m == moMonk || m == moCrusher ||
375     attackJustStuns(c, AF_NORMAL, moNone);
376   }
377 
378 /** changing this wall for whatever reason may cause the game to crash */
do_not_touch_this_wall(cell * c)379 EX bool do_not_touch_this_wall(cell *c) {
380   return among(c->wall, waMirrorWall, waBarrier, waRoundTable, waWarpGate);
381   }
382 
is_paired(eMonster m)383 EX bool is_paired(eMonster m) {
384   return among(m, moPair, moNorthPole, moSouthPole);
385   }
386 
isDie(eMonster m)387 EX bool isDie(eMonster m) {
388   return among(m, moAnimatedDie, moAngryDie);
389   }
390 
isDie(eWall w)391 EX bool isDie(eWall w) {
392   return among(w, waRichDie, waHappyDie);
393   }
394 
395 }
396