Skip to content

Commit 530617c

Browse files
authored
sm64ex: Refactor Regions (#2546)
Refactors region code to remove references to course index. There were bugs somewhere, but I dont know where tbh. This fixes them but leaves logic otherwise intact, and much cleaner to look at as there's one list less to take care of. Additionally, this fixes stopping the clock from Big Boos Haunt.
1 parent 229a263 commit 530617c

File tree

3 files changed

+111
-86
lines changed

3 files changed

+111
-86
lines changed

worlds/sm64ex/Regions.py

+43-24
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,43 @@
55
locHMC_table, locLLL_table, locSSL_table, locDDD_table, locSL_table, \
66
locWDW_table, locTTM_table, locTHI_table, locTTC_table, locRR_table, \
77
locPSS_table, locSA_table, locBitDW_table, locTotWC_table, locCotMC_table, \
8-
locVCutM_table, locBitFS_table, locWMotR_table, locBitS_table, locSS_table
9-
10-
# List of all courses, including secrets, without BitS as that one is static
11-
sm64courses = ["Bob-omb Battlefield", "Whomp's Fortress", "Jolly Roger Bay", "Cool, Cool Mountain", "Big Boo's Haunt",
12-
"Hazy Maze Cave", "Lethal Lava Land", "Shifting Sand Land", "Dire, Dire Docks", "Snowman's Land",
13-
"Wet-Dry World", "Tall, Tall Mountain", "Tiny-Huge Island", "Tick Tock Clock", "Rainbow Ride",
14-
"The Princess's Secret Slide", "The Secret Aquarium", "Bowser in the Dark World", "Tower of the Wing Cap",
15-
"Cavern of the Metal Cap", "Vanish Cap under the Moat", "Bowser in the Fire Sea", "Wing Mario over the Rainbow"]
16-
17-
# sm64paintings is list of entrances, format LEVEL | AREA. String Reference below
18-
sm64paintings = [91,241,121,51,41,71,221,81,231,101,111,361,132,131,141,151]
19-
sm64paintings_s = ["BOB", "WF", "JRB", "CCM", "BBH", "HMC", "LLL", "SSL", "DDD", "SL", "WDW", "TTM", "THI Tiny", "THI Huge", "TTC", "RR"]
20-
# sm64secrets is list of secret areas
21-
sm64secrets = [271, 201, 171, 291, 281, 181, 191, 311]
22-
sm64secrets_s = ["PSS", "SA", "BitDW", "TOTWC", "COTMC", "VCUTM", "BitFS", "WMOTR"]
23-
24-
sm64entrances = sm64paintings + sm64secrets
25-
sm64entrances_s = sm64paintings_s + sm64secrets_s
26-
sm64_internalloc_to_string = dict(zip(sm64paintings+sm64secrets, sm64entrances_s))
27-
sm64_internalloc_to_regionid = dict(zip(sm64paintings+sm64secrets, list(range(13)) + [12,13,14] + list(range(15,15+len(sm64secrets)))))
8+
locVCutM_table, locBitFS_table, locWMotR_table, locBitS_table, locSS_table
9+
10+
# sm64paintings is dict of entrances, format LEVEL | AREA
11+
sm64_level_to_paintings = {
12+
91: "Bob-omb Battlefield",
13+
241: "Whomp's Fortress",
14+
121: "Jolly Roger Bay",
15+
51: "Cool, Cool Mountain",
16+
41: "Big Boo's Haunt",
17+
71: "Hazy Maze Cave",
18+
221: "Lethal Lava Land",
19+
81: "Shifting Sand Land",
20+
231: "Dire, Dire Docks",
21+
101: "Snowman's Land",
22+
111: "Wet-Dry World",
23+
361: "Tall, Tall Mountain",
24+
132: "Tiny-Huge Island (Tiny)",
25+
131: "Tiny-Huge Island (Huge)",
26+
141: "Tick Tock Clock",
27+
151: "Rainbow Ride"
28+
}
29+
sm64_paintings_to_level = { painting: level for (level,painting) in sm64_level_to_paintings.items() }
30+
# sm64secrets is list of secret areas, same format
31+
sm64_level_to_secrets = {
32+
271: "The Princess's Secret Slide",
33+
201: "The Secret Aquarium",
34+
171: "Bowser in the Dark World",
35+
291: "Tower of the Wing Cap",
36+
281: "Cavern of the Metal Cap",
37+
181: "Vanish Cap under the Moat",
38+
191: "Bowser in the Fire Sea",
39+
311: "Wing Mario over the Rainbow"
40+
}
41+
sm64_secrets_to_level = { secret: level for (level,secret) in sm64_level_to_secrets.items() }
42+
43+
sm64_entrances_to_level = { **sm64_paintings_to_level, **sm64_secrets_to_level }
44+
sm64_level_to_entrances = { **sm64_level_to_paintings, **sm64_level_to_secrets }
2845

2946
def create_regions(world: MultiWorld, player: int):
3047
regSS = Region("Menu", player, world, "Castle Area")
@@ -137,11 +154,13 @@ def create_regions(world: MultiWorld, player: int):
137154
regTTM.locations.append(SM64Location(player, "TTM: 100 Coins", location_table["TTM: 100 Coins"], regTTM))
138155
world.regions.append(regTTM)
139156

140-
regTHI = create_region("Tiny-Huge Island", player, world)
141-
create_default_locs(regTHI, locTHI_table, player)
157+
regTHIT = create_region("Tiny-Huge Island (Tiny)", player, world)
158+
create_default_locs(regTHIT, locTHI_table, player)
142159
if (world.EnableCoinStars[player].value):
143-
regTHI.locations.append(SM64Location(player, "THI: 100 Coins", location_table["THI: 100 Coins"], regTHI))
144-
world.regions.append(regTHI)
160+
regTHIT.locations.append(SM64Location(player, "THI: 100 Coins", location_table["THI: 100 Coins"], regTHIT))
161+
world.regions.append(regTHIT)
162+
regTHIH = create_region("Tiny-Huge Island (Huge)", player, world)
163+
world.regions.append(regTHIH)
145164

146165
regFloor3 = create_region("Third Floor", player, world)
147166
world.regions.append(regFloor3)

worlds/sm64ex/Rules.py

+63-56
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,84 @@
11
from ..generic.Rules import add_rule
2-
from .Regions import connect_regions, sm64courses, sm64paintings, sm64secrets, sm64entrances
3-
4-
def fix_reg(entrance_ids, reg, invalidspot, swaplist, world):
5-
if entrance_ids.index(reg) == invalidspot: # Unlucky :C
6-
swaplist.remove(invalidspot)
7-
rand = world.random.choice(swaplist)
8-
entrance_ids[invalidspot], entrance_ids[rand] = entrance_ids[rand], entrance_ids[invalidspot]
9-
swaplist.append(invalidspot)
10-
swaplist.remove(rand)
11-
12-
def set_rules(world, player: int, area_connections):
13-
destination_regions = list(range(13)) + [12,13,14] + list(range(15,15+len(sm64secrets))) # Two instances of Destination Course THI. Past normal course idx are secret regions
14-
secret_entrance_ids = list(range(len(sm64paintings), len(sm64paintings) + len(sm64secrets)))
15-
course_entrance_ids = list(range(len(sm64paintings)))
16-
if world.AreaRandomizer[player].value >= 1: # Some randomization is happening, randomize Courses
17-
world.random.shuffle(course_entrance_ids)
2+
from .Regions import connect_regions, sm64_level_to_paintings, sm64_paintings_to_level, sm64_level_to_secrets, sm64_entrances_to_level, sm64_level_to_entrances
3+
4+
def shuffle_dict_keys(world, obj: dict) -> dict:
5+
keys = list(obj.keys())
6+
values = list(obj.values())
7+
world.random.shuffle(keys)
8+
return dict(zip(keys,values))
9+
10+
def fix_reg(entrance_ids, entrance, destination, swapdict, world):
11+
if entrance_ids[entrance] == destination: # Unlucky :C
12+
rand = world.random.choice(swapdict.keys())
13+
entrance_ids[entrance], entrance_ids[swapdict[rand]] = rand, entrance_ids[entrance]
14+
swapdict[rand] = entrance_ids[entrance]
15+
swapdict.pop(entrance)
16+
17+
def set_rules(world, player: int, area_connections: dict):
18+
randomized_level_to_paintings = sm64_level_to_paintings.copy()
19+
randomized_level_to_secrets = sm64_level_to_secrets.copy()
20+
if world.AreaRandomizer[player].value == 1: # Some randomization is happening, randomize Courses
21+
randomized_level_to_paintings = shuffle_dict_keys(world,sm64_level_to_paintings)
1822
if world.AreaRandomizer[player].value == 2: # Randomize Secrets as well
19-
world.random.shuffle(secret_entrance_ids)
20-
entrance_ids = course_entrance_ids + secret_entrance_ids
23+
randomized_level_to_secrets = shuffle_dict_keys(world,sm64_level_to_secrets)
24+
randomized_entrances = { **randomized_level_to_paintings, **randomized_level_to_secrets }
2125
if world.AreaRandomizer[player].value == 3: # Randomize Courses and Secrets in one pool
22-
world.random.shuffle(entrance_ids)
26+
randomized_entrances = shuffle_dict_keys(world,randomized_entrances)
2327
# Guarantee first entrance is a course
24-
swaplist = list(range(len(entrance_ids)))
25-
if entrance_ids.index(0) > 15: # Unlucky :C
26-
rand = world.random.randint(0,15)
27-
entrance_ids[entrance_ids.index(0)], entrance_ids[rand] = entrance_ids[rand], entrance_ids[entrance_ids.index(0)]
28-
swaplist.remove(entrance_ids.index(0))
28+
swapdict = { entrance: level for (level,entrance) in randomized_entrances }
29+
if randomized_entrances[91] not in sm64_paintings_to_level.keys(): # Unlucky :C (91 -> BoB Entrance)
30+
rand = world.random.choice(sm64_paintings_to_level.values())
31+
randomized_entrances[91], randomized_entrances[swapdict[rand]] = rand, randomized_entrances[91]
32+
swapdict[rand] = randomized_entrances[91]
33+
swapdict.pop("Bob-omb Battlefield")
2934
# Guarantee COTMC is not mapped to HMC, cuz thats impossible
30-
fix_reg(entrance_ids, 20, 5, swaplist, world)
35+
fix_reg(randomized_entrances, "Cavern of the Metal Cap", "Hazy Maze Cave", swapdict, world)
3136
# Guarantee BITFS is not mapped to DDD
32-
fix_reg(entrance_ids, 22, 8, swaplist, world)
33-
if entrance_ids.index(22) == 5: # If BITFS is mapped to HMC...
34-
fix_reg(entrance_ids, 20, 8, swaplist, world) # ... then dont allow COTMC to be mapped to DDD
35-
temp_assign = dict(zip(entrance_ids,destination_regions)) # Used for Rules only
37+
fix_reg(randomized_entrances, "Bowser in the Fire Sea", "Dire, Dire Docks", swapdict, world)
38+
if randomized_entrances[191] == "Hazy Maze Cave": # If BITFS is mapped to HMC...
39+
fix_reg(randomized_entrances, "Cavern of the Metal Cap", "Dire, Dire Docks", swapdict, world) # ... then dont allow COTMC to be mapped to DDD
3640

3741
# Destination Format: LVL | AREA with LVL = LEVEL_x, AREA = Area as used in sm64 code
38-
area_connections.update({sm64entrances[entrance]: destination for entrance, destination in zip(entrance_ids,sm64entrances)})
42+
area_connections.update({entrance_lvl: sm64_entrances_to_level[destination] for (entrance_lvl,destination) in randomized_entrances.items()})
43+
randomized_entrances_s = {sm64_level_to_entrances[entrance_lvl]: destination for (entrance_lvl,destination) in randomized_entrances.items()}
3944

40-
connect_regions(world, player, "Menu", sm64courses[temp_assign[0]]) # BOB
41-
connect_regions(world, player, "Menu", sm64courses[temp_assign[1]], lambda state: state.has("Power Star", player, 1)) # WF
42-
connect_regions(world, player, "Menu", sm64courses[temp_assign[2]], lambda state: state.has("Power Star", player, 3)) # JRB
43-
connect_regions(world, player, "Menu", sm64courses[temp_assign[3]], lambda state: state.has("Power Star", player, 3)) # CCM
44-
connect_regions(world, player, "Menu", sm64courses[temp_assign[4]], lambda state: state.has("Power Star", player, 12)) # BBH
45-
connect_regions(world, player, "Menu", sm64courses[temp_assign[16]], lambda state: state.has("Power Star", player, 1)) # PSS
46-
connect_regions(world, player, "Menu", sm64courses[temp_assign[17]], lambda state: state.has("Power Star", player, 3)) # SA
47-
connect_regions(world, player, "Menu", sm64courses[temp_assign[19]], lambda state: state.has("Power Star", player, 10)) # TOTWC
48-
connect_regions(world, player, "Menu", sm64courses[temp_assign[18]], lambda state: state.has("Power Star", player, world.FirstBowserStarDoorCost[player].value)) # BITDW
45+
connect_regions(world, player, "Menu", randomized_entrances_s["Bob-omb Battlefield"])
46+
connect_regions(world, player, "Menu", randomized_entrances_s["Whomp's Fortress"], lambda state: state.has("Power Star", player, 1))
47+
connect_regions(world, player, "Menu", randomized_entrances_s["Jolly Roger Bay"], lambda state: state.has("Power Star", player, 3))
48+
connect_regions(world, player, "Menu", randomized_entrances_s["Cool, Cool Mountain"], lambda state: state.has("Power Star", player, 3))
49+
connect_regions(world, player, "Menu", randomized_entrances_s["Big Boo's Haunt"], lambda state: state.has("Power Star", player, 12))
50+
connect_regions(world, player, "Menu", randomized_entrances_s["The Princess's Secret Slide"], lambda state: state.has("Power Star", player, 1))
51+
connect_regions(world, player, "Menu", randomized_entrances_s["The Secret Aquarium"], lambda state: state.has("Power Star", player, 3))
52+
connect_regions(world, player, "Menu", randomized_entrances_s["Tower of the Wing Cap"], lambda state: state.has("Power Star", player, 10))
53+
connect_regions(world, player, "Menu", randomized_entrances_s["Bowser in the Dark World"], lambda state: state.has("Power Star", player, world.FirstBowserStarDoorCost[player].value))
4954

5055
connect_regions(world, player, "Menu", "Basement", lambda state: state.has("Basement Key", player) or state.has("Progressive Key", player, 1))
5156

52-
connect_regions(world, player, "Basement", sm64courses[temp_assign[5]]) # HMC
53-
connect_regions(world, player, "Basement", sm64courses[temp_assign[6]]) # LLL
54-
connect_regions(world, player, "Basement", sm64courses[temp_assign[7]]) # SSL
55-
connect_regions(world, player, "Basement", sm64courses[temp_assign[8]], lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value)) # DDD
56-
connect_regions(world, player, "Hazy Maze Cave", sm64courses[temp_assign[20]]) # COTMC
57-
connect_regions(world, player, "Basement", sm64courses[temp_assign[21]]) # VCUTM
58-
connect_regions(world, player, "Basement", sm64courses[temp_assign[22]], lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value) and
59-
state.can_reach("DDD: Board Bowser's Sub", 'Location', player)) # BITFS
57+
connect_regions(world, player, "Basement", randomized_entrances_s["Hazy Maze Cave"])
58+
connect_regions(world, player, "Basement", randomized_entrances_s["Lethal Lava Land"])
59+
connect_regions(world, player, "Basement", randomized_entrances_s["Shifting Sand Land"])
60+
connect_regions(world, player, "Basement", randomized_entrances_s["Dire, Dire Docks"], lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value))
61+
connect_regions(world, player, "Hazy Maze Cave", randomized_entrances_s["Cavern of the Metal Cap"])
62+
connect_regions(world, player, "Basement", randomized_entrances_s["Vanish Cap under the Moat"])
63+
connect_regions(world, player, "Basement", randomized_entrances_s["Bowser in the Fire Sea"], lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value) and
64+
state.can_reach("DDD: Board Bowser's Sub", 'Location', player))
6065

