LttP: Move more functionality into ALttPItem from Item

LttP: More efficiently build !hint entrance info
LttP: More efficiently check for and build Big Bomb Shop playthrough path
This commit is contained in:
Fabian Dill 2021-08-28 12:56:52 +02:00
parent de4775b0c8
commit de567cc701
5 changed files with 83 additions and 75 deletions

View File

@ -808,10 +808,6 @@ class Region(object):
return False
def can_fill(self, item: Item):
inside_dungeon_item = item.locked_dungeon_item
if inside_dungeon_item:
return self.dungeon.is_dungeon_item(item) and item.player == self.player
return True
def __repr__(self):
@ -973,15 +969,24 @@ class Location():
class Item():
location: Optional[Location] = None
world: Optional[MultiWorld] = None
code: Optional[str] = None # an item with ID None is called an Event, and does not get written to multidata
game: str = "Generic"
type: str = None
never_exclude = False # change manually to ensure that a specific nonprogression item never goes on an excluded location
# change manually to ensure that a specific non-progression item never goes on an excluded location
never_exclude = False
# need to find a decent place for these to live and to allow other games to register texts if they want.
pedestal_credit_text: str = "and the Unknown Item"
sickkid_credit_text: Optional[str] = None
magicshop_credit_text: Optional[str] = None
zora_credit_text: Optional[str] = None
fluteboy_credit_text: Optional[str] = None
code: Optional[str] = None # an item with ID None is called an Event, and does not get written to multidata
# hopefully temporary attributes to satisfy legacy LttP code, proper implementation in subclass ALttPItem
smallkey: bool = False
bigkey: bool = False
map: bool = False
compass: bool = False
def __init__(self, name: str, advancement: bool, code: Optional[int], player: int):
self.name = name
@ -1008,51 +1013,6 @@ class Item():
def __hash__(self):
return hash((self.name, self.player))
@property
def crystal(self) -> bool:
return self.type == 'Crystal'
@property
def smallkey(self) -> bool:
return self.type == 'SmallKey'
@property
def bigkey(self) -> bool:
return self.type == 'BigKey'
@property
def map(self) -> bool:
return self.type == 'Map'
@property
def compass(self) -> bool:
return self.type == 'Compass'
@property
def dungeon_item(self) -> Optional[str]:
if self.game == "A Link to the Past" and self.type in {"SmallKey", "BigKey", "Map", "Compass"}:
return self.type
@property
def shuffled_dungeon_item(self) -> bool:
dungeon_item_type = self.dungeon_item
if dungeon_item_type:
return {"SmallKey" : self.world.keyshuffle,
"BigKey": self.world.bigkeyshuffle,
"Map": self.world.mapshuffle,
"Compass": self.world.compassshuffle}[dungeon_item_type][self.player]
return False
@property
def locked_dungeon_item(self) -> bool:
dungeon_item_type = self.dungeon_item
if dungeon_item_type:
return not {"SmallKey" : self.world.keyshuffle,
"BigKey": self.world.bigkeyshuffle,
"Map": self.world.mapshuffle,
"Compass": self.world.compassshuffle}[dungeon_item_type][self.player]
return False
def __repr__(self):
return self.__str__()

46
Main.py
View File

