use getset::{Getters, Setters};
use serde_derive::{Serialize, Deserialize};
use crate::binary::{ReadBytes, WriteBytes};
use crate::error::{RLibError, Result};
use crate::files::{DecodeableExtraData, Decodeable, EncodeableExtraData, Encodeable};
use crate::games::supported_games::{KEY_THREE_KINGDOMS, KEY_WARHAMMER_3};
use crate::utils::check_size_mismatch;
pub const BASE_PATHS: [&str; 3] = ["animations/matched_combat", "animations/database/matched", "animations/database/trigger"];
pub const EXTENSION: &str = ".bin";
mod versions;
#[cfg(test)] mod matched_combat_test;
#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
#[getset(get = "pub", set = "pub")]
pub struct MatchedCombat {
version: u32,
entries: Vec<MatchedEntry>,
}
#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
#[getset(get = "pub", set = "pub")]
pub struct MatchedEntry {
id: String,
participants: Vec<Participant>,
}
#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
#[getset(get = "pub", set = "pub")]
pub struct Participant {
team: u32,
entity_info: Vec<EntityBundle>,
state: State,
uk1: u32,
uk2: u32,
uk3: u32,
uk4: u32,
}
#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
#[getset(get = "pub", set = "pub")]
pub struct EntityBundle {
entities: Vec<Entity>,
selection_weight: f32,
}
#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
#[getset(get = "pub", set = "pub")]
pub struct Entity {
animation_filename: String,
metadata_filenames: Vec<String>,
blend_in_time: f32,
equipment_display: u32,
filters: Vec<Filter>,
uk: u32,
mount_filename: String,
}
#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
#[getset(get = "pub", set = "pub")]
pub struct Filter {
equals: bool,
or: bool,
filter_type: u32,
value: String,
}
#[derive(PartialEq, Clone, Debug, Default, Getters, Setters, Serialize, Deserialize)]
#[getset(get = "pub", set = "pub")]
pub struct State {
start: StateParticipant,
end: StateParticipant,
}
#[derive(PartialEq, Clone, Copy, Debug, Default, Serialize, Deserialize)]
pub enum StateParticipant {
#[default] Alive,
Dead = 1,
NoIdea1 = 2,
NoIdea2 = 3,
NoIdea3 = 4,
NoIdea4 = 5,
NoIdea5 = 6,
}
impl Decodeable for MatchedCombat {
fn decode<R: ReadBytes>(data: &mut R, extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
let extra_data = extra_data.as_ref().ok_or(RLibError::DecodingMissingExtraData)?;
let game_info = extra_data.game_info.ok_or_else(|| RLibError::DecodingMissingExtraDataField("game_info".to_owned()))?;
let mut matched = Self::default();
matched.version = data.read_u32()?;
match matched.version {
1 => match game_info.key() {
KEY_WARHAMMER_3 => matched.read_v1_wh3(data)?,
KEY_THREE_KINGDOMS => matched.read_v1_3k(data)?,
_ => Err(RLibError::DecodingMatchedCombatUnsupportedVersion(matched.version as usize))?,
}
3 => matched.read_v3(data)?,
_ => Err(RLibError::DecodingMatchedCombatUnsupportedVersion(matched.version as usize))?,
}
check_size_mismatch(data.stream_position()? as usize, data.len()? as usize)?;
Ok(matched)
}
}
impl Encodeable for MatchedCombat {
fn encode<W: WriteBytes>(&mut self, buffer: &mut W, extra_data: &Option<EncodeableExtraData>) -> Result<()> {
let extra_data = extra_data.as_ref().ok_or(RLibError::EncodingMissingExtraData)?;
let game_info = extra_data.game_info.ok_or_else(|| RLibError::DecodingMissingExtraDataField("game_info".to_owned()))?;
buffer.write_u32(self.version)?;
match self.version {
1 => match game_info.key() {
KEY_WARHAMMER_3 => self.write_v1_wh3(buffer)?,
KEY_THREE_KINGDOMS => self.write_v1_3k(buffer)?,
_ => Err(RLibError::DecodingMatchedCombatUnsupportedVersion(self.version as usize))?,
}
3 => self.write_v3(buffer)?,
_ => Err(RLibError::DecodingMatchedCombatUnsupportedVersion(self.version as usize))?,
};
Ok(())
}
}
impl TryFrom<u32> for StateParticipant {
type Error = RLibError;
fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
match value {
0 => Ok(Self::Alive),
1 => Ok(Self::Dead),
2 => Ok(Self::NoIdea1),
3 => Ok(Self::NoIdea2),
4 => Ok(Self::NoIdea3),
5 => Ok(Self::NoIdea4),
6 => Ok(Self::NoIdea5),
_ => Err(RLibError::InvalidStateParticipantValue(value)),
}
}
}