6166
connect_regions(world, player, "Menu", "Second Floor", lambda state: state.has("Second Floor Key", player) or state.has("Progressive Key", player, 2))
6267

63-
connect_regions(world, player, "Second Floor", sm64courses[temp_assign[9]]) # SL
64-
connect_regions(world, player, "Second Floor", sm64courses[temp_assign[10]]) # WDW
65-
connect_regions(world, player, "Second Floor", sm64courses[temp_assign[11]]) # TTM
66-
connect_regions(world, player, "Second Floor", sm64courses[temp_assign[12]]) # THI Tiny
67-
connect_regions(world, player, "Second Floor", sm64courses[temp_assign[13]]) # THI Huge
68+
connect_regions(world, player, "Second Floor", randomized_entrances_s["Snowman's Land"])
69+
connect_regions(world, player, "Second Floor", randomized_entrances_s["Wet-Dry World"])
70+
connect_regions(world, player, "Second Floor", randomized_entrances_s["Tall, Tall Mountain"])
71+
connect_regions(world, player, "Second Floor", randomized_entrances_s["Tiny-Huge Island (Tiny)"])
72+
connect_regions(world, player, "Second Floor", randomized_entrances_s["Tiny-Huge Island (Huge)"])
73+
connect_regions(world, player, "Tiny-Huge Island (Tiny)", "Tiny-Huge Island (Huge)")
74+
connect_regions(world, player, "Tiny-Huge Island (Huge)", "Tiny-Huge Island (Tiny)")
6875

