use bevy::prelude::*;
use aerox_network::ConnectionId;
use std::net::SocketAddr;
use std::time::Instant;
#[derive(Component, Debug, Clone)]
pub struct PlayerConnection {
pub connection_id: ConnectionId,
pub address: SocketAddr,
pub connected_at: Instant,
pub last_activity: Instant,
}
impl PlayerConnection {
pub fn new(connection_id: ConnectionId, address: SocketAddr) -> Self {
let now = Instant::now();
Self {
connection_id,
address,
connected_at: now,
last_activity: now,
}
}
pub fn update_activity(&mut self) {
self.last_activity = Instant::now();
}
pub fn duration(&self) -> std::time::Duration {
self.connected_at.elapsed()
}
pub fn idle_time(&self) -> std::time::Duration {
self.last_activity.elapsed()
}
}
#[derive(Component, Debug, Clone, Copy, PartialEq)]
pub struct Position {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl Position {
pub fn new(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z }
}
pub fn origin() -> Self {
Self::new(0.0, 0.0, 0.0)
}
pub fn distance_to(&self, other: &Position) -> f32 {
let dx = self.x - other.x;
let dy = self.y - other.y;
let dz = self.z - other.z;
(dx * dx + dy * dy + dz * dz).sqrt()
}
}
impl Default for Position {
fn default() -> Self {
Self::origin()
}
}
#[derive(Component, Debug, Clone, Copy, PartialEq)]
pub struct Rotation {
pub pitch: f32,
pub yaw: f32,
pub roll: f32,
}
impl Rotation {
pub fn new(pitch: f32, yaw: f32, roll: f32) -> Self {
Self { pitch, yaw, roll }
}
pub fn zero() -> Self {
Self::new(0.0, 0.0, 0.0)
}
}
impl Default for Rotation {
fn default() -> Self {
Self::zero()
}
}
#[derive(Component, Debug, Clone, Copy, PartialEq)]
pub struct Velocity {
pub vx: f32,
pub vy: f32,
pub vz: f32,
}
impl Velocity {
pub fn new(vx: f32, vy: f32, vz: f32) -> Self {
Self { vx, vy, vz }
}
pub fn zero() -> Self {
Self::new(0.0, 0.0, 0.0)
}
pub fn magnitude(&self) -> f32 {
(self.vx * self.vx + self.vy * self.vy + self.vz * self.vz).sqrt()
}
}
impl Default for Velocity {
fn default() -> Self {
Self::zero()
}
}
#[derive(Component, Debug, Clone, Copy, PartialEq)]
pub struct Health {
pub current: f32,
pub max: f32,
}
impl Health {
pub fn new(max: f32) -> Self {
Self {
current: max,
max,
}
}
pub fn full(max: f32) -> Self {
Self::new(max)
}
pub fn damage(&mut self, amount: f32) {
self.current = (self.current - amount).max(0.0);
}
pub fn heal(&mut self, amount: f32) {
self.current = (self.current + amount).min(self.max);
}
pub fn is_dead(&self) -> bool {
self.current <= 0.0
}
pub fn is_full(&self) -> bool {
self.current >= self.max
}
pub fn percentage(&self) -> f32 {
if self.max > 0.0 {
self.current / self.max
} else {
0.0
}
}
}
#[derive(Component, Debug, Clone, PartialEq, Eq)]
pub struct PlayerName {
pub name: String,
}
impl PlayerName {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
}
}
pub fn len(&self) -> usize {
self.name.len()
}
pub fn is_empty(&self) -> bool {
self.name.is_empty()
}
}
impl From<String> for PlayerName {
fn from(name: String) -> Self {
Self::new(name)
}
}
impl From<&str> for PlayerName {
fn from(name: &str) -> Self {
Self::new(name)
}
}
#[derive(Component, Debug, Clone)]
pub struct GameTimer {
pub duration: std::time::Duration,
pub elapsed: std::time::Duration,
pub repeating: bool,
}
impl GameTimer {
pub fn new(duration: std::time::Duration, repeating: bool) -> Self {
Self {
duration,
elapsed: std::time::Duration::ZERO,
repeating,
}
}
pub fn once(duration: std::time::Duration) -> Self {
Self::new(duration, false)
}
pub fn repeating(duration: std::time::Duration) -> Self {
Self::new(duration, true)
}
pub fn tick(&mut self, delta: std::time::Duration) -> bool {
self.elapsed += delta;
if self.elapsed >= self.duration {
if self.repeating {
self.elapsed = std::time::Duration::ZERO;
}
return true;
}
false
}
pub fn reset(&mut self) {
self.elapsed = std::time::Duration::ZERO;
}
pub fn progress(&self) -> f32 {
if self.duration.as_secs_f32() > 0.0 {
(self.elapsed.as_secs_f32() / self.duration.as_secs_f32()).min(1.0)
} else {
1.0
}
}
pub fn finished(&self) -> bool {
self.elapsed >= self.duration
}
pub fn remaining(&self) -> std::time::Duration {
if self.elapsed >= self.duration {
std::time::Duration::ZERO
} else {
self.duration - self.elapsed
}
}
}
pub use GameTimer as Timer;
#[derive(Component, Debug, Clone, PartialEq, Eq)]
pub struct Label {
pub value: String,
}
impl Label {
pub fn new(value: impl Into<String>) -> Self {
Self {
value: value.into(),
}
}
}
impl From<String> for Label {
fn from(value: String) -> Self {
Self::new(value)
}
}
impl From<&str> for Label {
fn from(value: &str) -> Self {
Self::new(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_player_connection() {
let conn_id = ConnectionId::new(1);
let addr = "127.0.0.1:8080".parse().unwrap();
let mut player = PlayerConnection::new(conn_id, addr);
assert_eq!(player.connection_id, conn_id);
assert_eq!(player.address, addr);
std::thread::sleep(std::time::Duration::from_millis(10));
player.update_activity();
assert!(player.idle_time() < std::time::Duration::from_millis(10));
}
#[test]
fn test_position() {
let pos1 = Position::new(1.0, 2.0, 3.0);
let pos2 = Position::new(4.0, 6.0, 8.0);
assert_eq!(pos1.x, 1.0);
assert_eq!(pos1.y, 2.0);
assert_eq!(pos1.z, 3.0);
let distance = pos1.distance_to(&pos2);
assert!((distance - 7.071).abs() < 0.01); }
#[test]
fn test_health() {
let mut health = Health::full(100.0);
assert_eq!(health.current, 100.0);
assert_eq!(health.max, 100.0);
assert!(health.is_full());
health.damage(30.0);
assert_eq!(health.current, 70.0);
assert!(!health.is_full());
assert!(!health.is_dead());
health.heal(20.0);
assert_eq!(health.current, 90.0);
health.damage(100.0);
assert!(health.is_dead());
}
#[test]
fn test_timer() {
let mut timer = GameTimer::once(std::time::Duration::from_millis(100));
assert!(!timer.finished());
assert_eq!(timer.progress(), 0.0);
let triggered = timer.tick(std::time::Duration::from_millis(50));
assert!(!triggered);
assert!((timer.progress() - 0.5) < 0.01);
let triggered = timer.tick(std::time::Duration::from_millis(60));
assert!(triggered);
assert!(timer.finished());
}
#[test]
fn test_repeating_timer() {
let mut timer = GameTimer::repeating(std::time::Duration::from_millis(100));
let triggered = timer.tick(std::time::Duration::from_millis(100));
assert!(triggered);
assert!(!timer.finished()); assert_eq!(timer.elapsed, std::time::Duration::ZERO);
}
}