From 835869deeafc023562313acea32729b5943bcebf Mon Sep 17 00:00:00 2001 From: Ada Werefox Date: Sat, 6 Apr 2024 19:23:44 -0500 Subject: [PATCH] Many styling changes, reoganized elements to be at the bottom, styled buttons to look proper. Added hints list. --- tunictracker/empty_doors.json | 23 + tunictracker/empty_hints.json | 3 + tunictracker/empty_items.json | 23 + tunictracker/empty_overview.json | 10 + tunictracker/empty_spoiler.json | 38 - tunictracker/less_fun_doors.json | 643 ++++++++++++ tunictracker/less_fun_hints.json | 1 + tunictracker/less_fun_items.json | 752 ++++++++++++++ tunictracker/less_fun_overview.json | 8 + .../static/tracker/assets/button_functions.js | 40 + .../tracker/static/tracker/assets/refresh.js | 973 ------------------ .../static/tracker/assets/refresh_elements.js | 624 +++++++++++ .../static/tracker/assets/translate-hints.js | 99 ++ .../static/tracker/data/holy_cross_codes.json | 61 +- .../static/tracker/fonts/Trunic-Regular.otf | Bin 0 -> 61176 bytes tunictracker/tracker/templates/index.html | 7 +- .../templates/tracker/breakdown/block.html | 4 +- .../templates/tracker/codes/index.html | 2 +- .../templates/tracker/hints/index.html | 3 + .../templates/tracker/howto/index.html | 12 +- .../tracker/templates/tracker/index.html | 83 +- .../templates/tracker/summary/block.html | 4 +- tunictracker/tracker/views.py | 108 +- tunictracker/tunictracker/settings.py | 1 + .../static/fonts/Trunic-Regular.otf | Bin 0 -> 61176 bytes .../static_src/src/fonts/Trunic-Regular.otf | Bin 0 -> 61176 bytes .../werefoxtheme/static_src/src/styles.css | 10 +- .../static_src/tailwind.config.js | 104 ++ 28 files changed, 2458 insertions(+), 1178 deletions(-) create mode 100644 tunictracker/empty_doors.json create mode 100644 tunictracker/empty_hints.json create mode 100644 tunictracker/empty_items.json create mode 100644 tunictracker/empty_overview.json delete mode 100644 tunictracker/empty_spoiler.json create mode 100644 tunictracker/less_fun_doors.json create mode 100644 tunictracker/less_fun_hints.json create mode 100644 tunictracker/less_fun_items.json create mode 100644 tunictracker/less_fun_overview.json create mode 100644 tunictracker/tracker/static/tracker/assets/button_functions.js delete mode 100644 tunictracker/tracker/static/tracker/assets/refresh.js create mode 100644 tunictracker/tracker/static/tracker/assets/refresh_elements.js create mode 100644 tunictracker/tracker/static/tracker/assets/translate-hints.js create mode 100644 tunictracker/tracker/static/tracker/fonts/Trunic-Regular.otf create mode 100644 tunictracker/tracker/templates/tracker/hints/index.html create mode 100644 tunictracker/werefoxtheme/static/fonts/Trunic-Regular.otf create mode 100644 tunictracker/werefoxtheme/static_src/src/fonts/Trunic-Regular.otf diff --git a/tunictracker/empty_doors.json b/tunictracker/empty_doors.json new file mode 100644 index 0000000..8ba9873 --- /dev/null +++ b/tunictracker/empty_doors.json @@ -0,0 +1,23 @@ +{ + "found": 0, + "remaining": 0, + "total": 1312, + "scenes": { + "???": { + "found": 0, + "remaining": 0, + "total": 0, + "doors": { + "Door To Light": { "scene": "???", "door": "Door To Darkness" } + } + }, + "Another One": { + "found": 69, + "remaining": 0, + "total": 420, + "doors": { + "Door To Darkness": { "scene": "???", "door": "Door To Light" } + } + } + } +} diff --git a/tunictracker/empty_hints.json b/tunictracker/empty_hints.json new file mode 100644 index 0000000..8f3df37 --- /dev/null +++ b/tunictracker/empty_hints.json @@ -0,0 +1,3 @@ +{ + "???": "There is no spoon (but in trunic)" +} \ No newline at end of file diff --git a/tunictracker/empty_items.json b/tunictracker/empty_items.json new file mode 100644 index 0000000..aca2d19 --- /dev/null +++ b/tunictracker/empty_items.json @@ -0,0 +1,23 @@ +{ + "collected": 0, + "remaining": 0, + "total": 1312, + "scenes": { + "???": { + "collected": 0, + "remaining": 0, + "total": 0, + "checks": { + "It's a mystery": { "name": "", "owner": "" } + } + }, + "Another One": { + "collected": 69, + "remaining": 0, + "total": 420, + "checks": { + "nini time mister the missile": { "name": "", "owner": "" } + } + } + } +} diff --git a/tunictracker/empty_overview.json b/tunictracker/empty_overview.json new file mode 100644 index 0000000..87d9dbb --- /dev/null +++ b/tunictracker/empty_overview.json @@ -0,0 +1,10 @@ +{ + "scene": "???", + "seed": 0, + "items": 1312, + "entrances": 1312, + "hints": 0, + "codes": { + "empty": 0 + } +} diff --git a/tunictracker/empty_spoiler.json b/tunictracker/empty_spoiler.json deleted file mode 100644 index 6586a19..0000000 --- a/tunictracker/empty_spoiler.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "Debug": { - "Name": "", - "Seed": "", - "SpoilerSeed": "", - "Archipelago": false, - "Randomized": false, - "HexQuest": false, - "EntranceRando": false, - "FixedShops": false - }, - "Totals": { - "Entrances": { "Total": 0, "Undiscovered": 0 }, - "Checks": { "Total": 0, "Undiscovered": 0 } - }, - "Current": { - "Scene": "No Scene", - "Respawn": "", - "Dath": "", - "HasLaurels": false, - "HasDath": false - }, - "Scenes": { - "No Scene": { - "Totals": { - "Entrances": { "Total": 0, "Undiscovered": 0 }, - "Checks": { "Total": 0, "Undiscovered": 0 } - }, - "Checks": {}, - "Entrances": {} - } - }, - "Codes": { - "None": { - "None": "" - } - } -} diff --git a/tunictracker/less_fun_doors.json b/tunictracker/less_fun_doors.json new file mode 100644 index 0000000..c8aa6cb --- /dev/null +++ b/tunictracker/less_fun_doors.json @@ -0,0 +1,643 @@ +{ + "found": 0, + "remaining": 216, + "total": 216, + "scenes": { + "Beneath the Fortress": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Beneath the Earth to Fortress Courtyard": { "scene": "", "door": "" }, + "Beneath the Earth to Fortress Interior": { "scene": "", "door": "" } + } + }, + "Beneath the Well": { + "found": 0, + "remaining": 3, + "total": 3, + "doors": { + "Well Exit towards Furnace": { "scene": "", "door": "" }, + "Well Ladder Exit": { "scene": "", "door": "" }, + "Well to Well Boss": { "scene": "", "door": "" } + } + }, + "Cathedral": { + "found": 0, + "remaining": 3, + "total": 3, + "doors": { + "Cathedral Elevator": { "scene": "", "door": "" }, + "Cathedral Main Exit": { "scene": "", "door": "" }, + "Cathedral Secret Legend Room Exit": { "scene": "", "door": "" } + } + }, + "Cathedral Gauntlet": { + "found": 0, + "remaining": 3, + "total": 3, + "doors": { + "Gauntlet Elevator": { "scene": "", "door": "" }, + "Gauntlet Shop": { "scene": "", "door": "" }, + "Gauntlet to Swamp": { "scene": "", "door": "" } + } + }, + "Caustic Light Cave": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Caustic Light Cave Exit": { "scene": "", "door": "" } } + }, + "Changing Room": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Changing Room Exit": { "scene": "", "door": "" } } + }, + "Coins in the Well": { + "found": 0, + "remaining": 0, + "total": 0, + "doors": {} + }, + "Cube Cave": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Cube Cave Exit": { "scene": "", "door": "" } } + }, + "Dark Tomb": { + "found": 0, + "remaining": 3, + "total": 3, + "doors": { + "Dark Tomb to Checkpoint": { "scene": "", "door": "" }, + "Dark Tomb to Furnace": { "scene": "", "door": "" }, + "Dark Tomb to Overworld": { "scene": "", "door": "" } + } + }, + "Dark Tomb Checkpoint": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Checkpoint to Dark Tomb": { "scene": "", "door": "" }, + "Well Boss to Well": { "scene": "", "door": "" } + } + }, + "East Forest": { + "found": 0, + "remaining": 9, + "total": 9, + "doors": { + "Forest Dance Fox Outside Doorway": { "scene": "", "door": "" }, + "Forest Grave Path Lower Entrance": { "scene": "", "door": "" }, + "Forest Grave Path Upper Entrance": { "scene": "", "door": "" }, + "Forest Guard House 1 Gate Entrance": { "scene": "", "door": "" }, + "Forest Guard House 1 Lower Entrance": { "scene": "", "door": "" }, + "Forest Guard House 2 Lower Entrance": { "scene": "", "door": "" }, + "Forest Guard House 2 Upper Entrance": { "scene": "", "door": "" }, + "Forest to Belltower": { "scene": "", "door": "" }, + "Forest to Far Shore": { "scene": "", "door": "" } + } + }, + "Eastern Vault Fortress": { + "found": 0, + "remaining": 6, + "total": 6, + "doors": { + "Fortress Interior Main Exit": { "scene": "", "door": "" }, + "Fortress Interior Shop": { "scene": "", "door": "" }, + "Fortress Interior to Beneath the Earth": { "scene": "", "door": "" }, + "Fortress Interior to East Fortress Lower": { "scene": "", "door": "" }, + "Fortress Interior to East Fortress Upper": { "scene": "", "door": "" }, + "Fortress Interior to Siege Engine Arena": { "scene": "", "door": "" } + } + }, + "Far Shore": { + "found": 0, + "remaining": 10, + "total": 10, + "doors": { + "Far Shore to Atoll": { "scene": "", "door": "" }, + "Far Shore to East Forest": { "scene": "", "door": "" }, + "Far Shore to Fortress": { "scene": "", "door": "" }, + "Far Shore to Heir": { "scene": "", "door": "" }, + "Far Shore to Library": { "scene": "", "door": "" }, + "Far Shore to Quarry": { "scene": "", "door": "" }, + "Far Shore to Spawn": { "scene": "", "door": "" }, + "Far Shore to Town": { "scene": "", "door": "" }, + "Far Shore to West Garden": { "scene": "", "door": "" }, + "Far Shore to Ziggurat": { "scene": "", "door": "" } + } + }, + "Forest Belltower": { + "found": 0, + "remaining": 4, + "total": 4, + "doors": { + "Forest Belltower to Forest": { "scene": "", "door": "" }, + "Forest Belltower to Fortress": { "scene": "", "door": "" }, + "Forest Belltower to Guard Captain Room": { "scene": "", "door": "" }, + "Forest Belltower to Overworld": { "scene": "", "door": "" } + } + }, + "Forest Boss Room": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Guard Captain Room Gate Exit": { "scene": "", "door": "" }, + "Guard Captain Room Non-Gate Exit": { "scene": "", "door": "" } + } + }, + "Forest Grave Path": { + "found": 0, + "remaining": 3, + "total": 3, + "doors": { + "East Forest Hero's Grave": { "scene": "", "door": "" }, + "Forest Grave Path Lower Exit": { "scene": "", "door": "" }, + "Forest Grave Path Upper Exit": { "scene": "", "door": "" } + } + }, + "Fortress Arena": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Fortress to Far Shore": { "scene": "", "door": "" }, + "Siege Engine Arena to Fortress": { "scene": "", "door": "" } + } + }, + "Fortress Courtyard": { + "found": 0, + "remaining": 8, + "total": 8, + "doors": { + "Fortress Courtyard Shop": { "scene": "", "door": "" }, + "Fortress Courtyard to Beneath the Earth": { "scene": "", "door": "" }, + "Fortress Courtyard to East Fortress": { "scene": "", "door": "" }, + "Fortress Courtyard to Forest Belltower": { "scene": "", "door": "" }, + "Fortress Courtyard to Fortress Grave Path Lower": { + "scene": "", + "door": "" + }, + "Fortress Courtyard to Fortress Grave Path Upper": { + "scene": "", + "door": "" + }, + "Fortress Courtyard to Fortress Interior": { "scene": "", "door": "" }, + "Fortress Courtyard to Overworld": { "scene": "", "door": "" } + } + }, + "Fortress East Shortcut": { + "found": 0, + "remaining": 3, + "total": 3, + "doors": { + "East Fortress to Courtyard": { "scene": "", "door": "" }, + "East Fortress to Interior Lower": { "scene": "", "door": "" }, + "East Fortress to Interior Upper": { "scene": "", "door": "" } + } + }, + "Fortress Grave Path": { + "found": 0, + "remaining": 4, + "total": 4, + "doors": { + "Fortress Grave Path Dusty Entrance": { "scene": "", "door": "" }, + "Fortress Grave Path Lower Exit": { "scene": "", "door": "" }, + "Fortress Grave Path Upper Exit": { "scene": "", "door": "" }, + "Fortress Hero's Grave": { "scene": "", "door": "" } + } + }, + "Fortress Leaf Piles": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Dusty Exit": { "scene": "", "door": "" } } + }, + "Fountain Cross Room": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Fountain HC Room Exit": { "scene": "", "door": "" } } + }, + "Frog Stairway": { + "found": 0, + "remaining": 4, + "total": 4, + "doors": { + "Frog Stairs Eye Exit": { "scene": "", "door": "" }, + "Frog Stairs Mouth Exit": { "scene": "", "door": "" }, + "Frog Stairs to Frog's Domain's Entrance": { "scene": "", "door": "" }, + "Frog Stairs to Frog's Domain's Exit": { "scene": "", "door": "" } + } + }, + "Frog's Domain": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Frog's Domain Ladder Exit": { "scene": "", "door": "" }, + "Frog's Domain Orb Exit": { "scene": "", "door": "" } + } + }, + "Glyph Tower": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Glyph Tower Exit": { "scene": "", "door": "" } } + }, + "Guardhouse 1": { + "found": 0, + "remaining": 4, + "total": 4, + "doors": { + "Guard House 1 Dance Fox Exit": { "scene": "", "door": "" }, + "Guard House 1 Lower Exit": { "scene": "", "door": "" }, + "Guard House 1 to Guard Captain Room": { "scene": "", "door": "" }, + "Guard House 1 Upper Forest Exit": { "scene": "", "door": "" } + } + }, + "Guardhouse 2": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Guard House 2 Lower Exit": { "scene": "", "door": "" }, + "Guard House 2 Upper Exit": { "scene": "", "door": "" } + } + }, + "Hero's Grave": { + "found": 0, + "remaining": 6, + "total": 6, + "doors": { + "Hero's Grave to East Forest": { "scene": "", "door": "" }, + "Hero's Grave to Fortress": { "scene": "", "door": "" }, + "Hero's Grave to Library": { "scene": "", "door": "" }, + "Hero's Grave to Monastery": { "scene": "", "door": "" }, + "Hero's Grave to Swamp": { "scene": "", "door": "" }, + "Hero's Grave to West Garden": { "scene": "", "door": "" } + } + }, + "Hourglass Cave": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Hourglass Cave Exit": { "scene": "", "door": "" } } + }, + "Librarian": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Librarian Arena Exit": { "scene": "", "door": "" } } + }, + "Library Exterior": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Library Exterior Ladder": { "scene": "", "door": "" }, + "Library Exterior Tree": { "scene": "", "door": "" } + } + }, + "Library Hall": { + "found": 0, + "remaining": 3, + "total": 3, + "doors": { + "Library Hall Bookshelf Exit": { "scene": "", "door": "" }, + "Library Hall to Rotunda": { "scene": "", "door": "" }, + "Library Hero's Grave": { "scene": "", "door": "" } + } + }, + "Library Lab": { + "found": 0, + "remaining": 3, + "total": 3, + "doors": { + "Library Lab to Librarian Arena": { "scene": "", "door": "" }, + "Library Lab to Rotunda": { "scene": "", "door": "" }, + "Library to Far Shore": { "scene": "", "door": "" } + } + }, + "Library Rotunda": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Library Rotunda Lower Exit": { "scene": "", "door": "" }, + "Library Rotunda Upper Exit": { "scene": "", "door": "" } + } + }, + "Loading": { "found": 0, "remaining": 0, "total": 0, "doors": {} }, + "Lower Mountain": { + "found": 0, + "remaining": 3, + "total": 3, + "doors": { + "Mountain to Overworld": { "scene": "", "door": "" }, + "Mountain to Quarry": { "scene": "", "door": "" }, + "Stairs to Top of the Mountain": { "scene": "", "door": "" } + } + }, + "Maze Cave": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Maze Cave Exit": { "scene": "", "door": "" } } + }, + "Monastery": { + "found": 0, + "remaining": 3, + "total": 3, + "doors": { + "Monastery Front Exit": { "scene": "", "door": "" }, + "Monastery Hero's Grave": { "scene": "", "door": "" }, + "Monastery Rear Exit": { "scene": "", "door": "" } + } + }, + "Old House": { + "found": 0, + "remaining": 3, + "total": 3, + "doors": { + "Old House Door Exit": { "scene": "", "door": "" }, + "Old House to Glyph Tower": { "scene": "", "door": "" }, + "Old House Waterfall Exit": { "scene": "", "door": "" } + } + }, + "Overworld": { + "found": 0, + "remaining": 39, + "total": 39, + "doors": { + "Atoll Lower Entrance": { "scene": "", "door": "" }, + "Atoll Upper Entrance": { "scene": "", "door": "" }, + "Caustic Light Cave Entrance": { "scene": "", "door": "" }, + "Changing Room Entrance": { "scene": "", "door": "" }, + "Cube Cave Entrance": { "scene": "", "door": "" }, + "Dark Tomb Main Entrance": { "scene": "", "door": "" }, + "Entrance to Furnace from Beach": { "scene": "", "door": "" }, + "Entrance to Furnace from Well Rail": { "scene": "", "door": "" }, + "Entrance to Furnace near West Garden": { "scene": "", "door": "" }, + "Entrance to Furnace under Windmill": { "scene": "", "door": "" }, + "Entrance to Well from Well Rail": { "scene": "", "door": "" }, + "Fountain HC Door Entrance": { "scene": "", "door": "" }, + "Hourglass Cave Entrance": { "scene": "", "door": "" }, + "Maze Cave Entrance": { "scene": "", "door": "" }, + "Old House Door Entrance": { "scene": "", "door": "" }, + "Old House Waterfall Entrance": { "scene": "", "door": "" }, + "Overworld to Forest Belltower": { "scene": "", "door": "" }, + "Overworld to Fortress": { "scene": "", "door": "" }, + "Overworld to Quarry Connector": { "scene": "", "door": "" }, + "Patrol Cave Entrance": { "scene": "", "door": "" }, + "Ruined Passage Door Entrance": { "scene": "", "door": "" }, + "Ruined Passage Not-Door Entrance": { "scene": "", "door": "" }, + "Ruined Shop Entrance": { "scene": "", "door": "" }, + "Secret Gathering Place Entrance": { "scene": "", "door": "" }, + "Southeast HC Door Entrance": { "scene": "", "door": "" }, + "Spawn to Far Shore": { "scene": "", "door": "" }, + "Special Shop Entrance": { "scene": "", "door": "" }, + "Stairs from Overworld to Mountain": { "scene": "", "door": "" }, + "Stick House Entrance": { "scene": "", "door": "" }, + "Swamp Lower Entrance": { "scene": "", "door": "" }, + "Swamp Upper Entrance": { "scene": "", "door": "" }, + "Temple Door Entrance": { "scene": "", "door": "" }, + "Temple Rafters Entrance": { "scene": "", "door": "" }, + "Town to Far Shore": { "scene": "", "door": "" }, + "Well Ladder Entrance": { "scene": "", "door": "" }, + "West Garden Entrance from Furnace": { "scene": "", "door": "" }, + "West Garden Entrance near Belltower": { "scene": "", "door": "" }, + "West Garden Laurels Entrance": { "scene": "", "door": "" }, + "Windmill Entrance": { "scene": "", "door": "" } + } + }, + "Patrol Cave": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Guard Patrol Cave Exit": { "scene": "", "door": "" } } + }, + "Posterity": { "found": 0, "remaining": 0, "total": 0, "doors": {} }, + "Purgatory": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Purgatory Bottom Exit": { "scene": "", "door": "" }, + "Purgatory Top Exit": { "scene": "", "door": "" } + } + }, + "Quarry": { + "found": 0, + "remaining": 7, + "total": 7, + "doors": { + "Quarry Shop": { "scene": "", "door": "" }, + "Quarry to Far Shore": { "scene": "", "door": "" }, + "Quarry to Monastery Back": { "scene": "", "door": "" }, + "Quarry to Monastery Front": { "scene": "", "door": "" }, + "Quarry to Mountain": { "scene": "", "door": "" }, + "Quarry to Overworld Exit": { "scene": "", "door": "" }, + "Quarry to Ziggurat": { "scene": "", "door": "" } + } + }, + "Quarry Entryway": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Quarry Connector to Overworld": { "scene": "", "door": "" }, + "Quarry Connector to Quarry": { "scene": "", "door": "" } + } + }, + "Resurrection": { "found": 0, "remaining": 0, "total": 0, "doors": {} }, + "Rooted Ziggurat Entrance": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Ziggurat Entry Hallway to Quarry": { "scene": "", "door": "" }, + "Ziggurat Entry Hallway to Ziggurat Upper": { "scene": "", "door": "" } + } + }, + "Rooted Ziggurat Lower": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Ziggurat Lower to Ziggurat Tower": { "scene": "", "door": "" }, + "Ziggurat Portal Room Entrance": { "scene": "", "door": "" } + } + }, + "Rooted Ziggurat Teleporter": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Ziggurat Portal Room Exit": { "scene": "", "door": "" }, + "Ziggurat to Far Shore": { "scene": "", "door": "" } + } + }, + "Rooted Ziggurat Tower": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Ziggurat Tower to Ziggurat Lower": { "scene": "", "door": "" }, + "Ziggurat Tower to Ziggurat Upper": { "scene": "", "door": "" } + } + }, + "Rooted Ziggurat Upper": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Ziggurat Upper to Ziggurat Entry Hallway": { "scene": "", "door": "" }, + "Ziggurat Upper to Ziggurat Tower": { "scene": "", "door": "" } + } + }, + "Ruined Atoll": { + "found": 0, + "remaining": 7, + "total": 7, + "doors": { + "Atoll Lower Exit": { "scene": "", "door": "" }, + "Atoll Shop": { "scene": "", "door": "" }, + "Atoll Statue Teleporter": { "scene": "", "door": "" }, + "Atoll to Far Shore": { "scene": "", "door": "" }, + "Atoll Upper Exit": { "scene": "", "door": "" }, + "Frog Stairs Eye Entrance": { "scene": "", "door": "" }, + "Frog Stairs Mouth Entrance": { "scene": "", "door": "" } + } + }, + "Ruined Passage": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Ruined Passage Door Exit": { "scene": "", "door": "" }, + "Ruined Passage Not-Door Exit": { "scene": "", "door": "" } + } + }, + "Ruined Shop": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Ruined Shop Exit": { "scene": "", "door": "" } } + }, + "Sealed Temple": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Temple Door Exit": { "scene": "", "door": "" }, + "Temple Rafters Exit": { "scene": "", "door": "" } + } + }, + "Secret Gathering Place": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Secret Gathering Place Exit": { "scene": "", "door": "" } } + }, + "Shop": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { "Shop Portal": { "scene": "", "door": "" } } + }, + "Southeast Cross Room": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Southeast HC Room Exit": { "scene": "", "door": "" } } + }, + "Special Shop": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Special Shop Exit": { "scene": "", "door": "" } } + }, + "Stick House": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Stick House Exit": { "scene": "", "door": "" } } + }, + "Swamp": { + "found": 0, + "remaining": 7, + "total": 7, + "doors": { + "Swamp Hero's Grave": { "scene": "", "door": "" }, + "Swamp Lower Exit": { "scene": "", "door": "" }, + "Swamp Shop": { "scene": "", "door": "" }, + "Swamp to Cathedral Main Entrance": { "scene": "", "door": "" }, + "Swamp to Cathedral Secret Legend Room Entrance": { + "scene": "", + "door": "" + }, + "Swamp to Gauntlet": { "scene": "", "door": "" }, + "Swamp Upper Exit": { "scene": "", "door": "" } + } + }, + "The Heir": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Heir Arena Exit": { "scene": "", "door": "" } } + }, + "Top of the Mountain": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Top of the Mountain Exit": { "scene": "", "door": "" } } + }, + "West Furnace": { + "found": 0, + "remaining": 5, + "total": 5, + "doors": { + "Furnace Exit to Beach": { "scene": "", "door": "" }, + "Furnace Exit to Dark Tomb": { "scene": "", "door": "" }, + "Furnace Exit towards Well": { "scene": "", "door": "" }, + "Furnace Exit towards West Garden": { "scene": "", "door": "" }, + "Furnace Exit under Windmill": { "scene": "", "door": "" } + } + }, + "West Garden": { + "found": 0, + "remaining": 7, + "total": 7, + "doors": { + "West Garden Exit after Boss": { "scene": "", "door": "" }, + "West Garden Exit near Hero's Grave": { "scene": "", "door": "" }, + "West Garden Hero's Grave": { "scene": "", "door": "" }, + "West Garden Laurels Exit": { "scene": "", "door": "" }, + "West Garden Shop": { "scene": "", "door": "" }, + "West Garden to Far Shore": { "scene": "", "door": "" }, + "West Garden to Magic Dagger House": { "scene": "", "door": "" } + } + }, + "West Garden House": { + "found": 0, + "remaining": 1, + "total": 1, + "doors": { "Magic Dagger House Exit": { "scene": "", "door": "" } } + }, + "Windmill": { + "found": 0, + "remaining": 2, + "total": 2, + "doors": { + "Windmill Exit": { "scene": "", "door": "" }, + "Windmill Shop": { "scene": "", "door": "" } + } + } + } +} diff --git a/tunictracker/less_fun_hints.json b/tunictracker/less_fun_hints.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/tunictracker/less_fun_hints.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tunictracker/less_fun_items.json b/tunictracker/less_fun_items.json new file mode 100644 index 0000000..ef4ee51 --- /dev/null +++ b/tunictracker/less_fun_items.json @@ -0,0 +1,752 @@ +{ + "collected": 0, + "remaining": 302, + "total": 302, + "scenes": { + "Beneath the Fortress": { + "collected": 0, + "remaining": 5, + "total": 5, + "checks": { + "Back Room Chest": { "name": "", "owner": "" }, + "Bridge": { "name": "", "owner": "" }, + "Cell Chest 1": { "name": "", "owner": "" }, + "Cell Chest 2": { "name": "", "owner": "" }, + "Obscured Behind Waterfall": { "name": "", "owner": "" } + } + }, + "Beneath the Well": { + "collected": 0, + "remaining": 14, + "total": 14, + "checks": { + "[Back Corridor] Left Secret": { "name": "", "owner": "" }, + "[Back Corridor] Right Secret": { "name": "", "owner": "" }, + "[Entryway] Chest": { "name": "", "owner": "" }, + "[Entryway] Obscured Behind Waterfall": { "name": "", "owner": "" }, + "[Powered Secret Room] Chest": { "name": "", "owner": "" }, + "[Save Room] Upper Floor Chest 1": { "name": "", "owner": "" }, + "[Save Room] Upper Floor Chest 2": { "name": "", "owner": "" }, + "[Second Room] Obscured Behind Waterfall": { "name": "", "owner": "" }, + "[Second Room] Page": { "name": "", "owner": "" }, + "[Second Room] Underwater Chest": { "name": "", "owner": "" }, + "[Side Room] Chest By Phrends": { "name": "", "owner": "" }, + "[Side Room] Chest By Pots": { "name": "", "owner": "" }, + "[Third Room] Beneath Platform Chest": { "name": "", "owner": "" }, + "[Third Room] Tentacle Chest": { "name": "", "owner": "" } + } + }, + "Cathedral": { + "collected": 0, + "remaining": 10, + "total": 10, + "checks": { + "[1F] Guarded By Lasers": { "name": "", "owner": "" }, + "[1F] Library": { "name": "", "owner": "" }, + "[1F] Library Secret": { "name": "", "owner": "" }, + "[1F] Near Spikes": { "name": "", "owner": "" }, + "[2F] Bird Room": { "name": "", "owner": "" }, + "[2F] Bird Room Secret": { "name": "", "owner": "" }, + "[2F] Entryway Upper Walkway": { "name": "", "owner": "" }, + "[2F] Guarded By Lasers": { "name": "", "owner": "" }, + "[2F] Library": { "name": "", "owner": "" }, + "Secret Legend Trophy Chest": { "name": "", "owner": "" } + } + }, + "Cathedral Gauntlet": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Gauntlet Reward": { "name": "", "owner": "" } } + }, + "Caustic Light Cave": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Holy Cross Chest": { "name": "", "owner": "" } } + }, + "Changing Room": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Normal Chest": { "name": "", "owner": "" } } + }, + "Coins in the Well": { + "collected": 0, + "remaining": 4, + "total": 4, + "checks": { + "10 Coins": { "name": "", "owner": "" }, + "15 Coins": { "name": "", "owner": "" }, + "3 Coins": { "name": "", "owner": "" }, + "6 Coins": { "name": "", "owner": "" } + } + }, + "Cube Cave": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Holy Cross Chest": { "name": "", "owner": "" } } + }, + "Dark Tomb": { + "collected": 0, + "remaining": 7, + "total": 7, + "checks": { + "1st Laser Room": { "name": "", "owner": "" }, + "1st Laser Room Obscured": { "name": "", "owner": "" }, + "2nd Laser Room": { "name": "", "owner": "" }, + "Skulls Chest": { "name": "", "owner": "" }, + "Spike Maze Near Exit": { "name": "", "owner": "" }, + "Spike Maze Near Stairs": { "name": "", "owner": "" }, + "Spike Maze Upper Walkway": { "name": "", "owner": "" } + } + }, + "Dark Tomb Checkpoint": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { + "[Passage To Dark Tomb] Page Pickup": { "name": "", "owner": "" } + } + }, + "East Forest": { + "collected": 0, + "remaining": 14, + "total": 14, + "checks": { + "Above Save Point": { "name": "", "owner": "" }, + "Above Save Point Obscured": { "name": "", "owner": "" }, + "Beneath Spider Chest": { "name": "", "owner": "" }, + "Bombable Wall": { "name": "", "owner": "" }, + "Dancing Fox Spirit Holy Cross": { "name": "", "owner": "" }, + "From Guardhouse 1 Chest": { "name": "", "owner": "" }, + "Golden Obelisk Holy Cross": { "name": "", "owner": "" }, + "Ice Rod Grapple Chest": { "name": "", "owner": "" }, + "Lower Dash Chest": { "name": "", "owner": "" }, + "Lower Grapple Chest": { "name": "", "owner": "" }, + "Near Save Point": { "name": "", "owner": "" }, + "Near Telescope": { "name": "", "owner": "" }, + "Page On Teleporter": { "name": "", "owner": "" }, + "Spider Chest": { "name": "", "owner": "" } + } + }, + "Eastern Vault Fortress": { + "collected": 0, + "remaining": 5, + "total": 5, + "checks": { + "[East Wing] Bombable Wall": { "name": "", "owner": "" }, + "[West Wing] Candles Holy Cross": { "name": "", "owner": "" }, + "[West Wing] Dark Room Chest 1": { "name": "", "owner": "" }, + "[West Wing] Dark Room Chest 2": { "name": "", "owner": "" }, + "[West Wing] Page Pickup": { "name": "", "owner": "" } + } + }, + "Far Shore": { + "collected": 0, + "remaining": 2, + "total": 2, + "checks": { + "Page Pickup": { "name": "", "owner": "" }, + "Secret Chest": { "name": "", "owner": "" } + } + }, + "Forest Belltower": { + "collected": 0, + "remaining": 5, + "total": 5, + "checks": { + "After Guard Captain": { "name": "", "owner": "" }, + "Near Save Point": { "name": "", "owner": "" }, + "Obscured Beneath Bell Bottom Floor": { "name": "", "owner": "" }, + "Obscured Near Bell Top Floor": { "name": "", "owner": "" }, + "Page Pickup": { "name": "", "owner": "" } + } + }, + "Forest Boss Room": { + "collected": 0, + "remaining": 0, + "total": 0, + "checks": {} + }, + "Forest Grave Path": { + "collected": 0, + "remaining": 5, + "total": 5, + "checks": { + "Above Gate": { "name": "", "owner": "" }, + "Holy Cross Code by Grave": { "name": "", "owner": "" }, + "Obscured Chest": { "name": "", "owner": "" }, + "Sword Pickup": { "name": "", "owner": "" }, + "Upper Walkway": { "name": "", "owner": "" } + } + }, + "Fortress Arena": { + "collected": 0, + "remaining": 2, + "total": 2, + "checks": { + "Hexagon Red": { "name": "", "owner": "" }, + "Siege Engine/Vault Key Pickup": { "name": "", "owner": "" } + } + }, + "Fortress Courtyard": { + "collected": 0, + "remaining": 5, + "total": 5, + "checks": { + "Below Walkway": { "name": "", "owner": "" }, + "Chest Near Cave": { "name": "", "owner": "" }, + "From East Belltower": { "name": "", "owner": "" }, + "Near Fuse": { "name": "", "owner": "" }, + "Page Near Cave": { "name": "", "owner": "" } + } + }, + "Fortress East Shortcut": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Chest Near Slimes": { "name": "", "owner": "" } } + }, + "Fortress Grave Path": { + "collected": 0, + "remaining": 3, + "total": 3, + "checks": { + "Chest Right of Grave": { "name": "", "owner": "" }, + "Obscured Chest Left of Grave": { "name": "", "owner": "" }, + "Upper Walkway": { "name": "", "owner": "" } + } + }, + "Fortress Leaf Piles": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Secret Chest": { "name": "", "owner": "" } } + }, + "Fountain Cross Room": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Page Pickup": { "name": "", "owner": "" } } + }, + "Frog Stairway": { + "collected": 0, + "remaining": 0, + "total": 0, + "checks": {} + }, + "Frog's Domain": { + "collected": 0, + "remaining": 11, + "total": 11, + "checks": { + "Above Vault": { "name": "", "owner": "" }, + "Escape Chest": { "name": "", "owner": "" }, + "Grapple Above Hot Tub": { "name": "", "owner": "" }, + "Magic Orb Pickup": { "name": "", "owner": "" }, + "Main Room Bottom Floor": { "name": "", "owner": "" }, + "Main Room Top Floor": { "name": "", "owner": "" }, + "Near Vault": { "name": "", "owner": "" }, + "Side Room Chest": { "name": "", "owner": "" }, + "Side Room Grapple Secret": { "name": "", "owner": "" }, + "Side Room Secret Passage": { "name": "", "owner": "" }, + "Slorm Room": { "name": "", "owner": "" } + } + }, + "Glyph Tower": { "collected": 0, "remaining": 0, "total": 0, "checks": {} }, + "Guardhouse 1": { + "collected": 0, + "remaining": 2, + "total": 2, + "checks": { + "Upper Floor": { "name": "", "owner": "" }, + "Upper Floor Obscured": { "name": "", "owner": "" } + } + }, + "Guardhouse 2": { + "collected": 0, + "remaining": 2, + "total": 2, + "checks": { + "Bottom Floor Secret": { "name": "", "owner": "" }, + "Upper Floor": { "name": "", "owner": "" } + } + }, + "Hero's Grave": { + "collected": 0, + "remaining": 6, + "total": 6, + "checks": { + "Ash Relic": { "name": "", "owner": "" }, + "Effigy Relic": { "name": "", "owner": "" }, + "Feathers Relic": { "name": "", "owner": "" }, + "Flowers Relic": { "name": "", "owner": "" }, + "Mushroom Relic": { "name": "", "owner": "" }, + "Tooth Relic": { "name": "", "owner": "" } + } + }, + "Hourglass Cave": { + "collected": 0, + "remaining": 2, + "total": 2, + "checks": { + "Holy Cross Chest": { "name": "", "owner": "" }, + "Hourglass Chest": { "name": "", "owner": "" } + } + }, + "Librarian": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Hexagon Green": { "name": "", "owner": "" } } + }, + "Library Exterior": { + "collected": 0, + "remaining": 0, + "total": 0, + "checks": {} + }, + "Library Hall": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Holy Cross Chest": { "name": "", "owner": "" } } + }, + "Library Lab": { + "collected": 0, + "remaining": 7, + "total": 7, + "checks": { + "Behind Chalkboard by Fuse": { "name": "", "owner": "" }, + "Chest By Shrine 1": { "name": "", "owner": "" }, + "Chest By Shrine 2": { "name": "", "owner": "" }, + "Chest By Shrine 3": { "name": "", "owner": "" }, + "Page 1": { "name": "", "owner": "" }, + "Page 2": { "name": "", "owner": "" }, + "Page 3": { "name": "", "owner": "" } + } + }, + "Library Rotunda": { + "collected": 0, + "remaining": 0, + "total": 0, + "checks": {} + }, + "Loading": { "collected": 0, "remaining": 0, "total": 0, "checks": {} }, + "Lower Mountain": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Page Before Door": { "name": "", "owner": "" } } + }, + "Maze Cave": { + "collected": 0, + "remaining": 2, + "total": 2, + "checks": { + "Maze Room Chest": { "name": "", "owner": "" }, + "Maze Room Holy Cross": { "name": "", "owner": "" } + } + }, + "Monastery": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Monastery Chest": { "name": "", "owner": "" } } + }, + "Old House": { + "collected": 0, + "remaining": 4, + "total": 4, + "checks": { + "Holy Cross Chest": { "name": "", "owner": "" }, + "Holy Cross Door Page": { "name": "", "owner": "" }, + "Normal Chest": { "name": "", "owner": "" }, + "Shield Pickup": { "name": "", "owner": "" } + } + }, + "Overworld": { + "collected": 0, + "remaining": 50, + "total": 50, + "checks": { + "[Central] Bombable Wall": { "name": "", "owner": "" }, + "[Central] Chest Across From Well": { "name": "", "owner": "" }, + "[East] Between Ladders Near Ruined Passage": { + "name": "", + "owner": "" + }, + "[East] Chest In Trees": { "name": "", "owner": "" }, + "[East] Chest Near Pots": { "name": "", "owner": "" }, + "[East] Grapple Chest": { "name": "", "owner": "" }, + "[East] Page Near Secret Shop": { "name": "", "owner": "" }, + "[East] Weathervane Holy Cross": { "name": "", "owner": "" }, + "[Northeast] Chest Above Patrol Cave": { "name": "", "owner": "" }, + "[Northeast] Flowers Holy Cross": { "name": "", "owner": "" }, + "[Northwest] Chest Beneath Quarry Gate": { "name": "", "owner": "" }, + "[Northwest] Chest Near Golden Obelisk": { "name": "", "owner": "" }, + "[Northwest] Chest Near Quarry Gate": { "name": "", "owner": "" }, + "[Northwest] Chest Near Turret": { "name": "", "owner": "" }, + "[Northwest] Fire Wand Pickup": { "name": "", "owner": "" }, + "[Northwest] Golden Obelisk Page": { "name": "", "owner": "" }, + "[Northwest] Page By Well": { "name": "", "owner": "" }, + "[Northwest] Page on Pillar by Dark Tomb": { "name": "", "owner": "" }, + "[Northwest] Shadowy Corner Chest": { "name": "", "owner": "" }, + "[South] Beach Chest": { "name": "", "owner": "" }, + "[South] Beach Page": { "name": "", "owner": "" }, + "[South] Starting Platform Holy Cross": { "name": "", "owner": "" }, + "[Southeast] Chest Near Swamp": { "name": "", "owner": "" }, + "[Southeast] Page on Pillar by Swamp": { "name": "", "owner": "" }, + "[Southwest] Beach Chest Beneath Guard": { "name": "", "owner": "" }, + "[Southwest] Beach Chest Near Flowers": { "name": "", "owner": "" }, + "[Southwest] Bombable Wall Near Fountain": { "name": "", "owner": "" }, + "[Southwest] Chest Guarded By Turret": { "name": "", "owner": "" }, + "[Southwest] Flowers Holy Cross": { "name": "", "owner": "" }, + "[Southwest] Fountain Holy Cross": { "name": "", "owner": "" }, + "[Southwest] Fountain Page": { "name": "", "owner": "" }, + "[Southwest] From West Garden": { "name": "", "owner": "" }, + "[Southwest] Grapple Chest Over Walkway": { "name": "", "owner": "" }, + "[Southwest] Haiku Holy Cross": { "name": "", "owner": "" }, + "[Southwest] Key Pickup": { "name": "", "owner": "" }, + "[Southwest] Obscured In Tunnel To Beach": { "name": "", "owner": "" }, + "[Southwest] South Chest Near Guard": { "name": "", "owner": "" }, + "[Southwest] Tunnel Guarded By Turret": { "name": "", "owner": "" }, + "[Southwest] West Beach Guarded By Turret": { "name": "", "owner": "" }, + "[Southwest] West Beach Guarded By Turret 2": { + "name": "", + "owner": "" + }, + "[West] Chest After Bell": { "name": "", "owner": "" }, + "[West] Chest Behind Moss Wall": { "name": "", "owner": "" }, + "[West] Key Pickup": { "name": "", "owner": "" }, + "[West] Moss Wall Holy Cross": { "name": "", "owner": "" }, + "[West] Near West Garden Entrance": { "name": "", "owner": "" }, + "[West] Obscured Behind Windmill": { "name": "", "owner": "" }, + "[West] Obscured Near Well": { "name": "", "owner": "" }, + "[West] Page On Teleporter": { "name": "", "owner": "" }, + "[West] Windchimes Holy Cross": { "name": "", "owner": "" }, + "[West] Windmill Holy Cross": { "name": "", "owner": "" } + } + }, + "Patrol Cave": { + "collected": 0, + "remaining": 2, + "total": 2, + "checks": { + "Holy Cross Chest": { "name": "", "owner": "" }, + "Normal Chest": { "name": "", "owner": "" } + } + }, + "Posterity": { "collected": 0, "remaining": 0, "total": 0, "checks": {} }, + "Purgatory": { "collected": 0, "remaining": 0, "total": 0, "checks": {} }, + "Quarry": { + "collected": 0, + "remaining": 28, + "total": 28, + "checks": { + "[Back Entrance] Bushes Holy Cross": { "name": "", "owner": "" }, + "[Back Entrance] Chest": { "name": "", "owner": "" }, + "[Back Entrance] Obscured Behind Wall": { "name": "", "owner": "" }, + "[Central] Above Ladder": { "name": "", "owner": "" }, + "[Central] Above Ladder Dash Chest": { "name": "", "owner": "" }, + "[Central] Below Entry Walkway": { "name": "", "owner": "" }, + "[Central] Near Shortcut Ladder": { "name": "", "owner": "" }, + "[Central] Obscured Behind Staircase": { "name": "", "owner": "" }, + "[Central] Obscured Below Entry Walkway": { "name": "", "owner": "" }, + "[Central] Top Floor Overhang": { "name": "", "owner": "" }, + "[East] Bombable Wall": { "name": "", "owner": "" }, + "[East] Near Bridge": { "name": "", "owner": "" }, + "[East] Near Telescope": { "name": "", "owner": "" }, + "[East] Obscured Beneath Scaffolding": { "name": "", "owner": "" }, + "[East] Obscured Near Telescope": { "name": "", "owner": "" }, + "[East] Obscured Near Winding Staircase": { "name": "", "owner": "" }, + "[East] Upper Floor": { "name": "", "owner": "" }, + "[Lowlands] Below Broken Ladder": { "name": "", "owner": "" }, + "[Lowlands] Near Elevator": { "name": "", "owner": "" }, + "[Lowlands] Upper Walkway": { "name": "", "owner": "" }, + "[West] Below Shooting Range": { "name": "", "owner": "" }, + "[West] Lower Area After Bridge": { "name": "", "owner": "" }, + "[West] Lower Area Below Bridge": { "name": "", "owner": "" }, + "[West] Lower Area Isolated Chest": { "name": "", "owner": "" }, + "[West] Near Shooting Range": { "name": "", "owner": "" }, + "[West] Shooting Range Secret Path": { "name": "", "owner": "" }, + "[West] Upper Area Bombable Wall": { "name": "", "owner": "" }, + "[West] Upper Area Near Waterfall": { "name": "", "owner": "" } + } + }, + "Quarry Entryway": { + "collected": 0, + "remaining": 0, + "total": 0, + "checks": {} + }, + "Resurrection": { + "collected": 0, + "remaining": 0, + "total": 0, + "checks": {} + }, + "Rooted Ziggurat Entrance": { + "collected": 0, + "remaining": 0, + "total": 0, + "checks": {} + }, + "Rooted Ziggurat Lower": { + "collected": 0, + "remaining": 8, + "total": 8, + "checks": { + "After 2nd Double Turret Chest": { "name": "", "owner": "" }, + "After Guarded Fuse": { "name": "", "owner": "" }, + "Guarded By Double Turrets": { "name": "", "owner": "" }, + "Guarded By Double Turrets 2": { "name": "", "owner": "" }, + "Hexagon Blue": { "name": "", "owner": "" }, + "Left Of Checkpoint Before Fuse": { "name": "", "owner": "" }, + "Near Corpses": { "name": "", "owner": "" }, + "Spider Ambush": { "name": "", "owner": "" } + } + }, + "Rooted Ziggurat Teleporter": { + "collected": 0, + "remaining": 0, + "total": 0, + "checks": {} + }, + "Rooted Ziggurat Tower": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Inside Tower": { "name": "", "owner": "" } } + }, + "Rooted Ziggurat Upper": { + "collected": 0, + "remaining": 2, + "total": 2, + "checks": { + "Beneath Bridge To Administrator": { "name": "", "owner": "" }, + "Near Bridge Switch": { "name": "", "owner": "" } + } + }, + "Ruined Atoll": { + "collected": 0, + "remaining": 17, + "total": 17, + "checks": { + "[East] Locked Room Lower Chest": { "name": "", "owner": "" }, + "[East] Locked Room Upper Chest": { "name": "", "owner": "" }, + "[North] From Lower Overworld Entrance": { "name": "", "owner": "" }, + "[North] Guarded By Bird": { "name": "", "owner": "" }, + "[North] Obscured Beneath Bridge": { "name": "", "owner": "" }, + "[Northeast] Chest Beneath Brick Walkway": { "name": "", "owner": "" }, + "[Northeast] Chest On Brick Walkway": { "name": "", "owner": "" }, + "[Northeast] Key Pickup": { "name": "", "owner": "" }, + "[Northwest] Behind Envoy": { "name": "", "owner": "" }, + "[Northwest] Bombable Wall": { "name": "", "owner": "" }, + "[South] Chest Near Big Crabs": { "name": "", "owner": "" }, + "[South] Near Birds": { "name": "", "owner": "" }, + "[South] Upper Floor On Bricks": { "name": "", "owner": "" }, + "[South] Upper Floor On Power Line": { "name": "", "owner": "" }, + "[Southeast] Chest Near Fuse": { "name": "", "owner": "" }, + "[Southwest] Obscured Behind Fuse": { "name": "", "owner": "" }, + "[West] Near Kevin Block": { "name": "", "owner": "" } + } + }, + "Ruined Passage": { + "collected": 0, + "remaining": 2, + "total": 2, + "checks": { + "Holy Cross Chest": { "name": "", "owner": "" }, + "Page Pickup": { "name": "", "owner": "" } + } + }, + "Ruined Shop": { + "collected": 0, + "remaining": 3, + "total": 3, + "checks": { + "Chest 1": { "name": "", "owner": "" }, + "Chest 2": { "name": "", "owner": "" }, + "Chest 3": { "name": "", "owner": "" } + } + }, + "Sealed Temple": { + "collected": 0, + "remaining": 2, + "total": 2, + "checks": { + "Holy Cross Chest": { "name": "", "owner": "" }, + "Page Pickup": { "name": "", "owner": "" } + } + }, + "Secret Gathering Place": { + "collected": 0, + "remaining": 3, + "total": 3, + "checks": { + "10 Fairy Reward": { "name": "", "owner": "" }, + "20 Fairy Reward": { "name": "", "owner": "" }, + "Holy Cross Chest": { "name": "", "owner": "" } + } + }, + "Shop": { + "collected": 0, + "remaining": 4, + "total": 4, + "checks": { + "Coin 1": { "name": "", "owner": "" }, + "Coin 2": { "name": "", "owner": "" }, + "Potion 1": { "name": "", "owner": "" }, + "Potion 2": { "name": "", "owner": "" } + } + }, + "Southeast Cross Room": { + "collected": 0, + "remaining": 3, + "total": 3, + "checks": { + "Chest 1": { "name": "", "owner": "" }, + "Chest 2": { "name": "", "owner": "" }, + "Chest 3": { "name": "", "owner": "" } + } + }, + "Special Shop": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Secret Page Pickup": { "name": "", "owner": "" } } + }, + "Stick House": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Stick Chest": { "name": "", "owner": "" } } + }, + "Swamp": { + "collected": 0, + "remaining": 22, + "total": 22, + "checks": { + "[Central] Beneath Memorial": { "name": "", "owner": "" }, + "[Central] Near Ramps Up": { "name": "", "owner": "" }, + "[Central] Obscured Behind Northern Mountain": { + "name": "", + "owner": "" + }, + "[Central] South Secret Passage": { "name": "", "owner": "" }, + "[Entrance] Above Entryway": { "name": "", "owner": "" }, + "[Entrance] North Small Island": { "name": "", "owner": "" }, + "[Entrance] Obscured Inside Watchtower": { "name": "", "owner": "" }, + "[Entrance] South Near Fence": { "name": "", "owner": "" }, + "[Outside Cathedral] Near Moonlight Bridge Door": { + "name": "", + "owner": "" + }, + "[Outside Cathedral] Obscured Behind Memorial": { + "name": "", + "owner": "" + }, + "[South Graveyard] 4 Orange Skulls": { "name": "", "owner": "" }, + "[South Graveyard] Above Big Skeleton": { "name": "", "owner": "" }, + "[South Graveyard] Chest Near Graves": { "name": "", "owner": "" }, + "[South Graveyard] Guarded By Big Skeleton": { + "name": "", + "owner": "" + }, + "[South Graveyard] Guarded By Tentacles": { "name": "", "owner": "" }, + "[South Graveyard] Obscured Behind Ridge": { "name": "", "owner": "" }, + "[South Graveyard] Obscured Beneath Telescope": { + "name": "", + "owner": "" + }, + "[South Graveyard] Upper Walkway Dash Chest": { + "name": "", + "owner": "" + }, + "[South Graveyard] Upper Walkway On Pedestal": { + "name": "", + "owner": "" + }, + "[Upper Graveyard] Near Shield Fleemers": { "name": "", "owner": "" }, + "[Upper Graveyard] Near Telescope": { "name": "", "owner": "" }, + "[Upper Graveyard] Obscured Behind Hill": { "name": "", "owner": "" } + } + }, + "The Heir": { "collected": 0, "remaining": 0, "total": 0, "checks": {} }, + "Top of the Mountain": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { "Page At The Peak": { "name": "", "owner": "" } } + }, + "West Furnace": { + "collected": 0, + "remaining": 2, + "total": 2, + "checks": { + "Chest": { "name": "", "owner": "" }, + "Lantern Pickup": { "name": "", "owner": "" } + } + }, + "West Garden": { + "collected": 0, + "remaining": 20, + "total": 20, + "checks": { + "[Central Highlands] After Garden Knight": { "name": "", "owner": "" }, + "[Central Highlands] Behind Guard Captain": { "name": "", "owner": "" }, + "[Central Highlands] Holy Cross (Blue Lines)": { + "name": "", + "owner": "" + }, + "[Central Highlands] Top of Ladder Before Boss": { + "name": "", + "owner": "" + }, + "[Central Lowlands] Below Left Walkway": { "name": "", "owner": "" }, + "[Central Lowlands] Chest Beneath Faeries": { "name": "", "owner": "" }, + "[Central Lowlands] Chest Beneath Save Point": { + "name": "", + "owner": "" + }, + "[Central Lowlands] Chest Near Shortcut Bridge": { + "name": "", + "owner": "" + }, + "[Central Lowlands] Passage Beneath Bridge": { + "name": "", + "owner": "" + }, + "[East Lowlands] Page Behind Ice Dagger House": { + "name": "", + "owner": "" + }, + "[North] Across From Page Pickup": { "name": "", "owner": "" }, + "[North] Behind Holy Cross Door": { "name": "", "owner": "" }, + "[North] Obscured Beneath Hero's Memorial": { "name": "", "owner": "" }, + "[North] Page Pickup": { "name": "", "owner": "" }, + "[South Highlands] Secret Chest Beneath Fuse": { + "name": "", + "owner": "" + }, + "[Southeast Lowlands] Outside Cave": { "name": "", "owner": "" }, + "[West Highlands] Upper Left Walkway": { "name": "", "owner": "" }, + "[West Lowlands] Tree Holy Cross Chest": { "name": "", "owner": "" }, + "[West] In Flooded Walkway": { "name": "", "owner": "" }, + "[West] Past Flooded Walkway": { "name": "", "owner": "" } + } + }, + "West Garden House": { + "collected": 0, + "remaining": 1, + "total": 1, + "checks": { + "[Southeast Lowlands] Ice Dagger Pickup": { "name": "", "owner": "" } + } + }, + "Windmill": { "collected": 0, "remaining": 0, "total": 0, "checks": {} } + } +} diff --git a/tunictracker/less_fun_overview.json b/tunictracker/less_fun_overview.json new file mode 100644 index 0000000..1c4463c --- /dev/null +++ b/tunictracker/less_fun_overview.json @@ -0,0 +1,8 @@ +{ + "scene": "Overworld", + "seed": 98135423, + "items": 0, + "entrances": 0, + "hints": 0, + "codes": {} +} diff --git a/tunictracker/tracker/static/tracker/assets/button_functions.js b/tunictracker/tracker/static/tracker/assets/button_functions.js new file mode 100644 index 0000000..142247e --- /dev/null +++ b/tunictracker/tracker/static/tracker/assets/button_functions.js @@ -0,0 +1,40 @@ +function open_breakdown(event) { + let scene = event.dataset.scene; + Array.from(document.getElementById("breakdown-list").children).forEach( + (breakdown) => { + let breakdown_scene_title = breakdown.querySelector( + ".breakdown-block-title" + ).textContent; + if ( + breakdown_scene_title == scene || + breakdown.dataset.current == "true" + ) { + breakdown.classList.remove("hidden"); + } else { + breakdown.classList.add("hidden"); + } + } + ); +} + +function hide_empty_summaries() { + let summary_divs = + document.getElementById("summary-list").firstElementChild.children; + Array.from(summary_divs).forEach((summary) => { + let checks_undiscovered = + summary.querySelector(".summary-checks").dataset.checksUndiscovered; + let entrances_undiscovered = + summary.querySelector(".summary-entrances").dataset.entrancesUndiscovered; + if ( + !( + summary.querySelector(".summary-title").textContent == "Posterity" || + summary.querySelector(".summary-title").textContent == "Resurrection" + ) && + checks_undiscovered <= 0 && + entrances_undiscovered <= 0 + ) { + console.log(`${checks_undiscovered} and ${entrances_undiscovered}`); + summary.classList.toggle("hidden"); + } + }); +} diff --git a/tunictracker/tracker/static/tracker/assets/refresh.js b/tunictracker/tracker/static/tracker/assets/refresh.js deleted file mode 100644 index 0e7873f..0000000 --- a/tunictracker/tracker/static/tracker/assets/refresh.js +++ /dev/null @@ -1,973 +0,0 @@ -// Global state for overview -var current_scene = ""; -var current_seed = Number.MAX_VALUE; -var current_checks = 0; -var current_entrances = 0; -var current_hints = 0; -var current_codes = 0; -var total_checks = 0; -var total_entrances = 0; - -// Global state for all entrances -// This should hold numbers for how many entrances are mapped/total per scene. -var current_entrances_list = {}; - -// Global state for all checks -// This should hold numbers for how many checks are cleared/total per scene. -var current_checks_list = {}; - -// Global state internal -var server_address = ""; -var cross_codes = {}; - -window.onload = () => { - get_updated_server_address(); - fetch(`${document.URL}static/tracker/data/holy_cross_codes.json`) - .then((response) => response.json()) - .then( - (data) => { - cross_codes = JSON.parse(JSON.stringify(data)); - const refresh_interval = setInterval(refresh_elements, 500); - }, - (error) => { - console.log(error); - } - ); -}; - -function open_breakdown(event) { - let scene = event.dataset.scene; - Array.from(document.getElementById("breakdown-list").children).forEach( - (breakdown) => { - let breakdown_scene_title = breakdown.querySelector( - ".breakdown-block-title" - ).textContent; - if ( - breakdown_scene_title == scene || - breakdown.dataset.current == "true" - ) { - breakdown.classList.remove("hidden"); - } else { - breakdown.classList.add("hidden"); - } - } - ); -} - -function hide_empty_summaries() { - let summary_divs = - document.getElementById("summary-list").firstElementChild.children; - Array.from(summary_divs).forEach((summary) => { - let checks_undiscovered = - summary.querySelector(".summary-checks").dataset.checksUndiscovered; - let entrances_undiscovered = - summary.querySelector(".summary-entrances").dataset.entrancesUndiscovered; - if ( - !( - summary.querySelector(".summary-title").textContent == "Posterity" || - summary.querySelector(".summary-title").textContent == "Resurrection" - ) && - checks_undiscovered <= 0 && - entrances_undiscovered <= 0 - ) { - console.log(`${checks_undiscovered} and ${entrances_undiscovered}`); - summary.classList.toggle("hidden"); - } - }); -} - -function notices_ur_debug() { - document.getElementById("debug-block").classList.toggle("hidden"); -} - -async function get_updated_server_address() { - fetch(`${document.URL}get/address`) - .then( - (response) => response.json(), - (error) => { - console.log("Are you sure the front end is up?"); - } - ) - .then( - (data) => { - parsed_data = JSON.parse(JSON.stringify(data)); - server_address = parsed_data["listen_address"]; - if (parsed_data["backend_filepath_updated"]) { - get_updated_filepath(); - } - }, - (error) => { - console.log(error); - } - ); -} - -async function refresh_overview() { - const response = await fetch(`${server_address}overview`); - const data = await response.json(); - return data; -} - -async function refresh_hints() { - const response = await fetch(`${server_address}hints`); - const data = await response.json(); - return data; -} - -async function refresh_checks() { - const response = await fetch(`${server_address}items`); - const data = await response.json(); - return data; -} - -async function refresh_entrances() { - const response = await fetch(`${server_address}doors`); - const data = await response.json(); - return data; -} - -async function update_overview(overview, changed_scene, changed_codes) { - let overview_checks_title = document - .getElementById("overview-totals") - .querySelector(".overview-checks"); - let overview_entrances_title = document - .getElementById("overview-totals") - .querySelector(".overview-entrances"); - - // Set content to updated data. - overview_checks_title.textContent = `Checks: ${overview.items}/${total_checks}`; - overview_entrances_title.textContent = `Entrances: ${ - overview.entrances * 2 - }/${total_entrances}`; - - current_seed = overview.seed; - current_scene = overview.scene; - current_checks = overview.items; - current_entrances = overview.entrances; - current_hints = overview.hints; - current_codes = Object.keys(overview.codes).length; - - if (changed_scene) { - await update_scene(overview.scene); - } - if (changed_codes) { - console.log(Object.keys(overview.codes).length); - await update_codes(overview.codes); - } -} - -async function update_codes(codes) { - // Codes that are always active - const default_cross_codes = cross_codes.Default; - - // One time codes independent of scene - const global_cross_codes = cross_codes.Global; - - let new_cross_codes_block_list = document - .getElementById("codes-list") - .cloneNode(true); - let cross_codes_block_list_item = document - .getElementById("codes-list") - .firstElementChild.cloneNode(true); - new_cross_codes_block_list.innerHTML = ""; - new_cross_codes_block_list.appendChild( - cross_codes_block_list_item.cloneNode(true) - ); - cross_codes_block_list_item.classList.remove("hidden"); - - Object.keys(default_cross_codes).forEach((code) => { - cross_codes_block_list_item.querySelector( - ".codes-list-item-title" - ).textContent = code; - cross_codes_block_list_item.querySelector( - ".codes-list-item-code" - ).textContent = default_cross_codes[code] - .replace(/U/g, "⬆️") - .replace(/R/g, "➡️") - .replace(/D/g, "⬇️") - .replace(/L/g, "⬅️"); - new_cross_codes_block_list.appendChild( - cross_codes_block_list_item.cloneNode(true) - ); - }); - Object.keys(global_cross_codes).forEach((code) => { - cross_codes_block_list_item.querySelector( - ".codes-list-item-title" - ).textContent = code; - cross_codes_block_list_item.querySelector( - ".codes-list-item-code" - ).textContent = global_cross_codes[code] - .replace(/U/g, "⬆️") - .replace(/R/g, "➡️") - .replace(/D/g, "⬇️") - .replace(/L/g, "⬅️"); - new_cross_codes_block_list.appendChild( - cross_codes_block_list_item.cloneNode(true) - ); - }); - Object.keys(codes).forEach((code) => { - cross_codes_block_list_item.querySelector( - ".codes-list-item-title" - ).textContent = code; - cross_codes_block_list_item.querySelector( - ".codes-list-item-code" - ).textContent = cross_codes[current_scene][code] - .replace(/U/g, "⬆️") - .replace(/R/g, "➡️") - .replace(/D/g, "⬇️") - .replace(/L/g, "⬅️"); - new_cross_codes_block_list.appendChild( - cross_codes_block_list_item.cloneNode(true) - ); - }); - console.log(codes); - document.getElementById("codes-list").replaceWith(new_cross_codes_block_list); -} - -async function apply_summary_colors(summary) { - // Apply color coding to summary block - summary.element.classList.remove( - "from-highlight-both-light", - "from-highlight-checks-light", - "from-highlight-entrances-light", - "from-highlight-empty-light", - "from-highlight-undiscovered-light", - "to-highlight-both-dark", - "to-highlight-checks-dark", - "to-highlight-entrances-dark", - "to-highlight-empty-dark", - "to-highlight-undiscovered-dark", - "text-highlight-both-text", - "text-highlight-checks-text", - "text-highlight-entrances-text", - "text-highlight-empty-text", - "text-highlight-undiscovered-text" - ); - - console.log(summary); - if (summary.checks_total > 0 && summary.entrances_total > 0) { - if ( - summary.checks_collected == summary.checks_total && - summary.entrances_found == summary.entrances_total - ) { - summary.element.classList.add( - "from-highlight-empty-light", - "to-highlight-empty-dark", - "text-highlight-empty-text" - ); - } else if ( - summary.checks_collected <= 0 && - summary.entrances_found <= 0 && - summary.entrances_total > 0 - ) { - summary.element.classList.add( - "from-highlight-undiscovered-light", - "to-highlight-undiscovered-dark", - "text-highlight-undiscovered-text" - ); - } else if (summary.checks_collected >= summary.checks_total) { - summary.element.classList.add( - "from-highlight-checks-light", - "to-highlight-checks-dark", - "text-highlight-checks-text" - ); - } else if (summary.entrances_found >= summary.entrances_total) { - summary.element.classList.add( - "from-highlight-entrances-light", - "to-highlight-entrances-dark", - "text-highlight-entrances-text" - ); - } else { - summary.element.classList.add( - "from-highlight-both-light", - "to-highlight-both-dark", - "text-highlight-both-text" - ); - } - } -} - -async function update_summary(updates) { - if ("checks" in updates) { - const summary_checks = document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`) - .querySelector(".summary-checks"); - summary_checks.textContent = `Checks: ${updates.checks}/${summary_checks.dataset.checksTotal}`; - summary_checks.dataset.checksUndiscovered = updates.checks; - apply_summary_colors({ - element: document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`).firstElementChild, - checks_collected: updates.checks, - checks_total: summary_checks.dataset.checksTotal, - entrances_found: document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`) - .querySelector(".summary-entrances").dataset.entrancesUndiscovered, - entrances_total: document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`) - .querySelector(".summary-entrances").dataset.entrancesTotal, - }); - } else if ("entrances" in updates) { - console.log( - `scene: ${document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`)}\n${ - updates.entrances - }` - ); - const summary_entrances = document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`) - .querySelector(".summary-entrances"); - summary_entrances.textContent = `Entrances: ${updates.entrances}/${summary_entrances.dataset.entrancesTotal}`; - summary_entrances.dataset.entrancesUndiscovered = updates.entrances; - apply_summary_colors({ - element: document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`).firstElementChild, - checks_collected: document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`) - .querySelector(".summary-checks").dataset.checksUndiscovered, - checks_total: document - .getElementById("summary-list") - .querySelector(`[data-scene="${updates.scene}"]`) - .querySelector(".summary-checks").dataset.checksTotal, - entrances_found: updates.entrances, - entrances_total: summary_entrances.dataset.entrancesTotal, - }); - } else { - console.log("What the heck is this"); - } -} - -async function update_checks(checks) { - total_checks = checks.total; - - let new_breakdown_block_checks_list = document - .getElementById("breakdown-list") - .firstElementChild.querySelector(".breakdown-block-checks-list") - .cloneNode(true); - let breakdown_block_checks_list_item = document - .getElementById("breakdown-list") - .firstElementChild.querySelector(".breakdown-block-checks-list") - .firstElementChild.cloneNode(true); - - // Create new lists with updated data. - let breakdown_list = Array.from( - document.getElementById("breakdown-list").children - ); - breakdown_list.forEach((scene) => { - // Create variables for element pointers. - let scene_title = scene.querySelector(".breakdown-block-title").textContent; - if (scene_title) { - const scene_checks = parseInt( - scene.querySelector(".breakdown-block-checks-title").dataset.checks - ); - if (scene_checks != checks.scenes[scene_title].collected) { - scene.querySelector(".breakdown-block-checks-title").dataset.checks = - scene_checks; - update_summary({ - scene: scene_title, - checks: checks.scenes[scene_title].collected, - }); - scene.querySelector( - ".breakdown-block-checks-title" - ).textContent = `Checks: ${checks.scenes[scene_title].collected}/${checks.scenes[scene_title].total}`; - - new_breakdown_block_checks_list.innerHTML = ""; - breakdown_block_checks_list_item.classList.remove("hidden"); - Object.keys(checks.scenes[scene_title].checks).forEach((check) => { - if (checks.scenes[scene_title].checks[check].name == "") { - breakdown_block_checks_list_item.textContent = `❌ ${check}`; - new_breakdown_block_checks_list.appendChild( - breakdown_block_checks_list_item.cloneNode(true) - ); - } - }); - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene_title}"]`) - .querySelector(".breakdown-block-checks-list").innerHTML = ""; - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene_title}"]`) - .querySelector(".breakdown-block-checks-list") - .replaceWith(new_breakdown_block_checks_list.cloneNode(true)); - } - } - }); - // current_checks = checks.collected; -} - -async function update_entrances(entrances) { - total_entrances = entrances.total; - - let new_breakdown_block_entrances_list = document - .getElementById("breakdown-list") - .firstElementChild.querySelector(".breakdown-block-entrances-list") - .cloneNode(true); - let breakdown_block_entrances_list_item = document - .getElementById("breakdown-list") - .firstElementChild.querySelector(".breakdown-block-entrances-list") - .firstElementChild.cloneNode(true); - let new_breakdown_block_mapped_list = document - .getElementById("breakdown-list") - .firstElementChild.querySelector(".breakdown-block-mapped-list") - .cloneNode(true); - let breakdown_block_mapped_list_item = document - .getElementById("breakdown-list") - .firstElementChild.querySelector(".breakdown-block-mapped-list") - .firstElementChild.cloneNode(true); - // Create new lists with updated data. - let breakdown_list = Array.from( - document.getElementById("breakdown-list").children - ); - breakdown_list.forEach((scene) => { - // Create variables for element pointers. - let scene_title = scene.querySelector(".breakdown-block-title").textContent; - if (scene_title) { - const scene_entrances = parseInt( - scene.querySelector(".breakdown-block-entrances-title").dataset - .entrances - ); - if (scene_entrances != entrances.scenes[scene_title].found) { - scene.querySelector(".breakdown-block-checks-title").dataset.entrances = - scene_entrances; - update_summary({ - scene: scene_title, - entrances: entrances.scenes[scene_title].found, - }); - scene.querySelector( - ".breakdown-block-entrances-title" - ).textContent = `Entrances: ${entrances.scenes[scene_title].found}/${entrances.scenes[scene_title].total}`; - - new_breakdown_block_entrances_list.innerHTML = ""; - breakdown_block_entrances_list_item.classList.remove("hidden"); - new_breakdown_block_mapped_list.innerHTML = ""; - breakdown_block_mapped_list_item.classList.remove("hidden"); - - Object.keys(entrances.scenes[scene_title].doors).forEach((entrance) => { - if (entrances.scenes[scene_title].doors[entrance].door == "") { - breakdown_block_entrances_list_item.textContent = `❌ ${entrance}`; - new_breakdown_block_entrances_list.appendChild( - breakdown_block_entrances_list_item.cloneNode(true) - ); - } else { - breakdown_block_mapped_list_item.textContent = `✔️ ${entrance} -> ${entrances.scenes[scene_title].doors[entrance].door}`; - breakdown_block_mapped_list_item.id = `${entrance}-mapped`; - breakdown_block_mapped_list_item.dataset.scene = - entrances.scenes[scene_title].doors[entrance].scene; - new_breakdown_block_mapped_list.appendChild( - breakdown_block_mapped_list_item.cloneNode(true) - ); - } - }); - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene_title}"]`) - .querySelector(".breakdown-block-entrances-list").innerHTML = ""; - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene_title}"]`) - .querySelector(".breakdown-block-entrances-list") - .replaceWith(new_breakdown_block_entrances_list.cloneNode(true)); - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene_title}"]`) - .querySelector(".breakdown-block-mapped-list").innerHTML = ""; - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene_title}"]`) - .querySelector(".breakdown-block-mapped-list") - .replaceWith(new_breakdown_block_mapped_list.cloneNode(true)); - } - } - }); -} - -async function update_hints(hints) { - return; -} - -async function update_scene(scene) { - Array.from(document.getElementById("breakdown-list").children).forEach( - (breakdown) => { - const breakdown_title = breakdown.querySelector( - ".breakdown-block-title" - ).textContent; - if (breakdown_title == scene) { - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene}"]`) - .classList.remove("hidden"); - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene}"]`).dataset.current = - "true"; - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${scene}"]`) - .classList.add("order-last"); - } else { - document - .getElementById("breakdown-list") - .querySelector( - `[data-breakdown-scene="${breakdown_title}"]` - ).dataset.current = "false"; - - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${breakdown_title}"]`) - .classList.add("hidden"); - document - .getElementById("breakdown-list") - .querySelector(`[data-breakdown-scene="${breakdown_title}"]`) - .classList.remove("order-last"); - } - } - ); - Array.from( - document.getElementById("summary-list").firstElementChild.children - ).forEach((summary) => { - const summary_scene = summary.dataset.scene; - if (summary_scene == scene) { - document - .getElementById("summary-list") - .firstElementChild.querySelector(`[data-scene="${scene}"]`) - .classList.add("hidden"); - } else if (!(summary_scene == "")) { - document - .getElementById("summary-list") - .firstElementChild.querySelector(`[data-scene="${summary_scene}"]`) - .classList.remove("hidden"); - } - }); -} - -async function perform_updates( - changed_seed, - changed_checks, - changed_entrances, - changed_hints -) { - if (changed_seed) { - console.log("Seed changed."); - update_checks(await refresh_checks()); - update_entrances(await refresh_entrances()); - update_hints(await refresh_hints()); - } else { - if (changed_checks) { - refresh_checks().then((data) => update_checks(data)); - } - if (changed_entrances) { - refresh_entrances().then((data) => update_entrances(data)); - } - if (changed_hints) { - refresh_hints().then((data) => update_hints(data)); - } - } -} - -async function refresh_elements() { - const overview = await refresh_overview(); - - document.getElementById("status-block").classList.add("hidden"); - - const changed_seed = overview.seed != current_seed; - const changed_scene = overview.scene != current_scene; - const changed_checks = overview.items != current_checks; - const changed_entrances = overview.entrances != current_entrances; - const changed_codes = Object.keys(overview.codes).length != current_codes; - const changed_hints = overview.hints != current_hints; - const any_change = - changed_seed || - changed_scene || - changed_checks || - changed_entrances || - changed_codes || - changed_hints; - - if (any_change) { - await perform_updates( - changed_seed, - changed_checks, - changed_entrances, - changed_hints - ); - update_overview(overview, changed_scene, changed_codes); - } - // refresh_overview().then( - // (overview) => { - - // let summary_block = document - // .getElementById("summary-list") - // .firstElementChild.firstElementChild.cloneNode(true); - // let breakdown_block = document - // .getElementById("breakdown-list") - // .firstElementChild.cloneNode(true); - // breakdown_block.id = ""; - // breakdown_block.classList.remove("hidden"); - - // let debug_item = document - // .getElementById("debug-block") - // .querySelector(".debug-list") - // .firstElementChild.cloneNode(true); - // debug_item.classList.remove("hidden"); - // let new_breakdown_list = document - // .getElementById("breakdown-list") - // .cloneNode(true); - // let new_summary_list = document - // .getElementById("summary-list") - // .cloneNode(true); - // let new_debug_block = document - // .getElementById("debug-block") - // .querySelector(".debug-list") - // .cloneNode(true); - // let current_open_breakdown = ""; - // Array.from(new_breakdown_list.children).forEach((scene) => { - // if (!Array.from(scene.classList).includes("hidden")) { - // current_open_breakdown = scene.id; - // } - // }); - - // Clear out the current lists. - // new_breakdown_list.innerHTML = ""; - // new_summary_list.firstElementChild.innerHTML = ""; - // new_debug_block.innerHTML = ""; - - // Create new lists with updated data. - // Object.keys(current_entrances_list).forEach((scene) => { - // // Create variables for element pointers. - // summary_block = document - // .getElementById("summary-list") - // .firstElementChild.firstElementChild.cloneNode(true); - // summary_block.classList.remove("hidden"); - // let summary_title = summary_block.querySelector(".summary-title"); - // let summary_checks = summary_block.querySelector(".summary-checks"); - // let summary_entrances = - // summary_block.querySelector(".summary-entrances"); - - // breakdown_block = document - // .getElementById("breakdown-list") - // .firstElementChild.cloneNode(true); - // let breakdown_block_title = breakdown_block.querySelector( - // ".breakdown-block-title" - // ); - // let breakdown_block_checks_title = breakdown_block.querySelector( - // ".breakdown-block-checks-title" - // ); - // let new_breakdown_block_checks_list = breakdown_block - // .querySelector(".breakdown-block-checks-list") - // .cloneNode(true); - // let breakdown_block_checks_list_item = breakdown_block - // .querySelector(".breakdown-block-checks-list") - // .firstElementChild.cloneNode(true); - // let breakdown_block_entrances_title = breakdown_block.querySelector( - // ".breakdown-block-entrances-title" - // ); - // let new_breakdown_block_entrances_list = breakdown_block - // .querySelector(".breakdown-block-entrances-list") - // .cloneNode(true); - // let breakdown_block_entrances_list_item = breakdown_block - // .querySelector(".breakdown-block-entrances-list") - // .firstElementChild.cloneNode(true); - // let new_breakdown_block_mapped_list = breakdown_block - // .querySelector(".breakdown-block-mapped-list") - // .cloneNode(true); - // let breakdown_block_mapped_list_item = breakdown_block - // .querySelector(".breakdown-block-mapped-list") - // .firstElementChild.cloneNode(true); - - // // Clear out current list content. - // new_breakdown_block_checks_list.innerHTML = ""; - // new_breakdown_block_entrances_list.innerHTML = ""; - // new_breakdown_block_mapped_list.innerHTML = ""; - // new_breakdown_block_checks_list.appendChild( - // breakdown_block_checks_list_item.cloneNode(true) - // ); - // breakdown_block_checks_list_item.classList.remove("hidden"); - // new_breakdown_block_entrances_list.appendChild( - // breakdown_block_entrances_list_item.cloneNode(true) - // ); - // breakdown_block_entrances_list_item.classList.remove("hidden"); - // new_breakdown_block_mapped_list.appendChild( - // breakdown_block_mapped_list_item.cloneNode(true) - // ); - // breakdown_block_mapped_list_item.classList.remove("hidden"); - - // // Create variables for commonly used values. - // let scene_checks_undiscovered = 0; - // // all_items[scene]; - // let scene_checks_total = 0; //all_scenes[scene].Totals.Checks.Total; - // let scene_entrances_undiscovered = 0; - // // all_scenes[scene].Totals.Entrances.Undiscovered; - // let scene_entrances_total = 0; //all_scenes[scene].Totals.Entrances.Total; - // // let scene_has_codes = Object.keys(cross_codes).includes(scene); - - // // Set textContent. - // summary_title.textContent = scene; - // summary_checks.textContent = `Checks: ${scene_checks_undiscovered}/${scene_checks_total}`; - // summary_checks.dataset.checksUndiscovered = scene_checks_undiscovered; - // summary_checks.dataset.checksTotal = scene_checks_undiscovered; - // summary_entrances.textContent = `Entrances: ${scene_entrances_undiscovered}/${scene_entrances_total}`; - // summary_entrances.dataset.entrancesUndiscovered = - // scene_entrances_undiscovered; - // summary_entrances.dataset.entrancesTotal = scene_entrances_total; - // breakdown_block_title.textContent = scene; - // breakdown_block_checks_title.textContent = `Checks: ${all_scenes[scene].Totals.Checks.Undiscovered}/${all_scenes[scene].Totals.Checks.Total}`; - // breakdown_block_entrances_title.textContent = `Entrances: ${all_scenes[scene].Totals.Entrances.Undiscovered}/${all_scenes[scene].Totals.Entrances.Total}`; - - // // Create checks, entrances, mapped entrances, and cross code lists. - // Object.keys(all_scenes[scene].Checks).forEach((check) => { - // if (!all_scenes[scene].Checks[check]) { - // breakdown_block_checks_list_item.textContent = `❌ ${check}`; - // new_breakdown_block_checks_list.appendChild( - // breakdown_block_checks_list_item.cloneNode(true) - // ); - // } - // }); - // Object.keys(all_scenes[scene].Entrances).forEach((entrances) => { - // if (all_scenes[scene].Entrances[entrances].Door == "") { - // breakdown_block_entrances_list_item.textContent = `❌ ${entrances}`; - // new_breakdown_block_entrances_list.appendChild( - // breakdown_block_entrances_list_item.cloneNode(true) - // ); - // } else { - // breakdown_block_mapped_list_item.textContent = `✔️ ${entrances} -> ${all_scenes[scene].Entrances[entrances].Door}`; - // breakdown_block_mapped_list_item.id = `${entrances}-mapped`; - // breakdown_block_mapped_list_item.dataset.scene = - // all_scenes[scene].Entrances[entrances].Scene; - // new_breakdown_block_mapped_list.appendChild( - // breakdown_block_mapped_list_item.cloneNode(true) - // ); - // } - // }); - - // // Apply color coding to summary block - // summary_block.firstElementChild.classList.remove( - // "from-highlight-both-light", - // "from-highlight-checks-light", - // "from-highlight-entrances-light", - // "from-highlight-empty-light", - // "from-highlight-undiscovered-light", - // "to-highlight-both-dark", - // "to-highlight-checks-dark", - // "to-highlight-entrances-dark", - // "to-highlight-empty-dark", - // "to-highlight-undiscovered-dark", - // "text-highlight-both-text", - // "text-highlight-checks-text", - // "text-highlight-entrances-text", - // "text-highlight-empty-text", - // "text-highlight-undiscovered-text" - // ); - // if ( - // scene_checks_undiscovered > 0 && - // scene_entrances_undiscovered > 0 - // ) { - // if ( - // scene_checks_total == scene_checks_undiscovered && - // scene_entrances_total == scene_entrances_undiscovered - // ) { - // summary_block.firstElementChild.classList.add( - // "from-highlight-undiscovered-light", - // "to-highlight-undiscovered-dark", - // "text-highlight-undiscovered-text" - // ); - // } else { - // summary_block.firstElementChild.classList.add( - // "from-highlight-both-light", - // "to-highlight-both-dark", - // "text-highlight-both-text" - // ); - // } - // } else if (scene_checks_undiscovered > 0) { - // summary_block.firstElementChild.classList.add( - // "from-highlight-checks-light", - // "to-highlight-checks-dark", - // "text-highlight-checks-text" - // ); - // } else if (scene_entrances_undiscovered > 0) { - // summary_block.firstElementChild.classList.add( - // "from-highlight-entrances-light", - // "to-highlight-entrances-dark", - // "text-highlight-entrances-text" - // ); - // } else { - // summary_block.firstElementChild.classList.add( - // "from-highlight-empty-light", - // "to-highlight-empty-dark", - // "text-highlight-empty-text" - // ); - // } - - // // Replace lists - // breakdown_block - // .querySelector(".breakdown-block-checks-list") - // .replaceWith(new_breakdown_block_checks_list); - // breakdown_block - // .querySelector(".breakdown-block-entrances-list") - // .replaceWith(new_breakdown_block_entrances_list); - // breakdown_block - // .querySelector(".breakdown-block-mapped-list") - // .replaceWith(new_breakdown_block_mapped_list); - - // breakdown_block.id = `${ - // breakdown_block.querySelector(".breakdown-block-title").textContent - // }-breakdown`; - // breakdown_block.classList.add("hidden"); - - // // Append relevant elements to lists. - // if (current_open_breakdown == breakdown_block.id) { - // breakdown_block.classList.remove("hidden"); - // } - // if (scene == current_scene_name) { - // summary_block.classList.add("hidden"); - // breakdown_block.classList.remove("hidden"); - // document - // .getElementById("breakdown-current") - // .firstElementChild.replaceWith(breakdown_block.cloneNode(true)); - // breakdown_block.classList.add("hidden"); - // } else if ( - // scene_checks_undiscovered <= 0 && - // scene_entrances_total <= 0 - // ) { - // summary_block.classList.add("hidden"); - // } - // if (document.getElementById("hideDone").checked) { - // if ( - // scene_checks_undiscovered <= 0 && - // scene_entrances_undiscovered <= 0 - // ) { - // summary_block.classList.add("hidden"); - // } - // } - // summary_block.dataset.scene = scene; - // new_summary_list.firstElementChild.appendChild( - // summary_block.cloneNode(true) - // ); - // new_breakdown_list.appendChild(breakdown_block.cloneNode(true)); - // }); - // let new_cross_codes_block_list = document - // .getElementById("codes-list") - // .cloneNode(true); - // let cross_codes_block_list_item = document - // .getElementById("codes-list") - // .firstElementChild.cloneNode(true); - // new_cross_codes_block_list.innerHTML = ""; - - // Object.keys(default_cross_codes).forEach((code) => { - // cross_codes_block_list_item.querySelector( - // ".codes-list-item-title" - // ).textContent = code; - // cross_codes_block_list_item.querySelector( - // ".codes-list-item-code" - // ).textContent = default_cross_codes[code] - // .replace(/U/g, "⬆️") - // .replace(/R/g, "➡️") - // .replace(/D/g, "⬇️") - // .replace(/L/g, "⬅️"); - // new_cross_codes_block_list.appendChild( - // cross_codes_block_list_item.cloneNode(true) - // ); - // }); - // Object.keys(global_cross_codes).forEach((code) => { - // cross_codes_block_list_item.querySelector( - // ".codes-list-item-title" - // ).textContent = code; - // cross_codes_block_list_item.querySelector( - // ".codes-list-item-code" - // ).textContent = global_cross_codes[code] - // .replace(/U/g, "⬆️") - // .replace(/R/g, "➡️") - // .replace(/D/g, "⬇️") - // .replace(/L/g, "⬅️"); - // if (cross_codes_entered.Global[code]) { - // cross_codes_block_list_item.classList.add("hidden"); - // } else { - // cross_codes_block_list_item.classList.remove("hidden"); - // } - // new_cross_codes_block_list.appendChild( - // cross_codes_block_list_item.cloneNode(true) - // ); - // }); - // if (!(typeof cross_codes[current_scene_name] === "undefined")) { - // Object.keys(cross_codes[current_scene_name]).forEach((code) => { - // cross_codes_block_list_item.querySelector( - // ".codes-list-item-title" - // ).textContent = code; - // cross_codes_block_list_item.querySelector( - // ".codes-list-item-code" - // ).textContent = cross_codes[current_scene_name][code] - // .replace(/U/g, "⬆️") - // .replace(/R/g, "➡️") - // .replace(/D/g, "⬇️") - // .replace(/L/g, "⬅️"); - // if (cross_codes_entered[current_scene_name][code]) { - // cross_codes_block_list_item.classList.add("hidden"); - // } else { - // cross_codes_block_list_item.classList.remove("hidden"); - // } - // new_cross_codes_block_list.appendChild( - // cross_codes_block_list_item.cloneNode(true) - // ); - // }); - // } - // document - // .getElementById("codes-list") - // .replaceWith(new_cross_codes_block_list); - - // // Object.keys(debug_info).forEach((item) => { - // // debug_item.querySelector( - // // ".debug-item" - // // ).textContent = `${item}: ${debug_info[item]}`; - // // new_debug_block.appendChild(debug_item.cloneNode(true)); - // // }); - - // // Replace with new data. - // document - // .getElementById("summary-list") - // .replaceWith(new_summary_list.cloneNode(true)); - // document - // .getElementById("breakdown-list") - // .replaceWith(new_breakdown_list.cloneNode(true)); - // // document - // // .getElementById("debug-block") - // // .querySelector(".debug-list") - // // .replaceWith(new_debug_block.cloneNode(true)); - // }, - // (error) => { - // document.getElementById("status-block").classList.remove("hidden"); - // get_updated_server_address(); - // return error; - // } - // ); -} - -// Outdated funcion to log the data gathered from the backend. -function log_elements() { - console.log(overview.checks.textContent); - console.log(overview.entrances.textContent); - console.log(current_scene.name.textContent); - console.log(current_scene.checks.title.textContent); - for (i in current_scene.checks.list.children) { - if (current_scene.checks.list.children[i].textContent) { - console.log(current_scene.checks.list.children[i].textContent); - } - } - console.log(current_scene.entrances.title.textContent); - for (i in current_scene.entrances.list.children) { - if (current_scene.entrances.list.children[i].textContent) { - console.log(current_scene.entrances.list.children[i].textContent); - } - } - for (i in current_scene.entrances.mapped.children) { - if (current_scene.entrances.mapped.children[i].textContent) { - console.log(current_scene.entrances.mapped.children[i].textContent); - } - } -} diff --git a/tunictracker/tracker/static/tracker/assets/refresh_elements.js b/tunictracker/tracker/static/tracker/assets/refresh_elements.js new file mode 100644 index 0000000..94aafbb --- /dev/null +++ b/tunictracker/tracker/static/tracker/assets/refresh_elements.js @@ -0,0 +1,624 @@ +import translate from "./translate-hints.js"; + +// Global state for overview +var current_scene = ""; +var current_seed = Number.MAX_VALUE; +var current_checks = 0; +var current_entrances = 0; +var current_hints = 0; +var current_codes = Number.MAX_VALUE; +var total_checks = 0; +var total_entrances = 0; + +// Global state for all entrances +// This should hold numbers for how many entrances are mapped/total per scene. +var current_entrances_list = {}; + +// Global state for all checks +// This should hold numbers for how many checks are cleared/total per scene. +var current_checks_list = {}; + +// Global state internal +var server_address = ""; +var cross_codes = {}; + +window.onload = () => { + get_updated_server_address(); + fetch(`${document.URL}static/tracker/data/holy_cross_codes.json`) + .then((response) => response.json()) + .then( + (data) => { + cross_codes = JSON.parse(JSON.stringify(data)); + refresh_overview().then((overview) => { + perform_updates(true, true, true, true); + update_overview(overview, true, true); + }); + refresh_elements(); + }, + (error) => { + console.log(error); + } + ); +}; + +async function refresh_elements() { + const overview = await refresh_overview().catch(() => + console.log("Could not access the API server.") + ); + + if (overview) { + document.getElementById("status-block").classList.add("hidden"); + + const changed_seed = overview.seed != current_seed; + const changed_scene = overview.scene != current_scene; + const changed_checks = overview.items != current_checks; + const changed_entrances = overview.entrances != current_entrances; + const changed_codes = Object.keys(overview.codes).length != current_codes; + const changed_hints = overview.hints != current_hints; + const any_change = + changed_seed || + changed_scene || + changed_checks || + changed_entrances || + changed_codes || + changed_hints; + + if (any_change) { + await perform_updates( + changed_seed, + changed_checks, + changed_entrances, + changed_hints + ); + update_overview(overview, changed_scene, changed_codes); + } + } + setTimeout(refresh_elements, 500); +} + +async function perform_updates( + changed_seed, + changed_checks, + changed_entrances, + changed_hints +) { + if (changed_seed) { + // TODO: this at some point *needs* to initialize all the data when the seed changes, instead of relying on the initial HTML. + update_checks(await refresh_checks()); + update_entrances(await refresh_entrances()); + update_hints(await refresh_hints()); + console.log(`Seed changed to: ${current_seed}`); + } else { + if (changed_checks) { + refresh_checks().then((data) => update_checks(data)); + } + if (changed_entrances) { + refresh_entrances().then((data) => update_entrances(data)); + } + if (changed_hints) { + refresh_hints().then((data) => update_hints(data)); + } + } +} + +function notices_ur_debug() { + document.getElementById("debug-block").classList.toggle("hidden"); +} + +async function get_updated_server_address() { + fetch(`${document.URL}get/address`) + .then( + (response) => response.json(), + (error) => { + console.log("Are you sure the front end is up?"); + } + ) + .then( + (data) => { + const parsed_data = JSON.parse(JSON.stringify(data)); + server_address = parsed_data["listen_address"]; + if (parsed_data["backend_filepath_updated"]) { + get_updated_filepath(); + } + }, + (error) => { + console.log(error); + } + ); +} + +async function refresh_overview() { + const response = await fetch(`${server_address}overview`); + const data = await response.json(); + return data; +} + +async function refresh_hints() { + const response = await fetch(`${server_address}hints`); + const data = await response.json(); + return data; +} + +async function refresh_checks() { + const response = await fetch(`${server_address}items`); + const data = await response.json(); + return data; +} + +async function refresh_entrances() { + const response = await fetch(`${server_address}doors`); + const data = await response.json(); + return data; +} + +async function update_overview(overview, changed_scene, changed_codes) { + let overview_checks_title = document + .getElementById("overview-totals") + .querySelector(".overview-checks"); + let overview_entrances_title = document + .getElementById("overview-totals") + .querySelector(".overview-entrances"); + + // Set content to updated data. + overview_checks_title.textContent = `${overview.items}/${total_checks} (${ + total_checks - overview.items + } left)`; + overview_entrances_title.textContent = `${ + overview.entrances * 2 + }/${total_entrances} (${total_entrances - overview.entrances * 2} left)`; + + current_seed = overview.seed; + current_scene = overview.scene; + current_checks = overview.items; + current_entrances = overview.entrances; + current_hints = overview.hints; + current_codes = Object.keys(overview.codes).length; + + if (changed_scene) { + await update_scene(overview.scene); + } + if (changed_codes) { + await update_codes(overview.codes); + } +} + +async function update_codes(codes) { + // Codes that are always active + const default_cross_codes = cross_codes.Default; + + // One time codes independent of scene + const global_cross_codes = cross_codes.Global; + + let new_cross_codes_block_list = document + .getElementById("codes-list") + .cloneNode(true); + let cross_codes_block_list_item = document + .getElementById("codes-list") + .firstElementChild.cloneNode(true); + new_cross_codes_block_list.innerHTML = ""; + new_cross_codes_block_list.appendChild( + cross_codes_block_list_item.cloneNode(true) + ); + cross_codes_block_list_item.classList.remove("hidden"); + + Object.keys(default_cross_codes).forEach((code) => { + cross_codes_block_list_item.querySelector( + ".codes-list-item-title" + ).textContent = code; + cross_codes_block_list_item.querySelector( + ".codes-list-item-code" + ).textContent = default_cross_codes[code] + .replace(/U/g, "⬆️") + .replace(/R/g, "➡️") + .replace(/D/g, "⬇️") + .replace(/L/g, "⬅️"); + new_cross_codes_block_list.appendChild( + cross_codes_block_list_item.cloneNode(true) + ); + }); + Object.keys(global_cross_codes).forEach((code) => { + cross_codes_block_list_item.querySelector( + ".codes-list-item-title" + ).textContent = code; + cross_codes_block_list_item.querySelector( + ".codes-list-item-code" + ).textContent = global_cross_codes[code] + .replace(/U/g, "⬆️") + .replace(/R/g, "➡️") + .replace(/D/g, "⬇️") + .replace(/L/g, "⬅️"); + new_cross_codes_block_list.appendChild( + cross_codes_block_list_item.cloneNode(true) + ); + }); + Object.keys(codes).forEach((code) => { + cross_codes_block_list_item.querySelector( + ".codes-list-item-title" + ).textContent = code; + cross_codes_block_list_item.querySelector( + ".codes-list-item-code" + ).textContent = cross_codes[current_scene][code] + .replace(/U/g, "⬆️") + .replace(/R/g, "➡️") + .replace(/D/g, "⬇️") + .replace(/L/g, "⬅️"); + new_cross_codes_block_list.appendChild( + cross_codes_block_list_item.cloneNode(true) + ); + }); + document.getElementById("codes-list").replaceWith(new_cross_codes_block_list); +} + +async function apply_summary_colors(summary) { + // Apply color coding to summary block + summary.element.classList.remove( + "from-highlight-both-light", + "from-highlight-checks-light", + "from-highlight-entrances-light", + "from-highlight-empty-light", + "from-highlight-undiscovered-light", + "to-highlight-both-dark", + "to-highlight-checks-dark", + "to-highlight-entrances-dark", + "to-highlight-empty-dark", + "to-highlight-undiscovered-dark", + "text-highlight-both-text", + "text-highlight-checks-text", + "text-highlight-entrances-text", + "text-highlight-empty-text", + "text-highlight-undiscovered-text" + ); + + if (summary.checks_total > 0 && summary.entrances_total > 0) { + if ( + summary.checks_collected == summary.checks_total && + summary.entrances_found == summary.entrances_total + ) { + summary.element.classList.add( + "from-highlight-empty-light", + "to-highlight-empty-dark", + "text-highlight-empty-text" + ); + } else if ( + summary.checks_collected <= 0 && + summary.entrances_found <= 0 && + summary.entrances_total > 0 + ) { + summary.element.classList.add( + "from-highlight-undiscovered-light", + "to-highlight-undiscovered-dark", + "text-highlight-undiscovered-text" + ); + } else if (summary.checks_collected >= summary.checks_total) { + summary.element.classList.add( + "from-highlight-checks-light", + "to-highlight-checks-dark", + "text-highlight-checks-text" + ); + } else if (summary.entrances_found >= summary.entrances_total) { + summary.element.classList.add( + "from-highlight-entrances-light", + "to-highlight-entrances-dark", + "text-highlight-entrances-text" + ); + } else { + summary.element.classList.add( + "from-highlight-both-light", + "to-highlight-both-dark", + "text-highlight-both-text" + ); + } + } +} + +async function update_summary(updates) { + if ("checks" in updates) { + const summary_checks = document + .getElementById("summary-list") + .querySelector(`[data-scene="${updates.scene}"]`) + .querySelector(".summary-checks"); + summary_checks.textContent = `Checks: ${updates.checks.collected}/${summary_checks.dataset.checksTotal} (${updates.checks.remaining} left)`; + summary_checks.dataset.checksUndiscovered = updates.checks.collected; + apply_summary_colors({ + element: document + .getElementById("summary-list") + .querySelector(`[data-scene="${updates.scene}"]`).firstElementChild, + checks_collected: updates.checks.collected, + checks_total: summary_checks.dataset.checksTotal, + entrances_found: document + .getElementById("summary-list") + .querySelector(`[data-scene="${updates.scene}"]`) + .querySelector(".summary-entrances").dataset.entrancesUndiscovered, + entrances_total: document + .getElementById("summary-list") + .querySelector(`[data-scene="${updates.scene}"]`) + .querySelector(".summary-entrances").dataset.entrancesTotal, + }); + } else if ("entrances" in updates) { + const summary_entrances = document + .getElementById("summary-list") + .querySelector(`[data-scene="${updates.scene}"]`) + .querySelector(".summary-entrances"); + summary_entrances.textContent = `Entrances: ${updates.entrances.found}/${summary_entrances.dataset.entrancesTotal} (${updates.entrances.remaining})`; + summary_entrances.dataset.entrancesUndiscovered = updates.entrances.found; + apply_summary_colors({ + element: document + .getElementById("summary-list") + .querySelector(`[data-scene="${updates.scene}"]`).firstElementChild, + checks_collected: document + .getElementById("summary-list") + .querySelector(`[data-scene="${updates.scene}"]`) + .querySelector(".summary-checks").dataset.checksUndiscovered, + checks_total: document + .getElementById("summary-list") + .querySelector(`[data-scene="${updates.scene}"]`) + .querySelector(".summary-checks").dataset.checksTotal, + entrances_found: updates.entrances.found, + entrances_total: summary_entrances.dataset.entrancesTotal, + }); + } else { + console.log("What the heck is this"); + } +} + +async function update_checks(checks) { + total_checks = checks.total; + + let new_breakdown_block_checks_list = document + .getElementById("breakdown-list") + .firstElementChild.querySelector(".breakdown-block-checks-list") + .cloneNode(true); + let breakdown_block_checks_list_item = document + .getElementById("breakdown-list") + .firstElementChild.querySelector(".breakdown-block-checks-list") + .firstElementChild.cloneNode(true); + + // Create new lists with updated data. + let breakdown_list = Array.from( + document.getElementById("breakdown-list").children + ); + breakdown_list.forEach((scene) => { + // Create variables for element pointers. + let scene_title = scene.querySelector(".breakdown-block-title").textContent; + if (scene_title) { + const scene_checks = parseInt( + scene.querySelector(".breakdown-block-checks-title").dataset.checks + ); + if (scene_checks != checks.scenes[scene_title].collected) { + scene.querySelector(".breakdown-block-checks-title").dataset.checks = + scene_checks; + update_summary({ + scene: scene_title, + checks: { + collected: checks.scenes[scene_title].collected, + remaining: checks.scenes[scene_title].remaining, + }, + }); + scene.querySelector( + ".breakdown-block-checks-title" + ).textContent = `Checks: ${checks.scenes[scene_title].collected}/${checks.scenes[scene_title].total} (${checks.scenes[scene_title].remaining})`; + + new_breakdown_block_checks_list.innerHTML = ""; + breakdown_block_checks_list_item.classList.remove("hidden"); + Object.keys(checks.scenes[scene_title].checks).forEach((check) => { + if (checks.scenes[scene_title].checks[check].name == "") { + breakdown_block_checks_list_item.textContent = `❌ ${check}`; + new_breakdown_block_checks_list.appendChild( + breakdown_block_checks_list_item.cloneNode(true) + ); + } + }); + document + .getElementById("breakdown-list") + .querySelector(`[data-breakdown-scene="${scene_title}"]`) + .querySelector(".breakdown-block-checks-list").innerHTML = ""; + document + .getElementById("breakdown-list") + .querySelector(`[data-breakdown-scene="${scene_title}"]`) + .querySelector(".breakdown-block-checks-list") + .replaceWith(new_breakdown_block_checks_list.cloneNode(true)); + } + } + }); + // current_checks = checks.collected; +} + +async function update_entrances(entrances) { + total_entrances = entrances.total; + + let new_breakdown_block_entrances_list = document + .getElementById("breakdown-list") + .firstElementChild.querySelector(".breakdown-block-entrances-list") + .cloneNode(true); + let breakdown_block_entrances_list_item = document + .getElementById("breakdown-list") + .firstElementChild.querySelector(".breakdown-block-entrances-list") + .firstElementChild.cloneNode(true); + let new_breakdown_block_mapped_list = document + .getElementById("breakdown-list") + .firstElementChild.querySelector(".breakdown-block-mapped-list") + .cloneNode(true); + let breakdown_block_mapped_list_item = document + .getElementById("breakdown-list") + .firstElementChild.querySelector(".breakdown-block-mapped-list") + .firstElementChild.cloneNode(true); + // Create new lists with updated data. + let breakdown_list = Array.from( + document.getElementById("breakdown-list").children + ); + breakdown_list.forEach((scene) => { + // Create variables for element pointers. + let scene_title = scene.querySelector(".breakdown-block-title").textContent; + if (scene_title) { + const scene_entrances = parseInt( + scene.querySelector(".breakdown-block-entrances-title").dataset + .entrances + ); + if (scene_entrances != entrances.scenes[scene_title].found) { + scene.querySelector(".breakdown-block-checks-title").dataset.entrances = + scene_entrances; + update_summary({ + scene: scene_title, + entrances: { + found: entrances.scenes[scene_title].found, + remaining: entrance.scenes[scene_title].remaining, + }, + }); + scene.querySelector( + ".breakdown-block-entrances-title" + ).textContent = `Entrances: ${entrances.scenes[scene_title].found}/${entrances.scenes[scene_title].total} (${entrances.scenes[scene_title].remaining})`; + + new_breakdown_block_entrances_list.innerHTML = ""; + breakdown_block_entrances_list_item.classList.remove("hidden"); + new_breakdown_block_mapped_list.innerHTML = ""; + breakdown_block_mapped_list_item.classList.remove("hidden"); + + Object.keys(entrances.scenes[scene_title].doors).forEach((entrance) => { + if (entrances.scenes[scene_title].doors[entrance].door == "") { + breakdown_block_entrances_list_item.textContent = `❌ ${entrance}`; + new_breakdown_block_entrances_list.appendChild( + breakdown_block_entrances_list_item.cloneNode(true) + ); + } else { + breakdown_block_mapped_list_item.textContent = `✔️ ${entrance} -> ${entrances.scenes[scene_title].doors[entrance].door}`; + breakdown_block_mapped_list_item.id = `${entrance}-mapped`; + breakdown_block_mapped_list_item.dataset.scene = + entrances.scenes[scene_title].doors[entrance].scene; + new_breakdown_block_mapped_list.appendChild( + breakdown_block_mapped_list_item.cloneNode(true) + ); + } + }); + document + .getElementById("breakdown-list") + .querySelector(`[data-breakdown-scene="${scene_title}"]`) + .querySelector(".breakdown-block-entrances-list").innerHTML = ""; + document + .getElementById("breakdown-list") + .querySelector(`[data-breakdown-scene="${scene_title}"]`) + .querySelector(".breakdown-block-entrances-list") + .replaceWith(new_breakdown_block_entrances_list.cloneNode(true)); + document + .getElementById("breakdown-list") + .querySelector(`[data-breakdown-scene="${scene_title}"]`) + .querySelector(".breakdown-block-mapped-list").innerHTML = ""; + document + .getElementById("breakdown-list") + .querySelector(`[data-breakdown-scene="${scene_title}"]`) + .querySelector(".breakdown-block-mapped-list") + .replaceWith(new_breakdown_block_mapped_list.cloneNode(true)); + } + } + }); +} + +async function update_hints(hints) { + let hints_list = document.getElementById("hints-list").cloneNode(true); + let hints_list_item = hints_list.firstElementChild.cloneNode(true); + hints_list.innerHTML = ""; + Object.keys(hints).forEach((hint_index) => { + let hint = hints[hint_index].split( + /(\[[\w\s]+?\]|\"[\w \d\>\<#.\-\']+\"|\<[\w\d#]+\>)/gm + ); + hint = hint.map((segment) => { + segment = segment.trim(); + if (segment) { + if ( + !( + segment.startsWith("[") || + segment.startsWith("<") || + segment.startsWith('"') + ) + ) { + segment = translate(segment.trim()); + } else if (segment.startsWith("[")) { + segment = segment.trim(); + } else { + segment = ` ${segment.trim()} `; + } + return segment; + } + }); + hints[hint_index] = hint.join(""); + }); + Object.keys(hints).forEach((hint_index) => { + let hint = hints[hint_index]; + let matches = Array.from( + hint.matchAll(/\<([\w\d#]+)\>(.*)(\<[\w\d#]+\>)/gm) + ); + if (matches.length >= 1) { + hint = hint.replace( + /(\<[\w\d#]+\>.*\<[\w\d#]+\>)/gm, + `${matches[0][2]}` + ); + } + hint = hint.replace(/ "|" /gm, " "); + hints_list_item.firstElementChild.innerHTML = hint; + hints_list.appendChild(hints_list_item.cloneNode(true)); + }); + document.getElementById("hints-list").innerHTML = ""; + document.getElementById("hints-list").replaceWith(hints_list); +} + +async function update_scene(scene) { + Array.from(document.getElementById("breakdown-list").children).forEach( + (breakdown) => { + const breakdown_title = breakdown.querySelector( + ".breakdown-block-title" + ).textContent; + if (breakdown_title == scene) { + document + .getElementById("breakdown-list") + .querySelector(`[data-breakdown-scene="${scene}"]`) + .classList.remove("hidden"); + document + .getElementById("breakdown-list") + .querySelector(`[data-breakdown-scene="${scene}"]`).dataset.current = + "true"; + document + .getElementById("breakdown-list") + .querySelector(`[data-breakdown-scene="${scene}"]`) + .classList.remove("order-last"); + document + .getElementById("breakdown-list") + .querySelector(`[data-breakdown-scene="${scene}"]`) + .classList.add("order-first"); + } else { + document + .getElementById("breakdown-list") + .querySelector( + `[data-breakdown-scene="${breakdown_title}"]` + ).dataset.current = "false"; + + document + .getElementById("breakdown-list") + .querySelector(`[data-breakdown-scene="${breakdown_title}"]`) + .classList.add("hidden"); + document + .getElementById("breakdown-list") + .querySelector(`[data-breakdown-scene="${scene}"]`) + .classList.add("order-last"); + document + .getElementById("breakdown-list") + .querySelector(`[data-breakdown-scene="${breakdown_title}"]`) + .classList.remove("order-first"); + } + } + ); + Array.from( + document.getElementById("summary-list").firstElementChild.children + ).forEach((summary) => { + const summary_scene = summary.dataset.scene; + if (summary_scene == scene) { + document + .getElementById("summary-list") + .firstElementChild.querySelector(`[data-scene="${scene}"]`) + .classList.add("hidden"); + } else if (!(summary_scene == "")) { + document + .getElementById("summary-list") + .firstElementChild.querySelector(`[data-scene="${summary_scene}"]`) + .classList.remove("hidden"); + } + }); +} diff --git a/tunictracker/tracker/static/tracker/assets/translate-hints.js b/tunictracker/tracker/static/tracker/assets/translate-hints.js new file mode 100644 index 0000000..26fae1a --- /dev/null +++ b/tunictracker/tracker/static/tracker/assets/translate-hints.js @@ -0,0 +1,99 @@ +const lookup = { + ah: "aa", + R: "ax", + aw: "a", + A: "ei", + eh: "e", + E: "i", + Er: "ix", + uh: "uu", + Ar: "ex", + i: "ii", + I: "ai", + ur: "x", + O: "o", + oi: "oi", + oo: "u", + ou: "oo", + ow: "au", + or: "ox", + b: "b", + J: "ch", + d: "d", + f: "f", + g: "g", + h: "h", + j: "j", + k: "k", + l: "l", + m: "m", + n: "n", + "^": "ng", + p: "p", + r: "r", + s: "s", + $: "sh", + t: "t", + "%": "th", + "#": "tz", + v: "v", + w: "w", + y: "y", + z: "z", + "&": "zh", +}; +const skip = [" ", ",", "."]; + +const translate = (input) => { + let payload = ""; + let inQuote = false; + let cursor = 0; + + // // remove [text in square brackets] + // // remove extra whitespace (eg., "I saw A [hourglass] "HOURGLASS"" becomes "I saw A "HOURGLASS"") + // input = input.replace(/\s+\[.+?\]\s+/gm, " "); + // // remove + // // no whitespace concerns (eg., "sehz <#FF00FF>sohm%i^" becomes "sehz sohm%i^") + // input = input.replace(/<.+?>/gm, ""); + // input = input.trim(); + + while (cursor < input.length) { + // get what one character and two characters ahead would be + const one = input[cursor]; + const two = input.slice(cursor, cursor + 2); + // things in between quotes are in english, ignore it and set span accordingly + if (one == '"') { + payload += ``; + inQuote = !inQuote; + cursor++; + continue; + } + // if we're in an english quote, add it in and move on + if (inQuote) { + payload += one; + cursor++; + continue; + } + // check if this is a skipped character + if (skip.includes(one)) { + payload += one; + // check if we have a translation for this character + } else if (lookup.hasOwnProperty(one)) { + payload += lookup[one]; + // check if we have a translation for two characters ahead + // if so, advance the cursor head extra to make up for using two + } else if (lookup.hasOwnProperty(two)) { + payload += lookup[two]; + cursor++; + // uh oh + } else { + // console.log(input[cursor], "UNKNOWN", input[cursor].charCodeAt(0)); + } + // advance the cursor head to the next character + cursor++; + } + + return `${payload} `; +}; + +export default translate; diff --git a/tunictracker/tracker/static/tracker/data/holy_cross_codes.json b/tunictracker/tracker/static/tracker/data/holy_cross_codes.json index 595a04e..e8667d6 100644 --- a/tunictracker/tracker/static/tracker/data/holy_cross_codes.json +++ b/tunictracker/tracker/static/tracker/data/holy_cross_codes.json @@ -8,18 +8,21 @@ "Cathedral": { "Secret Legend Door": "LULURULURDRRURDLDRDLDL" }, + "Swamp": { + "Secret Legend Door": "LULURULURDRRURDLDRDLDL" + }, "Caustic Light Cave": { - "Casting Light Fairy": "RULURDRURDLDR" + "Holy Cross Chest": "RULURDRURDLDR" }, "Cube Cave": { - "Cube Fairy": "RRRRUUUURRRUUURRUURU" + "Holy Cross Chest": "RRRRUUUURRRUUURRUURU" }, "East Forest": { - "Dancer Fairy": "UDUDLRLRDLUR", - "Obelisk Fairy": "DRDLULDLURULURURULURDRDRDLDRDLU" + "Dancing Fox Spirit Holy Cross": "UDUDLRLRDLUR", + "Golden Obelisk Holy Cross": "DRDLULDLURULURURULURDRDRDLDRDLU" }, "Eastern Vault Fortress": { - "Candles Fairy": "RLDRUL" + "Candles Holy Cross": "RLDRUL" }, "Global": { "Firebomb": "LURDRURDRURDL", @@ -27,54 +30,54 @@ "Icebomb": "LDRURDRURDRUL" }, "Hourglass Cave": { - "Hourglass Door": "RULURULUURDLDRDLDR", - "Hourglass Fairy": "LURURDRURULLLURU" + "Holy Cross Door": "RULURULUURDLDRDLDR", + "Holy Cross Chest": "LURURDRURULLLURU" }, "Library Hall": { - "Library Fairy": "URDRULURULURDLDRURDRULURULULDRDLDRDL" + "Holy Cross Chest": "URDRULURULURDLDRURDRULURULULDRDLDRDL" }, "Lower Mountain": { "Top Of Mountain Door": "ULDLULDLURURULURDRULULURULDLURUULURDRDRURDLDRURRDRURDRURRDLDLDRDRDLLDRDLDRURDRURRDDLURULDLULULURRULU" }, "Maze Cave": { - "Maze Fairy": "ULDLURDRURDLULU" + "Maze Room Holy Cross": "ULDLURDRURDLULU" }, "Old House": { - "Old House Door": "ULDRDL", - "Old House Fairy": "UURDDRUURDDD" + "Holy Cross Door": "ULDRDL", + "Holy Cross Chest": "UURDDRUURDDD" }, "Overworld": { - "Back To Work Treasure": "RDLULLDRRDD", - "Compass Fairy": "LRDUUDRLURDLUDRL", - "Fire Wand Obelisk Page": "URDLDRUL", + "Starting Platform Holy Cross": "RDLULLDRRDD", + "Weathervane Holy Cross": "LRDUUDRLURDLUDRL", + "Golden Obelisk Page": "URDLDRUL", "Fountain Cross Door": "DRULUR", - "Fountain Fairy": "DLLDURD", - "Lower Flowers Fairy": "URDLDLDLDLU", - "Moss Fairy": "URULDLULURDRDRULURDRD", - "Power Up Treasure": "UDRUDLUDLRDLRU", - "Sacred Geometry Treasure": "DLLUURRLDRUD", + "Fountain Holy Cross": "DLLDURD", + "Southwest Flowers Holy Cross": "URDLDLDLDLU", + "Moss Wall Holy Cross": "URULDLULURDRDRULURDRD", + "Windchimes Holy Cross": "UDRUDLUDLRDLRU", + "Windmill Holy Cross": "DLLUURRLDRUD", "Southeast Cross Door": "DRULUR", - "Upper Flowers Fairy": "ULDLDLDLURDRRDR", - "Vintage Treasure": "DRRRRRRRRRRRLLLLLLLLLLLL" + "Northeast Flowers Holy Cross": "ULDLDLDLURDRRDR", + "Haiku Holy Cross": "DRRRRRRRRRRRLLLLLLLLLLLL" }, "Patrol Cave": { - "Patrol Fairy": "DDRURDLDRURDD" + "Holy Cross Chest": "DDRURDLDRURDD" }, "Quarry": { - "Quarry Fairy": "URDLURULDLURRD" + "Bushes Holy Cross": "URDLURULDLURRD" }, "Ruined Passage": { - "Ruined Passage Door": "LURULDLURDRULURD" + "Holy Cross Door": "LURULDLURDRULURD" }, "Sealed Temple": { - "Temple Fairy": "URULURULURDRUUL" + "Holy Cross Chest": "URULURULURDRUUL" }, "Secret Gathering Place": { - "Waterfall Fairy": "DRURURULULURURULDLDLDRDLDRDRUR" + "Holy Cross Chest": "DRURURULULURURULDLDLDRDLDRDRUR" }, "West Garden": { - "Sword Door": "DRULUR", - "Tiles Fairy": "URULURULURDRULDLURULURULU", - "Tree Fairy": "LDRLUD" + "Holy Cross Door": "DRULUR", + "Holy Cross (Blue Lines)": "URULURULURDRULDLURULURULU", + "Tree Holy Cross Chest": "LDRLUD" } } diff --git a/tunictracker/tracker/static/tracker/fonts/Trunic-Regular.otf b/tunictracker/tracker/static/tracker/fonts/Trunic-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..da4a6e89b8f329efc870fa2a1b4843805af9ee31 GIT binary patch literal 61176 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 literal 0 HcmV?d00001 diff --git a/tunictracker/tracker/templates/index.html b/tunictracker/tracker/templates/index.html index 2a547fb..a855253 100644 --- a/tunictracker/tracker/templates/index.html +++ b/tunictracker/tracker/templates/index.html @@ -11,9 +11,12 @@ {% load static tailwind_tags %} {% tailwind_css %} - + + + + - + {% block content %} {% endblock content %} diff --git a/tunictracker/tracker/templates/tracker/breakdown/block.html b/tunictracker/tracker/templates/tracker/breakdown/block.html index 612cac8..aee0432 100644 --- a/tunictracker/tracker/templates/tracker/breakdown/block.html +++ b/tunictracker/tracker/templates/tracker/breakdown/block.html @@ -8,7 +8,7 @@
- Checks: {{ scene_data.checks.collected }}/{{ scene_data.checks.total }} + Checks: {{ scene_data.checks.collected }}/{{ scene_data.checks.total }} ({{ scene_data.checks.remaining }} left)

