lib_efo/entity/implementations/accesors.rs
1//! All field accesors for entities
2use crate::{
3 entity::{Entity, *},
4 stats::{self, Symbols},
5};
6use log::debug;
7use rand::prelude::IndexedRandom;
8use rand::prelude::IteratorRandom;
9use std::ops::Add;
10
11impl Entity {
12 /// Change one of the entity's statistics.
13 ///
14 /// The stat identified by `pair` is replaced with the new value specified in
15 /// the `StatPair`. This is a direct overwrite and does not apply any delta.
16 ///
17 /// # Arguments
18 /// * `pair` - The stat pair containing the key and new value.
19 ///
20 pub fn change_stat_strict(&mut self, pair: stats::StatPair, delta: stats::Modification) {
21 implementations::overwrite_stat(&mut self.stats, pair, delta);
22 }
23
24 /// Mutate a statistic in a “fuzzy” way.
25 ///
26 /// This method behaves like `change_stat_strict` for ordinary stats, but
27 /// for *meta-stats* it applies a random modification instead of an
28 /// outright overwrite.
29 ///
30 /// # Parameters
31 /// * `pair` - a `StatPair` that contains the target statistic and the
32 /// value that should be used for the fuzzy change.
33 pub fn change_stat_fuzzy(&mut self, pair: stats::StatPair, delta: stats::Modification) {
34 let stats_demo = [
35 stats::ListStats::Population,
36 stats::ListStats::Growthrate,
37 stats::ListStats::Crime,
38 stats::ListStats::Happiness,
39 ];
40 let stats_eco = [
41 stats::ListStats::Food,
42 stats::ListStats::Industry,
43 stats::ListStats::Science,
44 stats::ListStats::Trade,
45 ];
46 let stats_soc = [
47 stats::ListStats::Culture,
48 stats::ListStats::Education,
49 stats::ListStats::Tourism,
50 stats::ListStats::Influence,
51 ];
52 let stats_mil = [
53 stats::ListStats::Army,
54 stats::ListStats::Strength,
55 stats::ListStats::Fortification,
56 stats::ListStats::Threat,
57 ];
58 let stats_inf = [
59 stats::ListStats::Bureaucracy,
60 stats::ListStats::Transportation,
61 stats::ListStats::Sprawl,
62 stats::ListStats::Health,
63 ];
64
65 match pair.stat {
66 stats::ListStats::Demographics => {
67 self.change_random_stats_by(&stats_demo, pair.value, delta)
68 }
69 stats::ListStats::Economy => self.change_random_stats_by(&stats_eco, pair.value, delta),
70 stats::ListStats::Social => self.change_random_stats_by(&stats_soc, pair.value, delta),
71 stats::ListStats::Military => {
72 self.change_random_stats_by(&stats_mil, pair.value, delta)
73 }
74 stats::ListStats::Infrastructure => {
75 self.change_random_stats_by(&stats_inf, pair.value, delta)
76 }
77
78 _ => self.change_stat_strict(pair, delta),
79 }
80 }
81
82 /// Mutates a statistic in a “fuzzy” manner.
83 ///
84 /// For ordinary statistics this behaves like `change_stat_strict` and
85 /// simply assigns the supplied value. For *meta-stats* the function
86 /// chooses a random subset of the associated sub-statistics and
87 /// distributes the requested change evenly among them. The selection
88 /// is performed using a thread-local RNG.
89 pub(crate) fn change_random_stats_by(
90 &mut self,
91 stats_demo: &[stats::ListStats],
92 amount: stats::GenericStat,
93 delta: stats::Modification,
94 ) {
95 let mut r = rand::rng();
96 let nb = (1..stats_demo.len() + 1).choose(&mut r).unwrap_or(1);
97 let stats = stats_demo.choose_multiple(&mut r, nb);
98 let (a, b) = (*amount / 2, *amount);
99 let (a, b) = (std::cmp::min(a, b), std::cmp::max(a, b));
100 let mut value = || stats::GenericStat((a..b).choose(&mut r).unwrap_or(1));
101 stats.for_each(|stat| {
102 self.change_stat_strict(
103 stats::StatPair {
104 stat: *stat,
105 value: value(),
106 },
107 delta,
108 );
109 });
110 }
111 /// Change the entity's duration.
112 ///
113 /// This simply replaces the current `EntityDuration` with the one supplied.
114 /// No validation or transformation is performed; it is a direct overwrite.
115 ///
116 /// # Arguments
117 /// * `duration` - The new `EntityDuration` to assign to the entity.
118 ///
119 pub fn change_duration(&mut self, duration: EntityDuration) {
120 self.duration = duration
121 }
122
123 /// Change the entity's description.
124 ///
125 /// Replaces the current description string with the new one supplied.
126 /// This method performs a direct overwrite; the previous description is dropped.
127 ///
128 /// # Arguments
129 /// * `descritption` - The new description string for the entity.
130 ///
131 pub fn change_description(&mut self, descritption: String) {
132 self.description = descritption
133 }
134
135 /// Apply a delta to all of the entity's statistics.
136 ///
137 /// The delta specified by `pair` is added to each of the statistics in
138 /// `self.stats`. Positive values increase the stat, negative values
139 /// decrease it.
140 ///
141 /// # Arguments
142 /// * `pair` - The stat pair representing the change to apply.
143 #[deprecated]
144 pub fn delta_stat(&mut self, pair: stats::StatPair) {
145 implementations::overwrite_stat(&mut self.stats, pair, stats::Modification::Delta);
146 }
147
148 /// Replace the entity's duration.
149 ///
150 /// The method simply overwrites the current `duration` field with the
151 /// supplied value. It is primarily intended for use when an entity
152 /// is instantiated from external data that already contains a duration value.
153 pub fn modify_duration(&mut self, duration: EntityDuration) {
154 self.duration = duration
155 }
156
157 /// Set the way the entity's effects are applied.
158 ///
159 /// The field `effect_nature` determines how the entity's abilities or
160 /// status effects interact with the world.
161 /// This method overwrites the existing value with the supplied `EffectApplication`.
162 pub fn modify_effect_application(&mut self, effect: EffectApplication) {
163 self.effect_nature = effect
164 }
165
166 /// Update the bounds that govern the entity's effects.
167 ///
168 /// `effect_bounds` controls the maximum/minimum strength, duration or
169 /// other limits that can be applied to the entity's effects. This
170 /// method simply replaces the current bounds with the supplied
171 /// `EffectBounds`.
172 pub fn modify_effect_bounds(&mut self, effect: EffectBounds) {
173 self.effect_bounds = effect
174 }
175
176 /// Sets the entity's position using a world-space `Position`.
177 ///
178 /// This method updates the internal metadata to the specified
179 /// world coordinates.
180 pub fn modify_position(&mut self, pos: stats::Position) {
181 self.metadata.position = pos;
182 }
183
184 /// Sets the entity's faction identifier.
185 ///
186 /// This method updates the internal metadata to reflect the provided
187 /// `faction`. A `None` value indicates that the entity is not
188 /// affiliated with any faction.
189 pub fn modify_faction(&mut self, faction: Option<stats::FactionId>) {
190 self.metadata.faction = faction;
191 }
192
193 /// Sets the entity's reach description.
194 ///
195 /// The `reach` string typically contains information such as the
196 /// distance or area the entity can affect. This method replaces
197 /// the current reach value stored in the entity's metadata.
198 pub fn modify_reach(&mut self, reach: stats::Reach) {
199 self.metadata.reach = reach;
200 }
201
202 /// Sets the entity's position using a grid-space `PositionGrid`.
203 ///
204 /// The supplied grid coordinates are converted to world space
205 /// before being stored.
206 pub fn modify_position_grid(&mut self, pos: stats::PositionGrid) {
207 self.metadata.position = pos.to_position();
208 }
209
210 /// Changes the entity's description text.
211 pub fn modify_description(&mut self, descritption: String) {
212 self.description = descritption;
213 }
214 /// Sets the entity's prestige stat to an absolute value.
215 ///
216 /// The prestige value is stored directly in the entity's metadata
217 /// and overwrites any previous value.
218 pub fn modify_prestige(&mut self, prestige: stats::GenericStat, delta: stats::Modification) {
219 match delta {
220 stats::Modification::Delta => {
221 self.metadata.prestige = self.metadata.prestige.add(prestige)
222 }
223 stats::Modification::Replace => self.metadata.prestige = prestige,
224 stats::Modification::Delete => self.metadata.prestige = stats::GenericStat(0),
225 }
226 }
227
228 /// Add a governor to the entity if the id exists and return the old governor if it existed
229 pub fn change_governor(&mut self, governor: stats::CharacterId) -> Option<stats::CharacterId> {
230 debug!("Swapping the governor with {}", governor.to_symbol());
231 self.metadata.governor.replace(governor)
232 }
233 /// Remove the entity's governor and return it
234 pub fn remove_governor(&mut self) -> Option<stats::CharacterId> {
235 self.metadata.governor.take()
236 }
237
238 /// Add a governor to the entity if the id exists
239 pub fn change_capital(&mut self, capital: stats::TownId) -> Option<stats::TownId> {
240 debug!("Swapping the capital with {}", capital.to_symbol());
241 self.metadata.capital.replace(capital)
242 }
243 /// Remove the entity's governor and return it
244 pub fn remove_capital(&mut self) -> Option<stats::TownId> {
245 self.metadata.capital.take()
246 }
247
248 /// Returns a **read-only reference** to the entity's current position.
249 pub fn position(&self) -> &stats::Position {
250 &self.metadata.position
251 }
252
253 /// Defer the update of all stats
254 pub fn update_metastats(&mut self) {
255 self.stats.update();
256 }
257
258 /// Returns whether the entity match an optionnal identifier
259 pub fn match_identifier(&self, entity_identifier: &Option<EntityIdentifier>) -> bool {
260 match entity_identifier {
261 Some(EntityIdentifier::Name(name)) => {
262 self.name.to_lowercase().starts_with(&name.to_lowercase())
263 }
264 Some(EntityIdentifier::Id(id)) => self.id.hyphenated().to_string().starts_with(id),
265 None => false,
266 }
267 }
268}