add some annotations to BaseClasses.py
This commit is contained in:
		
							parent
							
								
									80fa9f4c58
								
							
						
					
					
						commit
						260e156316
					
				
							
								
								
									
										205
									
								
								BaseClasses.py
								
								
								
								
							
							
						
						
									
										205
									
								
								BaseClasses.py
								
								
								
								
							| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
from __future__ import annotations
 | 
			
		||||
 | 
			
		||||
import copy
 | 
			
		||||
from enum import Enum, unique
 | 
			
		||||
import logging
 | 
			
		||||
| 
						 | 
				
			
			@ -9,7 +11,12 @@ from Utils import int16_as_bytes
 | 
			
		|||
 | 
			
		||||
class World(object):
 | 
			
		||||
    player_names: list
 | 
			
		||||
    def __init__(self, players, shuffle, logic, mode, swords, difficulty, difficulty_adjustments, timer, progressive, goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints):
 | 
			
		||||
    _region_cache: dict
 | 
			
		||||
    difficulty_requirements: dict
 | 
			
		||||
    required_medallions: dict
 | 
			
		||||
 | 
			
		||||
    def __init__(self, players, shuffle, logic, mode, swords, difficulty, difficulty_adjustments, timer, progressive,
 | 
			
		||||
                 goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints):
 | 
			
		||||
        self.players = players
 | 
			
		||||
        self.teams = 1
 | 
			
		||||
        self.shuffle = shuffle.copy()
 | 
			
		||||
| 
						 | 
				
			
			@ -94,11 +101,12 @@ class World(object):
 | 
			
		|||
            set_player_attr('clock_mode', 'off')
 | 
			
		||||
            set_player_attr('can_take_damage', True)
 | 
			
		||||
 | 
			
		||||
    def get_name_string_for_object(self, obj):
 | 
			
		||||
    def get_name_string_for_object(self, obj) -> str:
 | 
			
		||||
        return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_names(obj.player)})'
 | 
			
		||||
 | 
			
		||||
    def get_player_names(self, player):
 | 
			
		||||
        return ", ".join([name for i, name in enumerate(self.player_names[player]) if self.player_names[player].index(name) == i])
 | 
			
		||||
    def get_player_names(self, player) -> str:
 | 
			
		||||
        return ", ".join(
 | 
			
		||||
            [name for i, name in enumerate(self.player_names[player]) if self.player_names[player].index(name) == i])
 | 
			
		||||
 | 
			
		||||
    def initialize_regions(self, regions=None):
 | 
			
		||||
        for region in regions if regions else self.regions:
 | 
			
		||||
| 
						 | 
				
			
			@ -108,7 +116,7 @@ class World(object):
 | 
			
		|||
    def get_regions(self, player=None):
 | 
			
		||||
        return self.regions if player is None else self._region_cache[player].values()
 | 
			
		||||
 | 
			
		||||
    def get_region(self, regionname, player):
 | 
			
		||||
    def get_region(self, regionname, player: int) -> Region:
 | 
			
		||||
        if isinstance(regionname, Region):
 | 
			
		||||
            return regionname
 | 
			
		||||
        try:
 | 
			
		||||
| 
						 | 
				
			
			@ -116,11 +124,11 @@ class World(object):
 | 
			
		|||
        except KeyError:
 | 
			
		||||
            for region in self.regions:
 | 
			
		||||
                if region.name == regionname and region.player == player:
 | 
			
		||||
                    assert not region.world # this should only happen before initialization
 | 
			
		||||
                    assert not region.world  # this should only happen before initialization
 | 
			
		||||
                    return region
 | 
			
		||||
            raise RuntimeError('No such region %s for player %d' % (regionname, player))
 | 
			
		||||
 | 
			
		||||
    def get_entrance(self, entrance, player):
 | 
			
		||||
    def get_entrance(self, entrance, player: int) -> Entrance:
 | 
			
		||||
        if isinstance(entrance, Entrance):
 | 
			
		||||
            return entrance
 | 
			
		||||
        try:
 | 
			
		||||
