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}