182 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			182 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | from typing import Dict, Callable, TYPE_CHECKING | ||
|  | from BaseClasses import CollectionState, LocationProgressType | ||
|  | from .Options import Goal, PaintingChecksBalancing | ||
|  | 
 | ||
|  | if TYPE_CHECKING: | ||
|  |     from . import InscryptionWorld | ||
|  | else: | ||
|  |     InscryptionWorld = object | ||
|  | 
 | ||
|  | 
 | ||
|  | # Based on The Messenger's implementation | ||
|  | class InscryptionRules: | ||
|  |     player: int | ||
|  |     world: InscryptionWorld | ||
|  |     location_rules: Dict[str, Callable[[CollectionState], bool]] | ||
|  |     region_rules: Dict[str, Callable[[CollectionState], bool]] | ||
|  | 
 | ||
|  |     def __init__(self, world: InscryptionWorld) -> None: | ||
|  |         self.player = world.player | ||
|  |         self.world = world | ||
|  |         self.location_rules = { | ||
|  |             "Act 1 - Wardrobe Drawer 1": self.has_wardrobe_key, | ||
|  |             "Act 1 - Wardrobe Drawer 2": self.has_wardrobe_key, | ||
|  |             "Act 1 - Wardrobe Drawer 3": self.has_wardrobe_key, | ||
|  |             "Act 1 - Wardrobe Drawer 4": self.has_wardrobe_key, | ||
|  |             "Act 1 - Dagger": self.has_caged_wolf, | ||
|  |             "Act 1 - Magnificus Eye": self.has_dagger, | ||
|  |             "Act 1 - Clock Main Compartment": self.has_magnificus_eye, | ||
|  |             "Act 2 - Battle Prospector": self.has_camera_and_meat, | ||
|  |             "Act 2 - Battle Angler": self.has_camera_and_meat, | ||
|  |             "Act 2 - Battle Trapper": self.has_camera_and_meat, | ||
|  |             "Act 2 - Battle Pike Mage": self.has_tower_requirements, | ||
|  |             "Act 2 - Battle Goobert": self.has_tower_requirements, | ||
|  |             "Act 2 - Battle Lonely Wizard": self.has_tower_requirements, | ||
|  |             "Act 2 - Battle Inspector": self.has_act2_bridge_requirements, | ||
|  |             "Act 2 - Battle Melter": self.has_act2_bridge_requirements, | ||
|  |             "Act 2 - Battle Dredger": self.has_act2_bridge_requirements, | ||
|  |             "Act 2 - Forest Meadow Chest": self.has_camera_and_meat, | ||
|  |             "Act 2 - Tower Chest 1": self.has_act2_bridge_requirements, | ||
|  |             "Act 2 - Tower Chest 2": self.has_tower_requirements, | ||
|  |             "Act 2 - Tower Chest 3": self.has_tower_requirements, | ||
|  |             "Act 2 - Tentacle": self.has_tower_requirements, | ||
|  |             "Act 2 - Factory Trash Can": self.has_act2_bridge_requirements, | ||
|  |             "Act 2 - Factory Drawer 1": self.has_act2_bridge_requirements, | ||
|  |             "Act 2 - Factory Drawer 2": self.has_act2_bridge_requirements, | ||
|  |             "Act 2 - Factory Chest 1": self.has_act2_bridge_requirements, | ||
|  |             "Act 2 - Factory Chest 2": self.has_act2_bridge_requirements, | ||
|  |             "Act 2 - Factory Chest 3": self.has_act2_bridge_requirements, | ||
|  |             "Act 2 - Factory Chest 4": self.has_act2_bridge_requirements, | ||
|  |             "Act 2 - Monocle": self.has_act2_bridge_requirements, | ||
|  |             "Act 2 - Boss Grimora": self.has_all_epitaph_pieces, | ||
|  |             "Act 2 - Boss Leshy": self.has_camera_and_meat, | ||
|  |             "Act 2 - Boss Magnificus": self.has_tower_requirements, | ||
|  |             "Act 2 - Boss P03": self.has_act2_bridge_requirements, | ||
|  |             "Act 2 - Bone Lord Femur": self.has_obol, | ||
|  |             "Act 2 - Bone Lord Horn": self.has_obol, | ||
|  |             "Act 2 - Bone Lord Holo Key": self.has_obol, | ||
|  |             "Act 2 - Mycologists Holo Key": self.has_tower_requirements,  # Could need money | ||
|  |             "Act 2 - Ancient Obol": self.has_tower_requirements,  # Need money for the pieces? Use the tower mannequin. | ||
|  |             "Act 3 - Boss Photographer": self.has_inspectometer_battery, | ||
|  |             "Act 3 - Boss Archivist": self.has_battery_and_quill, | ||
|  |             "Act 3 - Boss Unfinished": self.has_gems_and_battery, | ||
|  |             "Act 3 - Boss G0lly": self.has_gems_and_battery, | ||
|  |             "Act 3 - Extra Battery": self.has_inspectometer_battery,  # Hard to miss but soft lock still possible. | ||
|  |             "Act 3 - Nano Armor Generator": self.has_gems_and_battery,  # Costs money, so can need multiple battles. | ||
|  |             "Act 3 - Shop Holo Pelt": self.has_gems_and_battery,  # Costs money, so can need multiple battles. | ||
|  |             "Act 3 - Middle Holo Pelt": self.has_inspectometer_battery,  # Can be reached without but possible soft lock | ||
|  |             "Act 3 - Forest Holo Pelt": self.has_inspectometer_battery, | ||
|  |             "Act 3 - Crypt Holo Pelt": self.has_inspectometer_battery, | ||
|  |             "Act 3 - Tower Holo Pelt": self.has_gems_and_battery, | ||
|  |             "Act 3 - Trader 1": self.has_pelts(1), | ||
|  |             "Act 3 - Trader 2": self.has_pelts(2), | ||
|  |             "Act 3 - Trader 3": self.has_pelts(3), | ||
|  |             "Act 3 - Trader 4": self.has_pelts(4), | ||
|  |             "Act 3 - Trader 5": self.has_pelts(5), | ||
|  |             "Act 3 - Goobert's Painting": self.has_gems_and_battery, | ||
|  |             "Act 3 - The Great Transcendence": self.has_transcendence_requirements, | ||
|  |             "Act 3 - Boss Mycologists": self.has_mycologists_boss_requirements, | ||
|  |             "Act 3 - Bone Lord Room": self.has_bone_lord_room_requirements, | ||
|  |             "Act 3 - Luke's File Entry 1": self.has_battery_and_quill, | ||
|  |             "Act 3 - Luke's File Entry 2": self.has_battery_and_quill, | ||
|  |             "Act 3 - Luke's File Entry 3": self.has_battery_and_quill, | ||
|  |             "Act 3 - Luke's File Entry 4": self.has_transcendence_requirements, | ||
|  |             "Act 3 - Well": self.has_inspectometer_battery, | ||
|  |             "Act 3 - Gems Drone": self.has_inspectometer_battery, | ||
|  |             "Act 3 - Clock": self.has_gems_and_battery,  # Can be brute-forced, but the solution needs those items. | ||
|  |         } | ||
|  |         self.region_rules = { | ||
|  |             "Act 2": self.has_act2_requirements, | ||
|  |             "Act 3": self.has_act3_requirements, | ||
|  |             "Epilogue": self.has_epilogue_requirements | ||
|  |         } | ||
|  | 
 | ||
