* duh * Fuck it * Major fixes * a * b * Even more fixes * New option - NoFreeRoamFinale * a * Hat Logic Fix * Just to be safe * multiworld.random to world.random * KeyError fix * Update .gitignore * Update __init__.py * Zoinks Scoob * ffs * Ruh Roh Raggy, more r-r-r-random bugs! * 0.9b - cleanup + expanded logic difficulty * Update Rules.py * Update Regions.py * AttributeError fix * 0.10b - New Options * 1.0 Preparations * Docs * Docs 2 * Fixes * Update __init__.py * Fixes * variable capture my beloathed * Fixes * a * 10 Seconds logic fix * 1.1 * 1.2 * a * New client * More client changes * 1.3 * Final touch-ups for 1.3 * 1.3.1 * 1.3.3 * Zero Jumps gen error fix * more fixes * Formatting improvements * typo * Update __init__.py * Revert "Update __init__.py" This reverts commit e178a7c0a6904ace803241cab3021d7b97177e90. * init * Update to new options API * Missed some * Snatcher Coins fix * Missed some more * some slight touch ups * rewind * a * fix things * Revert "Merge branch 'main' of https://github.com/CookieCat45/Archipelago-ahit" This reverts commit a2360fe197e77a723bb70006c5eb5725c7ed3826, reversing changes made to b8948bc4958855c6e342e18bdb8dc81cfcf09455. * Update .gitignore * 1.3.6 * Final touch-ups * Fix client and leftover old options api * Delete setup-ahitclient.py * Update .gitignore * old python version fix * proper warnings for invalid act plandos * Update worlds/ahit/docs/en_A Hat in Time.md Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> * Update worlds/ahit/docs/setup_en.md Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> * 120 char per line * "settings" to "options" * Update DeathWishRules.py * Update worlds/ahit/docs/en_A Hat in Time.md Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> * No more loading the data package * cleanup + act plando fixes * almost forgot * Update Rules.py * a * Update worlds/ahit/Options.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Options stuff * oop * no unnecessary type hints * warn about depot download length in setup guide * Update worlds/ahit/Options.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * typo Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Update worlds/ahit/Rules.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * review stuff * More stuff from review * comment * 1.5 Update * link fix? * link fix 2 * Update setup_en.md * Update setup_en.md * Update setup_en.md * Evil * Good fucking lord * Review stuff again + Logic fixes * More review stuff * Even more review stuff - we're almost done * DW review stuff * Finish up review stuff * remove leftover stuff * a * assert item * add A Hat in Time to readme/codeowners files * Fix range options not being corrected properly * 120 chars per line in docs * Update worlds/ahit/Regions.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Update worlds/ahit/DeathWishLocations.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Remove some unnecessary option.class.value * Remove data_version and more option.class.value * Update worlds/ahit/Items.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Remove the rest of option.class.value * Update worlds/ahit/DeathWishLocations.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * review stuff * Replace connect_regions with Region.connect * review stuff * Remove unnecessary Optional from LocData * Remove HatType.NONE * Update worlds/ahit/test/TestActs.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * fix so default tests actually don't run * Improve performance for death wish rules * rename test file * change test imports * 1000 is probably unnecessary * a * change state.count to state.has * stuff * starting inventory hats fix * shouldn't have done this lol * make ship shape task goal equal to number of tasksanity checks if set to 0 * a * change act shuffle starting acts + logic updates * dumb * option groups + lambda capture cringe + typo * a * b * missing option in groups * c * Fix Your Contract Has Expired being placed on first level when it shouldn't * yche fix * formatting * major logic bug fix for death wish * Update Regions.py * Add missing indirect connections * Fix generation error from chapter 2 start with act shuffle off * a * Revert "a" This reverts commit df58bbcd998585760cc6ac9ea54b6fdf142b4fd1. * Revert "Fix generation error from chapter 2 start with act shuffle off" This reverts commit 0f4d441824af34bf7a7cff19f5f14161752d8661. * bunch of fixes * Update Regions.py * Update __init__.py * Update __init__.py * Update __init__.py * Update Regions.py * Update worlds/ahit/__init__.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Update __init__.py * Update __init__.py --------- Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> Co-authored-by: Ixrec <ericrhitchcock@gmail.com> Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com>
		
			
				
	
	
		
			303 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			303 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from BaseClasses import Item, ItemClassification
 | |