6976
connect_regions(world, player, "Second Floor", "Third Floor", lambda state: state.has("Power Star", player, world.SecondFloorStarDoorCost[player].value))
7077

71-
connect_regions(world, player, "Third Floor", sm64courses[temp_assign[14]]) # TTC
72-
connect_regions(world, player, "Third Floor", sm64courses[temp_assign[15]]) # RR
73-
connect_regions(world, player, "Third Floor", sm64courses[temp_assign[23]]) # WMOTR
74-
connect_regions(world, player, "Third Floor", "Bowser in the Sky", lambda state: state.has("Power Star", player, world.StarsToFinish[player].value)) # BITS
78+
connect_regions(world, player, "Third Floor", randomized_entrances_s["Tick Tock Clock"])
79+
connect_regions(world, player, "Third Floor", randomized_entrances_s["Rainbow Ride"])
80+
connect_regions(world, player, "Third Floor", randomized_entrances_s["Wing Mario over the Rainbow"])
81+
connect_regions(world, player, "Third Floor", "Bowser in the Sky", lambda state: state.has("Power Star", player, world.StarsToFinish[player].value))
7582

7683
#Special Rules for some Locations
7784
add_rule(world.get_location("BoB: Mario Wings to the Sky", player), lambda state: state.has("Cannon Unlock BoB", player))

