use crate::{EntityId, Tick};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TimewaveId(pub u32);
#[derive(Debug)]
pub struct Timewave {
pub id: TimewaveId,
pub position: Tick,
pub previous_position: Tick,
pub speed: f64,
pub base_speed: f64,
fractional: f64,
pub is_active: bool,
pub needs_sync: bool,
entity_views: HashMap<EntityId, Tick>,
dirty_entities: HashSet<EntityId>,
pub prev_wave: Option<TimewaveId>,
pub next_wave: Option<TimewaveId>,
}
impl Timewave {
pub fn new(id: TimewaveId, position: Tick, speed: f64) -> Self {
Self {
id,
position,
previous_position: position,
speed,
base_speed: 1.0,
fractional: 0.0,
is_active: true,
needs_sync: false,
entity_views: HashMap::new(),
dirty_entities: HashSet::new(),
prev_wave: None,
next_wave: None,
}
}
pub fn stationary(id: TimewaveId, position: Tick) -> Self {
Self::new(id, position, 0.0)
}
pub fn reverse(id: TimewaveId, position: Tick, speed: f64) -> Self {
Self::new(id, position, -speed.abs())
}
pub fn advance(&mut self, delta: u64) {
if !self.is_active {
return;
}
self.previous_position = self.position;
let advance = self.speed * self.base_speed * (delta as f64);
let new_frac = self.fractional + advance;
if new_frac >= 0.0 {
let whole = new_frac.floor() as u64;
self.position = self.position.saturating_add(whole);
self.fractional = new_frac - (whole as f64);
} else {
let whole = (-new_frac).ceil() as u64;
self.position = self.position.saturating_sub(whole);
self.fractional = new_frac + (whole as f64);
}
}
pub fn set_speed(&mut self, speed: f64) {
self.speed = speed;
}
pub fn pause(&mut self) {
self.is_active = false;
}
pub fn resume(&mut self) {
self.is_active = true;
}
pub fn jump_to(&mut self, position: Tick) {
self.previous_position = self.position;
self.position = position;
self.fractional = 0.0;
}
pub fn delta(&self) -> i64 {
(self.position as i64) - (self.previous_position as i64)
}
pub fn is_forward(&self) -> bool {
self.speed > 0.0
}
pub fn is_backward(&self) -> bool {
self.speed < 0.0
}
pub fn is_stationary(&self) -> bool {
self.speed == 0.0 || !self.is_active
}
pub fn update_entity_view(&mut self, id: EntityId, event_tick: Tick) {
self.entity_views.insert(id, event_tick);
}
pub fn get_entity_view(&self, id: EntityId) -> Option<Tick> {
self.entity_views.get(&id).copied()
}
pub fn mark_dirty(&mut self, id: EntityId) {
self.dirty_entities.insert(id);
}
pub fn is_dirty(&self, id: EntityId) -> bool {
self.dirty_entities.contains(&id)
}
pub fn clear_dirty(&mut self) {
self.dirty_entities.clear();
}
pub fn dirty_entities(&self) -> impl Iterator<Item = EntityId> + '_ {
self.dirty_entities.iter().copied()
}
pub fn entity_views(&self) -> impl Iterator<Item = (EntityId, Tick)> + '_ {
self.entity_views.iter().map(|(&id, &tick)| (id, tick))
}
pub fn inherit_views_from(&mut self, other: &Timewave) {
for (&id, &tick) in &other.entity_views {
if let Some(&our_tick) = self.entity_views.get(&id) {
if tick <= our_tick {
continue;
}
}
self.entity_views.insert(id, tick);
self.dirty_entities.insert(id);
}
}
pub fn clear_views(&mut self) {
self.entity_views.clear();
self.dirty_entities.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_forward_movement() {
let mut wave = Timewave::new(TimewaveId(0), 0, 1.0);
wave.advance(10);
assert_eq!(wave.position, 10);
assert_eq!(wave.delta(), 10);
}
#[test]
fn test_fast_movement() {
let mut wave = Timewave::new(TimewaveId(0), 0, 3.0);
wave.advance(10);
assert_eq!(wave.position, 30);
}
#[test]
fn test_slow_movement() {
let mut wave = Timewave::new(TimewaveId(0), 0, 0.5);
wave.advance(1);
assert_eq!(wave.position, 0);
wave.advance(1);
assert_eq!(wave.position, 1);
}
#[test]
fn test_backward_movement() {
let mut wave = Timewave::reverse(TimewaveId(0), 100, 1.0);
wave.advance(10);
assert_eq!(wave.position, 90);
assert_eq!(wave.delta(), -10);
}
#[test]
fn test_stationary() {
let mut wave = Timewave::stationary(TimewaveId(0), 50);
wave.advance(100);
assert_eq!(wave.position, 50);
}
#[test]
fn test_pause_resume() {
let mut wave = Timewave::new(TimewaveId(0), 0, 1.0);
wave.advance(5);
assert_eq!(wave.position, 5);
wave.pause();
wave.advance(10);
assert_eq!(wave.position, 5);
wave.resume();
wave.advance(10);
assert_eq!(wave.position, 15);
}
#[test]
fn test_jump() {
let mut wave = Timewave::new(TimewaveId(0), 0, 1.0);
wave.advance(10);
wave.jump_to(100);
assert_eq!(wave.position, 100);
assert_eq!(wave.previous_position, 10);
}
}