use crate::{as_transparent, moves::flags::MoveFlags};
use indexmap::IndexMap;
use reikland::Ignored;
use serde::{Deserialize, Deserializer, Serialize};
pub mod flags;
#[derive(Debug, Clone, Copy, Deserialize, Serialize, Hash, PartialEq, Eq)]
pub enum MoveTarget {
AllBattlers,
AllNearFoes,
AllNearOthers,
BothSides,
FoeSide,
NearAlly,
NearFoe,
NearOther,
None,
Other,
RandomNearFoe,
User,
UserAndAllies,
UserOrNearAlly,
UserSide,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[cfg_attr(test, serde(deny_unknown_fields))]
pub struct Move {
#[serde(rename = "@id")]
pub id: String,
#[serde(rename = "@id_number")]
pub id_number: u16,
#[serde(rename = "@real_name")]
pub real_name: String,
#[serde(rename = "@function_code")]
pub function_code: String,
#[serde(rename = "@base_damage")]
pub base_damage: u8,
#[serde(rename = "@type")]
pub r#type: String,
#[serde(rename = "@category")]
pub category: u8,
#[serde(rename = "@accuracy")]
pub accuracy: u8,
#[serde(rename = "@total_pp")]
pub total_pp: u8,
#[serde(rename = "@effect_chance")]
pub effect_chance: u8,
#[serde(rename = "@target")]
pub target: MoveTarget,
#[serde(rename = "@priority")]
pub priority: i8,
#[serde(rename = "@flags")]
pub flags: MoveFlags,
#[serde(rename = "@real_description", deserialize_with = "as_transparent")]
pub real_description: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct MoveDex(pub Vec<Move>);
impl<'de> serde::Deserialize<'de> for MoveDex {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
reikland::DualKeyVec::<(Move, Ignored)>::deserialize(deserializer)
.map(|reikland::DualKeyVec(mut d)| {
d.sort_by_key(|(m, _)| m.id_number);
d.into_iter().map(|(m, _)| m).collect()
})
.map(MoveDex)
}
}
#[derive(Debug, Clone, Serialize)]
pub struct MoveMap(pub IndexMap<String, Move>);
impl<'de> serde::Deserialize<'de> for MoveMap {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
MoveDex::deserialize(deserializer)
.map(|MoveDex(v)| MoveMap(v.into_iter().map(|m| (m.id.clone(), m)).collect()))
}
}
#[cfg(test)]
mod tests {
use crate::test::PathTracked;
use super::*;
const MOVES_DAT: &[u8] = include_bytes!("../InfiniteFusion/Data/moves.dat");
#[test]
fn deserialize_moves_dat_vec() {
let PathTracked(dex) = reikland::from_bytes::<PathTracked<MoveDex>>(MOVES_DAT)
.unwrap_or_else(|e| panic!("moves.dat failed to deserialize: {e}"));
assert!(!dex.0.is_empty());
assert_megahorn(dex.0.first().unwrap());
}
#[test]
fn deserialize_moves_dat_map() {
let PathTracked(dex) = reikland::from_bytes::<PathTracked<MoveMap>>(MOVES_DAT)
.unwrap_or_else(|e| panic!("moves.dat failed to deserialize: {e}"));
assert!(!dex.0.is_empty());
assert_megahorn(dex.0.first().unwrap().1);
}
fn assert_megahorn(m: &Move) {
assert_eq!(m.id, "MEGAHORN");
assert_eq!(m.id_number, 1);
assert_eq!(m.real_name, "Megahorn");
assert_eq!(m.function_code, "000");
assert_eq!(m.base_damage, 120);
assert_eq!(m.r#type, "BUG");
assert_eq!(m.category, 0);
assert_eq!(m.accuracy, 85);
assert_eq!(m.total_pp, 10);
assert_eq!(m.effect_chance, 0);
assert_eq!(m.target, MoveTarget::NearOther);
assert_eq!(m.priority, 0);
assert!(m.flags.contains(MoveFlags::CONTACT));
assert!(m.flags.contains(MoveFlags::CAN_PROTECT));
assert!(m.flags.contains(MoveFlags::CAN_MIRROR_MOVE));
assert!(m.flags.contains(MoveFlags::CAN_KINGS_ROCK));
}
}