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
|
import copy
|
||||||
from enum import Enum, unique
|
from enum import Enum, unique
|
||||||
import logging
|
import logging
|
||||||
|
@ -9,7 +11,12 @@ from Utils import int16_as_bytes
|
||||||
|
|
||||||
class World(object):
|
class World(object):
|
||||||
player_names: list
|
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.players = players
|
||||||
self.teams = 1
|
self.teams = 1
|
||||||
self.shuffle = shuffle.copy()
|
self.shuffle = shuffle.copy()
|
||||||
|
@ -94,11 +101,12 @@ class World(object):
|
||||||
set_player_attr('clock_mode', 'off')
|
set_player_attr('clock_mode', 'off')
|
||||||
set_player_attr('can_take_damage', True)
|
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)})'
|
return obj.name if self.players == 1 else f'{obj.name} ({self.get_player_names(obj.player)})'
|
||||||
|
|
||||||
def get_player_names(self, player):
|
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])
|
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):
|
def initialize_regions(self, regions=None):
|
||||||
for region in regions if regions else self.regions:
|
for region in regions if regions else self.regions:
|
||||||
|
@ -108,7 +116,7 @@ class World(object):
|
||||||
def get_regions(self, player=None):
|
def get_regions(self, player=None):
|
||||||
return self.regions if player is None else self._region_cache[player].values()
|
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):
|
if isinstance(regionname, Region):
|
||||||
return regionname
|
return regionname
|
||||||
try:
|
try:
|
||||||
|
@ -116,11 +124,11 @@ class World(object):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
for region in self.regions:
|
for region in self.regions:
|
||||||
if region.name == regionname and region.player == player:
|
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
|
return region
|
||||||
raise RuntimeError('No such region %s for player %d' % (regionname, player))
|
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):
|
if isinstance(entrance, Entrance):
|
||||||
return entrance
|
return entrance
|
||||||
try:
|
try:
|
||||||
|
@ -133,7 +141,7 @@ class World(object):
|
||||||
return exit
|
return exit
|
||||||
raise RuntimeError('No such entrance %s for player %d' % (entrance, player))
|
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):
|
if isinstance(location, Location):
|
||||||
return location
|
return location
|
||||||
try:
|
try:
|
||||||
|
@ -146,7 +154,7 @@ class World(object):
|
||||||
return r_location
|
return r_location
|
||||||
raise RuntimeError('No such location %s for player %d' % (location, player))
|
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):
|
if isinstance(dungeonname, Dungeon):
|
||||||
return dungeonname
|
return dungeonname
|
||||||
|
|
||||||
|
@ -155,7 +163,7 @@ class World(object):
|
||||||
return dungeon
|
return dungeon
|
||||||
raise RuntimeError('No such dungeon %s for player %d' % (dungeonname, player))
|
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)
|
ret = CollectionState(self)
|
||||||
|
|
||||||
def soft_collect(item):
|
def soft_collect(item):
|
||||||
|
@ -163,9 +171,11 @@ class World(object):
|
||||||
if 'Sword' in item.name:
|
if 'Sword' in item.name:
|
||||||
if ret.has('Golden Sword', item.player):
|
if ret.has('Golden Sword', item.player):
|
||||||
pass
|
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))
|
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))
|
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:
|
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))
|
ret.prog_items.add(('Master Sword', item.player))
|
||||||
|
@ -214,20 +224,21 @@ class World(object):
|
||||||
ret.sweep_for_events()
|
ret.sweep_for_events()
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_items(self):
|
def get_items(self) -> list:
|
||||||
return [loc.item for loc in self.get_filled_locations()] + self.itempool
|
return [loc.item for loc in self.get_filled_locations()] + self.itempool
|
||||||
|
|
||||||
def find_items(self, item, 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]
|
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
|
item.world = self
|
||||||
if (item.smallkey and self.keyshuffle[item.player]) or (item.bigkey and self.bigkeyshuffle[item.player]):
|
if (item.smallkey and self.keyshuffle[item.player]) or (item.bigkey and self.bigkeyshuffle[item.player]):
|
||||||
item.advancement = True
|
item.advancement = True
|
||||||
self.precollected_items.append(item)
|
self.precollected_items.append(item)
|
||||||
self.state.collect(item, True)
|
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):
|
if not isinstance(location, Location):
|
||||||
raise RuntimeError('Cannot assign item %s to location %s (player %d).' % (item, location, item.player))
|
raise RuntimeError('Cannot assign item %s to location %s (player %d).' % (item, location, item.player))
|
||||||
|
|
||||||
|
@ -242,7 +253,7 @@ class World(object):
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('Cannot assign item %s to location %s.' % (item, location))
|
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:
|
if self._cached_entrances is None:
|
||||||
self._cached_entrances = []
|
self._cached_entrances = []
|
||||||
for region in self.regions:
|
for region in self.regions:
|
||||||
|
@ -252,7 +263,7 @@ class World(object):
|
||||||
def clear_entrance_cache(self):
|
def clear_entrance_cache(self):
|
||||||
self._cached_entrances = None
|
self._cached_entrances = None
|
||||||
|
|
||||||
def get_locations(self):
|
def get_locations(self) -> list:
|
||||||
if self._cached_locations is None:
|
if self._cached_locations is None:
|
||||||
self._cached_locations = []
|
self._cached_locations = []
|
||||||
for region in self.regions:
|
for region in self.regions:
|
||||||
|
@ -262,23 +273,27 @@ class World(object):
|
||||||
def clear_location_cache(self):
|
def clear_location_cache(self):
|
||||||
self._cached_locations = None
|
self._cached_locations = None
|
||||||
|
|
||||||
def get_unfilled_locations(self, player=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]
|
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):
|
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]
|
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:
|
if state is None:
|
||||||
state = self.state
|
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:
|
if state is None:
|
||||||
state = self.state
|
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 = self.state.copy()
|
||||||
temp_state.collect(item, True)
|
temp_state.collect(item, True)
|
||||||
|
|
||||||
|
@ -327,7 +342,7 @@ class World(object):
|
||||||
|
|
||||||
class CollectionState(object):
|
class CollectionState(object):
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent: World):
|
||||||
self.prog_items = bag()
|
self.prog_items = bag()
|
||||||
self.world = parent
|
self.world = parent
|
||||||
self.reachable_regions = {player: set() for player in range(1, parent.players + 1)}
|
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:
|
for item in parent.precollected_items:
|
||||||
self.collect(item, True)
|
self.collect(item, True)
|
||||||
|
|
||||||
def update_reachable_regions(self, player):
|
def update_reachable_regions(self, player: int):
|
||||||
player_regions = self.world.get_regions(player)
|
player_regions = self.world.get_regions(player)
|
||||||
self.stale[player] = False
|
self.stale[player] = False
|
||||||
rrp = self.reachable_regions[player]
|
rrp = self.reachable_regions[player]
|
||||||
|
@ -352,10 +367,11 @@ class CollectionState(object):
|
||||||
new_regions = len(rrp) > reachable_regions_count
|
new_regions = len(rrp) > reachable_regions_count
|
||||||
reachable_regions_count = len(rrp)
|
reachable_regions_count = len(rrp)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self) -> CollectionState:
|
||||||
ret = CollectionState(self.world)
|
ret = CollectionState(self.world)
|
||||||
ret.prog_items = self.prog_items.copy()
|
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.events = copy.copy(self.events)
|
||||||
ret.path = copy.copy(self.path)
|
ret.path = copy.copy(self.path)
|
||||||
ret.locations_checked = copy.copy(self.locations_checked)
|
ret.locations_checked = copy.copy(self.locations_checked)
|
||||||
|
@ -405,46 +421,46 @@ class CollectionState(object):
|
||||||
return (item, player) in self.prog_items
|
return (item, player) in self.prog_items
|
||||||
return self.prog_items.count((item, player)) >= count
|
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:
|
for shop in self.world.shops:
|
||||||
if shop.region.player == player and shop.has_unlimited(item) and shop.region.can_reach(self):
|
if shop.region.player == player and shop.has_unlimited(item) and shop.region.can_reach(self):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def item_count(self, item, player):
|
def item_count(self, item, player: int) -> int:
|
||||||
return self.prog_items.count((item, player))
|
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']
|
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
|
return len([crystal for crystal in crystals if self.has(crystal, player)]) >= count
|
||||||
|
|
||||||
def can_lift_rocks(self, player):
|
def can_lift_rocks(self, player):
|
||||||
return self.has('Power Glove', player) or self.has('Titans Mitts', 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
|
return self.bottle_count(player) > 0
|
||||||
|
|
||||||
def bottle_count(self, 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])
|
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
|
# Warning: This only considers items that are marked as advancement items
|
||||||
return self.heart_count(player) >= count
|
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
|
# Warning: This only considers items that are marked as advancement items
|
||||||
diff = self.world.difficulty_requirements[player]
|
diff = self.world.difficulty_requirements[player]
|
||||||
return (
|
return min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit) \
|
||||||
min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit)
|
+ self.item_count('Sanctuary Heart Container', player) \
|
||||||
+ self.item_count('Sanctuary Heart Container', player)
|
+ min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4 \
|
||||||
+ min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4
|
+ 3 # starting hearts
|
||||||
+ 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)
|
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
|
basemagic = 8
|
||||||
if self.has('Quarter Magic', player):
|
if self.has('Quarter Magic', player):
|
||||||
basemagic = 32
|
basemagic = 32
|
||||||
|
@ -459,85 +475,88 @@ class CollectionState(object):
|
||||||
basemagic = basemagic + basemagic * self.bottle_count(player)
|
basemagic = basemagic + basemagic * self.bottle_count(player)
|
||||||
return basemagic >= smallmagic
|
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)
|
return (self.has_blunt_weapon(player)
|
||||||
or self.has('Cane of Somaria', 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.has('Cane of Byrna', player) and (enemies < 6 or self.can_extend_magic(player)))
|
||||||
or self.can_shoot_arrows(player)
|
or self.can_shoot_arrows(player)
|
||||||
or self.has('Fire Rod', 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]:
|
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)
|
# 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
|
# 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) and (
|
||||||
|
self.has('Silver Arrows', player) or self.can_buy_unlimited('Single Arrow', player))
|
||||||
return self.has('Bow', 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)
|
cave = self.world.get_region('Good Bee Cave', player)
|
||||||
return (
|
return (
|
||||||
self.has_bottle(player) and
|
self.has_bottle(player) and
|
||||||
self.has('Bug Catching Net', player) and
|
self.has('Bug Catching Net', player) and
|
||||||
(self.has_Boots(player) or (self.has_sword(player) and self.has('Quake', player))) and
|
(self.has_Boots(player) or (self.has_sword(player) and self.has('Quake', player))) and
|
||||||
cave.can_reach(self) and
|
cave.can_reach(self) and
|
||||||
self.is_not_bunny(cave, player)
|
self.is_not_bunny(cave, player)
|
||||||
)
|
)
|
||||||
|
|
||||||
def has_sword(self, 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)
|
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)
|
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)
|
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)
|
return self.has('Magic Mirror', player)
|
||||||
|
|
||||||
def has_Boots(self, player):
|
def has_Boots(self, player: int) -> bool:
|
||||||
return self.has('Pegasus Boots', player)
|
return self.has('Pegasus Boots', player)
|
||||||
|
|
||||||
def has_Pearl(self, player):
|
def has_Pearl(self, player: int) -> bool:
|
||||||
return self.has('Moon Pearl', player)
|
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)
|
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)
|
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)
|
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))
|
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)
|
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):
|
if self.has_Pearl(player):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return region.is_light_world if self.world.mode[player] != 'inverted' else region.is_dark_world
|
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]]:
|
if True in [i.is_light_world for i in self.reachable_regions[player]]:
|
||||||
return True
|
return True
|
||||||
return False
|
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]]:
|
if True in [i.is_dark_world for i in self.reachable_regions[player]]:
|
||||||
return True
|
return True
|
||||||
return False
|
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)
|
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)
|
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:
|
if location:
|
||||||
self.locations_checked.add(location)
|
self.locations_checked.add(location)
|
||||||
changed = False
|
changed = False
|
||||||
|
@ -545,7 +564,8 @@ class CollectionState(object):
|
||||||
if 'Sword' in item.name:
|
if 'Sword' in item.name:
|
||||||
if self.has('Golden Sword', item.player):
|
if self.has('Golden Sword', item.player):
|
||||||
pass
|
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))
|
self.prog_items.add(('Golden Sword', item.player))
|
||||||
changed = True
|
changed = True
|
||||||
elif self.has('Master Sword', item.player) and self.world.difficulty_requirements[item.player].progressive_sword_limit >= 3:
|
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):
|
class Region(object):
|
||||||
|
|
||||||
def __init__(self, name, type, hint, player):
|
def __init__(self, name: str, type, hint, player: int):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.type = type
|
self.type = type
|
||||||
self.entrances = []
|
self.entrances = []
|
||||||
|
@ -684,7 +704,7 @@ class Region(object):
|
||||||
self.dungeon = None
|
self.dungeon = None
|
||||||
self.shop = None
|
self.shop = None
|
||||||
self.world = 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.is_dark_world = False
|
||||||
self.spot_type = 'Region'
|
self.spot_type = 'Region'
|
||||||
self.hint_text = hint
|
self.hint_text = hint
|
||||||
|
@ -724,7 +744,7 @@ class Region(object):
|
||||||
|
|
||||||
class Entrance(object):
|
class Entrance(object):
|
||||||
|
|
||||||
def __init__(self, player, name='', parent=None):
|
def __init__(self, player: int, name: str = '', parent=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.parent_region = parent
|
self.parent_region = parent
|
||||||
self.connected_region = None
|
self.connected_region = None
|
||||||
|
@ -760,7 +780,7 @@ class Entrance(object):
|
||||||
|
|
||||||
class Dungeon(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.name = name
|
||||||
self.regions = regions
|
self.regions = regions
|
||||||
self.big_key = big_key
|
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})'
|
return self.world.get_name_string_for_object(self) if self.world else f'{self.name} (Player {self.player})'
|
||||||
|
|
||||||
class Boss(object):
|
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.name = name
|
||||||
self.enemizer_name = enemizer_name
|
self.enemizer_name = enemizer_name
|
||||||
self.defeat_rule = defeat_rule
|
self.defeat_rule = defeat_rule
|
||||||
self.player = player
|
self.player = player
|
||||||
|
|
||||||
def can_defeat(self, state):
|
def can_defeat(self, state) -> bool:
|
||||||
return self.defeat_rule(state, self.player)
|
return self.defeat_rule(state, self.player)
|
||||||
|
|
||||||
class Location(object):
|
class Location(object):
|
||||||
|
@ -824,10 +844,11 @@ class Location(object):
|
||||||
self.item_rule = lambda item: True
|
self.item_rule = lambda item: True
|
||||||
self.player = player
|
self.player = player
|
||||||
|
|
||||||
def can_fill(self, state, item, check_access=True):
|
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)))
|
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):
|
if self.parent_region.can_reach(state) and self.access_rule(state):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -860,23 +881,23 @@ class Item(object):
|
||||||
self.player = player
|
self.player = player
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def crystal(self):
|
def crystal(self) -> bool:
|
||||||
return self.type == 'Crystal'
|
return self.type == 'Crystal'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def smallkey(self):
|
def smallkey(self) -> bool:
|
||||||
return self.type == 'SmallKey'
|
return self.type == 'SmallKey'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bigkey(self):
|
def bigkey(self) -> bool:
|
||||||
return self.type == 'BigKey'
|
return self.type == 'BigKey'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def map(self):
|
def map(self) -> bool:
|
||||||
return self.type == 'Map'
|
return self.type == 'Map'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def compass(self):
|
def compass(self) -> bool:
|
||||||
return self.type == 'Compass'
|
return self.type == 'Compass'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
Loading…
Reference in New Issue