|  |     def has_wardrobe_key(self, state: CollectionState) -> bool: | ||
|  |         return state.has("Wardrobe Key", self.player) | ||
|  | 
 | ||
|  |     def has_caged_wolf(self, state: CollectionState) -> bool: | ||
|  |         return state.has("Caged Wolf Card", self.player) | ||
|  | 
 | ||
|  |     def has_dagger(self, state: CollectionState) -> bool: | ||
|  |         return state.has("Dagger", self.player) | ||
|  | 
 | ||
|  |     def has_magnificus_eye(self, state: CollectionState) -> bool: | ||
|  |         return state.has("Magnificus Eye", self.player) | ||
|  | 
 | ||
|  |     def has_useful_act1_items(self, state: CollectionState) -> bool: | ||
|  |         return state.has_all(("Oil Painting's Clover Plant", "Squirrel Totem Head"), self.player) | ||
|  | 
 | ||
|  |     def has_all_epitaph_pieces(self, state: CollectionState) -> bool: | ||
|  |         return state.has(self.world.required_epitaph_pieces_name, self.player, self.world.required_epitaph_pieces_count) | ||
|  | 
 | ||
|  |     def has_camera_and_meat(self, state: CollectionState) -> bool: | ||
|  |         return state.has_all(("Camera Replica", "Pile Of Meat"), self.player) | ||
|  | 
 | ||
|  |     def has_monocle(self, state: CollectionState) -> bool: | ||
|  |         return state.has("Monocle", self.player) | ||
|  | 
 | ||
