use crate::coordinates::{ Distance, Neighbors };
use serde::{ Deserialize, Serialize };
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize ) ]
pub struct Position< C >
{
pub coord : C,
}
impl< C > Position< C >
{
pub fn new( coord : C ) -> Self
{
Self { coord }
}
pub fn set( &mut self, coord : C )
{
self.coord = coord;
}
pub fn get( &self ) -> &C
{
&self.coord
}
}
impl< C > Position< C >
where
C : Distance,
{
pub fn distance_to( &self, other : &Position< C > ) -> u32
{
self.coord.distance( &other.coord )
}
}
impl< C > Position< C >
where
C : Neighbors,
{
pub fn neighbors( &self ) -> Vec< Position< C > >
{
self.coord.neighbors().into_iter().map( Position::new ).collect()
}
pub fn is_adjacent_to( &self, other : &Position< C > ) -> bool
where
C : PartialEq,
{
self.coord.neighbors().contains( &other.coord )
}
}
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize ) ]
pub struct Movable
{
pub range : u32,
pub diagonal_movement : bool,
pub can_pass_through_entities : bool,
pub can_pass_through_obstacles : bool,
}
impl Movable
{
pub fn new( range : u32 ) -> Self
{
Self
{
range,
diagonal_movement : false,
can_pass_through_entities : false,
can_pass_through_obstacles : false,
}
}
pub fn with_diagonal( mut self ) -> Self
{
self.diagonal_movement = true;
self
}
pub fn with_entity_passthrough( mut self ) -> Self
{
self.can_pass_through_entities = true;
self
}
pub fn with_obstacle_passthrough( mut self ) -> Self
{
self.can_pass_through_obstacles = true;
self
}
}
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize ) ]
pub struct Size
{
pub width : u32,
pub height : u32,
}
impl Size
{
pub fn new( width : u32, height : u32 ) -> Self
{
Self { width, height }
}
pub fn single() -> Self
{
Self::new( 1, 1 )
}
pub fn square( size : u32 ) -> Self
{
Self::new( size, size )
}
pub fn area( &self ) -> u32
{
self.width * self.height
}
}
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize ) ]
pub struct Health
{
pub current : u32,
pub maximum : u32,
}
impl Health
{
pub fn new( maximum : u32 ) -> Self
{
Self
{
current : maximum,
maximum,
}
}
pub fn damage( &mut self, amount : u32 )
{
self.current = self.current.saturating_sub( amount );
}
pub fn heal( &mut self, amount : u32 )
{
self.current = ( self.current + amount ).min( self.maximum );
}
pub fn full_heal( &mut self )
{
self.current = self.maximum;
}
pub fn is_alive( &self ) -> bool
{
self.current > 0
}
pub fn is_full_health( &self ) -> bool
{
self.current == self.maximum
}
pub fn health_percentage( &self ) -> f32
{
if self.maximum == 0
{
0.0
}
else
{
self.current as f32 / self.maximum as f32
}
}
}
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize ) ]
pub struct Stats
{
pub attack : u32,
pub defense : u32,
pub speed : u32,
pub level : u32,
}
impl Stats
{
pub fn new( attack : u32, defense : u32, speed : u32, level : u32 ) -> Self
{
Self { attack, defense, speed, level }
}
pub fn basic() -> Self
{
Self::new( 10, 10, 10, 1 )
}
pub fn calculate_damage( &self, target_defense : u32 ) -> u32
{
self.attack.saturating_sub( target_defense / 2 ).max( 1 )
}
}
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize ) ]
pub struct Team
{
pub id : u32,
pub default_hostile : bool,
}
impl Team
{
pub fn new( id : u32 ) -> Self
{
Self
{
id,
default_hostile : false,
}
}
pub fn hostile( id : u32 ) -> Self
{
Self
{
id,
default_hostile : true,
}
}
pub fn is_allied_with( &self, other : &Team ) -> bool
{
self.id == other.id
}
pub fn is_hostile_to( &self, other : &Team ) -> bool
{
if self.id == other.id
{
false }
else
{
self.default_hostile || other.default_hostile
}
}
}
#[ derive( Debug, Clone, PartialEq, Serialize, Deserialize ) ]
pub struct Sprite
{
pub texture_id : String,
pub tint : [ f32; 4 ],
pub scale : f32,
pub rotation : f32,
pub visible : bool,
}
impl Sprite
{
pub fn new( texture_id : impl Into< String > ) -> Self
{
Self
{
texture_id : texture_id.into(),
tint : [ 1.0, 1.0, 1.0, 1.0 ], scale : 1.0,
rotation : 0.0,
visible : true,
}
}
pub fn with_tint( mut self, r : f32, g : f32, b : f32, a : f32 ) -> Self
{
self.tint = [ r, g, b, a ];
self
}
pub fn with_scale( mut self, scale : f32 ) -> Self
{
self.scale = scale;
self
}
pub fn with_rotation( mut self, rotation : f32 ) -> Self
{
self.rotation = rotation;
self
}
pub fn hide( &mut self )
{
self.visible = false;
}
pub fn show( &mut self )
{
self.visible = true;
}
}
#[ derive( Debug, Clone, PartialEq, Serialize, Deserialize ) ]
pub struct Animation
{
pub current_frame : u32,
pub frame_count : u32,
pub frame_duration : f32,
pub frame_timer : f32,
pub looping : bool,
pub playing : bool,
}
impl Animation
{
pub fn new( frame_count : u32, frame_duration : f32 ) -> Self
{
Self
{
current_frame : 0,
frame_count,
frame_duration,
frame_timer : 0.0,
looping : true,
playing : true,
}
}
pub fn update( &mut self, dt : f32 )
{
if !self.playing
{
return;
}
self.frame_timer += dt;
if self.frame_timer >= self.frame_duration
{
self.frame_timer = 0.0;
self.current_frame += 1;
if self.current_frame >= self.frame_count
{
if self.looping
{
self.current_frame = 0;
}
else
{
self.current_frame = self.frame_count - 1;
self.playing = false;
}
}
}
}
pub fn play( &mut self )
{
self.playing = true;
}
pub fn pause( &mut self )
{
self.playing = false;
}
pub fn reset( &mut self )
{
self.current_frame = 0;
self.frame_timer = 0.0;
}
}
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize ) ]
pub struct PlayerControlled
{
pub player_id : u32,
}
impl PlayerControlled
{
pub fn new( player_id : u32 ) -> Self
{
Self { player_id }
}
}
#[ derive( Debug, Clone, PartialEq ) ]
pub struct AI
{
pub state : AIState,
pub target : Option< hecs::Entity >,
pub decision_timer : f32,
pub decision_interval : f32,
}
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize ) ]
pub enum AIState
{
Idle,
Patrolling,
Pursuing,
Attacking,
Fleeing,
Guarding,
}
impl AI
{
pub fn new( decision_interval : f32 ) -> Self
{
Self
{
state : AIState::Idle,
target : None,
decision_timer : 0.0,
decision_interval,
}
}
pub fn update( &mut self, dt : f32 )
{
self.decision_timer += dt;
}
pub fn should_make_decision( &self ) -> bool
{
self.decision_timer >= self.decision_interval
}
pub fn reset_decision_timer( &mut self )
{
self.decision_timer = 0.0;
}
pub fn set_target( &mut self, target : Option< hecs::Entity > )
{
self.target = target;
}
pub fn set_state( &mut self, state : AIState )
{
self.state = state;
}
}
#[ derive( Debug, Clone, PartialEq, Serialize, Deserialize ) ]
pub struct Trigger
{
pub trigger_type : TriggerType,
pub repeatable : bool,
pub activated : bool,
pub cooldown : f32,
pub cooldown_timer : f32,
}
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize ) ]
pub enum TriggerType
{
OnEnter,
OnExit,
OnProximity,
OnInteract,
OnTimer( u32 ), }
impl Trigger
{
pub fn new( trigger_type : TriggerType ) -> Self
{
Self
{
trigger_type,
repeatable : false,
activated : false,
cooldown : 0.0,
cooldown_timer : 0.0,
}
}
pub fn repeatable( mut self, cooldown : f32 ) -> Self
{
self.repeatable = true;
self.cooldown = cooldown;
self
}
pub fn update( &mut self, dt : f32 )
{
if self.cooldown_timer > 0.0
{
self.cooldown_timer -= dt;
}
}
pub fn can_activate( &self ) -> bool
{
if self.activated && !self.repeatable
{
false
}
else
{
self.cooldown_timer <= 0.0
}
}
pub fn activate( &mut self )
{
self.activated = true;
if self.repeatable
{
self.cooldown_timer = self.cooldown;
}
}
}