| 
						 | 
				
			
			@ -133,7 +141,7 @@ class World(object):
 | 
			
		|||
                        return exit
 | 
			
		||||
            raise RuntimeError('No such entrance %s for player %d' % (entrance, player))
 | 
			
		||||
 | 
			
		||||
    def get_location(self, location, player):
 | 
			
		||||
    def get_location(self, location, player: int) -> Location:
 | 
			
		||||
        if isinstance(location, Location):
 | 
			
		||||
            return location
 | 
			
		||||
        try:
 | 
			
		||||
| 
						 | 
				
			
			@ -146,7 +154,7 @@ class World(object):
 | 
			
		|||
                        return r_location
 | 
			
		||||
        raise RuntimeError('No such location %s for player %d' % (location, player))
 | 
			
		||||
 | 
			
		||||
    def get_dungeon(self, dungeonname, player):
 | 
			
		||||
    def get_dungeon(self, dungeonname, player: int) -> Dungeon:
 | 
			
		||||
        if isinstance(dungeonname, Dungeon):
 | 
			
		||||
            return dungeonname
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -155,7 +163,7 @@ class World(object):
 | 
			
		|||
                return dungeon
 | 
			
		||||
        raise RuntimeError('No such dungeon %s for player %d' % (dungeonname, player))
 | 
			
		||||
 | 
			
		||||
    def get_all_state(self, keys=False):
 | 
			
		||||
    def get_all_state(self, keys=False) -> CollectionState:
 | 
			
		||||
        ret = CollectionState(self)
 | 
			
		||||
 | 
			
		||||
        def soft_collect(item):
 | 
			
		||||
| 
						 | 
				
			
			@ -163,9 +171,11 @@ class World(object):
 | 
			
		|||
                if 'Sword' in item.name:
 | 
			
		||||
                    if ret.has('Golden Sword', item.player):
 | 
			
		||||
                        pass
 | 
			
		||||
                    elif ret.has('Tempered Sword', item.player) and self.difficulty_requirements[item.player].progressive_sword_limit >= 4:
 | 
			
		||||
                    elif ret.has('Tempered Sword', item.player) and self.difficulty_requirements[
 | 
			
		||||
                        item.player].progressive_sword_limit >= 4:
 | 
			
		||||
                        ret.prog_items.add(('Golden Sword', item.player))
 | 
			
		||||
                    elif ret.has('Master Sword', item.player) and self.difficulty_requirements[item.player].progressive_sword_limit >= 3:
 | 
			
		||||
                    elif ret.has('Master Sword', item.player) and self.difficulty_requirements[
 | 
			
		||||
                        item.player].progressive_sword_limit >= 3:
 | 
			
		||||
                        ret.prog_items.add(('Tempered Sword', item.player))
 | 
			
		||||
                    elif ret.has('Fighter Sword', item.player) and self.difficulty_requirements[item.player].progressive_sword_limit >= 2:
 | 
			
		||||
                        ret.prog_items.add(('Master Sword', item.player))
 | 
			
		||||