| from .Types import HatDLC, HatType, hat_type_to_item, Difficulty, ItemData, HatInTimeItem
 | |
| from .Locations import get_total_locations
 | |
| from .Rules import get_difficulty
 | |
| from .Options import get_total_time_pieces, CTRLogic
 | |
| from typing import List, Dict, TYPE_CHECKING
 | |
| 
 | |
| if TYPE_CHECKING:
 | |
|     from . import HatInTimeWorld
 | |
| 
 | |
| 
 | |
| def create_itempool(world: "HatInTimeWorld") -> List[Item]:
 | |
|     itempool: List[Item] = []
 | |
|     if world.has_yarn():
 | |
|         yarn_pool: List[Item] = create_multiple_items(world, "Yarn",
 | |
|                                                       world.options.YarnAvailable.value,
 | |
|                                                       ItemClassification.progression_skip_balancing)
 | |
| 
 | |
|         for i in range(int(len(yarn_pool) * (0.01 * world.options.YarnBalancePercent))):
 | |
|             yarn_pool[i].classification = ItemClassification.progression
 | |
| 
 | |
|         itempool += yarn_pool
 | |
| 
 | |
|     for name in item_table.keys():
 | |
|         if name == "Yarn":
 | |
|             continue
 | |
| 
 | |
|         if not item_dlc_enabled(world, name):
 | |
|             continue
 | |
| 
 | |
|         if not world.options.HatItems and name in hat_type_to_item.values():
 | |
|             continue
 | |
| 
 | |
|         item_type: ItemClassification = item_table.get(name).classification
 | |
| 
 | |
|         if world.is_dw_only():
 | |
|             if item_type is ItemClassification.progression \
 | |
|                or item_type is ItemClassification.progression_skip_balancing:
 | |
|                 continue
 | |
|         else:
 | |
|             if name == "Scooter Badge":
 | |
|                 if world.options.CTRLogic == CTRLogic.option_scooter or get_difficulty(world) >= Difficulty.MODERATE:
 | |
|                     item_type = ItemClassification.progression
 | |
|             elif name == "No Bonk Badge" and world.is_dw():
 | |
|                 item_type = ItemClassification.progression
 | |
| 
 | |
|         # some death wish bonuses require one hit hero + hookshot
 | |
|         if world.is_dw() and name == "Badge Pin" and not world.is_dw_only():
 | |
|             item_type = ItemClassification.progression
 | |
| 
 | |
|         if item_type is ItemClassification.filler or item_type is ItemClassification.trap:
 | |
|             continue
 | |
| 
 | |
|         if name in act_contracts.keys() and not world.options.ShuffleActContracts:
 | |
|             continue
 | |
| 
 | |
|         if name in alps_hooks.keys() and not world.options.ShuffleAlpineZiplines:
 | |
|             continue
 | |
| 
 | |
|         if name == "Progressive Painting Unlock" and not world.options.ShuffleSubconPaintings:
 | |
|             continue
 | |
| 
 | |
|         if world.options.StartWithCompassBadge and name == "Compass Badge":
 | |
|             continue
 | |
| 
 | |
|         if name == "Time Piece":
 | |
|             tp_list: List[Item] = create_multiple_items(world, name, get_total_time_pieces(world), item_type)
 | |
|             for i in range(int(len(tp_list) * (0.01 * world.options.TimePieceBalancePercent))):
 | |
|                 tp_list[i].classification = ItemClassification.progression
 | |
| 
 | |
|             itempool += tp_list
 | |
|             continue
 | |
| 
 | |
|         itempool += create_multiple_items(world, name, item_frequencies.get(name, 1), item_type)
 | |
| 
 | |
|     itempool += create_junk_items(world, get_total_locations(world) - len(itempool))
 | |
|     return itempool
 | |
| 
 | |
| 
 | |
| def calculate_yarn_costs(world: "HatInTimeWorld"):
 | |
|     min_yarn_cost = int(min(world.options.YarnCostMin.value, world.options.YarnCostMax.value))
 | |
|     max_yarn_cost = int(max(world.options.YarnCostMin.value, world.options.YarnCostMax.value))
 | |
| 
 | |
|     max_cost = 0
 | |
|     for i in range(5):
 | |
|         hat: HatType = HatType(i)
 | |
|         if not world.is_hat_precollected(hat):
 | |
