From 532cff13347d9f65ecc567dfe384386a5280c79c Mon Sep 17 00:00:00 2001 From: Zach Parks Date: Sat, 20 Apr 2024 17:29:41 -0500 Subject: [PATCH] ALTTP: Updates and refactors to multi-tracker and player tracker. (#3183) * ALTTP: Massive game tracker update. * Adds dropdowns separated by region for each location and its checked status. * Adds Bombs for bombless start seeds. * Adds Triforce Pieces to track. * Update icon image URLs to match in-game closer. * Fix issue with grouped progressive items. * Couple missed points. * Another edge case with details being refreshed. * Remove old commented out CSS * Consolidate a table and move an erroneous location in wrong region. * ALTTP: Updates and refactors to multi-tracker and player tracker. * Removed some missed commented out code. * Add triforce to prepare inventory logic. --- .../multitracker__ALinkToThePast.html | 351 +++++++------ .../templates/tracker__ALinkToThePast.html | 7 +- WebHostLib/tracker.py | 475 +++++------------- 3 files changed, 318 insertions(+), 515 deletions(-) diff --git a/WebHostLib/templates/multitracker__ALinkToThePast.html b/WebHostLib/templates/multitracker__ALinkToThePast.html index 8cea5ba0..9b8f460c 100644 --- a/WebHostLib/templates/multitracker__ALinkToThePast.html +++ b/WebHostLib/templates/multitracker__ALinkToThePast.html @@ -6,52 +6,42 @@ {% endblock %} {# List all tracker-relevant icons. Format: (Name, Image URL) #} -{%- set icons = { - "Blue Shield": "https://www.zeldadungeon.net/wiki/images/8/85/Fighters-Shield.png", - "Red Shield": "https://www.zeldadungeon.net/wiki/images/5/55/Fire-Shield.png", - "Mirror Shield": "https://www.zeldadungeon.net/wiki/images/8/84/Mirror-Shield.png", - "Fighter Sword": "https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/4/40/SFighterSword.png?width=1920", - "Master Sword": "https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/6/65/SMasterSword.png?width=1920", - "Tempered Sword": "https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/9/92/STemperedSword.png?width=1920", - "Golden Sword": "https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/2/28/SGoldenSword.png?width=1920", - "Bow": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/b/bc/ALttP_Bow_%26_Arrows_Sprite.png?version=5f85a70e6366bf473544ef93b274f74c", - "Silver Bow": "https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/6/65/Bow.png?width=1920", - "Green Mail": "https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/c/c9/SGreenTunic.png?width=1920", - "Blue Mail": "https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/9/98/SBlueTunic.png?width=1920", - "Red Mail": "https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/7/74/SRedTunic.png?width=1920", - "Power Glove": "https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/f/f5/SPowerGlove.png?width=1920", - "Titan Mitts": "https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/c/c1/STitanMitt.png?width=1920", - "Progressive Sword": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/c/cc/ALttP_Master_Sword_Sprite.png?version=55869db2a20e157cd3b5c8f556097725", - "Pegasus Boots": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/e/ed/ALttP_Pegasus_Shoes_Sprite.png?version=405f42f97240c9dcd2b71ffc4bebc7f9", - "Progressive Glove": "https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/c/c1/STitanMitt.png?width=1920", - "Flippers": "https://oyster.ignimgs.com/mediawiki/apis.ign.com/the-legend-of-zelda-a-link-to-the-past/4/4c/ZoraFlippers.png?width=1920", - "Moon Pearl": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/6/63/ALttP_Moon_Pearl_Sprite.png?version=d601542d5abcc3e006ee163254bea77e", - "Progressive Bow": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/b/bc/ALttP_Bow_%26_Arrows_Sprite.png?version=cfb7648b3714cccc80e2b17b2adf00ed", - "Blue Boomerang": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/c/c3/ALttP_Boomerang_Sprite.png?version=96127d163759395eb510b81a556d500e", - "Red Boomerang": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/b/b9/ALttP_Magical_Boomerang_Sprite.png?version=47cddce7a07bc3e4c2c10727b491f400", - "Hookshot": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/2/24/Hookshot.png?version=c90bc8e07a52e8090377bd6ef854c18b", - "Mushroom": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/3/35/ALttP_Mushroom_Sprite.png?version=1f1acb30d71bd96b60a3491e54bbfe59", - "Magic Powder": "https://www.zeldadungeon.net/wiki/images/thumb/6/62/MagicPowder-ALttP-Sprite.png/86px-MagicPowder-ALttP-Sprite.png", - "Fire Rod": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/d/d6/FireRod.png?version=6eabc9f24d25697e2c4cd43ddc8207c0", - "Ice Rod": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/d/d7/ALttP_Ice_Rod_Sprite.png?version=1f944148223d91cfc6a615c92286c3bc", - "Bombos": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/8/8c/ALttP_Bombos_Medallion_Sprite.png?version=f4d6aba47fb69375e090178f0fc33b26", - "Ether": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/3/3c/Ether.png?version=34027651a5565fcc5a83189178ab17b5", - "Quake": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/5/56/ALttP_Quake_Medallion_Sprite.png?version=efd64d451b1831bd59f7b7d6b61b5879", - "Lamp": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/6/63/ALttP_Lantern_Sprite.png?version=e76eaa1ec509c9a5efb2916698d5a4ce", - "Hammer": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/d/d1/ALttP_Hammer_Sprite.png?version=e0adec227193818dcaedf587eba34500", - "Shovel": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/c/c4/ALttP_Shovel_Sprite.png?version=e73d1ce0115c2c70eaca15b014bd6f05", - "Flute": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/d/db/Flute.png?version=ec4982b31c56da2c0c010905c5c60390", - "Bug Catching Net": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/5/54/Bug-CatchingNet.png?version=4d40e0ee015b687ff75b333b968d8be6", - "Book of Mudora": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/2/22/ALttP_Book_of_Mudora_Sprite.png?version=11e4632bba54f6b9bf921df06ac93744", - "Bottle": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/e/ef/ALttP_Magic_Bottle_Sprite.png?version=fd98ab04db775270cbe79fce0235777b", - "Cane of Somaria": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/e/e1/ALttP_Cane_of_Somaria_Sprite.png?version=8cc1900dfd887890badffc903bb87943", - "Cane of Byrna": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/b/bc/ALttP_Cane_of_Byrna_Sprite.png?version=758b607c8cbe2cf1900d42a0b3d0fb54", - "Cape": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/1/1c/ALttP_Magic_Cape_Sprite.png?version=6b77f0d609aab0c751307fc124736832", - "Magic Mirror": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/e/e5/ALttP_Magic_Mirror_Sprite.png?version=e035dbc9cbe2a3bd44aa6d047762b0cc", - "Triforce": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/4/4e/TriforceALttPTitle.png?version=dc398e1293177581c16303e4f9d12a48", +{% set icons = { + "Blue Shield": "https://www.zeldadungeon.net/wiki/images/thumb/c/c3/FightersShield-ALttP-Sprite.png/100px-FightersShield-ALttP-Sprite.png", + "Red Shield": "https://www.zeldadungeon.net/wiki/images/thumb/9/9e/FireShield-ALttP-Sprite.png/111px-FireShield-ALttP-Sprite.png", + "Mirror Shield": "https://www.zeldadungeon.net/wiki/images/thumb/e/e3/MirrorShield-ALttP-Sprite.png/105px-MirrorShield-ALttP-Sprite.png", + "Progressive Sword": "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/c/cc/ALttP_Master_Sword_Sprite.png", + "Progressive Bow": "https://www.zeldadungeon.net/wiki/images/thumb/8/8c/BowArrows-ALttP-Sprite.png/120px-BowArrows-ALttP-Sprite.png", + "Progressive Glove": "https://www.zeldadungeon.net/wiki/images/thumb/4/41/PowerGlove-ALttP-Sprite.png/105px-PowerGlove-ALttP-Sprite.png", + "Pegasus Boots": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/e/ed/ALttP_Pegasus_Shoes_Sprite.png", + "Flippers": "https://www.zeldadungeon.net/wiki/images/thumb/b/bc/ZoraFlippers-ALttP-Sprite.png/112px-ZoraFlippers-ALttP-Sprite.png", + "Moon Pearl": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/6/63/ALttP_Moon_Pearl_Sprite.png", + "Blue Boomerang": "https://www.zeldadungeon.net/wiki/images/thumb/f/f0/Boomerang-ALttP-Sprite.png/86px-Boomerang-ALttP-Sprite.png", + "Red Boomerang": "https://www.zeldadungeon.net/wiki/images/thumb/3/3c/MagicalBoomerang-ALttP-Sprite.png/86px-MagicalBoomerang-ALttP-Sprite.png", + "Hookshot": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/2/24/Hookshot.png", + "Mushroom": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/3/35/ALttP_Mushroom_Sprite.png", + "Magic Powder": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/e/e5/ALttP_Magic_Powder_Sprite.png", + "Fire Rod": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/d/d6/FireRod.png", + "Ice Rod": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/d/d7/ALttP_Ice_Rod_Sprite.png", + "Bombos": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/8/8c/ALttP_Bombos_Medallion_Sprite.png", + "Ether": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/3/3c/Ether.png", + "Quake": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/5/56/ALttP_Quake_Medallion_Sprite.png", + "Lamp": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/6/63/ALttP_Lantern_Sprite.png", + "Hammer": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/d/d1/ALttP_Hammer_Sprite.png", + "Shovel": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/c/c4/ALttP_Shovel_Sprite.png", + "Flute": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/d/db/Flute.png", + "Bug Catching Net": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/5/54/Bug-CatchingNet.png", + "Book of Mudora": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/2/22/ALttP_Book_of_Mudora_Sprite.png", + "Bottles": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/e/ef/ALttP_Magic_Bottle_Sprite.png", + "Cane of Somaria": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/e/e1/ALttP_Cane_of_Somaria_Sprite.png", + "Cane of Byrna": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/b/bc/ALttP_Cane_of_Byrna_Sprite.png", + "Cape": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/1/1c/ALttP_Magic_Cape_Sprite.png", + "Magic Mirror": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/e/e5/ALttP_Magic_Mirror_Sprite.png", + "Triforce": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/4/4e/TriforceALttPTitle.png", "Triforce Piece": "https://www.zeldadungeon.net/wiki/images/thumb/5/54/Triforce_Fragment_-_BS_Zelda.png/62px-Triforce_Fragment_-_BS_Zelda.png", - "Small Key": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/f/f1/ALttP_Small_Key_Sprite.png?version=4f35d92842f0de39d969181eea03774e", - "Big Key": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/3/33/ALttP_Big_Key_Sprite.png?version=136dfa418ba76c8b4e270f466fc12f4d", + "Bombs": "https://static.wikia.nocookie.net/zelda_gamepedia_en/images/3/38/ALttP_Bomb_Sprite.png", + "Small Key": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/f/f1/ALttP_Small_Key_Sprite.png", + "Big Key": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/3/33/ALttP_Big_Key_Sprite.png", "Chest": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/7/73/ALttP_Treasure_Chest_Sprite.png?version=5f530ecd98dcb22251e146e8049c0dda", "Light World": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/e/e7/ALttP_Soldier_Green_Sprite.png?version=d650d417934cd707a47e496489c268a6", "Dark World": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/9/94/ALttP_Moblin_Sprite.png?version=ebf50e33f4657c377d1606bcc0886ddc", @@ -68,33 +58,93 @@ "Misery Mire": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/8/85/ALttP_Vitreous_Sprite.png?version=92b2e9cb0aa63f831760f08041d8d8d8", "Turtle Rock": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/9/91/ALttP_Trinexx_Sprite.png?version=0cc867d513952aa03edd155597a0c0be", "Ganons Tower": "https://gamepedia.cursecdn.com/zelda_gamepedia_en/b/b9/ALttP_Ganon_Sprite.png?version=956f51f054954dfff53c1a9d4f929c74", -} -%} +} %} + +{% set inventory_order = [ + "Progressive Sword", + "Progressive Bow", + "Blue Boomerang", + "Red Boomerang", + "Hookshot", + "Bombs", + "Mushroom", + "Magic Powder", + "Fire Rod", + "Ice Rod", + "Bombos", + "Ether", + "Quake", + "Lamp", + "Hammer", + "Flute", + "Bug Catching Net", + "Book of Mudora", + "Cane of Somaria", + "Cane of Byrna", + "Cape", + "Magic Mirror", + "Shovel", + "Pegasus Boots", + "Flippers", + "Progressive Glove", + "Moon Pearl", + "Bottles", + "Triforce Piece", + "Triforce", +] %} + +{% set dungeon_keys = { + "Hyrule Castle": ("Small Key (Hyrule Castle)", "Big Key (Hyrule Castle)"), + "Agahnims Tower": ("Small Key (Agahnims Tower)", "Big Key (Agahnims Tower)"), + "Eastern Palace": ("Small Key (Eastern Palace)", "Big Key (Eastern Palace)"), + "Desert Palace": ("Small Key (Desert Palace)", "Big Key (Desert Palace)"), + "Tower of Hera": ("Small Key (Tower of Hera)", "Big Key (Tower of Hera)"), + "Palace of Darkness": ("Small Key (Palace of Darkness)", "Big Key (Palace of Darkness)"), + "Thieves Town": ("Small Key (Thieves Town)", "Big Key (Thieves Town)"), + "Skull Woods": ("Small Key (Skull Woods)", "Big Key (Skull Woods)"), + "Swamp Palace": ("Small Key (Swamp Palace)", "Big Key (Swamp Palace)"), + "Ice Palace": ("Small Key (Ice Palace)", "Big Key (Ice Palace)"), + "Misery Mire": ("Small Key (Misery Mire)", "Big Key (Misery Mire)"), + "Turtle Rock": ("Small Key (Turtle Rock)", "Big Key (Turtle Rock)"), + "Ganons Tower": ("Small Key (Ganons Tower)", "Big Key (Ganons Tower)"), +} %} + +{% set multi_items = [ + "Progressive Sword", + "Progressive Glove", + "Progressive Bow", + "Bottles", + "Triforce Piece", +] %} {%- block custom_table_headers %} -{#- macro that creates a table header with display name and image -#} -{%- macro make_header(name, img_src) %} - - {{ name }} - -{% endmacro -%} - -{#- call the macro to build the table header -#} -{%- for name in tracking_names %} - {%- if name in icons -%} + {#- macro that creates a table header with display name and image -#} + {%- macro make_header(name, img_src) %} - {{ name | e }} + {{ name }} - {%- endif %} -{% endfor -%} + {% endmacro -%} + + {#- call the macro to build the table header -#} + {%- for item in inventory_order %} + {%- if item in icons -%} + + {{ item | e }} + + {%- endif %} + {% endfor -%} {% endblock %} {# build each row of custom entries #} {% block custom_table_row scoped %} - {%- for id in tracking_ids -%} -{# {{ checks }}#} - {%- if inventories[(team, player)][id] -%} + {%- for item in inventory_order -%} + {%- if inventories[(team, player)][item] -%} - {% if id in multi_items %}{{ inventories[(team, player)][id] }}{% else %}✔️{% endif %} + {% if item in multi_items %} + {{ inventories[(team, player)][item] }} + {% else %} + ✔️ + {% endif %} {%- else -%} @@ -104,102 +154,95 @@ {% block custom_tables %} -{% for team, _ in total_team_locations.items() %} -
- - - - - - {% for area in ordered_areas %} - {% set colspan = 1 %} - {% if area in key_locations %} - {% set colspan = colspan + 1 %} - {% endif %} - {% if area in big_key_locations %} - {% set colspan = colspan + 1 %} - {% endif %} - {% if area in icons %} - - {%- else -%} - - {%- endif -%} - {%- endfor -%} - - - - - {% for area in ordered_areas %} +{% for team in total_team_locations %} +
+
#Name - {{ area }}{{ area }}%Last
Activity
+ + + + + {% for region in known_regions %} + {% set colspan = 1 %} + {% if region == "Agahnims Tower" %} + {% set colspan = 2 %} + {% elif region in dungeon_keys %} + {% set colspan = 3 %} + {% endif %} + + {% if region in icons %} + + {% else %} + + {% endif %} + {% endfor %} + + + + + {% for region in known_regions %} + + + {% if region in dungeon_keys %} + + + {# Special check just for Agahnims Tower, which has no big keys. #} + {% if region != "Agahnims Tower" %} + + {% endif %} + {% endif %} + {% endfor %} + + {# For "total" checks #} - {% if area in key_locations %} - - {% endif %} - {% if area in big_key_locations %} - - {%- endif -%} - {%- endfor -%} - - - - {%- for (checks_team, player), area_checks in checks_done.items() if games[(team, player)] == current_tracker and team == checks_team -%} - - - - {%- for area in ordered_areas -%} - {% if (team, player) in checks_in_area and area in checks_in_area[(team, player)] %} - {%- set checks_done = area_checks[area] -%} - {%- set checks_total = checks_in_area[(team, player)][area] -%} - {%- if checks_done == checks_total -%} + + + + + {% for (player_team, player), player_regions in regions.items() if team == player_team %} + + + + + {% for region, counts in player_regions.items() %} - {%- else -%} - - {%- endif -%} - {%- if area in key_locations -%} - - {%- endif -%} - {%- if area in big_key_locations -%} - - {%- endif -%} - {% else %} - - {%- if area in key_locations -%} - - {%- endif -%} - {%- if area in big_key_locations -%} - - {%- endif -%} - {% endif %} - {%- endfor -%} + {{ counts.checked }}/{{ counts.total }} + - + {% if region in dungeon_keys %} + - {%- if activity_timers[(team, player)] -%} - - {%- else -%} - - {%- endif -%} - - {%- endfor -%} - -
#Name + {{ region }} + {{ region }}Total
+ Checks + + Small Key + + Big Key + - Checks + Checks - Small Key - - Big Key -
{{ player }}{{ player_names_with_alias[(team, player)] | e }}
+ + {{ player }} + + {{ player_names_with_alias[(team, player)] | e }} - {{ checks_done }}/{{ checks_total }}{{ checks_done }}/{{ checks_total }}{{ inventories[(team, player)][small_key_ids[area]] }}{% if inventories[(team, player)][big_key_ids[area]] %}✔️{% endif %} - {% set location_count = locations[(team, player)] | length %} - {%- if locations[(team, player)] | length > 0 -%} - {% set percentage_of_completion = locations_complete[(team, player)] / location_count * 100 %} - {{ "{0:.2f}".format(percentage_of_completion) }} - {%- else -%} - 100.00 - {%- endif -%} - + {{ inventories[(team, player)][dungeon_keys[region][0]] }} + {{ activity_timers[(team, player)].total_seconds() }}None
-
+ {# Special check just for Agahnims Tower, which has no big keys. #} + {% if region != "Agahnims Tower" %} + + {% if inventories[(team, player)][dungeon_keys[region][1]] %} + ✔️ + {% endif %} + + {% endif %} + {% endif %} + {% endfor %} + + {% endfor %} + + + + {% endfor %} {% endblock %} diff --git a/WebHostLib/templates/tracker__ALinkToThePast.html b/WebHostLib/templates/tracker__ALinkToThePast.html index 13a83a34..99179797 100644 --- a/WebHostLib/templates/tracker__ALinkToThePast.html +++ b/WebHostLib/templates/tracker__ALinkToThePast.html @@ -68,9 +68,9 @@ "Desert Palace": ("Small Key (Desert Palace)", "Big Key (Desert Palace)"), "Tower of Hera": ("Small Key (Tower of Hera)", "Big Key (Tower of Hera)"), "Palace of Darkness": ("Small Key (Palace of Darkness)", "Big Key (Palace of Darkness)"), - "Thieves' Town": ("Small Key (Thieves Town)", "Big Key (Thieves Town)"), - "Skull Woods": ("Small Key (Skull Woods)", "Big Key (Skull Woods)"), "Swamp Palace": ("Small Key (Swamp Palace)", "Big Key (Swamp Palace)"), + "Thieves Town": ("Small Key (Thieves Town)", "Big Key (Thieves Town)"), + "Skull Woods": ("Small Key (Skull Woods)", "Big Key (Skull Woods)"), "Ice Palace": ("Small Key (Ice Palace)", "Big Key (Ice Palace)"), "Misery Mire": ("Small Key (Misery Mire)", "Big Key (Misery Mire)"), "Turtle Rock": ("Small Key (Turtle Rock)", "Big Key (Turtle Rock)"), @@ -146,7 +146,8 @@
BK
- {% for region_name, region_data in regions.items() %} + {% for region_name in known_regions %} + {% set region_data = regions[region_name] %} {% if region_data["locations"] | length > 0 %}
diff --git a/WebHostLib/tracker.py b/WebHostLib/tracker.py index 0ae7b2a5..e4d38c46 100644 --- a/WebHostLib/tracker.py +++ b/WebHostLib/tracker.py @@ -1,7 +1,7 @@ import datetime import collections from dataclasses import dataclass -from typing import Any, Callable, Dict, List, Optional, Set, Tuple +from typing import Any, Callable, Dict, List, Optional, Set, Tuple, NamedTuple from uuid import UUID from flask import render_template @@ -456,210 +456,111 @@ if "Factorio" in network_data_package["games"]: _multiworld_trackers["Factorio"] = render_Factorio_multiworld_tracker if "A Link to the Past" in network_data_package["games"]: + # Mapping from non-progressive item to progressive name and max level. + non_progressive_items = { + "Fighter Sword": ("Progressive Sword", 1), + "Master Sword": ("Progressive Sword", 2), + "Tempered Sword": ("Progressive Sword", 3), + "Golden Sword": ("Progressive Sword", 4), + "Power Glove": ("Progressive Glove", 1), + "Titans Mitts": ("Progressive Glove", 2), + "Bow": ("Progressive Bow", 1), + "Silver Bow": ("Progressive Bow", 2), + "Blue Mail": ("Progressive Mail", 1), + "Red Mail": ("Progressive Mail", 2), + "Blue Shield": ("Progressive Shield", 1), + "Red Shield": ("Progressive Shield", 2), + "Mirror Shield": ("Progressive Shield", 3), + } + + progressive_item_max = { + "Progressive Sword": 4, + "Progressive Glove": 2, + "Progressive Bow": 2, + "Progressive Mail": 2, + "Progressive Shield": 3, + } + + bottle_items = [ + "Bottle", + "Bottle (Bee)", + "Bottle (Blue Potion)", + "Bottle (Fairy)", + "Bottle (Good Bee)", + "Bottle (Green Potion)", + "Bottle (Red Potion)", + ] + + known_regions = [ + "Light World", "Dark World", "Hyrule Castle", "Agahnims Tower", "Eastern Palace", "Desert Palace", + "Tower of Hera", "Palace of Darkness", "Swamp Palace", "Thieves Town", "Skull Woods", "Ice Palace", + "Misery Mire", "Turtle Rock", "Ganons Tower" + ] + + class RegionCounts(NamedTuple): + total: int + checked: int + + def prepare_inventories(team: int, player: int, inventory: collections.Counter[str], tracker_data: TrackerData): + for item, (prog_item, level) in non_progressive_items.items(): + if item in inventory: + inventory[prog_item] = min(max(inventory[prog_item], level), progressive_item_max[prog_item]) + + for bottle in bottle_items: + inventory["Bottles"] = min(inventory["Bottles"] + inventory[bottle], 4) + + if "Progressive Bow (Alt)" in inventory: + inventory["Progressive Bow"] += inventory["Progressive Bow (Alt)"] + inventory["Progressive Bow"] = min(inventory["Progressive Bow"], progressive_item_max["Progressive Bow"]) + + # Highlight 'bombs' if we received any bomb upgrades in bombless start. + # In race mode, we'll just assume bombless start for simplicity. + if tracker_data.get_slot_data(team, player).get("bombless_start", True): + inventory["Bombs"] = sum(count for item, count in inventory.items() if item.startswith("Bomb Upgrade")) + else: + inventory["Bombs"] = 1 + + # Triforce item if we meet goal. + if tracker_data.get_room_client_statuses()[team, player] == ClientStatus.CLIENT_GOAL: + inventory["Triforce"] = 1 + def render_ALinkToThePast_multiworld_tracker(tracker_data: TrackerData, enabled_trackers: List[str]): - # Helper objects. - alttp_id_lookup = tracker_data.item_name_to_id["A Link to the Past"] + inventories: Dict[Tuple[int, int], collections.Counter[str]] = { + (team, player): collections.Counter({ + tracker_data.item_id_to_name["A Link to the Past"][code]: count + for code, count in tracker_data.get_player_inventory_counts(team, player).items() + }) + for team, players in tracker_data.get_all_players().items() + for player in players if tracker_data.get_slot_info(team, player).game == "A Link to the Past" + } - multi_items = { - alttp_id_lookup[name] - for name in ("Progressive Sword", "Progressive Bow", "Bottle", "Progressive Glove", "Triforce Piece") - } - links = { - "Bow": "Progressive Bow", - "Silver Arrows": "Progressive Bow", - "Silver Bow": "Progressive Bow", - "Progressive Bow (Alt)": "Progressive Bow", - "Bottle (Red Potion)": "Bottle", - "Bottle (Green Potion)": "Bottle", - "Bottle (Blue Potion)": "Bottle", - "Bottle (Fairy)": "Bottle", - "Bottle (Bee)": "Bottle", - "Bottle (Good Bee)": "Bottle", - "Fighter Sword": "Progressive Sword", - "Master Sword": "Progressive Sword", - "Tempered Sword": "Progressive Sword", - "Golden Sword": "Progressive Sword", - "Power Glove": "Progressive Glove", - "Titans Mitts": "Progressive Glove", - } - links = {alttp_id_lookup[key]: alttp_id_lookup[value] for key, value in links.items()} - levels = { - "Fighter Sword": 1, - "Master Sword": 2, - "Tempered Sword": 3, - "Golden Sword": 4, - "Power Glove": 1, - "Titans Mitts": 2, - "Bow": 1, - "Silver Bow": 2, - "Triforce Piece": 90, - } - tracking_names = [ - "Progressive Sword", "Progressive Bow", "Book of Mudora", "Hammer", "Hookshot", "Magic Mirror", "Flute", - "Pegasus Boots", "Progressive Glove", "Flippers", "Moon Pearl", "Blue Boomerang", "Red Boomerang", - "Bug Catching Net", "Cape", "Shovel", "Lamp", "Mushroom", "Magic Powder", "Cane of Somaria", - "Cane of Byrna", "Fire Rod", "Ice Rod", "Bombos", "Ether", "Quake", "Bottle", "Triforce Piece", "Triforce", - ] - default_locations = { - "Light World": { - 1572864, 1572865, 60034, 1572867, 1572868, 60037, 1572869, 1572866, 60040, 59788, 60046, 60175, - 1572880, 60049, 60178, 1572883, 60052, 60181, 1572885, 60055, 60184, 191256, 60058, 60187, 1572884, - 1572886, 1572887, 1572906, 60202, 60205, 59824, 166320, 1010170, 60208, 60211, 60214, 60217, 59836, - 60220, 60223, 59839, 1573184, 60226, 975299, 1573188, 1573189, 188229, 60229, 60232, 1573193, - 1573194, 60235, 1573187, 59845, 59854, 211407, 60238, 59857, 1573185, 1573186, 1572882, 212328, - 59881, 59761, 59890, 59770, 193020, 212605 - }, - "Dark World": { - 59776, 59779, 975237, 1572870, 60043, 1572881, 60190, 60193, 60196, 60199, 60840, 1573190, 209095, - 1573192, 1573191, 60241, 60244, 60247, 60250, 59884, 59887, 60019, 60022, 60028, 60031 - }, - "Desert Palace": {1573216, 59842, 59851, 59791, 1573201, 59830}, - "Eastern Palace": {1573200, 59827, 59893, 59767, 59833, 59773}, - "Hyrule Castle": {60256, 60259, 60169, 60172, 59758, 59764, 60025, 60253}, - "Agahnims Tower": {60082, 60085}, - "Tower of Hera": {1573218, 59878, 59821, 1573202, 59896, 59899}, - "Swamp Palace": {60064, 60067, 60070, 59782, 59785, 60073, 60076, 60079, 1573204, 60061}, - "Thieves Town": {59905, 59908, 59911, 59914, 59917, 59920, 59923, 1573206}, - "Skull Woods": {59809, 59902, 59848, 59794, 1573205, 59800, 59803, 59806}, - "Ice Palace": {59872, 59875, 59812, 59818, 59860, 59797, 1573207, 59869}, - "Misery Mire": {60001, 60004, 60007, 60010, 60013, 1573208, 59866, 59998}, - "Turtle Rock": {59938, 59941, 59944, 1573209, 59947, 59950, 59953, 59956, 59926, 59929, 59932, 59935}, - "Palace of Darkness": { - 59968, 59971, 59974, 59977, 59980, 59983, 59986, 1573203, 59989, 59959, 59992, 59962, 59995, - 59965 - }, - "Ganons Tower": { - 60160, 60163, 60166, 60088, 60091, 60094, 60097, 60100, 60103, 60106, 60109, 60112, 60115, 60118, - 60121, 60124, 60127, 1573217, 60130, 60133, 60136, 60139, 60142, 60145, 60148, 60151, 60157 - }, - "Total": set() - } - key_only_locations = { - "Light World": set(), - "Dark World": set(), - "Desert Palace": {0x140031, 0x14002b, 0x140061, 0x140028}, - "Eastern Palace": {0x14005b, 0x140049}, - "Hyrule Castle": {0x140037, 0x140034, 0x14000d, 0x14003d}, - "Agahnims Tower": {0x140061, 0x140052}, - "Tower of Hera": set(), - "Swamp Palace": {0x140019, 0x140016, 0x140013, 0x140010, 0x14000a}, - "Thieves Town": {0x14005e, 0x14004f}, - "Skull Woods": {0x14002e, 0x14001c}, - "Ice Palace": {0x140004, 0x140022, 0x140025, 0x140046}, - "Misery Mire": {0x140055, 0x14004c, 0x140064}, - "Turtle Rock": {0x140058, 0x140007}, - "Palace of Darkness": set(), - "Ganons Tower": {0x140040, 0x140043, 0x14003a, 0x14001f}, - "Total": set() - } - location_to_area = {} - for area, locations in default_locations.items(): - for location in locations: - location_to_area[location] = area - for area, locations in key_only_locations.items(): - for location in locations: - location_to_area[location] = area + # Translate non-progression items to progression items for tracker simplicity. + for (team, player), inventory in inventories.items(): + prepare_inventories(team, player, inventory, tracker_data) - checks_in_area = {area: len(checks) for area, checks in default_locations.items()} - checks_in_area["Total"] = 216 - 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" - ) - - player_checks_in_area = { + regions: Dict[Tuple[int, int], Dict[str, RegionCounts]] = { (team, player): { - area_name: len(tracker_data._multidata["checks_in_area"][player][area_name]) - if area_name != "Total" else tracker_data._multidata["checks_in_area"][player]["Total"] - for area_name in ordered_areas + region_name: RegionCounts( + total=len(tracker_data._multidata["checks_in_area"][player][region_name]), + checked=sum( + 1 for location in tracker_data._multidata["checks_in_area"][player][region_name] + if location in tracker_data.get_player_checked_locations(team, player) + ), + ) + for region_name in known_regions } - for team, players in tracker_data.get_all_slots().items() - for player in players - if tracker_data.get_slot_info(team, player).type != SlotType.group and - tracker_data.get_slot_info(team, player).game == "A Link to the Past" + for team, players in tracker_data.get_all_players().items() + for player in players if tracker_data.get_slot_info(team, player).game == "A Link to the Past" } - tracking_ids = [] - for item in tracking_names: - tracking_ids.append(alttp_id_lookup[item]) - - # Can't wait to get this into the apworld. Oof. - from worlds.alttp import Items - - small_key_ids = {} - big_key_ids = {} - ids_small_key = {} - ids_big_key = {} - for item_name, data in Items.item_table.items(): - if "Key" in item_name: - area = item_name.split("(")[1][:-1] - if "Small" in item_name: - small_key_ids[area] = data[2] - ids_small_key[data[2]] = area - else: - big_key_ids[area] = data[2] - ids_big_key[data[2]] = area - - def _get_location_table(checks_table: dict) -> dict: - loc_to_area = {} - for area, locations in checks_table.items(): - if area == "Total": - continue - for location in locations: - loc_to_area[location] = area - return loc_to_area - - player_location_to_area = { - (team, player): _get_location_table(tracker_data._multidata["checks_in_area"][player]) - for team, players in tracker_data.get_all_slots().items() - for player in players - if tracker_data.get_slot_info(team, player).type != SlotType.group and - tracker_data.get_slot_info(team, player).game == "A Link to the Past" - } - - checks_done: Dict[TeamPlayer, Dict[str: int]] = { - (team, player): {location_name: 0 for location_name in default_locations} - for team, players in tracker_data.get_all_slots().items() - for player in players - if tracker_data.get_slot_info(team, player).type != SlotType.group and - tracker_data.get_slot_info(team, player).game == "A Link to the Past" - } - - inventories: Dict[TeamPlayer, Dict[int, int]] = {} - player_big_key_locations = {(player): set() for player in tracker_data.get_all_slots()[0]} - player_small_key_locations = {player: set() for player in tracker_data.get_all_slots()[0]} - group_big_key_locations = set() - group_key_locations = set() - - for (team, player), locations in checks_done.items(): - # Check if game complete. - if tracker_data.get_player_client_status(team, player) == ClientStatus.CLIENT_GOAL: - inventories[team, player][106] = 1 # Triforce - - # Count number of locations checked. - for location in tracker_data.get_player_checked_locations(team, player): - checks_done[team, player][player_location_to_area[team, player][location]] += 1 - checks_done[team, player]["Total"] += 1 - - # Count keys. - for location, (item, receiving, _) in tracker_data.get_player_locations(team, player).items(): - if item in ids_big_key: - player_big_key_locations[receiving].add(ids_big_key[item]) - elif item in ids_small_key: - player_small_key_locations[receiving].add(ids_small_key[item]) - - # Iterate over received items and build inventory/key counts. - inventories[team, player] = collections.Counter() - for network_item in tracker_data.get_player_received_items(team, player): - target_item = links.get(network_item.item, network_item.item) - if network_item.item in levels: # non-progressive - inventories[team, player][target_item] = (max(inventories[team, player][target_item], levels[network_item.item])) - else: - inventories[team, player][target_item] += 1 - - group_key_locations |= player_small_key_locations[player] - group_big_key_locations |= player_big_key_locations[player] + # Get a totals count. + for player, player_regions in regions.items(): + total = 0 + checked = 0 + for region, region_counts in player_regions.items(): + total += region_counts.total + checked += region_counts.checked + regions[player]["Total"] = RegionCounts(total, checked) return render_template( "multitracker__ALinkToThePast.html", @@ -682,16 +583,8 @@ if "A Link to the Past" in network_data_package["games"]: item_id_to_name=tracker_data.item_id_to_name, location_id_to_name=tracker_data.location_id_to_name, inventories=inventories, - tracking_names=tracking_names, - tracking_ids=tracking_ids, - multi_items=multi_items, - checks_done=checks_done, - ordered_areas=ordered_areas, - checks_in_area=player_checks_in_area, - key_locations=group_key_locations, - big_key_locations=group_big_key_locations, - small_key_ids=small_key_ids, - big_key_ids=big_key_ids, + regions=regions, + known_regions=known_regions, ) def render_ALinkToThePast_tracker(tracker_data: TrackerData, team: int, player: int) -> str: @@ -700,161 +593,26 @@ if "A Link to the Past" in network_data_package["games"]: for code, count in tracker_data.get_player_inventory_counts(team, player).items() }) - # Mapping from non-progressive item to progressive name and max level. - non_progressive_items = { - "Fighter Sword": ("Progressive Sword", 1), - "Master Sword": ("Progressive Sword", 2), - "Tempered Sword": ("Progressive Sword", 3), - "Golden Sword": ("Progressive Sword", 4), - "Power Glove": ("Progressive Glove", 1), - "Titans Mitts": ("Progressive Glove", 2), - "Bow": ("Progressive Bow", 1), - "Silver Bow": ("Progressive Bow", 2), - "Blue Mail": ("Progressive Mail", 1), - "Red Mail": ("Progressive Mail", 2), - "Blue Shield": ("Progressive Shield", 1), - "Red Shield": ("Progressive Shield", 2), - "Mirror Shield": ("Progressive Shield", 3), - } - - progressive_item_max = { - "Progressive Sword": 4, - "Progressive Glove": 2, - "Progressive Bow": 2, - "Progressive Mail": 2, - "Progressive Shield": 3, - } - - bottle_items = [ - "Bottle", - "Bottle (Bee)", - "Bottle (Blue Potion)", - "Bottle (Fairy)", - "Bottle (Good Bee)", - "Bottle (Green Potion)", - "Bottle (Red Potion)", - ] - # Translate non-progression items to progression items for tracker simplicity. - for item, (prog_item, level) in non_progressive_items.items(): - if item in inventory: - inventory[prog_item] = min(max(inventory[prog_item], level), progressive_item_max[prog_item]) - - for bottle in bottle_items: - inventory["Bottles"] = min(inventory["Bottles"] + inventory[bottle], 4) - - if "Progressive Bow (Alt)" in inventory: - inventory["Progressive Bow"] += inventory["Progressive Bow (Alt)"] - inventory["Progressive Bow"] = min(inventory["Progressive Bow"], progressive_item_max["Progressive Bow"]) - - # Highlight 'bombs' if we received any bomb upgrades in bombless start. - # In race mode, we'll just assume bombless start for simplicity. - if tracker_data.get_slot_data(team, player).get("bombless_start", True): - inventory["Bombs"] = sum(count for item, count in inventory.items() if item.startswith("Bomb Upgrade")) - else: - inventory["Bombs"] = 1 - - known_regions = { - "Light World": { - 0x180013, 0x02eb18, 0x18014a, 0x180145, 0x033d68, 0x00eb0f, 0x00eb12, 0x00eb15, 0x00eb18, 0x00eb1b, - 0x02df45, 0x00e971, 0x0ee1c3, 0x180149, 0x00e9b0, 0x00e9d1, 0x00e97a, 0x00e98c, 0x00e9bc, 0x00e9ce, - 0x00e9e9, 0x00e9f2, 0x00ea82, 0x00ea85, 0x00ea88, 0x02f1fc, 0x00ea8e, 0x00ea91, 0x00ea94, 0x00ea97, - 0x00ea9a, 0x18002a, 0x180015, 0x0339cf, 0x033e7d, 0x180000, 0x180001, 0x180003, 0x180004, 0x180005, - 0x00eb42, 0x00eb45, 0x00eb48, 0x00eb4b, 0x180010, 0x00eb4e, 0x00eb3f, 0x180012, 0x180014, 0x180144, - 0x180142, 0x180143, 0x0289b0, 0x0f69fa, 0x180002, 0x00eb2a, 0x00eb2d, 0x00eb30, 0x00eb33, 0x00eb36, - 0x00eb39, 0x00eb3c, 0x00e9bf, 0x180016, 0x180017, 0x180140, 0x180141, 0x00e9c5, 0x400018, 0x400019, - 0x40001a, 0x400015, 0x400016, 0x400017, 0x400012, 0x400013, 0x400014, 0x40001b, 0x40001c, 0x40001d, - 0x400022, 0x400023, 0x400024, 0x400025, 0x400021, 0x40001e, 0x40001f, - }, - "Dark World": { - 0x180147, 0x0ee185, 0x0330c7, 0x180148, 0x00eb1e, 0x00eb21, 0x00eb24, 0x00eb27, 0x180011, 0x180006, - 0x00e980, 0x00e983, 0x00e9ec, 0x00e9ef, 0x00eda8, 0x180146, 0x00ea73, 0x00ea76, 0x00ea7c, 0x00ea7f, - 0x00ea8b, 0x00eb51, 0x00eb54, 0x00eb5a, 0x00eb57, 0x400000, 0x400001, 0x400002, 0x400006, 0x400007, - 0x400008, 0x400009, 0x40000a, 0x40000b, 0x40000f, 0x400010, 0x400011, 0x400003, 0x400004, 0x400005, - 0x40000c, 0x40000d, 0x40000e, - }, - "Hyrule Castle": { - 0x00e974, 0x00eb0c, 0x00eb09, 0x00e96e, 0x00eb5d, 0x00eb60, 0x00eb63, 0x00ea79, 0x140037, 0x140034, - 0x14000d, 0x14003d, - }, - "Agahnims Tower": { - 0x00eab5, 0x00eab2, 0x140061, 0x140052, - }, - "Eastern Palace": { - 0x00e977, 0x00e97d, 0x00e9b3, 0x00e9b9, 0x00e9f5, 0x180150, 0x14005b, 0x140049, - }, - "Desert Palace": { - 0x00e98f, 0x180160, 0x00e9b6, 0x00e9cb, 0x00e9c2, 0x180151, 0x140031, 0x14002b, 0x140028, - }, - "Tower of Hera": { - 0x180162, 0x00e9ad, 0x00e9e6, 0x00e9fb, 0x00e9f8, 0x180152 - }, - "Palace of Darkness": { - 0x00ea5b, 0x00ea3d, 0x00ea49, 0x00ea37, 0x00ea3a, 0x00ea52, 0x00ea43, 0x00ea4c, 0x00ea4f, 0x00ea55, - 0x00ea58, 0x00ea40, 0x00ea46, 0x180153, - }, - "Swamp Palace": { - 0x00ea9d, 0x00e986, 0x00e989, 0x00eaa0, 0x00eaa6, 0x00eaa3, 0x00eaa9, 0x00eaac, 0x00eaaf, 0x180154, - 0x140019, 0x140016, 0x140013, 0x140010, 0x14000a, - }, - "Thieves' Town": { - 0x00ea04, 0x00ea01, 0x00ea07, 0x00ea0a, 0x00ea0d, 0x00ea10, 0x00ea13, 0x180156, 0x14005e, 0x14004f, - }, - "Skull Woods": { - 0x00e992, 0x00e99b, 0x00e998, 0x00e9a1, 0x00e9c8, 0x00e99e, 0x00e9fe, 0x180155, 0x14002e, 0x14001c, - }, - "Ice Palace": { - 0x00e9d4, 0x00e995, 0x00e9aa, 0x00e9e3, 0x00e9e0, 0x00e9a4, 0x00e9dd, 0x180157, 0x140004, 0x140022, - 0x140025, 0x140046, - }, - "Misery Mire": { - 0x00ea67, 0x00ea6a, 0x00ea5e, 0x00ea61, 0x00e9da, 0x00ea64, 0x00ea6d, 0x180158, 0x140055, 0x14004c, - 0x140064, - }, - "Turtle Rock": { - 0x00ea22, 0x00ea1c, 0x00ea1f, 0x00ea16, 0x00ea25, 0x00ea19, 0x00ea34, 0x00ea31, 0x00ea2e, 0x00ea2b, - 0x00ea28, 0x180159, 0x140058, 0x140007, - }, - "Ganons Tower": { - 0x180161, 0x00ead9, 0x00eadc, 0x00eae2, 0x00eae5, 0x00eae8, 0x00eaeb, 0x00eaee, 0x00eab8, 0x00eabb, - 0x00eabe, 0x00eac1, 0x00ead3, 0x00ead0, 0x00eac4, 0x00eac7, 0x00eaca, 0x00eacd, 0x00eadf, 0x00eadf, - 0x00ead6, 0x00eaf4, 0x00eaf7, 0x00eaf1, 0x00eafd, 0x00eb00, 0x00eb03, 0x00eb06, 0x140040, 0x140043, - 0x14003a, 0x14001f, - }, - } + prepare_inventories(team, player, inventory, tracker_data) regions = { - "Light World": {"checked": 0, "locations": []}, - "Dark World": {"checked": 0, "locations": []}, - "Hyrule Castle": {"checked": 0, "locations": []}, - "Agahnims Tower": {"checked": 0, "locations": []}, - "Eastern Palace": {"checked": 0, "locations": []}, - "Desert Palace": {"checked": 0, "locations": []}, - "Tower of Hera": {"checked": 0, "locations": []}, - "Palace of Darkness": {"checked": 0, "locations": []}, - "Skull Woods": {"checked": 0, "locations": []}, - "Thieves' Town": {"checked": 0, "locations": []}, - "Swamp Palace": {"checked": 0, "locations": []}, - "Ice Palace": {"checked": 0, "locations": []}, - "Misery Mire": {"checked": 0, "locations": []}, - "Turtle Rock": {"checked": 0, "locations": []}, - "Ganons Tower": {"checked": 0, "locations": []}, - "Unknown": {"checked": 0, "locations": []}, + region_name: { + "checked": sum( + 1 for location in tracker_data._multidata["checks_in_area"][player][region_name] + if location in tracker_data.get_player_checked_locations(team, player) + ), + "locations": [ + ( + tracker_data.location_id_to_name["A Link to the Past"][location], + location in tracker_data.get_player_checked_locations(team, player) + ) + for location in tracker_data._multidata["checks_in_area"][player][region_name] + ], + } + for region_name in known_regions } - for location in tracker_data.get_player_locations(team, player): - location_name = tracker_data.location_id_to_name["A Link to the Past"][location] - location_checked = location in tracker_data.get_player_checked_locations(team, player) - for region, region_locations in known_regions.items(): - if location in region_locations: - regions[region]["locations"].append((location_name, location_checked)) - regions[region]["checked"] += 1 if location_checked else 0 - break - else: - # New or missed location in the tables above. Add it to an "unknown region", so it's not forgotten. - regions["Unknown"]["locations"].append((location_name, location_checked)) - regions["Unknown"]["checked"] += 1 if location_checked else 0 - # Sort locations in regions by name for region in regions: regions[region]["locations"].sort() @@ -867,6 +625,7 @@ if "A Link to the Past" in network_data_package["games"]: inventory=inventory, player_name=tracker_data.get_player_name(team, player), regions=regions, + known_regions=known_regions, ) _multiworld_trackers["A Link to the Past"] = render_ALinkToThePast_multiworld_tracker