| 
						 | 
				
			
			@ -214,20 +224,21 @@ class World(object):
 | 
			
		|||
        ret.sweep_for_events()
 | 
			
		||||
        return ret
 | 
			
		||||
 | 
			
		||||
    def get_items(self):
 | 
			
		||||
    def get_items(self) -> list:
 | 
			
		||||
        return [loc.item for loc in self.get_filled_locations()] + self.itempool
 | 
			
		||||
 | 
			
		||||
    def find_items(self, item, player):
 | 
			
		||||
        return [location for location in self.get_locations() if location.item is not None and location.item.name == item and location.item.player == player]
 | 
			
		||||
    def find_items(self, item, player: int) -> list:
 | 
			
		||||
        return [location for location in self.get_locations() if
 | 
			
		||||
                location.item is not None and location.item.name == item and location.item.player == player]
 | 
			
		||||
 | 
			
		||||
    def push_precollected(self, item):
 | 
			
		||||
    def push_precollected(self, item: Item):
 | 
			
		||||
        item.world = self
 | 
			
		||||
        if (item.smallkey and self.keyshuffle[item.player]) or (item.bigkey and self.bigkeyshuffle[item.player]):
 | 
			
		||||
            item.advancement = True
 | 
			
		||||
        self.precollected_items.append(item)
 | 
			
		||||
        self.state.collect(item, True)
 | 
			
		||||
 | 
			
		||||
    def push_item(self, location, item, collect=True):
 | 
			
		||||
    def push_item(self, location: Location, item: Item, collect: bool = True):
 | 
			
		||||
        if not isinstance(location, Location):
 | 
			
		||||
            raise RuntimeError('Cannot assign item %s to location %s (player %d).' % (item, location, item.player))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -242,7 +253,7 @@ class World(object):
 | 
			
		|||
        else:
 | 
			
		||||
            raise RuntimeError('Cannot assign item %s to location %s.' % (item, location))
 | 
			
		||||
 | 
			
		||||
    def get_entrances(self):
 | 
			
		||||
    def get_entrances(self) -> list:
 | 
			
		||||
        if self._cached_entrances is None:
 | 
			
		||||
            self._cached_entrances = []
 | 
			
		||||
            for region in self.regions:
 | 
			
		||||
| 
						 | 
				
			
			@ -252,7 +263,7 @@ class World(object):
 | 
			
		|||
    def clear_entrance_cache(self):
 | 
			
		||||
        self._cached_entrances = None
 | 
			
		||||
 | 
			
		||||
    def get_locations(self):
 | 
			
		||||
    def get_locations(self) -> list:
 | 
			
		||||
        if self._cached_locations is None:
 | 
			
		||||
            self._cached_locations = []
 | 
			
		||||
            for region in self.regions:
 | 
			
		||||
| 
						 | 
				
			
			@ -262,23 +273,27 @@ class World(object):
 | 
			
		|||
    def clear_location_cache(self):
 | 
			
		||||
        self._cached_locations = None
 | 
			
		||||
 | 
			
		||||
    def get_unfilled_locations(self, player=None):
 | 
			
		||||
        return [location for location in self.get_locations() if (player is None or location.player == player) and location.item is None]
 | 
			
		||||
    def get_unfilled_locations(self, player=None) -> list:
 | 
			
		||||
        return [location for location in self.get_locations() if
 | 
			
		||||
                (player is None or location.player == player) and location.item is None]
 | 
			
		||||
 | 
			
		||||
    def get_filled_locations(self, player=None):
 | 
			
		||||
        return [location for location in self.get_locations() if (player is None or location.player == player) and location.item is not None]
 | 
			
		||||
    def get_filled_locations(self, player=None) -> list:
 | 
			
		||||
        return [location for location in self.get_locations() if
 | 
			
		||||
                (player is None or location.player == player) and location.item is not None]
 | 
			
		||||
 | 
			
		||||
    def get_reachable_locations(self, state=None, player=None):
 | 
			
		||||
    def get_reachable_locations(self, state=None, player=None) -> list:
 | 
			
		||||
        if state is None:
 | 
			
		||||
            state = self.state
 | 
			
		||||
        return [location for location in self.get_locations() if (player is None or location.player == player) and location.can_reach(state)]
 | 
			
		||||
        return [location for location in self.get_locations() if
 | 
			
		||||
                (player is None or location.player == player) and location.can_reach(state)]
 | 
			
		||||
 | 
			
		||||
    def get_placeable_locations(self, state=None, player=None):
 | 
			
		||||
    def get_placeable_locations(self, state=None, player=None) -> list:
 | 
			
		||||
        if state is None:
 | 
			
		||||
            state = self.state
 | 
			
		||||
        return [location for location in self.get_locations() if (player is None or location.player == player) and location.item is None and location.can_reach(state)]
 | 
			
		||||
        return [location for location in self.get_locations() if
 | 
			
		||||
                (player is None or location.player == player) and location.item is None and location.can_reach(state)]
 | 
			
		||||
 | 
			
		||||
    def unlocks_new_location(self, item):
 | 
			
		||||
    def unlocks_new_location(self, item) -> bool:
 | 
			
		||||
        temp_state = self.state.copy()
 | 
			
		||||
        temp_state.collect(item, True)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -327,7 +342,7 @@ class World(object):
 | 
			
		|||
 | 
			
		||||
