SM64: Move Randomizer Content Update (#2569)
* Super Mario 64: Move Randomizer Update Co-authored-by: RBman <139954693+RBmans@users.noreply.github.com> Signed-off-by: Magnemania <magnemight@gmail.com> * Fixed logic for Vanish Cap Under the Moat Signed-off-by: Magnemania <magnemight@gmail.com>
This commit is contained in:
		| @@ -16,6 +16,21 @@ generic_item_table = { | ||||
|     "1Up Mushroom": 3626184 | ||||
| } | ||||
|  | ||||
| action_item_table = { | ||||
|     "Double Jump": 3626185, | ||||
|     "Triple Jump": 3626186, | ||||
|     "Long Jump": 3626187, | ||||
|     "Backflip": 3626188, | ||||
|     "Side Flip": 3626189, | ||||
|     "Wall Kick": 3626190, | ||||
|     "Dive": 3626191, | ||||
|     "Ground Pound": 3626192, | ||||
|     "Kick": 3626193, | ||||
|     "Climb": 3626194, | ||||
|     "Ledge Grab": 3626195 | ||||
| } | ||||
|  | ||||
|  | ||||
| cannon_item_table = { | ||||
|     "Cannon Unlock BoB": 3626200, | ||||
|     "Cannon Unlock WF": 3626201, | ||||
| @@ -29,4 +44,4 @@ cannon_item_table = { | ||||
|     "Cannon Unlock RR": 3626214 | ||||
| } | ||||
|  | ||||
| item_table = {**generic_item_table, **cannon_item_table} | ||||
| item_table = {**generic_item_table, **action_item_table, **cannon_item_table} | ||||
| @@ -1,9 +1,10 @@ | ||||
| import typing | ||||
| from Options import Option, DefaultOnToggle, Range, Toggle, DeathLink, Choice | ||||
|  | ||||
| from .Items import action_item_table | ||||
|  | ||||
| class EnableCoinStars(DefaultOnToggle): | ||||
|     """Disable to Ignore 100 Coin Stars. You can still collect them, but they don't do anything""" | ||||
|     """Disable to Ignore 100 Coin Stars. You can still collect them, but they don't do anything. | ||||
|     Removes 15 locations from the pool.""" | ||||
|     display_name = "Enable 100 Coin Stars" | ||||
|  | ||||
|  | ||||
| @@ -13,56 +14,63 @@ class StrictCapRequirements(DefaultOnToggle): | ||||
|  | ||||
|  | ||||
| class StrictCannonRequirements(DefaultOnToggle): | ||||
|     """If disabled, Stars that expect cannons may have to be acquired without them. Only makes a difference if Buddy | ||||
|     Checks are enabled""" | ||||
|     """If disabled, Stars that expect cannons may have to be acquired without them. | ||||
|     Has no effect if Buddy Checks and Move Randomizer are disabled""" | ||||
|     display_name = "Strict Cannon Requirements" | ||||
|  | ||||
|  | ||||
| class FirstBowserStarDoorCost(Range): | ||||
|     """How many stars are required at the Star Door to Bowser in the Dark World""" | ||||
|     """What percent of the total stars are required at the Star Door to Bowser in the Dark World""" | ||||
|     display_name = "First Star Door Cost %" | ||||
|     range_start = 0 | ||||
|     range_end = 50 | ||||
|     default = 8 | ||||
|     range_end = 40 | ||||
|     default = 7 | ||||
|  | ||||
|  | ||||
| class BasementStarDoorCost(Range): | ||||
|     """How many stars are required at the Star Door in the Basement""" | ||||
|     """What percent of the total stars are required at the Star Door in the Basement""" | ||||
|     display_name = "Basement Star Door %" | ||||
|     range_start = 0 | ||||
|     range_end = 70 | ||||
|     default = 30 | ||||
|     range_end = 50 | ||||
|     default = 25 | ||||
|  | ||||
|  | ||||
| class SecondFloorStarDoorCost(Range): | ||||
|     """How many stars are required to access the third floor""" | ||||
|     """What percent of the total stars are required to access the third floor""" | ||||
|     display_name = 'Second Floor Star Door %' | ||||
|     range_start = 0 | ||||
|     range_end = 90 | ||||
|     default = 50 | ||||
|     range_end = 70 | ||||
|     default = 42 | ||||
|  | ||||
|  | ||||
| class MIPS1Cost(Range): | ||||
|     """How many stars are required to spawn MIPS the first time""" | ||||
|     """What percent of the total stars are required to spawn MIPS the first time""" | ||||
|     display_name = "MIPS 1 Star %" | ||||
|     range_start = 0 | ||||
|     range_end = 40 | ||||
|     default = 15 | ||||
|     range_end = 35 | ||||
|     default = 12 | ||||
|  | ||||
|  | ||||
| class MIPS2Cost(Range): | ||||
|     """How many stars are required to spawn MIPS the second time.""" | ||||
|     """What percent of the total stars are required to spawn MIPS the second time.""" | ||||
|     display_name = "MIPS 2 Star %" | ||||
|     range_start = 0 | ||||
|     range_end = 80 | ||||
|     default = 50 | ||||
|     range_end = 70 | ||||
|     default = 42 | ||||
|  | ||||
|  | ||||
| class StarsToFinish(Range): | ||||
|     """How many stars are required at the infinite stairs""" | ||||
|     display_name = "Endless Stairs Stars" | ||||
|     """What percent of the total stars are required at the infinite stairs""" | ||||
|     display_name = "Endless Stairs Star %" | ||||
|     range_start = 0 | ||||
|     range_end = 100 | ||||
|     default = 70 | ||||
|     range_end = 90 | ||||
|     default = 58 | ||||
|  | ||||
|  | ||||
| class AmountOfStars(Range): | ||||
|     """How many stars exist. Disabling 100 Coin Stars removes 15 from the Pool. At least max of any Cost set""" | ||||
|     """How many stars exist. | ||||
|     If there aren't enough locations to hold the given total, the total will be reduced.""" | ||||
|     display_name = "Total Power Stars" | ||||
|     range_start = 35 | ||||
|     range_end = 120 | ||||
|     default = 120 | ||||
| @@ -83,11 +91,13 @@ class BuddyChecks(Toggle): | ||||
|  | ||||
|  | ||||
| class ExclamationBoxes(Choice): | ||||
|     """Include 1Up Exclamation Boxes during randomization""" | ||||
|     """Include 1Up Exclamation Boxes during randomization. | ||||
|     Adds 29 locations to the pool.""" | ||||
|     display_name = "Randomize 1Up !-Blocks" | ||||
|     option_Off = 0 | ||||
|     option_1Ups_Only = 1 | ||||
|  | ||||
|  | ||||
| class CompletionType(Choice): | ||||
|     """Set goal for game completion""" | ||||
|     display_name = "Completion Goal" | ||||
| @@ -96,17 +106,32 @@ class CompletionType(Choice): | ||||
|  | ||||
|  | ||||
| class ProgressiveKeys(DefaultOnToggle): | ||||
|     """Keys will first grant you access to the Basement, then to the Secound Floor""" | ||||
|     """Keys will first grant you access to the Basement, then to the Second Floor""" | ||||
|     display_name = "Progressive Keys" | ||||
|  | ||||
| class StrictMoveRequirements(DefaultOnToggle): | ||||
|     """If disabled, Stars that expect certain moves may have to be acquired without them. Only makes a difference | ||||
|     if Move Randomization is enabled""" | ||||
|     display_name = "Strict Move Requirements" | ||||
|  | ||||
| def getMoveRandomizerOption(action: str): | ||||
|     class MoveRandomizerOption(Toggle): | ||||
|         """Mario is unable to perform this action until a corresponding item is picked up. | ||||
|         This option is incompatible with builds using a 'nomoverando' branch.""" | ||||
|         display_name = f"Randomize {action}" | ||||
|     return MoveRandomizerOption | ||||
|  | ||||
|  | ||||
| sm64_options: typing.Dict[str, type(Option)] = { | ||||
|     "AreaRandomizer": AreaRandomizer, | ||||
|     "BuddyChecks": BuddyChecks, | ||||
|     "ExclamationBoxes": ExclamationBoxes, | ||||
|     "ProgressiveKeys": ProgressiveKeys, | ||||
|     "EnableCoinStars": EnableCoinStars, | ||||
|     "AmountOfStars": AmountOfStars, | ||||
|     "StrictCapRequirements": StrictCapRequirements, | ||||
|     "StrictCannonRequirements": StrictCannonRequirements, | ||||
|     "StrictMoveRequirements": StrictMoveRequirements, | ||||
|     "AmountOfStars": AmountOfStars, | ||||
|     "FirstBowserStarDoorCost": FirstBowserStarDoorCost, | ||||
|     "BasementStarDoorCost": BasementStarDoorCost, | ||||
|     "SecondFloorStarDoorCost": SecondFloorStarDoorCost, | ||||
| @@ -114,7 +139,10 @@ sm64_options: typing.Dict[str, type(Option)] = { | ||||
|     "MIPS2Cost": MIPS2Cost, | ||||
|     "StarsToFinish": StarsToFinish, | ||||
|     "death_link": DeathLink, | ||||
|     "BuddyChecks": BuddyChecks, | ||||
|     "ExclamationBoxes": ExclamationBoxes, | ||||
|     "CompletionType" : CompletionType, | ||||
|     "CompletionType": CompletionType, | ||||
| } | ||||
|  | ||||
| for action in action_item_table: | ||||
|     # HACK: Disable randomization of double jump | ||||
|     if action == 'Double Jump': continue | ||||
|     sm64_options[f"MoveRandomizer{action.replace(' ','')}"] = getMoveRandomizerOption(action) | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| import typing | ||||
|  | ||||
| from enum import Enum | ||||
|  | ||||
| from BaseClasses import MultiWorld, Region, Entrance, Location | ||||
| @@ -8,7 +7,8 @@ from .Locations import SM64Location, location_table, locBoB_table, locWhomp_tabl | ||||
|     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     | ||||
|     locVCutM_table, locBitFS_table, locWMotR_table, locBitS_table, locSS_table | ||||
|  | ||||
|  | ||||
| class SM64Levels(int, Enum): | ||||
|     BOB_OMB_BATTLEFIELD = 91 | ||||
| @@ -55,7 +55,7 @@ sm64_level_to_paintings: typing.Dict[SM64Levels, str] = { | ||||
|     SM64Levels.TICK_TOCK_CLOCK: "Tick Tock Clock", | ||||
|     SM64Levels.RAINBOW_RIDE: "Rainbow Ride" | ||||
| } | ||||
| sm64_paintings_to_level = { painting: level for (level,painting) in sm64_level_to_paintings.items() } | ||||
| sm64_paintings_to_level = {painting: level for (level, painting) in sm64_level_to_paintings.items() } | ||||
|  | ||||
| # sm64secrets is a dict of secret areas, same format as sm64paintings | ||||
| sm64_level_to_secrets: typing.Dict[SM64Levels, str] = { | ||||
| @@ -68,152 +68,163 @@ sm64_level_to_secrets: typing.Dict[SM64Levels, str] = { | ||||
|     SM64Levels.BOWSER_IN_THE_FIRE_SEA: "Bowser in the Fire Sea", | ||||
|     SM64Levels.WING_MARIO_OVER_THE_RAINBOW: "Wing Mario over the Rainbow" | ||||
| } | ||||
| sm64_secrets_to_level = { secret: level for (level,secret) in sm64_level_to_secrets.items() } | ||||
| 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 } | ||||
| 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") | ||||
|     create_default_locs(regSS, locSS_table, player) | ||||
|     create_default_locs(regSS, locSS_table) | ||||
|     world.regions.append(regSS) | ||||
|  | ||||
|     regBoB = create_region("Bob-omb Battlefield", player, world) | ||||
|     create_default_locs(regBoB, locBoB_table, player) | ||||
|     create_locs(regBoB, "BoB: Big Bob-Omb on the Summit", "BoB: Footrace with Koopa The Quick", | ||||
|                         "BoB: Mario Wings to the Sky", "BoB: Behind Chain Chomp's Gate", "BoB: Bob-omb Buddy") | ||||
|     create_subregion(regBoB, "BoB: Island", "BoB: Shoot to the Island in the Sky", "BoB: Find the 8 Red Coins") | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         regBoB.locations.append(SM64Location(player, "BoB: 100 Coins", location_table["BoB: 100 Coins"], regBoB)) | ||||
|     world.regions.append(regBoB) | ||||
|         create_locs(regBoB, "BoB: 100 Coins") | ||||
|  | ||||
|     regWhomp = create_region("Whomp's Fortress", player, world) | ||||
|     create_default_locs(regWhomp, locWhomp_table, player) | ||||
|     create_locs(regWhomp, "WF: Chip Off Whomp's Block", "WF: Shoot into the Wild Blue", "WF: Red Coins on the Floating Isle", | ||||
|                           "WF: Fall onto the Caged Island", "WF: Blast Away the Wall") | ||||
|     create_subregion(regWhomp, "WF: Tower", "WF: To the Top of the Fortress", "WF: Bob-omb Buddy") | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         regWhomp.locations.append(SM64Location(player, "WF: 100 Coins", location_table["WF: 100 Coins"], regWhomp)) | ||||
|     world.regions.append(regWhomp) | ||||
|         create_locs(regWhomp, "WF: 100 Coins") | ||||
|  | ||||
|     regJRB = create_region("Jolly Roger Bay", player, world) | ||||
|     create_default_locs(regJRB, locJRB_table, player) | ||||
|     create_locs(regJRB, "JRB: Plunder in the Sunken Ship", "JRB: Can the Eel Come Out to Play?", "JRB: Treasure of the Ocean Cave", | ||||
|                         "JRB: Blast to the Stone Pillar", "JRB: Through the Jet Stream", "JRB: Bob-omb Buddy") | ||||
|     jrb_upper = create_subregion(regJRB, 'JRB: Upper', "JRB: Red Coins on the Ship Afloat") | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         regJRB.locations.append(SM64Location(player, "JRB: 100 Coins", location_table["JRB: 100 Coins"], regJRB)) | ||||
|     world.regions.append(regJRB) | ||||
|         create_locs(jrb_upper, "JRB: 100 Coins") | ||||
|  | ||||
|     regCCM = create_region("Cool, Cool Mountain", player, world) | ||||
|     create_default_locs(regCCM, locCCM_table, player) | ||||
|     create_default_locs(regCCM, locCCM_table) | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         regCCM.locations.append(SM64Location(player, "CCM: 100 Coins", location_table["CCM: 100 Coins"], regCCM)) | ||||
|     world.regions.append(regCCM) | ||||
|         create_locs(regCCM, "CCM: 100 Coins") | ||||
|  | ||||
|     regBBH = create_region("Big Boo's Haunt", player, world) | ||||
|     create_default_locs(regBBH, locBBH_table, player) | ||||
|     create_locs(regBBH, "BBH: Go on a Ghost Hunt", "BBH: Ride Big Boo's Merry-Go-Round", | ||||
|                         "BBH: Secret of the Haunted Books", "BBH: Seek the 8 Red Coins") | ||||
|     bbh_third_floor = create_subregion(regBBH, "BBH: Third Floor", "BBH: Eye to Eye in the Secret Room") | ||||
|     create_subregion(bbh_third_floor, "BBH: Roof", "BBH: Big Boo's Balcony", "BBH: 1Up Block Top of Mansion") | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         regBBH.locations.append(SM64Location(player, "BBH: 100 Coins", location_table["BBH: 100 Coins"], regBBH)) | ||||
|     world.regions.append(regBBH) | ||||
|         create_locs(regBBH, "BBH: 100 Coins") | ||||
|  | ||||
|     regPSS = create_region("The Princess's Secret Slide", player, world) | ||||
|     create_default_locs(regPSS, locPSS_table, player) | ||||
|     world.regions.append(regPSS) | ||||
|     create_default_locs(regPSS, locPSS_table) | ||||
|  | ||||
|     regSA = create_region("The Secret Aquarium", player, world) | ||||
|     create_default_locs(regSA, locSA_table, player) | ||||
|     world.regions.append(regSA) | ||||
|     create_default_locs(regSA, locSA_table) | ||||
|  | ||||
|     regTotWC = create_region("Tower of the Wing Cap", player, world) | ||||
|     create_default_locs(regTotWC, locTotWC_table, player) | ||||
|     world.regions.append(regTotWC) | ||||
|     create_default_locs(regTotWC, locTotWC_table) | ||||
|  | ||||
|     regBitDW = create_region("Bowser in the Dark World", player, world) | ||||
|     create_default_locs(regBitDW, locBitDW_table, player) | ||||
|     world.regions.append(regBitDW) | ||||
|     create_default_locs(regBitDW, locBitDW_table) | ||||
|  | ||||
|     regBasement = create_region("Basement", player, world) | ||||
|     world.regions.append(regBasement) | ||||
|     create_region("Basement", player, world) | ||||
|  | ||||
|     regHMC = create_region("Hazy Maze Cave", player, world) | ||||
|     create_default_locs(regHMC, locHMC_table, player) | ||||
|     create_locs(regHMC, "HMC: Swimming Beast in the Cavern", "HMC: Metal-Head Mario Can Move!", | ||||
|                         "HMC: Watch for Rolling Rocks", "HMC: Navigating the Toxic Maze","HMC: 1Up Block Past Rolling Rocks") | ||||
|     hmc_red_coin_area = create_subregion(regHMC, "HMC: Red Coin Area", "HMC: Elevate for 8 Red Coins") | ||||
|     create_subregion(regHMC, "HMC: Pit Islands", "HMC: A-Maze-Ing Emergency Exit", "HMC: 1Up Block above Pit") | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         regHMC.locations.append(SM64Location(player, "HMC: 100 Coins", location_table["HMC: 100 Coins"], regHMC)) | ||||
|     world.regions.append(regHMC) | ||||
|         create_locs(hmc_red_coin_area, "HMC: 100 Coins") | ||||
|  | ||||
|     regLLL = create_region("Lethal Lava Land", player, world) | ||||
|     create_default_locs(regLLL, locLLL_table, player) | ||||
|     create_locs(regLLL, "LLL: Boil the Big Bully", "LLL: Bully the Bullies", | ||||
|                         "LLL: 8-Coin Puzzle with 15 Pieces", "LLL: Red-Hot Log Rolling") | ||||
|     create_subregion(regLLL, "LLL: Upper Volcano", "LLL: Hot-Foot-It into the Volcano", "LLL: Elevator Tour in the Volcano") | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         regLLL.locations.append(SM64Location(player, "LLL: 100 Coins", location_table["LLL: 100 Coins"], regLLL)) | ||||
|     world.regions.append(regLLL) | ||||
|         create_locs(regLLL, "LLL: 100 Coins") | ||||
|  | ||||
|     regSSL = create_region("Shifting Sand Land", player, world) | ||||
|     create_default_locs(regSSL, locSSL_table, player) | ||||
|     create_locs(regSSL, "SSL: In the Talons of the Big Bird", "SSL: Shining Atop the Pyramid", "SSL: Inside the Ancient Pyramid", | ||||
|                         "SSL: Free Flying for 8 Red Coins", "SSL: Bob-omb Buddy", | ||||
|                         "SSL: 1Up Block Outside Pyramid", "SSL: 1Up Block Pyramid Left Path", "SSL: 1Up Block Pyramid Back") | ||||
|     create_subregion(regSSL, "SSL: Upper Pyramid", "SSL: Stand Tall on the Four Pillars", "SSL: Pyramid Puzzle") | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         regSSL.locations.append(SM64Location(player, "SSL: 100 Coins", location_table["SSL: 100 Coins"], regSSL)) | ||||
|     world.regions.append(regSSL) | ||||
|         create_locs(regSSL, "SSL: 100 Coins") | ||||
|  | ||||
|     regDDD = create_region("Dire, Dire Docks", player, world) | ||||
|     create_default_locs(regDDD, locDDD_table, player) | ||||
|     create_locs(regDDD, "DDD: Board Bowser's Sub", "DDD: Chests in the Current", "DDD: Through the Jet Stream", | ||||
|                         "DDD: The Manta Ray's Reward", "DDD: Collect the Caps...") | ||||
|     ddd_moving_poles = create_subregion(regDDD, "DDD: Moving Poles", "DDD: Pole-Jumping for Red Coins") | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         regDDD.locations.append(SM64Location(player, "DDD: 100 Coins", location_table["DDD: 100 Coins"], regDDD)) | ||||
|     world.regions.append(regDDD) | ||||
|         create_locs(ddd_moving_poles, "DDD: 100 Coins") | ||||
|  | ||||
|     regCotMC = create_region("Cavern of the Metal Cap", player, world) | ||||
|     create_default_locs(regCotMC, locCotMC_table, player) | ||||
|     world.regions.append(regCotMC) | ||||
|     create_default_locs(regCotMC, locCotMC_table) | ||||
|  | ||||
|     regVCutM = create_region("Vanish Cap under the Moat", player, world) | ||||
|     create_default_locs(regVCutM, locVCutM_table, player) | ||||
|     world.regions.append(regVCutM) | ||||
|     create_default_locs(regVCutM, locVCutM_table) | ||||
|  | ||||
|     regBitFS = create_region("Bowser in the Fire Sea", player, world) | ||||
|     create_default_locs(regBitFS, locBitFS_table, player) | ||||
|     world.regions.append(regBitFS) | ||||
|     create_subregion(regBitFS, "BitFS: Upper", *locBitFS_table.keys()) | ||||
|  | ||||
|     regFloor2 = create_region("Second Floor", player, world) | ||||
|     world.regions.append(regFloor2) | ||||
|     create_region("Second Floor", player, world) | ||||
|  | ||||
|     regSL = create_region("Snowman's Land", player, world) | ||||
|     create_default_locs(regSL, locSL_table, player) | ||||
|     create_default_locs(regSL, locSL_table) | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         regSL.locations.append(SM64Location(player, "SL: 100 Coins", location_table["SL: 100 Coins"], regSL)) | ||||
|     world.regions.append(regSL) | ||||
|         create_locs(regSL, "SL: 100 Coins") | ||||
|  | ||||
|     regWDW = create_region("Wet-Dry World", player, world) | ||||
|     create_default_locs(regWDW, locWDW_table, player) | ||||
|     create_locs(regWDW, "WDW: Express Elevator--Hurry Up!") | ||||
|     wdw_top = create_subregion(regWDW, "WDW: Top", "WDW: Shocking Arrow Lifts!", "WDW: Top o' the Town", | ||||
|                                                    "WDW: Secrets in the Shallows & Sky", "WDW: Bob-omb Buddy") | ||||
|     create_subregion(regWDW, "WDW: Downtown", "WDW: Go to Town for Red Coins", "WDW: Quick Race Through Downtown!", "WDW: 1Up Block in Downtown") | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         regWDW.locations.append(SM64Location(player, "WDW: 100 Coins", location_table["WDW: 100 Coins"], regWDW)) | ||||
|     world.regions.append(regWDW) | ||||
|         create_locs(wdw_top, "WDW: 100 Coins") | ||||
|  | ||||
|     regTTM = create_region("Tall, Tall Mountain", player, world) | ||||
|     create_default_locs(regTTM, locTTM_table, player) | ||||
|     ttm_middle = create_subregion(regTTM, "TTM: Middle", "TTM: Scary 'Shrooms, Red Coins", "TTM: Blast to the Lonely Mushroom", | ||||
|                                                          "TTM: Bob-omb Buddy", "TTM: 1Up Block on Red Mushroom") | ||||
|     ttm_top = create_subregion(ttm_middle, "TTM: Top", "TTM: Scale the Mountain", "TTM: Mystery of the Monkey Cage", | ||||
|                                                        "TTM: Mysterious Mountainside", "TTM: Breathtaking View from Bridge") | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         regTTM.locations.append(SM64Location(player, "TTM: 100 Coins", location_table["TTM: 100 Coins"], regTTM)) | ||||
|     world.regions.append(regTTM) | ||||
|         create_locs(ttm_top, "TTM: 100 Coins") | ||||
|  | ||||
|     regTHIT = create_region("Tiny-Huge Island (Tiny)", player, world) | ||||
|     create_default_locs(regTHIT, locTHI_table, player) | ||||
|     create_region("Tiny-Huge Island (Huge)", player, world) | ||||
|     create_region("Tiny-Huge Island (Tiny)", player, world) | ||||
|     regTHI = create_region("Tiny-Huge Island", player, world) | ||||
|     create_locs(regTHI, "THI: The Tip Top of the Huge Island", "THI: 1Up Block THI Small near Start") | ||||
|     thi_pipes = create_subregion(regTHI, "THI: Pipes", "THI: Pluck the Piranha Flower", "THI: Rematch with Koopa the Quick", | ||||
|                                                        "THI: Five Itty Bitty Secrets", "THI: Wiggler's Red Coins", "THI: Bob-omb Buddy", | ||||
|                                                        "THI: 1Up Block THI Large near Start", "THI: 1Up Block Windy Area") | ||||
|     thi_large_top = create_subregion(thi_pipes, "THI: Large Top", "THI: Make Wiggler Squirm") | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         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) | ||||
|         create_locs(thi_large_top, "THI: 100 Coins") | ||||
|  | ||||
|     regFloor3 = create_region("Third Floor", player, world) | ||||
|     world.regions.append(regFloor3) | ||||
|  | ||||
|     regTTC = create_region("Tick Tock Clock", player, world) | ||||
|     create_default_locs(regTTC, locTTC_table, player) | ||||
|     create_locs(regTTC, "TTC: Stop Time for Red Coins") | ||||
|     ttc_lower = create_subregion(regTTC, "TTC: Lower", "TTC: Roll into the Cage", "TTC: Get a Hand", "TTC: 1Up Block Midway Up") | ||||
|     ttc_upper = create_subregion(ttc_lower, "TTC: Upper", "TTC: Timed Jumps on Moving Bars", "TTC: The Pit and the Pendulums") | ||||
|     ttc_top = create_subregion(ttc_upper, "TTC: Top", "TTC: Stomp on the Thwomp", "TTC: 1Up Block at the Top") | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         regTTC.locations.append(SM64Location(player, "TTC: 100 Coins", location_table["TTC: 100 Coins"], regTTC)) | ||||
|     world.regions.append(regTTC) | ||||
|         create_locs(ttc_top, "TTC: 100 Coins") | ||||
|  | ||||
|     regRR = create_region("Rainbow Ride", player, world) | ||||
|     create_default_locs(regRR, locRR_table, player) | ||||
|     create_locs(regRR, "RR: Swingin' in the Breeze", "RR: Tricky Triangles!", | ||||
|                        "RR: 1Up Block Top of Red Coin Maze", "RR: 1Up Block Under Fly Guy", "RR: Bob-omb Buddy") | ||||
|     rr_maze = create_subregion(regRR, "RR: Maze", "RR: Coins Amassed in a Maze") | ||||
|     create_subregion(regRR, "RR: Cruiser", "RR: Cruiser Crossing the Rainbow", "RR: Somewhere Over the Rainbow") | ||||
|     create_subregion(regRR, "RR: House", "RR: The Big House in the Sky", "RR: 1Up Block On House in the Sky") | ||||
|     if (world.EnableCoinStars[player].value): | ||||
|         regRR.locations.append(SM64Location(player, "RR: 100 Coins", location_table["RR: 100 Coins"], regRR)) | ||||
|     world.regions.append(regRR) | ||||
|         create_locs(rr_maze, "RR: 100 Coins") | ||||
|  | ||||
|     regWMotR = create_region("Wing Mario over the Rainbow", player, world) | ||||
|     create_default_locs(regWMotR, locWMotR_table, player) | ||||
|     world.regions.append(regWMotR) | ||||
|     create_default_locs(regWMotR, locWMotR_table) | ||||
|  | ||||
|     regBitS = create_region("Bowser in the Sky", player, world) | ||||
|     create_default_locs(regBitS, locBitS_table, player) | ||||
|     world.regions.append(regBitS) | ||||
|     create_locs(regBitS, "Bowser in the Sky 1Up Block") | ||||
|     create_subregion(regBitS, "BitS: Top", "Bowser in the Sky Red Coins") | ||||
|  | ||||
|  | ||||
| def connect_regions(world: MultiWorld, player: int, source: str, target: str, rule=None): | ||||
| @@ -227,9 +238,30 @@ 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, 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] | ||||
| def create_region(name: str, player: int, world: MultiWorld) -> Region: | ||||
|     region = Region(name, player, world) | ||||
|     world.regions.append(region) | ||||
|     return region | ||||
|  | ||||
|  | ||||
| def create_subregion(source_region: Region, name: str, *locs: str) -> Region: | ||||
|     region = Region(name, source_region.player, source_region.multiworld) | ||||
|     connection = Entrance(source_region.player, name, source_region) | ||||
|     source_region.exits.append(connection) | ||||
|     connection.connect(region) | ||||
|     source_region.multiworld.regions.append(region) | ||||
|     create_locs(region, *locs) | ||||
|     return region | ||||
|  | ||||
|  | ||||
| def set_subregion_access_rule(world, player, region_name: str, rule): | ||||
|     world.get_entrance(world, player, region_name).access_rule = rule | ||||
|  | ||||
|  | ||||
| def create_default_locs(reg: Region, default_locs: dict): | ||||
|     create_locs(reg, *default_locs.keys()) | ||||
|  | ||||
|  | ||||
| def create_locs(reg: Region, *locs: str): | ||||
|     reg.locations += [SM64Location(reg.player, loc_name, location_table[loc_name], reg) for loc_name in locs] | ||||
|   | ||||
| @@ -1,36 +1,59 @@ | ||||
| from ..generic.Rules import add_rule | ||||
| from .Regions import connect_regions, SM64Levels, sm64_level_to_paintings, sm64_paintings_to_level, sm64_level_to_secrets, sm64_secrets_to_level, sm64_entrances_to_level, sm64_level_to_entrances | ||||
| from typing import Callable, Union, Dict, Set | ||||
|  | ||||
| def shuffle_dict_keys(world, obj: dict) -> dict: | ||||
|     keys = list(obj.keys()) | ||||
|     values = list(obj.values()) | ||||
| from BaseClasses import MultiWorld | ||||
| from ..generic.Rules import add_rule, set_rule | ||||
| from .Locations import location_table | ||||
| from .Regions import connect_regions, SM64Levels, sm64_level_to_paintings, sm64_paintings_to_level,\ | ||||
| sm64_level_to_secrets, sm64_secrets_to_level, sm64_entrances_to_level, sm64_level_to_entrances | ||||
| from .Items import action_item_table | ||||
|  | ||||
| def shuffle_dict_keys(world, dictionary: dict) -> dict: | ||||
|     keys = list(dictionary.keys()) | ||||
|     values = list(dictionary.values()) | ||||
|     world.random.shuffle(keys) | ||||
|     return dict(zip(keys,values)) | ||||
|     return dict(zip(keys, values)) | ||||
|  | ||||
| def fix_reg(entrance_map: dict, entrance: SM64Levels, invalid_regions: set, | ||||
|             swapdict: dict, world): | ||||
| def fix_reg(entrance_map: Dict[SM64Levels, str], entrance: SM64Levels, invalid_regions: Set[str], | ||||
|             swapdict: Dict[SM64Levels, str], world): | ||||
|     if entrance_map[entrance] in invalid_regions: # Unlucky :C | ||||
|         replacement_regions = [(rand_region, rand_entrance) for rand_region, rand_entrance in swapdict.items() | ||||
|         replacement_regions = [(rand_entrance, rand_region) for rand_entrance, rand_region in swapdict.items() | ||||
|                                if rand_region not in invalid_regions] | ||||
|         rand_region, rand_entrance = world.random.choice(replacement_regions) | ||||
|         rand_entrance, rand_region = world.random.choice(replacement_regions) | ||||
|         old_dest = entrance_map[entrance] | ||||
|         entrance_map[entrance], entrance_map[rand_entrance] = rand_region, old_dest | ||||
|         swapdict[rand_region] = entrance | ||||
|     swapdict.pop(entrance_map[entrance]) # Entrance now fixed to rand_region | ||||
|         swapdict[entrance], swapdict[rand_entrance] = rand_region, old_dest | ||||
|     swapdict.pop(entrance) | ||||
|  | ||||
| def set_rules(world, player: int, area_connections: dict): | ||||
| def set_rules(world, player: int, area_connections: dict, star_costs: dict, move_rando_bitvec: int): | ||||
|     randomized_level_to_paintings = sm64_level_to_paintings.copy() | ||||
|     randomized_level_to_secrets = sm64_level_to_secrets.copy() | ||||
|     valid_move_randomizer_start_courses = [ | ||||
|         "Bob-omb Battlefield", "Jolly Roger Bay", "Cool, Cool Mountain", | ||||
|         "Big Boo's Haunt", "Lethal Lava Land", "Shifting Sand Land", | ||||
|         "Dire, Dire Docks", "Snowman's Land" | ||||
|     ]  # Excluding WF, HMC, WDW, TTM, THI, TTC, and RR | ||||
|     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 not shuffling later, ensure a valid start course on move randomizer | ||||
|         if world.AreaRandomizer[player].value < 3 and move_rando_bitvec > 0: | ||||
|             swapdict = randomized_level_to_paintings.copy() | ||||
|             invalid_start_courses = {course for course in randomized_level_to_paintings.values() if course not in valid_move_randomizer_start_courses} | ||||
|             fix_reg(randomized_level_to_paintings, SM64Levels.BOB_OMB_BATTLEFIELD, invalid_start_courses, swapdict, world) | ||||
|             fix_reg(randomized_level_to_paintings, SM64Levels.WHOMPS_FORTRESS, invalid_start_courses, swapdict, world) | ||||
|  | ||||
|     if world.AreaRandomizer[player].value == 2:  # Randomize Secrets as well | ||||
|         randomized_level_to_secrets = shuffle_dict_keys(world,sm64_level_to_secrets) | ||||
|     randomized_entrances = { **randomized_level_to_paintings, **randomized_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 | ||||
|         randomized_entrances = shuffle_dict_keys(world,randomized_entrances) | ||||
|         swapdict = { entrance: level for (level,entrance) in randomized_entrances.items() } | ||||
|         randomized_entrances = shuffle_dict_keys(world, randomized_entrances) | ||||
|         # Guarantee first entrance is a course | ||||
|         fix_reg(randomized_entrances, SM64Levels.BOB_OMB_BATTLEFIELD, sm64_secrets_to_level.keys(), swapdict, world) | ||||
|         swapdict = randomized_entrances.copy() | ||||
|         if move_rando_bitvec == 0: | ||||
|             fix_reg(randomized_entrances, SM64Levels.BOB_OMB_BATTLEFIELD, sm64_secrets_to_level.keys(), swapdict, world) | ||||
|         else: | ||||
|             invalid_start_courses = {course for course in randomized_entrances.values() if course not in valid_move_randomizer_start_courses} | ||||
|             fix_reg(randomized_entrances, SM64Levels.BOB_OMB_BATTLEFIELD, invalid_start_courses, swapdict, world) | ||||
|             fix_reg(randomized_entrances, SM64Levels.WHOMPS_FORTRESS, invalid_start_courses, swapdict, world) | ||||
|         # Guarantee BITFS is not mapped to DDD | ||||
|         fix_reg(randomized_entrances, SM64Levels.BOWSER_IN_THE_FIRE_SEA, {"Dire, Dire Docks"}, swapdict, world) | ||||
|         # Guarantee COTMC is not mapped to HMC, cuz thats impossible. If BitFS -> HMC, also no COTMC -> DDD. | ||||
| @@ -43,27 +66,34 @@ def set_rules(world, player: int, area_connections: dict): | ||||
|     # Cast to int to not rely on availability of SM64Levels enum. Will cause crash in MultiServer otherwise | ||||
|     area_connections.update({int(entrance_lvl): int(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()} | ||||
|      | ||||
|  | ||||
|     rf = RuleFactory(world, player, move_rando_bitvec) | ||||
|  | ||||
|     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)) | ||||
|     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, randomized_entrances_s["Jolly Roger Bay"], randomized_entrances_s["The Secret Aquarium"], | ||||
|                     rf.build_rule("SF/BF | TJ & LG | MOVELESS & TJ")) | ||||
|     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", randomized_entrances_s["Bowser in the Dark World"], | ||||
|                     lambda state: state.has("Power Star", player, star_costs["FirstBowserDoorCost"])) | ||||
|  | ||||
|     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", 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, "Basement", randomized_entrances_s["Dire, Dire Docks"], | ||||
|                     lambda state: state.has("Power Star", player, star_costs["BasementDoorCost"])) | ||||
|     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, "Basement", randomized_entrances_s["Vanish Cap under the Moat"], | ||||
|                     rf.build_rule("GP")) | ||||
|     connect_regions(world, player, "Basement", randomized_entrances_s["Bowser in the Fire Sea"], | ||||
|                     lambda state: state.has("Power Star", player, star_costs["BasementDoorCost"]) 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)) | ||||
|  | ||||
| @@ -72,66 +102,127 @@ def set_rules(world, player: int, area_connections: dict): | ||||
|     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, "Tiny-Huge Island (Tiny)", "Tiny-Huge Island") | ||||
|     connect_regions(world, player, "Tiny-Huge Island (Huge)", "Tiny-Huge Island") | ||||
|  | ||||
|     connect_regions(world, player, "Second Floor", "Third Floor", lambda state: state.has("Power Star", player, world.SecondFloorStarDoorCost[player].value)) | ||||
|     connect_regions(world, player, "Second Floor", "Third Floor", lambda state: state.has("Power Star", player, star_costs["SecondFloorDoorCost"])) | ||||
|  | ||||
|     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)) | ||||
|     connect_regions(world, player, "Third Floor", "Bowser in the Sky", lambda state: state.has("Power Star", player, star_costs["StarsToFinish"])) | ||||
|  | ||||
|     #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)) | ||||
|     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)) | ||||
|     add_rule(world.get_location("DDD: Pole-Jumping for Red Coins", player), lambda state: state.can_reach("Bowser in the Fire Sea", 'Region', player)) | ||||
|     # Course Rules | ||||
|     # Bob-omb Battlefield | ||||
|     rf.assign_rule("BoB: Island", "CANN | CANNLESS & WC & TJ | CAPLESS & CANNLESS & LJ") | ||||
|     rf.assign_rule("BoB: Mario Wings to the Sky",  "CANN & WC | CAPLESS & CANN") | ||||
|     rf.assign_rule("BoB: Behind Chain Chomp's Gate", "GP | MOVELESS") | ||||
|     # Whomp's Fortress | ||||
|     rf.assign_rule("WF: Tower", "{{WF: Chip Off Whomp's Block}}") | ||||
|     rf.assign_rule("WF: Chip Off Whomp's Block", "GP") | ||||
|     rf.assign_rule("WF: Shoot into the Wild Blue", "WK & TJ/SF | CANN") | ||||
|     rf.assign_rule("WF: Fall onto the Caged Island", "CL & {WF: Tower} | MOVELESS & TJ | MOVELESS & LJ | MOVELESS & CANN") | ||||
|     rf.assign_rule("WF: Blast Away the Wall", "CANN | CANNLESS & LG") | ||||
|     # Jolly Roger Bay | ||||
|     rf.assign_rule("JRB: Upper", "TJ/BF/SF/WK | MOVELESS & LG") | ||||
|     rf.assign_rule("JRB: Red Coins on the Ship Afloat", "CL/CANN/TJ/BF/WK") | ||||
|     rf.assign_rule("JRB: Blast to the Stone Pillar", "CANN+CL | CANNLESS & MOVELESS | CANN & MOVELESS") | ||||
|     rf.assign_rule("JRB: Through the Jet Stream", "MC | CAPLESS") | ||||
|     # Cool, Cool Mountain | ||||
|     rf.assign_rule("CCM: Wall Kicks Will Work", "TJ/WK & CANN | CANNLESS & TJ/WK | MOVELESS") | ||||
|     # Big Boo's Haunt | ||||
|     rf.assign_rule("BBH: Third Floor", "WK+LG | MOVELESS & WK") | ||||
|     rf.assign_rule("BBH: Roof", "LJ | MOVELESS") | ||||
|     rf.assign_rule("BBH: Secret of the Haunted Books", "KK | MOVELESS") | ||||
|     rf.assign_rule("BBH: Seek the 8 Red Coins", "BF/WK/TJ/SF") | ||||
|     rf.assign_rule("BBH: Eye to Eye in the Secret Room", "VC") | ||||
|     # Haze Maze Cave | ||||
|     rf.assign_rule("HMC: Red Coin Area", "CL & WK/LG/BF/SF/TJ | MOVELESS & WK") | ||||
|     rf.assign_rule("HMC: Pit Islands", "TJ+CL | MOVELESS & WK & TJ/LJ | MOVELESS & WK+SF+LG") | ||||
|     rf.assign_rule("HMC: Metal-Head Mario Can Move!", "LJ+MC | CAPLESS & LJ+TJ | CAPLESS & MOVELESS & LJ/TJ/WK") | ||||
|     rf.assign_rule("HMC: Navigating the Toxic Maze", "WK/SF/BF/TJ") | ||||
|     rf.assign_rule("HMC: Watch for Rolling Rocks", "WK") | ||||
|     # Lethal Lava Land | ||||
|     rf.assign_rule("LLL: Upper Volcano", "CL") | ||||
|     # Shifting Sand Land | ||||
|     rf.assign_rule("SSL: Upper Pyramid", "CL & TJ/BF/SF/LG | MOVELESS") | ||||
|     rf.assign_rule("SSL: Free Flying for 8 Red Coins", "TJ/SF/BF & TJ+WC | TJ/SF/BF & CAPLESS | MOVELESS") | ||||
|     # Dire, Dire Docks | ||||
|     rf.assign_rule("DDD: Moving Poles", "CL & {{Bowser in the Fire Sea Key}} | TJ+DV+LG+WK & MOVELESS") | ||||
|     rf.assign_rule("DDD: Through the Jet Stream", "MC | CAPLESS") | ||||
|     rf.assign_rule("DDD: Collect the Caps...", "VC+MC | CAPLESS & VC") | ||||
|     # Snowman's Land | ||||
|     rf.assign_rule("SL: Snowman's Big Head", "BF/SF/CANN/TJ") | ||||
|     rf.assign_rule("SL: In the Deep Freeze", "WK/SF/LG/BF/CANN/TJ") | ||||
|     rf.assign_rule("SL: Into the Igloo", "VC & TJ/SF/BF/WK/LG | MOVELESS & VC") | ||||
|     # Wet-Dry World | ||||
|     rf.assign_rule("WDW: Top", "WK/TJ/SF/BF | MOVELESS") | ||||
|     rf.assign_rule("WDW: Downtown", "NAR & LG & TJ/SF/BF | CANN | MOVELESS & TJ+DV") | ||||
|     rf.assign_rule("WDW: Go to Town for Red Coins", "WK | MOVELESS & TJ") | ||||
|     rf.assign_rule("WDW: Quick Race Through Downtown!", "VC & WK/BF | VC & TJ+LG | MOVELESS & VC & TJ") | ||||
|     rf.assign_rule("WDW: Bob-omb Buddy", "TJ | SF+LG | NAR & BF/SF") | ||||
|     # Tall, Tall Mountain | ||||
|     rf.assign_rule("TTM: Top", "MOVELESS & TJ | LJ/DV & LG/KK | MOVELESS & WK & SF/LG | MOVELESS & KK/DV") | ||||
|     rf.assign_rule("TTM: Blast to the Lonely Mushroom", "CANN | CANNLESS & LJ | MOVELESS & CANNLESS") | ||||
|     # Tiny-Huge Island | ||||
|     rf.assign_rule("THI: Pipes", "NAR | LJ/TJ/DV/LG | MOVELESS & BF/SF/KK") | ||||
|     rf.assign_rule("THI: Large Top", "NAR | LJ/TJ/DV | MOVELESS") | ||||
|     rf.assign_rule("THI: Wiggler's Red Coins", "WK") | ||||
|     rf.assign_rule("THI: Make Wiggler Squirm", "GP | MOVELESS & DV") | ||||
|     # Tick Tock Clock | ||||
|     rf.assign_rule("TTC: Lower", "LG/TJ/SF/BF/WK") | ||||
|     rf.assign_rule("TTC: Upper", "CL | SF+WK") | ||||
|     rf.assign_rule("TTC: Top", "CL | SF+WK") | ||||
|     rf.assign_rule("TTC: Stomp on the Thwomp", "LG & TJ/SF/BF") | ||||
|     rf.assign_rule("TTC: Stop Time for Red Coins", "NAR | {TTC: Lower}") | ||||
|     # Rainbow Ride | ||||
|     rf.assign_rule("RR: Maze", "WK | LJ & SF/BF/TJ | MOVELESS & LG/TJ") | ||||
|     rf.assign_rule("RR: Bob-omb Buddy", "WK | MOVELESS & LG") | ||||
|     rf.assign_rule("RR: Swingin' in the Breeze", "LG/TJ/BF/SF") | ||||
|     rf.assign_rule("RR: Tricky Triangles!", "LG/TJ/BF/SF") | ||||
|     rf.assign_rule("RR: Cruiser", "WK/SF/BF/LG/TJ") | ||||
|     rf.assign_rule("RR: House", "TJ/SF/BF/LG") | ||||
|     rf.assign_rule("RR: Somewhere Over the Rainbow", "CANN") | ||||
|     # Cavern of the Metal Cap | ||||
|     rf.assign_rule("Cavern of the Metal Cap Red Coins", "MC | CAPLESS") | ||||
|     # Vanish Cap Under the Moat | ||||
|     rf.assign_rule("Vanish Cap Under the Moat Switch", "WK/TJ/BF/SF/LG | MOVELESS") | ||||
|     rf.assign_rule("Vanish Cap Under the Moat Red Coins", "TJ/BF/SF/LG/WK & VC | CAPLESS & WK") | ||||
|     # Bowser in the Fire Sea | ||||
|     rf.assign_rule("BitFS: Upper", "CL") | ||||
|     rf.assign_rule("Bowser in the Fire Sea Red Coins", "LG/WK") | ||||
|     rf.assign_rule("Bowser in the Fire Sea 1Up Block Near Poles", "LG/WK") | ||||
|     # Wing Mario Over the Rainbow | ||||
|     rf.assign_rule("Wing Mario Over the Rainbow Red Coins", "TJ+WC") | ||||
|     rf.assign_rule("Wing Mario Over the Rainbow 1Up Block", "TJ+WC") | ||||
|     # Bowser in the Sky | ||||
|     rf.assign_rule("BitS: Top", "CL+TJ | CL+SF+LG | MOVELESS & TJ+WK+LG") | ||||
|     # 100 Coin Stars | ||||
|     if world.EnableCoinStars[player]: | ||||
|         add_rule(world.get_location("DDD: 100 Coins", player), lambda state: state.can_reach("Bowser in the Fire Sea", 'Region', player)) | ||||
|     add_rule(world.get_location("SL: Into the Igloo", player), lambda state: state.has("Vanish Cap", player)) | ||||
|     add_rule(world.get_location("WDW: Quick Race Through Downtown!", player), lambda state: state.has("Vanish Cap", player)) | ||||
|     add_rule(world.get_location("RR: Somewhere Over the Rainbow", player), lambda state: state.has("Cannon Unlock RR", player)) | ||||
|  | ||||
|     if world.AreaRandomizer[player] or world.StrictCannonRequirements[player]: | ||||
|         # If area rando is on, it may not be possible to modify WDW's starting water level, | ||||
|         # which would make it impossible to reach downtown area without the cannon. | ||||
|         add_rule(world.get_location("WDW: Quick Race Through Downtown!", player), lambda state: state.has("Cannon Unlock WDW", player)) | ||||
|         add_rule(world.get_location("WDW: Go to Town for Red Coins", player), lambda state: state.has("Cannon Unlock WDW", player)) | ||||
|         add_rule(world.get_location("WDW: 1Up Block in Downtown", player), lambda state: state.has("Cannon Unlock WDW", player)) | ||||
|  | ||||
|     if world.StrictCapRequirements[player]: | ||||
|         add_rule(world.get_location("BoB: Mario Wings to the Sky", player), lambda state: state.has("Wing Cap", player)) | ||||
|         add_rule(world.get_location("HMC: Metal-Head Mario Can Move!", player), lambda state: state.has("Metal Cap", player)) | ||||
|         add_rule(world.get_location("JRB: Through the Jet Stream", player), lambda state: state.has("Metal Cap", player)) | ||||
|         add_rule(world.get_location("SSL: Free Flying for 8 Red Coins", player), lambda state: state.has("Wing Cap", player)) | ||||
|         add_rule(world.get_location("DDD: Through the Jet Stream", player), lambda state: state.has("Metal Cap", player)) | ||||
|         add_rule(world.get_location("DDD: Collect the Caps...", player), lambda state: state.has("Metal Cap", player)) | ||||
|         add_rule(world.get_location("Vanish Cap Under the Moat Red Coins", player), lambda state: state.has("Vanish Cap", player)) | ||||
|         add_rule(world.get_location("Cavern of the Metal Cap Red Coins", player), lambda state: state.has("Metal Cap", player)) | ||||
|     if world.StrictCannonRequirements[player]: | ||||
|         add_rule(world.get_location("WF: Blast Away the Wall", player), lambda state: state.has("Cannon Unlock WF", player)) | ||||
|         add_rule(world.get_location("JRB: Blast to the Stone Pillar", player), lambda state: state.has("Cannon Unlock JRB", player)) | ||||
|         add_rule(world.get_location("CCM: Wall Kicks Will Work", player), lambda state: state.has("Cannon Unlock CCM", player)) | ||||
|         add_rule(world.get_location("TTM: Blast to the Lonely Mushroom", player), lambda state: state.has("Cannon Unlock TTM", player)) | ||||
|     if world.StrictCapRequirements[player] and world.StrictCannonRequirements[player]: | ||||
|         # Ability to reach the floating island. Need some of those coins to get 100 coin star as well. | ||||
|         add_rule(world.get_location("BoB: Find the 8 Red Coins", player), lambda state: state.has("Cannon Unlock BoB", player) or state.has("Wing Cap", player)) | ||||
|         add_rule(world.get_location("BoB: Shoot to the Island in the Sky", player), lambda state: state.has("Cannon Unlock BoB", player) or state.has("Wing Cap", player)) | ||||
|         if world.EnableCoinStars[player]: | ||||
|             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("Wing Mario Over the Rainbow Red Coins", player), lambda state: state.has("Wing Cap", player)) | ||||
|     add_rule(world.get_location("Wing Mario Over the Rainbow 1Up Block", player), lambda state: state.has("Wing Cap", player)) | ||||
|         rf.assign_rule("BoB: 100 Coins", "CANN & WC | CANNLESS & WC & TJ") | ||||
|         rf.assign_rule("WF: 100 Coins", "GP | MOVELESS") | ||||
|         rf.assign_rule("JRB: 100 Coins", "GP & {JRB: Upper}") | ||||
|         rf.assign_rule("HMC: 100 Coins", "GP") | ||||
|         rf.assign_rule("SSL: 100 Coins", "{SSL: Upper Pyramid} | GP") | ||||
|         rf.assign_rule("DDD: 100 Coins", "GP") | ||||
|         rf.assign_rule("SL: 100 Coins", "VC | MOVELESS") | ||||
|         rf.assign_rule("WDW: 100 Coins", "GP | {WDW: Downtown}") | ||||
|         rf.assign_rule("TTC: 100 Coins", "GP") | ||||
|         rf.assign_rule("THI: 100 Coins", "GP") | ||||
|         rf.assign_rule("RR: 100 Coins", "GP & WK") | ||||
|     # Castle Stars | ||||
|     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)) | ||||
|  | ||||
|     if world.MIPS1Cost[player].value > world.MIPS2Cost[player].value: | ||||
|         (world.MIPS2Cost[player].value, world.MIPS1Cost[player].value) = (world.MIPS1Cost[player].value, world.MIPS2Cost[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)) | ||||
|     if star_costs["MIPS1Cost"] > star_costs["MIPS2Cost"]: | ||||
|         (star_costs["MIPS2Cost"], star_costs["MIPS1Cost"]) = (star_costs["MIPS1Cost"], star_costs["MIPS2Cost"]) | ||||
|     rf.assign_rule("MIPS 1", "DV | MOVELESS") | ||||
|     rf.assign_rule("MIPS 2", "DV | MOVELESS") | ||||
|     add_rule(world.get_location("MIPS 1", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, star_costs["MIPS1Cost"])) | ||||
|     add_rule(world.get_location("MIPS 2", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, star_costs["MIPS2Cost"])) | ||||
|  | ||||
|     world.completion_condition[player] = lambda state: state.can_reach("BitS: Top", 'Region', player) | ||||
|  | ||||
|     if world.CompletionType[player] == "last_bowser_stage": | ||||
|         world.completion_condition[player] = lambda state: state.can_reach("Bowser in the Sky", 'Region', player) | ||||
| @@ -139,3 +230,145 @@ def set_rules(world, player: int, area_connections: dict): | ||||
|         world.completion_condition[player] = lambda state: state.can_reach("Bowser in the Dark World", 'Region', player) and \ | ||||
|                                                            state.can_reach("Bowser in the Fire Sea", 'Region', player) and \ | ||||
|                                                            state.can_reach("Bowser in the Sky", 'Region', player) | ||||
|  | ||||
|  | ||||
| class RuleFactory: | ||||
|  | ||||
|     world: MultiWorld | ||||
|     player: int | ||||
|     move_rando_bitvec: bool | ||||
|     area_randomizer: bool | ||||
|     capless: bool | ||||
|     cannonless: bool | ||||
|     moveless: bool | ||||
|  | ||||
|     token_table = { | ||||
|         "TJ": "Triple Jump", | ||||
|         "LJ": "Long Jump", | ||||
|         "BF": "Backflip", | ||||
|         "SF": "Side Flip", | ||||
|         "WK": "Wall Kick", | ||||
|         "DV": "Dive", | ||||
|         "GP": "Ground Pound", | ||||
|         "KK": "Kick", | ||||
|         "CL": "Climb", | ||||
|         "LG": "Ledge Grab", | ||||
|         "WC": "Wing Cap", | ||||
|         "MC": "Metal Cap", | ||||
|         "VC": "Vanish Cap" | ||||
|     } | ||||
|  | ||||
|     class SM64LogicException(Exception): | ||||
|         pass | ||||
|  | ||||
|     def __init__(self, world, player, move_rando_bitvec): | ||||
|         self.world = world | ||||
|         self.player = player | ||||
|         self.move_rando_bitvec = move_rando_bitvec | ||||
|         self.area_randomizer = world.AreaRandomizer[player].value > 0 | ||||
|         self.capless = not world.StrictCapRequirements[player] | ||||
|         self.cannonless = not world.StrictCannonRequirements[player] | ||||
|         self.moveless = not world.StrictMoveRequirements[player] or not move_rando_bitvec > 0 | ||||
|  | ||||
|     def assign_rule(self, target_name: str, rule_expr: str): | ||||
|         target = self.world.get_location(target_name, self.player) if target_name in location_table else self.world.get_entrance(target_name, self.player) | ||||
|         cannon_name = "Cannon Unlock " + target_name.split(':')[0] | ||||
|         try: | ||||
|             rule = self.build_rule(rule_expr, cannon_name) | ||||
|         except RuleFactory.SM64LogicException as exception: | ||||
|             raise RuleFactory.SM64LogicException( | ||||
|                 f"Error generating rule for {target_name} using rule expression {rule_expr}: {exception}") | ||||
|         if rule: | ||||
|             set_rule(target, rule) | ||||
|  | ||||
|     def build_rule(self, rule_expr: str, cannon_name: str = '') -> Callable: | ||||
|         expressions = rule_expr.split(" | ") | ||||
|         rules = [] | ||||
|         for expression in expressions: | ||||
|             or_clause = self.combine_and_clauses(expression, cannon_name) | ||||
|             if or_clause is True: | ||||
|                 return None | ||||
|             if or_clause is not False: | ||||
|                 rules.append(or_clause) | ||||
|         if rules: | ||||
|             if len(rules) == 1: | ||||
|                 return rules[0] | ||||
|             else: | ||||
|                 return lambda state: any(rule(state) for rule in rules) | ||||
|         else: | ||||
|             return None | ||||
|  | ||||
|     def combine_and_clauses(self, rule_expr: str, cannon_name: str) -> Union[Callable, bool]: | ||||
|         expressions = rule_expr.split(" & ") | ||||
|         rules = [] | ||||
|         for expression in expressions: | ||||
|             and_clause = self.make_lambda(expression, cannon_name) | ||||
|             if and_clause is False: | ||||
|                 return False | ||||
|             if and_clause is not True: | ||||
|                 rules.append(and_clause) | ||||
|         if rules: | ||||
|             if len(rules) == 1: | ||||
|                 return rules[0] | ||||
|             return lambda state: all(rule(state) for rule in rules) | ||||
|         else: | ||||
|             return True | ||||
|  | ||||
|     def make_lambda(self, expression: str, cannon_name: str) -> Union[Callable, bool]: | ||||
|         if '+' in expression: | ||||
|             tokens = expression.split('+') | ||||
|             items = set() | ||||
|             for token in tokens: | ||||
|                 item = self.parse_token(token, cannon_name) | ||||
|                 if item is True: | ||||
|                     continue | ||||
|                 if item is False: | ||||
|                     return False | ||||
|                 items.add(item) | ||||
|             if items: | ||||
|                 return lambda state: state.has_all(items, self.player) | ||||
|             else: | ||||
|                 return True | ||||
|         if '/' in expression: | ||||
|             tokens = expression.split('/') | ||||
|             items = set() | ||||
|             for token in tokens: | ||||
|                 item = self.parse_token(token, cannon_name) | ||||
|                 if item is True: | ||||
|                     return True | ||||
|                 if item is False: | ||||
|                     continue | ||||
|                 items.add(item) | ||||
|             if items: | ||||
|                 return lambda state: state.has_any(items, self.player) | ||||
|             else: | ||||
|                 return False | ||||
|         if '{{' in expression: | ||||
|             return lambda state: state.can_reach(expression[2:-2], "Location", self.player) | ||||
|         if '{' in expression: | ||||
|             return lambda state: state.can_reach(expression[1:-1], "Region", self.player) | ||||
|         item = self.parse_token(expression, cannon_name) | ||||
|         if item in (True, False): | ||||
|             return item | ||||
|         return lambda state: state.has(item, self.player) | ||||
|  | ||||
|     def parse_token(self, token: str, cannon_name: str) -> Union[str, bool]: | ||||
|         if token == "CANN": | ||||
|             return cannon_name | ||||
|         if token == "CAPLESS": | ||||
|             return self.capless | ||||
|         if token == "CANNLESS": | ||||
|             return self.cannonless | ||||
|         if token == "MOVELESS": | ||||
|             return self.moveless | ||||
|         if token == "NAR": | ||||
|             return not self.area_randomizer | ||||
|         item = self.token_table.get(token, None) | ||||
|         if not item: | ||||
|             raise Exception(f"Invalid token: '{item}'") | ||||
|         if item in action_item_table: | ||||
|             if self.move_rando_bitvec & (1 << (action_item_table[item] - action_item_table['Double Jump'])) == 0: | ||||
|                 # This action item is not randomized. | ||||
|                 return True | ||||
|         return item | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import typing | ||||
| import os | ||||
| import json | ||||
| from .Items import item_table, cannon_item_table, SM64Item | ||||
| from .Items import item_table, action_item_table, cannon_item_table, SM64Item | ||||
| from .Locations import location_table, SM64Location | ||||
| from .Options import sm64_options | ||||
| from .Rules import set_rules | ||||
| @@ -35,14 +35,44 @@ class SM64World(World): | ||||
|     item_name_to_id = item_table | ||||
|     location_name_to_id = location_table | ||||
|  | ||||
|     data_version = 8 | ||||
|     data_version = 9 | ||||
|     required_client_version = (0, 3, 5) | ||||
|  | ||||
|     area_connections: typing.Dict[int, int] | ||||
|  | ||||
|     option_definitions = sm64_options | ||||
|  | ||||
|     number_of_stars: int | ||||
|     move_rando_bitvec: int | ||||
|     filler_count: int | ||||
|     star_costs: typing.Dict[str, int] | ||||
|  | ||||
|     def generate_early(self): | ||||
|         max_stars = 120 | ||||
|         if (not self.multiworld.EnableCoinStars[self.player].value): | ||||
|             max_stars -= 15 | ||||
|         self.move_rando_bitvec = 0 | ||||
|         for action, itemid in action_item_table.items(): | ||||
|             # HACK: Disable randomization of double jump | ||||
|             if action == 'Double Jump': continue | ||||
|             if getattr(self.multiworld, f"MoveRandomizer{action.replace(' ','')}")[self.player].value: | ||||
|                 max_stars -= 1 | ||||
|                 self.move_rando_bitvec |= (1 << (itemid - action_item_table['Double Jump'])) | ||||
|         if (self.multiworld.ExclamationBoxes[self.player].value > 0): | ||||
|             max_stars += 29 | ||||
|         self.number_of_stars = min(self.multiworld.AmountOfStars[self.player].value, max_stars) | ||||
|         self.filler_count = max_stars - self.number_of_stars | ||||
|         self.star_costs = { | ||||
|             'FirstBowserDoorCost': round(self.multiworld.FirstBowserStarDoorCost[self.player].value * self.number_of_stars / 100), | ||||
|             'BasementDoorCost': round(self.multiworld.BasementStarDoorCost[self.player].value * self.number_of_stars / 100), | ||||
|             'SecondFloorDoorCost': round(self.multiworld.SecondFloorStarDoorCost[self.player].value * self.number_of_stars / 100), | ||||
|             'MIPS1Cost': round(self.multiworld.MIPS1Cost[self.player].value * self.number_of_stars / 100), | ||||
|             'MIPS2Cost': round(self.multiworld.MIPS2Cost[self.player].value * self.number_of_stars / 100), | ||||
|             'StarsToFinish': round(self.multiworld.StarsToFinish[self.player].value * self.number_of_stars / 100) | ||||
|         } | ||||
|         # Nudge MIPS 1 to match vanilla on default percentage | ||||
|         if self.number_of_stars == 120 and self.multiworld.MIPS1Cost[self.player].value == 12: | ||||
|             self.star_costs['MIPS1Cost'] = 15 | ||||
|         self.topology_present = self.multiworld.AreaRandomizer[self.player].value | ||||
|  | ||||
|     def create_regions(self): | ||||
| @@ -50,7 +80,7 @@ class SM64World(World): | ||||
|  | ||||
|     def set_rules(self): | ||||
|         self.area_connections = {} | ||||
|         set_rules(self.multiworld, self.player, self.area_connections) | ||||
|         set_rules(self.multiworld, self.player, self.area_connections, self.star_costs, self.move_rando_bitvec) | ||||
|         if self.topology_present: | ||||
|             # Write area_connections to spoiler log | ||||
|             for entrance, destination in self.area_connections.items(): | ||||
| @@ -72,31 +102,29 @@ class SM64World(World): | ||||
|         return item | ||||
|  | ||||
|     def create_items(self): | ||||
|         starcount = self.multiworld.AmountOfStars[self.player].value | ||||
|         if (not self.multiworld.EnableCoinStars[self.player].value): | ||||
|             starcount = max(35,self.multiworld.AmountOfStars[self.player].value-15) | ||||
|         starcount = max(starcount, self.multiworld.FirstBowserStarDoorCost[self.player].value, | ||||
|                         self.multiworld.BasementStarDoorCost[self.player].value, self.multiworld.SecondFloorStarDoorCost[self.player].value, | ||||
|                         self.multiworld.MIPS1Cost[self.player].value, self.multiworld.MIPS2Cost[self.player].value, | ||||
|                         self.multiworld.StarsToFinish[self.player].value) | ||||
|         self.multiworld.itempool += [self.create_item("Power Star") for i in range(0,starcount)] | ||||
|         self.multiworld.itempool += [self.create_item("1Up Mushroom") for i in range(starcount,120 - (15 if not self.multiworld.EnableCoinStars[self.player].value else 0))] | ||||
|  | ||||
|         # 1Up Mushrooms | ||||
|         self.multiworld.itempool += [self.create_item("1Up Mushroom") for i in range(0,self.filler_count)] | ||||
|         # Power Stars | ||||
|         self.multiworld.itempool += [self.create_item("Power Star") for i in range(0,self.number_of_stars)] | ||||
|         # Keys | ||||
|         if (not self.multiworld.ProgressiveKeys[self.player].value): | ||||
|             key1 = self.create_item("Basement Key") | ||||
|             key2 = self.create_item("Second Floor Key") | ||||
|             self.multiworld.itempool += [key1, key2] | ||||
|         else: | ||||
|             self.multiworld.itempool += [self.create_item("Progressive Key") for i in range(0,2)] | ||||
|  | ||||
|         wingcap = self.create_item("Wing Cap") | ||||
|         metalcap = self.create_item("Metal Cap") | ||||
|         vanishcap = self.create_item("Vanish Cap") | ||||
|         self.multiworld.itempool += [wingcap, metalcap, vanishcap] | ||||
|  | ||||
|         # Caps | ||||
|         self.multiworld.itempool += [self.create_item(cap_name) for cap_name in ["Wing Cap", "Metal Cap", "Vanish Cap"]] | ||||
|         # Cannons | ||||
|         if (self.multiworld.BuddyChecks[self.player].value): | ||||
|             self.multiworld.itempool += [self.create_item(name) for name, id in cannon_item_table.items()] | ||||
|         else: | ||||
|         # Moves | ||||
|         self.multiworld.itempool += [self.create_item(action) | ||||
|                                      for action, itemid in action_item_table.items() | ||||
|                                      if self.move_rando_bitvec & (1 << itemid - action_item_table['Double Jump'])] | ||||
|  | ||||
|     def generate_basic(self): | ||||
|         if not (self.multiworld.BuddyChecks[self.player].value): | ||||
|             self.multiworld.get_location("BoB: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock BoB")) | ||||
|             self.multiworld.get_location("WF: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock WF")) | ||||
|             self.multiworld.get_location("JRB: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock JRB")) | ||||
| @@ -108,9 +136,7 @@ class SM64World(World): | ||||
|             self.multiworld.get_location("THI: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock THI")) | ||||
|             self.multiworld.get_location("RR: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock RR")) | ||||
|  | ||||
|         if (self.multiworld.ExclamationBoxes[self.player].value > 0): | ||||
|             self.multiworld.itempool += [self.create_item("1Up Mushroom") for i in range(0,29)] | ||||
|         else: | ||||
|         if (self.multiworld.ExclamationBoxes[self.player].value == 0): | ||||
|             self.multiworld.get_location("CCM: 1Up Block Near Snowman", self.player).place_locked_item(self.create_item("1Up Mushroom")) | ||||
|             self.multiworld.get_location("CCM: 1Up Block Ice Pillar", self.player).place_locked_item(self.create_item("1Up Mushroom")) | ||||
|             self.multiworld.get_location("CCM: 1Up Block Secret Slide", self.player).place_locked_item(self.create_item("1Up Mushroom")) | ||||
| @@ -147,14 +173,10 @@ class SM64World(World): | ||||
|     def fill_slot_data(self): | ||||
|         return { | ||||
|             "AreaRando": self.area_connections, | ||||
|             "FirstBowserDoorCost": self.multiworld.FirstBowserStarDoorCost[self.player].value, | ||||
|             "BasementDoorCost": self.multiworld.BasementStarDoorCost[self.player].value, | ||||
|             "SecondFloorDoorCost": self.multiworld.SecondFloorStarDoorCost[self.player].value, | ||||
|             "MIPS1Cost": self.multiworld.MIPS1Cost[self.player].value, | ||||
|             "MIPS2Cost": self.multiworld.MIPS2Cost[self.player].value, | ||||
|             "StarsToFinish": self.multiworld.StarsToFinish[self.player].value, | ||||
|             "MoveRandoVec": self.move_rando_bitvec, | ||||
|             "DeathLink": self.multiworld.death_link[self.player].value, | ||||
|             "CompletionType" : self.multiworld.CompletionType[self.player].value, | ||||
|             "CompletionType": self.multiworld.CompletionType[self.player].value, | ||||
|             **self.star_costs | ||||
|         } | ||||
|  | ||||
|     def generate_output(self, output_directory: str): | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Magnemania
					Magnemania