2024-06-30 22:47:28 +03:00
|
|
|
#![allow(dead_code)]
|
|
|
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
use askama::Template;
|
|
|
|
use itertools::Itertools;
|
|
|
|
use serde::Deserialize;
|
|
|
|
use tracing_subscriber::EnvFilter;
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize, PartialEq, Eq)]
|
|
|
|
enum Character {
|
|
|
|
#[serde(alias = "A")]
|
|
|
|
Aqua,
|
|
|
|
#[serde(alias = "V")]
|
|
|
|
Ventus,
|
|
|
|
#[serde(alias = "T")]
|
|
|
|
Terra,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
|
|
|
struct Ability {
|
|
|
|
name: String,
|
|
|
|
from: String,
|
|
|
|
category: String,
|
|
|
|
max: u8,
|
|
|
|
types: Vec<char>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
struct Command {
|
|
|
|
name: String,
|
|
|
|
category: String,
|
|
|
|
char: Vec<char>,
|
|
|
|
#[serde(default)]
|
|
|
|
starting: Option<bool>,
|
|
|
|
#[serde(default)]
|
|
|
|
info: Option<String>,
|
|
|
|
#[serde(default)]
|
|
|
|
recipes: Vec<CommandRecipe>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Command {}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
struct CommandRecipe {
|
|
|
|
char: Vec<Character>,
|
|
|
|
r#type: char,
|
|
|
|
ingredients: (String, String),
|
|
|
|
chance: u8,
|
|
|
|
#[serde(skip_deserializing)]
|
|
|
|
abilities: Vec<(String, Ability)>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CommandRecipe {
|
|
|
|
pub fn get_ability(&self, crystal: &str) -> Option<Ability> {
|
|
|
|
if self.r#type == '-' {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
for ability in self.abilities.iter() {
|
|
|
|
if ability.0 == crystal {
|
|
|
|
return Some(ability.1.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This should never happen unless the json files are wrong
|
|
|
|
panic!(
|
|
|
|
"No ability found for {} + {} and {}",
|
|
|
|
self.ingredients.0, self.ingredients.1, crystal
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn can_unlock(&self, char: Character) -> bool {
|
|
|
|
self.char.contains(&char)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_unlock_chars(&self) -> String {
|
|
|
|
let mut id = String::new();
|
|
|
|
if self.can_unlock(Character::Terra) {
|
|
|
|
id += "T"
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.can_unlock(Character::Ventus) {
|
|
|
|
if !id.is_empty() {
|
|
|
|
id += " ";
|
|
|
|
}
|
|
|
|
id += "V";
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.can_unlock(Character::Aqua) {
|
|
|
|
if !id.is_empty() {
|
|
|
|
id += " ";
|
|
|
|
}
|
|
|
|
id += "A";
|
|
|
|
}
|
|
|
|
|
|
|
|
id
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_abilities(&mut self, abilities: &[Ability]) {
|
|
|
|
let mut vec: Vec<(String, Ability)> = vec![];
|
|
|
|
|
|
|
|
for ability in abilities.iter() {
|
|
|
|
if ability.types.contains(&self.r#type) {
|
|
|
|
vec.push((ability.from.clone(), ability.clone()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vec.sort();
|
|
|
|
self.abilities = vec;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
struct Finisher {
|
|
|
|
name: String,
|
|
|
|
char: Vec<Character>,
|
|
|
|
level: u8,
|
|
|
|
#[serde(default)]
|
|
|
|
follows: Vec<String>,
|
|
|
|
goal: String,
|
|
|
|
color: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Template)]
|
|
|
|
#[template(path = "pages/commands.html", whitespace = "suppress")]
|
|
|
|
struct CommandsTemplate {
|
|
|
|
pub commands: Vec<Command>,
|
|
|
|
pub crystals: Vec<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
const ABILITIES_PATH: &str = "./input/abilities.json";
|
|
|
|
const FINISHERS_PATH: &str = "./input/finish-commands.json";
|
|
|
|
const COMMANDS_PATH: &str = "./input/commands.json";
|
2024-07-01 18:24:37 +03:00
|
|
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
2024-06-30 22:47:28 +03:00
|
|
|
|
|
|
|
fn main() {
|
|
|
|
// Initialize tracing
|
|
|
|
tracing_subscriber::fmt()
|
|
|
|
.with_env_filter(EnvFilter::from_default_env())
|
|
|
|
.event_format(tracing_subscriber::fmt::format().with_source_location(true))
|
|
|
|
.init();
|
|
|
|
|
|
|
|
tracing::info!("Loading abilities json data from {}", ABILITIES_PATH);
|
|
|
|
let abilities_str = std::fs::read_to_string(ABILITIES_PATH).unwrap();
|
|
|
|
let abilities = serde_json::from_str::<Vec<Ability>>(&abilities_str).unwrap();
|
|
|
|
|
|
|
|
tracing::info!("Loading finishers json 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();
|
|
|
|
|
|
|
|
tracing::info!("Loading commands json data from {}", ABILITIES_PATH);
|
|
|
|
let commands_str = std::fs::read_to_string(COMMANDS_PATH).unwrap();
|
|
|
|
let mut commands = serde_json::from_str::<Vec<Command>>(&commands_str).unwrap();
|
|
|
|
|
|
|
|
// Create a vec with all the crystal variants found in abilities
|
|
|
|
let crystals = abilities
|
|
|
|
.iter()
|
|
|
|
.map(|x| x.from.clone())
|
|
|
|
.unique()
|
|
|
|
.sorted()
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
// Create a vec of crystals and what ability they give for each recipe
|
|
|
|
for cmd in commands.iter_mut() {
|
|
|
|
for recipe in cmd.recipes.iter_mut() {
|
|
|
|
recipe.set_abilities(&abilities);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tracing::info!("Generating the commands table template");
|
|
|
|
let template = CommandsTemplate { commands, crystals };
|
|
|
|
|
|
|
|
std::fs::write("./index.html", template.render().unwrap()).unwrap();
|
|
|
|
}
|