class CollectionState(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent):
 | 
			
		||||
    def __init__(self, parent: World):
 | 
			
		||||
        self.prog_items = bag()
 | 
			
		||||
        self.world = parent
 | 
			
		||||
        self.reachable_regions = {player: set() for player in range(1, parent.players + 1)}
 | 
			
		||||
| 
						 | 
				
			
			@ -338,7 +353,7 @@ class CollectionState(object):
 | 
			
		|||
        for item in parent.precollected_items:
 | 
			
		||||
            self.collect(item, True)
 | 
			
		||||
 | 
			
		||||
    def update_reachable_regions(self, player):
 | 
			
		||||
    def update_reachable_regions(self, player: int):
 | 
			
		||||
        player_regions = self.world.get_regions(player)
 | 
			
		||||
        self.stale[player] = False
 | 
			
		||||
        rrp = self.reachable_regions[player]
 | 
			
		||||
| 
						 | 
				
			
			@ -352,10 +367,11 @@ class CollectionState(object):
 | 
			
		|||
            new_regions = len(rrp) > reachable_regions_count
 | 
			
		||||
            reachable_regions_count = len(rrp)
 | 
			
		||||
 | 
			
		||||
    def copy(self):
 | 
			
		||||
    def copy(self) -> CollectionState:
 | 
			
		||||
        ret = CollectionState(self.world)
 | 
			
		||||
        ret.prog_items = self.prog_items.copy()
 | 
			
		||||
        ret.reachable_regions = {player: copy.copy(self.reachable_regions[player]) for player in range(1, self.world.players + 1)}
 | 
			
		||||
        ret.reachable_regions = {player: copy.copy(self.reachable_regions[player]) for player in
 | 
			
		||||
                                 range(1, self.world.players + 1)}
 | 
			
		||||
        ret.events = copy.copy(self.events)
 | 
			
		||||
        ret.path = copy.copy(self.path)
 | 
			
		||||
        ret.locations_checked = copy.copy(self.locations_checked)
 | 
			
		||||
