From c3ff201b9054bde4eb3c79199648413d27f81ba1 Mon Sep 17 00:00:00 2001 From: Yussur Mustafa Oraji Date: Mon, 25 Jul 2022 18:39:31 +0200 Subject: [PATCH] sm64ex: Various Features (#790) * sm64ex: Course and Secret Randomizer * sm64ex: Allow higher star door costs, raise minimum amount of stars, deprecate ExtraStars * sm64ex: Support setting MIPS costs * sm64ex: Safeguard MIPS Costs --- worlds/generic/docs/advanced_settings_en.md | 2 +- worlds/sm64ex/Locations.py | 53 ++++--- worlds/sm64ex/Options.py | 44 ++++-- worlds/sm64ex/Regions.py | 155 ++++++++++++-------- worlds/sm64ex/Rules.py | 106 +++++++------ worlds/sm64ex/__init__.py | 25 ++-- 6 files changed, 235 insertions(+), 150 deletions(-) diff --git a/worlds/generic/docs/advanced_settings_en.md b/worlds/generic/docs/advanced_settings_en.md index a0808fb4..d19c9d5e 100644 --- a/worlds/generic/docs/advanced_settings_en.md +++ b/worlds/generic/docs/advanced_settings_en.md @@ -272,7 +272,7 @@ Super Mario 64: StrictCapRequirements: true StrictCannonRequirements: true StarsToFinish: 70 - ExtraStars: 30 + AmountOfStars: 70 DeathLink: true BuddyChecks: true AreaRandomizer: true diff --git a/worlds/sm64ex/Locations.py b/worlds/sm64ex/Locations.py index 5aee0189..f33c4306 100644 --- a/worlds/sm64ex/Locations.py +++ b/worlds/sm64ex/Locations.py @@ -182,26 +182,50 @@ loc100Coin_table = { "RR: 100 Coins": 3626104 } +locPSS_table = { + "The Princess's Secret Slide Block": 3626126, + "The Princess's Secret Slide Fast": 3626127, +} + +locSA_table = { + "The Secret Aquarium": 3626161 +} + locBitDW_table = { "Bowser in the Dark World Red Coins": 3626105, "Bowser in the Dark World Key": 3626178 } +locTotWC_table = { + "Tower of the Wing Cap Switch": 3626181, + "Tower of the Wing Cap Red Coins": 3626140 +} + +locCotMC_table = { + "Cavern of the Metal Cap Switch": 3626182, + "Cavern of the Metal Cap Red Coins": 3626133 +} + +locVCutM_table = { + "Vanish Cap Under the Moat Switch": 3626183, + "Vanish Cap Under the Moat Red Coins": 3626147 +} + locBitFS_table = { "Bowser in the Fire Sea Red Coins": 3626112, "Bowser in the Fire Sea Key": 3626179 } -#Secret Stars and Stages +locWMotR_table = { + "Wing Mario Over the Rainbow": 3626154 +} + +locBitS_table = { + "Bowser in the Sky Red Coins": 3626119 +} + +#Secret Stars found inside the Castle locSS_table = { - "Bowser in the Sky Red Coins": 3626119, - "The Princess's Secret Slide Block": 3626126, - "The Princess's Secret Slide Fast": 3626127, - "Cavern of the Metal Cap Red Coins": 3626133, - "Tower of the Wing Cap Red Coins": 3626140, - "Vanish Cap Under the Moat Red Coins": 3626147, - "Wing Mario Over the Rainbow": 3626154, - "The Secret Aquarium": 3626161, "Toad (Basement)": 3626168, "Toad (Second Floor)": 3626169, "Toad (Third Floor)": 3626170, @@ -209,15 +233,10 @@ locSS_table = { "MIPS 2": 3626172 } -#Caps -locCap_table = { - "Tower of the Wing Cap Switch": 3626181, - "Cavern of the Metal Cap Switch": 3626182, - "Vanish Cap Under the Moat Switch": 3626183 -} - # Correspond to 3626000 + course index * 7 + star index, then secret stars, then keys, then 100 Coin Stars location_table = {**locBoB_table,**locWhomp_table,**locJRB_table,**locCCM_table,**locBBH_table, \ **locHMC_table,**locLLL_table,**locSSL_table,**locDDD_table,**locSL_table, \ **locWDW_table,**locTTM_table,**locTHI_table,**locTTC_table,**locRR_table, \ - **loc100Coin_table,**locBitDW_table,**locBitFS_table,**locSS_table,**locCap_table} \ No newline at end of file + **loc100Coin_table,**locPSS_table,**locSA_table,**locBitDW_table,**locTotWC_table, \ + **locCotMC_table, **locVCutM_table, **locBitFS_table, **locWMotR_table, **locBitS_table, \ + **locSS_table} \ No newline at end of file diff --git a/worlds/sm64ex/Options.py b/worlds/sm64ex/Options.py index bddfc3fb..594b0561 100644 --- a/worlds/sm64ex/Options.py +++ b/worlds/sm64ex/Options.py @@ -1,5 +1,5 @@ import typing -from Options import Option, DefaultOnToggle, Range, Toggle, DeathLink +from Options import Option, DefaultOnToggle, Range, Toggle, DeathLink, Choice class EnableCoinStars(DefaultOnToggle): """Disable to Ignore 100 Coin Stars. You can still collect them, but they don't do anything""" @@ -16,19 +16,31 @@ class StrictCannonRequirements(DefaultOnToggle): class FirstBowserStarDoorCost(Range): """How many stars are required at the Star Door to Bowser in the Dark World""" range_start = 0 - range_end = 20 + range_end = 50 default = 8 class BasementStarDoorCost(Range): """How many stars are required at the Star Door in the Basement""" range_start = 0 - range_end = 50 + range_end = 70 default = 30 class SecondFloorStarDoorCost(Range): """How many stars are required to access the third floor""" range_start = 0 - range_end = 50 + range_end = 90 + default = 50 + +class MIPS1Cost(Range): + """How many stars are required to spawn MIPS the first time""" + range_start = 0 + range_end = 40 + default = 15 + +class MIPS2Cost(Range): + """How many stars are required to spawn MIPS the secound time. Must be bigger or equal MIPS1Cost""" + range_start = 0 + range_end = 80 default = 50 class StarsToFinish(Range): @@ -38,15 +50,19 @@ class StarsToFinish(Range): range_end = 100 default = 70 -class ExtraStars(Range): - """How many stars exist beyond those set for StarsToFinish""" - range_start = 0 - range_end = 50 - default = 50 +class AmountOfStars(Range): + """How many stars exist. Disabling 100 Coin Stars removes 15 from the Pool. At least max of any Cost set""" + range_start = 35 + range_end = 120 + default = 120 -class AreaRandomizer(Toggle): - """Randomize Entrances to Courses""" - display_name = "Course Randomizer" +class AreaRandomizer(Choice): + """Randomize Entrances""" + display_name = "Entrance Randomizer" + alias_false = 0 + option_Off = 0 + option_Courses_Only = 1 + option_Courses_and_Secrets = 2 class BuddyChecks(Toggle): """Bob-omb Buddies are checks, Cannon Unlocks are items""" @@ -60,13 +76,15 @@ sm64_options: typing.Dict[str,type(Option)] = { "AreaRandomizer": AreaRandomizer, "ProgressiveKeys": ProgressiveKeys, "EnableCoinStars": EnableCoinStars, + "AmountOfStars": AmountOfStars, "StrictCapRequirements": StrictCapRequirements, "StrictCannonRequirements": StrictCannonRequirements, "FirstBowserStarDoorCost": FirstBowserStarDoorCost, "BasementStarDoorCost": BasementStarDoorCost, "SecondFloorStarDoorCost": SecondFloorStarDoorCost, + "MIPS1Cost": MIPS1Cost, + "MIPS2Cost": MIPS2Cost, "StarsToFinish": StarsToFinish, - "ExtraStars": ExtraStars, "death_link": DeathLink, "BuddyChecks": BuddyChecks, } \ No newline at end of file diff --git a/worlds/sm64ex/Regions.py b/worlds/sm64ex/Regions.py index d9a314df..f8e856a9 100644 --- a/worlds/sm64ex/Regions.py +++ b/worlds/sm64ex/Regions.py @@ -4,150 +4,168 @@ from .Locations import SM64Location, location_table, locBoB_table, locWhomp_tabl locBBH_table, \ locHMC_table, locLLL_table, locSSL_table, locDDD_table, locSL_table, \ locWDW_table, locTTM_table, locTHI_table, locTTC_table, locRR_table, \ - locBitDW_table, locBitFS_table, locSS_table, locCap_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"] + "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 strings for quick reference for Painting IDs (NOT warp node IDs!) -sm64paintings = ["BOB", "WF", "JRB", "CCM", "BBH", "HMC", "LLL", "SSL", "DDD", "SL", "WDW", "TTM", "THI Tiny", "THI Huge", "TTC", "RR"] +# 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))))) def create_regions(world: MultiWorld, player: int): regSS = Region("Menu", RegionType.Generic, "Castle Area", player, world) - locSS_names = [name for name, id in locSS_table.items()] - locSS_names += [name for name, id in locCap_table.items()] - regSS.locations += [SM64Location(player, loc_name, location_table[loc_name], regSS) for loc_name in locSS_names] + create_default_locs(regSS, locSS_table, player) world.regions.append(regSS) - regBoB = Region("Bob-omb Battlefield", RegionType.Generic, "Bob-omb Battlefield", player, world) - locBoB_names = [name for name, id in locBoB_table.items()] - regBoB.locations += [SM64Location(player, loc_name, location_table[loc_name], regBoB) for loc_name in locBoB_names] + regBoB = create_region("Bob-omb Battlefield", player, world) + create_default_locs(regBoB, locBoB_table, player) if (world.EnableCoinStars[player].value): regBoB.locations.append(SM64Location(player, "BoB: 100 Coins", location_table["BoB: 100 Coins"], regBoB)) world.regions.append(regBoB) - regWhomp = Region("Whomp's Fortress", RegionType.Generic, "Whomp's Fortress", player, world) - locWhomp_names = [name for name, id in locWhomp_table.items()] - regWhomp.locations += [SM64Location(player, loc_name, location_table[loc_name], regWhomp) for loc_name in - locWhomp_names] + regWhomp = create_region("Whomp's Fortress", player, world) + create_default_locs(regWhomp, locWhomp_table, player) if (world.EnableCoinStars[player].value): regWhomp.locations.append(SM64Location(player, "WF: 100 Coins", location_table["WF: 100 Coins"], regWhomp)) world.regions.append(regWhomp) - regJRB = Region("Jolly Roger Bay", RegionType.Generic, "Jolly Roger Bay", player, world) - locJRB_names = [name for name, id in locJRB_table.items()] - regJRB.locations += [SM64Location(player, loc_name, location_table[loc_name], regJRB) for loc_name in locJRB_names] + regJRB = create_region("Jolly Roger Bay", player, world) + create_default_locs(regJRB, locJRB_table, player) if (world.EnableCoinStars[player].value): regJRB.locations.append(SM64Location(player, "JRB: 100 Coins", location_table["JRB: 100 Coins"], regJRB)) world.regions.append(regJRB) - regCCM = Region("Cool, Cool Mountain", RegionType.Generic, "Cool, Cool Mountain", player, world) - locCCM_names = [name for name, id in locCCM_table.items()] - regCCM.locations += [SM64Location(player, loc_name, location_table[loc_name], regCCM) for loc_name in locCCM_names] + regCCM = create_region("Cool, Cool Mountain", player, world) + create_default_locs(regCCM, locCCM_table, player) if (world.EnableCoinStars[player].value): regCCM.locations.append(SM64Location(player, "CCM: 100 Coins", location_table["CCM: 100 Coins"], regCCM)) world.regions.append(regCCM) - regBBH = Region("Big Boo's Haunt", RegionType.Generic, "Big Boo's Haunt", player, world) - locBBH_names = [name for name, id in locBBH_table.items()] - regBBH.locations += [SM64Location(player, loc_name, location_table[loc_name], regBBH) for loc_name in locBBH_names] + regBBH = create_region("Big Boo's Haunt", player, world) + create_default_locs(regBBH, locBBH_table, player) if (world.EnableCoinStars[player].value): regBBH.locations.append(SM64Location(player, "BBH: 100 Coins", location_table["BBH: 100 Coins"], regBBH)) world.regions.append(regBBH) - regBitDW = Region("Bowser in the Dark World", RegionType.Generic, "Bowser in the Dark World", player, world) - locBitDW_names = [name for name, id in locBitDW_table.items()] - regBitDW.locations += [SM64Location(player, loc_name, location_table[loc_name], regBitDW) for loc_name in - locBitDW_names] + regPSS = create_region("The Princess's Secret Slide", player, world) + create_default_locs(regPSS, locPSS_table, player) + world.regions.append(regPSS) + + regSA = create_region("The Secret Aquarium", player, world) + create_default_locs(regSA, locSA_table, player) + world.regions.append(regSA) + + regTotWC = create_region("Tower of the Wing Cap", player, world) + create_default_locs(regTotWC, locTotWC_table, player) + world.regions.append(regTotWC) + + regBitDW = create_region("Bowser in the Dark World", player, world) + create_default_locs(regBitDW, locBitDW_table, player) world.regions.append(regBitDW) - regBasement = Region("Basement", RegionType.Generic, "Basement", player, world) + regBasement = create_region("Basement", player, world) world.regions.append(regBasement) - regHMC = Region("Hazy Maze Cave", RegionType.Generic, "Hazy Maze Cave", player, world) - locHMC_names = [name for name, id in locHMC_table.items()] - regHMC.locations += [SM64Location(player, loc_name, location_table[loc_name], regHMC) for loc_name in locHMC_names] + regHMC = create_region("Hazy Maze Cave", player, world) + create_default_locs(regHMC, locHMC_table, player) if (world.EnableCoinStars[player].value): regHMC.locations.append(SM64Location(player, "HMC: 100 Coins", location_table["HMC: 100 Coins"], regHMC)) world.regions.append(regHMC) - regLLL = Region("Lethal Lava Land", RegionType.Generic, "Lethal Lava Land", player, world) - locLLL_names = [name for name, id in locLLL_table.items()] - regLLL.locations += [SM64Location(player, loc_name, location_table[loc_name], regLLL) for loc_name in locLLL_names] + regLLL = create_region("Lethal Lava Land", player, world) + create_default_locs(regLLL, locLLL_table, player) if (world.EnableCoinStars[player].value): regLLL.locations.append(SM64Location(player, "LLL: 100 Coins", location_table["LLL: 100 Coins"], regLLL)) world.regions.append(regLLL) - regSSL = Region("Shifting Sand Land", RegionType.Generic, "Shifting Sand Land", player, world) - locSSL_names = [name for name, id in locSSL_table.items()] - regSSL.locations += [SM64Location(player, loc_name, location_table[loc_name], regSSL) for loc_name in locSSL_names] + regSSL = create_region("Shifting Sand Land", player, world) + create_default_locs(regSSL, locSSL_table, player) if (world.EnableCoinStars[player].value): regSSL.locations.append(SM64Location(player, "SSL: 100 Coins", location_table["SSL: 100 Coins"], regSSL)) world.regions.append(regSSL) - regDDD = Region("Dire, Dire Docks", RegionType.Generic, "Dire, Dire Docks", player, world) - locDDD_names = [name for name, id in locDDD_table.items()] - regDDD.locations += [SM64Location(player, loc_name, location_table[loc_name], regDDD) for loc_name in locDDD_names] + regDDD = create_region("Dire, Dire Docks", player, world) + create_default_locs(regDDD, locDDD_table, player) if (world.EnableCoinStars[player].value): regDDD.locations.append(SM64Location(player, "DDD: 100 Coins", location_table["DDD: 100 Coins"], regDDD)) world.regions.append(regDDD) - regBitFS = Region("Bowser in the Fire Sea", RegionType.Generic, "Bowser in the Fire Sea", player, world) - locBitFS_names = [name for name, id in locBitFS_table.items()] - regBitFS.locations += [SM64Location(player, loc_name, location_table[loc_name], regBitFS) for loc_name in - locBitFS_names] + regCotMC = create_region("Cavern of the Metal Cap", player, world) + create_default_locs(regCotMC, locCotMC_table, player) + world.regions.append(regCotMC) + + regVCutM = create_region("Vanish Cap under the Moat", player, world) + create_default_locs(regVCutM, locVCutM_table, player) + world.regions.append(regVCutM) + + regBitFS = create_region("Bowser in the Fire Sea", player, world) + create_default_locs(regBitFS, locBitFS_table, player) world.regions.append(regBitFS) - regFloor2 = Region("Second Floor", RegionType.Generic, "Second Floor", player, world) + regFloor2 = create_region("Second Floor", player, world) world.regions.append(regFloor2) - regSL = Region("Snowman's Land", RegionType.Generic, "Snowman's Land", player, world) - locSL_names = [name for name, id in locSL_table.items()] - regSL.locations += [SM64Location(player, loc_name, location_table[loc_name], regSL) for loc_name in locSL_names] + regSL = create_region("Snowman's Land", player, world) + create_default_locs(regSL, locSL_table, player) if (world.EnableCoinStars[player].value): regSL.locations.append(SM64Location(player, "SL: 100 Coins", location_table["SL: 100 Coins"], regSL)) world.regions.append(regSL) - regWDW = Region("Wet-Dry World", RegionType.Generic, "Wet-Dry World", player, world) - locWDW_names = [name for name, id in locWDW_table.items()] - regWDW.locations += [SM64Location(player, loc_name, location_table[loc_name], regWDW) for loc_name in locWDW_names] + regWDW = create_region("Wet-Dry World", player, world) + create_default_locs(regWDW, locWDW_table, player) if (world.EnableCoinStars[player].value): regWDW.locations.append(SM64Location(player, "WDW: 100 Coins", location_table["WDW: 100 Coins"], regWDW)) world.regions.append(regWDW) - regTTM = Region("Tall, Tall Mountain", RegionType.Generic, "Tall, Tall Mountain", player, world) - locTTM_names = [name for name, id in locTTM_table.items()] - regTTM.locations += [SM64Location(player, loc_name, location_table[loc_name], regTTM) for loc_name in locTTM_names] + regTTM = create_region("Tall, Tall Mountain", player, world) + create_default_locs(regTTM, locTTM_table, player) if (world.EnableCoinStars[player].value): regTTM.locations.append(SM64Location(player, "TTM: 100 Coins", location_table["TTM: 100 Coins"], regTTM)) world.regions.append(regTTM) - regTHI = Region("Tiny-Huge Island", RegionType.Generic, "Tiny-Huge Island", player, world) - locTHI_names = [name for name, id in locTHI_table.items()] - regTHI.locations += [SM64Location(player, loc_name, location_table[loc_name], regTHI) for loc_name in locTHI_names] + regTHI = create_region("Tiny-Huge Island", player, world) + create_default_locs(regTHI, 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) - regFloor3 = Region("Third Floor", RegionType.Generic, "Third Floor", player, world) + regFloor3 = create_region("Third Floor", player, world) world.regions.append(regFloor3) - regTTC = Region("Tick Tock Clock", RegionType.Generic, "Tick Tock Clock", player, world) - locTTC_names = [name for name, id in locTTC_table.items()] - regTTC.locations += [SM64Location(player, loc_name, location_table[loc_name], regTTC) for loc_name in locTTC_names] + regTTC = create_region("Tick Tock Clock", player, world) + create_default_locs(regTTC, locTTC_table, player) if (world.EnableCoinStars[player].value): regTTC.locations.append(SM64Location(player, "TTC: 100 Coins", location_table["TTC: 100 Coins"], regTTC)) world.regions.append(regTTC) - regRR = Region("Rainbow Ride", RegionType.Generic, "Rainbow Ride", player, world) - locRR_names = [name for name, id in locRR_table.items()] - regRR.locations += [SM64Location(player, loc_name, location_table[loc_name], regRR) for loc_name in locRR_names] + regRR = create_region("Rainbow Ride", player, world) + create_default_locs(regRR, locRR_table, player) if (world.EnableCoinStars[player].value): regRR.locations.append(SM64Location(player, "RR: 100 Coins", location_table["RR: 100 Coins"], regRR)) world.regions.append(regRR) + regWMotR = create_region("Wing Mario over the Rainbow", player, world) + create_default_locs(regWMotR, locWMotR_table, player) + world.regions.append(regWMotR) + + regBitS = create_region("Bowser in the Sky", player, world) + create_default_locs(regBitS, locBitS_table, player) + world.regions.append(regBitS) + def connect_regions(world: MultiWorld, player: int, source: str, target: str, rule=None): sourceRegion = world.get_region(source, player) @@ -159,3 +177,10 @@ def connect_regions(world: MultiWorld, player: int, source: str, target: str, ru sourceRegion.exits.append(connection) connection.connect(targetRegion) + +def create_region(name: str, player: int, world: MultiWorld) -> Region: + return Region(name, RegionType.Generic, name, player, world) + +def create_default_locs(reg: Region, locs, player): + reg_names = [name for name, id in locs.items()] + reg.locations += [SM64Location(player, loc_name, location_table[loc_name], reg) for loc_name in locs] diff --git a/worlds/sm64ex/Rules.py b/worlds/sm64ex/Rules.py index 6dc6e849..a4a82b27 100644 --- a/worlds/sm64ex/Rules.py +++ b/worlds/sm64ex/Rules.py @@ -1,56 +1,76 @@ from ..generic.Rules import add_rule -from .Regions import connect_regions, sm64courses, sm64paintings +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): - entrance_ids = list(range(len(sm64paintings))) - destination_courses = list(range(13)) + [12,13,14] # Two instances of Destination Course THI - if world.AreaRandomizer[player]: + 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 + if world.AreaRandomizer[player].value == 0: + entrance_ids = list(range(len(sm64paintings + sm64secrets))) + if world.AreaRandomizer[player].value >= 1: # Some randomization is happening, randomize Courses + entrance_ids = list(range(len(sm64paintings))) world.random.shuffle(entrance_ids) - temp_assign = dict(zip(entrance_ids,destination_courses)) # Used for Rules only + entrance_ids = entrance_ids + list(range(len(sm64paintings), len(sm64paintings) + len(sm64secrets))) + if world.AreaRandomizer[player].value == 2: # Secret Regions as well + world.random.shuffle(entrance_ids) + # 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)) + # Guarantee COTMC is not mapped to HMC, cuz thats impossible + fix_reg(entrance_ids, 20, 5, swaplist, world) + # Guarantee BITFS is not mapped to DDD + fix_reg(entrance_ids, 22, 8, swaplist, world) + temp_assign = dict(zip(entrance_ids,destination_regions)) # Used for Rules only - # Destination Format: LVL | AREA with LVL = Course ID, 0-indexed, AREA = Area as used in sm64 code - area_connections.update({entrance: (destination_course*10 + 1) for entrance, destination_course in temp_assign.items()}) - for i in range(len(area_connections)): - if (int(area_connections[i]/10) == 12): - # Change first occurence of course 12 (THI) to Area 2 (THI Tiny) - area_connections[i] = 12*10 + 2 - break - - connect_regions(world, player, "Menu", sm64courses[temp_assign[0]]) - connect_regions(world, player, "Menu", sm64courses[temp_assign[1]], lambda state: state.has("Power Star", player, 1)) - connect_regions(world, player, "Menu", sm64courses[temp_assign[2]], lambda state: state.has("Power Star", player, 3)) - connect_regions(world, player, "Menu", sm64courses[temp_assign[3]], lambda state: state.has("Power Star", player, 3)) - connect_regions(world, player, "Menu", "Bowser in the Dark World", lambda state: state.has("Power Star", player, world.FirstBowserStarDoorCost[player].value)) - connect_regions(world, player, "Menu", sm64courses[temp_assign[4]], lambda state: state.has("Power Star", player, 12)) + # 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)}) + + 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", "Basement", lambda state: state.has("Basement Key", player) or state.has("Progressive Key", player, 1)) - connect_regions(world, player, "Basement", sm64courses[temp_assign[5]]) - connect_regions(world, player, "Basement", sm64courses[temp_assign[6]]) - connect_regions(world, player, "Basement", sm64courses[temp_assign[7]]) - connect_regions(world, player, "Basement", sm64courses[temp_assign[8]], lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value)) - connect_regions(world, player, "Basement", "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, "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, "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]]) - connect_regions(world, player, "Second Floor", sm64courses[temp_assign[10]]) - connect_regions(world, player, "Second Floor", sm64courses[temp_assign[11]]) + 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", "Third Floor", lambda state: state.has("Power Star", player, world.SecondFloorStarDoorCost[player].value)) - connect_regions(world, player, "Third Floor", sm64courses[temp_assign[14]]) - connect_regions(world, player, "Third Floor", sm64courses[temp_assign[15]]) + 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 #Special Rules for some Locations - add_rule(world.get_location("Tower of the Wing Cap Switch", player), lambda state: state.has("Power Star", player, 10)) - add_rule(world.get_location("Cavern of the Metal Cap Switch", player), lambda state: state.can_reach("Hazy Maze Cave", 'Region', player)) - add_rule(world.get_location("Vanish Cap Under the Moat Switch", player), lambda state: state.can_reach("Basement", 'Region', player)) - add_rule(world.get_location("BoB: Mario Wings to the Sky", player), lambda state: state.has("Cannon Unlock BoB", player)) add_rule(world.get_location("BBH: Eye to Eye in the Secret Room", player), lambda state: state.has("Vanish Cap", player)) add_rule(world.get_location("DDD: Collect the Caps...", player), lambda state: state.has("Vanish Cap", player)) @@ -89,18 +109,14 @@ def set_rules(world, player: int, area_connections): add_rule(world.get_location("BoB: 100 Coins", player), lambda state: state.has("Cannon Unlock BoB", player) or state.has("Wing Cap", player)) #Rules for Secret Stars - add_rule(world.get_location("Bowser in the Sky Red Coins", player), lambda state: state.can_reach("Third Floor", 'Region',player) and state.has("Power Star", player, world.StarsToFinish[player].value)) - add_rule(world.get_location("The Princess's Secret Slide Block", player), lambda state: state.has("Power Star", player, 1)) - add_rule(world.get_location("The Princess's Secret Slide Fast", player), lambda state: state.has("Power Star", player, 1)) - add_rule(world.get_location("Cavern of the Metal Cap Red Coins", player), lambda state: state.can_reach("Cavern of the Metal Cap Switch", 'Location', player)) - add_rule(world.get_location("Tower of the Wing Cap Red Coins", player), lambda state: state.can_reach("Tower of the Wing Cap Switch", 'Location', player)) - add_rule(world.get_location("Vanish Cap Under the Moat Red Coins", player), lambda state: state.can_reach("Vanish Cap Under the Moat Switch", 'Location', player)) - add_rule(world.get_location("Wing Mario Over the Rainbow", player), lambda state: state.can_reach("Third Floor", 'Region', player) and state.has("Wing Cap", player)) - add_rule(world.get_location("The Secret Aquarium", player), lambda state: state.has("Power Star", player, 3)) + add_rule(world.get_location("Wing Mario Over the Rainbow", player), lambda state: state.has("Wing Cap", player)) add_rule(world.get_location("Toad (Basement)", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, 12)) add_rule(world.get_location("Toad (Second Floor)", player), lambda state: state.can_reach("Second Floor", 'Region', player) and state.has("Power Star", player, 25)) add_rule(world.get_location("Toad (Third Floor)", player), lambda state: state.can_reach("Third Floor", 'Region', player) and state.has("Power Star", player, 35)) - add_rule(world.get_location("MIPS 1", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, 15)) - add_rule(world.get_location("MIPS 2", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, 50)) - world.completion_condition[player] = lambda state: state.can_reach("Third Floor", 'Region', player) and state.has("Power Star", player, world.StarsToFinish[player].value) + if world.MIPS1Cost[player].value > world.MIPS2Cost[player].value: + world.MIPS2Cost[player].value = world.MIPS1Cost[player].value + add_rule(world.get_location("MIPS 1", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, world.MIPS1Cost[player].value)) + add_rule(world.get_location("MIPS 2", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, world.MIPS2Cost[player].value)) + + world.completion_condition[player] = lambda state: state.can_reach("Bowser in the Sky", 'Region', player) diff --git a/worlds/sm64ex/__init__.py b/worlds/sm64ex/__init__.py index bcf1bf2a..401a2d68 100644 --- a/worlds/sm64ex/__init__.py +++ b/worlds/sm64ex/__init__.py @@ -5,7 +5,7 @@ from .Items import item_table, cannon_item_table, SM64Item from .Locations import location_table, SM64Location from .Options import sm64_options from .Rules import set_rules -from .Regions import create_regions, sm64courses, sm64paintings +from .Regions import create_regions, sm64courses, sm64entrances_s, sm64_internalloc_to_string, sm64_internalloc_to_regionid from BaseClasses import Item, Tutorial, ItemClassification from ..AutoWorld import World, WebWorld @@ -54,10 +54,10 @@ class SM64World(World): set_rules(self.world, self.player, self.area_connections) if self.topology_present: # Write area_connections to spoiler log - for painting_id, destination in self.area_connections.items(): + for entrance, destination in self.area_connections.items(): self.world.spoiler.set_entrance( - sm64paintings[painting_id] + " Painting", - sm64courses[destination // 10], + sm64_internalloc_to_string[entrance] + " Entrance", + sm64_internalloc_to_string[destination], 'entrance', self.player) def create_item(self, name: str) -> Item: @@ -74,9 +74,13 @@ class SM64World(World): def generate_basic(self): staritem = self.create_item("Power Star") - starcount = min(self.world.StarsToFinish[self.player].value + self.world.ExtraStars[self.player].value,120) + starcount = self.world.AmountOfStars[self.player].value if (not self.world.EnableCoinStars[self.player].value): - starcount = max(starcount - 15,self.world.StarsToFinish[self.player].value) + starcount = max(35,self.world.AmountOfStars[self.player].value-15) + starcount = max(starcount, self.world.FirstBowserStarDoorCost[self.player].value, + self.world.BasementStarDoorCost[self.player].value, self.world.SecondFloorStarDoorCost[self.player].value, + self.world.MIPS1Cost[self.player].value, self.world.MIPS2Cost[self.player].value, + self.world.StarsToFinish[self.player].value) self.world.itempool += [staritem for i in range(0,starcount)] mushroomitem = self.create_item("1Up Mushroom") self.world.itempool += [mushroomitem for i in range(starcount,120 - (15 if not self.world.EnableCoinStars[self.player].value else 0))] @@ -117,6 +121,8 @@ class SM64World(World): "FirstBowserDoorCost": self.world.FirstBowserStarDoorCost[self.player].value, "BasementDoorCost": self.world.BasementStarDoorCost[self.player].value, "SecondFloorCost": self.world.SecondFloorStarDoorCost[self.player].value, + "MIPS1Cost": self.world.MIPS1Cost[self.player].value, + "MIPS2Cost": self.world.MIPS2Cost[self.player].value, "StarsToFinish": self.world.StarsToFinish[self.player].value, "DeathLink": self.world.death_link[self.player].value, } @@ -145,8 +151,9 @@ class SM64World(World): def modify_multidata(self, multidata): if self.topology_present: er_hint_data = {} - for painting_id, destination in self.area_connections.items(): - region = self.world.get_region(sm64courses[destination // 10], self.player) + for entrance, destination in self.area_connections.items(): + regionid = sm64_internalloc_to_regionid[destination] + region = self.world.get_region(sm64courses[regionid], self.player) for location in region.locations: - er_hint_data[location.address] = sm64paintings[painting_id] + er_hint_data[location.address] = sm64_internalloc_to_string[entrance] multidata['er_hint_data'][self.player] = er_hint_data