use std::{collections::HashMap, io, io::BufReader};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::Definition;
use crate::{extension::ReadExt, util};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Eq, PartialEq, Debug, Default)]
pub struct ObjectDefinition {
pub id: u16,
pub name: String,
pub config_id: Option<u16>,
pub map_area_id: Option<u16>,
pub map_scene_id: u16,
pub animation_id: u16,
pub solid: bool,
pub shadow: bool,
pub obstruct_ground: bool,
pub supports_items: Option<u8>,
pub actions: [String; 5],
pub interact_type: u8,
pub rotated: bool,
pub ambient_sound_id: u16,
pub blocks_projectile: bool,
pub wall_or_door: Option<u8>,
pub contoured_ground: Option<u8>,
pub config_change_dest: Vec<u16>,
pub params: HashMap<u32, String>,
pub model_data: ObjectModelData,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct ObjectModelData {
pub models: Vec<u16>,
pub types: Vec<u8>,
pub recolor_find: Vec<u16>,
pub recolor_replace: Vec<u16>,
pub retexture_find: Vec<u16>,
pub retexture_replace: Vec<u16>,
pub size_x: u8,
pub size_y: u8,
pub offset_x: u16,
pub offset_y: u16,
pub offset_z: u16,
pub model_size_x: u16,
pub model_size_y: u16,
pub model_size_z: u16,
pub varp_id: Option<u16>,
pub ambient: u8,
pub contrast: u8,
pub decord_displacement: u8,
pub merge_normals: bool,
pub blocking_mask: u8,
}
impl Definition for ObjectDefinition {
fn new(id: u16, buffer: &[u8]) -> crate::Result<Self> {
let mut reader = BufReader::new(buffer);
let mut obj_def = decode_buffer(id, &mut reader)?;
post(&mut obj_def);
Ok(obj_def)
}
}
fn decode_buffer(id: u16, reader: &mut BufReader<&[u8]>) -> io::Result<ObjectDefinition> {
let mut obj_def = ObjectDefinition {
id,
interact_type: 2,
blocks_projectile: true,
solid: true,
model_data: ObjectModelData {
decord_displacement: 16,
size_x: 1,
size_y: 1,
model_size_x: 128,
model_size_y: 128,
model_size_z: 128,
..ObjectModelData::default()
},
..ObjectDefinition::default()
};
loop {
let opcode = reader.read_u8()?;
match opcode {
0 => break,
1 => {
let len = reader.read_u8()?;
for _ in 0..len {
obj_def.model_data.models.push(reader.read_u16()?);
obj_def.model_data.types.push(reader.read_u8()?);
}
}
2 => {
obj_def.name = reader.read_string()?;
}
5 => {
let len = reader.read_u8()?;
obj_def.model_data.types.clear();
for _ in 0..len {
obj_def.model_data.models.push(reader.read_u16()?);
}
}
14 => {
obj_def.model_data.size_x = reader.read_u8()?;
}
15 => {
obj_def.model_data.size_y = reader.read_u8()?;
}
17 => {
obj_def.interact_type = 0;
obj_def.blocks_projectile = false;
}
18 => {
obj_def.blocks_projectile = false;
}
19 => {
obj_def.wall_or_door = Some(reader.read_u8()?);
}
21 => {
obj_def.contoured_ground = Some(0);
}
22 => {
obj_def.model_data.merge_normals = true;
}
24 => {
obj_def.animation_id = reader.read_u16()?;
}
27 => {
obj_def.interact_type = 1;
}
28 => {
obj_def.model_data.decord_displacement = reader.read_u8()?;
}
29 => {
obj_def.model_data.ambient = reader.read_u8()?;
}
30..=34 => {
obj_def.actions[opcode as usize - 30] = reader.read_string()?;
}
39 => {
obj_def.model_data.contrast = reader.read_u8()?;
}
40 => {
let len = reader.read_u8()?;
for _ in 0..len {
obj_def.model_data.recolor_find.push(reader.read_u16()?);
obj_def.model_data.recolor_replace.push(reader.read_u16()?);
}
}
41 => {
let len = reader.read_u8()?;
for _ in 0..len {
obj_def.model_data.retexture_find.push(reader.read_u16()?);
obj_def
.model_data
.retexture_replace
.push(reader.read_u16()?);
}
}
62 => {
obj_def.rotated = true;
}
64 => {
obj_def.shadow = true;
}
65 => {
obj_def.model_data.model_size_x = reader.read_u16()?;
}
66 => {
obj_def.model_data.model_size_z = reader.read_u16()?;
}
67 => {
obj_def.model_data.model_size_y = reader.read_u16()?;
}
68 => {
obj_def.map_scene_id = reader.read_u16()?;
}
69 => {
obj_def.model_data.blocking_mask = reader.read_u8()?;
}
70 => {
obj_def.model_data.offset_x = reader.read_u16()?;
}
71 => {
obj_def.model_data.offset_z = reader.read_u16()?;
}
72 => {
obj_def.model_data.offset_y = reader.read_u16()?;
}
73 => {
obj_def.obstruct_ground = true;
}
74 => {
obj_def.solid = false;
}
75 => {
obj_def.supports_items = Some(reader.read_u8()?);
}
77 => {
let varp_id = reader.read_u16()?;
obj_def.model_data.varp_id = if varp_id == u16::MAX {
None
} else {
Some(varp_id)
};
let config_id = reader.read_u16()?;
obj_def.config_id = if config_id == u16::MAX {
None
} else {
Some(config_id)
};
let len = reader.read_u8()?;
obj_def.config_change_dest = Vec::new();
for _ in 0..=len {
obj_def.config_change_dest.push(reader.read_u16()?);
}
}
78 => {
obj_def.ambient_sound_id = reader.read_u16()?;
reader.read_u8()?;
}
79 => {
reader.read_u16()?;
reader.read_u16()?;
reader.read_u8()?;
let len = reader.read_u8()?;
for _ in 0..len {
reader.read_u16()?;
}
}
81 => {
obj_def.contoured_ground = Some(reader.read_u8()?);
}
82 => {
obj_def.map_area_id = Some(reader.read_u16()?);
}
92 => {
let varp_id = reader.read_u16()?;
obj_def.model_data.varp_id = if varp_id == u16::MAX {
None
} else {
Some(varp_id)
};
let config_id = reader.read_u16()?;
obj_def.config_id = if config_id == u16::MAX {
None
} else {
Some(config_id)
};
let _var = reader.read_u16()?;
let len = reader.read_u8()?;
obj_def.config_change_dest = Vec::new();
for _ in 0..=len {
obj_def.config_change_dest.push(reader.read_u16()?);
}
}
249 => {
obj_def.params = util::read_parameters(reader)?;
}
23 => { }
_ => unreachable!(),
}
}
Ok(obj_def)
}
fn post(obj_def: &mut ObjectDefinition) {
if obj_def.wall_or_door.is_none() {
obj_def.wall_or_door = Some(0);
if !obj_def.model_data.models.is_empty()
&& (obj_def.model_data.types.is_empty() || obj_def.model_data.types[0] == 10)
{
obj_def.wall_or_door = Some(1);
}
for var in 0..5 {
if !obj_def.actions[var].is_empty() {
obj_def.wall_or_door = Some(1);
}
}
}
if obj_def.supports_items.is_none() {
obj_def.supports_items = Some(u8::from(obj_def.interact_type != 0));
}
}