From b8755b847c2a090e63cfc12370a9f90697080063 Mon Sep 17 00:00:00 2001 From: Wynd Date: Tue, 3 Mar 2026 22:51:42 +0200 Subject: [PATCH] Finishers page for BBS --- input/bbs/finish-commands.json | 225 -------------------------- input/bbs/finishers.toml | 250 +++++++++++++++++++++++++++++ public/scripts/bbs/finishers.js | 43 +++++ public/styles/bbs/common.css | 19 +++ public/styles/bbs/finishers.css | 31 ++++ public/styles/bbs/melding.css | 34 ---- public/styles/common/base.css | 16 ++ src/bbs.rs | 65 +++++++- src/bbs/finisher.rs | 4 +- templates/pages/bbs/finishers.html | 44 +++++ templates/pages/index.html | 1 + 11 files changed, 468 insertions(+), 264 deletions(-) delete mode 100644 input/bbs/finish-commands.json create mode 100644 input/bbs/finishers.toml create mode 100644 public/scripts/bbs/finishers.js create mode 100644 public/styles/bbs/common.css create mode 100644 public/styles/bbs/finishers.css create mode 100644 templates/pages/bbs/finishers.html diff --git a/input/bbs/finish-commands.json b/input/bbs/finish-commands.json deleted file mode 100644 index 137b01b..0000000 --- a/input/bbs/finish-commands.json +++ /dev/null @@ -1,225 +0,0 @@ -{ - "finish": { - "name": "Finish", - "char": ["A", "V", "T"], - "level": 1, - "goal": "Unlocked from the start", - "color": "blue" - }, - "heat-slash-1": { - "name": "Heat Slash 1", - "char": ["A", "V", "T"], - "level": 2, - "follows": ["finish"], - "goal": "Activate the Firestorm Command Style 8 times", - "color": "red" - }, - "heat-slash-2": { - "name": "Heat Slash 2", - "char": ["A"], - "level": 3, - "follows": ["heat-slash-1"], - "goal": "Activate the Firestorm Command Style 12 times", - "color": "red" - }, - "rising-rock-1": { - "name": "Rising Rock 1", - "char": ["T"], - "level": 2, - "follows": ["finish"], - "goal": "Earn 2000 CP", - "color": "brown" - }, - "rising-rock-2": { - "name": "Rising Rock 2", - "char": ["T"], - "level": 3, - "follows": ["rising-rock-1"], - "goal": "Earn 4200 CP", - "color": "brown" - }, - "dark-star-1": { - "name": "Dark Star 1", - "char": ["T"], - "level": 4, - "follows": ["rising-rock-2"], - "goal": "Defeat 420 enemies", - "color": "brown" - }, - "dark-star-2": { - "name": "Dark Star 2", - "char": ["T"], - "level": 5, - "follows": ["dark-star-1"], - "goal": "Defeat 550 enemies", - "color": "brown" - }, - "air-flair-1": { - "name": "Air Flair 1", - "char": ["V"], - "level": 2, - "follows": ["finish"], - "goal": "Earn 2000 CP", - "color": "blue" - }, - "air-flair-2": { - "name": "Air Flair 2", - "char": ["V"], - "level": 3, - "follows": ["air-flair-1"], - "goal": "Earn 4000 CP", - "color": "blue" - }, - "air-flair-3": { - "name": "Air Flair 3", - "char": ["V"], - "level": 4, - "follows": ["air-flair-2"], - "goal": "Take 4500 steps", - "color": "blue" - }, - "air-flair-4": { - "name": "Air Flair 4", - "char": ["V"], - "level": 5, - "follows": ["air-flair-3"], - "goal": "Take 7000 steps", - "color": "blue" - }, - "magic-pulse-1": { - "name": "Magic Pulse 1", - "char": ["A"], - "level": 2, - "follows": ["finish"], - "goal": "Earn 2000 CP", - "color": "purple" - }, - "magic-pulse-2": { - "name": "Magic Pulse 2", - "char": ["A"], - "level": 3, - "follows": ["magic-pulse-1"], - "goal": "Earn 3800 CP", - "color": "purple" - }, - "magic-pulse-3": { - "name": "Magic Pulse 3", - "char": ["A"], - "level": 4, - "follows": ["magic-pulse-2"], - "goal": "Defeat 350 enemies", - "color": "purple" - }, - "magic-pulse-4": { - "name": "Magic Pulse 4", - "char": ["A"], - "level": 5, - "follows": ["magic-pulse-3"], - "goal": "Defeat 500 enemies", - "color": "purple" - }, - "gold-rush": { - "name": "Gold Rush", - "char": ["A", "V", "T"], - "level": 2, - "follows": ["finish"], - "goal": "Collect 1000 munny", - "color": "yellow" - }, - "ramuhs-judgement": { - "name": "Ramuh's Judgement", - "char": ["A", "V", "T"], - "level": 3, - "follows": ["air-flair-1", "magic-pulse-1", "rising-rock-1"], - "goal": "Activate the Thunderbolt Command Style 12 times", - "color": "green" - }, - "twisted-hours": { - "name": "Twisted Hours", - "char": ["A", "V", "T"], - "level": 3, - "follows": ["air-flair-1", "magic-pulse-1", "rising-rock-1", "gold-rush"], - "goal": "Take 7000 steps", - "color": "gray" - }, - "surprise-1": { - "name": "Surprise! 1", - "char": ["A", "V", "T"], - "level": 3, - "follows": ["gold-rush"], - "goal": "Collect 1400 munny", - "color": "yellow" - }, - "surprise-2": { - "name": "Surprise! 2", - "char": ["A", "V", "T"], - "level": 4, - "follows": ["twisted-hours", "surprise-1"], - "goal": "Collect 5200 munny", - "color": "yellow" - }, - "heal-strike": { - "name": "Heal Strike", - "char": ["A", "V", "T"], - "level": 4, - "follows": ["rising-rock-2", "air-flair-2", "magic-pulse-2"], - "goal": "Use the effect of Second Chance or Once More to survive lethal damage 5 times", - "color": "green" - }, - "random-end": { - "name": "Random End", - "char": ["T"], - "level": 4, - "follows": ["twisted-hours"], - "goal": "Take 8000 steps", - "color": "gray" - }, - "explosion": { - "name": "Explosion", - "char": ["A", "V", "T"], - "level": 5, - "follows": ["dark-star-1", "air-flair-3", "magic-pulse-3"], - "goal": "Earn 6400 CP", - "color": "orange" - }, - "celebration": { - "name": "Celebration", - "char": ["V"], - "level": 5, - "follows": ["surprise-2"], - "goal": "Collect 7000 munny", - "color": "yellow" - }, - "ice-burst": { - "name": "Ice Burst", - "char": ["A"], - "level": 5, - "follows": ["magic-pulse-3"], - "goal": "Activate the Diamond Dust Command Style 15 times", - "color": "cyan" - }, - "demolition": { - "name": "Demolition", - "char": ["T"], - "level": 6, - "follows": ["dark-star-2"], - "goal": "Earn 10000 CP", - "color": "dark-blue" - }, - "stratosphere": { - "name": "Stratosphere", - "char": ["V"], - "level": 6, - "follows": ["air-flair-4"], - "goal": "Defeat 800 enemies", - "color": "white" - }, - "teleport-spike": { - "name": "Teleport Spike", - "char": ["A"], - "level": 6, - "follows": ["magic-pulse-4"], - "goal": "Defeat 800 enemies", - "color": "pink" - } -} diff --git a/input/bbs/finishers.toml b/input/bbs/finishers.toml new file mode 100644 index 0000000..ce8bd73 --- /dev/null +++ b/input/bbs/finishers.toml @@ -0,0 +1,250 @@ +[finish] +name = "Finish" +char = ["A", "V", "T"] +level = 1 +row = 3 +goal = "Unlocked from the start" +color = "#0088f8" + +[heat-slash-1] +name = "Heat Slash 1" +char = ["A", "V", "T"] +level = 2 +row = 1 +next = ["finish"] +goal = "Activate the Firestorm Command Style 8 times" +color = "#d00000" + +[heat-slash-2] +name = "Heat Slash 2" +char = ["A"] +level = 3 +row = 1 +next = ["heat-slash-1"] +goal = "Activate the Firestorm Command Style 12 times" +color = "#d00000" + +[rising-rock-1] +name = "Rising Rock 1" +char = ["T"] +level = 2 +row = 3 +next = ["finish"] +goal = "Earn 2000 CP" +color = "#683000" + +[rising-rock-2] +name = "Rising Rock 2" +char = ["T"] +level = 3 +row = 3 +next = ["rising-rock-1"] +goal = "Earn 4200 CP" +color = "#683000" + +[dark-star-1] +name = "Dark Star 1" +char = ["T"] +level = 4 +row = 2 +next = ["rising-rock-2"] +goal = "Defeat 420 enemies" +color = "#683000" + +[dark-star-2] +name = "Dark Star 2" +char = ["T"] +level = 5 +row = 1 +next = ["dark-star-1"] +goal = "Defeat 550 enemies" +color = "#683000" + +[air-flair-1] +name = "Air Flair 1" +char = ["V"] +level = 2 +row = 3 +next = ["finish"] +goal = "Earn 2000 CP" +color = "#0088f8" + +[air-flair-2] +name = "Air Flair 2" +char = ["V"] +level = 3 +row = 3 +next = ["air-flair-1"] +goal = "Earn 4000 CP" +color = "#0088f8" + +[air-flair-3] +name = "Air Flair 3" +char = ["V"] +level = 4 +row = 2 +next = ["air-flair-2"] +goal = "Take 4500 steps" +color = "#0088f8" + +[air-flair-4] +name = "Air Flair 4" +char = ["V"] +level = 5 +row = 1 +next = ["air-flair-3"] +goal = "Take 7000 steps" +color = "#0088f8" + +[magic-pulse-1] +name = "Magic Pulse 1" +char = ["A"] +level = 2 +row = 3 +next = ["finish"] +goal = "Earn 2000 CP" +color = "#7000d0" + +[magic-pulse-2] +name = "Magic Pulse 2" +char = ["A"] +level = 3 +row = 3 +next = ["magic-pulse-1"] +goal = "Earn 3800 CP" +color = "#7000d0" + +[magic-pulse-3] +name = "Magic Pulse 3" +char = ["A"] +level = 4 +row = 2 +next = ["magic-pulse-2"] +goal = "Defeat 350 enemies" +color = "#7000d0" + +[magic-pulse-4] +name = "Magic Pulse 4" +char = ["A"] +level = 5 +row = 1 +next = ["magic-pulse-3"] +goal = "Defeat 500 enemies" +color = "#7000d0" + +[gold-rush] +name = "Gold Rush" +char = ["A", "V", "T"] +level = 2 +row = 5 +next = ["finish"] +goal = "Collect 1000 munny" +color = "#e8d800" + +[ramuhs-judgement] +name = "Ramuh's Judgement" +char = ["A", "V", "T"] +level = 3 +row = 2 +next = ["air-flair-1", "magic-pulse-1", "rising-rock-1"] +goal = "Activate the Thunderbolt Command Style 12 times" +color = "green" + +[twisted-hours] +name = "Twisted Hours" +char = ["A", "V", "T"] +level = 3 +row = 4 +next = ["air-flair-1", "magic-pulse-1", "rising-rock-1", "gold-rush"] +goal = "Take 7000 steps" +color = "gray" + +[surprise-1] +name = "Surprise! 1" +char = ["A", "V", "T"] +level = 3 +row = 5 +next = ["gold-rush"] +goal = "Collect 1400 munny" +color = "#e8d800" + +[surprise-2] +name = "Surprise! 2" +char = ["A", "V", "T"] +level = 4 +row = 4 +next = ["twisted-hours", "surprise-1"] +goal = "Collect 5200 munny" +color = "#e8d800" + +[heal-strike] +name = "Heal Strike" +char = ["A", "V", "T"] +level = 4 +row = 3 +next = ["rising-rock-2", "air-flair-2", "magic-pulse-2"] +goal = "Use the effect of Second Chance or Once More to survive lethal damage 5 times" +color = "green" + +[random-end] +name = "Random End" +char = ["T"] +level = 4 +row = 4 +next = ["twisted-hours"] +goal = "Take 8000 steps" +color = "gray" + +[explosion] +name = "Explosion" +char = ["A", "V", "T"] +level = 5 +row = 2 +next = ["dark-star-1", "air-flair-3", "magic-pulse-3"] +goal = "Earn 6400 CP" +color = "orange" + +[celebration] +name = "Celebration" +char = ["V"] +level = 5 +row = 4 +next = ["surprise-2"] +goal = "Collect 7000 munny" +color = "#e8d800" + +[ice-burst] +name = "Ice Burst" +char = ["A"] +level = 5 +row = 3 +next = ["magic-pulse-3"] +goal = "Activate the Diamond Dust Command Style 15 times" +color = "#50b8e8" + +[demolition] +name = "Demolition" +char = ["T"] +level = 6 +row = 1 +next = ["dark-star-2"] +goal = "Earn 10000 CP" +color = "#183050" + +[stratosphere] +name = "Stratosphere" +char = ["V"] +level = 6 +row = 1 +next = ["air-flair-4"] +goal = "Defeat 800 enemies" +color = "#e8e8d0" + +[teleport-spike] +name = "Teleport Spike" +char = ["A"] +level = 6 +row = 1 +next = ["magic-pulse-4"] +goal = "Defeat 800 enemies" +color = "#b830a0" diff --git a/public/scripts/bbs/finishers.js b/public/scripts/bbs/finishers.js new file mode 100644 index 0000000..a0b7c87 --- /dev/null +++ b/public/scripts/bbs/finishers.js @@ -0,0 +1,43 @@ +document.addEventListener("DOMContentLoaded", (event) => { + document.querySelector(".filters .row div").remove(); + + const charFilters = document.querySelectorAll( + 'input[type="radio"][name="charFilter"]', + ); + + let detailsField = document.getElementById("finisherDetails"); + + charFilters[0].checked = true; + charFilters.forEach(function (item, _) { + item.addEventListener("input", function () { + showSelectedFinishers(this); + detailsField.innerText = "---"; + }); + }); + + showSelectedFinishers(charFilters[0]); + + document.querySelectorAll(".finisher").forEach(function (item, _) { + item.addEventListener("click", function () { + const goal = item.dataset["goal"]; + detailsField.innerText = goal; + }); + }); +}); + +function showSelectedFinishers(elem) { + let name = ""; + if (elem.value === "A") { + name = "Aqua"; + } else if (elem.value === "V") { + name = "Ventus"; + } else if (elem.value === "T") { + name = "Terra"; + } + + document.querySelectorAll("tbody").forEach(function (list, _) { + list.style.display = "none"; + }); + + document.getElementById("finishers" + name).style.display = "contents"; +} diff --git a/public/styles/bbs/common.css b/public/styles/bbs/common.css new file mode 100644 index 0000000..3c1f084 --- /dev/null +++ b/public/styles/bbs/common.css @@ -0,0 +1,19 @@ +table { + .charlist { + display: inline-grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; + + .aqua { + color: #97c8ff; + } + + .ventus { + color: #26ff62; + } + + .terra { + color: #ff7400; + } + } +} diff --git a/public/styles/bbs/finishers.css b/public/styles/bbs/finishers.css new file mode 100644 index 0000000..facd882 --- /dev/null +++ b/public/styles/bbs/finishers.css @@ -0,0 +1,31 @@ +@import url("./common.css"); + +table { + table-layout: fixed; + + & tbody tr:hover { + background-color: inherit; + } + + & td, + & td:first-child { + border: 0px; + height: 33px; + text-shadow: 2px 2px #000; + + &.finisher { + cursor: pointer; + + &:hover { + background-color: var(--bg-light-color); + } + } + } +} + +#finisherDetails { + text-align: center; + font-size: 18px; + margin-top: 16px; + text-shadow: 2px 2px #000; +} diff --git a/public/styles/bbs/melding.css b/public/styles/bbs/melding.css index 8998494..aff1e39 100644 --- a/public/styles/bbs/melding.css +++ b/public/styles/bbs/melding.css @@ -10,38 +10,4 @@ table { border-right: var(--table-border); border-radius: 0; } - - .charlist { - display: inline-grid; - grid-template-columns: repeat(3, 1fr); - gap: 8px; - - .aqua { - color: #97c8ff; - } - - .ventus { - color: #26ff62; - } - - .terra { - color: #ff7400; - } - } -} - -.filters { - display: flex; - flex-direction: column; - - .row { - display: flex; - - div { - display: flex; - align-items: center; - gap: 0.5rem; - flex: 0 1 100px; - } - } } diff --git a/public/styles/common/base.css b/public/styles/common/base.css index 2dd5262..20bebd9 100644 --- a/public/styles/common/base.css +++ b/public/styles/common/base.css @@ -227,3 +227,19 @@ input[type="radio"] { font-size: 12px; } } + +.filters { + display: flex; + flex-direction: column; + + .row { + display: flex; + + div { + display: flex; + align-items: center; + gap: 0.5rem; + flex: 0 1 100px; + } + } +} diff --git a/src/bbs.rs b/src/bbs.rs index e2c301f..d62f810 100644 --- a/src/bbs.rs +++ b/src/bbs.rs @@ -1,7 +1,12 @@ -use std::collections::HashMap; +use std::{ + collections::HashMap, + fmt::{self, Display}, + str::FromStr, + string::ParseError, +}; use ability::Abilities; -use askama::Template; +use askama::{FastWritable, Template, filters::HtmlSafe}; use command::Command; use finisher::Finisher; use itertools::Itertools; @@ -15,7 +20,7 @@ mod finisher; mod melding; pub const ABILITIES_PATH: &str = "./input/bbs/abilities.toml"; -const FINISHERS_PATH: &str = "./input/bbs/finish-commands.json"; +const FINISHERS_PATH: &str = "./input/bbs/finishers.toml"; pub const COMMANDS_PATH: &str = "./input/bbs/commands"; #[derive(Debug, Deserialize, PartialEq, Eq)] @@ -28,6 +33,35 @@ enum Character { Terra, } +impl fmt::Display for Character { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Character::Aqua => f.write_str("Aqua"), + Character::Ventus => f.write_str("Ventus"), + Character::Terra => f.write_str("Terra"), + } + } +} + +impl FromStr for Character { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + Ok(match s { + "Aqua" => Character::Aqua, + "Ventus" => Character::Ventus, + "Terra" => Character::Terra, + _ => Character::Ventus, + }) + } +} + +impl Character { + pub fn values() -> Vec { + vec![Character::Aqua, Character::Ventus, Character::Terra] + } +} + #[derive(Template)] #[template(path = "pages/bbs/melding.html")] struct CommandsTemplate { @@ -35,6 +69,24 @@ struct CommandsTemplate { pub crystals: Vec, } +#[derive(Template)] +#[template(path = "pages/bbs/finishers.html")] +struct FinishersTemplate { + pub finishers: HashMap, +} + +impl FinishersTemplate { + pub fn get_at(&self, char: &Character, level: u8, pos: u8) -> Option<&Finisher> { + // let char = Character::from_str(char).unwrap(); + let mut col = self + .finishers + .values() + .filter(|f| f.char.contains(char) && f.level == level); + + col.find(|f| f.row == pos) + } +} + pub struct Module; impl RuntimeModule for Module { @@ -44,7 +96,7 @@ impl RuntimeModule for Module { tracing::info!("Loading BBS finishers data from {}", ABILITIES_PATH); let finishers_str = std::fs::read_to_string(FINISHERS_PATH).unwrap(); - let finishers = serde_json::from_str::>(&finishers_str).unwrap(); + let finishers = toml::from_str::>(&finishers_str).unwrap(); tracing::info!("Loading BBS commands data from {}", ABILITIES_PATH); let mut commands = Command::import(); @@ -69,5 +121,10 @@ impl RuntimeModule for Module { let melding_template = CommandsTemplate { commands, crystals }; create_file("./out/bbs", "melding", melding_template).unwrap(); + + tracing::info!("Generating BBS finishers table template"); + let finishers_template = FinishersTemplate { finishers }; + + create_file("./out/bbs", "finishers", finishers_template).unwrap(); } } diff --git a/src/bbs/finisher.rs b/src/bbs/finisher.rs index c2567e0..678eabe 100644 --- a/src/bbs/finisher.rs +++ b/src/bbs/finisher.rs @@ -8,7 +8,9 @@ pub struct Finisher { pub char: Vec, pub level: u8, #[serde(default)] - pub follows: Vec, + pub next: Vec, + #[serde(default)] + pub row: u8, pub goal: String, pub color: String, } diff --git a/templates/pages/bbs/finishers.html b/templates/pages/bbs/finishers.html new file mode 100644 index 0000000..3522809 --- /dev/null +++ b/templates/pages/bbs/finishers.html @@ -0,0 +1,44 @@ +{% extends "layouts/base.html" %} + +{% block title %}BBS - Finishers{% endblock %} + +{% block head %} + + + +{% endblock %} + +{% block content %} +
+
{% include "components/bbs/char-filters.html" %}
+
+ + + + + {% for lvl in 1..=6u8 %} + + {% endfor %} + + + {% for char in Character::values() %} + + {% for row in 1..=5u8 %} + + {% for lvl in 1..=6u8 %} + {% match get_at(char, *lvl, *row) %} + {% when Some with (val) %} + + {% when None %} + + {% endmatch %} + {% endfor %} + + {% endfor %} + + {% endfor %} +
LV {{lvl}}
{{ val.name }}
+ +
---
+ +{% endblock %} diff --git a/templates/pages/index.html b/templates/pages/index.html index fa4c333..e408172 100644 --- a/templates/pages/index.html +++ b/templates/pages/index.html @@ -41,6 +41,7 @@

Kingdom Hearts Birth by Sleep

{% endif %}