| 
						 | 
				
			
			@ -405,46 +421,46 @@ class CollectionState(object):
 | 
			
		|||
            return (item, player) in self.prog_items
 | 
			
		||||
        return self.prog_items.count((item, player)) >= count
 | 
			
		||||
 | 
			
		||||
    def can_buy_unlimited(self, item, player):
 | 
			
		||||
    def can_buy_unlimited(self, item: str, player: int) -> bool:
 | 
			
		||||
        for shop in self.world.shops:
 | 
			
		||||
            if shop.region.player == player and shop.has_unlimited(item) and shop.region.can_reach(self):
 | 
			
		||||
                return True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def item_count(self, item, player):
 | 
			
		||||
    def item_count(self, item, player: int) -> int:
 | 
			
		||||
        return self.prog_items.count((item, player))
 | 
			
		||||
 | 
			
		||||
    def has_crystals(self, count, player):
 | 
			
		||||
    def has_crystals(self, count: int, player: int) -> bool:
 | 
			
		||||
        crystals = ['Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7']
 | 
			
		||||
        return len([crystal for crystal in crystals if self.has(crystal, player)]) >= count
 | 
			
		||||
 | 
			
		||||
    def can_lift_rocks(self, player):
 | 
			
		||||
        return self.has('Power Glove', player) or self.has('Titans Mitts', player)
 | 
			
		||||
 | 
			
		||||
    def has_bottle(self, player):
 | 
			
		||||
    def has_bottle(self, player: int) -> bool:
 | 
			
		||||
        return self.bottle_count(player) > 0
 | 
			
		||||
 | 
			
		||||
    def bottle_count(self, player):
 | 
			
		||||
        return len([item for (item, itemplayer) in self.prog_items if item.startswith('Bottle') and itemplayer == player])
 | 
			
		||||
    def bottle_count(self, player: int) -> int:
 | 
			
		||||
        return len(
 | 
			
		||||
            [item for (item, itemplayer) in self.prog_items if item.startswith('Bottle') and itemplayer == player])
 | 
			
		||||
 | 
			
		||||
    def has_hearts(self, player, count):
 | 
			
		||||
    def has_hearts(self, player: int, count: int) -> int:
 | 
			
		||||
        # Warning: This only considers items that are marked as advancement items
 | 
			
		||||
        return self.heart_count(player) >= count
 | 
			
		||||
 | 
			
		||||
    def heart_count(self, player):
 | 
			
		||||
    def heart_count(self, player: int) -> int:
 | 
			
		||||
        # Warning: This only considers items that are marked as advancement items
 | 
			
		||||
        diff = self.world.difficulty_requirements[player]
 | 
			
		||||
        return (
 | 
			
		||||
            min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit)
 | 
			
		||||
            + self.item_count('Sanctuary Heart Container', player)
 | 
			
		||||
            + min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4
 | 
			
		||||
            + 3 # starting hearts
 | 
			
		||||
        )
 | 
			
		||||
        return min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit) \
 | 
			
		||||
               + self.item_count('Sanctuary Heart Container', player) \
 | 
			
		||||
               + min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4 \
 | 
			
		||||
               + 3  # starting hearts
 | 
			
		||||
 | 
			
		||||
    def can_lift_heavy_rocks(self, player):
 | 
			
		||||
    def can_lift_heavy_rocks(self, player: int) -> bool:
 | 
			
		||||
        return self.has('Titans Mitts', player)
 | 
			
		||||
 | 
			
		||||
    def can_extend_magic(self, player, smallmagic=16, fullrefill=False): #This reflects the total magic Link has, not the total extra he has.
 | 
			
		||||
    def can_extend_magic(self, player: int, smallmagic: int = 16,
 | 
			
		||||
                         fullrefill: bool = False):  # This reflects the total magic Link has, not the total extra he has.
 | 
			
		||||
        basemagic = 8
 | 
			
		||||
        if self.has('Quarter Magic', player):
 | 
			
		||||
            basemagic = 32
 | 
			
		||||