|             cost: int = world.random.randint(min_yarn_cost, max_yarn_cost)
 | |
|             world.hat_yarn_costs[hat] = cost
 | |
|             max_cost += cost
 | |
|         else:
 | |
|             world.hat_yarn_costs[hat] = 0
 | |
| 
 | |
|     available_yarn: int = world.options.YarnAvailable.value
 | |
|     if max_cost > available_yarn:
 | |
|         world.options.YarnAvailable.value = max_cost
 | |
|         available_yarn = max_cost
 | |
| 
 | |
|     extra_yarn = max_cost + world.options.MinExtraYarn - available_yarn
 | |
|     if extra_yarn > 0:
 | |
|         world.options.YarnAvailable.value += extra_yarn
 | |
| 
 | |
| 
 | |
| def item_dlc_enabled(world: "HatInTimeWorld", name: str) -> bool:
 | |
|     data = item_table[name]
 | |
| 
 | |
|     if data.dlc_flags == HatDLC.none:
 | |
|         return True
 | |
|     elif data.dlc_flags == HatDLC.dlc1 and world.is_dlc1():
 | |
|         return True
 | |
|     elif data.dlc_flags == HatDLC.dlc2 and world.is_dlc2():
 | |
|         return True
 | |
|     elif data.dlc_flags == HatDLC.death_wish and world.is_dw():
 | |
|         return True
 | |
| 
 | |
|     return False
 | |
| 
 | |
| 
 | |
| def create_item(world: "HatInTimeWorld", name: str) -> Item:
 | |
|     data = item_table[name]
 | |
|     return HatInTimeItem(name, data.classification, data.code, world.player)
 | |
| 
 | |
| 
 | |
| def create_multiple_items(world: "HatInTimeWorld", name: str, count: int = 1,
 | |
|                           item_type: ItemClassification = ItemClassification.progression) -> List[Item]:
 | |
| 
 | |
|     data = item_table[name]
 | |
|     itemlist: List[Item] = []
 | |
| 
 | |
|     for i in range(count):
 | |
|         itemlist += [HatInTimeItem(name, item_type, data.code, world.player)]
 | |
| 
 | |
|     return itemlist
 | |
| 
 | |
| 
 | |
| def create_junk_items(world: "HatInTimeWorld", count: int) -> List[Item]:
 | |
|     trap_chance = world.options.TrapChance.value
 | |
|     junk_pool: List[Item] = []
 | |
|     junk_list: Dict[str, int] = {}
 | |
|     trap_list: Dict[str, int] = {}
 | |
|     ic: ItemClassification
 | |
| 
 | |
|     for name in item_table.keys():
 | |
|         ic = item_table[name].classification
 | |
|         if ic == ItemClassification.filler:
 | |
|             if world.is_dw_only() and "Pons" in name:
 | |
|                 continue
 | |
| 
 | |
|             junk_list[name] = junk_weights.get(name)
 | |
| 
 | |
|         elif trap_chance > 0 and ic == ItemClassification.trap:
 | |
|             if name == "Baby Trap":
 | |
|                 trap_list[name] = world.options.BabyTrapWeight.value
 | |
|             elif name == "Laser Trap":
 | |
|                 trap_list[name] = world.options.LaserTrapWeight.value
 | |
|             elif name == "Parade Trap":
 | |
|                 trap_list[name] = world.options.ParadeTrapWeight.value
 | |
| 
 | |
|     for i in range(count):
 | |
|         if trap_chance > 0 and world.random.randint(1, 100) <= trap_chance:
 | |
|             junk_pool.append(world.create_item(
 | |
|                 world.random.choices(list(trap_list.keys()), weights=list(trap_list.values()), k=1)[0]))
 | |
|         else:
 | |
|             junk_pool.append(world.create_item(
 | |
|                 world.random.choices(list(junk_list.keys()), weights=list(junk_list.values()), k=1)[0]))
 | |
| 
 | |
|     return junk_pool
 | |
| 
 | |
| 
 | |
| def get_shop_trap_name(world: "HatInTimeWorld") -> str:
 | |
|     rand = world.random.randint(1, 9)
 | |
|     name = ""
 | |
|     if rand == 1:
 | |
|         name = "Time Plece"
 | |
|     elif rand == 2:
 | |
|         name = "Time Piece (Trust me bro)"
 | |
|     elif rand == 3:
 | |
