From 168f006fd762f34f909bdfaaea91bd151ffaca79 Mon Sep 17 00:00:00 2001 From: Emerassi <78173681+Emerassi@users.noreply.github.com> Date: Sat, 29 Nov 2025 18:24:53 -0800 Subject: [PATCH] refactor homepoints so they don't need as much special logic. Also fix to discipline hollow (unrelated). Also add them to location groups. --- worlds/crystal_project/__init__.py | 21 ++++++++------------- worlds/crystal_project/home_points.py | 1 + worlds/crystal_project/locations.py | 9 +++++++++ worlds/crystal_project/regions.py | 6 ++++-- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/worlds/crystal_project/__init__.py b/worlds/crystal_project/__init__.py index 9690fccf818b..a3d6367ce967 100644 --- a/worlds/crystal_project/__init__.py +++ b/worlds/crystal_project/__init__.py @@ -9,11 +9,11 @@ from .constants.teleport_stones import * from .constants.item_groups import * from .constants.region_passes import * +from .home_points import get_home_points from .items import item_table, optional_scholar_abilities, get_random_starting_jobs, filler_items, \ get_item_names_per_category, progressive_equipment, non_progressive_equipment, get_starting_jobs, \ set_jobs_at_default_locations, default_starting_job_list, key_rings, dungeon_keys, singleton_keys, \ - display_region_name_to_pass_dict, job_crystal_beginner_dictionary, job_crystal_advanced_dictionary, job_crystal_expert_dictionary, home_point_item_index_offset -from .home_points import get_home_points + display_region_name_to_pass_dict, job_crystal_beginner_dictionary, job_crystal_advanced_dictionary, job_crystal_expert_dictionary, home_point_item_index_offset, ItemData from .locations import get_treasure_and_npc_locations, get_boss_locations, get_shop_locations, get_region_completion_locations, LocationData, get_location_names_per_category, \ get_location_name_to_id, get_crystal_locations, home_point_location_index_offset from .presets import crystal_project_options_presets @@ -49,6 +49,11 @@ class CrystalProjectWorld(World): options: CrystalProjectOptions topology_present = True # show path to required location checks in spoiler + # Add the homepoints to the item_table so they don't require any special code after this + home_points = get_home_points(-1, options) + for home_point in home_points: + item_table[home_point.name] = ItemData(HOME_POINT, home_point.code + home_point_item_index_offset, ItemClassification.progression) + item_name_to_id = {item: item_table[item].code for item in item_table} location_name_to_id = get_location_name_to_id() @@ -58,7 +63,6 @@ class CrystalProjectWorld(World): mod_info = get_mod_info() modded_items = get_modded_items(mod_info) - home_points = get_home_points(-1, options) modded_job_count: int = 0 for modded_item in modded_items: @@ -71,11 +75,6 @@ class CrystalProjectWorld(World): else: item_name_groups.setdefault(MOD, set()).add(modded_item.name) - for home_point in home_points: - item_name_to_id[home_point.name] = (home_point.code + home_point_item_index_offset) - item_name_groups.setdefault(HOME_POINT, set()).add(home_point.name) - location_name_to_id[home_point.name] = (home_point.code + home_point_location_index_offset) - modded_locations = get_modded_locations(mod_info) for modded_location in modded_locations: @@ -301,12 +300,8 @@ def create_item(self, name: str) -> Item: return Item(name, data.classification, data.code, self.player) else: matches_mod = [item for (index, item) in enumerate(self.modded_items) if item.name == name] - matches_home_point = [item for (index, item) in enumerate(self.home_points) if item.name == name] - if len(matches_mod) > 0: - return Item(matches_mod[0].name, matches_mod[0].classification, matches_mod[0].code, self.player) - else: - return Item(matches_home_point[0].name, ItemClassification.progression, (matches_home_point[0].code + home_point_item_index_offset), self.player) + return Item(matches_mod[0].name, matches_mod[0].classification, matches_mod[0].code, self.player) def create_items(self) -> None: pool = self.get_item_pool(self.get_excluded_items()) diff --git a/worlds/crystal_project/home_points.py b/worlds/crystal_project/home_points.py index 7cd1b0355a01..c1174b1fa367 100644 --- a/worlds/crystal_project/home_points.py +++ b/worlds/crystal_project/home_points.py @@ -6,6 +6,7 @@ from .locations import LocationData from .options import CrystalProjectOptions from .rules import CrystalProjectLogic + #Remember if you update the AP Region a Home Point is in or its name, go change it in the Menu connections function in the region.py file def get_home_points(player: Optional[int], options: Optional[CrystalProjectOptions]) -> List[LocationData]: logic = CrystalProjectLogic(player, options) diff --git a/worlds/crystal_project/locations.py b/worlds/crystal_project/locations.py index b6515dc32394..f2fb55ce859b 100644 --- a/worlds/crystal_project/locations.py +++ b/worlds/crystal_project/locations.py @@ -28,14 +28,18 @@ class LocationData(NamedTuple): home_point_location_index_offset = 1000000000 def get_location_name_to_id() -> dict[str, int]: + from .home_points import get_home_points + location_name_to_id = {location.name: location.code for location in get_treasure_and_npc_locations(-1, None)} crystal_name_to_id = {crystal.name: crystal.code for crystal in get_crystal_locations(-1, None)} boss_name_to_id = {boss.name: boss.code for boss in get_boss_locations(-1, None)} shop_name_to_id = {shop.name: shop.code for shop in get_shop_locations(-1, None)} + homepoint_name_to_id = {homepoint.name: homepoint.code + home_point_location_index_offset for homepoint in get_home_points(-1, None)} region_completion_name_to_id = {region_completion.name: region_completion.code for region_completion in get_region_completion_locations(-1, None)} location_name_to_id.update(crystal_name_to_id) location_name_to_id.update(boss_name_to_id) location_name_to_id.update(shop_name_to_id) + location_name_to_id.update(homepoint_name_to_id) location_name_to_id.update(region_completion_name_to_id) return location_name_to_id @@ -1993,6 +1997,8 @@ def get_region_completion_locations(player: int, options: CrystalProjectOptions) def get_location_names_per_category() -> Dict[str, Set[str]]: categories: Dict[str, Set[str]] = {} + from .home_points import get_home_points + for location in get_crystal_locations(-1, None): categories.setdefault("Crystals", set()).add(location.name) @@ -2002,6 +2008,9 @@ def get_location_names_per_category() -> Dict[str, Set[str]]: for location in get_boss_locations(-1, None): categories.setdefault("Bosses", set()).add(location.name) + for location in get_home_points(-1, None): + categories.setdefault("Homepoints", set()).add(location.name) + for location in get_region_completion_locations(-1, None): categories.setdefault("Region Completions", set()).add(location.name) diff --git a/worlds/crystal_project/regions.py b/worlds/crystal_project/regions.py index 945a28eea32a..e01118719129 100644 --- a/worlds/crystal_project/regions.py +++ b/worlds/crystal_project/regions.py @@ -1007,7 +1007,7 @@ def fancy_add_exits(self, region: str, exits: List[str], def connect_menu_region(world: "CrystalProjectWorld", options: CrystalProjectOptions) -> None: logic = CrystalProjectLogic(world.player, options) - fancy_add_exits(world, MENU_AP_REGION, [SPAWNING_MEADOWS_AP_REGION, DELENDE_PLAINS_AP_REGION, DELENDE_HIGH_BRIDGES_AP_REGION, DELENDE_PEAK_AP_REGION, MERCURY_SHRINE_AP_REGION, THE_PALE_GROTTO_AP_REGION, SEASIDE_CLIFFS_AP_REGION, YAMAGAWA_MA_AP_REGION, PROVING_MEADOWS_AP_REGION, SKUMPARADISE_AP_REGION, CAPITAL_SEQUOIA_AP_REGION, CAPITAL_JAIL_AP_REGION, ROLLING_QUINTAR_FIELDS_AP_REGION, SANCTUM_ENTRANCE_AP_REGION, QUINTAR_SANCTUM_AP_REGION, BOOMER_SOCIETY_AP_REGION, OKIMOTO_NS_AP_REGION, SALMON_PASS_EAST_AP_REGION, SALMON_RIVER_AP_REGION, CASTLE_SEQUOIA_AP_REGION, TOWER_OF_ZOT_AP_REGION, POKO_POKO_DESERT_AP_REGION, SARA_SARA_BAZAAR_AP_REGION, IBEK_CAVE_MOUTH_AP_REGION, BEACH_BIRDS_NEST_AP_REGION, BEAURIOR_VOLCANO_AP_REGION, BEAURIOR_ROCK_AP_REGION, ANCIENT_RESERVOIR_AP_REGION, SHOUDU_PROVINCE_AP_REGION, GANYMEDE_SHRINE_AP_REGION, THE_UNDERCITY_AP_REGION, PIPELINE_NORTH_AP_REGION, PIPELINE_SOUTH_AP_REGION, SEQUOIA_ATHENAEUM_ENTRANCE_AP_REGION, LOWER_ICE_LAKES_AP_REGION, SOUVENIR_SHOP_AP_REGION, SLIP_GLIDE_RIDE_EXIT_AP_REGION, UPPER_ICE_LAKES_AP_REGION, TALL_TALL_SAVE_POINT_AP_REGION, PEAK_RAMPARTS_AP_REGION, SLIP_GLIDE_RIDE_ENTRANCE_AP_REGION, LANDS_END_AP_REGION, OWL_TREE_AP_REGION, QUINTAR_RESERVE_AP_REGION, EUROPA_SHRINE_AP_REGION, JIDAMBA_EACLANEYA_AP_REGION, LABYRINTH_CORE_AP_REGION, DIONE_SHRINE_AP_REGION, DIONE_ROOF_AP_REGION, THE_SEQUOIA_AP_REGION, THE_CHALICE_OF_TAR_AP_REGION, THE_OPEN_SEA_AP_REGION, CONTINENTAL_TRAM_AP_REGION, POSEIDON_SHRINE_ROOF_AP_REGION, NEPTUNE_SHRINE_AP_REGION, THE_OLD_WORLD_AP_REGION, THE_NEW_WORLD_AP_REGION, MODDED_ZONE_AP_REGION], + fancy_add_exits(world, MENU_AP_REGION, [SPAWNING_MEADOWS_AP_REGION, DELENDE_PLAINS_AP_REGION, DELENDE_HIGH_BRIDGES_AP_REGION, DELENDE_PEAK_AP_REGION, MERCURY_SHRINE_AP_REGION, THE_PALE_GROTTO_AP_REGION, SEASIDE_CLIFFS_AP_REGION, YAMAGAWA_MA_AP_REGION, PROVING_MEADOWS_AP_REGION, SKUMPARADISE_AP_REGION, CAPITAL_SEQUOIA_AP_REGION, CAPITAL_JAIL_AP_REGION, ROLLING_QUINTAR_FIELDS_AP_REGION, SANCTUM_ENTRANCE_AP_REGION, QUINTAR_SANCTUM_AP_REGION, BOOMER_SOCIETY_AP_REGION, OKIMOTO_NS_AP_REGION, SALMON_PASS_EAST_AP_REGION, SALMON_RIVER_AP_REGION, CASTLE_SEQUOIA_AP_REGION, TOWER_OF_ZOT_AP_REGION, POKO_POKO_DESERT_AP_REGION, SARA_SARA_BAZAAR_AP_REGION, IBEK_CAVE_MOUTH_AP_REGION, BEACH_BIRDS_NEST_AP_REGION, BEAURIOR_VOLCANO_AP_REGION, BEAURIOR_ROCK_AP_REGION, ANCIENT_RESERVOIR_AP_REGION, SHOUDU_PROVINCE_AP_REGION, GANYMEDE_SHRINE_AP_REGION, THE_UNDERCITY_AP_REGION, PIPELINE_NORTH_AP_REGION, PIPELINE_SOUTH_AP_REGION, SEQUOIA_ATHENAEUM_ENTRANCE_AP_REGION, LOWER_ICE_LAKES_AP_REGION, SOUVENIR_SHOP_AP_REGION, SLIP_GLIDE_RIDE_EXIT_AP_REGION, UPPER_ICE_LAKES_AP_REGION, TALL_TALL_SAVE_POINT_AP_REGION, PEAK_RAMPARTS_AP_REGION, SLIP_GLIDE_RIDE_ENTRANCE_AP_REGION, LANDS_END_AP_REGION, OWL_TREE_AP_REGION, QUINTAR_RESERVE_AP_REGION, EUROPA_SHRINE_AP_REGION, JIDAMBA_EACLANEYA_AP_REGION, LABYRINTH_CORE_AP_REGION, DIONE_SHRINE_AP_REGION, DIONE_ROOF_AP_REGION, THE_SEQUOIA_AP_REGION, THE_CHALICE_OF_TAR_AP_REGION, THE_OPEN_SEA_AP_REGION, CONTINENTAL_TRAM_AP_REGION, POSEIDON_SHRINE_ROOF_AP_REGION, NEPTUNE_SHRINE_AP_REGION, THE_OLD_WORLD_AP_REGION, THE_NEW_WORLD_AP_REGION, DISCIPLINE_HOLLOW_AP_REGION, MODDED_ZONE_AP_REGION], {SPAWNING_MEADOWS_AP_REGION: lambda state: (options.regionsanity.value == options.regionsanity.option_disabled or state.has("HomePoint - AP Spawn Point", world.player) or state.has("HomePoint - Old Nan's Watering Hole", world.player)), DELENDE_PLAINS_AP_REGION: lambda state: (state.has("HomePoint - The Pale Grotto Entrance", world.player) or state.has("HomePoint - Soiled Den", world.player) or state.has("HomePoint - Fish Hatchery", world.player)), DELENDE_HIGH_BRIDGES_AP_REGION: lambda state: (state.has("HomePoint - Cabin On The Cliff", world.player) or state.has("HomePoint - Delende Falls", world.player)), @@ -1064,5 +1064,7 @@ def connect_menu_region(world: "CrystalProjectWorld", options: CrystalProjectOpt POSEIDON_SHRINE_ROOF_AP_REGION: lambda state: state.has(POSEIDON_STONE, world.player), NEPTUNE_SHRINE_AP_REGION: lambda state: (state.has(NEPTUNE_STONE, world.player) or state.has("HomePoint - Neptune Shrine", world.player)), THE_OLD_WORLD_AP_REGION: lambda state: logic.old_world_requirements(state), - THE_NEW_WORLD_AP_REGION: lambda state: (logic.new_world_requirements(state) or state.has("HomePoint - Astley's Shrine", world.player) or state.has("HomePoint - Astley's Keep", world.player) or state.has("HomePoint - Discipline Hollow", world.player))}) + THE_NEW_WORLD_AP_REGION: lambda state: (logic.new_world_requirements(state) or state.has("HomePoint - Astley's Shrine", world.player) or state.has("HomePoint - Astley's Keep", world.player)), + DISCIPLINE_HOLLOW_AP_REGION: lambda state: state.has("HomePoint - Discipline Hollow", world.player), + }), world.multiworld.register_indirect_condition(world.get_region(THE_DEPTHS_AP_REGION), world.get_entrance(MENU_AP_REGION + " -> " + THE_OLD_WORLD_AP_REGION)) \ No newline at end of file