@@ -28,7 +28,7 @@
- Entrances: {{ scene_data.entrances.found }}/{{ scene_data.entrances.total }} + Entrances: {{ scene_data.entrances.found }}/{{ scene_data.entrances.total }} ({{ scene_data.entrances.remaining }} left)

diff --git a/tunictracker/tracker/templates/tracker/codes/index.html b/tunictracker/tracker/templates/tracker/codes/index.html index ee33f89..23d583a 100644 --- a/tunictracker/tracker/templates/tracker/codes/index.html +++ b/tunictracker/tracker/templates/tracker/codes/index.html @@ -1,4 +1,4 @@ -
+
{% comment %} drop-shadow-[0px_0px_4px_rgba(255,255,255,1)]"> {% endcomment %} Holy Cross Codes
diff --git a/tunictracker/tracker/templates/tracker/hints/index.html b/tunictracker/tracker/templates/tracker/hints/index.html new file mode 100644 index 0000000..5ee37cb --- /dev/null +++ b/tunictracker/tracker/templates/tracker/hints/index.html @@ -0,0 +1,3 @@ +
+
{{ value }}
+
diff --git a/tunictracker/tracker/templates/tracker/howto/index.html b/tunictracker/tracker/templates/tracker/howto/index.html index 45b7b24..b7617d2 100644 --- a/tunictracker/tracker/templates/tracker/howto/index.html +++ b/tunictracker/tracker/templates/tracker/howto/index.html @@ -1,8 +1,8 @@ -
+
How To Use This Tracker
-
Setup
- -
Usage
-
    +
    Usage
    +
    • The ratios count down, so if you have 3 out of 5 total checks, that means you still need to get 3 checks out of the 5 that are in the area.
    • @@ -72,7 +72,7 @@

    -
    +
  • * Since tunic doesn't save when you do a code, it won't update until you do something that causes the game to make a new save, like pausing, going to another area, or clearing a check.
  • diff --git a/tunictracker/tracker/templates/tracker/index.html b/tunictracker/tracker/templates/tracker/index.html index e6036b8..8095f85 100644 --- a/tunictracker/tracker/templates/tracker/index.html +++ b/tunictracker/tracker/templates/tracker/index.html @@ -6,39 +6,49 @@ {% if debug != '' %}
    -
    - +
    + A trans pride fox emoji. - A trans pride fox emoji. +
    Tunic Transition Tracker
    +
    +
    + ( +
     tuniik' t'raan'ziishuun' t'raakx 
    + )
    -
    (Tunic Transition Tracker)
    - {% include "tracker/howto/index.html" %} -
    +
    -
    Checks: {{ totals.Checks.Undiscovered }}/{{ totals.Checks.Total }}
    -
    - Entrances: {{ totals.Entrances.Undiscovered }}/{{ totals.Entrances.Total }} +
    +
    +
    + Checks +
    +
    +
    + {{ totals.Checks.Undiscovered }}/{{ totals.Checks.Total }} ({{ totals.Checks.Remaining }} left) +
    +
    +
    +
    +
    +
    + Entrances +
    +
    +
    + {{ totals.Entrances.Undiscovered }}/{{ totals.Entrances.Total }} ({{ totals.Entrances.Remaining }} left) +
    +
    -
    - Summary -
    -
    -
    {% include "tracker/summary/list.html" %}
    -
    -

    @@ -48,20 +58,36 @@ {% include "tracker/breakdown/block.html" with extra_classes="hidden" is_current_scene="false" %} {% for scene_title, scene_data in scenes.items %} {% if scene_title == current_scene %} - {% include "tracker/breakdown/block.html" with extra_classes="order-last" is_current_scene="true" %} + {% include "tracker/breakdown/block.html" with extra_classes="order-first" is_current_scene="true" %} {% else %} - {% include "tracker/breakdown/block.html" with extra_classes="hidden" is_current_scene="false" %} + {% include "tracker/breakdown/block.html" with extra_classes="order-last hidden" is_current_scene="false" %} {% endif %} {% endfor %}
    - {% comment %}
    - {% include "tracker/breakdown/block.html" with scene_title=current_scene.title scene_data=current_scene.data %} -
    {% endcomment %} - {% include "tracker/codes/index.html" %}
    -
    +
    + Summary +
    +
    +
    {% include "tracker/summary/list.html" %}
    +
    +
    + {% include "tracker/codes/index.html" %} +
    + Hints +
    +
    +
    + {% include "tracker/hints/index.html" with value="" %} + {% for name, value in hints.items %} + {% include "tracker/hints/index.html" %} + {% endfor %} +
    +
    +
    +
    Settings - {% comment %} {% include "tracker/settings/index.html" %} {% endcomment %} {% include "tracker/address/index.html" %}
    + {% include "tracker/howto/index.html" %}
    {% else %}
    {% endif %}
    diff --git a/tunictracker/tracker/templates/tracker/summary/block.html b/tunictracker/tracker/templates/tracker/summary/block.html index f7869c6..64de2ab 100644 --- a/tunictracker/tracker/templates/tracker/summary/block.html +++ b/tunictracker/tracker/templates/tracker/summary/block.html @@ -7,12 +7,12 @@
    - Checks: {{ scene_data.checks.collected }}/{{ scene_data.checks.total }} + Checks: {{ scene_data.checks.collected }}/{{ scene_data.checks.total }} ({{ scene_data.checks.remaining }})
    - Entrances: {{ scene_data.entrances.found }}/{{ scene_data.entrances.total }} + Entrances: {{ scene_data.entrances.found }}/{{ scene_data.entrances.total }} ({{ scene_data.entrances.remaining }})
    diff --git a/tunictracker/tracker/views.py b/tunictracker/tracker/views.py index 1d90d1a..c8c390e 100644 --- a/tunictracker/tracker/views.py +++ b/tunictracker/tracker/views.py @@ -4,7 +4,7 @@ from django.template import loader from json import loads, dumps from math import floor import requests -from .forms import ServerAddressForm # , BackendFilepathForm +from .forms import ServerAddressForm import logging # Create your views here. @@ -12,8 +12,6 @@ import logging # Debug and defaults. defaults = { "listen_address": "http://localhost:51111/", - # 'backend_filepath': '', - # 'backend_filepath_updated': False, } logging.basicConfig(encoding="utf-8", level=logging.DEBUG) @@ -35,7 +33,6 @@ def session_key(session, key): def index(request): listen_address = session_key(request.session, "listen_address") - # backend_filepath = session_key(request.session, 'backend_filepath') try: request_overview_data = requests.get( @@ -50,31 +47,19 @@ def index(request): request_hints_data = requests.get( f"{listen_address}hints", timeout=5, verify=True ).text - # if (loads(request_data)['Scenes'] == None): - # with open('empty_spoiler.json', 'r') as t: - # try: - # request_data = t.read() - - # except: - # return - except: - with open("empty_spoiler.json", "r") as t: - try: - tracker_output = loads(t.read()) - - except: - return - try: tracker_overview_data = loads(request_overview_data) tracker_items_data = loads(request_items_data) tracker_doors_data = loads(request_doors_data) tracker_hints_data = loads(request_hints_data) except: - with open("empty_spoiler.json", "r") as t: - try: - tracker_output = loads(t.read()) - except: - return + with open("less_fun_overview.json", "r") as t: + tracker_overview_data = loads(t.read()) + with open("less_fun_items.json", "r") as t: + tracker_items_data = loads(t.read()) + with open("less_fun_doors.json", "r") as t: + tracker_doors_data = loads(t.read()) + with open("less_fun_hints.json", "r") as t: + tracker_hints_data = loads(t.read()) with open("tracker/static/tracker/data/holy_cross_codes.json", "r") as t: try: temp_codes = loads(t.read()) @@ -92,13 +77,6 @@ def index(request): except: return - # tracker_debug = tracker_output['Debug'] - # tracker_totals = tracker_output['Totals'] - # tracker_current_scene = tracker_output['Current']['Scene'] - # tracker_current_scene_data = tracker_output['Scenes'][tracker_current_scene] - # tracker_scenes = tracker_output['Scenes'] - # entered_codes = tracker_output['Codes'] - # Data from the /overview API call tracker_current_scene = tracker_overview_data["scene"] tracker_seed = tracker_overview_data["seed"] @@ -107,10 +85,12 @@ def index(request): # Data from the /doors API call tracker_entrances_total = tracker_doors_data["total"] + tracker_entrances_remaining = tracker_doors_data["remaining"] tracker_entrances_mapped_total = tracker_doors_data["found"] # Data from the /items API call tracker_checks_total = tracker_items_data["total"] + tracker_checks_remaining = tracker_items_data["remaining"] tracker_checks_cleared_total = tracker_items_data["collected"] active_codes = {} @@ -121,16 +101,6 @@ def index(request): } logging.debug(active_codes) - # try: - # tracker_codes = cross_codes - # for scene in entered_codes: - # for k, v in cross_codes[scene].items(): - # tracker_codes[scene][k] = ( - # v, entered_codes[scene][k]) - # except Exception as e: - # current_cross_codes = {} - # # print(e) - scene_data = {} for scene in tracker_doors_data["scenes"].keys(): scene_data[scene] = { @@ -141,64 +111,34 @@ def index(request): template = loader.get_template("tracker/index.html") server_address_form = ServerAddressForm() server_address_form.fields["server_address_form"].initial = listen_address - # backend_filepath_form = BackendFilepathForm() - # backend_filepath_form.fields['backend_filepath_form'].initial = backend_filepath logging.debug(format_overview_output(tracker_overview_data)) context = { - # 'backend_filepath': backend_filepath, "server_address": listen_address, - # 'is_hidden': is_hidden, "debug": {}, "default_codes": cross_codes["Default"], "totals": { "Checks": { "Undiscovered": tracker_checks_cleared_total, + "Remaining": tracker_checks_remaining, "Total": tracker_checks_total, }, "Entrances": { "Undiscovered": tracker_entrances_mapped_total, + "Remaining": tracker_entrances_remaining, "Total": tracker_entrances_total, }, }, "scenes": scene_data, "current_scene": tracker_current_scene, - # "current_scene": { - # "title": tracker_current_scene, - # "data": { - # "checks": tracker_items_data["scenes"][tracker_current_scene], - # "entrances": tracker_doors_data["scenes"][tracker_current_scene], - # "Totals": { - # "Checks": { - # "Undiscovered": tracker_items_data["scenes"][ - # tracker_current_scene - # ]["collected"], - # "Total": tracker_items_data["scenes"][tracker_current_scene][ - # "total" - # ], - # }, - # "Entrances": { - # "Undiscovered": tracker_doors_data["scenes"][ - # tracker_current_scene - # ]["found"], - # "Total": tracker_doors_data["scenes"][tracker_current_scene][ - # "total" - # ], - # }, - # }, - # "Codes": tracker_current_scene_codes, - # }, "codes": active_codes, - # }, "server_address_form": server_address_form, - # 'backend_filepath_form': backend_filepath_form + "hints": tracker_hints_data, } return HttpResponse(template.render(context, request)) def get_address(request): if request.method == "GET": - # session_key(request.session, 'backend_filepath_updated') - # 'backend_filepath_updated': request.session['backend_filepath_updated']}), content_type="application/json") return HttpResponse( dumps({"listen_address": request.session["listen_address"]}), content_type="application/json", @@ -218,26 +158,6 @@ def set_address(request): return render(request, "tracker/index.html", {"server_address_form": form}) -# def set_settings(request): -# if request.method == 'POST': -# form = BackendFilepathForm(request.POST) -# if form.is_valid(): -# request.session['backend_filepath'] = form.cleaned_data['backend_filepath_form'] -# request.session['backend_filepath_updated'] = True -# return HttpResponseRedirect('/') -# else: -# form = BackendFilepathForm() -# return render(request, 'tracker/index.html', {'backend_filepath_form': form}) - - -# def get_settings(request): -# if request.method == 'GET': -# request.session['backend_filepath_updated'] = False -# return HttpResponse(dumps(request.session['backend_filepath']), content_type="application/json") -# else: -# return render(request, 'tracker/index.html') - - def format_overview_output(overview): overview_string = "\nReceived Overview Data:\n" for key, value in overview.items(): diff --git a/tunictracker/tunictracker/settings.py b/tunictracker/tunictracker/settings.py index 90d1551..ecc1e33 100644 --- a/tunictracker/tunictracker/settings.py +++ b/tunictracker/tunictracker/settings.py @@ -28,6 +28,7 @@ DEBUG = True ALLOWED_HOSTS = [ "localhost", "127.0.0.1", + "100.64.0.4", "tunic.werefox.cafe" ] diff --git a/tunictracker/werefoxtheme/static/fonts/Trunic-Regular.otf b/tunictracker/werefoxtheme/static/fonts/Trunic-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..da4a6e89b8f329efc870fa2a1b4843805af9ee31 GIT binary patch literal 61176 zcmeF4dwg6~wfOhrWb$Y-?>yR>CX-B>Ce3^DY-vN97ik+yNJB`l(4;TgT4*UPlv)w7 zVy%dX6%i^TR;;z+v{bBE5wRD0xs{9Diio|)_^OIXl(+yZ=f) z*=y~!_St*wwZD6xGmjF+8Dm~348|_%>Fvwu$ehg>&jstTiWPiHA8VZo_2bay^%d7|j${mF|0`oJL2s;M^~S5NUS9Xt z4??|%F?a4&6W49T(HQ&HIgnXbt-o>gZ~pjrgt3YvjBOfSvua{x@!w8-iLoz08`io8 z4DuBF3)K6eUcTn)&0D9Ob5%ioJ!6@7uivm@;@d4h-ObpSVZ6*8S5Iu+n9=6#Vr<_| zXdk&|;_6l9qH`V8_d$H8Hg33X^P!6`^E0-82V*TaY}~YJV|>w1V7%7{q5anw<^pGq z{|QzMWe99xHeJ;spqIjrU}dZv^cnCYSOxrWHWPjXt7Nl4uZADNYT$>nTKEyHjx~VZ z2tR_wSQF^YtQGV&_~EP_egvD%=74?y{0KIeT?YCn{0KG%Kb&0-KZ3m)oQsC?PTF&c~!W4rNyaol)4BbYHKs-ay`P-qO64dF%5w=WWm1k#}3(?z}yDd-D$D9nL$N zcP#Hj-pRaEd8hNvABPW(~G7TPcNT7i!IFT($C$!+`Z1d z$-T{ei+h**PWL_T2i*JJhun|4pKw3re%Aeh`(^iQ?l;}GkmqvGa?d)?CeJp{EuLMTJ3aS!9`NiBw}$72yTbk9#o^)b<>B%0>hSvTrtsGA zP2nBkUEw>zcZK(a9|-S*sLC_zGTWW8rO(x~)U(pF-m}@W-Lu1Un`gIYk7uvvfakF1 zsOOmHgy*E^l;^bPjOVQ9oY(N?dV}7ux7=IfZT8Of_IL-p!``LdmEodrakxA@E8KAY z_}Gd}SEe^}+GMd2GoI%>FM3|_yzY6+^RCzBb$biE#oh{UowwE7;qCJ-_KtY3^se?^ zZY`<-T>k zO`*p^$3jnqo(Vk{Iu&|3^lIq!(Ao5Oncvs%8}yC(#(isi8+}`SH~V(_?(p61yWie3 z3%EA9w!3bHoqLaKuj?V#5!Vy06Rzi6FZpiq?eg8}yT|u{Z-3}5=*_D64*4GSJ>h%G z_pI*)-^=!xq17?p3ExTIDc@<|8Q)poIltl0^#}do(6gcELobF-hh7W40j=KkyZml{ zfxp;a;jiAm5^xxxuz`x&r$p5JS3I9`}6Ui~5)dBxu|55)j{|Wy||0(}z{~7;T|G9u+&my#X z#s9khE&sa#SHK-82owh@0(F7bK!<&8m_HB+R0bLX?Salfe_${$8W<0(32Y2(4LzA0 zlg-GuGhCFPJe~1;#>*LJGTw5rz~zDEfpvjRfo*|X0=oit2JQ(w5ZE6$ z6nHf7MBu5wvw;@^FQ=|9jCfz*!N5a-M*@!po(w!4crNf_;FZAZfww{rg!Y9V3LOqT z8hRXBJs)@}@M_?Vz}rC<%nEvgMZrk0GT0EhKlEVeKxF=(8 z#zPrLGM>mdk?~x{OF@5dTCglQD;NvT33de+28V)|2bTxe1vdq^1#b!N3f>vKCv;zE zZ)kslSk|6#U&g+SLm5Xip3HbAHiV)xm3m*9UJ3-Wt3;cvtYg;Df=3f{z3r3qBcqI`~}h#o#N!*Mn~b-_3XB zyF=SUw}f_vZV&Aa-Gj4W{@~u=f#Bib(crP*iQviLso?40nc&&rxzL@ggp^i{bXklnDG{XG(Mfs8Z%KV1>_WaKL{`|rG(fslJHTfGu zt)aQ0u26qyacCIwwLjx<#$y@BGoH;jmGMf(8yWAo442zgls}YzdH(YJb@`j}x8>iG zzbpUF{Cn~r$lsrTDF4y?C-R@le>VSxP;*LL9PXtdR>GpJiq*qCe-7N&=dljf$+}n% z>t%hcpDkbu*#KM22H6l>!iL$1m<4y^5VY(Pol0B4Iz@-l7K3fB(gs1BXKEwjLZywc zF1Zb~C|)b?P#Rn-Z&w;zD{oU8Tq{3iY9sQ~O2cdSgI2GueOzgH?PE&AYmX`oul>HM zjmRG;4Xzbkpv6?oqe{b=N0f#!4=W90zSPu4#AQmuahHQeF>h5Gj(fe*aNNyG!*REm z+K9YCX*ljC&}iJ9O2ct)RT_@FLuokf$4qTRd|YXG?E|2xam5j(;kAz_4X-_{G`#lP zrZytJqcptsQP61IXOxE5KCLvo_Jq>#+Mk%(i1>G<;e77|jpn;gX*k~pm4@@(t2CVN zS50k1eobjO?rWe`sWrl9^(vsJQ@pD*9QT~kaNNI{+K6~hX*jL`O~ovWl!oILC=JIA zDhI}D2wtOQ;YGJl~x6|Ii^;{hfFQTZ&6x3M2kD1Q;qwQsYQj? z)an`L9=-6St27+M!V%()P9JkoiV*D+o;kb)Ut%|QUwHUukX*e$KZW{NLsYOMusns*& zipD*rG#vM&sl~XVG#oc=6x0_KHR&`$SC1e$k}l0@1AGLeZk+fM`{6v1n6rP_!#KBxWnQM9fig zSX`jwh`bSGl+BSJRC1o&u4IS&kdmG9CMCP%hn4J+H!ImIKcZxxyhX`=`B5bo$Q?>9 zlpj-aK;Ek4V)=0;2jxyBhvX-eTq1WVIV?Y^UA^BY;m&iwz9G2fx zazxAr3HM&nt>iq>qhyEZRkBm`DcL11QnE+%E7>a+DA^|#D%md%nL?{biMcF$09M9c(cr{su0JLVJXF*Z+ZR~Pn-|t6X%Kf#CZ-vJDeZnQ+ydDu1@#E*POA%5gL3h^V~QHUS;jzV0>E#?z> z#e5!BvO|1d$xiW@l3n5lO7@7ymFyKiRI*Pzp=7`Kk&+9^sC zw~`(5b4qr~yOivbpI5R+-mPS>{DP8w@*XAo<-JNSkT?$HQ{p&~Pl@A0J|&I^`IHzJ zgo+m-B`-#CyimzO*{S4^oUh~(*`?&L>{fC_ zP6G+?%Lb4TzpMiZ@yi;J5WlQdazxgH#P}D2#P;JLvHg`GvHenz*!~J7N8~b)(4O50 z5}#Iae}v#^0M|7H89>hQIp!cpe44=hR|1*+BgiPU!}W@S|Gz-uQv}YxidFGTl#KC@ zfW+sP;)yGyiCa|zD~({evgtd_Cb&s5AIKl2f4y{aKB+Z4}-*baQ|aG ze*%f|ATJote}lw$F#j0O5+!5&qaZP!?I5xL6G}$;IVI~E?qlr#Fi7kl&lT(+_XGBi z9Af{-4fc=giv15O8RI)ZV*eikiT(dX$teGulJyMF2kigbAhCZuudsjIXV^dTjr}8! z*gx`u{a>nNjDHLy_J0#d?Eh&cqx@YZ>lvOq*#9FSv41=Vv47mZ*gx(Y>>v5Z{*goM ze?-X`zZE3*|6!2W|Gz64Q#|Ia8H6->!`hUX;qe*`48RMS-iT&RK68pz< z1^5&~$$EzTV*HPS#QvXBGREEkiT&fbgz>)y68rB`vWj1>WQ^|uiTxv|*#Cbh85L5= zdUmgpG5$S}*gx(Mj34(0_8(9(#(xJA`|nn=ioajU82=Tj7jEWp3>)8WJ#`qH;v47m(*gx)X?0=e)G5#hReZUUF@7gV z>>u|T_Ww&IqhgAZ_3SH3#`upwV*j|mv47m(*nddL82=MU?0=DxReXh#G5%SQ*#8Y6 zaea{!Twmk?*SAf{D*j<5>-o2ojKL?l0$R5~8C9RT)~io;W5NfPIU)e%JQ0MlL*zr* zDGH$M5`|Fqh$1L^#Z)N!#55@TMF`3T0{dJj@Dt(zQ4Hl`Q3BBY~OhDNumP6SuRzSHx ztb}r*SOw*PSPkW3aTSz$^TcgX zc8E_w*(q*^vP*m#${ukCl)d6JQ1*#Cq3jo*g>r$|4dp`dIVcCjT~IC-pNDc#+zsWB z_yUwm#63_Bi+iCQQJ-^1)hFUr>a*~e_$k=ui2s0co;V3*hxi$ko#HttyTs3->=DmH z*(-hlWuJHf%6{=nC>MxRP%aez3FUxz5z58lS5OX$m!KRH{{`g|@iLUd;=iFBQJ=Wi zOI)=%64z^<#P#ZsxL%zS*Q-n7di6+LuU?7k(kIKH?3cJ+3uHNz3ngZ6KvqDxSmG!B zgR&CJAz20G5;+UXVTqsmkH`g3&XJ3toF@mM?2zw+vQsXGvP)hJWse+$vR7ULWuF{^ zvR^KNa)BI%a-qBw$^kh7kH#DWG2ME0c8opOh!4*u8MyE%6fh;lre@xh1dZoOBm)k%5m;hd_9!) zd=Hc{hP;K?H=!(HxC&8@nW^GeLs`$i2xW{RjUo0BlqC$;D9SN!Rs0$#>-l|9#u&01 zV&8(Ygy9NCIcBwrZ-BC%e+kMMLy|)*4rK|$^^J1Oc@^IXWj((i${53$hS)(UOBk+x zlvhDn#jl03o_`t27{gr3HvpaQC|Q{VIGVwjT<`wo;P>^D$Gc|DX>{01oN z`PZO~sc(vOsqcu4Fw9Sg9f7ihy$WTNH$Yj%w?bLZ_dywBn4b{)E|ewgw@^lTBa~JA zMkwp~*P)Ct%uk3t3S|j<4az8wL0QGOL0QlDLm6Y3pAh>VlqKwUP)2zZlvVtLP}cKr zKpBJYs33s`AAbJ%H8lz8EygfG?z&|H*`cvVR~Ffjeo0eN$ZZ z-nXIURcsNM;bsa&4Sc@Cz=z*)!K?5$Og@;qR2sgoeFE%3_~wxWy%bJC{6ynI$k#Tw zSAP?}8-sn!gzwDE0qcC&SElv7U%vPJdr!ajZ{TO|O}sY>{RL51|GDAWkp9E_F1#-c zea?eC(5HM++vj{$@JU~<`B`5*!%zF*RXOb6|DS6Zd{Xdt|KpTFfenoeS7!2ptZdM8 zSZK}#^GZ58yQ1)wul@y93ws9^57rMYi4BiX)4%6WcKCnP)H42m^r6+V`H76++5Bo$|J?oih}-J_(qdEFEfKp z;Zt%l3#xC3732q9(Xxt`=GK`lP5II0)~1BXXVX8F>-XovZ%%D(UUs(Iot>SRkcX86 zoJ-z$2dz1IdE77+NFIm0{ej&J&wqu?*Xee<;3fv<>kEP$#t#JHi!$)j(%jVA*4kDv z69a(=?&TuOQ^#WZ- zT=23BvrZZ$K0^vjz0aFr6y|$FzR;9x!;|6hd9yN$s+YjLTN1OT@vWrXc*`y3Ty9HD zuDiC@K7Y&u%@f`;zfIqa|MGasB& z&UcFZxh~0NtonvPp`6*l3yXQCo6oG{0h_+{(%MUFg(o{FhpTb~zB`t!^z11Y@|UyT zEzU|*1^4hX*;7DJ3p`wx!Fx{k!t1Unuu_ekbq0nG-xXY&!G)A!41|#I>Oja#@_?v- zFSjtSkf!8Hn^K`>dPooc@`n=BJf2Y0*q>Bm2jFb1>8#2jN(8%Aau}A6f#3ojWw^K` zKNy-CEXgnNdkq-2jQ~o4XQpj<1j!wO<1K+vu^Gp4q{%M~mxa0?>!#3#{;oQ5rU=t$KzRIi>J+| zXSNk)ddx85ru@d&y}EE#t8f4|#x7;W0$(4~{i&L@u^T(D&x1|O#6~#rU3|(W%p|;o zU+3dFxg72R{3;JTEGSOhaBOE7V=T+dvoio!s2dp*EW^mSq_(EAa(Wm~PsW-WYuakt zDyu82XH`W@!!xJPw4bOjy7c)o3#%^yYU~-dA9*P=O>0-2m^QA2{Wsh4@S5Ytf1dEY zyD_0z3v}Jem6jXgVekDB-W|QpEIIWrM) zVxjiz_kR64M^wvconSHEdj~!pz<4~Yy0g*)(eMFxp5P14f77ssfV?J#b?*;<(Hc&y z4B$I@```N`ymqn8S?v?tF#n4=ud>ZE)2;)Yj?E(Wdo+`#y&~A5_uet%QFF%G!lYs9 ze`n4qA=rn7&2XPJY1jf+B+iJBz!@P`ZbNKt=WG~B3QVyWfMWqNBjj2*EWj|P&Ws85 zNallo-9Qv4Z)oB4u5r7suu=Mva=nQ42^KE?gT#ny&PY(sv>@#8C3uC(%g z+%`$rXYd);aeMAPFax#H|EJvJ4zmZA$UW||Oh@ibG_8Y zT@}wSP25uXA_)ZUV0~H>q2V zkgM<(6J&6D7ovl4d2^a)tTUi{Of4*PgU=3W*HasK&qTF^e|d-F;5~hkb{^HvXX{T| z@jxWZ)|0KT^fp#6llBAe-%NVFVD|A-^HGNRutzcJe5jojiE0P=;TKr*apc72b6n@h z7xu4aVVj38Ry7-29@5+38aifmo<8mEttf5%r>=#-mA7(~?t|+Sd2!4yIZo|1~)>_9X##;tYBPPw7xa@hP+5P=|Q#*UUlWRhReO|(D#Y^^%N$j1I*t-(;wElZ0vG*qIY2)@K>}mb=r+1 z?VbEkdnZ3+ck)Aai=VXqEq>DMPJXB#i=VXi7C&irCqLAWlOM7>`60WLAF^Bgq>XFw zlV*4FL;X1UA-j_wvOD=9yOSTXTl}QO`60WLAF`+NlN>jdpQJsNpQPQ%5B2ZlhwQ2RB>QplL+w-fNw#e2y%ggdegy`60WLAF@07A-j_wvOD=9yOSTX zJNY5IlOM7>`60WLAF@07A-j_wvOD=9yOSTXJNY5IlOM7>`60WLAF@07A-j_wvOD=9 zyOSTXJNb!f_(877?&OE;sr)3{r}C4uJNco0ocxg8$q(6`{E$7BpX9iy{3PwE{3Pv8 zeyD#ZKV(nkC)tmaA8MbL(ubar8!jF1?i0rBSB;!ftCuw)`L;X1U zA$ux6$$p&tQ2SJVlI@-RPhx&2yL-tgDlKnXO zq4uf#B-=arq4rLG$nNBa>`s2jp2|;hTqi%&-pLQyo%~cy!VlS<{E*$r580jkklo1- z*`555-N_Hxo&1p9$q(6`{E&TtgF~`A`60WLAF@07A-j_wvOD=9yOSTXJNY5IlOM7> z`60WLAF@07A-j_wvOD>y*6@Sp8`+)wkUf>3WcyTpl6EIQ)Q^)NvOD=9yOSTXr}C2= zHfgx^*;DyR_T%J-+Nbi9Z13cU+B^9nyOSTXJNY4dDnH3_o%~RH zCqHC&@>8qf2hTUMJNY4dDnH5gsr)4EPJXB#CqHC&@L#gFw}7;5k2hwM&%$nNBa z>`s2j?&OE;7C&imI{BgY7C+Ye)v-zVA-ly-T6-ry)ZWPt*`555-Qp*$e~X_qyOSU4 z$Kofgy~R(O-N_I2`s2j zZt;^Ar;{IQZ}DUC);tM6WViT9YwzTT+B^9nyOSTXTl}Q;Z}F36ck)C1Sp1~5xA;l3 zJNco0ocxg8$q(6`{E*$^CvDuJ1V7gKL+zdXkbOAO-dbO>JNY5IlOM7>`60W-Pg`60WLAF@07A-j_wvOD=9yOSTXJNY5I zlOM7>`60WLAF@07A^Wg{AF@07A-j_wvOD=9yOSTXJNY5I#gD~vtA-zZeMfeSpS1R= z{3P3_@{_bX`Jw(Te$x84_(`)n`JsL+ez3iI|C#I-KdPT(JgNL7?M{BEA16O#Pvs}s zkHt?~{1!iH@i_UR{+;}geZ;{P*`555J(ZtioEAT6aXR^-ew_TYPr?t`o&1p9$q(6` z{E*$r580jkklo1-*`555-N_Hxo&1p9$q(6`{E*$r580jkklo1-*`555-N_Hxo&1p9 z$q(5_9bA#!$q(6`{E*$r580FaeB1nva|lK(Q{NvGY?}FOm+G6V2-EwJqsgwj@I1cx z=kax)$A?@^HXgDyneU?W_>i&5u0zfy^DR1$?|tX-U3?zjCFk)Wdy~b2{7vS&^gO=H z&f^<9kMI5G@m+BqA96Waoa0(w7_5`=Nj^QFK0Tl0)ALC_J)h*$^GQBEpXAf? zNj^QFK0Tl0)ALC_J)h*$^GQBEpXAf?Nj^QFK0Tl0)ALC_J)h*$^GQBE zpT+0llYDcvT$4}FC;9Yzl26Yk`Sg5}PtPa$^n8*}&nNlxe3DPkC;9Yzl26Yk`Sg5} zPtPa$^n8*}&nNlxe3DPkC;9Yzl26Yk`Sg5}@5=MAq~|jNSLn`9d}i zuG8~LK0Tl0)ALC_C!fh!^nB8FdOpcF&UE+wGO$j@C;9Yzl26Yk`Sg5}PtPa$^n8*} z&nNlxe3DPkC;9Yzl26Yk`Sg5}PtPa$^n8*}&nNlxe3DPkC;9Yzl26Yk`Sg5}PtPa$ z^n8*}&nNlxe3nnbCw_)XK0Tl0bMl$I&dF!er{|OU)bmNc9_=0@pPoG`Db^n8-f$!D@pJ)d-)lh5RJdOqnoJ)h*$^GQBEpX77$nT$oxCtauKlYDwU zD<`6Qp7Px3kVRPQm8&&g*pPA8v9pPoG>p|o=@^Q z`Ao**G`Db^n8-f$!D@pJ)d-)lh5RJdOqnoJ)h*$^GQBEpX77$nT$oxCtauKlYDwU8_vTg z`Sg5}PtPa$^n8*}&nNlxe3DPkC;9Yzl26Yk`Sg5}PtPa$^n8*}&nNlxe3DPkC;9Yz zl26Yk`Sg5}PtPa$^n8*}&nNlxe3DPkC;9YzHlBx1^6B{`pPoG>p|o=@`W`6Qp7 zPx9&cB%hv7^6B{`pPoG>p|o=@`W`6S;Zuo}+Kr^%=1lYDwU$*1R&e0n~~r{|M= zdOpdg=aYPTKFO!&v*|p1l26Yk`Sg5}PtPa$^n8*}&nNlxe3DPkC;9Yzl26Yk`Sg5} zPtPa$^n8*}&nNjV(egG>p| zo=@`W`6Qp7Px9&cB%hv7^6B{`pPoG>p|o=@`W`6Qp7Px9&cB;Tc4cFCvblYDwU z$*1R&e0n~~r{|M=dOq7G;S=94AfKL3@;UiTUgzX9>C^K`ed_rnpPoG>p|gHQGO z5&4{aCgXJSne^%Tq(1e0lF!L!vQIsqbe)sWAFj`e3I|7^Z4|9(sfQgldK0Tl0)ALC_J)h*$ z^GQBEpXAf?Nj^QFK0Tl0)ALEbF)h2~)ALC_J)h*$^GQC7PxxC7@V^LWEb^ho zfn}b#e`X#r3KsUO#*chN$+qlo!8hsEx9Fknf>)IEA6!F!pOg=S|7G~=8rYNlzbWWZ zf3*ay`_kVNnG`1VZjJGp-G9GaW*8u#ru=_G!&8o}V zmbEkMo~(zmp2#|x^-9*+Y>{1%JuACCyDxh*dtLS|+4p4c%YG#Lsq7cC-^_941am5L z=H~S04CjpJtk2n+vm@uuocnVg%6UBJ*__ikZ%xUXGHpuDlsQudrd&Q{&6Mk>?3{Af zl)Y0PnR0x}sVQ&diripsMQ(F$U+!q``rMmycjxZQJ(~MW?yGq$uP85;H;^})w=!>I z-uAqmdAsxO&pVKJB=1<>GkK@-Ud=n}X6{^fk-Oa8;GXO5cMrSA-Rs?3-8_B*!+p+^ZXz(8O$urjbQusyIdusd*n z;6UI=;8@_9z^TBifwMst%ncR=%YzNUxxxP6aBw`hKDafwBX~z}PjFxGaPaZqiQx0W z)4?}_=kl}igZYvCn*8?sp8Uc5rTJ^}H|O7+e_Q_D`Fr!@`A74Q=by}fDgR9V+XY5} zw;)_lSpL$^Gk*UX~J~Q>y)K{mToyMl+PAi&LKCNNe+-d#OhNq2BTR&~ns#n6_uy zzG;W2JwENkwCAUtp7zGHbD^wIFcb;ZgxW(rp~2A7(3;TZ(9NOSLU)JuhT@^4q2r;G zp_f8uLT`tSus0kISB9Izo#BD-Xn19KV|aUbXLxt`{_uhDk?^tbGvQO=SHowgv+23h zi>8-PZEqMaPv1Iy$Mid<@0q@D`r+x1Pd_pJ`RS*pzcKw>aaM7#I8t0w z++N&MJXpN6cun!<;+uSXAWPN07WJlzV z$ezf)$l=K2krR>UBc~&8M9!6Fl?F>Ar8TAPr9GvCrAtfKlx{A)x%9TudrJ3}K2mzD z^x4vvN?$KMSC&&&R8~>eT-H@KSaxOE`m$|hJIn4W+go<1?D4Xv%TATOR`zz(6%9tq zq7BiG=s@)H=$h#D(H+q{qxVN2iXM$V6@5PXO7v{GDEF2Zm)DffDeo^IDPLK>sr=^h z+spTq?=L@6{$%;d@|VlsD1UcG?u=OnGq%szHRJ9X56(C|hVn?#giGtjhMvzRKas<&_&N zZ>qel@}A0lm5)>&t9-WdrOMYU&sF7A6;)MKHCJ_24OU%QwZ3Xw)y}HBs`geLs(QTY z>8evzuT{N0%QY)Ft87-otd3a&vo4>tX4dtycFekS*8Q^{nss#6Q?s6*^~$WX)uP&4 zU0hvLJ*T?AdZc<~^``2Zt8cH~Q@y|XNcEG|C#zqsexv%`n%tUcHI+53H9a*$HRCnc z)NHTWRdaXEgEfb1o~U`I=Ea&bHSg4B)fUv2*T!l)YZuoptzB2Uwf5H9-L((Y#%mv| zJyH8Y?W?tK)fsjEx=3AJ-Q2o`b)$8w>o(WjQg=t)eRT)w9<4iG_gvlSx;N`ty}LeK zKdZjIzOR0`etG@I`kU%+tG}myU;QKX$LgQ0f2scU`g09A4MhzV4b2T*4TB9=Hmq;h z*08hTu7kCmRuh{O>yM4ZR>n5PZjRj^+Y{R#I}&>`b~5&I?2XvFO}S0enkt)G zn|hjtn#P;1Y1-bjtLg5h2b&HzJ<;?`(~C`Kn%-&7YA$FlZ;my0HZN{o+PtoLYxAwm zyPF?qjyFHne4_b<=2x5FYB5^;Es>VGmbonpTSi+}w`^{?rR9#6`&tgPJlb-+<++yA zEpN86R(ET-byjP8YhUYd>+;r(tv9va)_PCtzSc)tkF`GA`cmubt>@Zu+KSpL+M3(C z+6LRMY+K*9t!-!9U2S{Y4z)er_H^5+w%6L;Zg;f@+soP;+B@0@+AnWk(|&#Xj`lm- z?{9yo{b>7B?a#Nr(tdWfnC+ciJi7*8Ez2LWe&*tP;Qvo3;NM>?W}kvv`Yv}a{DDq< zm7T>_*c4SD_!L|*4 z@n0tnfbBZauL5blhw|P`b{DLx<3Hd6S33v(*0KlwCZvz~*>qOS%DCW?8$1L4_<1JJ z;@LchPvN;dkGr{td%2I5b3YI8Agkp0ynq+78eYVw@@YH-s~&^@OHnZ|;SpZS+Ibm| zvI}@QpTR5mOkT;W_$>Ipgjeut_ ztc^FZ+3FXH`t0bj@$ z@d5rmzL;Ok2l*v@h%e#8{8B!`FXN+pj9<>*56`1l@GJQ;KF%lja=wDE~+xQ3hcK#uL6aO&3nSX@e!avG) z@Q?9Z`N#QA{t3Q|f0EzEKgDn7pXPV)&+t3>XZddaId}s8JinWNf#1XL<$L%S`F;FL z{C@sr{s8|9-^;(sALL)-`}o)Se*O)9fPa%e#J|Pk{2)KXALfVoxA`OdJNyX$E`OAN zk00gV=a2Cp@W=TN*}eP;{v&>j{|kST|Ck@=|H_}@|He=7pYW&ozw>AKPx-U_Kln-h zGyWX^Ie(u2g1^9j$xreB) z6?wufJi;q{!Y=|MDDp*tC=^9vs+cB1@M>VXC>A9mB1%P>h>CJCLsW>FqEb|eS)y9h zh+0u6>P3TS6fw~xnnjCf6>Xwj%ocOR1!At4CpyH1qEpNlU7}m`h+ferE)xA>fmkRO zi2?CG_Ia^bTr38~C1OY{5yRqAF(NJ#qhd^4F5WMeiYvsGVwo5h6JohoAy$f2VzszR ztPyL)I`ILqUR*7%5gWutajn=St`nQZ7ID3}L2MN_if!V9V!QZ|xJi6i+$=sKZV?|9 zJH*Gtt>WWir}%`}B|a%`6Q2^di%*L?#An2v;|*;!onN__KIR{6)Mi{)a`yJL0e6ocNn~SG)&*B%Q-wUzgI58PX*)WtPmAIdY23 zm3h)FJ<=} z%0+TOzE3We7t29;i5!wkya8s2r1*%lFHr@(OvSTqeimgj_CH$dz)HTrIDX zYvfwFPJTeHmsiVcUMn}r>*Qv+MP4s&kXz-Aa-001+%7*PZ;~IDH_MO6TjWRO z4*4;8tNgg!DL)~1$xq7LgHIqP$Oj zN!~BNEFX|xk$dG=<%9BTa-aOV+%LZ&56ExIhvc_pTppB%gQ1Npf8p?pIANFI~_BA=8$mdEA4%BSSN$rJJ?@@e_+@)`M4`K&=zshs+Z}MIFp1}+buj{2@7#W7k$TYHyY$L~*V&ocm zhTHHMUc+bjjerp}@{Izc&?qvd8qWq4$!DuvMMw8KOv>2^Mo6&B}Hs%-?7;}wzMu%~s(P_*#x{Pk4$LKZsjEjtZ zV*$L&u*euN-e)W}E;a^@ON=37i7{+kYK$0{8KcISak=q+W2te4aiy`$7&j)2<;DtQ zrLoFbZCqunG1eOEj1L&=jjN4oj19&{<62{rahQj%v-S)~sqPRO=OW*m{L&X;M12T2Ys1)grI0TLZtF zGPcZbYii2AcA5Dz#nS2;))g&sHC%BO{H}rD_3*n9 zemBAIb@00xes6@|ZFMo*TJp}7%$V>Z3XrZ?O4R-4{t)7x$Oe4E~7)4OeYk4^7Q>Mad6-4;)aEuI!zJT10( zT5R#O%(nG2-?Ig0m>^}@wNt3B^5{$RExR`0u+%j=ZP*JExC~oEq?QhmS~^5(=@6-< zL!_1tnX`1roTWqNEFCgu>5w@~hs-5(%$%j$;=#;Wc3V7{Im>Q~2Qz2cA#+N%m`V7M zKwp{c zmR3)Tza=gHmbCa=(&BHKoz~y{fR+Cw!}d0~8u^+w%dRcdTI(A1c4S(e1}(PJ0QJP~ zLp`zkP*3bW)Dyc8^~7m_dg3%dJ#iYKo;VFqPn-s*Cr$&oh=n;xs^=PJ@-svJBWorXd2G&pZuMEaqvDVuQ?4rt$j3nT>jr&E9O&TWxxq zO>ej9^KE*UP4BkpJvO~JsYB+hcp!60-4+jIE@`*L1DQ+OXWMl9K2j|bK9xDT##TD1 zt!va#YTbioWA5aNHET@@vEH1qrLU_YahF0%f-}?;oS~lJ4D|$Os3$l>J;5333C=*b z*g`$Q7U~JsQBSaix-ABaEwmF`v(wm`ZDY&kGucY9m~5rkth>8n%euQOx>9P*DYe#= zT3br3J*753rPh^F>rSclq||!twU&mIS}dj3lu}EHvn3_YmXtVKW~W>Sar$h0VV0AA zWDv5K=7$zBjjc(`Ve&~yW!idfQkhPvHK){CQ)+D~wf2bYk`h@uO?HQ=(`T|-e}92zR=U6R!Tz)Bi9Lt*#GXSv zvFA`v>^amE`yTbgzDGT=?@>?ed(;#A9`(e&2i>~Apl*xDIs?#di^n!^m^iIoQ8Pr!(}c}r6|L@mPeS-4>5UDB2T*qE3Xi+Yd&tw%Z7``AkAnEGD5Twq@6v`?Xn66^zYcyzUNcyP6Jcx<(FcxbhBc(k^3cwn`3 zc&xT`c&N5?c%-&;c%V+`_&9Cp@Gx!Zws>GqChfL(@FCi24-dtb4)NIDHh>w{(aS+~ z(rF&9TO4_7)#MF!4!-r7cr>weAl}kp2U_E#7JJ8Z$2U?JJ7P*;=vtg*5w@~R}Xb&KkA{*)YU_ssjG)NQ&$glq|-y4sjG)NQ*V-+ zC*Ure7=s?>OrI?ddYCiq>R}FaTgK>m%9zqEA3e<3TPGgoQf+Wo#XZ?b8MA6;KQ>=8 zoNm-|wWVEUX=|)2vCaIHT9?&oeTuy&rPgb;+GuH;EbTh@Pyn+vI~h?6WJ1|`%(%AE zIceP>jxdYL z1|?-Ny#-}aV}&d_t;?>porAa{F<3j>c;;PZc3LaQsr3^3+GUVeTMcq+t3iI#u7w<@ zSs>50D;(1KBm}kX2KI zY=Q@}2qk0>N^_UDc2Nt=lBwZdU$qRf0wrVvtwbVxNEmB}kMSB}vtx4AHCLHgGnq=e zHz(eHK`%tFo_rKXriS~I=7{@~G;E9eGg-s^Nj2P`NjvV(WDWNx)o_23hWnE=+@GWY z8_BEG{v)~t*3z#{^kLd? z>DQ{IU#pgWEiS!SyUujuny*!BPF>)dlZI=)R;~G3wdN3RYY$i;3)DibxoWG<_qA%x z*W#MzSwUbh*qR9Nq}X9!rPbjnEX`tlTxL?@c!p~+xiL90pZ63gmJX~~I!xZ`!{jBU z4*9U!0y8N-$W2NeGGw&{ax5L#v2>uv(jjy9>oI>RbzsP93lv#8WE9?ua9~o`n0TNy z(>uv04DH&Nj5z0whooEE+e~^&9g=Rf zg`}tWAn7S}NV?S)l5XjcbW4Y%TRJ4&emy2Vr4C8A+CtJT9g<$)NI5P<@&iya?UN+3 zbrO5qWG8rCQ}zaH8*i|-G1+h%Z&2HKgWAR$)Co>LJi$rB-Mj&JGk$fWrFph##;v?T zZRHJWD{oN8_Xf3<)rD#+lZMCl2DOzpsI6=TqP8-$F}HGx9Vkhw!&XkSz_9?=(z~7b z5MkEL9NT;_ReaQemgeIaXn5R{4UhX|KX}|HYcPGY6(09VJ0ADR8f3$4g~vVFfgV%C z2^Df_Zx6||S0S5;hLBFX2l8pJLP8S_A)^TuQkqa9 zrwJ93YP%G(YOg|C6AdA+2^A8nc_+3mW?f0CEpWbti@DXCV_Gw=rL_gOSX+>6xCOVU zEx1K(!7XYFk`K2aX}ATq;1|z_dE-z%+|>@|oz=*}uC1{M04Bn_;RJeddy!+9kOV(#hYp3I=q?2 zbT?Bg7)ozlOn>}0>dmo9PW$%ktrk#QEuhGTKy6h(ZB;;RRX~vsfg%lo+Nz$=s1AuU zJqXoS1=Uss)m8=7Rt1%6tDqtcq1vjT+Nz+kVo^{*8xyJ&J3y6I2dL64<}K4i%6vXn zhf(7FHF|P++)ca?l5YF`zvY3-PM?}rLia0qWZ-hMeT4uk%qg8G~7$pwQ46NYq*c7 z749O^a1S*)5=Wn@n~Bpuu(s$@*Q!rlGrSvw?-?e@N_h{`=7Z$HyGqy+l4oj=JX3?@ z+1f(#Y&A%p*$R?pvqSQ1HDJwb1xYkDNTR7h5={+~n7j&;XsbaI%~p^^Q-dVJ7c0}= z6;JNi|bI0F%5B^P+?mrLMi&+trDD&A$_`loy5&pdilqi!r zV{uj!iL*;bd&eS?MMs$Xl0|XXlFLWq&7pYZ*!b$mu1iPbV#dUGv*4fbtymFV9x5%3 zv#~hqj`kda7`n&1YU8{n9vNR<8y7Xv(r9UIT-HQZJ}iUztgAa7=#Gq!cO4YL?yiF~ zWOrP24{wdcbEBYiPppg^gIf>5Kk9*Q;-#y?rD#3m&gWg>2&mDnLjfKDUo_4JM^}v< zD&W|%Hg43!WpzBidlbiu7j$=<4MLHXk@(|-aiik$LzO(QyLUxz+|@f;8kaN1hTcC4 zjYGReBk{pOFm;ZFBJpd9SLXfR^Zug2f8ybzXMcB9$>Y+vPV$_NkO&syE71-iP23tNhwsK-D z&a219%o&VDR>FLuU1PQJjG9PqByP-@fH`G$501t&qh0Z=Xcw#iTryr8cd2wkCL${j zW-jlFpcAJXqVTbVcD*a&8M8{k*&W#x*#%=AY{-}a8MNsBk1H?0D)yA`HA{eb3re(0@ z&;;V`93R6b<2@?coSH+~Zr0t~HLKJDXG%>fNV(JtuZ9r1Vd~?N-d)iNTvU~87QzLI zM?w%}B1%}==tK_^dbWDJ#pJ)rEtnuE;Wed*{yzaN%xqATvJ#w`bE zbsh9z;e{g33t%nejKR@^xDO#+UAtg~VOa01(kNV((18c^<0Y_@(K`k+xd`IF2&}1# zHOUelWGoPcBzMPI$05$S;vv6=9b}?+cr@;hc13#QZon@O1|IKe0<_yFsnNL$?8xU;9UT7%&)GE7uFo)D2pK9D5usOlqjdw95hgdY7SomW02!}CW*cha_0{ou*pH2{6)pl|wHq=z z(H_Nv{k$yz$J%%-vBU@6u3jWA-Z<-E2G8#ug+mcnvB{R6$=Wp6L|RnjEzleF)9XAY z;PlZ(i*{CUSl#A&I->0dn|VIYwH5LK6VP5W4x447y*A!fQ&%{*Hs1dCngZA>p!sZA zB33XXQWv=hj|52Vf?c~Vie3aq&?wya;M9dXQak7QLCEkNIJ*ksh0x4^<8+2L8jRN=5Qx+g-QkB`Cmkbcbsga+q-v~Qw4 z6onD`V4fhaAtFo{I^q8H(yxQZPKpPCG1I?jei>mqaEhK|t-GD4@;Qp8;|ptb;9 z*0T#OqLaMwsJU8j^4G>MOpKU@Q06D#0j-OsjyOkQr`5sEqY3rZ9L#}x4brd3|KA~fkq)r`ABr*Gc#h4D zwuee>yp)caab5%m@SN&IR{EiKL3Jsv8Ro^Djg^80kb->L_V9E9J3LSqZ-rgHa1#3> z=#B>i@iu5PP!pdG;(eIKUPyPO4{pMVY%Q)qpyTg@G+$hEh_OCU20`JdTvBt0E6Wfl z%CZF8Tm-IRY=g?B*anpmY=g>WH4npS*bUk!XmD=_ZLH>DPPWTIBisA29Y^m{Y^Ss< zu$|Jb#CA$shGX_ZvvC{~l?faZmE|}lDl4!}KPW4)4Jxa!4Jxa#4JucuxOzZaqvAqs zt%?h^bt*2@KA_@4ZM}*MwX0QJs9mGtLTv-&c&@d88&xgd365(?oe%0JOgc7;cR}qs zxYvS;Z8npNZMLX3=)gAD!^QKgUT;t}b;VXv@roNs#pc_fpLx{f2T8>S+eyU+AA+_W zR)06Cn(FVv|F4I$*Fg{l0&pu>Q4$s>gCmIs^%caBx~p-}S==2R@drhXJ_|n!GCBHI zde@6~`tJJmrY6^0N{D($i24pr4yoRr5QPIF3P*6gf4xXWs@{nZ^)n&rF`V?M-h~i_ zD>kZBj=!eEqOa%ra>OqE6m gf5VL8)m)jdfh%tcZn*{jM2UWf&Mfn4AefZw3u;t|hyVZp literal 0 HcmV?d00001 diff --git a/tunictracker/werefoxtheme/static_src/src/fonts/Trunic-Regular.otf b/tunictracker/werefoxtheme/static_src/src/fonts/Trunic-Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..da4a6e89b8f329efc870fa2a1b4843805af9ee31 GIT binary patch literal 61176 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 literal 0 HcmV?d00001 diff --git a/tunictracker/werefoxtheme/static_src/src/styles.css b/tunictracker/werefoxtheme/static_src/src/styles.css index 391d723..71bfc11 100644 --- a/tunictracker/werefoxtheme/static_src/src/styles.css +++ b/tunictracker/werefoxtheme/static_src/src/styles.css @@ -3,6 +3,10 @@ @tailwind utilities; @font-face { - font-family: "DejaVuSansMono"; - src: url("/static/fonts/DejaVuSansMono.ttf"); -} \ No newline at end of file + font-family: "DejaVuSansMono"; + src: url("/static/fonts/DejaVuSansMono.ttf"); +} +@font-face { + font-family: "Trunic-Regular"; + src: url("/static/fonts/Trunic-Regular.otf"); +} diff --git a/tunictracker/werefoxtheme/static_src/tailwind.config.js b/tunictracker/werefoxtheme/static_src/tailwind.config.js index 7732a61..310824d 100644 --- a/tunictracker/werefoxtheme/static_src/tailwind.config.js +++ b/tunictracker/werefoxtheme/static_src/tailwind.config.js @@ -52,6 +52,7 @@ module.exports = { extend: { fontFamily: { nerd: ["DejaVuSansMono"], + trunic: ["Trunic-Regular"], }, colors: { bluelight: { @@ -118,6 +119,109 @@ module.exports = { DEFAULT: "fffae0", }, }, + keyframes: { + shake: { + "2%": { transform: "translate(-0.5px, 2.5px) " }, + "2%": { transform: "rotate(-0.5deg)" }, + "4%": { transform: "translate(0.5px, -1.5px) " }, + "4%": { transform: "rotate(-0.5deg)" }, + "6%": { transform: "translate(1.5px, -1.5px) " }, + "6%": { transform: "rotate(1.5deg)" }, + "8%": { transform: "translate(0.5px, 2.5px) " }, + "8%": { transform: "rotate(-0.5deg)" }, + "10%": { transform: "translate(-1.5px, 2.5px) " }, + "10%": { transform: "rotate(1.5deg)" }, + "12%": { transform: "translate(2.5px, -0.5px) " }, + "12%": { transform: "rotate(-0.5deg)" }, + "14%": { transform: "translate(2.5px, 2.5px) " }, + "14%": { transform: "rotate(1.5deg)" }, + "16%": { transform: "translate(1.5px, 1.5px) " }, + "16%": { transform: "rotate(0.5deg)" }, + "18%": { transform: "translate(0.5px, 2.5px) " }, + "18%": { transform: "rotate(1.5deg)" }, + "20%": { transform: "translate(1.5px, -1.5px) " }, + "20%": { transform: "rotate(-0.5deg)" }, + "22%": { transform: "translate(0.5px, 1.5px) " }, + "22%": { transform: "rotate(1.5deg)" }, + "24%": { transform: "translate(-0.5px, 1.5px) " }, + "24%": { transform: "rotate(0.5deg)" }, + "26%": { transform: "translate(-1.5px, -1.5px) " }, + "26%": { transform: "rotate(0.5deg)" }, + "28%": { transform: "translate(1.5px, 2.5px) " }, + "28%": { transform: "rotate(1.5deg)" }, + "30%": { transform: "translate(2.5px, -1.5px) " }, + "30%": { transform: "rotate(1.5deg)" }, + "32%": { transform: "translate(1.5px, 1.5px) " }, + "32%": { transform: "rotate(1.5deg)" }, + "34%": { transform: "translate(2.5px, 0.5px) " }, + "34%": { transform: "rotate(-0.5deg)" }, + "36%": { transform: "translate(2.5px, -0.5px) " }, + "36%": { transform: "rotate(-0.5deg)" }, + "38%": { transform: "translate(0.5px, 0.5px) " }, + "38%": { transform: "rotate(0.5deg)" }, + "40%": { transform: "translate(1.5px, -1.5px) " }, + "40%": { transform: "rotate(1.5deg)" }, + "42%": { transform: "translate(0.5px, -0.5px) " }, + "42%": { transform: "rotate(0.5deg)" }, + "44%": { transform: "translate(-0.5px, -0.5px) " }, + "44%": { transform: "rotate(-0.5deg)" }, + "46%": { transform: "translate(1.5px, 2.5px) " }, + "46%": { transform: "rotate(-0.5deg)" }, + "48%": { transform: "translate(2.5px, 2.5px) " }, + "48%": { transform: "rotate(1.5deg)" }, + "50%": { transform: "translate(0.5px, 2.5px) " }, + "50%": { transform: "rotate(1.5deg)" }, + "52%": { transform: "translate(1.5px, -1.5px) " }, + "52%": { transform: "rotate(1.5deg)" }, + "54%": { transform: "translate(0.5px, 0.5px) " }, + "54%": { transform: "rotate(1.5deg)" }, + "56%": { transform: "translate(1.5px, 1.5px) " }, + "56%": { transform: "rotate(0.5deg)" }, + "58%": { transform: "translate(0.5px, 0.5px) " }, + "58%": { transform: "rotate(1.5deg)" }, + "60%": { transform: "translate(-0.5px, 0.5px) " }, + "60%": { transform: "rotate(-0.5deg)" }, + "62%": { transform: "translate(2.5px, -1.5px) " }, + "62%": { transform: "rotate(1.5deg)" }, + "64%": { transform: "translate(2.5px, -0.5px) " }, + "64%": { transform: "rotate(0.5deg)" }, + "66%": { transform: "translate(0.5px, 0.5px) " }, + "66%": { transform: "rotate(1.5deg)" }, + "68%": { transform: "translate(-1.5px, -0.5px) " }, + "68%": { transform: "rotate(0.5deg)" }, + "70%": { transform: "translate(2.5px, 0.5px) " }, + "70%": { transform: "rotate(-0.5deg)" }, + "72%": { transform: "translate(1.5px, 0.5px) " }, + "72%": { transform: "rotate(1.5deg)" }, + "74%": { transform: "translate(0.5px, 0.5px) " }, + "74%": { transform: "rotate(1.5deg)" }, + "76%": { transform: "translate(-1.5px, -0.5px) " }, + "76%": { transform: "rotate(0.5deg)" }, + "78%": { transform: "translate(-1.5px, -1.5px) " }, + "78%": { transform: "rotate(-0.5deg)" }, + "80%": { transform: "translate(-0.5px, -0.5px) " }, + "80%": { transform: "rotate(0.5deg)" }, + "82%": { transform: "translate(-0.5px, 1.5px) " }, + "82%": { transform: "rotate(1.5deg)" }, + "84%": { transform: "translate(-1.5px, 2.5px) " }, + "84%": { transform: "rotate(-0.5deg)" }, + "86%": { transform: "translate(-1.5px, -0.5px) " }, + "86%": { transform: "rotate(-0.5deg)" }, + "88%": { transform: "translate(2.5px, -0.5px) " }, + "88%": { transform: "rotate(-0.5deg)" }, + "90%": { transform: "translate(2.5px, -1.5px) " }, + "90%": { transform: "rotate(-0.5deg)" }, + "92%": { transform: "translate(2.5px, 0.5px) " }, + "92%": { transform: "rotate(-0.5deg)" }, + "94%": { transform: "translate(0.5px, 1.5px) " }, + "94%": { transform: "rotate(-0.5deg)" }, + "96%": { transform: "translate(1.5px, 2.5px) " }, + "96%": { transform: "rotate(0.5deg)" }, + "98%": { transform: "translate(2.5px, -0.5px) " }, + "98%": { transform: "rotate(1.5deg)" }, + "0%, 100%": { transform: "translate(0, 0) rotate(0)" }, + }, + }, }, }, plugins: [