sm64ex: New Options API and WebHost fix (#2979)

This commit is contained in:
Yussur Mustafa Oraji 2024-03-26 14:29:25 +01:00 committed by GitHub
parent ea47b90367
commit 98ce8f8844
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 90 additions and 87 deletions

View File

@ -1,5 +1,6 @@
import typing import typing
from Options import Option, DefaultOnToggle, Range, Toggle, DeathLink, Choice from dataclasses import dataclass
from Options import DefaultOnToggle, Range, Toggle, DeathLink, Choice, PerGameCommonOptions, OptionSet
from .Items import action_item_table from .Items import action_item_table
class EnableCoinStars(DefaultOnToggle): class EnableCoinStars(DefaultOnToggle):
@ -114,35 +115,37 @@ class StrictMoveRequirements(DefaultOnToggle):
if Move Randomization is enabled""" if Move Randomization is enabled"""
display_name = "Strict Move Requirements" display_name = "Strict Move Requirements"
def getMoveRandomizerOption(action: str): class EnableMoveRandomizer(Toggle):
class MoveRandomizerOption(Toggle): """Mario is unable to perform some actions until a corresponding item is picked up.
"""Mario is unable to perform this action until a corresponding item is picked up. This option is incompatible with builds using a 'nomoverando' branch.
This option is incompatible with builds using a 'nomoverando' branch.""" Specific actions to randomize can be specified in the YAML."""
display_name = f"Randomize {action}" display_name = "Enable Move Randomizer"
return MoveRandomizerOption
class MoveRandomizerActions(OptionSet):
"""Which actions to randomize when Move Randomizer is enabled"""
display_name = "Randomized Moves"
# HACK: Disable randomization for double jump
valid_keys = [action for action in action_item_table if action != 'Double Jump']
default = valid_keys
sm64_options: typing.Dict[str, type(Option)] = { @dataclass
"AreaRandomizer": AreaRandomizer, class SM64Options(PerGameCommonOptions):
"BuddyChecks": BuddyChecks, area_rando: AreaRandomizer
"ExclamationBoxes": ExclamationBoxes, buddy_checks: BuddyChecks
"ProgressiveKeys": ProgressiveKeys, exclamation_boxes: ExclamationBoxes
"EnableCoinStars": EnableCoinStars, progressive_keys: ProgressiveKeys
"StrictCapRequirements": StrictCapRequirements, enable_coin_stars: EnableCoinStars
"StrictCannonRequirements": StrictCannonRequirements, enable_move_rando: EnableMoveRandomizer
"StrictMoveRequirements": StrictMoveRequirements, move_rando_actions: MoveRandomizerActions
"AmountOfStars": AmountOfStars, strict_cap_requirements: StrictCapRequirements
"FirstBowserStarDoorCost": FirstBowserStarDoorCost, strict_cannon_requirements: StrictCannonRequirements
"BasementStarDoorCost": BasementStarDoorCost, strict_move_requirements: StrictMoveRequirements
"SecondFloorStarDoorCost": SecondFloorStarDoorCost, amount_of_stars: AmountOfStars
"MIPS1Cost": MIPS1Cost, first_bowser_star_door_cost: FirstBowserStarDoorCost
"MIPS2Cost": MIPS2Cost, basement_star_door_cost: BasementStarDoorCost
"StarsToFinish": StarsToFinish, second_floor_star_door_cost: SecondFloorStarDoorCost
"death_link": DeathLink, mips1_cost: MIPS1Cost
"CompletionType": CompletionType, mips2_cost: MIPS2Cost
} stars_to_finish: StarsToFinish
death_link: DeathLink
for action in action_item_table: completion_type: CompletionType
# HACK: Disable randomization of double jump
if action == 'Double Jump': continue
sm64_options[f"MoveRandomizer{action.replace(' ','')}"] = getMoveRandomizerOption(action)

View File

@ -2,6 +2,7 @@ import typing
from enum import Enum from enum import Enum
from BaseClasses import MultiWorld, Region, Entrance, Location from BaseClasses import MultiWorld, Region, Entrance, Location
from .Options import SM64Options
from .Locations import SM64Location, location_table, locBoB_table, locWhomp_table, locJRB_table, locCCM_table, \ from .Locations import SM64Location, location_table, locBoB_table, locWhomp_table, locJRB_table, locCCM_table, \
locBBH_table, \ locBBH_table, \
locHMC_table, locLLL_table, locSSL_table, locDDD_table, locSL_table, \ locHMC_table, locLLL_table, locSSL_table, locDDD_table, locSL_table, \
@ -78,7 +79,7 @@ sm64_secrets_to_level = {secret: level for (level,secret) in sm64_level_to_secre
sm64_entrances_to_level = {**sm64_paintings_to_level, **sm64_secrets_to_level } 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_level_to_entrances = {**sm64_level_to_paintings, **sm64_level_to_secrets }
def create_regions(world: MultiWorld, player: int): def create_regions(world: MultiWorld, options: SM64Options, player: int):
regSS = Region("Menu", player, world, "Castle Area") regSS = Region("Menu", player, world, "Castle Area")
create_default_locs(regSS, locSS_table) create_default_locs(regSS, locSS_table)
world.regions.append(regSS) world.regions.append(regSS)
@ -88,7 +89,7 @@ def create_regions(world: MultiWorld, player: int):
"BoB: Mario Wings to the Sky", "BoB: Behind Chain Chomp's Gate", "BoB: Bob-omb Buddy") "BoB: Mario Wings to the Sky", "BoB: Behind Chain Chomp's Gate", "BoB: Bob-omb Buddy")
bob_island = create_subregion(regBoB, "BoB: Island", "BoB: Shoot to the Island in the Sky", "BoB: Find the 8 Red Coins") bob_island = create_subregion(regBoB, "BoB: Island", "BoB: Shoot to the Island in the Sky", "BoB: Find the 8 Red Coins")
regBoB.subregions = [bob_island] regBoB.subregions = [bob_island]
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(regBoB, "BoB: 100 Coins") create_locs(regBoB, "BoB: 100 Coins")
regWhomp = create_region("Whomp's Fortress", player, world) regWhomp = create_region("Whomp's Fortress", player, world)
@ -96,7 +97,7 @@ def create_regions(world: MultiWorld, player: int):
"WF: Fall onto the Caged Island", "WF: Blast Away the Wall") "WF: Fall onto the Caged Island", "WF: Blast Away the Wall")
wf_tower = create_subregion(regWhomp, "WF: Tower", "WF: To the Top of the Fortress", "WF: Bob-omb Buddy") wf_tower = create_subregion(regWhomp, "WF: Tower", "WF: To the Top of the Fortress", "WF: Bob-omb Buddy")
regWhomp.subregions = [wf_tower] regWhomp.subregions = [wf_tower]
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(regWhomp, "WF: 100 Coins") create_locs(regWhomp, "WF: 100 Coins")
regJRB = create_region("Jolly Roger Bay", player, world) regJRB = create_region("Jolly Roger Bay", player, world)
@ -104,12 +105,12 @@ def create_regions(world: MultiWorld, player: int):
"JRB: Blast to the Stone Pillar", "JRB: Through the Jet Stream", "JRB: Bob-omb Buddy") "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") jrb_upper = create_subregion(regJRB, 'JRB: Upper', "JRB: Red Coins on the Ship Afloat")
regJRB.subregions = [jrb_upper] regJRB.subregions = [jrb_upper]
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(jrb_upper, "JRB: 100 Coins") create_locs(jrb_upper, "JRB: 100 Coins")
regCCM = create_region("Cool, Cool Mountain", player, world) regCCM = create_region("Cool, Cool Mountain", player, world)
create_default_locs(regCCM, locCCM_table) create_default_locs(regCCM, locCCM_table)
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(regCCM, "CCM: 100 Coins") create_locs(regCCM, "CCM: 100 Coins")
regBBH = create_region("Big Boo's Haunt", player, world) regBBH = create_region("Big Boo's Haunt", player, world)
@ -118,7 +119,7 @@ def create_regions(world: MultiWorld, player: int):
bbh_third_floor = create_subregion(regBBH, "BBH: Third Floor", "BBH: Eye to Eye in the Secret Room") bbh_third_floor = create_subregion(regBBH, "BBH: Third Floor", "BBH: Eye to Eye in the Secret Room")
bbh_roof = create_subregion(bbh_third_floor, "BBH: Roof", "BBH: Big Boo's Balcony", "BBH: 1Up Block Top of Mansion") bbh_roof = create_subregion(bbh_third_floor, "BBH: Roof", "BBH: Big Boo's Balcony", "BBH: 1Up Block Top of Mansion")
regBBH.subregions = [bbh_third_floor, bbh_roof] regBBH.subregions = [bbh_third_floor, bbh_roof]
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(regBBH, "BBH: 100 Coins") create_locs(regBBH, "BBH: 100 Coins")
regPSS = create_region("The Princess's Secret Slide", player, world) regPSS = create_region("The Princess's Secret Slide", player, world)
@ -141,7 +142,7 @@ def create_regions(world: MultiWorld, player: int):
hmc_red_coin_area = create_subregion(regHMC, "HMC: Red Coin Area", "HMC: Elevate for 8 Red Coins") hmc_red_coin_area = create_subregion(regHMC, "HMC: Red Coin Area", "HMC: Elevate for 8 Red Coins")
hmc_pit_islands = create_subregion(regHMC, "HMC: Pit Islands", "HMC: A-Maze-Ing Emergency Exit", "HMC: 1Up Block above Pit") hmc_pit_islands = create_subregion(regHMC, "HMC: Pit Islands", "HMC: A-Maze-Ing Emergency Exit", "HMC: 1Up Block above Pit")
regHMC.subregions = [hmc_red_coin_area, hmc_pit_islands] regHMC.subregions = [hmc_red_coin_area, hmc_pit_islands]
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(hmc_red_coin_area, "HMC: 100 Coins") create_locs(hmc_red_coin_area, "HMC: 100 Coins")
regLLL = create_region("Lethal Lava Land", player, world) regLLL = create_region("Lethal Lava Land", player, world)
@ -149,7 +150,7 @@ def create_regions(world: MultiWorld, player: int):
"LLL: 8-Coin Puzzle with 15 Pieces", "LLL: Red-Hot Log Rolling") "LLL: 8-Coin Puzzle with 15 Pieces", "LLL: Red-Hot Log Rolling")
lll_upper_volcano = create_subregion(regLLL, "LLL: Upper Volcano", "LLL: Hot-Foot-It into the Volcano", "LLL: Elevator Tour in the Volcano") lll_upper_volcano = create_subregion(regLLL, "LLL: Upper Volcano", "LLL: Hot-Foot-It into the Volcano", "LLL: Elevator Tour in the Volcano")
regLLL.subregions = [lll_upper_volcano] regLLL.subregions = [lll_upper_volcano]
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(regLLL, "LLL: 100 Coins") create_locs(regLLL, "LLL: 100 Coins")
regSSL = create_region("Shifting Sand Land", player, world) regSSL = create_region("Shifting Sand Land", player, world)
@ -159,7 +160,7 @@ def create_regions(world: MultiWorld, player: int):
ssl_upper_pyramid = create_subregion(regSSL, "SSL: Upper Pyramid", "SSL: Inside the Ancient Pyramid", ssl_upper_pyramid = create_subregion(regSSL, "SSL: Upper Pyramid", "SSL: Inside the Ancient Pyramid",
"SSL: Stand Tall on the Four Pillars", "SSL: Pyramid Puzzle") "SSL: Stand Tall on the Four Pillars", "SSL: Pyramid Puzzle")
regSSL.subregions = [ssl_upper_pyramid] regSSL.subregions = [ssl_upper_pyramid]
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(regSSL, "SSL: 100 Coins") create_locs(regSSL, "SSL: 100 Coins")
regDDD = create_region("Dire, Dire Docks", player, world) regDDD = create_region("Dire, Dire Docks", player, world)
@ -167,7 +168,7 @@ def create_regions(world: MultiWorld, player: int):
"DDD: The Manta Ray's Reward", "DDD: Collect the Caps...") "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") ddd_moving_poles = create_subregion(regDDD, "DDD: Moving Poles", "DDD: Pole-Jumping for Red Coins")
regDDD.subregions = [ddd_moving_poles] regDDD.subregions = [ddd_moving_poles]
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(ddd_moving_poles, "DDD: 100 Coins") create_locs(ddd_moving_poles, "DDD: 100 Coins")
regCotMC = create_region("Cavern of the Metal Cap", player, world) regCotMC = create_region("Cavern of the Metal Cap", player, world)
@ -184,7 +185,7 @@ def create_regions(world: MultiWorld, player: int):
regSL = create_region("Snowman's Land", player, world) regSL = create_region("Snowman's Land", player, world)
create_default_locs(regSL, locSL_table) create_default_locs(regSL, locSL_table)
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(regSL, "SL: 100 Coins") create_locs(regSL, "SL: 100 Coins")
regWDW = create_region("Wet-Dry World", player, world) regWDW = create_region("Wet-Dry World", player, world)
@ -193,7 +194,7 @@ def create_regions(world: MultiWorld, player: int):
"WDW: Secrets in the Shallows & Sky", "WDW: Bob-omb Buddy") "WDW: Secrets in the Shallows & Sky", "WDW: Bob-omb Buddy")
wdw_downtown = create_subregion(regWDW, "WDW: Downtown", "WDW: Go to Town for Red Coins", "WDW: Quick Race Through Downtown!", "WDW: 1Up Block in Downtown") wdw_downtown = create_subregion(regWDW, "WDW: Downtown", "WDW: Go to Town for Red Coins", "WDW: Quick Race Through Downtown!", "WDW: 1Up Block in Downtown")
regWDW.subregions = [wdw_top, wdw_downtown] regWDW.subregions = [wdw_top, wdw_downtown]
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(wdw_top, "WDW: 100 Coins") create_locs(wdw_top, "WDW: 100 Coins")
regTTM = create_region("Tall, Tall Mountain", player, world) regTTM = create_region("Tall, Tall Mountain", player, world)
@ -202,7 +203,7 @@ def create_regions(world: MultiWorld, player: int):
ttm_top = create_subregion(ttm_middle, "TTM: Top", "TTM: Scale the Mountain", "TTM: Mystery of the Monkey Cage", 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") "TTM: Mysterious Mountainside", "TTM: Breathtaking View from Bridge")
regTTM.subregions = [ttm_middle, ttm_top] regTTM.subregions = [ttm_middle, ttm_top]
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(ttm_top, "TTM: 100 Coins") create_locs(ttm_top, "TTM: 100 Coins")
create_region("Tiny-Huge Island (Huge)", player, world) create_region("Tiny-Huge Island (Huge)", player, world)
@ -214,7 +215,7 @@ def create_regions(world: MultiWorld, player: int):
"THI: 1Up Block THI Large near Start", "THI: 1Up Block Windy Area") "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") thi_large_top = create_subregion(thi_pipes, "THI: Large Top", "THI: Make Wiggler Squirm")
regTHI.subregions = [thi_pipes, thi_large_top] regTHI.subregions = [thi_pipes, thi_large_top]
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(thi_large_top, "THI: 100 Coins") create_locs(thi_large_top, "THI: 100 Coins")
regFloor3 = create_region("Third Floor", player, world) regFloor3 = create_region("Third Floor", player, world)
@ -225,7 +226,7 @@ def create_regions(world: MultiWorld, player: int):
ttc_upper = create_subregion(ttc_lower, "TTC: Upper", "TTC: Timed Jumps on Moving Bars", "TTC: The Pit and the Pendulums") 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") ttc_top = create_subregion(ttc_upper, "TTC: Top", "TTC: Stomp on the Thwomp", "TTC: 1Up Block at the Top")
regTTC.subregions = [ttc_lower, ttc_upper, ttc_top] regTTC.subregions = [ttc_lower, ttc_upper, ttc_top]
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(ttc_top, "TTC: 100 Coins") create_locs(ttc_top, "TTC: 100 Coins")
regRR = create_region("Rainbow Ride", player, world) regRR = create_region("Rainbow Ride", player, world)
@ -235,7 +236,7 @@ def create_regions(world: MultiWorld, player: int):
rr_cruiser = create_subregion(regRR, "RR: Cruiser", "RR: Cruiser Crossing the Rainbow", "RR: Somewhere Over the Rainbow") rr_cruiser = create_subregion(regRR, "RR: Cruiser", "RR: Cruiser Crossing the Rainbow", "RR: Somewhere Over the Rainbow")
rr_house = create_subregion(regRR, "RR: House", "RR: The Big House in the Sky", "RR: 1Up Block On House in the Sky") rr_house = create_subregion(regRR, "RR: House", "RR: The Big House in the Sky", "RR: 1Up Block On House in the Sky")
regRR.subregions = [rr_maze, rr_cruiser, rr_house] regRR.subregions = [rr_maze, rr_cruiser, rr_house]
if (world.EnableCoinStars[player].value): if options.enable_coin_stars:
create_locs(rr_maze, "RR: 100 Coins") create_locs(rr_maze, "RR: 100 Coins")
regWMotR = create_region("Wing Mario over the Rainbow", player, world) regWMotR = create_region("Wing Mario over the Rainbow", player, world)

