Finishers page for BBS

master
Wynd 2026-03-03 22:51:42 +02:00
parent 22f5f67298
commit b8755b847c
11 changed files with 468 additions and 264 deletions

View File

@ -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"
}
}

View File

@ -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"

View File

@ -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";
}

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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<Self, Self::Err> {
Ok(match s {
"Aqua" => Character::Aqua,
"Ventus" => Character::Ventus,
"Terra" => Character::Terra,
_ => Character::Ventus,
})
}
}
impl Character {
pub fn values() -> Vec<Character> {
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<String>,
}
#[derive(Template)]
#[template(path = "pages/bbs/finishers.html")]
struct FinishersTemplate {
pub finishers: HashMap<String, Finisher>,
}
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::<HashMap<String, Finisher>>(&finishers_str).unwrap();
let finishers = toml::from_str::<HashMap<String, Finisher>>(&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();
}
}

View File

@ -8,7 +8,9 @@ pub struct Finisher {
pub char: Vec<Character>,
pub level: u8,
#[serde(default)]
pub follows: Vec<String>,
pub next: Vec<String>,
#[serde(default)]
pub row: u8,
pub goal: String,
pub color: String,
}

View File

@ -0,0 +1,44 @@
{% extends "layouts/base.html" %}
{% block title %}BBS - Finishers{% endblock %}
{% block head %}
<link rel="stylesheet" href="{{ crate::find_hash("/public/styles/bbs/finishers.css") }}">
</link>
<script type="module" src="{{ crate::find_hash("/public/scripts/bbs/finishers.js") }}"></script>
{% endblock %}
{% block content %}
<div class="filters">
<div class="row">{% include "components/bbs/char-filters.html" %}</div>
</div>
<table>
<thead>
<tr>
{% for lvl in 1..=6u8 %}
<th>LV {{lvl}}</th>
{% endfor %}
</tr>
</thead>
{% for char in Character::values() %}
<tbody id="finishers{{char}}">
{% for row in 1..=5u8 %}
<tr>
{% for lvl in 1..=6u8 %}
{% match get_at(char, *lvl, *row) %}
{% when Some with (val) %}
<td class="finisher" style="color: {{val.color}};" data-goal="{{ val.goal }}">{{ val.name }}</td>
{% when None %}
<td></td>
{% endmatch %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
{% endfor %}
</table>
<div id="finisherDetails">---</div>
{% endblock %}

View File

@ -41,6 +41,7 @@
<h1>Kingdom Hearts Birth by Sleep</h1>
<ul>
<li><a href="./bbs/melding.html">Command Melding</a></li>
<li><a href="./bbs/finishers.html">Finishers</a></li>
</ul>
{% endif %}