|         name = "TimePiece"
 | |
|     elif rand == 4:
 | |
|         name = "Time Piece?"
 | |
|     elif rand == 5:
 | |
|         name = "Time Pizza"
 | |
|     elif rand == 6:
 | |
|         name = "Time piece"
 | |
|     elif rand == 7:
 | |
|         name = "TIme Piece"
 | |
|     elif rand == 8:
 | |
|         name = "Time Piece (maybe)"
 | |
|     elif rand == 9:
 | |
|         name = "Time Piece ;)"
 | |
| 
 | |
|     return name
 | |
| 
 | |
| 
 | |
| ahit_items = {
 | |
|     "Yarn": ItemData(2000300001, ItemClassification.progression_skip_balancing),
 | |
|     "Time Piece": ItemData(2000300002, ItemClassification.progression_skip_balancing),
 | |
| 
 | |
|     # for HatItems option
 | |
|     "Sprint Hat": ItemData(2000300049, ItemClassification.progression),
 | |
|     "Brewing Hat": ItemData(2000300050, ItemClassification.progression),
 | |
|     "Ice Hat": ItemData(2000300051, ItemClassification.progression),
 | |
|     "Dweller Mask": ItemData(2000300052, ItemClassification.progression),
 | |
|     "Time Stop Hat": ItemData(2000300053, ItemClassification.progression),
 | |
| 
 | |
|     # Badges
 | |
|     "Projectile Badge": ItemData(2000300024, ItemClassification.useful),
 | |
|     "Fast Hatter Badge": ItemData(2000300025, ItemClassification.useful),
 | |
|     "Hover Badge": ItemData(2000300026, ItemClassification.useful),
 | |
|     "Hookshot Badge": ItemData(2000300027, ItemClassification.progression),
 | |
|     "Item Magnet Badge": ItemData(2000300028, ItemClassification.useful),
 | |
|     "No Bonk Badge": ItemData(2000300029, ItemClassification.useful),
 | |
|     "Compass Badge": ItemData(2000300030, ItemClassification.useful),
 | |
|     "Scooter Badge": ItemData(2000300031, ItemClassification.useful),
 | |
|     "One-Hit Hero Badge": ItemData(2000300038, ItemClassification.progression, HatDLC.death_wish),
 | |
|     "Camera Badge": ItemData(2000300042, ItemClassification.progression, HatDLC.death_wish),
 | |
| 
 | |
|     # Relics
 | |
|     "Relic (Burger Patty)": ItemData(2000300006, ItemClassification.progression),
 | |
|     "Relic (Burger Cushion)": ItemData(2000300007, ItemClassification.progression),
 | |
|     "Relic (Mountain Set)": ItemData(2000300008, ItemClassification.progression),
 | |
|     "Relic (Train)": ItemData(2000300009, ItemClassification.progression),
 | |
|     "Relic (UFO)": ItemData(2000300010, ItemClassification.progression),
 | |
|     "Relic (Cow)": ItemData(2000300011, ItemClassification.progression),
 | |
|     "Relic (Cool Cow)": ItemData(2000300012, ItemClassification.progression),
 | |
|     "Relic (Tin-foil Hat Cow)": ItemData(2000300013, ItemClassification.progression),
 | |
|     "Relic (Crayon Box)": ItemData(2000300014, ItemClassification.progression),
 | |
|     "Relic (Red Crayon)": ItemData(2000300015, ItemClassification.progression),
 | |
|     "Relic (Blue Crayon)": ItemData(2000300016, ItemClassification.progression),
 | |
|     "Relic (Green Crayon)": ItemData(2000300017, ItemClassification.progression),
 | |
|     # DLC
 | |
|     "Relic (Cake Stand)": ItemData(2000300018, ItemClassification.progression, HatDLC.dlc1),
 | |
|     "Relic (Shortcake)": ItemData(2000300019, ItemClassification.progression, HatDLC.dlc1),
 | |
|     "Relic (Chocolate Cake Slice)": ItemData(2000300020, ItemClassification.progression, HatDLC.dlc1),
 | |
|     "Relic (Chocolate Cake)": ItemData(2000300021, ItemClassification.progression, HatDLC.dlc1),
 | |
|     "Relic (Necklace Bust)": ItemData(2000300022, ItemClassification.progression, HatDLC.dlc2),
 | |
|     "Relic (Necklace)": ItemData(2000300023, ItemClassification.progression, HatDLC.dlc2),
 | |
| 
 | |
|     # Garbage items
 | |
|     "25 Pons": ItemData(2000300034, ItemClassification.filler),
 | |
|     "50 Pons": ItemData(2000300035, ItemClassification.filler),
 | |
|     "100 Pons": ItemData(2000300036, ItemClassification.filler),
 | |
|     "Health Pon": ItemData(2000300037, ItemClassification.filler),
 | |
|     "Random Cosmetic": ItemData(2000300044, ItemClassification.filler),
 | |
| 
 | |
|     # Traps
 | |
|     "Baby Trap": ItemData(2000300039, ItemClassification.trap),
 | |
|     "Laser Trap": ItemData(2000300040, ItemClassification.trap),
 | |
|     "Parade Trap": ItemData(2000300041, ItemClassification.trap),
 | |
| 
 | |
|     # Other
 | |
|     "Badge Pin": ItemData(2000300043, ItemClassification.useful),
 | |
|     "Umbrella": ItemData(2000300033, ItemClassification.progression),
 | |
|     "Progressive Painting Unlock": ItemData(2000300003, ItemClassification.progression),
 | |
|     # DLC
 | |
|     "Metro Ticket - Yellow": ItemData(2000300045, ItemClassification.progression, HatDLC.dlc2),
 | |
|     "Metro Ticket - Green": ItemData(2000300046, ItemClassification.progression, HatDLC.dlc2),
 | |
|     "Metro Ticket - Blue": ItemData(2000300047, ItemClassification.progression, HatDLC.dlc2),
 | |
|     "Metro Ticket - Pink": ItemData(2000300048, ItemClassification.progression, HatDLC.dlc2),
 | |
| }
 | |
