use super::types::*;
use crate::state::State;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct WorldMapState {
travels: HashMap<EntityId, Travel>,
positions: HashMap<EntityId, EntityPosition>,
discovered_locations: HashSet<LocationId>,
travel_history: HashMap<EntityId, Vec<LocationId>>,
}
impl State for WorldMapState {}
impl WorldMapState {
pub fn new() -> Self {
Self::default()
}
pub fn start_travel(&mut self, travel: Travel) -> TravelId {
let entity_id = travel.entity_id.clone();
let travel_id = travel.id.clone();
self.positions.insert(
entity_id.clone(),
EntityPosition::Traveling(travel_id.clone()),
);
self.travels.insert(entity_id, travel);
travel_id
}
pub fn update_travels(&mut self, delta_time: f32) -> Vec<TravelCompleted> {
let mut completed = Vec::new();
for (entity_id, travel) in &mut self.travels {
if travel.status == TravelStatus::InProgress {
travel.update(delta_time);
if travel.status == TravelStatus::Arrived {
completed.push(TravelCompleted {
entity_id: entity_id.clone(),
travel_id: travel.id.clone(),
destination: travel.to.clone(),
});
}
}
}
for complete in &completed {
self.positions.insert(
complete.entity_id.clone(),
EntityPosition::AtLocation(complete.destination.clone()),
);
self.travel_history
.entry(complete.entity_id.clone())
.or_default()
.push(complete.destination.clone());
self.discovered_locations
.insert(complete.destination.clone());
self.travels.remove(&complete.entity_id);
}
completed
}
pub fn get_position(&self, entity_id: &EntityId) -> Option<&EntityPosition> {
self.positions.get(entity_id)
}
pub fn set_position(&mut self, entity_id: impl Into<String>, location_id: impl Into<String>) {
let entity = entity_id.into();
let location = location_id.into();
self.positions
.insert(entity.clone(), EntityPosition::AtLocation(location.clone()));
self.discovered_locations.insert(location.clone());
self.travel_history
.entry(entity)
.or_default()
.push(location);
}
pub fn get_travel(&self, entity_id: &EntityId) -> Option<&Travel> {
self.travels.get(entity_id)
}
pub fn get_travel_mut(&mut self, entity_id: &EntityId) -> Option<&mut Travel> {
self.travels.get_mut(entity_id)
}
pub fn travels(&self) -> &HashMap<EntityId, Travel> {
&self.travels
}
pub fn cancel_travel(&mut self, entity_id: &EntityId) -> Option<Travel> {
if let Some(mut travel) = self.travels.remove(entity_id) {
travel.status = TravelStatus::Cancelled;
self.positions.insert(
entity_id.clone(),
EntityPosition::AtLocation(travel.from.clone()),
);
Some(travel)
} else {
None
}
}
pub fn pause_travel(&mut self, entity_id: &EntityId) -> bool {
if let Some(travel) = self.travels.get_mut(entity_id) {
travel.pause();
true
} else {
false
}
}
pub fn resume_travel(&mut self, entity_id: &EntityId) -> bool {
if let Some(travel) = self.travels.get_mut(entity_id) {
travel.resume();
true
} else {
false
}
}
pub fn is_discovered(&self, location_id: &LocationId) -> bool {
self.discovered_locations.contains(location_id)
}
pub fn discover_location(&mut self, location_id: impl Into<String>) {
self.discovered_locations.insert(location_id.into());
}
pub fn discovered_locations(&self) -> &HashSet<LocationId> {
&self.discovered_locations
}
pub fn get_history(&self, entity_id: &EntityId) -> Option<&Vec<LocationId>> {
self.travel_history.get(entity_id)
}
pub fn clear(&mut self) {
self.travels.clear();
self.positions.clear();
self.discovered_locations.clear();
self.travel_history.clear();
}
pub fn active_travel_count(&self) -> usize {
self.travels.len()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum EntityPosition {
AtLocation(LocationId),
Traveling(TravelId),
}
#[derive(Clone, Debug)]
pub struct TravelCompleted {
pub entity_id: EntityId,
pub travel_id: TravelId,
pub destination: LocationId,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_state_new() {
let state = WorldMapState::new();
assert_eq!(state.active_travel_count(), 0);
assert_eq!(state.discovered_locations().len(), 0);
}
#[test]
fn test_start_travel() {
let mut state = WorldMapState::new();
let travel = Travel::new(
"travel_1", "player_1", "route_1", "city_a", "city_b", 100.0, 10.0,
);
let travel_id = state.start_travel(travel);
assert_eq!(travel_id, "travel_1");
assert_eq!(state.active_travel_count(), 1);
assert!(state.get_travel(&"player_1".to_string()).is_some());
}
#[test]
fn test_update_travels() {
let mut state = WorldMapState::new();
let travel = Travel::new(
"travel_1", "player_1", "route_1", "city_a", "city_b", 100.0,
10.0, );
state.start_travel(travel);
let completed = state.update_travels(5.0);
assert!(completed.is_empty());
assert_eq!(state.active_travel_count(), 1);
let completed = state.update_travels(5.0);
assert_eq!(completed.len(), 1);
assert_eq!(completed[0].entity_id, "player_1");
assert_eq!(completed[0].destination, "city_b");
assert_eq!(state.active_travel_count(), 0);
}
#[test]
fn test_set_position() {
let mut state = WorldMapState::new();
state.set_position("player_1", "city_a");
let position = state.get_position(&"player_1".to_string()).unwrap();
assert_eq!(position, &EntityPosition::AtLocation("city_a".to_string()));
assert!(state.is_discovered(&"city_a".to_string()));
}
#[test]
fn test_cancel_travel() {
let mut state = WorldMapState::new();
let travel = Travel::new(
"travel_1", "player_1", "route_1", "city_a", "city_b", 100.0, 10.0,
);
state.start_travel(travel);
assert_eq!(state.active_travel_count(), 1);
let cancelled = state.cancel_travel(&"player_1".to_string());
assert!(cancelled.is_some());
assert_eq!(cancelled.unwrap().status, TravelStatus::Cancelled);
assert_eq!(state.active_travel_count(), 0);
let position = state.get_position(&"player_1".to_string()).unwrap();
assert_eq!(position, &EntityPosition::AtLocation("city_a".to_string()));
}
#[test]
fn test_pause_resume_travel() {
let mut state = WorldMapState::new();
let travel = Travel::new(
"travel_1", "player_1", "route_1", "city_a", "city_b", 100.0, 10.0,
);
state.start_travel(travel);
assert!(state.pause_travel(&"player_1".to_string()));
let travel = state.get_travel(&"player_1".to_string()).unwrap();
assert_eq!(travel.status, TravelStatus::Paused);
assert!(state.resume_travel(&"player_1".to_string()));
let travel = state.get_travel(&"player_1".to_string()).unwrap();
assert_eq!(travel.status, TravelStatus::InProgress);
}
#[test]
fn test_travel_history() {
let mut state = WorldMapState::new();
state.set_position("player_1", "city_a");
let travel = Travel::new(
"travel_1", "player_1", "route_1", "city_a", "city_b", 100.0, 10.0,
);
state.start_travel(travel);
state.update_travels(10.0);
let history = state.get_history(&"player_1".to_string()).unwrap();
assert_eq!(history.len(), 2);
assert_eq!(history[0], "city_a");
assert_eq!(history[1], "city_b");
}
#[test]
fn test_discover_location() {
let mut state = WorldMapState::new();
assert!(!state.is_discovered(&"city_a".to_string()));
state.discover_location("city_a");
assert!(state.is_discovered(&"city_a".to_string()));
assert_eq!(state.discovered_locations().len(), 1);
}
#[test]
fn test_clear() {
let mut state = WorldMapState::new();
state.set_position("player_1", "city_a");
state.discover_location("city_b");
assert!(!state.positions.is_empty());
assert!(!state.discovered_locations.is_empty());
state.clear();
assert!(state.positions.is_empty());
assert!(state.discovered_locations.is_empty());
assert_eq!(state.active_travel_count(), 0);
}
}