Core: add generic interface to add ER data to hints (#1014)
This commit is contained in:
parent
58f66e0f42
commit
c2d69cb05e
|
@ -955,6 +955,13 @@ class Region:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_connecting_entrance(self, is_main_entrance: typing.Callable[[Entrance], bool]) -> Entrance:
|
||||||
|
for entrance in self.entrances:
|
||||||
|
if is_main_entrance(entrance):
|
||||||
|
return entrance
|
||||||
|
for entrance in self.entrances: # BFS might be better here, trying DFS for now.
|
||||||
|
return entrance.parent_region.get_connecting_entrance(is_main_entrance)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.__str__()
|
return self.__str__()
|
||||||
|
|
||||||
|
|
29
Main.py
29
Main.py
|
@ -12,7 +12,7 @@ from typing import Dict, Tuple, Optional, Set
|
||||||
|
|
||||||
from BaseClasses import MultiWorld, CollectionState, Region, RegionType, LocationProgressType, Location
|
from BaseClasses import MultiWorld, CollectionState, Region, RegionType, LocationProgressType, Location
|
||||||
from worlds.alttp.Items import item_name_groups
|
from worlds.alttp.Items import item_name_groups
|
||||||
from worlds.alttp.Regions import lookup_vanilla_location_to_entrance
|
from worlds.alttp.Regions import is_main_entrance
|
||||||
from Fill import distribute_items_restrictive, flood_items, balance_multiworld_progression, distribute_planned
|
from Fill import distribute_items_restrictive, flood_items, balance_multiworld_progression, distribute_planned
|
||||||
from worlds.alttp.Shops import SHOP_ID_START, total_shop_slots, FillDisabledShopSlots
|
from worlds.alttp.Shops import SHOP_ID_START, total_shop_slots, FillDisabledShopSlots
|
||||||
from Utils import output_path, get_options, __version__, version_tuple
|
from Utils import output_path, get_options, __version__, version_tuple
|
||||||
|
@ -249,24 +249,9 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||||
output_file_futures.append(
|
output_file_futures.append(
|
||||||
pool.submit(AutoWorld.call_single, world, "generate_output", player, temp_dir))
|
pool.submit(AutoWorld.call_single, world, "generate_output", player, temp_dir))
|
||||||
|
|
||||||
def get_entrance_to_region(region: Region):
|
|
||||||
for entrance in region.entrances:
|
|
||||||
if entrance.parent_region.type in (RegionType.DarkWorld, RegionType.LightWorld, RegionType.Generic):
|
|
||||||
return entrance
|
|
||||||
for entrance in region.entrances: # BFS might be better here, trying DFS for now.
|
|
||||||
return get_entrance_to_region(entrance.parent_region)
|
|
||||||
|
|
||||||
# collect ER hint info
|
# collect ER hint info
|
||||||
er_hint_data = {player: {} for player in world.get_game_players("A Link to the Past") if
|
er_hint_data: Dict[int, Dict[int, str]] = {}
|
||||||
world.shuffle[player] != "vanilla" or world.retro_caves[player]}
|
AutoWorld.call_all(world, 'extend_hint_information', er_hint_data)
|
||||||
|
|
||||||
for region in world.regions:
|
|
||||||
if region.player in er_hint_data and region.locations:
|
|
||||||
main_entrance = get_entrance_to_region(region)
|
|
||||||
for location in region.locations:
|
|
||||||
if type(location.address) == int: # skips events and crystals
|
|
||||||
if lookup_vanilla_location_to_entrance[location.address] != main_entrance.name:
|
|
||||||
er_hint_data[region.player][location.address] = main_entrance.name
|
|
||||||
|
|
||||||
checks_in_area = {player: {area: list() for area in ordered_areas}
|
checks_in_area = {player: {area: list() for area in ordered_areas}
|
||||||
for player in range(1, world.players + 1)}
|
for player in range(1, world.players + 1)}
|
||||||
|
@ -276,10 +261,11 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||||
|
|
||||||
for location in world.get_filled_locations():
|
for location in world.get_filled_locations():
|
||||||
if type(location.address) is int:
|
if type(location.address) is int:
|
||||||
main_entrance = get_entrance_to_region(location.parent_region)
|
|
||||||
if location.game != "A Link to the Past":
|
if location.game != "A Link to the Past":
|
||||||
checks_in_area[location.player]["Light World"].append(location.address)
|
checks_in_area[location.player]["Light World"].append(location.address)
|
||||||
elif location.parent_region.dungeon:
|
else:
|
||||||
|
main_entrance = location.parent_region.get_connecting_entrance(is_main_entrance)
|
||||||
|
if location.parent_region.dungeon:
|
||||||
dungeonname = {'Inverted Agahnims Tower': 'Agahnims Tower',
|
dungeonname = {'Inverted Agahnims Tower': 'Agahnims Tower',
|
||||||
'Inverted Ganons Tower': 'Ganons Tower'} \
|
'Inverted Ganons Tower': 'Ganons Tower'} \
|
||||||
.get(location.parent_region.dungeon.name, location.parent_region.dungeon.name)
|
.get(location.parent_region.dungeon.name, location.parent_region.dungeon.name)
|
||||||
|
@ -305,7 +291,7 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||||
player = region.player
|
player = region.player
|
||||||
location_id = SHOP_ID_START + total_shop_slots + index
|
location_id = SHOP_ID_START + total_shop_slots + index
|
||||||
|
|
||||||
main_entrance = get_entrance_to_region(region)
|
main_entrance = region.get_connecting_entrance(is_main_entrance)
|
||||||
if main_entrance.parent_region.type == RegionType.LightWorld:
|
if main_entrance.parent_region.type == RegionType.LightWorld:
|
||||||
checks_in_area[player]["Light World"].append(location_id)
|
checks_in_area[player]["Light World"].append(location_id)
|
||||||
else:
|
else:
|
||||||
|
@ -340,7 +326,6 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||||
for player, world_precollected in world.precollected_items.items()}
|
for player, world_precollected in world.precollected_items.items()}
|
||||||
precollected_hints = {player: set() for player in range(1, world.players + 1 + len(world.groups))}
|
precollected_hints = {player: set() for player in range(1, world.players + 1 + len(world.groups))}
|
||||||
|
|
||||||
|
|
||||||
for slot in world.player_ids:
|
for slot in world.player_ids:
|
||||||
slot_data[slot] = world.worlds[slot].fill_slot_data()
|
slot_data[slot] = world.worlds[slot].fill_slot_data()
|
||||||
|
|
||||||
|
|
|
@ -240,6 +240,11 @@ class World(metaclass=AutoWorldRegister):
|
||||||
"""Fill in the slot_data field in the Connected network package."""
|
"""Fill in the slot_data field in the Connected network package."""
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]):
|
||||||
|
"""Fill in additional entrance information text into locations, which is displayed when hinted.
|
||||||
|
structure is {player_id: {location_id: text}} You will need to insert your own player_id."""
|
||||||
|
pass
|
||||||
|
|
||||||
def modify_multidata(self, multidata: Dict[str, Any]) -> None: # TODO: TypedDict for multidata?
|
def modify_multidata(self, multidata: Dict[str, Any]) -> None: # TODO: TypedDict for multidata?
|
||||||
"""For deeper modification of server multidata."""
|
"""For deeper modification of server multidata."""
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -4,6 +4,10 @@ import typing
|
||||||
from BaseClasses import Region, Entrance, RegionType
|
from BaseClasses import Region, Entrance, RegionType
|
||||||
|
|
||||||
|
|
||||||
|
def is_main_entrance(entrance: Entrance) -> bool:
|
||||||
|
return entrance.parent_region.type in {RegionType.DarkWorld, RegionType.LightWorld, RegionType.Generic}
|
||||||
|
|
||||||
|
|
||||||
def create_regions(world, player):
|
def create_regions(world, player):
|
||||||
|
|
||||||
world.regions += [
|
world.regions += [
|
||||||
|
|
|
@ -12,7 +12,8 @@ from .InvertedRegions import create_inverted_regions, mark_dark_world_regions
|
||||||
from .ItemPool import generate_itempool, difficulties
|
from .ItemPool import generate_itempool, difficulties
|
||||||
from .Items import item_init_table, item_name_groups, item_table, GetBeemizerItem
|
from .Items import item_init_table, item_name_groups, item_table, GetBeemizerItem
|
||||||
from .Options import alttp_options, smallkey_shuffle
|
from .Options import alttp_options, smallkey_shuffle
|
||||||
from .Regions import lookup_name_to_id, create_regions, mark_light_world_regions
|
from .Regions import lookup_name_to_id, create_regions, mark_light_world_regions, lookup_vanilla_location_to_entrance, \
|
||||||
|
is_main_entrance
|
||||||
from .Rom import LocalRom, patch_rom, patch_race_rom, check_enemizer, patch_enemizer, apply_rom_settings, \
|
from .Rom import LocalRom, patch_rom, patch_race_rom, check_enemizer, patch_enemizer, apply_rom_settings, \
|
||||||
get_hash_string, get_base_rom_path, LttPDeltaPatch
|
get_hash_string, get_base_rom_path, LttPDeltaPatch
|
||||||
from .Rules import set_rules
|
from .Rules import set_rules
|
||||||
|
@ -24,6 +25,7 @@ lttp_logger = logging.getLogger("A Link to the Past")
|
||||||
|
|
||||||
extras_list = sum(difficulties['normal'].extras[0:5], [])
|
extras_list = sum(difficulties['normal'].extras[0:5], [])
|
||||||
|
|
||||||
|
|
||||||
class ALTTPWeb(WebWorld):
|
class ALTTPWeb(WebWorld):
|
||||||
setup_en = Tutorial(
|
setup_en = Tutorial(
|
||||||
"Multiworld Setup Tutorial",
|
"Multiworld Setup Tutorial",
|
||||||
|
@ -410,6 +412,20 @@ class ALTTPWorld(World):
|
||||||
finally:
|
finally:
|
||||||
self.rom_name_available_event.set() # make sure threading continues and errors are collected
|
self.rom_name_available_event.set() # make sure threading continues and errors are collected
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def stage_extend_hint_information(cls, world, hint_data: typing.Dict[int, typing.Dict[int, str]]):
|
||||||
|
er_hint_data = {player: {} for player in world.get_game_players("A Link to the Past") if
|
||||||
|
world.shuffle[player] != "vanilla" or world.retro_caves[player]}
|
||||||
|
|
||||||
|
for region in world.regions:
|
||||||
|
if region.player in er_hint_data and region.locations:
|
||||||
|
main_entrance = region.get_connecting_entrance(is_main_entrance)
|
||||||
|
for location in region.locations:
|
||||||
|
if type(location.address) == int: # skips events and crystals
|
||||||
|
if lookup_vanilla_location_to_entrance[location.address] != main_entrance.name:
|
||||||
|
er_hint_data[region.player][location.address] = main_entrance.name
|
||||||
|
hint_data.update(er_hint_data)
|
||||||
|
|
||||||
def modify_multidata(self, multidata: dict):
|
def modify_multidata(self, multidata: dict):
|
||||||
import base64
|
import base64
|
||||||
# wait for self.rom_name to be available.
|
# wait for self.rom_name to be available.
|
||||||
|
|
Loading…
Reference in New Issue