204 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			204 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								from typing import Dict, TYPE_CHECKING
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from BaseClasses import CollectionState
							 | 
						||
| 
								 | 
							
								from worlds.generic.Rules import CollectionRule
							 | 
						||
| 
								 | 
							
								from .data import iname, lname
							 | 
						||
| 
								 | 
							
								from .options import CompletionGoal, IronMaidenBehavior
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if TYPE_CHECKING:
							 | 
						||
| 
								 | 
							
								    from . import CVCotMWorld
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class CVCotMRules:
							 | 
						||
| 
								 | 
							
								    player: int
							 | 
						||
| 
								 | 
							
								    world: "CVCotMWorld"
							 | 
						||
| 
								 | 
							
								    rules: Dict[str, CollectionRule]
							 | 
						||
| 
								 | 
							
								    required_last_keys: int
							 | 
						||
| 
								 | 
							
								    iron_maiden_behavior: int
							 | 
						||
| 
								 | 
							
								    nerf_roc_wing: int
							 | 
						||
| 
								 | 
							
								    ignore_cleansing: int
							 | 
						||
| 
								 | 
							
								    completion_goal: int
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, world: "CVCotMWorld") -> None:
							 | 
						||
| 
								 | 
							
								        self.player = world.player
							 | 
						||
| 
								 | 
							
								        self.world = world
							 | 
						||
| 
								 | 
							
								        self.required_last_keys = world.required_last_keys
							 | 
						||
| 
								 | 
							
								        self.iron_maiden_behavior = world.options.iron_maiden_behavior.value
							 | 
						||
| 
								 | 
							
								        self.nerf_roc_wing = world.options.nerf_roc_wing.value
							 | 
						||
| 
								 | 
							
								        self.ignore_cleansing = world.options.ignore_cleansing.value
							 | 
						||