@ -249,20 +249,21 @@ def main(args, seed=None):
for player in range(1, world.players + 1):
checks_in_area[player]["Total"] = 0
for location in [loc for loc in world.get_filled_locations() if type(loc.address) is int]:
main_entrance = get_entrance_to_region(location.parent_region)
if location.game != "A Link to the Past":
checks_in_area[location.player]["Light World"].append(location.address)
elif location.parent_region.dungeon:
dungeonname = {'Inverted Agahnims Tower': 'Agahnims Tower',
'Inverted Ganons Tower': 'Ganons Tower'} \
.get(location.parent_region.dungeon.name, location.parent_region.dungeon.name)
checks_in_area[location.player][dungeonname].append(location.address)
elif main_entrance.parent_region.type == RegionType.LightWorld:
checks_in_area[location.player]["Light World"].append(location.address)
elif main_entrance.parent_region.type == RegionType.DarkWorld:
checks_in_area[location.player]["Dark World"].append(location.address)
checks_in_area[location.player]["Total"] += 1
for location in world.get_filled_locations():
if type(location.address) is int:
main_entrance = get_entrance_to_region(location.parent_region)
if location.game != "A Link to the Past":
checks_in_area[location.player]["Light World"].append(location.address)
elif location.parent_region.dungeon:
dungeonname = {'Inverted Agahnims Tower': 'Agahnims Tower',
'Inverted Ganons Tower': 'Ganons Tower'} \
.get(location.parent_region.dungeon.name, location.parent_region.dungeon.name)
checks_in_area[location.player][dungeonname].append(location.address)
elif main_entrance.parent_region.type == RegionType.LightWorld:
checks_in_area[location.player]["Light World"].append(location.address)
elif main_entrance.parent_region.type == RegionType.DarkWorld:
checks_in_area[location.player]["Dark World"].append(location.address)
checks_in_area[location.player]["Total"] += 1
oldmancaves = []
takeanyregions = ["Old Man Sword Cave", "Take-Any #1", "Take-Any #2", "Take-Any #3", "Take-Any #4"]
@ -497,14 +498,15 @@ def create_playthrough(world):
{str(location): get_path(state, location.parent_region) for sphere in collection_spheres for location in
sphere if location.player == player})
if player in world.get_game_players("A Link to the Past"):
for path in dict(world.spoiler.paths).values():
if any(exit_path == 'Pyramid Fairy' for (_, exit_path) in path):
if world.mode[player] != 'inverted':
world.spoiler.paths[str(world.get_region('Big Bomb Shop', player))] = \
get_path(state,world.get_region('Big Bomb Shop', player))
else:
world.spoiler.paths[str(world.get_region('Inverted Big Bomb Shop', player))] = \
get_path(state,world.get_region('Inverted Big Bomb Shop', player))
# If Pyramid Fairy Entrance needs to be reached, also path to Big Bomb Shop
# Maybe move the big bomb over to the Event system instead?
if any(exit_path == 'Pyramid Fairy' for path in world.spoiler.paths.values() for (_, exit_path) in path):
if world.mode[player] != 'inverted':
world.spoiler.paths[str(world.get_region('Big Bomb Shop', player))] = \
get_path(state, world.get_region('Big Bomb Shop', player))
else:
world.spoiler.paths[str(world.get_region('Inverted Big Bomb Shop', player))] = \
get_path(state, world.get_region('Inverted Big Bomb Shop', player))
# we can finally output our playthrough
world.spoiler.playthrough = {"0": sorted([str(item) for item in world.precollected_items if item.advancement])}

View File

@ -13,7 +13,7 @@ class Version(typing.NamedTuple):
build: int
__version__ = "0.1.6"
__version__ = "0.1.7"
version_tuple = tuplize_version(__version__)
import builtins

View File

@ -29,4 +29,49 @@ class ALttPItem(Item):
self.zora_credit_text = zora_credit
self.magicshop_credit_text = witch_credit
self.fluteboy_credit_text = flute_boy_credit
self._hint_text = hint_text
self._hint_text = hint_text
@property
def crystal(self) -> bool:
return self.type == 'Crystal'
@property
def smallkey(self) -> bool:
return self.type == 'SmallKey'
@property
def bigkey(self) -> bool:
return self.type == 'BigKey'
@property
def map(self) -> bool:
return self.type == 'Map'
@property
def compass(self) -> bool:
return self.type == 'Compass'
@property
def dungeon_item(self) -> Optional[str]:
if self.game == "A Link to the Past" and self.type in {"SmallKey", "BigKey", "Map", "Compass"}:
return self.type
@property
def shuffled_dungeon_item(self) -> bool:
dungeon_item_type = self.dungeon_item
if dungeon_item_type:
return {"SmallKey" : self.world.keyshuffle,
"BigKey": self.world.bigkeyshuffle,
"Map": self.world.mapshuffle,
"Compass": self.world.compassshuffle}[dungeon_item_type][self.player]
return False
@property
def locked_dungeon_item(self) -> bool:
dungeon_item_type = self.dungeon_item
if dungeon_item_type:
return not {"SmallKey" : self.world.keyshuffle,
"BigKey": self.world.bigkeyshuffle,
"Map": self.world.mapshuffle,
"Compass": self.world.compassshuffle}[dungeon_item_type][self.player]
return False

View File

@ -62,6 +62,7 @@ class ALTTPWorld(World):
world.difficulty_requirements[player] = difficulties[world.difficulty[player]]
def create_regions(self):
# noinspection PyAttributeOutsideInit
self.rom_name_available_event = threading.Event()
player = self.player