Skip to content

Commit e9620be

Browse files
authored
SM64: Goal Logic and Hint Bugfixes (ArchipelagoMW#2886)
1 parent 183ca35 commit e9620be

File tree

3 files changed

+64
-32
lines changed

3 files changed

+64
-32
lines changed

worlds/sm64ex/Regions.py

+39-18
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ class SM64Levels(int, Enum):
3636
BOWSER_IN_THE_FIRE_SEA = 191
3737
WING_MARIO_OVER_THE_RAINBOW = 311
3838

39+
40+
class SM64Region(Region):
41+
subregions: typing.List[Region] = []
42+
43+
3944
# sm64paintings is a dict of entrances, format LEVEL | AREA
4045
sm64_level_to_paintings: typing.Dict[SM64Levels, str] = {
4146
SM64Levels.BOB_OMB_BATTLEFIELD: "Bob-omb Battlefield",
@@ -81,21 +86,24 @@ def create_regions(world: MultiWorld, player: int):
8186
regBoB = create_region("Bob-omb Battlefield", player, world)
8287
create_locs(regBoB, "BoB: Big Bob-Omb on the Summit", "BoB: Footrace with Koopa The Quick",
8388
"BoB: Mario Wings to the Sky", "BoB: Behind Chain Chomp's Gate", "BoB: Bob-omb Buddy")
84-
create_subregion(regBoB, "BoB: Island", "BoB: Shoot to the Island in the Sky", "BoB: Find the 8 Red Coins")
89+
bob_island = create_subregion(regBoB, "BoB: Island", "BoB: Shoot to the Island in the Sky", "BoB: Find the 8 Red Coins")
90+
regBoB.subregions = [bob_island]
8591
if (world.EnableCoinStars[player].value):
8692
create_locs(regBoB, "BoB: 100 Coins")
8793

8894
regWhomp = create_region("Whomp's Fortress", player, world)
8995
create_locs(regWhomp, "WF: Chip Off Whomp's Block", "WF: Shoot into the Wild Blue", "WF: Red Coins on the Floating Isle",
9096
"WF: Fall onto the Caged Island", "WF: Blast Away the Wall")
91-
create_subregion(regWhomp, "WF: Tower", "WF: To the Top of the Fortress", "WF: Bob-omb Buddy")
97+
wf_tower = create_subregion(regWhomp, "WF: Tower", "WF: To the Top of the Fortress", "WF: Bob-omb Buddy")
98+
regWhomp.subregions = [wf_tower]
9299
if (world.EnableCoinStars[player].value):
93100
create_locs(regWhomp, "WF: 100 Coins")
94101

95102
regJRB = create_region("Jolly Roger Bay", player, world)
96103
create_locs(regJRB, "JRB: Plunder in the Sunken Ship", "JRB: Can the Eel Come Out to Play?", "JRB: Treasure of the Ocean Cave",
97104
"JRB: Blast to the Stone Pillar", "JRB: Through the Jet Stream", "JRB: Bob-omb Buddy")
98105
jrb_upper = create_subregion(regJRB, 'JRB: Upper', "JRB: Red Coins on the Ship Afloat")
106+
regJRB.subregions = [jrb_upper]
99107
if (world.EnableCoinStars[player].value):
100108
create_locs(jrb_upper, "JRB: 100 Coins")
101109

@@ -108,7 +116,8 @@ def create_regions(world: MultiWorld, player: int):
108116
create_locs(regBBH, "BBH: Go on a Ghost Hunt", "BBH: Ride Big Boo's Merry-Go-Round",
109117
"BBH: Secret of the Haunted Books", "BBH: Seek the 8 Red Coins")
110118
bbh_third_floor = create_subregion(regBBH, "BBH: Third Floor", "BBH: Eye to Eye in the Secret Room")
111-
create_subregion(bbh_third_floor, "BBH: Roof", "BBH: Big Boo's Balcony", "BBH: 1Up Block Top of Mansion")
119+
bbh_roof = create_subregion(bbh_third_floor, "BBH: Roof", "BBH: Big Boo's Balcony", "BBH: 1Up Block Top of Mansion")
120+
regBBH.subregions = [bbh_third_floor, bbh_roof]
112121
if (world.EnableCoinStars[player].value):
113122
create_locs(regBBH, "BBH: 100 Coins")
114123

@@ -130,29 +139,34 @@ def create_regions(world: MultiWorld, player: int):
130139
create_locs(regHMC, "HMC: Swimming Beast in the Cavern", "HMC: Metal-Head Mario Can Move!",
131140
"HMC: Watch for Rolling Rocks", "HMC: Navigating the Toxic Maze","HMC: 1Up Block Past Rolling Rocks")
132141
hmc_red_coin_area = create_subregion(regHMC, "HMC: Red Coin Area", "HMC: Elevate for 8 Red Coins")
133-
create_subregion(regHMC, "HMC: Pit Islands", "HMC: A-Maze-Ing Emergency Exit", "HMC: 1Up Block above Pit")
142+
hmc_pit_islands = create_subregion(regHMC, "HMC: Pit Islands", "HMC: A-Maze-Ing Emergency Exit", "HMC: 1Up Block above Pit")
143+
regHMC.subregions = [hmc_red_coin_area, hmc_pit_islands]
134144
if (world.EnableCoinStars[player].value):
135145
create_locs(hmc_red_coin_area, "HMC: 100 Coins")
136146

137147
regLLL = create_region("Lethal Lava Land", player, world)
138148
create_locs(regLLL, "LLL: Boil the Big Bully", "LLL: Bully the Bullies",
139149
"LLL: 8-Coin Puzzle with 15 Pieces", "LLL: Red-Hot Log Rolling")
140-
create_subregion(regLLL, "LLL: Upper Volcano", "LLL: Hot-Foot-It into the Volcano", "LLL: Elevator Tour in the Volcano")
150+
lll_upper_volcano = create_subregion(regLLL, "LLL: Upper Volcano", "LLL: Hot-Foot-It into the Volcano", "LLL: Elevator Tour in the Volcano")
151+
regLLL.subregions = [lll_upper_volcano]
141152
if (world.EnableCoinStars[player].value):
142153
create_locs(regLLL, "LLL: 100 Coins")
143154

144155
regSSL = create_region("Shifting Sand Land", player, world)
145-
create_locs(regSSL, "SSL: In the Talons of the Big Bird", "SSL: Shining Atop the Pyramid", "SSL: Inside the Ancient Pyramid",
156+
create_locs(regSSL, "SSL: In the Talons of the Big Bird", "SSL: Shining Atop the Pyramid",
146157
"SSL: Free Flying for 8 Red Coins", "SSL: Bob-omb Buddy",
147158
"SSL: 1Up Block Outside Pyramid", "SSL: 1Up Block Pyramid Left Path", "SSL: 1Up Block Pyramid Back")
148-
create_subregion(regSSL, "SSL: Upper Pyramid", "SSL: Stand Tall on the Four Pillars", "SSL: Pyramid Puzzle")
159+
ssl_upper_pyramid = create_subregion(regSSL, "SSL: Upper Pyramid", "SSL: Inside the Ancient Pyramid",
160+
"SSL: Stand Tall on the Four Pillars", "SSL: Pyramid Puzzle")
161+
regSSL.subregions = [ssl_upper_pyramid]
149162
if (world.EnableCoinStars[player].value):
150163
create_locs(regSSL, "SSL: 100 Coins")
151164

152165
regDDD = create_region("Dire, Dire Docks", player, world)
153166
create_locs(regDDD, "DDD: Board Bowser's Sub", "DDD: Chests in the Current", "DDD: Through the Jet Stream",
154167
"DDD: The Manta Ray's Reward", "DDD: Collect the Caps...")
155168
ddd_moving_poles = create_subregion(regDDD, "DDD: Moving Poles", "DDD: Pole-Jumping for Red Coins")
169+
regDDD.subregions = [ddd_moving_poles]
156170
if (world.EnableCoinStars[player].value):
157171
create_locs(ddd_moving_poles, "DDD: 100 Coins")
158172

@@ -163,7 +177,8 @@ def create_regions(world: MultiWorld, player: int):
163177
create_default_locs(regVCutM, locVCutM_table)
164178

165179
regBitFS = create_region("Bowser in the Fire Sea", player, world)
166-
create_subregion(regBitFS, "BitFS: Upper", *locBitFS_table.keys())
180+
bitfs_upper = create_subregion(regBitFS, "BitFS: Upper", *locBitFS_table.keys())
181+
regBitFS.subregions = [bitfs_upper]
167182

168183
create_region("Second Floor", player, world)
169184

@@ -176,7 +191,8 @@ def create_regions(world: MultiWorld, player: int):
176191
create_locs(regWDW, "WDW: Express Elevator--Hurry Up!")
177192
wdw_top = create_subregion(regWDW, "WDW: Top", "WDW: Shocking Arrow Lifts!", "WDW: Top o' the Town",
178193
"WDW: Secrets in the Shallows & Sky", "WDW: Bob-omb Buddy")
179-
create_subregion(regWDW, "WDW: Downtown", "WDW: Go to Town for Red Coins", "WDW: Quick Race Through Downtown!", "WDW: 1Up Block in Downtown")
194+
wdw_downtown = create_subregion(regWDW, "WDW: Downtown", "WDW: Go to Town for Red Coins", "WDW: Quick Race Through Downtown!", "WDW: 1Up Block in Downtown")
195+
regWDW.subregions = [wdw_top, wdw_downtown]
180196
if (world.EnableCoinStars[player].value):
181197
create_locs(wdw_top, "WDW: 100 Coins")
182198

@@ -185,17 +201,19 @@ def create_regions(world: MultiWorld, player: int):
185201
"TTM: Bob-omb Buddy", "TTM: 1Up Block on Red Mushroom")
186202
ttm_top = create_subregion(ttm_middle, "TTM: Top", "TTM: Scale the Mountain", "TTM: Mystery of the Monkey Cage",
187203
"TTM: Mysterious Mountainside", "TTM: Breathtaking View from Bridge")
204+
regTTM.subregions = [ttm_middle, ttm_top]
188205
if (world.EnableCoinStars[player].value):
189206
create_locs(ttm_top, "TTM: 100 Coins")
190207

191208
create_region("Tiny-Huge Island (Huge)", player, world)
192209
create_region("Tiny-Huge Island (Tiny)", player, world)
193210
regTHI = create_region("Tiny-Huge Island", player, world)
194-
create_locs(regTHI, "THI: The Tip Top of the Huge Island", "THI: 1Up Block THI Small near Start")
195-
thi_pipes = create_subregion(regTHI, "THI: Pipes", "THI: Pluck the Piranha Flower", "THI: Rematch with Koopa the Quick",
211+
create_locs(regTHI, "THI: 1Up Block THI Small near Start")
212+
thi_pipes = create_subregion(regTHI, "THI: Pipes", "THI: The Tip Top of the Huge Island", "THI: Pluck the Piranha Flower", "THI: Rematch with Koopa the Quick",
196213
"THI: Five Itty Bitty Secrets", "THI: Wiggler's Red Coins", "THI: Bob-omb Buddy",
197214
"THI: 1Up Block THI Large near Start", "THI: 1Up Block Windy Area")
198215
thi_large_top = create_subregion(thi_pipes, "THI: Large Top", "THI: Make Wiggler Squirm")
216+
regTHI.subregions = [thi_pipes, thi_large_top]
199217
if (world.EnableCoinStars[player].value):
200218
create_locs(thi_large_top, "THI: 100 Coins")
201219

@@ -206,15 +224,17 @@ def create_regions(world: MultiWorld, player: int):
206224
ttc_lower = create_subregion(regTTC, "TTC: Lower", "TTC: Roll into the Cage", "TTC: Get a Hand", "TTC: 1Up Block Midway Up")
207225
ttc_upper = create_subregion(ttc_lower, "TTC: Upper", "TTC: Timed Jumps on Moving Bars", "TTC: The Pit and the Pendulums")
208226
ttc_top = create_subregion(ttc_upper, "TTC: Top", "TTC: Stomp on the Thwomp", "TTC: 1Up Block at the Top")
227+
regTTC.subregions = [ttc_lower, ttc_upper, ttc_top]
209228
if (world.EnableCoinStars[player].value):
210229
create_locs(ttc_top, "TTC: 100 Coins")
211230

212231
regRR = create_region("Rainbow Ride", player, world)
213232
create_locs(regRR, "RR: Swingin' in the Breeze", "RR: Tricky Triangles!",
214233
"RR: 1Up Block Top of Red Coin Maze", "RR: 1Up Block Under Fly Guy", "RR: Bob-omb Buddy")
215234
rr_maze = create_subregion(regRR, "RR: Maze", "RR: Coins Amassed in a Maze")
216-
create_subregion(regRR, "RR: Cruiser", "RR: Cruiser Crossing the Rainbow", "RR: Somewhere Over the Rainbow")
217-
create_subregion(regRR, "RR: House", "RR: The Big House in the Sky", "RR: 1Up Block On House in the Sky")
235+
rr_cruiser = create_subregion(regRR, "RR: Cruiser", "RR: Cruiser Crossing the Rainbow", "RR: Somewhere Over the Rainbow")
236+
rr_house = create_subregion(regRR, "RR: House", "RR: The Big House in the Sky", "RR: 1Up Block On House in the Sky")
237+
regRR.subregions = [rr_maze, rr_cruiser, rr_house]
218238
if (world.EnableCoinStars[player].value):
219239
create_locs(rr_maze, "RR: 100 Coins")
220240

@@ -223,7 +243,8 @@ def create_regions(world: MultiWorld, player: int):
223243

224244
regBitS = create_region("Bowser in the Sky", player, world)
225245
create_locs(regBitS, "Bowser in the Sky 1Up Block")
226-
create_subregion(regBitS, "BitS: Top", "Bowser in the Sky Red Coins")
246+
bits_top = create_subregion(regBitS, "BitS: Top", "Bowser in the Sky Red Coins")
247+
regBitS.subregions = [bits_top]
227248

228249

229250
def connect_regions(world: MultiWorld, player: int, source: str, target: str, rule=None):
@@ -232,14 +253,14 @@ def connect_regions(world: MultiWorld, player: int, source: str, target: str, ru
232253
sourceRegion.connect(targetRegion, rule=rule)
233254

234255

235-
def create_region(name: str, player: int, world: MultiWorld) -> Region:
236-
region = Region(name, player, world)
256+
def create_region(name: str, player: int, world: MultiWorld) -> SM64Region:
257+
region = SM64Region(name, player, world)
237258
world.regions.append(region)
238259
return region
239260

240261

241-
def create_subregion(source_region: Region, name: str, *locs: str) -> Region:
242-
region = Region(name, source_region.player, source_region.multiworld)
262+
def create_subregion(source_region: Region, name: str, *locs: str) -> SM64Region:
263+
region = SM64Region(name, source_region.player, source_region.multiworld)
243264
connection = Entrance(source_region.player, name, source_region)
244265
source_region.exits.append(connection)
245266
connection.connect(region)

worlds/sm64ex/Rules.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ def set_rules(world, player: int, area_connections: dict, star_costs: dict, move
107107

108108
connect_regions(world, player, "Second Floor", "Third Floor", lambda state: state.has("Power Star", player, star_costs["SecondFloorDoorCost"]))
109109

110-
connect_regions(world, player, "Third Floor", randomized_entrances_s["Tick Tock Clock"])
111-
connect_regions(world, player, "Third Floor", randomized_entrances_s["Rainbow Ride"])
112-
connect_regions(world, player, "Third Floor", randomized_entrances_s["Wing Mario over the Rainbow"])
110+
connect_regions(world, player, "Third Floor", randomized_entrances_s["Tick Tock Clock"], rf.build_rule("LG/TJ/SF/BF/WK"))
111+
connect_regions(world, player, "Third Floor", randomized_entrances_s["Rainbow Ride"], rf.build_rule("TJ/SF/BF"))
112+
connect_regions(world, player, "Third Floor", randomized_entrances_s["Wing Mario over the Rainbow"], rf.build_rule("TJ/SF/BF"))
113113
connect_regions(world, player, "Third Floor", "Bowser in the Sky", lambda state: state.has("Power Star", player, star_costs["StarsToFinish"]))
114114

115115
# Course Rules
@@ -146,7 +146,7 @@ def set_rules(world, player: int, area_connections: dict, star_costs: dict, move
146146
rf.assign_rule("LLL: Upper Volcano", "CL")
147147
# Shifting Sand Land
148148
rf.assign_rule("SSL: Upper Pyramid", "CL & TJ/BF/SF/LG | MOVELESS")
149-
rf.assign_rule("SSL: Free Flying for 8 Red Coins", "TJ/SF/BF & TJ+WC | TJ/SF/BF & CAPLESS | MOVELESS")
149+
rf.assign_rule("SSL: Free Flying for 8 Red Coins", "TJ/SF/BF & TJ+WC | TJ/SF/BF & CAPLESS | MOVELESS & CAPLESS")
150150
# Dire, Dire Docks
151151
rf.assign_rule("DDD: Moving Poles", "CL & {{Bowser in the Fire Sea Key}} | TJ+DV+LG+WK & MOVELESS")
152152
rf.assign_rule("DDD: Through the Jet Stream", "MC | CAPLESS")
@@ -165,6 +165,7 @@ def set_rules(world, player: int, area_connections: dict, star_costs: dict, move
165165
rf.assign_rule("TTM: Top", "MOVELESS & TJ | LJ/DV & LG/KK | MOVELESS & WK & SF/LG | MOVELESS & KK/DV")
166166
rf.assign_rule("TTM: Blast to the Lonely Mushroom", "CANN | CANNLESS & LJ | MOVELESS & CANNLESS")
167167
# Tiny-Huge Island
168+
rf.assign_rule("THI: 1Up Block THI Small near Start", "NAR | {THI: Pipes}")
168169
rf.assign_rule("THI: Pipes", "NAR | LJ/TJ/DV/LG | MOVELESS & BF/SF/KK")
169170
rf.assign_rule("THI: Large Top", "NAR | LJ/TJ/DV | MOVELESS")
170171
rf.assign_rule("THI: Wiggler's Red Coins", "WK")
@@ -225,11 +226,11 @@ def set_rules(world, player: int, area_connections: dict, star_costs: dict, move
225226
world.completion_condition[player] = lambda state: state.can_reach("BitS: Top", 'Region', player)
226227

227228
if world.CompletionType[player] == "last_bowser_stage":
228-
world.completion_condition[player] = lambda state: state.can_reach("Bowser in the Sky", 'Region', player)
229+
world.completion_condition[player] = lambda state: state.can_reach("BitS: Top", 'Region', player)
229230
elif world.CompletionType[player] == "all_bowser_stages":
230231
world.completion_condition[player] = lambda state: state.can_reach("Bowser in the Dark World", 'Region', player) and \
231-
state.can_reach("Bowser in the Fire Sea", 'Region', player) and \
232-
state.can_reach("Bowser in the Sky", 'Region', player)
232+
state.can_reach("BitFS: Upper", 'Region', player) and \
233+
state.can_reach("BitS: Top", 'Region', player)
233234

234235

235236
class RuleFactory:

worlds/sm64ex/__init__.py

+17-7
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
from .Locations import location_table, SM64Location
66
from .Options import sm64_options
77
from .Rules import set_rules
8-
from .Regions import create_regions, sm64_level_to_entrances
9-
from BaseClasses import Item, Tutorial, ItemClassification
8+
from .Regions import create_regions, sm64_level_to_entrances, SM64Levels
9+
from BaseClasses import Item, Tutorial, ItemClassification, Region
1010
from ..AutoWorld import World, WebWorld
1111

1212

@@ -200,11 +200,21 @@ def generate_output(self, output_directory: str):
200200
with open(os.path.join(output_directory, filename), 'w') as f:
201201
json.dump(data, f)
202202

203-
def modify_multidata(self, multidata):
203+
def extend_hint_information(self, hint_data: typing.Dict[int, typing.Dict[int, str]]):
204204
if self.topology_present:
205205
er_hint_data = {}
206206
for entrance, destination in self.area_connections.items():
207-
region = self.multiworld.get_region(sm64_level_to_entrances[destination], self.player)
208-
for location in region.locations:
209-
er_hint_data[location.address] = sm64_level_to_entrances[entrance]
210-
multidata['er_hint_data'][self.player] = er_hint_data
207+
regions = [self.multiworld.get_region(sm64_level_to_entrances[destination], self.player)]
208+
if regions[0].name == "Tiny-Huge Island (Huge)":
209+
# Special rules for Tiny-Huge Island's dual entrances
210+
reverse_area_connections = {destination: entrance for entrance, destination in self.area_connections.items()}
211+
entrance_name = sm64_level_to_entrances[reverse_area_connections[SM64Levels.TINY_HUGE_ISLAND_HUGE]] \
212+
+ ' or ' + sm64_level_to_entrances[reverse_area_connections[SM64Levels.TINY_HUGE_ISLAND_TINY]]
213+
regions[0] = self.multiworld.get_region("Tiny-Huge Island", self.player)
214+
else:
215+
entrance_name = sm64_level_to_entrances[entrance]
216+
regions += regions[0].subregions
217+
for region in regions:
218+
for location in region.locations:
219+
er_hint_data[location.address] = entrance_name
220+
hint_data[self.player] = er_hint_data

0 commit comments

Comments
 (0)