1 // Hyperbolic Rogue -- orb generation routines
2 // Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
3 
4 /** \file orbgen.cpp
5  *  \brief Orb generation: which orbs in which lands, functions generating prize/local orbs, etc.
6  */
7 
8 #include "hyper.h"
9 namespace hr {
10 // orbgen flags
11 
12 #if HDR
13 enum eOrbLandRelation {
14   olrForbidden, // never appears: forbidden
15   olrDangerous, // never appears: would be dangerous
16   olrUseless,   // never appears: useless here
17   olrNoPrizes,  // no prizes in this land
18   olrNoPrizeOrb,// orb not allowed as a prize
19   olrPrize25,   // prize for collecting 25
20   olrPrize3,    // prize for collecting 3
21   olrNative,    // native orb in this land
22   olrNative1,   // native orb in this land (1)
23   olrGuest,     // extra orb in this land
24   olrPNative,   // Land of Power: native
25   olrPBasic,    // Land of Power: basic orbs
26   olrPPrized,   // Land of Power: prized orbs
27   olrPNever,    // Land of Power: foreign orbs
28   olrHub,       // hub lands
29   olrMonster,   // available from a monster
30   olrAlways,    // always available
31   olrBurns      // burns
32   };
33 #endif
34 
35 #if HDR
36 namespace orbgenflags {
37   // generates in the given land from 10 treasures, in the classic mode
38   static const int LOCAL10 = 1;
39   // generates in the Crossroads from 10 treasures, in the classic mode
40   static const int CROSS10 = 2;
41   // generates in other places from 25 treasures, in the classic mode
42   static const int GLOBAL25 = 4;
43   // in OSM you get it once at 10 treasures
44   static const int OSM_AT10 = 8;
45 
46 
47   // 'native' functions return this
48   static const int NATIVE = 64;
49   // 'local' orb will be also placed in OSM (at 25 treasures); needs LOCAL10
50   static const int OSM_LOCAL25 = 128;
51   // 'crossroads' orb will be also placed in OSM (at 50 treasures)
52   static const int OSM_CROSS50 = 256;
53   // 'crossroads' orb will be also placed in OSM (at 25 treasures)
54   static const int OSM_CROSS25 = 512;
55   // 'global' orb will be also placed in OSM (at 100 treasures)
56   static const int OSM_GLOBAL100 = 1024;
57   // do not create in the Crossroads in the tactics mode
58   static const int NO_TACTIC = (1<<11);
59   // guest Orb
60   static const int GUEST = (1<<12);
61 
62   // typical combinations
63   static const int S_NATIVE = LOCAL10 | CROSS10 | GLOBAL25 | NATIVE;
64   static const int S_GUEST  = LOCAL10 | OSM_AT10 | GUEST;
65   static const int S_YENDOR = S_NATIVE | OSM_LOCAL25 | OSM_CROSS50 | OSM_GLOBAL100 | NO_TACTIC;
66   static const int S_NAT_NT = S_NATIVE | NO_TACTIC;
67   static const int S_NA_O25 = S_NATIVE | OSM_CROSS25;
68   }
69 
70 struct orbinfo {
71   int flags;
72   eLand l;
73   int lchance;
74   int gchance;
75   eItem orb;
is_nativehr::orbinfo76   bool is_native() const { using namespace orbgenflags; return flags & NATIVE; }
77   };
78 #endif
79 
80 EX vector<orbinfo> orbinfos = {
81   {orbgenflags::S_NATIVE, laGraveyard, 200, 200,itGreenStone}, // must be first so that it does not reduce
82   {orbgenflags::S_NATIVE, laJungle, 1200, 1500,itOrbLightning},
83   {orbgenflags::S_NATIVE, laIce, 2000, 1500,itOrbFlash},
84   {orbgenflags::S_NATIVE, laCaves, 1800, 2000,itOrbLife},
85   {orbgenflags::S_NATIVE, laAlchemist, 800, 800,itOrbSpeed},
86   {orbgenflags::S_NATIVE, laDesert, 2500, 1500,itOrbShield},
87   {orbgenflags::S_YENDOR, laHell, 2000, 1000,itOrbYendor},
88   {orbgenflags::S_NATIVE, laRlyeh, 1500, 1500,itOrbTeleport},
89   {orbgenflags::S_NA_O25, laMotion, 2000, 700, itOrbSafety},
90   {orbgenflags::S_GUEST,  laIce, 1500, 0, itOrbWinter},
91   {orbgenflags::S_GUEST,  laDragon, 2500, 0, itOrbWinter},
92   {orbgenflags::S_GUEST,  laDryForest, 2500, 0, itOrbWinter},
93   {orbgenflags::S_NATIVE, laCocytus, 1500, 1500, itOrbMorph},
94   {orbgenflags::S_GUEST,  laCocytus, 1500, 0, itOrbWinter},
95   {orbgenflags::S_GUEST,  laCaves, 1200, 0, itOrbDigging},
96   {orbgenflags::S_NATIVE, laDryForest, 500, 4500, itOrbThorns},
97   {orbgenflags::S_GUEST,  laDeadCaves, 1800, 0, itGreenStone},
98   {orbgenflags::S_NAT_NT, laDeadCaves, 1800, 1500, itOrbDigging},
99   {orbgenflags::S_NATIVE, laEmerald, 1500, 3500, itOrbPsi},
100   {orbgenflags::S_NATIVE, laWineyard, 900, 1200, itOrbAether},
101   {orbgenflags::S_NATIVE, laHive, 800, 1200, itOrbInvis},
102   {orbgenflags::S_NATIVE, laPower, 0, 3000, itOrbFire},
103   {orbgenflags::S_NATIVE, laMinefield, 0, 3500, itOrbFriend},
104   {orbgenflags::S_NATIVE, laTemple, 0, 3000, itOrbDragon},
105   {orbgenflags::S_NATIVE, laCaribbean, 0, 3500, itOrbTime},
106   {orbgenflags::S_NATIVE, laRedRock, 0, 2500, itOrbSpace},
107   {orbgenflags::S_NATIVE, laCamelot, 1000, 1500, itOrbIllusion},
108   {orbgenflags::S_NATIVE, laOcean, 0, 1500, itOrbEmpathy},
109   {orbgenflags::S_GUEST,  laOcean, 0, 0, itOrbAir},
110   {orbgenflags::S_NATIVE, laPalace, 0, 4000, itOrbDiscord},
111   {orbgenflags::S_GUEST,  laPalace, 0, 0, itOrbFrog},
112   {orbgenflags::S_NATIVE, laZebra, 500, 2100, itOrbFrog},
113   {orbgenflags::S_NAT_NT, laLivefjord, 0, 1800, itOrbFish},
114   {orbgenflags::S_NAT_NT, laPrincessQuest, 0, 200, itOrbLove},
115   {orbgenflags::S_NATIVE, laIvoryTower, 500, 4000, itOrbMatter},
116   {orbgenflags::S_NAT_NT, laElementalWall, 1500, 4000, itOrbSummon},
117   {orbgenflags::S_NATIVE, laStorms, 1000, 2500, itOrbStunning},
118   {orbgenflags::S_NATIVE, laOvergrown, 1000, 800, itOrbWoods},
119   {orbgenflags::S_GUEST,  laOvergrown, 1000, 0, itOrbLuck},
120   {orbgenflags::S_NATIVE, laWhirlwind, 1250, 3000, itOrbAir},
121   {orbgenflags::S_NATIVE, laHaunted, 1000, 5000, itOrbUndeath},
122   {orbgenflags::S_NATIVE, laClearing, 5000, 5000, itOrbFreedom},
123   {orbgenflags::S_NATIVE, laRose, 2000, 8000, itOrbBeauty},
124   {orbgenflags::S_NATIVE, laWarpCoast, 2000, 8000, itOrb37},
125   {orbgenflags::S_NATIVE, laDragon, 500, 5000, itOrbDomination},
126   {orbgenflags::S_NATIVE, laTortoise, 2500, 1500, itOrbShell},
127   {orbgenflags::S_NATIVE, laEndorian, 150, 2500, itOrbEnergy},
128   {orbgenflags::S_GUEST,  laEndorian, 450, 0, itOrbTeleport},
129   {orbgenflags::S_NATIVE, laKraken, 500, 2500, itOrbSword},
130   {orbgenflags::S_NATIVE, laBurial, 500, 2500, itOrbSword2},
131   {orbgenflags::S_NATIVE, laTrollheim, 750, 1800, itOrbStone},
132   {orbgenflags::S_NATIVE, laMountain, 400, 3500, itOrbNature},
133   {orbgenflags::S_NATIVE, laDungeon, 120, 2500, itOrbRecall},
134   {orbgenflags::S_NATIVE, laReptile, 500, 2100, itOrbDash},
135   {orbgenflags::S_NATIVE, laBull, 720, 3000, itOrbHorns},
136   {orbgenflags::S_NATIVE, laPrairie, 0, 3500, itOrbBull},
137   {orbgenflags::S_GUEST,  laWhirlpool, 0, 0, itOrbSafety},
138   {orbgenflags::S_NATIVE, laVolcano, 0, 7000, itOrbLava},
139   {orbgenflags::S_NATIVE, laHunting, 0, 2500, itOrbSide3},
140   {orbgenflags::S_NATIVE, laBlizzard, 0, 2000, itOrbWinter},
141   {orbgenflags::S_NATIVE, laTerracotta, 800, 2500, itOrbSide1},
142   {orbgenflags::S_NATIVE, laDual, 600, 2500, itOrbSide2},
143   {orbgenflags::S_GUEST, laSnakeNest, 2000, 0, itOrbDomination},
144   {orbgenflags::S_GUEST, laDocks, 3000, 0, itOrbFish},
145   {orbgenflags::S_GUEST, laDocks, 3000, 0, itOrbDragon},
146   {orbgenflags::S_GUEST, laDocks, 3000, 0, itOrbDash},
147   {orbgenflags::S_GUEST, laSwitch, 2000, 0, itOrbSpace},
148   {orbgenflags::S_NATIVE, laSwitch, 2000, 3000, itOrbPhasing},
149   {orbgenflags::S_NATIVE, laMagnetic, 2000, 3000, itOrbMagnetism},
150   {orbgenflags::S_NATIVE, laRuins, 1200, 2500, itOrbSlaying},
151   {orbgenflags::S_NATIVE, laWestWall, 2000, 4200, itOrbGravity},
152   {orbgenflags::S_NATIVE, laVariant, 900, 4200, itOrbIntensity},
153   {orbgenflags::S_GUEST, laVariant, 4000, 0, itOrbFire},
154   {orbgenflags::S_GUEST, laVariant, 4000, 0, itOrbWinter},
155   {orbgenflags::S_GUEST, laVariant, 4000, 0, itOrbPhasing},
156   {orbgenflags::S_GUEST, laVariant, 4000, 0, itOrbDash},
157   {orbgenflags::S_NATIVE, laBrownian, 900, 4200, itOrbChoice},
158   {orbgenflags::S_GUEST, laBrownian, 1000, 0, itOrbAir},
159   {orbgenflags::S_GUEST, laBrownian, 1000, 0, itOrbDash},
160   {orbgenflags::S_GUEST, laBrownian, 1000, 0, itOrbSafety},
161   {orbgenflags::S_NATIVE, laFrog, 1000, 1500, itOrbImpact},
162   {orbgenflags::S_NATIVE, laEclectic, 1000, 1000, itOrbChaos},
163   {orbgenflags::S_GUEST, laEclectic, 4000, 0, itOrbWinter},
164   {orbgenflags::S_GUEST, laEclectic, 2000, 0, itOrbLightning},
165   {orbgenflags::S_NATIVE, laWet, 800, 2500, itOrbPlague},
166   {orbgenflags::S_GUEST, laWet, 1200, 0, itOrbFish},
167   {orbgenflags::S_GUEST, laWet, 1200, 0, itOrbAether},
168   {orbgenflags::S_GUEST, laWet, 1200, 0, itOrbFrog},
169   {orbgenflags::S_NATIVE, laCursed, 400, 1500, itOrbPurity},
170   {orbgenflags::S_NAT_NT, laDice, 500, 800, itOrbLuck},
171   {orbgenflags::S_GUEST, laDice, 750, 0, itOrbAir},
172   {orbgenflags::S_NATIVE, laWhirlpool, 0, 2000, itOrbWater}, // needs to be last
173   };
174 
nativeOrbType(eLand l)175 EX eItem nativeOrbType(eLand l) {
176   if(isElemental(l)) l = laElementalWall;
177   if(inv::on && (l == laMirror || l == laMirrorOld || isCrossroads(l)))
178     return itOrbMirror;
179   if(l == laMirror || l == laMirrorOld) return itShard;
180   for(auto& oi: orbinfos)
181     if(oi.l == l && oi.is_native())
182       return oi.orb;
183   return itNone;
184   }
185 
getNativityOrbInfo(eItem orb)186 const orbinfo& getNativityOrbInfo(eItem orb) {
187   for(auto& oi: orbinfos)
188     if(oi.orb == orb && oi.is_native())
189       return oi;
190   static orbinfo oi;
191   oi.l = laMirror;
192   return oi;
193   }
194 
195 EX string olrDescriptions[18] = {
196   "forbidden to find in %the1",
197   "too dangerous to use in %the1",
198   "useless in %the1",
199   "only native Orbs allowed in %the1",
200   "this Orb is never unlocked globally (only hubs)",
201   "collect 25 %2 to unlock it in %the1",
202   "collect 3 %2 to unlock it in %the1",
203   "native to %the1 (collect 10 %2)",
204   "native to %the1 (collect 1 %2)",
205   "secondary in %the1 (collect 10 %3, or 25 %2)",
206   "the native Orb of %the1",
207   "this Orb appears on floors and is used by witches",
208   "a prized Orb, it appears only in cabinets",
209   "this Orb never appears in %the1",
210   "Hub Land: orbs appear here if unlocked in their native land",
211   "kill a monster, or collect 25 %2",
212   "always available",
213   "would be destroyed in %the1"
214   };
215 
getOLR(eItem it,eLand l)216 EX eOrbLandRelation getOLR(eItem it, eLand l) {
217 
218   if(l == laMotion && it == itOrbChoice && !inv::on) return olrUseless;
219 
220   if(l == laPower) {
221     if(it == itOrbFire) return olrPNative;
222 
223     if(
224       it == itOrbFlash || it == itOrbSpeed || it == itOrbWinter || it == itOrbAether ||
225       it == itOrbLife) return olrPBasic;
226 
227     if(
228       it == itOrbLightning || it == itOrbThorns || it == itOrbInvis ||
229       it == itOrbShield || it == itOrbTeleport || it == itOrbPsi ||
230       it == itOrbDragon || it == itOrbIllusion || it == itOrbTime)
231         return olrPPrized;
232 
233     return olrPNever;
234     }
235 
236   if(it == itOrbWater && l == laWestWall) return olrUseless;
237   if(it == itOrbSafety && l == laCrossroads5) return olrDangerous;
238   if(it == itOrbFire && l == laKraken) return olrUseless;
239   if(it == itOrbDragon && l == laKraken) return olrUseless;
240   if(it == itOrbDigging && l == laKraken) return olrUseless;
241   if(it == itOrbIllusion && l == laKraken) return olrUseless;
242 
243   if(it == itOrbYendor && among(l, laWhirlwind, laWestWall)) return olrUseless;
244 
245   if(it == itOrbLife && (l == laKraken)) return olrUseless;
246 
247   if(l == laVolcano && it == itOrbSafety) return olrDangerous;
248   if(l == laVariant && it == itOrbSafety) return olrDangerous;
249   if(l == laVolcano && itemBurns(it)) return olrBurns;
250 
251   if(it == itOrbAir && l == laAlchemist) return olrUseless;
252   // if(it == itOrbShield && l == laMotion) return olrUseless;
253 
254   if(it == itOrbIllusion && l == laCamelot) return olrNative1;
255   if(it == itOrbLove) return olrNoPrizeOrb;
256   if(nativeOrbType(l) == it) return olrNative;
257 
258   for(const orbinfo& oi: orbinfos)
259     if((oi.flags & orbgenflags::GUEST) && oi.l == l && oi.orb == it)
260       return olrGuest;
261 
262   if(it == itOrbLuck && l == laIvoryTower)
263     return olrUseless;
264   if(it == itOrbLuck && l == laEndorian)
265     return olrUseless;
266   if(it == itOrbLuck && l == laDungeon)
267     return olrUseless;
268   if(it == itOrbWater && l == laRedRock)
269     return olrUseless;
270   if(it == itOrbLuck && l == laTortoise)
271     return olrUseless;
272   if(it == itOrbLuck && l == laMountain)
273     return olrUseless;
274   if(it == itOrbLuck && l == laWestWall)
275     return olrUseless;
276    if(it == itOrbLuck && l == laCamelot)
277     return olrUseless;
278    if(it == itOrbLuck && l == laHaunted)
279     return olrUseless;
280    if(it == itOrbNature && l == laWineyard)
281     return olrDangerous;
282   if((it == itOrbFrog || it == itOrbDiscord) && l == laPrincessQuest)
283     return olrGuest;
284   if(it == itOrbDragon && l == laRlyeh)
285     return olrMonster;
286 
287   if(it == itOrbSafety && l == laWhirlpool)
288     return olrAlways;
289   if(it == itOrbSafety && l == laPrairie)
290     return olrAlways;
291   if(it == itGreenStone && isHaunted(l))
292     return olrAlways;
293   if(it == itOrbWater && l == laLivefjord)
294     return olrMonster;
295   if(isCrossroads(l) || l == laOcean)
296     return olrHub;
297 
298   if(it == itOrbSlaying && !among(l,
299     laMirror, laHell, laEmerald, laDryForest, laCamelot, laPalace, laStorms, laRose, laTortoise, laBurial, laDungeon, laReptile,
300     laPrairie, laBull, laVolcano, laTerracotta, laRuins, laVariant, laEclectic, laBrownian, laCursed))
301     return olrUseless;
302 
303   if(l == laCocytus)
304     if(it == itOrbDragon || it == itOrbFire || it == itOrbFlash || it == itOrbLightning)
305       return olrDangerous;
306 
307   if(it == itOrbSafety)
308     if(l == laCaves || l == laLivefjord || l == laRedRock || l == laCocytus || l == laHell ||
309       l == laDesert || l == laAlchemist || l == laDeadCaves || l == laMinefield || isHaunted(l) ||
310       l == laDragon || l == laWet || l == laCursed)
311       return olrDangerous;
312 
313   if(it == itOrbMatter)
314     if(among(l, laCaves, laEmerald, laAlchemist, laCaribbean, laMinefield, laCocytus, laWestWall))
315       return olrUseless;
316 
317   if(l == laPrincessQuest)
318     if(among(it, itOrbAether, itOrbFlash, itOrbTeleport, itOrbSummon, itOrbFreedom, itOrbFriend, itOrbPhasing, itOrbChaos))
319       return olrForbidden;
320 
321   if(l == laTemple)
322     return olrNoPrizes;
323 
324   if(it == itOrbDigging) {
325     if(among(l, laCaves, laOcean, laLivefjord, laEmerald, laDesert, laDeadCaves, laRedRock, laCaribbean, laGraveyard,
326       laMountain, laHunting, laWarpSea, laWarpCoast, laCursed))
327         return olrPrize25;
328     return olrUseless;
329     }
330 
331   if(it == itOrbWoods && (l == laWarpSea || l == laWarpCoast))
332     return olrDangerous;
333 
334   if(it == itOrbWoods && !among(l,
335     laDryForest, laWineyard, laCaribbean, laOvergrown, laHaunted, laHauntedWall, laHauntedBorder, laTortoise, laFrog, laEclectic,
336     laVariant, laBull))
337     return olrUseless;
338 
339   if(it == itShard) {
340     if(l == laDesert || l == laIce || l == laJungle || l == laGraveyard ||
341       l == laRlyeh || l == laHell || l == laDryForest || l == laWineyard ||
342       l == laHive || l == laCamelot || l == laRedRock || l == laPalace ||
343       l == laLivefjord || l == laZebra || isElemental(l) || l == laPrincessQuest ||
344       l == laDragon || l == laTortoise || l == laBurial || l == laTrollheim ||
345       l == laOcean || l == laHaunted || l == laWarpCoast || l == laRose)
346       return olrPrize25;
347     return olrForbidden;
348     }
349 
350   if(it == itOrbWater)
351     if(l == laMotion || l == laZebra || l == laIvoryTower || l == laEndorian ||
352       l == laMountain || l == laReptile || l == laDungeon)
353       return olrUseless;
354 
355   if(it == itOrbWinter && l == laMinefield)
356     return olrForbidden;
357 
358   if(it == itOrbWinter && !among(l, laRlyeh, laTemple, laVolcano, laCursed))
359     return olrUseless;
360 
361   if(it == itOrbLife && l == laMotion)
362     return olrUseless;
363 
364   if(it == itOrbFish && l == laKraken)
365     return olrAlways;
366 
367   if(it == itOrbSword && l == laBurial)
368     return olrAlways;
369 
370   if(it == itOrbFish && !among(l, laOcean, laLivefjord, laWhirlpool, laCamelot, laTortoise, laWarpCoast, laWarpSea, laCocytus, laBrownian, laVariant, laWet, laFrog))
371     return olrUseless;
372 
373   if(it == itOrbDomination && l != laOcean && l != laRedRock && l != laDesert &&
374     l != laRlyeh && l != laDragon)
375     return olrUseless;
376 
377   if(it == itOrbIllusion) return olrPrize3;
378 
379   if(l == laTortoise)
380     if(it == itOrbFlash || it == itOrbLightning || it == itOrbFreedom ||
381       it == itOrbPsi || it == itOrbFriend || it == itOrbDragon)
382       return olrForbidden;
383 
384   if(l == laEndorian)
385     if(it == itOrbDragon || it == itOrbFire || it == itOrbLightning || it == itOrbLava)
386       return olrDangerous;
387 
388   if(isHaunted(l) && it == itOrbLava)
389     return olrUseless;
390 
391   if(l == laGraveyard && it == itOrbLava)
392     return olrUseless;
393 
394   if(l == laDryForest)
395     if(it == itOrbFire || it == itOrbLightning || it == itOrbLava)
396       return olrDangerous;
397 
398   if(l == laWet && among(it, itOrbDragon, itOrbLava, itOrbFire))
399     return olrUseless;
400 
401   if(l == laDungeon) {
402     if(it == itOrbSafety || it == itOrbFrog ||
403       it == itOrbTeleport || it == itOrbMatter || it == itOrbNature ||
404       it == itOrbAether || it == itOrbSummon || it == itOrbStone)
405       return olrForbidden;
406     }
407 
408   if(l == laTerracotta && it == itOrbFriend)
409     return olrDangerous;
410 
411   return olrPrize25;
412   }
413 
orbsUnlocked()414 EX int orbsUnlocked() {
415   int i = 0;
416   for(int t=0; t<ittypes; t++)
417     if(itemclass(eItem(t)) == IC_TREASURE && items[t] >= R10)
418       i++;
419   return i;
420   }
421 
orbprizefun(int tr)422 EX ld orbprizefun(int tr) {
423   if(tactic::on) return 1;
424   if(tr < 10) return 0;
425   return .6 + .4 * log(tr/25.) / log(2);
426   }
427 
orbcrossfun(int tr)428 EX ld orbcrossfun(int tr) {
429   if(tactic::on || (ls::single() && isCrossroads(specialland))) return 1;
430   if(tr < 10) return 0;
431   if(tr > 25) return 1;
432   return (tr*2 + 50) / 100.;
433   }
434 
buildPrizeMirror(cell * c,int freq)435 EX bool buildPrizeMirror(cell *c, int freq) {
436   if(inv::on) return false;
437   if(items[itShard] < 25) return false;
438   if(freq && hrand(freq * 100 / orbprizefun(items[itShard])) >= 100)
439     return false;
440   return mirror::build(c);
441   }
442 
443 #if HDR
444 extern cellwalker cwt;
445 #endif
446 
447 EX eLand getPrizeLand(cell *c IS(cwt.at)) {
448   eLand l = c->land;
449   if(isElemental(l)) l = laElementalWall;
450   if(isHaunted(l)) l = laHaunted;
451   if(l == laMercuryRiver) l = laTerracotta;
452   if(l == laWarpSea) l = laWarpCoast;
453   if(l == laPalace && princess::dist(c) < OUT_OF_PRISON)
454     l = laPrincessQuest;
455   return l;
456   }
457 
placePrizeOrb(cell * c)458 EX void placePrizeOrb(cell *c) {
459   if(peace::on) return;
460   if(daily::on) return;
461 
462   eLand l = getPrizeLand(c);
463 
464   // these two lands would have too much orbs according to normal rules
465   if(l == laPalace && hrand(100) >= 20) return;
466   if(l == laPrincessQuest && hrand(100) >= 20) return;
467   if(l == laGraveyard && hrand(100) >= 15) return;
468   if(l == laBurial && hrand(100) >= 10) return;
469   if(l == laLivefjord && hrand(100) >= 35) return;
470   if(l == laMinefield && hrand(100) >= 25) return;
471   if(l == laElementalWall && hrand(100) >= 25) return;
472 
473   for(auto& oi: orbinfos) {
474     if(!(oi.flags & orbgenflags::GLOBAL25)) continue;
475 
476     int mintreas = 25;
477     if(inv::on) {
478       if(oi.flags & orbgenflags::OSM_GLOBAL100) mintreas = 100;
479       else continue;
480       }
481 
482     eOrbLandRelation olr = getOLR(oi.orb, l);
483     if(olr != olrPrize25 && olr != olrPrize3) continue;
484     int treas = items[treasureType(oi.l)];
485     if(olr == olrPrize3) treas *= 10;
486     if(olr == olrPrize25 || olr == olrPrize3 || olr == olrGuest || olr == olrMonster || olr == olrAlways) {
487       if(treas < mintreas) continue;
488       }
489     else continue;
490 
491     int gch = oi.gchance;
492     if(!gch) continue;
493     gch = int(gch / orbprizefun(treas));
494     if(hrand(gch) >= 60) continue;
495     if(oi.orb == itOrbWater && c->land != laOcean && c->land != laKraken) {
496       if(cellHalfvine(c)) continue;
497       c->item = oi.orb;
498       c->wall = waStrandedBoat;
499       return;
500       }
501     c->item = oi.orb;
502     }
503 
504   // printf("land: %s orb: %s\n", dnameof(l), dnameof(c->item));
505   }
506 
507 // 10 not in chaos, less in chaos
treasureForLocal()508 EX int treasureForLocal() {
509   return (ls::any_chaos() ? 1+hrand(10) : 10);
510   }
511 
extra_safety_for_memory(cell * c)512 EX bool extra_safety_for_memory(cell *c) {
513   if(hyperbolic && (arcm::in() || S3 > 3) && !quotient && !tactic::on && in_full_game()) {
514     if(hrand(1000) < 1) {
515       c->item = itOrbSafety;
516       return true;
517       }
518     }
519   return false;
520   }
521 
placeLocalOrbs(cell * c)522 EX void placeLocalOrbs(cell *c) {
523   eLand l = c->land;
524   if(l == laZebra && c->wall == waTrapdoor) return;
525   if(isGravityLand(l) && l != laWestWall && cellEdgeUnstable(c)) return;
526   if(isElemental(l)) l = laElementalWall;
527   if(daily::on) return;
528 
529   if(extra_safety_for_memory(c)) return;
530 
531   if(peace::on) return;
532 
533   for(auto& oi: orbinfos) {
534     if(!(oi.flags & orbgenflags::LOCAL10)) continue;
535     if(oi.l != l) continue;
536     if(yendor::on && (oi.orb == itOrbSafety || oi.orb == itOrbYendor))
537       continue;
538     if(!oi.lchance) continue;
539     int ch = hrand(oi.lchance);
540     if(ch == 1 && ls::any_chaos() && hrand(2) == 0 && items[treasureType(oi.l)] * landMultiplier(oi.l) >= (11+hrand(15)))
541       ch = 0;
542     int tc = items[treasureType(oi.l)] * landMultiplier(oi.l);
543     int tcmin = treasureForLocal();
544     if(inv::on) {
545       if(!(oi.flags & orbgenflags::OSM_LOCAL25))
546         tc = 0;
547       else
548         tcmin = 25;
549       }
550     if(ch == 0 && tc >= tcmin) {
551       // printf("local orb\n");
552       c->item = oi.orb;
553       if(oi.orb == itOrbWater && c->land != laOcean) c->wall = waStrandedBoat;
554       return;
555       }
556     else if(oi.gchance && (ch >= 1 && ch < 11) && getOLR(itShard, l) == olrPrize25 && l != laRedRock && l != laWhirlwind)
557       buildPrizeMirror(c, 10);
558     else if(oi.gchance && (ch >= 11 && ch < 11+PRIZEMUL))
559       placePrizeOrb(c);
560     }
561   }
562 
563 EX void placeLocalSpecial(cell *c, int outof, int loc IS(1), int priz IS(1)) {
564   if(safety || daily::on || extra_safety_for_memory(c) || peace::on) return;
565   int i = hrand(outof);
566   if(i < loc && items[treasureType(c->land)] >= treasureForLocal() && !inv::on)
567     c->item = nativeOrbType(c->land);
568   else if(i >= loc && i < loc + PRIZEMUL * priz)
569     placePrizeOrb(c);
570   }
571 
placeCrossroadOrbs(cell * c)572 EX void placeCrossroadOrbs(cell *c) {
573   if(peace::on) return;
574   if(daily::on) return;
575   for(auto& oi: orbinfos) {
576     if(!(oi.flags & orbgenflags::CROSS10)) continue;
577     if(!oi.gchance) continue;
578 
579     int treas = items[treasureType(oi.l)] * landMultiplier(oi.l);
580     int mintreas = 10;
581 
582     if(inv::on) {
583       if(oi.flags & orbgenflags::OSM_CROSS25)
584         mintreas = 25;
585       else if(oi.flags & orbgenflags::OSM_CROSS50)
586         mintreas = 50;
587       else continue;
588       }
589 
590     if(ls::single() && isCrossroads(specialland)) {
591       if(oi.flags & orbgenflags::NO_TACTIC)
592         continue;
593       else mintreas = 0;
594       }
595     if(treas < mintreas) continue;
596 
597     if(oi.orb == itOrbSafety && c->land == laCrossroads5) continue;
598     int mul = c->land == laCrossroads5 ? 10 : 1;
599     int gch = oi.gchance;
600     if(!inv::on) gch /= orbcrossfun(treas); else gch /= 2;
601     if(hrand(gch) >= mul) continue;
602     if(hrand(50+items[itHyperstone]) >= 50) continue;
603     c->item = oi.orb;
604     if(oi.orb == itOrbWater && c->land != laOcean) c->wall = waStrandedBoat;
605     }
606   }
607 
placeOceanOrbs(cell * c)608 EX void placeOceanOrbs(cell *c) {
609   if(peace::on) return;
610   for(auto& oi: orbinfos) {
611     if(!(oi.flags & orbgenflags::CROSS10)) continue;
612 
613     int treas = items[treasureType(oi.l)] * landMultiplier(oi.l);
614     int mintreas = 10;
615 
616     if(inv::on) {
617       if(oi.flags & orbgenflags::OSM_CROSS25)
618         mintreas = 25;
619       else if(oi.flags & orbgenflags::OSM_CROSS50)
620         mintreas = 50;
621       else continue;
622       }
623 
624     if(treas < mintreas) continue;
625     if(!oi.gchance) continue;
626     if(oi.orb == itOrbLife) continue; // useless
627     if(hrand(oi.gchance) >= 20) continue;
628     c->item = oi.orb;
629     }
630   }
631 
632 }
633