use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Position {
pub x: f32,
pub y: f32,
pub z: Option<f32>,
}
impl Position {
pub fn new_2d(x: f32, y: f32) -> Self {
Self { x, y, z: None }
}
pub fn new_3d(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z: Some(z) }
}
pub fn distance_2d(&self, other: &Position) -> f32 {
let dx = self.x - other.x;
let dy = self.y - other.y;
(dx * dx + dy * dy).sqrt()
}
pub fn distance(&self, other: &Position) -> f32 {
let dx = self.x - other.x;
let dy = self.y - other.y;
if let (Some(z1), Some(z2)) = (self.z, other.z) {
let dz = z1 - z2;
(dx * dx + dy * dy + dz * dz).sqrt()
} else {
(dx * dx + dy * dy).sqrt()
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum EntityType {
Player,
NPC,
Object,
Environment,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Entity {
pub id: String,
pub entity_type: EntityType,
pub name: String,
pub position: Position,
pub properties: HashMap<String, serde_json::Value>,
}
impl Entity {
pub fn new(
id: &str,
entity_type: EntityType,
name: &str,
position: Position,
) -> Self {
Self {
id: id.to_string(),
entity_type,
name: name.to_string(),
position,
properties: HashMap::new(),
}
}
pub fn set_property<T: Serialize>(&mut self, key: &str, value: T) -> Result<(), serde_json::Error> {
let json = serde_json::to_value(value)?;
self.properties.insert(key.to_string(), json);
Ok(())
}
pub fn get_property<T: for<'de> Deserialize<'de>>(&self, key: &str) -> Result<Option<T>, serde_json::Error> {
if let Some(value) = self.properties.get(key) {
let typed_value = serde_json::from_value(value.clone())?;
Ok(Some(typed_value))
} else {
Ok(None)
}
}
pub fn distance_to(&self, other: &Entity) -> f32 {
self.position.distance(&other.position)
}
}
pub fn create_player(id: &str, name: &str, position: Position) -> Entity {
Entity::new(id, EntityType::Player, name, position)
}
pub fn create_npc(id: &str, name: &str, position: Position) -> Entity {
Entity::new(id, EntityType::NPC, name, position)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_position_distance() {
let pos1 = Position::new_2d(0.0, 0.0);
let pos2 = Position::new_2d(3.0, 4.0);
assert_eq!(pos1.distance_2d(&pos2), 5.0);
}
#[test]
fn test_entity_properties() {
let mut entity = Entity::new("test", EntityType::NPC, "Test Entity", Position::new_2d(0.0, 0.0));
entity.set_property("health", 100).unwrap();
entity.set_property("name", "Test").unwrap();
let health: Option<i32> = entity.get_property("health").unwrap();
let name: Option<String> = entity.get_property("name").unwrap();
assert_eq!(health, Some(100));
assert_eq!(name, Some("Test".to_string()));
}
}