#![allow(unused)] use assetpath::AssetPath; use anyhow::Result; use engine::prelude::*; use serde::{Deserialize, Serialize}; use crate::animation_info::AnimationType; use super::entity_tags::EntityTags; use std::collections::HashMap; use std::{fmt::Display, str::FromStr, time::Duration}; // parse state keys const META_KEY: &str = "Meta"; const ANIMATION_KEY: &str = "Animation"; const AUDIO_KEY: &str = "Sounds"; // meta information keys const GLTF_FILE_NAME: &str = "Object"; const ENTITY_TAGS: &str = "Tags"; const HITBOX: &str = "HitBox"; const HITBOX_RADIUS: &str = "HitboxRadius"; const HITBOX_HEIGHT: &str = "HitboxHeight"; // animation information keys const ANIMATION_DATA_KEY: &str = "Info"; const ANIMATION_MOVE_KEY: &str = "Move"; const ANIMATION_IDLE_KEY: &str = "Idle"; const ANIMATION_CAST_KEY: &str = "Cast"; const ANIMATION_ATTACK_KEY: &str = "Attack"; // sound information keys pub const SOUND_MOVE_KEY: &str = "Move"; pub const SOUND_CREATE_KEY: &str = ON_ENABLE_SOUND; pub const SOUND_DESTROY_KEY: &str = ON_DISABLE_SOUND; // TODO: more pub fn split_matches(s: &str, c: char) -> (&str, &str) { match s.find(c) { Some(pos) => { let (f, l) = s.split_at(pos); let l = l.trim_start_matches(c); (f, l) } None => (s, ""), } } #[derive(Clone, Serialize, Deserialize)] pub struct AnimationData { pub index: u32, pub name: String, pub duration: Duration, } impl FromStr for AnimationData { type Err = anyhow::Error; fn from_str(s: &str) -> Result { let (index, rest) = split_matches(s, ';'); let (name, duration) = split_matches(rest, ';'); Ok(Self { index: index.parse()?, name: name.to_string(), duration: PersistentDuration::from_str(duration)?.into(), }) } } impl Display for AnimationData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{};{};{}", self.index, self.name, PersistentDuration::from(self.duration) ) } } pub struct EntityParser { pub enable_hitbox: bool, pub hitbox_radius: f32, pub hitbox_height: f32, pub gltf_file_name: String, pub tags: Vec, pub animations: Vec, pub animation_map: HashMap>, pub sound_map: HashMap, } impl EntityParser { pub fn parse(file: &AssetPath) -> Result { let mut entity_parser = EntityParser::default(); let parsed_entity = ConfigHandler::read_config(&file.full_path())?; if let Some(info) = parsed_entity.get(META_KEY) { if let Some(key_value) = info.get(GLTF_FILE_NAME) { entity_parser.gltf_file_name = key_value.to_value()?; } if let Some(key_value) = info.get(HITBOX) { entity_parser.enable_hitbox = key_value.to_value()?; } if let Some(key_value) = info.get(HITBOX_RADIUS) { entity_parser.hitbox_radius = key_value.to_value()?; } if let Some(key_value) = info.get(HITBOX_HEIGHT) { entity_parser.hitbox_height = key_value.to_value()?; } if let Some(key_value) = info.get(ENTITY_TAGS) { let tag_strings: Vec = key_value.to_array()?; for tag_string in tag_strings { match tag_string.as_str() { "Ability" => { if !entity_parser.tags.contains(&EntityTags::Ability) { entity_parser.tags.push(EntityTags::Ability); } } "WorldObject" => { if !entity_parser.tags.contains(&EntityTags::WorldObject) { entity_parser.tags.push(EntityTags::WorldObject); } } "Creature" => { if !entity_parser.tags.contains(&EntityTags::Creature) { entity_parser.tags.push(EntityTags::Creature); } } _ => (), } } } } if let Some(info) = parsed_entity.get(ANIMATION_KEY) { if let Some(key_value) = info.get(ANIMATION_DATA_KEY) { entity_parser.animations = key_value.to_array()?; } if let Some(key_value) = info.get(ANIMATION_MOVE_KEY) { Self::insert_animation( key_value, &mut entity_parser.animation_map, AnimationType::Move, )?; } if let Some(key_value) = info.get(ANIMATION_IDLE_KEY) { Self::insert_animation( key_value, &mut entity_parser.animation_map, AnimationType::Idle, )?; } if let Some(key_value) = info.get(ANIMATION_ATTACK_KEY) { Self::insert_animation( key_value, &mut entity_parser.animation_map, AnimationType::Attack, )?; } if let Some(key_value) = info.get(ANIMATION_CAST_KEY) { Self::insert_animation( key_value, &mut entity_parser.animation_map, AnimationType::Cast, )?; } } if let Some(info) = parsed_entity.get(AUDIO_KEY) { if let Some(key_value) = info.get(SOUND_MOVE_KEY) { Self::insert_sound(key_value, &mut entity_parser.sound_map, SOUND_MOVE_KEY)?; } if let Some(key_value) = info.get(SOUND_CREATE_KEY) { Self::insert_sound(key_value, &mut entity_parser.sound_map, SOUND_CREATE_KEY)?; } if let Some(key_value) = info.get(SOUND_DESTROY_KEY) { Self::insert_sound(key_value, &mut entity_parser.sound_map, SOUND_DESTROY_KEY)?; } } Ok(entity_parser) } pub fn save(&self, file: &str) -> Result<()> { let data = vec![ ( META_KEY, vec![ (GLTF_FILE_NAME, Value::from(&self.gltf_file_name)), (ENTITY_TAGS, Value::from(self.tags.as_slice())), (HITBOX, Value::from(&self.enable_hitbox)), (HITBOX_RADIUS, Value::from(&self.hitbox_radius)), (HITBOX_HEIGHT, Value::from(&self.hitbox_height)), ], ), ( ANIMATION_KEY, vec![ (ANIMATION_DATA_KEY, Value::from(self.animations.as_slice())), ( ANIMATION_MOVE_KEY, match self.animation_map.get(&AnimationType::Move) { Some(indices) => Value::from(indices.as_slice()), None => Value::empty_array(), }, ), ( ANIMATION_CAST_KEY, match self.animation_map.get(&AnimationType::Cast) { Some(indices) => Value::from(indices.as_slice()), None => Value::empty_array(), }, ), ( ANIMATION_IDLE_KEY, match self.animation_map.get(&AnimationType::Idle) { Some(indices) => Value::from(indices.as_slice()), None => Value::empty_array(), }, ), ( ANIMATION_ATTACK_KEY, match self.animation_map.get(&AnimationType::Attack) { Some(indices) => Value::from(indices.as_slice()), None => Value::empty_array(), }, ), ], ), ( AUDIO_KEY, vec![ ( SOUND_MOVE_KEY, match self.sound_map.get(SOUND_MOVE_KEY) { Some(file_name) => Value::from(&file_name), None => Value::empty(), }, ), ( SOUND_CREATE_KEY, match self.sound_map.get(SOUND_CREATE_KEY) { Some(file_name) => Value::from(&file_name), None => Value::empty(), }, ), ( SOUND_DESTROY_KEY, match self.sound_map.get(SOUND_DESTROY_KEY) { Some(file_name) => Value::from(&file_name), None => Value::empty(), }, ), ], ), ]; Ok(ConfigHandler::write_config(file, &data)?) } fn insert_animation( key_value: &Value, animation_map: &mut HashMap>, animation_type: AnimationType, ) -> Result<()> { animation_map.insert(animation_type, key_value.to_array()?); Ok(()) } fn insert_sound( key_value: &Value, sound_map: &mut HashMap, sound_type: &str, ) -> Result<()> { sound_map.insert(sound_type.to_string(), key_value.to_value()?); Ok(()) } } impl Default for EntityParser { fn default() -> EntityParser { EntityParser { enable_hitbox: false, hitbox_radius: 0.0, hitbox_height: 0.0, gltf_file_name: String::new(), animations: Vec::new(), animation_map: HashMap::new(), sound_map: HashMap::new(), tags: Vec::new(), } } }