| 
								 | 
							
								        self.completion_goal = world.options.completion_goal.value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.location_rules = {
							 | 
						||
| 
								 | 
							
								            # Sealed Room
							 | 
						||
| 
								 | 
							
								            lname.sr3: self.has_jump_level_5,
							 | 
						||
| 
								 | 
							
								            # Catacomb
							 | 
						||
| 
								 | 
							
								            lname.cc1: self.has_push,
							 | 
						||
| 
								 | 
							
								            lname.cc3: self.has_jump_level_1,
							 | 
						||
| 
								 | 
							
								            lname.cc3b: lambda state:
							 | 
						||
| 
								 | 
							
								                (self.has_jump_level_1(state) and self.has_ice_or_stone(state)) or self.has_jump_level_4(state),
							 | 
						||
| 
								 | 
							
								            lname.cc5: self.has_tackle,
							 | 
						||
| 
								 | 
							
								            lname.cc8b: lambda state: self.has_jump_level_3(state) or self.has_kick(state),
							 | 
						||
| 
								 | 
							
								            lname.cc14b: lambda state: self.has_jump_level_1(state) or self.has_kick(state),
							 | 
						||
| 
								 | 
							
								            lname.cc25: self.has_jump_level_1,
							 | 
						||
| 
								 | 
							
								            # Abyss Staircase
							 | 
						||
| 
								 | 
							
								            lname.as4: self.has_jump_level_4,
							 | 
						||
| 
								 | 
							
								            # Audience Room
							 | 
						||
| 
								 | 
							
								            lname.ar9: self.has_push,
							 | 
						||
| 
								 | 
							
								            lname.ar11: self.has_tackle,
							 | 
						||
| 
								 | 
							
								            lname.ar14b: self.has_jump_level_4,
							 | 
						||
| 
								 | 
							
								            lname.ar17b: lambda state: self.has_jump_level_2(state) or self.has_kick(state),
							 | 
						||
| 
								 | 
							
								            lname.ar19: lambda state: self.has_jump_level_2(state) or self.has_kick(state),
							 | 
						||
| 
								 | 
							
								            lname.ar26: lambda state: self.has_tackle(state) and self.has_jump_level_5(state),
							 | 
						||
| 
								 | 
							
								            lname.ar27: lambda state: self.has_tackle(state) and self.has_push(state),
							 | 
						||
| 
								 | 
							
								            lname.ar30: lambda state:
							 | 
						||
| 
								 | 
							
								                (self.has_jump_level_3(state) and self.has_ice_or_stone(state)) or self.has_jump_level_4(state),
							 | 
						||
| 
								 | 
							
								            lname.ar30b: lambda state:
							 | 
						||
| 
								 | 
							
								                (self.has_jump_level_3(state) and self.has_ice_or_stone(state)) or self.has_jump_level_4(state),
							 | 
						||
| 
								 | 
							
								            # Outer Wall
							 | 
						||
| 
								 | 
							
								            lname.ow0: self.has_jump_level_4,
							 | 
						||
| 
								 | 
							
								            lname.ow1: lambda state: self.has_jump_level_5(state) or self.has_ice_or_stone(state),
							 | 
						||
| 
								 | 
							
								            # Triumph Hallway
							 | 
						||
| 
								 | 
							
								            lname.th3: lambda state:
							 | 
						||
| 
								 | 
							
								                (self.has_kick(state) and self.has_ice_or_stone(state)) or self.has_jump_level_2(state),
							 | 
						||
| 
								 | 
							
								            # Machine Tower
							 | 
						||
| 
								 | 
							
								            lname.mt3: lambda state: self.has_jump_level_2(state) or self.has_kick(state),
							 | 
						||
| 
								 | 
							
								            lname.mt6: lambda state: self.has_jump_level_2(state) or self.has_kick(state),
							 | 
						||
| 
								 | 
							
								            lname.mt14: self.has_tackle,
							 | 
						||
| 
								 | 
							
								            # Chapel Tower
							 | 
						||
| 
								 | 
							
								            lname.ct1: lambda state: self.has_jump_level_2(state) or self.has_ice_or_stone(state),
							 | 
						||
| 
								 | 
							
								            lname.ct4: self.has_push,
							 | 
						||
| 
								 | 
							
								            lname.ct10: self.has_push,
							 | 
						||
| 
								 | 
							
								            lname.ct13: lambda state: self.has_jump_level_2(state) or self.has_ice_or_stone(state),
							 | 
						||
| 
								 | 
							
								            lname.ct22: self.broke_iron_maidens,
							 | 
						||
| 
								 | 
							
								            lname.ct26: lambda state:
							 | 
						||
| 
								 | 
							
								                (self.has_jump_level_3(state) and self.has_ice_or_stone(state)) or self.has_jump_level_4(state),
							 | 
						||
| 
								 | 
							
								            lname.ct26b: lambda state:
							 | 
						||
| 
								 | 
							
								                (self.has_jump_level_3(state) and self.has_ice_or_stone(state)) or self.has_jump_level_4(state),
							 | 
						||
| 
								 | 
							
								            # Underground Gallery
							 | 
						||
| 
								 | 
							
								            lname.ug1: self.has_push,
							 | 
						||
| 
								 | 
							
								            lname.ug2: self.has_push,
							 | 
						||
| 
								 | 
							
								            lname.ug3: lambda state: self.has_jump_level_2(state) or self.has_ice_or_stone(state),
							 | 
						||
| 
								 | 
							
								            lname.ug3b: lambda state: self.has_jump_level_4(state) or self.has_ice_or_stone(state),
							 | 
						||
| 
								 | 
							
								            lname.ug8: self.has_tackle,
							 | 
						||
| 
								 | 
							
								            # Underground Warehouse
							 | 
						||
| 
								 | 
							
								            lname.uw10: lambda state:
							 | 
						||
| 
								 | 
							
								                (self.has_jump_level_4(state) and self.has_ice_or_stone(state)) or self.has_jump_level_5(state),
							 | 
						||
| 
								 | 
							
								            lname.uw14: lambda state: self.has_jump_level_2(state) or self.has_ice_or_stone(state),
							 | 
						||
| 
								 | 
							
								            lname.uw16b: lambda state:
							 | 
						||
| 
								 | 
							
								                (self.has_jump_level_2(state) and self.has_ice_or_stone(state)) or self.has_jump_level_3(state),
							 | 
						||
| 
								 | 
							
								            # Underground Waterway
							 | 
						||
| 
								 | 
							
								            lname.uy5: lambda state: self.has_jump_level_3(state) or self.has_ice_or_stone(state),
							 | 
						||
| 
								 | 
							
								            lname.uy8: self.has_jump_level_2,
							 | 
						||
| 
								 | 
							
								            lname.uy12b: self.can_touch_water,
							 | 
						||
| 
								 | 
							
								            lname.uy17: self.can_touch_water,
							 | 
						||
| 
								 | 
							
								            lname.uy13: self.has_jump_level_3,
							 | 
						||
| 
								 | 
							
								            lname.uy18: self.has_jump_level_3,
							 | 
						||
| 
								 | 
							
								            # Ceremonial Room
							 | 
						||
| 
								 | 
							
								            lname.cr1: lambda state: self.has_jump_level_2(state) or self.has_kick(state),
							 | 
						||
| 
								 | 
							
								            lname.dracula: self.has_jump_level_2,
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.entrance_rules = {
							 | 
						||
| 
								 | 
							
								            "Catacomb to Stairway": lambda state: self.has_jump_level_1(state) or self.has_kick(state),
							 | 
						||
| 
								 | 
							
								            "Stairway to Audience": self.has_jump_level_1,
							 | 
						||
| 
								 | 
							
								            "Audience to Machine Bottom": self.has_tackle,
							 | 
						||
| 
								 | 
							
								            "Audience to Machine Top": lambda state: self.has_jump_level_2(state) or self.has_kick(state),
							 | 
						||
| 
								 | 
							
								            "Audience to Chapel": lambda state:
							 | 
						||
| 
								 | 
							
								                (self.has_jump_level_2(state) and self.has_ice_or_stone(state)) or self.has_jump_level_3(state)
							 | 
						||
| 
								 | 
							
								                or self.has_kick(state),
							 | 
						||
| 
								 | 
							
								            "Audience to Gallery": lambda state: self.broke_iron_maidens(state) and self.has_push(state),
							 | 
						||
| 
								 | 
							
								            "Audience to Warehouse": self.has_push,
							 | 
						||
| 
								 | 
							
								            "Audience to Waterway": self.broke_iron_maidens,
							 | 
						||
| 
								 | 
							
								            "Audience to Observation": self.has_jump_level_5,
							 | 
						||
| 
								 | 
							
								            "Ceremonial Door": self.can_open_ceremonial_door,
							 | 
						||
| 
								 | 
							
								            "Corridor to Gallery": self.broke_iron_maidens,
							 | 
						||
| 
								 | 
							
								            "Escape the Gallery Pit": lambda state: self.has_jump_level_2(state) or self.has_kick(state),
							 | 
						||
| 
								 | 
							
								            "Climb to Chapel Top": lambda state: self.has_jump_level_3(state) or self.has_kick(state),
							 | 
						||
| 
								 | 
							
								            "Arena Passage": lambda state: self.has_push(state) and self.has_jump_level_2(state),
							 | 
						||
| 
								 | 
							
								            "Dip Into Waterway End": self.has_jump_level_3,
							 | 
						||
| 
								 | 
							
								            "Gallery Upper to Lower": self.has_tackle,
							 | 
						||
| 
								 | 
							
								            "Gallery Lower to Upper": self.has_tackle,
							 | 
						||
| 
								 | 
							
								            "Into Warehouse Main": self.has_tackle,
							 | 
						||
| 
								 | 
							
								            "Into Waterway Main": self.can_touch_water,
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def has_jump_level_1(self, state: CollectionState) -> bool:
							 | 
						||
| 
								 | 
							
								        """Double or Roc Wing, regardless of Roc being nerfed or not."""
							 | 
						||
| 
								 | 
							
								        return state.has_any([iname.double, iname.roc_wing], self.player)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def has_jump_level_2(self, state: CollectionState) -> bool:
							 | 
						||
| 
								 | 
							
								        """Specifically Roc Wing, regardless of Roc being nerfed or not."""
							 | 
						||
| 
								 | 
							
								        return state.has(iname.roc_wing, self.player)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def has_jump_level_3(self, state: CollectionState) -> bool:
							 | 
						||
| 
								 | 
							
								        """Roc Wing and Double OR Kick Boots if Roc is nerfed. Otherwise, just Roc."""
							 | 
						||
| 
								 | 
							
								        if self.nerf_roc_wing:
							 | 
						||
| 
								 | 
							
								            return state.has(iname.roc_wing, self.player) and \
							 | 
						||
| 
								 | 
							
								                   state.has_any([iname.double, iname.kick_boots], self.player)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return state.has(iname.roc_wing, self.player)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def has_jump_level_4(self, state: CollectionState) -> bool:
							 | 
						||
| 
								 | 
							
								        """Roc Wing and Kick Boots specifically if Roc is nerfed. Otherwise, just Roc."""
							 | 
						||
| 
								 | 
							
								        if self.nerf_roc_wing:
							 | 
						||
| 
								 | 
							
								            return state.has_all([iname.roc_wing, iname.kick_boots], self.player)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return state.has(iname.roc_wing, self.player)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def has_jump_level_5(self, state: CollectionState) -> bool:
							 | 
						||
| 
								 | 
							
								        """Roc Wing, Double, AND Kick Boots if Roc is nerfed. Otherwise, just Roc."""
							 | 
						||
| 
								 | 
							
								        if self.nerf_roc_wing:
							 | 
						||
| 
								 | 
							
								            return state.has_all([iname.roc_wing, iname.double, iname.kick_boots], self.player)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return state.has(iname.roc_wing, self.player)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def has_tackle(self, state: CollectionState) -> bool:
							 | 
						||
| 
								 | 
							
								        return state.has(iname.tackle, self.player)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def has_push(self, state: CollectionState) -> bool:
							 | 
						||
| 
								 | 
							
								        return state.has(iname.heavy_ring, self.player)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def has_kick(self, state: CollectionState) -> bool:
							 | 
						||
| 
								 | 
							
								        return state.has(iname.kick_boots, self.player)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def has_ice_or_stone(self, state: CollectionState) -> bool:
							 | 
						||
| 
								 | 
							
								        """Valid DSS combo that allows freezing or petrifying enemies to use as platforms."""
							 | 
						||
| 
								 | 
							
								        return state.has_any([iname.serpent, iname.cockatrice], self.player) and \
							 | 
						||
| 
								 | 
							
								            state.has_any([iname.mercury, iname.mars], self.player)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def can_touch_water(self, state: CollectionState) -> bool:
							 | 
						||
| 
								 | 
							
								        """Cleansing unless it's ignored, in which case this will always return True."""
							 | 
						||
| 
								 | 
							
								        return self.ignore_cleansing or state.has(iname.cleansing, self.player)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def broke_iron_maidens(self, state: CollectionState) -> bool:
							 | 
						||
| 
								 | 
							
								        """Maiden Detonator unless the Iron Maidens start broken, in which case this will always return True."""
							 | 
						||
| 
								 | 
							
								        return (self.iron_maiden_behavior == IronMaidenBehavior.option_start_broken
							 | 
						||
| 
								 | 
							
								                or state.has(iname.ironmaidens, self.player))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def can_open_ceremonial_door(self, state: CollectionState) -> bool:
							 | 
						||
| 
								 | 
							
								        """The required number of Last Keys. If 0 keys are required, this should always return True."""
							 | 
						||
| 
								 | 
							
								        return state.has(iname.last_key, self.player, self.required_last_keys)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def set_cvcotm_rules(self) -> None:
							 | 
						||
| 
								 | 
							
								        multiworld = self.world.multiworld
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for region in multiworld.get_regions(self.player):
							 | 
						||
| 
								 | 
							
								            # Set each Entrance's rule if it should have one.
							 | 
						||
| 
								 | 
							
								            for ent in region.entrances:
							 | 
						||
| 
								 | 
							
								                if ent.name in self.entrance_rules:
							 | 
						||
| 
								 | 
							
								                    ent.access_rule = self.entrance_rules[ent.name]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # Set each Location's rule if it should have one.
							 | 
						||
| 
								 | 
							
								            for loc in region.locations:
							 | 
						||
| 
								 | 
							
								                if loc.name in self.location_rules:
							 | 
						||
| 
								 | 
							
								                    loc.access_rule = self.location_rules[loc.name]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Set the World's completion condition depending on what its Completion Goal option is.
							 | 
						||
| 
								 | 
							
								        if self.completion_goal == CompletionGoal.option_dracula:
							 | 
						||
| 
								 | 
							
								            multiworld.completion_condition[self.player] = lambda state: state.has(iname.dracula, self.player)
							 | 
						||
| 
								 | 
							
								        elif self.completion_goal == CompletionGoal.option_battle_arena:
							 | 
						||
| 
								 | 
							
								            multiworld.completion_condition[self.player] = lambda state: state.has(iname.shinning_armor, self.player)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            multiworld.completion_condition[self.player] = \
							 | 
						||
| 
								 | 
							
								                lambda state: state.has_all([iname.dracula, iname.shinning_armor], self.player)
							 |