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