Skip to content

Commit 98ce8f8

Browse files
authored
sm64ex: New Options API and WebHost fix (ArchipelagoMW#2979)
1 parent ea47b90 commit 98ce8f8

File tree

4 files changed

+92
-89
lines changed

4 files changed

+92
-89
lines changed

worlds/sm64ex/Options.py

+36-33
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import typing
2-
from Options import Option, DefaultOnToggle, Range, Toggle, DeathLink, Choice
2+
from dataclasses import dataclass
3+
from Options import DefaultOnToggle, Range, Toggle, DeathLink, Choice, PerGameCommonOptions, OptionSet
34
from .Items import action_item_table
45

56
class EnableCoinStars(DefaultOnToggle):
@@ -114,35 +115,37 @@ class StrictMoveRequirements(DefaultOnToggle):
114115
if Move Randomization is enabled"""
115116
display_name = "Strict Move Requirements"
116117

117-
def getMoveRandomizerOption(action: str):
118-
class MoveRandomizerOption(Toggle):
119-
"""Mario is unable to perform this action until a corresponding item is picked up.
120-
This option is incompatible with builds using a 'nomoverando' branch."""
121-
display_name = f"Randomize {action}"
122-
return MoveRandomizerOption
123-
124-
125-
sm64_options: typing.Dict[str, type(Option)] = {
126-
"AreaRandomizer": AreaRandomizer,
127-
"BuddyChecks": BuddyChecks,
128-
"ExclamationBoxes": ExclamationBoxes,
129-
"ProgressiveKeys": ProgressiveKeys,
130-
"EnableCoinStars": EnableCoinStars,
131-
"StrictCapRequirements": StrictCapRequirements,
132-
"StrictCannonRequirements": StrictCannonRequirements,
133-
"StrictMoveRequirements": StrictMoveRequirements,
134-
"AmountOfStars": AmountOfStars,
135-
"FirstBowserStarDoorCost": FirstBowserStarDoorCost,
136-
"BasementStarDoorCost": BasementStarDoorCost,
137-
"SecondFloorStarDoorCost": SecondFloorStarDoorCost,
138-
"MIPS1Cost": MIPS1Cost,
139-
"MIPS2Cost": MIPS2Cost,
140-
"StarsToFinish": StarsToFinish,
141-
"death_link": DeathLink,
142-
"CompletionType": CompletionType,
143-
}
144-
145-
for action in action_item_table:
146-
# HACK: Disable randomization of double jump
147-
if action == 'Double Jump': continue
148-
sm64_options[f"MoveRandomizer{action.replace(' ','')}"] = getMoveRandomizerOption(action)
118+
class EnableMoveRandomizer(Toggle):
119+
"""Mario is unable to perform some actions until a corresponding item is picked up.
120+
This option is incompatible with builds using a 'nomoverando' branch.
121+
Specific actions to randomize can be specified in the YAML."""
122+
display_name = "Enable Move Randomizer"
123+
124+
class MoveRandomizerActions(OptionSet):
125+
"""Which actions to randomize when Move Randomizer is enabled"""
126+
display_name = "Randomized Moves"
127+
# HACK: Disable randomization for double jump
128+
valid_keys = [action for action in action_item_table if action != 'Double Jump']
129+
default = valid_keys
130+
131+
@dataclass
132+
class SM64Options(PerGameCommonOptions):
133+
area_rando: AreaRandomizer
134+
buddy_checks: BuddyChecks
135+
exclamation_boxes: ExclamationBoxes
136+
progressive_keys: ProgressiveKeys
137+
enable_coin_stars: EnableCoinStars
138+
enable_move_rando: EnableMoveRandomizer
139+
move_rando_actions: MoveRandomizerActions
140+
strict_cap_requirements: StrictCapRequirements
141+
strict_cannon_requirements: StrictCannonRequirements
142+
strict_move_requirements: StrictMoveRequirements
143+
amount_of_stars: AmountOfStars
144+
first_bowser_star_door_cost: FirstBowserStarDoorCost
145+
basement_star_door_cost: BasementStarDoorCost
146+
second_floor_star_door_cost: SecondFloorStarDoorCost
147+
mips1_cost: MIPS1Cost
148+
mips2_cost: MIPS2Cost
149+
stars_to_finish: StarsToFinish
150+
death_link: DeathLink
151+
completion_type: CompletionType

worlds/sm64ex/Regions.py

+17-16
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from enum import Enum
33

44
from BaseClasses import MultiWorld, Region, Entrance, Location
5+
from .Options import SM64Options
56
from .Locations import SM64Location, location_table, locBoB_table, locWhomp_table, locJRB_table, locCCM_table, \
67
locBBH_table, \
78
locHMC_table, locLLL_table, locSSL_table, locDDD_table, locSL_table, \
@@ -78,7 +79,7 @@ class SM64Region(Region):
7879
sm64_entrances_to_level = {**sm64_paintings_to_level, **sm64_secrets_to_level }
7980
sm64_level_to_entrances = {**sm64_level_to_paintings, **sm64_level_to_secrets }
8081

81-
def create_regions(world: MultiWorld, player: int):
82+
def create_regions(world: MultiWorld, options: SM64Options, player: int):
8283
regSS = Region("Menu", player, world, "Castle Area")
8384
create_default_locs(regSS, locSS_table)
8485
world.regions.append(regSS)
@@ -88,28 +89,28 @@ def create_regions(world: MultiWorld, player: int):
8889
"BoB: Mario Wings to the Sky", "BoB: Behind Chain Chomp's Gate", "BoB: Bob-omb Buddy")
8990
bob_island = create_subregion(regBoB, "BoB: Island", "BoB: Shoot to the Island in the Sky", "BoB: Find the 8 Red Coins")
9091
regBoB.subregions = [bob_island]
91-
if (world.EnableCoinStars[player].value):
92+
if options.enable_coin_stars:
9293
create_locs(regBoB, "BoB: 100 Coins")
9394

9495
regWhomp = create_region("Whomp's Fortress", player, world)
9596
create_locs(regWhomp, "WF: Chip Off Whomp's Block", "WF: Shoot into the Wild Blue", "WF: Red Coins on the Floating Isle",
9697
"WF: Fall onto the Caged Island", "WF: Blast Away the Wall")
9798
wf_tower = create_subregion(regWhomp, "WF: Tower", "WF: To the Top of the Fortress", "WF: Bob-omb Buddy")
9899
regWhomp.subregions = [wf_tower]
99-
if (world.EnableCoinStars[player].value):
100+
if options.enable_coin_stars:
100101
create_locs(regWhomp, "WF: 100 Coins")
101102

102103
regJRB = create_region("Jolly Roger Bay", player, world)
103104
create_locs(regJRB, "JRB: Plunder in the Sunken Ship", "JRB: Can the Eel Come Out to Play?", "JRB: Treasure of the Ocean Cave",
104105
"JRB: Blast to the Stone Pillar", "JRB: Through the Jet Stream", "JRB: Bob-omb Buddy")
105106
jrb_upper = create_subregion(regJRB, 'JRB: Upper', "JRB: Red Coins on the Ship Afloat")
106107
regJRB.subregions = [jrb_upper]
107-
if (world.EnableCoinStars[player].value):
108+
if options.enable_coin_stars:
108109
create_locs(jrb_upper, "JRB: 100 Coins")
109110

110111
regCCM = create_region("Cool, Cool Mountain", player, world)
111112
create_default_locs(regCCM, locCCM_table)
112-
if (world.EnableCoinStars[player].value):
113+
if options.enable_coin_stars:
113114
create_locs(regCCM, "CCM: 100 Coins")
114115

115116
regBBH = create_region("Big Boo's Haunt", player, world)
@@ -118,7 +119,7 @@ def create_regions(world: MultiWorld, player: int):
118119
bbh_third_floor = create_subregion(regBBH, "BBH: Third Floor", "BBH: Eye to Eye in the Secret Room")
119120
bbh_roof = create_subregion(bbh_third_floor, "BBH: Roof", "BBH: Big Boo's Balcony", "BBH: 1Up Block Top of Mansion")
120121
regBBH.subregions = [bbh_third_floor, bbh_roof]
121-
if (world.EnableCoinStars[player].value):
122+
if options.enable_coin_stars:
122123
create_locs(regBBH, "BBH: 100 Coins")
123124

124125
regPSS = create_region("The Princess's Secret Slide", player, world)
@@ -141,15 +142,15 @@ def create_regions(world: MultiWorld, player: int):
141142
hmc_red_coin_area = create_subregion(regHMC, "HMC: Red Coin Area", "HMC: Elevate for 8 Red Coins")
142143
hmc_pit_islands = create_subregion(regHMC, "HMC: Pit Islands", "HMC: A-Maze-Ing Emergency Exit", "HMC: 1Up Block above Pit")
143144
regHMC.subregions = [hmc_red_coin_area, hmc_pit_islands]
144-
if (world.EnableCoinStars[player].value):
145+
if options.enable_coin_stars:
145146
create_locs(hmc_red_coin_area, "HMC: 100 Coins")
146147

147148
regLLL = create_region("Lethal Lava Land", player, world)
148149
create_locs(regLLL, "LLL: Boil the Big Bully", "LLL: Bully the Bullies",
149150
"LLL: 8-Coin Puzzle with 15 Pieces", "LLL: Red-Hot Log Rolling")
150151
lll_upper_volcano = create_subregion(regLLL, "LLL: Upper Volcano", "LLL: Hot-Foot-It into the Volcano", "LLL: Elevator Tour in the Volcano")
151152
regLLL.subregions = [lll_upper_volcano]
152-
if (world.EnableCoinStars[player].value):
153+
if options.enable_coin_stars:
153154
create_locs(regLLL, "LLL: 100 Coins")
154155

155156
regSSL = create_region("Shifting Sand Land", player, world)
@@ -159,15 +160,15 @@ def create_regions(world: MultiWorld, player: int):
159160
ssl_upper_pyramid = create_subregion(regSSL, "SSL: Upper Pyramid", "SSL: Inside the Ancient Pyramid",
160161
"SSL: Stand Tall on the Four Pillars", "SSL: Pyramid Puzzle")
161162
regSSL.subregions = [ssl_upper_pyramid]
162-
if (world.EnableCoinStars[player].value):
163+
if options.enable_coin_stars:
163164
create_locs(regSSL, "SSL: 100 Coins")
164165

165166
regDDD = create_region("Dire, Dire Docks", player, world)
166167
create_locs(regDDD, "DDD: Board Bowser's Sub", "DDD: Chests in the Current", "DDD: Through the Jet Stream",
167168
"DDD: The Manta Ray's Reward", "DDD: Collect the Caps...")
168169
ddd_moving_poles = create_subregion(regDDD, "DDD: Moving Poles", "DDD: Pole-Jumping for Red Coins")
169170
regDDD.subregions = [ddd_moving_poles]
170-
if (world.EnableCoinStars[player].value):
171+
if options.enable_coin_stars:
171172
create_locs(ddd_moving_poles, "DDD: 100 Coins")
172173

173174
regCotMC = create_region("Cavern of the Metal Cap", player, world)
@@ -184,7 +185,7 @@ def create_regions(world: MultiWorld, player: int):
184185

185186
regSL = create_region("Snowman's Land", player, world)
186187
create_default_locs(regSL, locSL_table)
187-
if (world.EnableCoinStars[player].value):
188+
if options.enable_coin_stars:
188189
create_locs(regSL, "SL: 100 Coins")
189190

190191
regWDW = create_region("Wet-Dry World", player, world)
@@ -193,7 +194,7 @@ def create_regions(world: MultiWorld, player: int):
193194
"WDW: Secrets in the Shallows & Sky", "WDW: Bob-omb Buddy")
194195
wdw_downtown = create_subregion(regWDW, "WDW: Downtown", "WDW: Go to Town for Red Coins", "WDW: Quick Race Through Downtown!", "WDW: 1Up Block in Downtown")
195196
regWDW.subregions = [wdw_top, wdw_downtown]
196-
if (world.EnableCoinStars[player].value):
197+
if options.enable_coin_stars:
197198
create_locs(wdw_top, "WDW: 100 Coins")
198199

199200
regTTM = create_region("Tall, Tall Mountain", player, world)
@@ -202,7 +203,7 @@ def create_regions(world: MultiWorld, player: int):
202203
ttm_top = create_subregion(ttm_middle, "TTM: Top", "TTM: Scale the Mountain", "TTM: Mystery of the Monkey Cage",
203204
"TTM: Mysterious Mountainside", "TTM: Breathtaking View from Bridge")
204205
regTTM.subregions = [ttm_middle, ttm_top]
205-
if (world.EnableCoinStars[player].value):
206+
if options.enable_coin_stars:
206207
create_locs(ttm_top, "TTM: 100 Coins")
207208

208209
create_region("Tiny-Huge Island (Huge)", player, world)
@@ -214,7 +215,7 @@ def create_regions(world: MultiWorld, player: int):
214215
"THI: 1Up Block THI Large near Start", "THI: 1Up Block Windy Area")
215216
thi_large_top = create_subregion(thi_pipes, "THI: Large Top", "THI: Make Wiggler Squirm")
216217
regTHI.subregions = [thi_pipes, thi_large_top]
217-
if (world.EnableCoinStars[player].value):
218+
if options.enable_coin_stars:
218219
create_locs(thi_large_top, "THI: 100 Coins")
219220

220221
regFloor3 = create_region("Third Floor", player, world)
@@ -225,7 +226,7 @@ def create_regions(world: MultiWorld, player: int):
225226
ttc_upper = create_subregion(ttc_lower, "TTC: Upper", "TTC: Timed Jumps on Moving Bars", "TTC: The Pit and the Pendulums")
226227
ttc_top = create_subregion(ttc_upper, "TTC: Top", "TTC: Stomp on the Thwomp", "TTC: 1Up Block at the Top")
227228
regTTC.subregions = [ttc_lower, ttc_upper, ttc_top]
228-
if (world.EnableCoinStars[player].value):
229+
if options.enable_coin_stars:
229230
create_locs(ttc_top, "TTC: 100 Coins")
230231

231232
regRR = create_region("Rainbow Ride", player, world)
@@ -235,7 +236,7 @@ def create_regions(world: MultiWorld, player: int):
235236
rr_cruiser = create_subregion(regRR, "RR: Cruiser", "RR: Cruiser Crossing the Rainbow", "RR: Somewhere Over the Rainbow")
236237
rr_house = create_subregion(regRR, "RR: House", "RR: The Big House in the Sky", "RR: 1Up Block On House in the Sky")
237238
regRR.subregions = [rr_maze, rr_cruiser, rr_house]
238-
if (world.EnableCoinStars[player].value):
239+
if options.enable_coin_stars:
239240
create_locs(rr_maze, "RR: 100 Coins")
240241

241242
regWMotR = create_region("Wing Mario over the Rainbow", player, world)

worlds/sm64ex/Rules.py

+15-14
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from BaseClasses import MultiWorld
44
from ..generic.Rules import add_rule, set_rule
55
from .Locations import location_table
6+
from .Options import SM64Options
67
from .Regions import connect_regions, SM64Levels, sm64_level_to_paintings, sm64_paintings_to_level,\
78
sm64_level_to_secrets, sm64_secrets_to_level, sm64_entrances_to_level, sm64_level_to_entrances
89
from .Items import action_item_table
@@ -24,27 +25,27 @@ def fix_reg(entrance_map: Dict[SM64Levels, str], entrance: SM64Levels, invalid_r
2425
swapdict[entrance], swapdict[rand_entrance] = rand_region, old_dest
2526
swapdict.pop(entrance)
2627

27-
def set_rules(world, player: int, area_connections: dict, star_costs: dict, move_rando_bitvec: int):
28+
def set_rules(world, options: SM64Options, player: int, area_connections: dict, star_costs: dict, move_rando_bitvec: int):
2829
randomized_level_to_paintings = sm64_level_to_paintings.copy()
2930
randomized_level_to_secrets = sm64_level_to_secrets.copy()
3031
valid_move_randomizer_start_courses = [
3132
"Bob-omb Battlefield", "Jolly Roger Bay", "Cool, Cool Mountain",
3233
"Big Boo's Haunt", "Lethal Lava Land", "Shifting Sand Land",
3334
"Dire, Dire Docks", "Snowman's Land"
3435
] # Excluding WF, HMC, WDW, TTM, THI, TTC, and RR
35-
if world.AreaRandomizer[player].value >= 1: # Some randomization is happening, randomize Courses
36+
if options.area_rando >= 1: # Some randomization is happening, randomize Courses
3637
randomized_level_to_paintings = shuffle_dict_keys(world,sm64_level_to_paintings)
3738
# If not shuffling later, ensure a valid start course on move randomizer
38-
if world.AreaRandomizer[player].value < 3 and move_rando_bitvec > 0:
39+
if options.area_rando < 3 and move_rando_bitvec > 0:
3940
swapdict = randomized_level_to_paintings.copy()
4041
invalid_start_courses = {course for course in randomized_level_to_paintings.values() if course not in valid_move_randomizer_start_courses}
4142
fix_reg(randomized_level_to_paintings, SM64Levels.BOB_OMB_BATTLEFIELD, invalid_start_courses, swapdict, world)
4243
fix_reg(randomized_level_to_paintings, SM64Levels.WHOMPS_FORTRESS, invalid_start_courses, swapdict, world)
4344

44-
if world.AreaRandomizer[player].value == 2: # Randomize Secrets as well
45+
if options.area_rando == 2: # Randomize Secrets as well
4546
randomized_level_to_secrets = shuffle_dict_keys(world,sm64_level_to_secrets)
4647
randomized_entrances = {**randomized_level_to_paintings, **randomized_level_to_secrets}
47-
if world.AreaRandomizer[player].value == 3: # Randomize Courses and Secrets in one pool
48+
if options.area_rando == 3: # Randomize Courses and Secrets in one pool
4849
randomized_entrances = shuffle_dict_keys(world, randomized_entrances)
4950
# Guarantee first entrance is a course
5051
swapdict = randomized_entrances.copy()
@@ -67,7 +68,7 @@ def set_rules(world, player: int, area_connections: dict, star_costs: dict, move
6768
area_connections.update({int(entrance_lvl): int(sm64_entrances_to_level[destination]) for (entrance_lvl,destination) in randomized_entrances.items()})
6869
randomized_entrances_s = {sm64_level_to_entrances[entrance_lvl]: destination for (entrance_lvl,destination) in randomized_entrances.items()}
6970

70-
rf = RuleFactory(world, player, move_rando_bitvec)
71+
rf = RuleFactory(world, options, player, move_rando_bitvec)
7172

7273
connect_regions(world, player, "Menu", randomized_entrances_s["Bob-omb Battlefield"])
7374
connect_regions(world, player, "Menu", randomized_entrances_s["Whomp's Fortress"], lambda state: state.has("Power Star", player, 1))
@@ -199,7 +200,7 @@ def set_rules(world, player: int, area_connections: dict, star_costs: dict, move
199200
# Bowser in the Sky
200201
rf.assign_rule("BitS: Top", "CL+TJ | CL+SF+LG | MOVELESS & TJ+WK+LG")
201202
# 100 Coin Stars
202-
if world.EnableCoinStars[player]:
203+
if options.enable_coin_stars:
203204
rf.assign_rule("BoB: 100 Coins", "CANN & WC | CANNLESS & WC & TJ")
204205
rf.assign_rule("WF: 100 Coins", "GP | MOVELESS")
205206
rf.assign_rule("JRB: 100 Coins", "GP & {JRB: Upper}")
@@ -225,9 +226,9 @@ def set_rules(world, player: int, area_connections: dict, star_costs: dict, move
225226

226227
world.completion_condition[player] = lambda state: state.can_reach("BitS: Top", 'Region', player)
227228

228-
if world.CompletionType[player] == "last_bowser_stage":
229+
if options.completion_type == "last_bowser_stage":
229230
world.completion_condition[player] = lambda state: state.can_reach("BitS: Top", 'Region', player)
230-
elif world.CompletionType[player] == "all_bowser_stages":
231+
elif options.completion_type == "all_bowser_stages":
231232
world.completion_condition[player] = lambda state: state.can_reach("Bowser in the Dark World", 'Region', player) and \
232233
state.can_reach("BitFS: Upper", 'Region', player) and \
233234
state.can_reach("BitS: Top", 'Region', player)
@@ -262,14 +263,14 @@ class RuleFactory:
262263
class SM64LogicException(Exception):
263264
pass
264265

265-
def __init__(self, world, player, move_rando_bitvec):
266+
def __init__(self, world, options: SM64Options, player: int, move_rando_bitvec: int):
266267
self.world = world
267268
self.player = player
268269
self.move_rando_bitvec = move_rando_bitvec
269-
self.area_randomizer = world.AreaRandomizer[player].value > 0
270-
self.capless = not world.StrictCapRequirements[player]
271-
self.cannonless = not world.StrictCannonRequirements[player]
272-
self.moveless = not world.StrictMoveRequirements[player] or not move_rando_bitvec > 0
270+
self.area_randomizer = options.area_rando > 0
271+
self.capless = not options.strict_cap_requirements
272+
self.cannonless = not options.strict_cannon_requirements
273+
self.moveless = not options.strict_move_requirements or not move_rando_bitvec > 0
273274

274275
def assign_rule(self, target_name: str, rule_expr: str):
275276
target = self.world.get_location(target_name, self.player) if target_name in location_table else self.world.get_entrance(target_name, self.player)

0 commit comments

Comments
 (0)