Files
Grinch-AP/worlds/ahit/Items.py
CookieCat e5c9b8ad0c AHIT: Generation error fixes and some other bug fixes (#3663)
* 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>
2024-07-27 19:16:52 +02:00

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,
}