View File

@ -3,6 +3,7 @@ from typing import Callable, Union, Dict, Set
from BaseClasses import MultiWorld from BaseClasses import MultiWorld
from ..generic.Rules import add_rule, set_rule from ..generic.Rules import add_rule, set_rule
from .Locations import location_table from .Locations import location_table
from .Options import SM64Options
from .Regions import connect_regions, SM64Levels, sm64_level_to_paintings, sm64_paintings_to_level,\ 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 sm64_level_to_secrets, sm64_secrets_to_level, sm64_entrances_to_level, sm64_level_to_entrances
from .Items import action_item_table from .Items import action_item_table
@ -24,7 +25,7 @@ def fix_reg(entrance_map: Dict[SM64Levels, str], entrance: SM64Levels, invalid_r
swapdict[entrance], swapdict[rand_entrance] = rand_region, old_dest swapdict[entrance], swapdict[rand_entrance] = rand_region, old_dest
swapdict.pop(entrance) swapdict.pop(entrance)
def set_rules(world, player: int, area_connections: dict, star_costs: dict, move_rando_bitvec: int): def set_rules(world, options: SM64Options, player: int, area_connections: dict, star_costs: dict, move_rando_bitvec: int):
randomized_level_to_paintings = sm64_level_to_paintings.copy() randomized_level_to_paintings = sm64_level_to_paintings.copy()
randomized_level_to_secrets = sm64_level_to_secrets.copy() randomized_level_to_secrets = sm64_level_to_secrets.copy()
valid_move_randomizer_start_courses = [ valid_move_randomizer_start_courses = [
@ -32,19 +33,19 @@ def set_rules(world, player: int, area_connections: dict, star_costs: dict, move
"Big Boo's Haunt", "Lethal Lava Land", "Shifting Sand Land", "Big Boo's Haunt", "Lethal Lava Land", "Shifting Sand Land",
"Dire, Dire Docks", "Snowman's Land" "Dire, Dire Docks", "Snowman's Land"
] # Excluding WF, HMC, WDW, TTM, THI, TTC, and RR ] # Excluding WF, HMC, WDW, TTM, THI, TTC, and RR
if world.AreaRandomizer[player].value >= 1: # Some randomization is happening, randomize Courses if options.area_rando >= 1: # Some randomization is happening, randomize Courses
randomized_level_to_paintings = shuffle_dict_keys(world,sm64_level_to_paintings) 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 not shuffling later, ensure a valid start course on move randomizer
if world.AreaRandomizer[player].value < 3 and move_rando_bitvec > 0: if options.area_rando < 3 and move_rando_bitvec > 0:
swapdict = randomized_level_to_paintings.copy() 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} 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.BOB_OMB_BATTLEFIELD, invalid_start_courses, swapdict, world)
fix_reg(randomized_level_to_paintings, SM64Levels.WHOMPS_FORTRESS, 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 if options.area_rando == 2: # Randomize Secrets as well
randomized_level_to_secrets = shuffle_dict_keys(world,sm64_level_to_secrets) 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 if options.area_rando == 3: # Randomize Courses and Secrets in one pool
randomized_entrances = shuffle_dict_keys(world, randomized_entrances) randomized_entrances = shuffle_dict_keys(world, randomized_entrances)
# Guarantee first entrance is a course # Guarantee first entrance is a course
swapdict = randomized_entrances.copy() swapdict = randomized_entrances.copy()
@ -67,7 +68,7 @@ def set_rules(world, player: int, area_connections: dict, star_costs: dict, move
area_connections.update({int(entrance_lvl): int(sm64_entrances_to_level[destination]) for (entrance_lvl,destination) in randomized_entrances.items()}) 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()} 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) rf = RuleFactory(world, options, player, move_rando_bitvec)
connect_regions(world, player, "Menu", randomized_entrances_s["Bob-omb Battlefield"]) 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["Whomp's Fortress"], lambda state: state.has("Power Star", player, 1))
@ -199,7 +200,7 @@ def set_rules(world, player: int, area_connections: dict, star_costs: dict, move
# Bowser in the Sky # Bowser in the Sky
rf.assign_rule("BitS: Top", "CL+TJ | CL+SF+LG | MOVELESS & TJ+WK+LG") rf.assign_rule("BitS: Top", "CL+TJ | CL+SF+LG | MOVELESS & TJ+WK+LG")
# 100 Coin Stars # 100 Coin Stars
if world.EnableCoinStars[player]: if options.enable_coin_stars:
rf.assign_rule("BoB: 100 Coins", "CANN & WC | CANNLESS & WC & TJ") rf.assign_rule("BoB: 100 Coins", "CANN & WC | CANNLESS & WC & TJ")
rf.assign_rule("WF: 100 Coins", "GP | MOVELESS") rf.assign_rule("WF: 100 Coins", "GP | MOVELESS")
rf.assign_rule("JRB: 100 Coins", "GP & {JRB: Upper}") rf.assign_rule("JRB: 100 Coins", "GP & {JRB: Upper}")
@ -225,9 +226,9 @@ def set_rules(world, player: int, area_connections: dict, star_costs: dict, move
world.completion_condition[player] = lambda state: state.can_reach("BitS: Top", 'Region', player) world.completion_condition[player] = lambda state: state.can_reach("BitS: Top", 'Region', player)
if world.CompletionType[player] == "last_bowser_stage": if options.completion_type == "last_bowser_stage":
world.completion_condition[player] = lambda state: state.can_reach("BitS: Top", 'Region', player) world.completion_condition[player] = lambda state: state.can_reach("BitS: Top", 'Region', player)
elif world.CompletionType[player] == "all_bowser_stages": elif options.completion_type == "all_bowser_stages":
world.completion_condition[player] = lambda state: state.can_reach("Bowser in the Dark World", 'Region', player) and \ world.completion_condition[player] = lambda state: state.can_reach("Bowser in the Dark World", 'Region', player) and \
state.can_reach("BitFS: Upper", 'Region', player) and \ state.can_reach("BitFS: Upper", 'Region', player) and \
state.can_reach("BitS: Top", 'Region', player) state.can_reach("BitS: Top", 'Region', player)
@ -262,14 +263,14 @@ class RuleFactory:
class SM64LogicException(Exception): class SM64LogicException(Exception):
pass pass
def __init__(self, world, player, move_rando_bitvec): def __init__(self, world, options: SM64Options, player: int, move_rando_bitvec: int):
self.world = world self.world = world
self.player = player self.player = player
self.move_rando_bitvec = move_rando_bitvec self.move_rando_bitvec = move_rando_bitvec
self.area_randomizer = world.AreaRandomizer[player].value > 0 self.area_randomizer = options.area_rando > 0
self.capless = not world.StrictCapRequirements[player] self.capless = not options.strict_cap_requirements
self.cannonless = not world.StrictCannonRequirements[player] self.cannonless = not options.strict_cannon_requirements
self.moveless = not world.StrictMoveRequirements[player] or not move_rando_bitvec > 0 self.moveless = not options.strict_move_requirements or not move_rando_bitvec > 0
def assign_rule(self, target_name: str, rule_expr: str): 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) target = self.world.get_location(target_name, self.player) if target_name in location_table else self.world.get_entrance(target_name, self.player)

View File

@ -3,7 +3,7 @@ import os
import json import json
from .Items import item_table, action_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 .Locations import location_table, SM64Location
from .Options import sm64_options from .Options import SM64Options
from .Rules import set_rules from .Rules import set_rules
from .Regions import create_regions, sm64_level_to_entrances, SM64Levels from .Regions import create_regions, sm64_level_to_entrances, SM64Levels
from BaseClasses import Item, Tutorial, ItemClassification, Region from BaseClasses import Item, Tutorial, ItemClassification, Region
@ -40,7 +40,7 @@ class SM64World(World):
area_connections: typing.Dict[int, int] area_connections: typing.Dict[int, int]
option_definitions = sm64_options options_dataclass = SM64Options
number_of_stars: int number_of_stars: int
move_rando_bitvec: int move_rando_bitvec: int
@ -49,38 +49,36 @@ class SM64World(World):
def generate_early(self): def generate_early(self):
max_stars = 120 max_stars = 120
if (not self.multiworld.EnableCoinStars[self.player].value): if (not self.options.enable_coin_stars):
max_stars -= 15 max_stars -= 15
self.move_rando_bitvec = 0 self.move_rando_bitvec = 0
for action, itemid in action_item_table.items(): if self.options.enable_move_rando:
# HACK: Disable randomization of double jump for action in self.options.move_rando_actions.value:
if action == 'Double Jump': continue
if getattr(self.multiworld, f"MoveRandomizer{action.replace(' ','')}")[self.player].value:
max_stars -= 1 max_stars -= 1
self.move_rando_bitvec |= (1 << (itemid - action_item_table['Double Jump'])) self.move_rando_bitvec |= (1 << (action_item_table[action] - action_item_table['Double Jump']))
if (self.multiworld.ExclamationBoxes[self.player].value > 0): if (self.options.exclamation_boxes > 0):
max_stars += 29 max_stars += 29
self.number_of_stars = min(self.multiworld.AmountOfStars[self.player].value, max_stars) self.number_of_stars = min(self.options.amount_of_stars, max_stars)
self.filler_count = max_stars - self.number_of_stars self.filler_count = max_stars - self.number_of_stars
self.star_costs = { self.star_costs = {
'FirstBowserDoorCost': round(self.multiworld.FirstBowserStarDoorCost[self.player].value * self.number_of_stars / 100), 'FirstBowserDoorCost': round(self.options.first_bowser_star_door_cost * self.number_of_stars / 100),
'BasementDoorCost': round(self.multiworld.BasementStarDoorCost[self.player].value * self.number_of_stars / 100), 'BasementDoorCost': round(self.options.basement_star_door_cost * self.number_of_stars / 100),
'SecondFloorDoorCost': round(self.multiworld.SecondFloorStarDoorCost[self.player].value * self.number_of_stars / 100), 'SecondFloorDoorCost': round(self.options.second_floor_star_door_cost * self.number_of_stars / 100),
'MIPS1Cost': round(self.multiworld.MIPS1Cost[self.player].value * self.number_of_stars / 100), 'MIPS1Cost': round(self.options.mips1_cost * self.number_of_stars / 100),
'MIPS2Cost': round(self.multiworld.MIPS2Cost[self.player].value * self.number_of_stars / 100), 'MIPS2Cost': round(self.options.mips2_cost * self.number_of_stars / 100),
'StarsToFinish': round(self.multiworld.StarsToFinish[self.player].value * self.number_of_stars / 100) 'StarsToFinish': round(self.options.stars_to_finish * self.number_of_stars / 100)
} }
# Nudge MIPS 1 to match vanilla on default percentage # Nudge MIPS 1 to match vanilla on default percentage
if self.number_of_stars == 120 and self.multiworld.MIPS1Cost[self.player].value == 12: if self.number_of_stars == 120 and self.options.mips1_cost == 12:
self.star_costs['MIPS1Cost'] = 15 self.star_costs['MIPS1Cost'] = 15
self.topology_present = self.multiworld.AreaRandomizer[self.player].value self.topology_present = self.options.area_rando
def create_regions(self): def create_regions(self):
create_regions(self.multiworld, self.player) create_regions(self.multiworld, self.options, self.player)
def set_rules(self): def set_rules(self):
self.area_connections = {} self.area_connections = {}
set_rules(self.multiworld, self.player, self.area_connections, self.star_costs, self.move_rando_bitvec) set_rules(self.multiworld, self.options, self.player, self.area_connections, self.star_costs, self.move_rando_bitvec)
if self.topology_present: if self.topology_present:
# Write area_connections to spoiler log # Write area_connections to spoiler log
for entrance, destination in self.area_connections.items(): for entrance, destination in self.area_connections.items():
@ -107,7 +105,7 @@ class SM64World(World):
# Power Stars # Power Stars
self.multiworld.itempool += [self.create_item("Power Star") for i in range(0,self.number_of_stars)] self.multiworld.itempool += [self.create_item("Power Star") for i in range(0,self.number_of_stars)]
# Keys # Keys
if (not self.multiworld.ProgressiveKeys[self.player].value): if (not self.options.progressive_keys):
key1 = self.create_item("Basement Key") key1 = self.create_item("Basement Key")
key2 = self.create_item("Second Floor Key") key2 = self.create_item("Second Floor Key")
self.multiworld.itempool += [key1, key2] self.multiworld.itempool += [key1, key2]
@ -116,7 +114,7 @@ class SM64World(World):
# Caps # Caps
self.multiworld.itempool += [self.create_item(cap_name) for cap_name in ["Wing Cap", "Metal Cap", "Vanish Cap"]] self.multiworld.itempool += [self.create_item(cap_name) for cap_name in ["Wing Cap", "Metal Cap", "Vanish Cap"]]
# Cannons # Cannons
if (self.multiworld.BuddyChecks[self.player].value): if (self.options.buddy_checks):
self.multiworld.itempool += [self.create_item(name) for name, id in cannon_item_table.items()] self.multiworld.itempool += [self.create_item(name) for name, id in cannon_item_table.items()]
# Moves # Moves
self.multiworld.itempool += [self.create_item(action) self.multiworld.itempool += [self.create_item(action)
@ -124,7 +122,7 @@ class SM64World(World):
if self.move_rando_bitvec & (1 << itemid - action_item_table['Double Jump'])] if self.move_rando_bitvec & (1 << itemid - action_item_table['Double Jump'])]
def generate_basic(self): def generate_basic(self):
if not (self.multiworld.BuddyChecks[self.player].value): if not (self.options.buddy_checks):
self.multiworld.get_location("BoB: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock BoB")) 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("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")) self.multiworld.get_location("JRB: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock JRB"))
@ -136,7 +134,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("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")) 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): if (self.options.exclamation_boxes == 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 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 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")) self.multiworld.get_location("CCM: 1Up Block Secret Slide", self.player).place_locked_item(self.create_item("1Up Mushroom"))
@ -174,8 +172,8 @@ class SM64World(World):
return { return {
"AreaRando": self.area_connections, "AreaRando": self.area_connections,
"MoveRandoVec": self.move_rando_bitvec, "MoveRandoVec": self.move_rando_bitvec,
"DeathLink": self.multiworld.death_link[self.player].value, "DeathLink": self.options.death_link.value,
"CompletionType": self.multiworld.CompletionType[self.player].value, "CompletionType": self.options.completion_type.value,
**self.star_costs **self.star_costs
} }