diff --git a/worlds/sm64ex/Regions.py b/worlds/sm64ex/Regions.py index c2e9e2d98115..243501d9394d 100644 --- a/worlds/sm64ex/Regions.py +++ b/worlds/sm64ex/Regions.py @@ -5,26 +5,43 @@ locHMC_table, locLLL_table, locSSL_table, locDDD_table, locSL_table, \ locWDW_table, locTTM_table, locTHI_table, locTTC_table, locRR_table, \ locPSS_table, locSA_table, locBitDW_table, locTotWC_table, locCotMC_table, \ - locVCutM_table, locBitFS_table, locWMotR_table, locBitS_table, locSS_table - -# List of all courses, including secrets, without BitS as that one is static -sm64courses = ["Bob-omb Battlefield", "Whomp's Fortress", "Jolly Roger Bay", "Cool, Cool Mountain", "Big Boo's Haunt", - "Hazy Maze Cave", "Lethal Lava Land", "Shifting Sand Land", "Dire, Dire Docks", "Snowman's Land", - "Wet-Dry World", "Tall, Tall Mountain", "Tiny-Huge Island", "Tick Tock Clock", "Rainbow Ride", - "The Princess's Secret Slide", "The Secret Aquarium", "Bowser in the Dark World", "Tower of the Wing Cap", - "Cavern of the Metal Cap", "Vanish Cap under the Moat", "Bowser in the Fire Sea", "Wing Mario over the Rainbow"] - -# sm64paintings is list of entrances, format LEVEL | AREA. String Reference below -sm64paintings = [91,241,121,51,41,71,221,81,231,101,111,361,132,131,141,151] -sm64paintings_s = ["BOB", "WF", "JRB", "CCM", "BBH", "HMC", "LLL", "SSL", "DDD", "SL", "WDW", "TTM", "THI Tiny", "THI Huge", "TTC", "RR"] -# sm64secrets is list of secret areas -sm64secrets = [271, 201, 171, 291, 281, 181, 191, 311] -sm64secrets_s = ["PSS", "SA", "BitDW", "TOTWC", "COTMC", "VCUTM", "BitFS", "WMOTR"] - -sm64entrances = sm64paintings + sm64secrets -sm64entrances_s = sm64paintings_s + sm64secrets_s -sm64_internalloc_to_string = dict(zip(sm64paintings+sm64secrets, sm64entrances_s)) -sm64_internalloc_to_regionid = dict(zip(sm64paintings+sm64secrets, list(range(13)) + [12,13,14] + list(range(15,15+len(sm64secrets))))) + locVCutM_table, locBitFS_table, locWMotR_table, locBitS_table, locSS_table + +# sm64paintings is dict of entrances, format LEVEL | AREA +sm64_level_to_paintings = { + 91: "Bob-omb Battlefield", + 241: "Whomp's Fortress", + 121: "Jolly Roger Bay", + 51: "Cool, Cool Mountain", + 41: "Big Boo's Haunt", + 71: "Hazy Maze Cave", + 221: "Lethal Lava Land", + 81: "Shifting Sand Land", + 231: "Dire, Dire Docks", + 101: "Snowman's Land", + 111: "Wet-Dry World", + 361: "Tall, Tall Mountain", + 132: "Tiny-Huge Island (Tiny)", + 131: "Tiny-Huge Island (Huge)", + 141: "Tick Tock Clock", + 151: "Rainbow Ride" +} +sm64_paintings_to_level = { painting: level for (level,painting) in sm64_level_to_paintings.items() } +# sm64secrets is list of secret areas, same format +sm64_level_to_secrets = { + 271: "The Princess's Secret Slide", + 201: "The Secret Aquarium", + 171: "Bowser in the Dark World", + 291: "Tower of the Wing Cap", + 281: "Cavern of the Metal Cap", + 181: "Vanish Cap under the Moat", + 191: "Bowser in the Fire Sea", + 311: "Wing Mario over the Rainbow" +} +sm64_secrets_to_level = { secret: level for (level,secret) in sm64_level_to_secrets.items() } + +sm64_entrances_to_level = sm64_paintings_to_level | sm64_secrets_to_level +sm64_level_to_entrances = sm64_level_to_paintings | sm64_level_to_secrets def create_regions(world: MultiWorld, player: int): regSS = Region("Menu", player, world, "Castle Area") @@ -137,11 +154,13 @@ def create_regions(world: MultiWorld, player: int): regTTM.locations.append(SM64Location(player, "TTM: 100 Coins", location_table["TTM: 100 Coins"], regTTM)) world.regions.append(regTTM) - regTHI = create_region("Tiny-Huge Island", player, world) - create_default_locs(regTHI, locTHI_table, player) + regTHIT = create_region("Tiny-Huge Island (Tiny)", player, world) + create_default_locs(regTHIT, locTHI_table, player) if (world.EnableCoinStars[player].value): - regTHI.locations.append(SM64Location(player, "THI: 100 Coins", location_table["THI: 100 Coins"], regTHI)) - world.regions.append(regTHI) + regTHIT.locations.append(SM64Location(player, "THI: 100 Coins", location_table["THI: 100 Coins"], regTHIT)) + world.regions.append(regTHIT) + regTHIH = create_region("Tiny-Huge Island (Huge)", player, world) + world.regions.append(regTHIH) regFloor3 = create_region("Third Floor", player, world) world.regions.append(regFloor3) diff --git a/worlds/sm64ex/Rules.py b/worlds/sm64ex/Rules.py index 27b5fc8f7e38..e32bb79fee66 100644 --- a/worlds/sm64ex/Rules.py +++ b/worlds/sm64ex/Rules.py @@ -1,77 +1,85 @@ from ..generic.Rules import add_rule -from .Regions import connect_regions, sm64courses, sm64paintings, sm64secrets, sm64entrances - -def fix_reg(entrance_ids, reg, invalidspot, swaplist, world): - if entrance_ids.index(reg) == invalidspot: # Unlucky :C - swaplist.remove(invalidspot) - rand = world.random.choice(swaplist) - entrance_ids[invalidspot], entrance_ids[rand] = entrance_ids[rand], entrance_ids[invalidspot] - swaplist.append(invalidspot) - swaplist.remove(rand) - -def set_rules(world, player: int, area_connections): - 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 - secret_entrance_ids = list(range(len(sm64paintings), len(sm64paintings) + len(sm64secrets))) - course_entrance_ids = list(range(len(sm64paintings))) - if world.AreaRandomizer[player].value >= 1: # Some randomization is happening, randomize Courses - world.random.shuffle(course_entrance_ids) +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 + +def shuffle_dict_keys(world, obj: dict) -> dict: + keys = list(obj.keys()) + values = list(obj.values()) + world.random.shuffle(keys) + return dict(zip(keys,values)) + +def fix_reg(entrance_ids, entrance, destination, swapdict, world): + if entrance_ids[entrance] == destination: # Unlucky :C + rand = world.random.choice(swapdict.keys()) + entrance_ids[entrance], entrance_ids[swapdict[rand]] = rand, entrance_ids[entrance] + swapdict[rand] = entrance_ids[entrance] + swapdict.pop(entrance) + +def set_rules(world, player: int, area_connections: dict): + randomized_level_to_paintings = sm64_level_to_paintings.copy() + randomized_level_to_secrets = sm64_level_to_secrets.copy() + if world.AreaRandomizer[player].value == 1: # Some randomization is happening, randomize Courses + randomized_level_to_paintings = shuffle_dict_keys(world,sm64_level_to_paintings) if world.AreaRandomizer[player].value == 2: # Randomize Secrets as well - world.random.shuffle(secret_entrance_ids) - entrance_ids = course_entrance_ids + secret_entrance_ids + randomized_level_to_secrets = shuffle_dict_keys(world,sm64_level_to_secrets) + randomized_entrances = randomized_level_to_paintings | randomized_level_to_secrets if world.AreaRandomizer[player].value == 3: # Randomize Courses and Secrets in one pool - world.random.shuffle(entrance_ids) + randomized_entrances = shuffle_dict_keys(world,randomized_entrances) # Guarantee first entrance is a course - swaplist = list(range(len(entrance_ids))) - if entrance_ids.index(0) > 15: # Unlucky :C - rand = world.random.randint(0,15) - entrance_ids[entrance_ids.index(0)], entrance_ids[rand] = entrance_ids[rand], entrance_ids[entrance_ids.index(0)] - swaplist.remove(entrance_ids.index(0)) + swapdict = { entrance: level for (level,entrance) in randomized_entrances } + if randomized_entrances[91] not in sm64_paintings_to_level.keys(): # Unlucky :C (91 -> BoB Entrance) + rand = world.random.choice(sm64_paintings_to_level.values()) + randomized_entrances[91], randomized_entrances[swapdict[rand]] = rand, randomized_entrances[91] + swapdict[rand] = randomized_entrances[91] + swapdict.pop("Bob-omb Battlefield") # Guarantee COTMC is not mapped to HMC, cuz thats impossible - fix_reg(entrance_ids, 20, 5, swaplist, world) + fix_reg(randomized_entrances, "Cavern of the Metal Cap", "Hazy Maze Cave", swapdict, world) # Guarantee BITFS is not mapped to DDD - fix_reg(entrance_ids, 22, 8, swaplist, world) - if entrance_ids.index(22) == 5: # If BITFS is mapped to HMC... - fix_reg(entrance_ids, 20, 8, swaplist, world) # ... then dont allow COTMC to be mapped to DDD - temp_assign = dict(zip(entrance_ids,destination_regions)) # Used for Rules only + fix_reg(randomized_entrances, "Bowser in the Fire Sea", "Dire, Dire Docks", swapdict, world) + if randomized_entrances[191] == "Hazy Maze Cave": # If BITFS is mapped to HMC... + fix_reg(randomized_entrances, "Cavern of the Metal Cap", "Dire, Dire Docks", swapdict, world) # ... then dont allow COTMC to be mapped to DDD # Destination Format: LVL | AREA with LVL = LEVEL_x, AREA = Area as used in sm64 code - area_connections.update({sm64entrances[entrance]: destination for entrance, destination in zip(entrance_ids,sm64entrances)}) + area_connections.update({entrance_lvl: sm64_entrances_to_level[destination] for (entrance_lvl,destination) in randomized_entrances.items()}) + randomized_entrances_s = {sm64_level_to_entrances[entrance_lvl]: destination for (entrance_lvl,destination) in randomized_entrances.items()} - connect_regions(world, player, "Menu", sm64courses[temp_assign[0]]) # BOB - connect_regions(world, player, "Menu", sm64courses[temp_assign[1]], lambda state: state.has("Power Star", player, 1)) # WF - connect_regions(world, player, "Menu", sm64courses[temp_assign[2]], lambda state: state.has("Power Star", player, 3)) # JRB - connect_regions(world, player, "Menu", sm64courses[temp_assign[3]], lambda state: state.has("Power Star", player, 3)) # CCM - connect_regions(world, player, "Menu", sm64courses[temp_assign[4]], lambda state: state.has("Power Star", player, 12)) # BBH - connect_regions(world, player, "Menu", sm64courses[temp_assign[16]], lambda state: state.has("Power Star", player, 1)) # PSS - connect_regions(world, player, "Menu", sm64courses[temp_assign[17]], lambda state: state.has("Power Star", player, 3)) # SA - connect_regions(world, player, "Menu", sm64courses[temp_assign[19]], lambda state: state.has("Power Star", player, 10)) # TOTWC - connect_regions(world, player, "Menu", sm64courses[temp_assign[18]], lambda state: state.has("Power Star", player, world.FirstBowserStarDoorCost[player].value)) # BITDW + connect_regions(world, player, "Menu", randomized_entrances_s["Bob-omb Battlefield"]) + connect_regions(world, player, "Menu", randomized_entrances_s["Whomp's Fortress"], lambda state: state.has("Power Star", player, 1)) + connect_regions(world, player, "Menu", randomized_entrances_s["Jolly Roger Bay"], lambda state: state.has("Power Star", player, 3)) + connect_regions(world, player, "Menu", randomized_entrances_s["Cool, Cool Mountain"], lambda state: state.has("Power Star", player, 3)) + connect_regions(world, player, "Menu", randomized_entrances_s["Big Boo's Haunt"], lambda state: state.has("Power Star", player, 12)) + print(randomized_entrances_s["Big Boo's Haunt"]) + connect_regions(world, player, "Menu", randomized_entrances_s["The Princess's Secret Slide"], lambda state: state.has("Power Star", player, 1)) + connect_regions(world, player, "Menu", randomized_entrances_s["The Secret Aquarium"], lambda state: state.has("Power Star", player, 3)) + connect_regions(world, player, "Menu", randomized_entrances_s["Tower of the Wing Cap"], lambda state: state.has("Power Star", player, 10)) + connect_regions(world, player, "Menu", randomized_entrances_s["Bowser in the Dark World"], lambda state: state.has("Power Star", player, world.FirstBowserStarDoorCost[player].value)) connect_regions(world, player, "Menu", "Basement", lambda state: state.has("Basement Key", player) or state.has("Progressive Key", player, 1)) - connect_regions(world, player, "Basement", sm64courses[temp_assign[5]]) # HMC - connect_regions(world, player, "Basement", sm64courses[temp_assign[6]]) # LLL - connect_regions(world, player, "Basement", sm64courses[temp_assign[7]]) # SSL - connect_regions(world, player, "Basement", sm64courses[temp_assign[8]], lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value)) # DDD - connect_regions(world, player, "Hazy Maze Cave", sm64courses[temp_assign[20]]) # COTMC - connect_regions(world, player, "Basement", sm64courses[temp_assign[21]]) # VCUTM - connect_regions(world, player, "Basement", sm64courses[temp_assign[22]], lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value) and - state.can_reach("DDD: Board Bowser's Sub", 'Location', player)) # BITFS + connect_regions(world, player, "Basement", randomized_entrances_s["Hazy Maze Cave"]) + connect_regions(world, player, "Basement", randomized_entrances_s["Lethal Lava Land"]) + connect_regions(world, player, "Basement", randomized_entrances_s["Shifting Sand Land"]) + connect_regions(world, player, "Basement", randomized_entrances_s["Dire, Dire Docks"], lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value)) + connect_regions(world, player, "Hazy Maze Cave", randomized_entrances_s["Cavern of the Metal Cap"]) + connect_regions(world, player, "Basement", randomized_entrances_s["Vanish Cap under the Moat"]) + 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 + state.can_reach("DDD: Board Bowser's Sub", 'Location', player)) connect_regions(world, player, "Menu", "Second Floor", lambda state: state.has("Second Floor Key", player) or state.has("Progressive Key", player, 2)) - connect_regions(world, player, "Second Floor", sm64courses[temp_assign[9]]) # SL - connect_regions(world, player, "Second Floor", sm64courses[temp_assign[10]]) # WDW - connect_regions(world, player, "Second Floor", sm64courses[temp_assign[11]]) # TTM - connect_regions(world, player, "Second Floor", sm64courses[temp_assign[12]]) # THI Tiny - connect_regions(world, player, "Second Floor", sm64courses[temp_assign[13]]) # THI Huge + connect_regions(world, player, "Second Floor", randomized_entrances_s["Snowman's Land"]) + connect_regions(world, player, "Second Floor", randomized_entrances_s["Wet-Dry World"]) + connect_regions(world, player, "Second Floor", randomized_entrances_s["Tall, Tall Mountain"]) + connect_regions(world, player, "Second Floor", randomized_entrances_s["Tiny-Huge Island (Tiny)"]) + connect_regions(world, player, "Second Floor", randomized_entrances_s["Tiny-Huge Island (Huge)"]) + connect_regions(world, player, "Tiny-Huge Island (Tiny)", "Tiny-Huge Island (Huge)") + connect_regions(world, player, "Tiny-Huge Island (Huge)", "Tiny-Huge Island (Tiny)") connect_regions(world, player, "Second Floor", "Third Floor", lambda state: state.has("Power Star", player, world.SecondFloorStarDoorCost[player].value)) - connect_regions(world, player, "Third Floor", sm64courses[temp_assign[14]]) # TTC - connect_regions(world, player, "Third Floor", sm64courses[temp_assign[15]]) # RR - connect_regions(world, player, "Third Floor", sm64courses[temp_assign[23]]) # WMOTR - connect_regions(world, player, "Third Floor", "Bowser in the Sky", lambda state: state.has("Power Star", player, world.StarsToFinish[player].value)) # BITS + connect_regions(world, player, "Third Floor", randomized_entrances_s["Tick Tock Clock"]) + connect_regions(world, player, "Third Floor", randomized_entrances_s["Rainbow Ride"]) + connect_regions(world, player, "Third Floor", randomized_entrances_s["Wing Mario over the Rainbow"]) + connect_regions(world, player, "Third Floor", "Bowser in the Sky", lambda state: state.has("Power Star", player, world.StarsToFinish[player].value)) #Special Rules for some Locations add_rule(world.get_location("BoB: Mario Wings to the Sky", player), lambda state: state.has("Cannon Unlock BoB", player)) diff --git a/worlds/sm64ex/__init__.py b/worlds/sm64ex/__init__.py index 3cc87708e723..ab7409a324c3 100644 --- a/worlds/sm64ex/__init__.py +++ b/worlds/sm64ex/__init__.py @@ -5,7 +5,7 @@ from .Locations import location_table, SM64Location from .Options import sm64_options from .Rules import set_rules -from .Regions import create_regions, sm64courses, sm64entrances_s, sm64_internalloc_to_string, sm64_internalloc_to_regionid +from .Regions import create_regions, sm64_level_to_entrances from BaseClasses import Item, Tutorial, ItemClassification from ..AutoWorld import World, WebWorld @@ -55,8 +55,8 @@ def set_rules(self): # Write area_connections to spoiler log for entrance, destination in self.area_connections.items(): self.multiworld.spoiler.set_entrance( - sm64_internalloc_to_string[entrance] + " Entrance", - sm64_internalloc_to_string[destination], + sm64_level_to_entrances[entrance] + " Entrance", + sm64_level_to_entrances[destination], 'entrance', self.player) def create_item(self, name: str) -> Item: @@ -182,8 +182,7 @@ def modify_multidata(self, multidata): if self.topology_present: er_hint_data = {} for entrance, destination in self.area_connections.items(): - regionid = sm64_internalloc_to_regionid[destination] - region = self.multiworld.get_region(sm64courses[regionid], self.player) + region = self.multiworld.get_region(sm64_level_to_entrances[destination], self.player) for location in region.locations: - er_hint_data[location.address] = sm64_internalloc_to_string[entrance] + er_hint_data[location.address] = sm64_level_to_entrances[entrance] multidata['er_hint_data'][self.player] = er_hint_data