| 
						 | 
				
			
			@ -459,85 +475,88 @@ class CollectionState(object):
 | 
			
		|||
                basemagic = basemagic + basemagic * self.bottle_count(player)
 | 
			
		||||
        return basemagic >= smallmagic
 | 
			
		||||
 | 
			
		||||
    def can_kill_most_things(self, player, enemies=5):
 | 
			
		||||
    def can_kill_most_things(self, player: int, enemies=5) -> bool:
 | 
			
		||||
        return (self.has_blunt_weapon(player)
 | 
			
		||||
                or self.has('Cane of Somaria', player)
 | 
			
		||||
                or (self.has('Cane of Byrna', player) and (enemies < 6 or self.can_extend_magic(player)))
 | 
			
		||||
                or self.can_shoot_arrows(player)
 | 
			
		||||
                or self.has('Fire Rod', player)
 | 
			
		||||
               )
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
    def can_shoot_arrows(self, player):
 | 
			
		||||
    def can_shoot_arrows(self, player: int) -> bool:
 | 
			
		||||
        if self.world.retro[player]:
 | 
			
		||||
            #TODO: need to decide how we want to handle wooden arrows  longer-term (a can-buy-a check, or via dynamic shop location)
 | 
			
		||||
            #FIXME: Should do something about hard+ ganon only silvers. For the moment, i believe they effective grant wooden, so we are safe
 | 
			
		||||
            return self.has('Bow', player) and (self.has('Silver Arrows', player) or self.can_buy_unlimited('Single Arrow', player))
 | 
			
		||||
            # TODO: need to decide how we want to handle wooden arrows  longer-term (a can-buy-a check, or via dynamic shop location)
 | 
			
		||||
            # FIXME: Should do something about hard+ ganon only silvers. For the moment, i believe they effective grant wooden, so we are safe
 | 
			
		||||
            return self.has('Bow', player) and (
 | 
			
		||||
                        self.has('Silver Arrows', player) or self.can_buy_unlimited('Single Arrow', player))
 | 
			
		||||
        return self.has('Bow', player)
 | 
			
		||||
 | 
			
		||||
    def can_get_good_bee(self, player):
 | 
			
		||||
    def can_get_good_bee(self, player: int) -> bool:
 | 
			
		||||
        cave = self.world.get_region('Good Bee Cave', player)
 | 
			
		||||
        return (
 | 
			
		||||
            self.has_bottle(player) and
 | 
			
		||||
            self.has('Bug Catching Net', player) and
 | 
			
		||||
            (self.has_Boots(player) or (self.has_sword(player) and self.has('Quake', player))) and
 | 
			
		||||
            cave.can_reach(self) and
 | 
			
		||||
            self.is_not_bunny(cave, player)
 | 
			
		||||
                self.has_bottle(player) and
 | 
			
		||||
                self.has('Bug Catching Net', player) and
 | 
			
		||||
                (self.has_Boots(player) or (self.has_sword(player) and self.has('Quake', player))) and
 | 
			
		||||
                cave.can_reach(self) and
 | 
			
		||||
                self.is_not_bunny(cave, player)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def has_sword(self, player):
 | 
			
		||||
        return self.has('Fighter Sword', player) or self.has('Master Sword', player) or self.has('Tempered Sword', player) or self.has('Golden Sword', player)
 | 
			
		||||
    def has_sword(self, player: int) -> bool:
 | 
			
		||||
        return self.has('Fighter Sword', player) or self.has('Master Sword', player) or self.has('Tempered Sword',
 | 
			
		||||
                                                                                                 player) or self.has(
 | 
			
		||||
            'Golden Sword', player)
 | 
			
		||||
 | 
			
		||||
    def has_beam_sword(self, player):
 | 
			
		||||
    def has_beam_sword(self, player: int) -> bool:
 | 
			
		||||
        return self.has('Master Sword', player) or self.has('Tempered Sword', player) or self.has('Golden Sword', player)
 | 
			
		||||
 | 
			
		||||
    def has_blunt_weapon(self, player):
 | 
			
		||||
    def has_blunt_weapon(self, player: int) -> bool:
 | 
			
		||||
        return self.has_sword(player) or self.has('Hammer', player)
 | 
			
		||||
 | 
			
		||||
    def has_Mirror(self, player):
 | 
			
		||||
    def has_Mirror(self, player: int) -> bool:
 | 
			
		||||
        return self.has('Magic Mirror', player)
 | 
			
		||||
 | 
			
		||||
    def has_Boots(self, player):
 | 
			
		||||
    def has_Boots(self, player: int) -> bool:
 | 
			
		||||
        return self.has('Pegasus Boots', player)
 | 
			
		||||
 | 
			
		||||
    def has_Pearl(self, player):
 | 
			
		||||
    def has_Pearl(self, player: int) -> bool:
 | 
			
		||||
        return self.has('Moon Pearl', player)
 | 
			
		||||
 | 
			
		||||
    def has_fire_source(self, player):
 | 
			
		||||
    def has_fire_source(self, player: int) -> bool:
 | 
			
		||||
        return self.has('Fire Rod', player) or self.has('Lamp', player)
 | 
			
		||||
 | 
			
		||||
    def can_flute(self, player):
 | 
			
		||||
    def can_flute(self, player: int) -> bool:
 | 
			
		||||
        lw = self.world.get_region('Light World', player)
 | 
			
		||||
        return self.has('Flute', player) and lw.can_reach(self) and self.is_not_bunny(lw, player)
 | 
			
		||||
 | 
			
		||||
    def can_melt_things(self, player):
 | 
			
		||||
    def can_melt_things(self, player: int) -> bool:
 | 
			
		||||
        return self.has('Fire Rod', player) or (self.has('Bombos', player) and self.has_sword(player))
 | 
			
		||||
 | 
			
		||||
    def can_avoid_lasers(self, player):
 | 
			
		||||
    def can_avoid_lasers(self, player: int) -> bool:
 | 
			
		||||
        return self.has('Mirror Shield', player) or self.has('Cane of Byrna', player) or self.has('Cape', player)
 | 
			
		||||
 | 
			
		||||
    def is_not_bunny(self, region, player):
 | 
			
		||||
    def is_not_bunny(self, region: Region, player: int) -> bool:
 | 
			
		||||
        if self.has_Pearl(player):
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        return region.is_light_world if self.world.mode[player] != 'inverted' else region.is_dark_world
 | 
			
		||||
 | 
			
		||||
    def can_reach_light_world(self, player):
 | 
			
		||||
    def can_reach_light_world(self, player: int) -> bool:
 | 
			
		||||
        if True in [i.is_light_world for i in self.reachable_regions[player]]:
 | 
			
		||||
            return True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def can_reach_dark_world(self, player):
 | 
			
		||||
    def can_reach_dark_world(self, player: int) -> bool:
 | 
			
		||||
        if True in [i.is_dark_world for i in self.reachable_regions[player]]:
 | 
			
		||||
            return True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def has_misery_mire_medallion(self, player):
 | 
			
		||||
    def has_misery_mire_medallion(self, player: int) -> bool:
 | 
			
		||||
        return self.has(self.world.required_medallions[player][0], player)
 | 
			
		||||
 | 
			
		||||
    def has_turtle_rock_medallion(self, player):
 | 
			
		||||
    def has_turtle_rock_medallion(self, player: int) -> bool:
 | 
			
		||||
        return self.has(self.world.required_medallions[player][1], player)
 | 
			
		||||
 | 
			
		||||
    def collect(self, item, event=False, location=None):
 | 
			
		||||
    def collect(self, item: Item, event=False, location=None):
 | 
			
		||||
        if location:
 | 
			
		||||
            self.locations_checked.add(location)
 | 
			
		||||
        changed = False
 | 
			
		||||
| 
						 | 
				
			
			@ -545,7 +564,8 @@ class CollectionState(object):
 | 
			
		|||
            if 'Sword' in item.name:
 | 
			
		||||
                if self.has('Golden Sword', item.player):
 | 
			
		||||
                    pass
 | 
			
		||||
                elif self.has('Tempered Sword', item.player) and self.world.difficulty_requirements[item.player].progressive_sword_limit >= 4:
 | 
			
		||||
                elif self.has('Tempered Sword', item.player) and self.world.difficulty_requirements[
 | 
			
		||||
                    item.player].progressive_sword_limit >= 4:
 | 
			
		||||
                    self.prog_items.add(('Golden Sword', item.player))
 | 
			
		||||
                    changed = True
 | 
			
		||||
                elif self.has('Master Sword', item.player) and self.world.difficulty_requirements[item.player].progressive_sword_limit >= 3:
 | 
			
		||||
| 
						 | 
				
			
			@ -675,7 +695,7 @@ class RegionType(Enum):
 | 
			
		|||
 | 
			
		||||
class Region(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, name, type, hint, player):
 | 
			
		||||
    def __init__(self, name: str, type, hint, player: int):
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.type = type
 | 
			
		||||
        self.entrances = []
 | 
			
		||||
| 
						 | 
				
			
			@ -684,7 +704,7 @@ class Region(object):
 | 
			
		|||
        self.dungeon = None
 | 
			
		||||
        self.shop = None
 | 
			
		||||
        self.world = None
 | 
			
		||||
        self.is_light_world = False # will be set aftermaking connections.
 | 
			
		||||
        self.is_light_world = False  # will be set aftermaking connections.
 | 
			
		||||
        self.is_dark_world = False
 | 
			
		||||
        self.spot_type = 'Region'
 | 
			
		||||
        self.hint_text = hint
 | 
			
		||||
| 
						 | 
				
			
			@ -724,7 +744,7 @@ class Region(object):
 | 
			
		|||
 | 
			
		||||
class Entrance(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, player, name='', parent=None):
 | 
			
		||||
    def __init__(self, player: int, name: str = '', parent=None):
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.parent_region = parent
 | 
			
		||||
        self.connected_region = None
 | 
			
		||||
| 
						 | 
				
			
			@ -760,7 +780,7 @@ class Entrance(object):
 | 
			
		|||
 | 
			
		||||
class Dungeon(object):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, name, regions, big_key, small_keys, dungeon_items, player):
 | 
			
		||||
    def __init__(self, name, regions, big_key, small_keys, dungeon_items, player: int):
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.regions = regions
 | 
			
		||||
        self.big_key = big_key
 | 
			
		||||