worlds/sm64ex/__init__.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
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, sm64courses, sm64entrances_s, sm64_internalloc_to_string, sm64_internalloc_to_regionid
8+
from .Regions import create_regions, sm64_level_to_entrances
99
from BaseClasses import Item, Tutorial, ItemClassification
1010
from ..AutoWorld import World, WebWorld
1111

@@ -55,8 +55,8 @@ def set_rules(self):
5555
# Write area_connections to spoiler log
5656
for entrance, destination in self.area_connections.items():
5757
self.multiworld.spoiler.set_entrance(
58-
sm64_internalloc_to_string[entrance] + " Entrance",
59-
sm64_internalloc_to_string[destination],
58+
sm64_level_to_entrances[entrance] + " Entrance",
59+
sm64_level_to_entrances[destination],
6060
'entrance', self.player)
6161

6262
def create_item(self, name: str) -> Item:
@@ -182,8 +182,7 @@ def modify_multidata(self, multidata):
182182
if self.topology_present:
183183
er_hint_data = {}
184184
for entrance, destination in self.area_connections.items():
185-
regionid = sm64_internalloc_to_regionid[destination]
186-
region = self.multiworld.get_region(sm64courses[regionid], self.player)
185+
region = self.multiworld.get_region(sm64_level_to_entrances[destination], self.player)
187186
for location in region.locations:
188-
er_hint_data[location.address] = sm64_internalloc_to_string[entrance]
187+
er_hint_data[location.address] = sm64_level_to_entrances[entrance]
189188
multidata['er_hint_data'][self.player] = er_hint_data

0 commit comments

Comments
 (0)