244 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			244 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								from .Types import HatInTimeLocation, HatInTimeItem
							 | 
						||
| 
								 | 
							
								from .Regions import create_region
							 | 
						||
| 
								 | 
							
								from BaseClasses import Region, LocationProgressType, ItemClassification
							 | 
						||
| 
								 | 
							
								from worlds.generic.Rules import add_rule
							 | 
						||
| 
								 | 
							
								from typing import List, TYPE_CHECKING
							 | 
						||
| 
								 | 
							
								from .Locations import death_wishes
							 | 
						||
| 
								 | 
							
								from .Options import EndGoal
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if TYPE_CHECKING:
							 | 
						||
| 
								 | 
							
								    from . import HatInTimeWorld
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dw_prereqs = {
							 | 
						||
| 
								 | 
							
								    "So You're Back From Outer Space":  ["Beat the Heat"],
							 | 
						||
| 
								 | 
							
								    "Snatcher's Hit List":              ["Beat the Heat"],
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Mafia Town":     ["So You're Back From Outer Space"],
							 | 
						||
| 
								 | 
							
								    "Rift Collapse: Mafia of Cooks":    ["So You're Back From Outer Space"],
							 | 
						||
| 
								 | 
							
								    "Collect-a-thon":                   ["So You're Back From Outer Space"],
							 | 
						||
| 
								 | 
							
								    "She Speedran from Outer Space":    ["Rift Collapse: Mafia of Cooks"],
							 | 
						||
| 
								 | 
							
								    "Mafia's Jumps":                    ["She Speedran from Outer Space"],
							 | 
						||
| 
								 | 
							
								    "Vault Codes in the Wind":          ["Collect-a-thon", "She Speedran from Outer Space"],
							 | 
						||
| 
								 | 
							
								    "Encore! Encore!":                  ["Collect-a-thon"],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    "Security Breach":                          ["Beat the Heat"],
							 | 
						||
| 
								 | 
							
								    "Rift Collapse: Dead Bird Studio":          ["Security Breach"],
							 | 
						||
| 
								 | 
							
								    "The Great Big Hootenanny":                 ["Security Breach"],
							 | 
						||
| 
								 | 
							
								    "10 Seconds until Self-Destruct":           ["The Great Big Hootenanny"],
							 | 
						||
| 
								 | 
							
								    "Killing Two Birds":                        ["Rift Collapse: Dead Bird Studio", "10 Seconds until Self-Destruct"],
							 | 
						||
| 
								 | 
							
								    "Community Rift: Rhythm Jump Studio":       ["10 Seconds until Self-Destruct"],
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Battle of the Birds":    ["The Great Big Hootenanny"],
							 | 
						||
| 
								 | 
							
								    "Zero Jumps":                               ["Rift Collapse: Dead Bird Studio"],
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Nyakuza Metro":          ["Killing Two Birds"],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    "Speedrun Well":                    ["Beat the Heat"],
							 | 
						||
| 
								 | 
							
								    "Rift Collapse: Sleepy Subcon":     ["Speedrun Well"],
							 | 
						||
| 
								 | 
							
								    "Boss Rush":                        ["Speedrun Well"],
							 | 
						||
| 
								 | 
							
								    "Quality Time with Snatcher":       ["Rift Collapse: Sleepy Subcon"],
							 | 
						||
| 
								 | 
							
								    "Breaching the Contract":           ["Boss Rush", "Quality Time with Snatcher"],
							 | 
						||
| 
								 | 
							
								    "Community Rift: Twilight Travels": ["Quality Time with Snatcher"],
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Subcon Forest":  ["Rift Collapse: Sleepy Subcon"],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    "Bird Sanctuary":                       ["Beat the Heat"],
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Alpine Skyline":     ["Bird Sanctuary"],
							 | 
						||
| 
								 | 
							
								    "Wound-Up Windmill":                    ["Bird Sanctuary"],
							 | 
						||
| 
								 | 
							
								    "Rift Collapse: Alpine Skyline":        ["Bird Sanctuary"],
							 | 
						||
| 
								 | 
							
								    "Camera Tourist":                       ["Rift Collapse: Alpine Skyline"],
							 | 
						||
| 
								 | 
							
								    "Community Rift: The Mountain Rift":    ["Rift Collapse: Alpine Skyline"],
							 | 
						||
| 
								 | 
							
								    "The Illness has Speedrun":             ["Rift Collapse: Alpine Skyline", "Wound-Up Windmill"],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    "The Mustache Gauntlet":            ["Wound-Up Windmill"],
							 | 
						||
| 
								 | 
							
								    "No More Bad Guys":                 ["The Mustache Gauntlet"],
							 | 
						||
| 
								 | 
							
								    "Seal the Deal":                    ["Encore! Encore!", "Killing Two Birds",
							 | 
						||
| 
								 | 
							
								                                         "Breaching the Contract", "No More Bad Guys"],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    "Rift Collapse: Deep Sea":          ["Rift Collapse: Mafia of Cooks", "Rift Collapse: Dead Bird Studio",
							 | 
						||
| 
								 | 
							
								                                         "Rift Collapse: Sleepy Subcon", "Rift Collapse: Alpine Skyline"],
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    "Cruisin' for a Bruisin'":          ["Rift Collapse: Deep Sea"],
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dw_candles = [
							 | 
						||
| 
								 | 
							
								    "Snatcher's Hit List",
							 | 
						||
| 
								 | 
							
								    "Zero Jumps",
							 | 
						||
| 
								 | 
							
								    "Camera Tourist",
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Mafia Town",
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Battle of the Birds",
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Subcon Forest",
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Alpine Skyline",
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Nyakuza Metro",
							 | 
						||
| 
								 | 
							
								]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								annoying_dws = [
							 | 
						||
| 
								 | 
							
								    "Vault Codes in the Wind",
							 | 
						||
| 
								 | 
							
								    "Boss Rush",
							 | 
						||
| 
								 | 
							
								    "Camera Tourist",
							 | 
						||
| 
								 | 
							
								    "The Mustache Gauntlet",
							 | 
						||
| 
								 | 
							
								    "Rift Collapse: Deep Sea",
							 | 
						||
| 
								 | 
							
								    "Cruisin' for a Bruisin'",
							 | 
						||
| 
								 | 
							
								    "Seal the Deal",  # Non-excluded if goal
							 | 
						||
| 
								 | 
							
								]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# includes the above as well
							 | 
						||
| 
								 | 
							
								annoying_bonuses = [
							 | 
						||
| 
								 | 
							
								    "So You're Back From Outer Space",
							 | 
						||
| 
								 | 
							
								    "Encore! Encore!",
							 | 
						||
| 
								 | 
							
								    "Snatcher's Hit List",
							 | 
						||
| 
								 | 
							
								    "Vault Codes in the Wind",
							 | 
						||
| 
								 | 
							
								    "10 Seconds until Self-Destruct",
							 | 
						||
| 
								 | 
							
								    "Killing Two Birds",
							 | 
						||
| 
								 | 
							
								    "Zero Jumps",
							 | 
						||
| 
								 | 
							
								    "Boss Rush",
							 | 
						||
| 
								 | 
							
								    "Bird Sanctuary",
							 | 
						||
| 
								 | 
							
								    "The Mustache Gauntlet",
							 | 
						||
| 
								 | 
							
								    "Wound-Up Windmill",
							 | 
						||
| 
								 | 
							
								    "Camera Tourist",
							 | 
						||
| 
								 | 
							
								    "Rift Collapse: Deep Sea",
							 | 
						||
| 
								 | 
							
								    "Cruisin' for a Bruisin'",
							 | 
						||
| 
								 | 
							
								    "Seal the Deal",
							 | 
						||
| 
								 | 
							
								]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dw_classes = {
							 | 
						||
| 
								 | 
							
								    "Beat the Heat":                    "Hat_SnatcherContract_DeathWish_HeatingUpHarder",
							 | 
						||
| 
								 | 
							
								    "So You're Back From Outer Space":  "Hat_SnatcherContract_DeathWish_BackFromSpace",
							 | 
						||
| 
								 | 
							
								    "Snatcher's Hit List":              "Hat_SnatcherContract_DeathWish_KillEverybody",
							 | 
						||
| 
								 | 
							
								    "Collect-a-thon":                   "Hat_SnatcherContract_DeathWish_PonFrenzy",
							 | 
						||
| 
								 | 
							
								    "Rift Collapse: Mafia of Cooks":    "Hat_SnatcherContract_DeathWish_RiftCollapse_MafiaTown",
							 | 
						||
| 
								 | 
							
								    "Encore! Encore!":                  "Hat_SnatcherContract_DeathWish_MafiaBossEX",
							 | 
						||
| 
								 | 
							
								    "She Speedran from Outer Space":    "Hat_SnatcherContract_DeathWish_Speedrun_MafiaAlien",
							 | 
						||
| 
								 | 
							
								    "Mafia's Jumps":                    "Hat_SnatcherContract_DeathWish_NoAPresses_MafiaAlien",
							 | 
						||
| 
								 | 
							
								    "Vault Codes in the Wind":          "Hat_SnatcherContract_DeathWish_MovingVault",
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Mafia Town":     "Hat_SnatcherContract_DeathWish_Tokens_MafiaTown",
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    "Security Breach":                          "Hat_SnatcherContract_DeathWish_DeadBirdStudioMoreGuards",
							 | 
						||
| 
								 | 
							
								    "The Great Big Hootenanny":                 "Hat_SnatcherContract_DeathWish_DifficultParade",
							 | 
						||
| 
								 | 
							
								    "Rift Collapse: Dead Bird Studio":          "Hat_SnatcherContract_DeathWish_RiftCollapse_Birds",
							 | 
						||
| 
								 | 
							
								    "10 Seconds until Self-Destruct":           "Hat_SnatcherContract_DeathWish_TrainRushShortTime",
							 | 
						||
| 
								 | 
							
								    "Killing Two Birds":                        "Hat_SnatcherContract_DeathWish_BirdBossEX",
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Battle of the Birds":    "Hat_SnatcherContract_DeathWish_Tokens_Birds",
							 | 
						||
| 
								 | 
							
								    "Zero Jumps":                               "Hat_SnatcherContract_DeathWish_NoAPresses",
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    "Speedrun Well":                    "Hat_SnatcherContract_DeathWish_Speedrun_SubWell",
							 | 
						||
| 
								 | 
							
								    "Rift Collapse: Sleepy Subcon":     "Hat_SnatcherContract_DeathWish_RiftCollapse_Subcon",
							 | 
						||
| 
								 | 
							
								    "Boss Rush":                        "Hat_SnatcherContract_DeathWish_BossRush",
							 | 
						||
| 
								 | 
							
								    "Quality Time with Snatcher":       "Hat_SnatcherContract_DeathWish_SurvivalOfTheFittest",
							 | 
						||
| 
								 | 
							
								    "Breaching the Contract":           "Hat_SnatcherContract_DeathWish_SnatcherEX",
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Subcon Forest":  "Hat_SnatcherContract_DeathWish_Tokens_Subcon",
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    "Bird Sanctuary":                   "Hat_SnatcherContract_DeathWish_NiceBirdhouse",
							 | 
						||
| 
								 | 
							
								    "Rift Collapse: Alpine Skyline":    "Hat_SnatcherContract_DeathWish_RiftCollapse_Alps",
							 | 
						||
| 
								 | 
							
								    "Wound-Up Windmill":                "Hat_SnatcherContract_DeathWish_FastWindmill",
							 | 
						||
| 
								 | 
							
								    "The Illness has Speedrun":         "Hat_SnatcherContract_DeathWish_Speedrun_Illness",
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Alpine Skyline": "Hat_SnatcherContract_DeathWish_Tokens_Alps",
							 | 
						||
| 
								 | 
							
								    "Camera Tourist":                   "Hat_SnatcherContract_DeathWish_CameraTourist_1",
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    "The Mustache Gauntlet":            "Hat_SnatcherContract_DeathWish_HardCastle",
							 | 
						||
| 
								 | 
							
								    "No More Bad Guys":                 "Hat_SnatcherContract_DeathWish_MuGirlEX",
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    "Seal the Deal":                    "Hat_SnatcherContract_DeathWish_BossRushEX",
							 | 
						||
| 
								 | 
							
								    "Rift Collapse: Deep Sea":          "Hat_SnatcherContract_DeathWish_RiftCollapse_Cruise",
							 | 
						||
| 
								 | 
							
								    "Cruisin' for a Bruisin'":          "Hat_SnatcherContract_DeathWish_EndlessTasks",
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    "Community Rift: Rhythm Jump Studio":   "Hat_SnatcherContract_DeathWish_CommunityRift_RhythmJump",
							 | 
						||
| 
								 | 
							
								    "Community Rift: Twilight Travels":     "Hat_SnatcherContract_DeathWish_CommunityRift_TwilightTravels",
							 | 
						||
| 
								 | 
							
								    "Community Rift: The Mountain Rift":    "Hat_SnatcherContract_DeathWish_CommunityRift_MountainRift",
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    "Snatcher Coins in Nyakuza Metro":      "Hat_SnatcherContract_DeathWish_Tokens_Metro",
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def create_dw_regions(world: "HatInTimeWorld"):
							 | 
						||
| 
								 | 
							
								    if world.options.DWExcludeAnnoyingContracts:
							 | 
						||
| 
								 | 
							
								        for name in annoying_dws:
							 | 
						||
| 
								 | 
							
								            world.excluded_dws.append(name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if not world.options.DWEnableBonus or world.options.DWAutoCompleteBonuses:
							 | 
						||
| 
								 | 
							
								        for name in death_wishes:
							 | 
						||
| 
								 | 
							
								            world.excluded_bonuses.append(name)
							 | 
						||
| 
								 | 
							
								    elif world.options.DWExcludeAnnoyingBonuses:
							 | 
						||
| 
								 | 
							
								        for name in annoying_bonuses:
							 | 
						||
| 
								 | 
							
								            world.excluded_bonuses.append(name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if world.options.DWExcludeCandles:
							 | 
						||
| 
								 | 
							
								        for name in dw_candles:
							 | 
						||
| 
								 | 
							
								            if name not in world.excluded_dws:
							 | 
						||
| 
								 | 
							
								                world.excluded_dws.append(name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    spaceship = world.multiworld.get_region("Spaceship", world.player)
							 | 
						||
| 
								 | 
							
								    dw_map: Region = create_region(world, "Death Wish Map")
							 | 
						||
| 
								 | 
							
								    entrance = spaceship.connect(dw_map, "-> Death Wish Map")
							 | 
						||
| 
								 | 
							
								    add_rule(entrance, lambda state: state.has("Time Piece", world.player, world.options.DWTimePieceRequirement))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if world.options.DWShuffle:
							 | 
						||
| 
								 | 
							
								        # Connect Death Wishes randomly to one another in a linear sequence
							 | 
						||
| 
								 | 
							
								        dw_list: List[str] = []
							 | 
						||
| 
								 | 
							
								        for name in death_wishes.keys():
							 | 
						||
| 
								 | 
							
								            # Don't shuffle excluded or invalid Death Wishes
							 | 
						||
| 
								 | 
							
								            if not world.is_dlc2() and name == "Snatcher Coins in Nyakuza Metro" or world.is_dw_excluded(name):
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            dw_list.append(name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        world.random.shuffle(dw_list)
							 | 
						||
| 
								 | 
							
								        count = world.random.randint(world.options.DWShuffleCountMin.value, world.options.DWShuffleCountMax.value)
							 | 
						||
| 
								 | 
							
								        dw_shuffle: List[str] = []
							 | 
						||
| 
								 | 
							
								        total = min(len(dw_list), count)
							 | 
						||
| 
								 | 
							
								        for i in range(total):
							 | 
						||
| 
								 | 
							
								            dw_shuffle.append(dw_list[i])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Seal the Deal is always last if it's the goal
							 | 
						||
| 
								 | 
							
								        if world.options.EndGoal == EndGoal.option_seal_the_deal:
							 | 
						||
| 
								 | 
							
								            if "Seal the Deal" in dw_shuffle:
							 | 
						||
| 
								 | 
							
								                dw_shuffle.remove("Seal the Deal")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            dw_shuffle.append("Seal the Deal")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        world.dw_shuffle = dw_shuffle
							 | 
						||
| 
								 | 
							
								        prev_dw = dw_map
							 | 
						||
| 
								 | 
							
								        for death_wish_name in dw_shuffle:
							 | 
						||
| 
								 | 
							
								            dw = create_region(world, death_wish_name)
							 | 
						||
| 
								 | 
							
								            prev_dw.connect(dw)
							 | 
						||
| 
								 | 
							
								            create_dw_locations(world, dw)
							 | 
						||
| 
								 | 
							
								            prev_dw = dw
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        # DWShuffle is disabled, use vanilla connections
							 | 
						||
| 
								 | 
							
								        for key in death_wishes.keys():
							 | 
						||
| 
								 | 
							
								            if key == "Snatcher Coins in Nyakuza Metro" and not world.is_dlc2():
							 | 
						||
| 
								 | 
							
								                world.excluded_dws.append(key)
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            dw = create_region(world, key)
							 | 
						||
| 
								 | 
							
								            if key == "Beat the Heat":
							 | 
						||
| 
								 | 
							
								                dw_map.connect(dw, f"{dw_map.name} -> Beat the Heat")
							 | 
						||
| 
								 | 
							
								            elif key in dw_prereqs.keys():
							 | 
						||
| 
								 | 
							
								                for name in dw_prereqs[key]:
							 | 
						||
| 
								 | 
							
								                    parent = world.multiworld.get_region(name, world.player)
							 | 
						||
| 
								 | 
							
								                    parent.connect(dw, f"{parent.name} -> {key}")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            create_dw_locations(world, dw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def create_dw_locations(world: "HatInTimeWorld", dw: Region):
							 | 
						||
| 
								 | 
							
								    loc_id = death_wishes[dw.name]
							 | 
						||
| 
								 | 
							
								    main_objective = HatInTimeLocation(world.player, f"{dw.name} - Main Objective", loc_id, dw)
							 | 
						||
| 
								 | 
							
								    full_clear = HatInTimeLocation(world.player, f"{dw.name} - All Clear", loc_id + 1, dw)
							 | 
						||
| 
								 | 
							
								    main_stamp = HatInTimeLocation(world.player, f"Main Stamp - {dw.name}", None, dw)
							 | 
						||
| 
								 | 
							
								    bonus_stamps = HatInTimeLocation(world.player, f"Bonus Stamps - {dw.name}", None, dw)
							 | 
						||
| 
								 | 
							
								    main_stamp.show_in_spoiler = False
							 | 
						||
| 
								 | 
							
								    bonus_stamps.show_in_spoiler = False
							 | 
						||
| 
								 | 
							
								    dw.locations.append(main_stamp)
							 | 
						||
| 
								 | 
							
								    dw.locations.append(bonus_stamps)
							 | 
						||
| 
								 | 
							
								    main_stamp.place_locked_item(HatInTimeItem(f"1 Stamp - {dw.name}",
							 | 
						||
| 
								 | 
							
								                                               ItemClassification.progression, None, world.player))
							 | 
						||
| 
								 | 
							
								    bonus_stamps.place_locked_item(HatInTimeItem(f"2 Stamp - {dw.name}",
							 | 
						||
| 
								 | 
							
								                                                 ItemClassification.progression, None, world.player))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if dw.name in world.excluded_dws:
							 | 
						||
| 
								 | 
							
								        main_objective.progress_type = LocationProgressType.EXCLUDED
							 | 
						||
| 
								 | 
							
								        full_clear.progress_type = LocationProgressType.EXCLUDED
							 | 
						||
| 
								 | 
							
								    elif world.is_bonus_excluded(dw.name):
							 | 
						||
| 
								 | 
							
								        full_clear.progress_type = LocationProgressType.EXCLUDED
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    dw.locations.append(main_objective)
							 | 
						||
| 
								 | 
							
								    dw.locations.append(full_clear)
							 |