| 
						 | 
				
			
			@ -796,13 +816,13 @@ class Dungeon(object):
 | 
			
		|||
        return self.world.get_name_string_for_object(self) if self.world else f'{self.name} (Player {self.player})'
 | 
			
		||||
 | 
			
		||||
class Boss(object):
 | 
			
		||||
    def __init__(self, name, enemizer_name, defeat_rule, player):
 | 
			
		||||
    def __init__(self, name, enemizer_name, defeat_rule, player: int):
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.enemizer_name = enemizer_name
 | 
			
		||||
        self.defeat_rule = defeat_rule
 | 
			
		||||
        self.player = player
 | 
			
		||||
 | 
			
		||||
    def can_defeat(self, state):
 | 
			
		||||
    def can_defeat(self, state) -> bool:
 | 
			
		||||
        return self.defeat_rule(state, self.player)
 | 
			
		||||
 | 
			
		||||
class Location(object):
 | 
			
		||||
| 
						 | 
				
			
			@ -824,10 +844,11 @@ class Location(object):
 | 
			
		|||
        self.item_rule = lambda item: True
 | 
			
		||||
        self.player = player
 | 
			
		||||
 | 
			
		||||
    def can_fill(self, state, item, check_access=True):
 | 
			
		||||
        return self.always_allow(state, item) or (self.parent_region.can_fill(item) and self.item_rule(item) and (not check_access or self.can_reach(state)))
 | 
			
		||||
    def can_fill(self, state, item, check_access=True) -> bool:
 | 
			
		||||
        return self.always_allow(state, item) or (self.parent_region.can_fill(item) and self.item_rule(item) and (
 | 
			
		||||
                    not check_access or self.can_reach(state)))
 | 
			
		||||
 | 
			
		||||
    def can_reach(self, state):
 | 
			
		||||
    def can_reach(self, state) -> bool:
 | 
			
		||||
        if self.parent_region.can_reach(state) and self.access_rule(state):
 | 
			
		||||
            return True
 | 
			
		||||
        return False
 | 
			
		||||
| 
						 | 
				
			
			@ -860,23 +881,23 @@ class Item(object):
 | 
			
		|||
        self.player = player
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def crystal(self):
 | 
			
		||||
    def crystal(self) -> bool:
 | 
			
		||||
        return self.type == 'Crystal'
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def smallkey(self):
 | 
			
		||||
    def smallkey(self) -> bool:
 | 
			
		||||
        return self.type == 'SmallKey'
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def bigkey(self):
 | 
			
		||||
    def bigkey(self) -> bool:
 | 
			
		||||
        return self.type == 'BigKey'
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def map(self):
 | 
			
		||||
    def map(self) -> bool:
 | 
			
		||||
        return self.type == 'Map'
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def compass(self):
 | 
			
		||||
    def compass(self) -> bool:
 | 
			
		||||
        return self.type == 'Compass'
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue