This commit is contained in:
CookieCat
2023-09-15 18:38:43 -04:00
parent a1395eeae0
commit 714241b07a
9 changed files with 99 additions and 25 deletions

View File

@@ -1,6 +1,6 @@
from BaseClasses import Item, ItemClassification
from worlds.AutoWorld import World
from .Types import HatDLC, HatType
from .Types import HatDLC, HatType, hat_type_to_item
from .Locations import get_total_locations
from .Rules import get_difficulty, is_player_knowledgeable
from typing import Optional, NamedTuple, List, Dict
@@ -18,7 +18,7 @@ class HatInTimeItem(Item):
def create_itempool(world: World) -> List[Item]:
itempool: List[Item] = []
if not world.is_dw_only():
if not world.is_dw_only() and world.multiworld.HatItems[world.player].value == 0:
calculate_yarn_costs(world)
yarn_pool: List[Item] = create_multiple_items(world, "Yarn",
world.multiworld.YarnAvailable[world.player].value,
@@ -36,6 +36,9 @@ def create_itempool(world: World) -> List[Item]:
if not item_dlc_enabled(world, name):
continue
if world.multiworld.HatItems[world.player].value == 0 and name in hat_type_to_item.values():
continue
item_type: ItemClassification = item_table.get(name).classification
if world.is_dw_only():
@@ -181,7 +184,13 @@ def create_junk_items(world: World, count: int) -> List[Item]:
ahit_items = {
"Yarn": ItemData(300001, ItemClassification.progression_skip_balancing),
"Time Piece": ItemData(300002, ItemClassification.progression_skip_balancing),
"Progressive Painting Unlock": ItemData(300003, ItemClassification.progression),
# for HatItems option
"Sprint Hat": ItemData(300049, ItemClassification.progression),
"Brewing Hat": ItemData(300050, ItemClassification.progression),
"Ice Hat": ItemData(300051, ItemClassification.progression),
"Dweller Mask": ItemData(300052, ItemClassification.progression),
"Time Stop Hat": ItemData(300053, ItemClassification.progression),
# Relics
"Relic (Burger Patty)": ItemData(300006, ItemClassification.progression),
@@ -210,8 +219,9 @@ ahit_items = {
"Camera Badge": ItemData(300042, ItemClassification.progression, HatDLC.death_wish),
# Other
"Umbrella": ItemData(300033, ItemClassification.progression),
"Badge Pin": ItemData(300043, ItemClassification.useful),
"Umbrella": ItemData(300033, ItemClassification.progression),
"Progressive Painting Unlock": ItemData(300003, ItemClassification.progression),
# Garbage items
"25 Pons": ItemData(300034, ItemClassification.filler),

View File

@@ -312,6 +312,12 @@ class MinExtraYarn(Range):
default = 10
class HatItems(Toggle):
"""Removes all yarn from the pool and turns the hats into individual items instead."""
display_name = "Hat Items"
default = 0
class MinPonCost(Range):
"""The minimum amount of Pons that any shop item can cost."""
display_name = "Minimum Shop Pon Cost"
@@ -648,6 +654,7 @@ ahit_options: typing.Dict[str, type(Option)] = {
"YarnCostMax": YarnCostMax,
"YarnAvailable": YarnAvailable,
"MinExtraYarn": MinExtraYarn,
"HatItems": HatItems,
"MinPonCost": MinPonCost,
"MaxPonCost": MaxPonCost,
@@ -675,6 +682,7 @@ slot_data_options: typing.Dict[str, type(Option)] = {
"ShuffleStorybookPages": ShuffleStorybookPages,
"ShuffleActContracts": ShuffleActContracts,
"ShuffleSubconPaintings": ShuffleSubconPaintings,
"HatItems": HatItems,
"EnableDLC1": EnableDLC1,
"Tasksanity": Tasksanity,

View File

@@ -433,6 +433,11 @@ def create_rift_connections(world: World, region: Region):
connect_regions(act_region, region, entrance_name, world.player)
i += 1
# fix for some weird keyerror from tests
if region.name == "Time Rift - Rumbi Factory":
for entrance in region.entrances:
world.multiworld.get_entrance(entrance.name, world.player)
def create_tasksanity_locations(world: World):
ship_shape: Region = world.multiworld.get_region("Ship Shape", world.player)
@@ -873,11 +878,6 @@ def create_events(world: World) -> int:
event: Location = create_event(name, world.multiworld.get_region(data.region, world.player), world)
event.show_in_spoiler = False
if data.act_complete_event:
act_completion: str = f"Act Completion ({data.region})"
event.access_rule = world.multiworld.get_location(act_completion, world.player).access_rule
count += 1
return count

View File

@@ -1,8 +1,8 @@
from worlds.AutoWorld import World, CollectionState
from worlds.generic.Rules import add_rule, set_rule
from .Locations import location_table, tihs_locations, zipline_unlocks, is_location_valid, contract_locations, \
shop_locations
from .Types import HatType, ChapterIndex
shop_locations, event_locs
from .Types import HatType, ChapterIndex, hat_type_to_item
from BaseClasses import Location, Entrance, Region
import typing
@@ -32,6 +32,9 @@ act_connections = {
def can_use_hat(state: CollectionState, world: World, hat: HatType) -> bool:
if world.multiworld.HatItems[world.player].value > 0:
return state.has(hat_type_to_item[hat], world.player)
return state.count("Yarn", world.player) >= get_hat_cost(world, hat)
@@ -257,8 +260,9 @@ def set_rules(world: World):
if world.multiworld.ActRandomizer[world.player].value == 0:
set_default_rift_rules(world)
table = location_table | event_locs
location: Location
for (key, data) in location_table.items():
for (key, data) in table.items():
if not is_location_valid(world, key):
continue
@@ -340,6 +344,8 @@ def set_rules(world: World):
for rules in access_rules:
add_rule(e, rules)
set_event_rules(world)
for entrance in world.multiworld.get_region("Alpine Free Roam", world.player).entrances:
add_rule(entrance, lambda state: can_use_hookshot(state, world))
if world.multiworld.UmbrellaLogic[world.player].value > 0:
@@ -851,6 +857,17 @@ def set_default_rift_rules(world: World):
add_rule(entrance, lambda state: has_relic_combo(state, world, "Necklace"))
def set_event_rules(world: World):
for (name, data) in event_locs.items():
if not is_location_valid(world, name):
continue
event: Location = world.multiworld.get_location(name, world.player)
if data.act_complete_event:
add_rule(event, world.multiworld.get_location(f"Act Completion ({data.region})", world.player).access_rule)
def connect_regions(start_region: Region, exit_region: Region, entrancename: str, player: int) -> Entrance:
entrance = Entrance(player, entrancename, start_region)
start_region.exits.append(entrance)

View File

@@ -26,3 +26,12 @@ class ChapterIndex(IntEnum):
FINALE = 5
CRUISE = 6
METRO = 7
hat_type_to_item = {
HatType.SPRINT: "Sprint Hat",
HatType.BREWING: "Brewing Hat",
HatType.ICE: "Ice Hat",
HatType.DWELLER: "Dweller Mask",
HatType.TIME_STOP: "Time Stop Hat",
}

View File

@@ -18,7 +18,6 @@ excluded_bonuses: Dict[int, List[str]] = {}
dw_shuffle: Dict[int, List[str]] = {}
nyakuza_thug_items: Dict[int, Dict[str, int]] = {}
badge_seller_count: Dict[int, int] = {}
badge_seller_count: Dict[int, int] = {}
class AWebInTime(WebWorld):
@@ -112,7 +111,7 @@ class HatInTimeWorld(World):
hat_craft_order[self.player] = [HatType.SPRINT, HatType.BREWING, HatType.ICE,
HatType.DWELLER, HatType.TIME_STOP]
if self.multiworld.RandomizeHatOrder[self.player].value > 0:
if self.multiworld.HatItems[self.player].value == 0 and self.multiworld.RandomizeHatOrder[self.player].value > 0:
self.random.shuffle(hat_craft_order[self.player])
if self.multiworld.RandomizeHatOrder[self.player].value == 2:
hat_craft_order[self.player].remove(HatType.TIME_STOP)
@@ -165,26 +164,28 @@ class HatInTimeWorld(World):
return create_item(self, name)
def fill_slot_data(self) -> dict:
slot_data: dict = {"SprintYarnCost": hat_yarn_costs[self.player][HatType.SPRINT],
"BrewingYarnCost": hat_yarn_costs[self.player][HatType.BREWING],
"IceYarnCost": hat_yarn_costs[self.player][HatType.ICE],
"DwellerYarnCost": hat_yarn_costs[self.player][HatType.DWELLER],
"TimeStopYarnCost": hat_yarn_costs[self.player][HatType.TIME_STOP],
"Chapter1Cost": chapter_timepiece_costs[self.player][ChapterIndex.MAFIA],
slot_data: dict = {"Chapter1Cost": chapter_timepiece_costs[self.player][ChapterIndex.MAFIA],
"Chapter2Cost": chapter_timepiece_costs[self.player][ChapterIndex.BIRDS],
"Chapter3Cost": chapter_timepiece_costs[self.player][ChapterIndex.SUBCON],
"Chapter4Cost": chapter_timepiece_costs[self.player][ChapterIndex.ALPINE],
"Chapter5Cost": chapter_timepiece_costs[self.player][ChapterIndex.FINALE],
"Chapter6Cost": chapter_timepiece_costs[self.player][ChapterIndex.CRUISE],
"Chapter7Cost": chapter_timepiece_costs[self.player][ChapterIndex.METRO],
"Hat1": int(hat_craft_order[self.player][0]),
"Hat2": int(hat_craft_order[self.player][1]),
"Hat3": int(hat_craft_order[self.player][2]),
"Hat4": int(hat_craft_order[self.player][3]),
"Hat5": int(hat_craft_order[self.player][4]),
"BadgeSellerItemCount": badge_seller_count[self.player],
"SeedNumber": self.multiworld.seed} # For shop prices
if self.multiworld.HatItems[self.player].value == 0:
slot_data.setdefault("SprintYarnCost", hat_yarn_costs[self.player][HatType.SPRINT])
slot_data.setdefault("BrewingYarnCost", hat_yarn_costs[self.player][HatType.BREWING])
slot_data.setdefault("IceYarnCost", hat_yarn_costs[self.player][HatType.ICE])
slot_data.setdefault("DwellerYarnCost", hat_yarn_costs[self.player][HatType.DWELLER])
slot_data.setdefault("TimeStopYarnCost", hat_yarn_costs[self.player][HatType.TIME_STOP])
slot_data.setdefault("Hat1", int(hat_craft_order[self.player][0]))
slot_data.setdefault("Hat2", int(hat_craft_order[self.player][1]))
slot_data.setdefault("Hat3", int(hat_craft_order[self.player][2]))
slot_data.setdefault("Hat4", int(hat_craft_order[self.player][3]))
slot_data.setdefault("Hat5", int(hat_craft_order[self.player][4]))
if self.multiworld.ActRandomizer[self.player].value > 0:
for name in self.act_connections.keys():
slot_data[name] = self.act_connections[name]

View File

@@ -0,0 +1,24 @@
from worlds.ahit.Regions import act_chapters
from worlds.ahit.test.TestBase import HatInTimeTestBase
class TestActs(HatInTimeTestBase):
options = {
"ActRandomizer": 2,
"EnableDLC1": 1,
"EnableDLC2": 1,
}
def test_act_shuffle(self):
for i in range(1000):
self.world_setup()
self.collect_all_but([""])
for name in act_chapters.keys():
region = self.multiworld.get_region(name, 1)
for entrance in region.entrances:
self.assertTrue(self.can_reach_entrance(entrance.name),
f"Can't reach {name} from {entrance}\n"
f"{entrance.parent_region.entrances[0]} -> {entrance.parent_region} "
f"-> {entrance} -> {name}"
f" (expected method of access)")

View File

@@ -0,0 +1,5 @@
from test.TestBase import WorldTestBase
class HatInTimeTestBase(WorldTestBase):
game = "A Hat in Time"

View File