use std::fmt::{Debug, Display, Formatter, Result};
use serde::{Deserialize, Serialize};
use super::{
character,
frame,
metadata,
primitives::Port,
stage,
};
pub const NUM_PORTS: usize = 4;
pub const FIRST_FRAME_INDEX: i32 = -123;
pub const MAX_SUPPORTED_VERSION: SlippiVersion = SlippiVersion(3, 9, 0);
#[derive(Clone, Debug, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct SlippiVersion(pub u8, pub u8, pub u8);
impl Display for SlippiVersion {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{}.{}.{}", self.0, self.1, self.2)
}
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Slippi {
pub version: SlippiVersion,
}
pseudo_enum!(PlayerType: u8 {
0 => HUMAN,
1 => CPU,
2 => DEMO,
});
pseudo_enum!(TeamColor: u8 {
0 => RED,
1 => BLUE,
2 => GREEN,
});
pseudo_enum!(TeamShade: u8 {
0 => NORMAL,
1 => LIGHT,
2 => DARK,
});
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Team {
pub color: TeamColor,
pub shade: TeamShade,
}
pseudo_enum!(DashBack: u32 {
1 => UCF,
2 => ARDUINO,
});
pseudo_enum!(ShieldDrop: u32 {
1 => UCF,
2 => ARDUINO,
});
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Ucf {
pub dash_back: Option<DashBack>,
pub shield_drop: Option<ShieldDrop>,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Netplay {
pub name: String,
pub code: String,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Player {
pub port: Port,
pub character: character::External,
pub r#type: PlayerType,
pub stocks: u8,
pub costume: u8,
pub team: Option<Team>,
pub handicap: u8,
pub bitfield: u8,
pub cpu_level: Option<u8>,
pub offense_ratio: f32,
pub defense_ratio: f32,
pub model_scale: f32,
#[serde(skip_serializing_if = "Option::is_none")]
pub ucf: Option<Ucf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name_tag: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub netplay: Option<Netplay>,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Scene {
pub minor: u8,
pub major: u8,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Start {
pub slippi: Slippi,
pub bitfield: [u8; 3],
pub is_teams: bool,
pub item_spawn_frequency: i8,
pub self_destruct_score: i8,
pub stage: stage::Stage,
pub timer: u32,
pub item_spawn_bitfield: [u8; 5],
pub damage_ratio: f32,
pub players: Vec<Player>,
pub random_seed: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_pal: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_frozen_ps: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scene: Option<Scene>,
}
pseudo_enum!(EndMethod: u8 {
0 => UNRESOLVED,
1 => TIME,
2 => GAME,
3 => RESOLVED,
7 => NO_CONTEST,
});
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct End {
pub method: EndMethod,
#[serde(skip_serializing_if = "Option::is_none")]
pub lras_initiator: Option<Option<Port>>,
}
#[derive(Debug, PartialEq, Serialize)]
#[serde(untagged)]
pub enum Frames {
P1(Vec<frame::Frame1>),
P2(Vec<frame::Frame2>),
P3(Vec<frame::Frame3>),
P4(Vec<frame::Frame4>),
}
impl Frames {
pub fn len(&self) -> usize {
match self {
Self::P1(frames) => frames.len(),
Self::P2(frames) => frames.len(),
Self::P3(frames) => frames.len(),
Self::P4(frames) => frames.len(),
}
}
}
#[derive(PartialEq, Serialize)]
pub struct Game {
pub start: Start,
pub end: End,
pub frames: Frames,
pub metadata: metadata::Metadata,
pub metadata_raw: serde_json::Map<String, serde_json::Value>,
}
impl Debug for Game {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("Game")
.field("metadata", &self.metadata)
.field("start", &self.start)
.field("end", &self.end)
.field("frames", &self.frames)
.finish()
}
}