diff --git a/Main.py b/Main.py index 78ae39e9..9804e6bd 100644 --- a/Main.py +++ b/Main.py @@ -7,7 +7,7 @@ import tempfile import time import zipfile import zlib -from typing import Dict, List, Optional, Set, Tuple +from typing import Dict, List, Optional, Set, Tuple, Union import worlds from BaseClasses import CollectionState, Item, Location, LocationProgressType, MultiWorld, Region @@ -15,19 +15,10 @@ from Fill import balance_multiworld_progression, distribute_items_restrictive, d from Options import StartInventoryPool from Utils import __version__, get_options, output_path, version_tuple from worlds import AutoWorld -from worlds.alttp.Regions import is_main_entrance -from worlds.alttp.Shops import FillDisabledShopSlots -from worlds.alttp.SubClasses import LTTPRegionType from worlds.generic.Rules import exclusion_rules, locality_rules __all__ = ["main"] -ordered_areas = ( - 'Light World', 'Dark World', 'Hyrule Castle', 'Agahnims Tower', 'Eastern Palace', 'Desert Palace', - 'Tower of Hera', 'Palace of Darkness', 'Swamp Palace', 'Skull Woods', 'Thieves Town', 'Ice Palace', - 'Misery Mire', 'Turtle Rock', 'Ganons Tower', "Total" -) - def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = None): if not baked_server_options: @@ -313,35 +304,6 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No er_hint_data: Dict[int, Dict[int, str]] = {} AutoWorld.call_all(world, 'extend_hint_information', er_hint_data) - 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): - checks_in_area[player]["Total"] = 0 - - for location in world.get_filled_locations(): - if type(location.address) is int: - if location.game != "A Link to the Past": - checks_in_area[location.player]["Light World"].append(location.address) - else: - main_entrance = location.parent_region.get_connecting_entrance(is_main_entrance) - if 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 location.parent_region.type == LTTPRegionType.LightWorld: - checks_in_area[location.player]["Light World"].append(location.address) - elif location.parent_region.type == LTTPRegionType.DarkWorld: - checks_in_area[location.player]["Dark World"].append(location.address) - elif main_entrance.parent_region.type == LTTPRegionType.LightWorld: - checks_in_area[location.player]["Light World"].append(location.address) - elif main_entrance.parent_region.type == LTTPRegionType.DarkWorld: - checks_in_area[location.player]["Dark World"].append(location.address) - checks_in_area[location.player]["Total"] += 1 - - FillDisabledShopSlots(world) - def write_multidata(): import NetUtils slot_data = {} @@ -401,6 +363,8 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No for game_world in world.worlds.values() } + checks_in_area: Dict[int, Dict[str, Union[int, List[int]]]] = {} + multidata = { "slot_data": slot_data, "slot_info": slot_info, diff --git a/WebHostLib/templates/hintTable.html b/WebHostLib/templates/hintTable.html new file mode 100644 index 00000000..00b74111 --- /dev/null +++ b/WebHostLib/templates/hintTable.html @@ -0,0 +1,28 @@ +{% for team, hints in hints.items() %} +
+ + + + + + + + + + + + + {%- for hint in hints -%} + + + + + + + + + {%- endfor -%} + +
FinderReceiverItemLocationEntranceFound
{{ long_player_names[team, hint.finding_player] }}{{ long_player_names[team, hint.receiving_player] }}{{ hint.item|item_name }}{{ hint.location|location_name }}{% if hint.entrance %}{{ hint.entrance }}{% else %}Vanilla{% endif %}{% if hint.found %}✔{% endif %}
+
+{% endfor %} \ No newline at end of file diff --git a/WebHostLib/templates/lttpMultiTracker.html b/WebHostLib/templates/lttpMultiTracker.html index 276e1de3..2b943a22 100644 --- a/WebHostLib/templates/lttpMultiTracker.html +++ b/WebHostLib/templates/lttpMultiTracker.html @@ -128,20 +128,30 @@ tracked_team=team, tracked_player=player)}}">{{ loop.index }} {{ player_names[(team, loop.index)]|e }} {%- for area in ordered_areas -%} - {%- set checks_done = checks[area] -%} - {%- set checks_total = checks_in_area[player][area] -%} - {%- if checks_done == checks_total -%} - - {{ checks_done }}/{{ checks_total }} - {%- else -%} - {{ checks_done }}/{{ checks_total }} - {%- endif -%} - {%- if area in key_locations -%} - {{ inventory[team][player][small_key_ids[area]] }} - {%- endif -%} - {%- if area in big_key_locations -%} - {% if inventory[team][player][big_key_ids[area]] %}✔️{% endif %} - {%- endif -%} + {% if player in checks_in_area and area in checks_in_area[player] %} + {%- set checks_done = checks[area] -%} + {%- set checks_total = checks_in_area[player][area] -%} + {%- if checks_done == checks_total -%} + + {{ checks_done }}/{{ checks_total }} + {%- else -%} + {{ checks_done }}/{{ checks_total }} + {%- endif -%} + {%- if area in key_locations -%} + {{ inventory[team][player][small_key_ids[area]] }} + {%- endif -%} + {%- if area in big_key_locations -%} + {% if inventory[team][player][big_key_ids[area]] %}✔️{% endif %} + {%- endif -%} + {% else %} + + {%- if area in key_locations -%} + + {%- endif -%} + {%- if area in big_key_locations -%} + + {%- endif -%} + {% endif %} {%- endfor -%} {{ percent_total_checks_done[team][player] }} {%- if activity_timers[(team, player)] -%} @@ -155,34 +165,7 @@ {% endfor %} - {% for team, hints in hints.items() %} -
- - - - - - - - - - - - - {%- for hint in hints -%} - - - - - - - - - {%- endfor -%} - -
FinderReceiverItemLocationEntranceFound
{{ long_player_names[team, hint.finding_player] }}{{ long_player_names[team, hint.receiving_player] }}{{ hint.item|item_name }}{{ hint.location|location_name }}{% if hint.entrance %}{{ hint.entrance }}{% else %}Vanilla{% endif %}{% if hint.found %}✔{% endif %}
-
- {% endfor %} + {% include "hintTable.html" with context %} {% endblock %} diff --git a/WebHostLib/templates/multiTracker.html b/WebHostLib/templates/multiTracker.html index 8fc1a218..2232cd0f 100644 --- a/WebHostLib/templates/multiTracker.html +++ b/WebHostLib/templates/multiTracker.html @@ -53,7 +53,7 @@ {# implement this block in game-specific multi trackers #} {% endblock %} - {{ checks["Total"] }}/{{ checks_in_area[player]["Total"] }} + {{ checks["Total"] }}/{{ locations[player] | length }} {{ percent_total_checks_done[team][player] }} {%- if activity_timers[team, player] -%} @@ -67,34 +67,7 @@ {% endfor %} - {% for team, hints in hints.items() %} -
- - - - - - - - - - - - - {%- for hint in hints -%} - - - - - - - - - {%- endfor -%} - -
FinderReceiverItemLocationEntranceFound
{{ long_player_names[team, hint.finding_player] }}{{ long_player_names[team, hint.receiving_player] }}{{ hint.item|item_name }}{{ hint.location|location_name }}{% if hint.entrance %}{{ hint.entrance }}{% else %}Vanilla{% endif %}{% if hint.found %}✔{% endif %}
-
- {% endfor %} + {% include "hintTable.html" with context %} {% endblock %} diff --git a/WebHostLib/tracker.py b/WebHostLib/tracker.py index 8f9fb148..decb3dd8 100644 --- a/WebHostLib/tracker.py +++ b/WebHostLib/tracker.py @@ -305,11 +305,9 @@ def get_static_room_data(room: Room): player_checks_in_area = {playernumber: {areaname: len(multidata["checks_in_area"][playernumber][areaname]) if areaname != "Total" else multidata["checks_in_area"][playernumber]["Total"] for areaname in ordered_areas} - for playernumber in range(1, len(names[0]) + 1) - if playernumber not in groups} + for playernumber in multidata["checks_in_area"]} player_location_to_area = {playernumber: get_location_table(multidata["checks_in_area"][playernumber]) - for playernumber in range(1, len(names[0]) + 1) - if playernumber not in groups} + for playernumber in multidata["checks_in_area"]} saving_second = get_saving_second(multidata["seed_name"]) result = locations, names, use_door_tracker, player_checks_in_area, player_location_to_area, \ multidata["precollected_items"], games, multidata["slot_data"], groups, saving_second, \ @@ -380,10 +378,11 @@ def _get_player_tracker(tracker: UUID, tracked_team: int, tracked_player: int, w specific_tracker = game_specific_trackers.get(games[tracked_player], None) if specific_tracker and not want_generic: tracker = specific_tracker(multisave, room, locations, inventory, tracked_team, tracked_player, player_name, - seed_checks_in_area, checks_done, slot_data[tracked_player], saving_second) + seed_checks_in_area, checks_done, slot_data[tracked_player], saving_second) else: - tracker = __renderGenericTracker(multisave, room, locations, inventory, tracked_team, tracked_player, player_name, - seed_checks_in_area, checks_done, saving_second, custom_locations, custom_items) + tracker = __renderGenericTracker(multisave, room, locations, inventory, tracked_team, tracked_player, + player_name, seed_checks_in_area, checks_done, saving_second, + custom_locations, custom_items) return (saving_second - datetime.datetime.now().second) % 60 or 60, tracker @@ -1373,10 +1372,10 @@ def _get_multiworld_tracker_data(tracker: UUID) -> typing.Optional[typing.Dict[s if player in groups: continue player_locations = locations[player] - checks_done[team][player]["Total"] = sum(1 for loc in locations_checked if loc in player_locations) + checks_done[team][player]["Total"] = len(locations_checked) percent_total_checks_done[team][player] = int(checks_done[team][player]["Total"] / - checks_in_area[player]["Total"] * 100) \ - if checks_in_area[player]["Total"] else 100 + len(player_locations) * 100) \ + if player_locations else 100 activity_timers = {} now = datetime.datetime.utcnow() diff --git a/worlds/alttp/Shops.py b/worlds/alttp/Shops.py index 53a880d0..f17eb1ea 100644 --- a/worlds/alttp/Shops.py +++ b/worlds/alttp/Shops.py @@ -172,6 +172,7 @@ def FillDisabledShopSlots(world): shop: Shop = location.parent_region.shop location.item = ItemFactory(shop.inventory[location.shop_slot]['item'], location.player) location.item_rule = lambda item: item.name == location.item.name and item.player == location.player + location.locked = True def ShopSlotFill(multiworld): @@ -278,6 +279,8 @@ def ShopSlotFill(multiworld): if 'P' in multiworld.shop_shuffle[location.player]: price_to_funny_price(multiworld, shop.inventory[location.shop_slot], location.player) + FillDisabledShopSlots(multiworld) + def create_shops(world, player: int): option = world.shop_shuffle[player] diff --git a/worlds/alttp/__init__.py b/worlds/alttp/__init__.py index 3f68e34b..8f74a354 100644 --- a/worlds/alttp/__init__.py +++ b/worlds/alttp/__init__.py @@ -544,6 +544,44 @@ class ALTTPWorld(World): er_hint_data[region.player][location.address] = main_entrance.name hint_data.update(er_hint_data) + @classmethod + def stage_modify_multidata(cls, multiworld, multidata: dict): + + ordered_areas = ( + 'Light World', 'Dark World', 'Hyrule Castle', 'Agahnims Tower', 'Eastern Palace', 'Desert Palace', + 'Tower of Hera', 'Palace of Darkness', 'Swamp Palace', 'Skull Woods', 'Thieves Town', 'Ice Palace', + 'Misery Mire', 'Turtle Rock', 'Ganons Tower', "Total" + ) + + checks_in_area = {player: {area: list() for area in ordered_areas} + for player in multiworld.get_game_players(cls.game)} + + for player in checks_in_area: + checks_in_area[player]["Total"] = 0 + + for location in multiworld.get_locations(): + if location.game == cls.game and type(location.address) is int: + main_entrance = location.parent_region.get_connecting_entrance(is_main_entrance) + if 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 location.parent_region.type == LTTPRegionType.LightWorld: + checks_in_area[location.player]["Light World"].append(location.address) + elif location.parent_region.type == LTTPRegionType.DarkWorld: + checks_in_area[location.player]["Dark World"].append(location.address) + elif main_entrance.parent_region.type == LTTPRegionType.LightWorld: + checks_in_area[location.player]["Light World"].append(location.address) + elif main_entrance.parent_region.type == LTTPRegionType.DarkWorld: + checks_in_area[location.player]["Dark World"].append(location.address) + else: + assert False, "Unknown Location area." + # TODO: remove Total as it's duplicated data and breaks consistent typing + checks_in_area[location.player]["Total"] += 1 + + multidata["checks_in_area"].update(checks_in_area) + def modify_multidata(self, multidata: dict): import base64 # wait for self.rom_name to be available. diff --git a/worlds/alttp/test/owg/TestVanillaOWG.py b/worlds/alttp/test/owg/TestVanillaOWG.py index 5b02666d..c0888aa3 100644 --- a/worlds/alttp/test/owg/TestVanillaOWG.py +++ b/worlds/alttp/test/owg/TestVanillaOWG.py @@ -1,14 +1,10 @@ from argparse import Namespace from BaseClasses import MultiWorld -from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool -from worlds.alttp.EntranceShuffle import link_entrances +from worlds.alttp.Dungeons import get_dungeon_item_pool from worlds.alttp.InvertedRegions import mark_dark_world_regions -from worlds.alttp.ItemPool import difficulties, generate_itempool +from worlds.alttp.ItemPool import difficulties from worlds.alttp.Items import ItemFactory -from worlds.alttp.Regions import create_regions -from worlds.alttp.Shops import create_shops -from worlds.alttp.Rules import set_rules from test.TestBase import TestBase from worlds import AutoWorld diff --git a/worlds/alttp/test/vanilla/TestVanilla.py b/worlds/alttp/test/vanilla/TestVanilla.py index 92035c86..e338410d 100644 --- a/worlds/alttp/test/vanilla/TestVanilla.py +++ b/worlds/alttp/test/vanilla/TestVanilla.py @@ -1,14 +1,10 @@ from argparse import Namespace from BaseClasses import MultiWorld -from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool -from worlds.alttp.EntranceShuffle import link_entrances +from worlds.alttp.Dungeons import get_dungeon_item_pool from worlds.alttp.InvertedRegions import mark_dark_world_regions -from worlds.alttp.ItemPool import difficulties, generate_itempool +from worlds.alttp.ItemPool import difficulties from worlds.alttp.Items import ItemFactory -from worlds.alttp.Regions import create_regions -from worlds.alttp.Shops import create_shops -from worlds.alttp.Rules import set_rules from test.TestBase import TestBase from worlds import AutoWorld