1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::str::FromStr;
4
5use crate::address::GalacticAddress;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9#[cfg_attr(
10 feature = "archive",
11 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
12)]
13#[non_exhaustive]
14pub enum BaseType {
15 HomePlanetBase,
16 FreighterBase,
17 ExternalPlanetBase,
18}
19
20impl fmt::Display for BaseType {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 match self {
23 Self::HomePlanetBase => write!(f, "HomePlanetBase"),
24 Self::FreighterBase => write!(f, "FreighterBase"),
25 Self::ExternalPlanetBase => write!(f, "ExternalPlanetBase"),
26 }
27 }
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct BaseTypeParseError(pub String);
33
34impl fmt::Display for BaseTypeParseError {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 write!(f, "unknown base type: {}", self.0)
37 }
38}
39
40impl std::error::Error for BaseTypeParseError {}
41
42impl FromStr for BaseType {
43 type Err = BaseTypeParseError;
44
45 fn from_str(s: &str) -> Result<Self, Self::Err> {
46 match s.to_lowercase().as_str() {
47 "homeplanetbase" | "home" => Ok(Self::HomePlanetBase),
48 "freighterbase" | "freighter" => Ok(Self::FreighterBase),
49 "externalplanetbase" | "external" => Ok(Self::ExternalPlanetBase),
50 _ => Err(BaseTypeParseError(s.to_string())),
51 }
52 }
53}
54
55#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
59#[cfg_attr(
60 feature = "archive",
61 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
62)]
63#[non_exhaustive]
64pub struct PlayerBase {
65 pub name: String,
66 pub base_type: BaseType,
67 pub address: GalacticAddress,
68 pub position: [f32; 3],
70 pub owner_uid: Option<String>,
72}
73
74impl PlayerBase {
75 pub fn new(
76 name: String,
77 base_type: BaseType,
78 address: GalacticAddress,
79 position: [f32; 3],
80 owner_uid: Option<String>,
81 ) -> Self {
82 Self {
83 name,
84 base_type,
85 address,
86 position,
87 owner_uid,
88 }
89 }
90
91 pub fn reality_index(&self) -> u8 {
93 self.address.reality_index
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
99#[cfg_attr(
100 feature = "archive",
101 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
102)]
103#[non_exhaustive]
104pub struct PlayerState {
105 pub current_address: GalacticAddress,
106 pub current_reality: u8,
107 pub previous_address: Option<GalacticAddress>,
108 pub freighter_address: Option<GalacticAddress>,
109 pub units: u64,
110 pub nanites: u64,
111 pub quicksilver: u64,
112}
113
114impl PlayerState {
115 pub fn new(
116 current_address: GalacticAddress,
117 current_reality: u8,
118 previous_address: Option<GalacticAddress>,
119 freighter_address: Option<GalacticAddress>,
120 units: u64,
121 nanites: u64,
122 quicksilver: u64,
123 ) -> Self {
124 Self {
125 current_address,
126 current_reality,
127 previous_address,
128 freighter_address,
129 units,
130 nanites,
131 quicksilver,
132 }
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn base_type_display_fromstr_roundtrip() {
142 for bt in [
143 BaseType::HomePlanetBase,
144 BaseType::FreighterBase,
145 BaseType::ExternalPlanetBase,
146 ] {
147 let s = bt.to_string();
148 let parsed: BaseType = s.parse().unwrap();
149 assert_eq!(bt, parsed);
150 }
151 }
152
153 #[test]
154 fn base_type_short_names() {
155 assert_eq!(
156 "home".parse::<BaseType>().unwrap(),
157 BaseType::HomePlanetBase
158 );
159 assert_eq!(
160 "freighter".parse::<BaseType>().unwrap(),
161 BaseType::FreighterBase
162 );
163 assert_eq!(
164 "external".parse::<BaseType>().unwrap(),
165 BaseType::ExternalPlanetBase
166 );
167 }
168
169 #[test]
170 fn player_base_reality_index() {
171 let addr = GalacticAddress::new(0, 0, 0, 0, 0, 7);
172 let base = PlayerBase::new(
173 "My Base".into(),
174 BaseType::HomePlanetBase,
175 addr,
176 [1.0, 2.0, 3.0],
177 None,
178 );
179 assert_eq!(base.reality_index(), 7);
180 }
181
182 #[test]
183 fn player_state_constructor() {
184 let addr = GalacticAddress::new(0, 0, 0, 0, 0, 0);
185 let state = PlayerState::new(addr, 0, None, None, 1_000_000, 5000, 200);
186 assert_eq!(state.units, 1_000_000);
187 assert_eq!(state.nanites, 5000);
188 assert_eq!(state.quicksilver, 200);
189 }
190}