all_is_cubes/character/
spawn.rs1use alloc::vec::Vec;
2
3use euclid::{Point3D, Vector3D};
4
5use crate::camera::eye_for_look_at;
6use crate::inv::Slot;
7use crate::math::{Cube, Face6, FreeCoordinate, FreePoint, FreeVector, GridAab, NotNan};
8#[cfg(feature = "save")]
9use crate::save::schema;
10use crate::universe::{HandleVisitor, VisitHandles};
11
12#[cfg(doc)]
13use crate::space::Space;
14
15#[doc = include_str!("../save/serde-warning.md")]
20#[derive(Clone, Debug, Eq, PartialEq)]
23#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
24pub struct Spawn {
25 pub(super) bounds: GridAab,
27
28 pub(super) eye_position: Option<Point3D<NotNan<FreeCoordinate>, Cube>>,
30
31 pub(super) look_direction: Vector3D<NotNan<FreeCoordinate>, Cube>,
36
37 pub(super) inventory: Vec<Slot>,
39}
40
41impl Spawn {
42 pub fn default_for_new_space(bounds: GridAab) -> Self {
49 Spawn {
50 bounds: bounds.abut(Face6::PZ, 40).unwrap_or(bounds),
51 eye_position: None,
52 look_direction: Vector3D::new(NotNan::from(0), NotNan::from(0), NotNan::from(-1)),
53 inventory: vec![],
54 }
55 }
56
57 #[doc(hidden)]
65 pub fn looking_at_space(space_bounds: GridAab, direction: impl Into<FreeVector>) -> Self {
66 let direction = direction.into();
67 let mut spawn = Self::default_for_new_space(space_bounds);
68 spawn.set_eye_position(eye_for_look_at(space_bounds, direction));
69 spawn.set_look_direction(-direction);
70 spawn
71 }
72
73 pub fn set_eye_position(&mut self, position: impl Into<FreePoint>) {
75 self.eye_position = Some(position.into().map(notnan_or_zero));
78 }
79
80 pub fn set_bounds(&mut self, bounds: GridAab) {
82 self.bounds = bounds;
83 }
84
85 pub fn set_look_direction(&mut self, direction: impl Into<FreeVector>) {
89 self.look_direction = direction.into().map(notnan_or_zero);
90 }
91
92 pub fn set_inventory(&mut self, inventory: Vec<Slot>) {
94 self.inventory = inventory;
95 }
96}
97
98fn notnan_or_zero(value: FreeCoordinate) -> NotNan<FreeCoordinate> {
99 NotNan::new(value).unwrap_or_else(|_| NotNan::from(0))
100}
101
102impl VisitHandles for Spawn {
103 fn visit_handles(&self, visitor: &mut dyn HandleVisitor) {
104 let Self {
105 inventory,
106 bounds: _,
107 eye_position: _,
108 look_direction: _,
109 } = self;
110 inventory.visit_handles(visitor);
111 }
112}
113
114#[cfg(feature = "save")]
115impl serde::Serialize for Spawn {
116 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
117 where
118 S: serde::Serializer,
119 {
120 let Spawn {
121 bounds,
122 eye_position,
123 look_direction,
124 ref inventory,
125 } = *self;
126
127 schema::SpawnSer::SpawnV1 {
128 bounds,
129 eye_position: eye_position.map(|p| p.into()),
130 look_direction: look_direction.into(),
131 inventory: inventory.iter().map(|slot| slot.into()).collect(),
132 }
133 .serialize(serializer)
134 }
135}
136
137#[cfg(feature = "save")]
138impl<'de> serde::Deserialize<'de> for Spawn {
139 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
140 where
141 D: serde::Deserializer<'de>,
142 {
143 match schema::SpawnSer::deserialize(deserializer)? {
144 schema::SpawnSer::SpawnV1 {
145 bounds,
146 eye_position,
147 look_direction,
148 inventory,
149 } => Ok(Spawn {
150 bounds,
151 eye_position: eye_position.map(|p| p.into()),
152 look_direction: look_direction.into(),
153 inventory: inventory.into_iter().map(|slot| slot.into()).collect(),
154 }),
155 }
156 }
157}