| 
 | |
| act_contracts = {
 | |
|     "Snatcher's Contract - The Subcon Well": ItemData(2000300200, ItemClassification.progression),
 | |
|     "Snatcher's Contract - Toilet of Doom": ItemData(2000300201, ItemClassification.progression),
 | |
|     "Snatcher's Contract - Queen Vanessa's Manor": ItemData(2000300202, ItemClassification.progression),
 | |
|     "Snatcher's Contract - Mail Delivery Service": ItemData(2000300203, ItemClassification.progression),
 | |
| }
 | |
| 
 | |
| alps_hooks = {
 | |
|     "Zipline Unlock - The Birdhouse Path": ItemData(2000300204, ItemClassification.progression),
 | |
|     "Zipline Unlock - The Lava Cake Path": ItemData(2000300205, ItemClassification.progression),
 | |
|     "Zipline Unlock - The Windmill Path": ItemData(2000300206, ItemClassification.progression),
 | |
|     "Zipline Unlock - The Twilight Bell Path": ItemData(2000300207, ItemClassification.progression),
 | |
| }
 | |
| 
 | |
| relic_groups = {
 | |
|     "Burger": {"Relic (Burger Patty)", "Relic (Burger Cushion)"},
 | |
|     "Train": {"Relic (Mountain Set)", "Relic (Train)"},
 | |
|     "UFO": {"Relic (UFO)", "Relic (Cow)", "Relic (Cool Cow)", "Relic (Tin-foil Hat Cow)"},
 | |
|     "Crayon": {"Relic (Crayon Box)", "Relic (Red Crayon)", "Relic (Blue Crayon)", "Relic (Green Crayon)"},
 | |
|     "Cake": {"Relic (Cake Stand)", "Relic (Chocolate Cake)", "Relic (Chocolate Cake Slice)", "Relic (Shortcake)"},
 | |
|     "Necklace": {"Relic (Necklace Bust)", "Relic (Necklace)"},
 | |
| }
 | |
| 
 | |
| item_frequencies = {
 | |
|     "Badge Pin": 2,
 | |
|     "Progressive Painting Unlock": 3,
 | |
| }
 | |
| 
 | |
| junk_weights = {
 | |
|     "25 Pons": 50,
 | |
|     "50 Pons": 25,
 | |
|     "100 Pons": 10,
 | |
|     "Health Pon": 35,
 | |
|     "Random Cosmetic": 35,
 | |
| }
 | |
| 
 | |
| item_table = {
 | |
|     **ahit_items,
 | |
|     **act_contracts,
 | |
|     **alps_hooks,
 | |
| }
 |