forked from ArchipelagoMW/Archipelago
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathOptions.py
454 lines (375 loc) · 15.6 KB
/
Options.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
from typing import Dict, Union, List
from BaseClasses import MultiWorld
from Options import Toggle, DefaultOnToggle, DeathLink, Choice, Range, Option, OptionDict, OptionList
from schema import Schema, And, Optional, Or
class StartWithJewelryBox(Toggle):
"Start with Jewelry Box unlocked"
display_name = "Start with Jewelry Box"
class DownloadableItems(DefaultOnToggle):
"With the tablet you will be able to download items at terminals"
display_name = "Downloadable items"
class EyeSpy(Toggle):
"Requires Oculus Ring in inventory to be able to break hidden walls."
display_name = "Eye Spy"
class StartWithMeyef(Toggle):
"Start with Meyef, ideal for when you want to play multiplayer."
display_name = "Start with Meyef"
class QuickSeed(Toggle):
"Start with Talaria Attachment, Nyoom!"
display_name = "Quick seed"
class SpecificKeycards(Toggle):
"Keycards can only open corresponding doors"
display_name = "Specific Keycards"
class Inverted(Toggle):
"Start in the past"
display_name = "Inverted"
class GyreArchives(Toggle):
"Gyre locations are in logic. New warps are gated by Merchant Crow and Kobo"
display_name = "Gyre Archives"
class Cantoran(Toggle):
"Cantoran's fight and check are available upon revisiting his room"
display_name = "Cantoran"
class LoreChecks(Toggle):
"Memories and journal entries contain items."
display_name = "Lore Checks"
class BossRando(Choice):
"Wheter all boss locations are shuffled, and if their damage/hp should be scaled."
display_name = "Boss Randomization"
option_off = 0
option_scaled = 1
option_unscaled = 2
alias_true = 1
class EnemyRando(Choice):
"Wheter enemies will be randomized, and if their damage/hp should be scaled."
display_name = "Enemy Randomization"
option_off = 0
option_scaled = 1
option_unscaled = 2
option_ryshia = 3
alias_true = 1
class DamageRando(Choice):
"Randomly nerfs and buffs some orbs and their associated spells as well as some associated rings."
display_name = "Damage Rando"
option_off = 0
option_allnerfs = 1
option_mostlynerfs = 2
option_balanced = 3
option_mostlybuffs = 4
option_allbuffs = 5
option_manual = 6
alias_true = 2
class DamageRandoOverrides(OptionDict):
"""Manual +/-/normal odds for an orb. Put 0 if you don't want a certain nerf or buff to be a possibility. Orbs that
you don't specify will roll with 1/1/1 as odds"""
schema = Schema({
Optional("Blue"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("Blade"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("Fire"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("Plasma"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("Iron"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("Ice"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("Wind"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("Gun"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("Umbra"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("Empire"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("Eye"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("Blood"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("ForbiddenTome"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("Shattered"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("Nether"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
Optional("Radiant"): {
"MinusOdds": And(int, lambda n: n >= 0),
"NormalOdds": And(int, lambda n: n >= 0),
"PlusOdds": And(int, lambda n: n >= 0)
},
})
display_name = "Damage Rando Overrides"
default = {
"Blue": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"Blade": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"Fire": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"Plasma": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"Iron": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"Ice": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"Wind": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"Gun": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"Umbra": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"Empire": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"Eye": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"Blood": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"ForbiddenTome": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"Shattered": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"Nether": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
"Radiant": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
}
class HpCap(Range):
"Sets the number that Lunais's HP maxes out at."
display_name = "HP Cap"
range_start = 1
range_end = 999
default = 999
class LevelCap(Range):
"""Sets the max level Lunais can achieve."""
display_name = "Level Cap"
range_start = 1
range_end = 99
default = 99
class ExtraEarringsXP(Range):
"""Adds additional XP granted by Galaxy Earrings."""
display_name = "Extra Earrings XP"
range_start = 0
range_end = 24
default = 0
class BossHealing(DefaultOnToggle):
"Enables/disables healing after boss fights. NOTE: Currently only applicable when Boss Rando is enabled."
display_name = "Heal After Bosses"
class ShopFill(Choice):
"""Sets the items for sale in Merchant Crow's shops.
Default: No sunglasses or trendy jacket, but sand vials for sale.
Randomized: Up to 4 random items in each shop.
Vanilla: Keep shops the same as the base game.
Empty: Sell no items at the shop."""
display_name = "Shop Inventory"
option_default = 0
option_randomized = 1
option_vanilla = 2
option_empty = 3
class ShopWarpShards(DefaultOnToggle):
"Shops always sell warp shards (when keys possessed), ignoring inventory setting."
display_name = "Always Sell Warp Shards"
class ShopMultiplier(Range):
"Multiplier for the cost of items in the shop. Set to 0 for free shops."
display_name = "Shop Price Multiplier"
range_start = 0
range_end = 10
default = 1
class LootPool(Choice):
"""Sets the items that drop from enemies (does not apply to boss reward checks)
Vanilla: Drops are the same as the base game
Randomized: Each slot of every enemy's drop table is given a random use item or piece of equipment.
Empty: Enemies drop nothing."""
display_name = "Loot Pool"
option_vanilla = 0
option_randomized = 1
option_empty = 2
class DropRateCategory(Choice):
"""Sets the drop rate when 'Loot Pool' is set to 'Random'
Tiered: Based on item rarity/value
Vanilla: Based on bestiary slot the item is placed into
Random: Assigned a random tier/drop rate
Fixed: Set by the 'Fixed Drop Rate' setting
"""
display_name = "Drop Rate Category"
option_tiered = 0
option_vanilla = 1
option_randomized = 2
option_fixed = 3
class FixedDropRate(Range):
"Base drop rate percentage when 'Drop Rate Category' is set to 'Fixed'"
display_name = "Fixed Drop Rate"
range_start = 0
range_end = 100
default = 5
class LootTierDistro(Choice):
"""Sets how often items of each rarity tier are placed when 'Loot Pool' is set to 'Random'
Default Weight: Rarer items will be assigned to enemy drop slots less frequently than common items
Full Random: Any item has an equal chance of being placed in an enemy's drop slot
Inverted Weight: Rarest items show up the most frequently, while common items are the rarest
"""
display_name = "Loot Tier Distrubution"
option_default_weight = 0
option_full_random = 1
option_inverted_weight = 2
class ShowBestiary(Toggle):
"All entries in the bestiary are visible, without needing to kill one of a given enemy first"
display_name = "Show Bestiary Entries"
class ShowDrops(Toggle):
"All item drops in the bestiary are visible, without needing an enemy to drop one of a given item first"
display_name = "Show Bestiary Item Drops"
class EnterSandman(Toggle):
"The Ancient Pyramid is unlocked by the Twin Pyramid Keys, but the final boss door opens if you have all 5 Timespinner pieces"
display_name = "Enter Sandman"
class DadPercent(Toggle):
"""The win condition is beating the boss of Emperor's Tower"""
display_name = "Dad Percent"
class RisingTides(Toggle):
"""Random areas are flooded or drained, can be further specified with RisingTidesOverrides"""
display_name = "Rising Tides"
def rising_tide_option(location: str, with_save_point_option: bool = False) -> Dict[Optional, Or]:
if with_save_point_option:
return {
Optional(location): Or(
And({
Optional("Dry"): And(int, lambda n: n >= 0),
Optional("Flooded"): And(int, lambda n: n >= 0),
Optional("FloodedWithSavePointAvailable"): And(int, lambda n: n >= 0)
}, lambda d: any(v > 0 for v in d.values())),
"Dry",
"Flooded",
"FloodedWithSavePointAvailable")
}
else:
return {
Optional(location): Or(
And({
Optional("Dry"): And(int, lambda n: n >= 0),
Optional("Flooded"): And(int, lambda n: n >= 0)
}, lambda d: any(v > 0 for v in d.values())),
"Dry",
"Flooded")
}
class RisingTidesOverrides(OptionDict):
"""Odds for specific areas to be flooded or drained, only has effect when RisingTides is on.
Areas that are not specified will roll with the default 33% chance of getting flooded or drained"""
display_name = "Rising Tides Overrides"
schema = Schema({
**rising_tide_option("Xarion"),
**rising_tide_option("Maw"),
**rising_tide_option("AncientPyramidShaft"),
**rising_tide_option("Sandman"),
**rising_tide_option("CastleMoat"),
**rising_tide_option("CastleBasement", with_save_point_option=True),
**rising_tide_option("CastleCourtyard"),
**rising_tide_option("LakeDesolation"),
**rising_tide_option("LakeSerene"),
**rising_tide_option("LakeSereneBridge"),
**rising_tide_option("Lab"),
})
default = {
"Xarion": { "Dry": 67, "Flooded": 33 },
"Maw": { "Dry": 67, "Flooded": 33 },
"AncientPyramidShaft": { "Dry": 67, "Flooded": 33 },
"Sandman": { "Dry": 67, "Flooded": 33 },
"CastleMoat": { "Dry": 67, "Flooded": 33 },
"CastleBasement": { "Dry": 66, "Flooded": 17, "FloodedWithSavePointAvailable": 17 },
"CastleCourtyard": { "Dry": 67, "Flooded": 33 },
"LakeDesolation": { "Dry": 67, "Flooded": 33 },
"LakeSerene": { "Dry": 33, "Flooded": 67 },
"LakeSereneBridge": { "Dry": 67, "Flooded": 33 },
"Lab": { "Dry": 67, "Flooded": 33 },
}
class UnchainedKeys(Toggle):
"""Start with Twin Pyramid Key, which does not give free warp;
warp items for Past, Present, (and ??? with Enter Sandman) can be found."""
display_name = "Unchained Keys"
class TrapChance(Range):
"""Chance of traps in the item pool.
Traps will only replace filler items such as potions, vials and antidotes"""
display_name = "Trap Chance"
range_start = 0
range_end = 100
default = 10
class Traps(OptionList):
"""List of traps that may be in the item pool to find"""
display_name = "Traps Types"
valid_keys = { "Meteor Sparrow Trap", "Poison Trap", "Chaos Trap", "Neurotoxin Trap", "Bee Trap" }
default = [ "Meteor Sparrow Trap", "Poison Trap", "Chaos Trap", "Neurotoxin Trap", "Bee Trap" ]
class PresentAccessWithWheelAndSpindle(Toggle):
"""When inverted, allows using the refugee camp warp when both the Timespinner Wheel and Spindle is acquired."""
display_name = "Past Wheel & Spindle Warp"
# Some options that are available in the timespinner randomizer arent currently implemented
timespinner_options: Dict[str, Option] = {
"StartWithJewelryBox": StartWithJewelryBox,
"DownloadableItems": DownloadableItems,
"EyeSpy": EyeSpy,
"StartWithMeyef": StartWithMeyef,
"QuickSeed": QuickSeed,
"SpecificKeycards": SpecificKeycards,
"Inverted": Inverted,
"GyreArchives": GyreArchives,
"Cantoran": Cantoran,
"LoreChecks": LoreChecks,
"BossRando": BossRando,
"EnemyRando": EnemyRando,
"DamageRando": DamageRando,
"DamageRandoOverrides": DamageRandoOverrides,
"HpCap": HpCap,
"LevelCap": LevelCap,
"ExtraEarringsXP": ExtraEarringsXP,
"BossHealing": BossHealing,
"ShopFill": ShopFill,
"ShopWarpShards": ShopWarpShards,
"ShopMultiplier": ShopMultiplier,
"LootPool": LootPool,
"DropRateCategory": DropRateCategory,
"FixedDropRate": FixedDropRate,
"LootTierDistro": LootTierDistro,
"ShowBestiary": ShowBestiary,
"ShowDrops": ShowDrops,
"EnterSandman": EnterSandman,
"DadPercent": DadPercent,
"RisingTides": RisingTides,
"RisingTidesOverrides": RisingTidesOverrides,
"UnchainedKeys": UnchainedKeys,
"TrapChance": TrapChance,
"Traps": Traps,
"PresentAccessWithWheelAndSpindle": PresentAccessWithWheelAndSpindle,
"DeathLink": DeathLink,
}
def is_option_enabled(world: MultiWorld, player: int, name: str) -> bool:
return get_option_value(world, player, name) > 0
def get_option_value(world: MultiWorld, player: int, name: str) -> Union[int, Dict, List]:
option = getattr(world, name, None)
if option == None:
return 0
return option[player].value