use crate::io_ext::{ReadExt, WriteExt};
use std::io::{Read, Seek, Write};
use crate::chunks::animation::{M2AnimationBlock, M2AnimationTrack};
use crate::common::C3Vector;
use crate::error::Result;
use crate::version::M2Version;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum M2AttachmentType {
Shoulder = 0,
ShoulderLeft = 1,
ShoulderRight = 2,
Shield = 3,
Unknown4 = 4,
LeftPalm = 5,
RightPalm = 6,
Unknown7 = 7,
Unknown8 = 8,
Unknown9 = 9,
Head = 10,
SpellLeftHand = 11,
SpellRightHand = 12,
WeaponMain = 13,
WeaponOff = 14,
}
impl M2AttachmentType {
pub fn from_u16(value: u16) -> Option<Self> {
match value {
0 => Some(Self::Shoulder),
1 => Some(Self::ShoulderLeft),
2 => Some(Self::ShoulderRight),
3 => Some(Self::Shield),
4 => Some(Self::Unknown4),
5 => Some(Self::LeftPalm),
6 => Some(Self::RightPalm),
7 => Some(Self::Unknown7),
8 => Some(Self::Unknown8),
9 => Some(Self::Unknown9),
10 => Some(Self::Head),
11 => Some(Self::SpellLeftHand),
12 => Some(Self::SpellRightHand),
13 => Some(Self::WeaponMain),
14 => Some(Self::WeaponOff),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct M2Attachment {
pub id: u32,
pub bone_index: i32,
pub position: C3Vector,
pub scale_animation: M2AnimationBlock<f32>,
}
impl M2Attachment {
pub fn parse<R: Read + Seek>(reader: &mut R, _version: u32) -> Result<Self> {
let id = reader.read_u32_le()?;
let bone_index = reader.read_i32_le()?;
let position = C3Vector::parse(reader)?;
let scale_animation = M2AnimationBlock::parse(reader)?;
Ok(Self {
id,
bone_index,
position,
scale_animation,
})
}
pub fn write<W: Write>(&self, writer: &mut W, _version: u32) -> Result<()> {
writer.write_u32_le(self.id)?;
writer.write_i32_le(self.bone_index)?;
self.position.write(writer)?;
self.scale_animation.write(writer)?;
Ok(())
}
pub fn convert(&self, _target_version: M2Version) -> Self {
self.clone()
}
pub fn new(id: u32, bone_index: i32) -> Self {
Self {
id,
bone_index,
position: C3Vector::default(),
scale_animation: M2AnimationBlock::new(M2AnimationTrack::default()),
}
}
pub const fn size() -> usize {
48
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_attachment_parse_write() {
let attachment = M2Attachment::new(1, 2);
let mut data = Vec::new();
attachment
.write(&mut data, M2Version::Vanilla.to_header_version())
.unwrap();
assert_eq!(data.len(), 48);
let mut cursor = Cursor::new(data);
let parsed =
M2Attachment::parse(&mut cursor, M2Version::Vanilla.to_header_version()).unwrap();
assert_eq!(parsed.id, 1);
assert_eq!(parsed.bone_index, 2);
}
#[test]
fn test_attachment_size() {
assert_eq!(M2Attachment::size(), 48);
}
#[test]
fn test_attachment_types() {
assert_eq!(
M2AttachmentType::from_u16(0),
Some(M2AttachmentType::Shoulder)
);
assert_eq!(
M2AttachmentType::from_u16(13),
Some(M2AttachmentType::WeaponMain)
);
assert_eq!(
M2AttachmentType::from_u16(14),
Some(M2AttachmentType::WeaponOff)
);
assert_eq!(M2AttachmentType::from_u16(20), None);
}
}