|  |     def has_obol(self, state: CollectionState) -> bool: | ||
|  |         return state.has("Ancient Obol", self.player) | ||
|  | 
 | ||
|  |     def has_epitaphs_and_forest_items(self, state: CollectionState) -> bool: | ||
|  |         return self.has_camera_and_meat(state) and self.has_all_epitaph_pieces(state) | ||
|  | 
 | ||
|  |     def has_act2_bridge_requirements(self, state: CollectionState) -> bool: | ||
|  |         return self.has_camera_and_meat(state) or self.has_all_epitaph_pieces(state) | ||
|  | 
 | ||
|  |     def has_tower_requirements(self, state: CollectionState) -> bool: | ||
|  |         return self.has_monocle(state) and self.has_act2_bridge_requirements(state) | ||
|  | 
 | ||
|  |     def has_inspectometer_battery(self, state: CollectionState) -> bool: | ||
|  |         return state.has("Inspectometer Battery", self.player) | ||
|  | 
 | ||
|  |     def has_gems_and_battery(self, state: CollectionState) -> bool: | ||
|  |         return state.has("Gems Module", self.player) and self.has_inspectometer_battery(state) | ||
|  | 
 | ||
|  |     def has_pelts(self, count: int) -> Callable[[CollectionState], bool]: | ||
|  |         return lambda state: state.has("Holo Pelt", self.player, count) and self.has_gems_and_battery(state) | ||
|  | 
 | ||
|  |     def has_mycologists_boss_requirements(self, state: CollectionState) -> bool: | ||
|  |         return state.has("Mycologists Holo Key", self.player) and self.has_transcendence_requirements(state) | ||
|  | 
 | ||
|  |     def has_bone_lord_room_requirements(self, state: CollectionState) -> bool: | ||
|  |         return state.has("Bone Lord Holo Key", self.player) and self.has_inspectometer_battery(state) | ||
|  | 
 | ||
|  |     def has_battery_and_quill(self, state: CollectionState) -> bool: | ||
|  |         return state.has("Quill", self.player) and self.has_inspectometer_battery(state) | ||
|  | 
 | ||
|  |     def has_transcendence_requirements(self, state: CollectionState) -> bool: | ||
|  |         return state.has("Quill", self.player) and self.has_gems_and_battery(state) | ||
|  | 
 | ||
|  |     def has_act2_requirements(self, state: CollectionState) -> bool: | ||
|  |         return state.has("Film Roll", self.player) | ||
|  | 
 | ||
|  |     def has_act3_requirements(self, state: CollectionState) -> bool: | ||
|  |         return self.has_act2_requirements(state) and self.has_all_epitaph_pieces(state) and \ | ||
|  |             self.has_camera_and_meat(state) and self.has_monocle(state) | ||
|  | 
 | ||
|  |     def has_epilogue_requirements(self, state: CollectionState) -> bool: | ||
|  |         return self.has_act3_requirements(state) and self.has_transcendence_requirements(state) | ||
|  | 
 | ||
|  |     def set_all_rules(self) -> None: | ||
|  |         multiworld = self.world.multiworld | ||
|  |         if self.world.options.goal != Goal.option_first_act: | ||
|  |             multiworld.completion_condition[self.player] = self.has_epilogue_requirements | ||
|  |         else: | ||
|  |             multiworld.completion_condition[self.player] = self.has_act2_requirements | ||
|  |         for region in multiworld.get_regions(self.player): | ||
|  |             if self.world.options.goal == Goal.option_full_story_in_order: | ||
|  |                 if region.name in self.region_rules: | ||
|  |                     for entrance in region.entrances: | ||
|  |                         entrance.access_rule = self.region_rules[region.name] | ||
|  |             for loc in region.locations: | ||
|  |                 if loc.name in self.location_rules: | ||
|  |                     loc.access_rule = self.location_rules[loc.name] | ||
|  | 
 | ||
|  |         if self.world.options.painting_checks_balancing == PaintingChecksBalancing.option_balanced: | ||
|  |             self.world.get_location("Act 1 - Painting 2").access_rule = self.has_useful_act1_items | ||
|  |             self.world.get_location("Act 1 - Painting 3").access_rule = self.has_useful_act1_items | ||
|  |         elif self.world.options.painting_checks_balancing == PaintingChecksBalancing.option_force_filler: | ||
|  |             self.world.get_location("Act 1 - Painting 2").progress_type = LocationProgressType.EXCLUDED | ||
|  |             self.world.get_location("Act 1 - Painting 3").progress_type = LocationProgressType.EXCLUDED |