1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
use derivative::*;
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
// Different properties of a player/item/entity

/// The definition of a stat.
/// A stat is a named float value optionally constrained between two other values and with a
/// default value. It is used to create effects, conditions and in general to hold state
/// for each entity.
/// For example, it can be used to contain the health or mana of an entity just as well as it
/// can be used to keep track of the number of enemies positioned around an entity.
#[derive(Debug, Clone, Serialize, Deserialize, new, Builder)]
pub struct StatDefinition<K> {
    /// The key.
    pub key: K,
    /// The name.
    pub name: String,
    /// The computer friendly name.
    pub friendly_name: String,
    /// The default value.
    pub default_value: f64,
    /// The minimum value.
    #[new(default)]
    pub min_value: Option<f64>,
    /// The maximum value.
    #[new(default)]
    pub max_value: Option<f64>,
    /// The icon of this stat.
    #[new(default)]
    pub icon_path: Option<String>,
}

impl<K: Clone> StatDefinition<K> {
    /// Creates the default StatInstance for this StatDefinition.
    pub fn default_instance(&self) -> StatInstance<K> {
        StatInstance::new(self.key.clone(), self.default_value)
    }
}

/// An instance of a stat.
/// Contains a base value as well as a value after applying the stat effectors.
#[derive(Debug, Clone, Serialize, Deserialize, new, Builder)]
pub struct StatInstance<K> {
    /// The key of the stat.
    pub key: K,
    /// The base value of the stat.
    pub value: f64,
    /// The value of this stat after applying the effectors.
    #[new(value = "value")]
    pub value_with_effectors: f64,
}

/// The definitions of all known stats.
#[derive(Debug, Clone, Serialize, Deserialize, new)]
pub struct StatDefinitions<K: Hash + Eq> {
    /// The definitions.
    pub defs: HashMap<K, StatDefinition<K>>,
}

impl<K: Hash + Eq> Default for StatDefinitions<K> {
    fn default() -> Self {
        Self {
            defs: HashMap::default(),
        }
    }
}

impl<K: Hash + Eq + Clone> StatDefinitions<K> {
    /// Converts the `StatDefinitions` into a `StatSet` using the default stat values.
    pub fn to_statset(&self) -> StatSet<K> {
        let instances = self
            .defs
            .iter()
            .map(|(k, v)| (k.clone(), v.default_instance()))
            .collect::<HashMap<_, _>>();
        StatSet::new(instances)
    }
}

impl<K: Hash + Eq + Clone> From<Vec<StatDefinition<K>>> for StatDefinitions<K> {
    fn from(t: Vec<StatDefinition<K>>) -> Self {
        let defs = t
            .into_iter()
            .map(|s| (s.key.clone(), s))
            .collect::<HashMap<_, _>>();
        Self::new(defs)
    }
}

/// Holds the definitions of the stat effectors.
#[derive(Debug, Clone, Serialize, Deserialize, new)]
pub struct EffectorDefinitions<K, E: Hash + Eq> {
    /// The definitions.
    pub defs: HashMap<E, EffectorDefinition<K, E>>,
}

impl<K, E: Hash + Eq> Default for EffectorDefinitions<K, E> {
    fn default() -> Self {
        Self {
            defs: HashMap::default(),
        }
    }
}

impl<K: Hash + Eq + Clone, E: Hash + Eq + Clone> From<Vec<EffectorDefinition<K, E>>>
    for EffectorDefinitions<K, E>
{
    fn from(t: Vec<EffectorDefinition<K, E>>) -> Self {
        let defs = t
            .into_iter()
            .map(|s| (s.key.clone(), s))
            .collect::<HashMap<_, _>>();
        Self::new(defs)
    }
}

/// Holds the instances of all the stats an entity has.
#[derive(Debug, Clone, Default, Serialize, Deserialize, new)]
pub struct StatSet<K: Hash + Eq> {
    /// The stats.
    pub stats: HashMap<K, StatInstance<K>>,
}

/// A collection of currently active effectors.
#[derive(Debug, Clone, Serialize, Deserialize, new)]
pub struct EffectorSet<E> {
    /// The active effectors.
    pub effectors: Vec<EffectorInstance<E>>,
}

impl<E> Default for EffectorSet<E> {
    fn default() -> Self {
        Self { effectors: vec![] }
    }
}

impl<E: Hash + Eq> EffectorSet<E> {
    /// Applies the effects of this effector to the provided `StatSet`.
    /// The delta time is used when using effectors that apply directly to
    /// the base stat value. (WIP)
    pub fn apply_to<K: Eq + Hash>(
        self: &Self,
        effector_defs: &EffectorDefinitions<K, E>,
        stat_set: &mut StatSet<K>,
        _delta_time: f32,
    ) {
        for mut s in stat_set.stats.values_mut() {
            let mut new_value = s.value;
            let mut multiplicative_multiplier = 1.0;
            let mut additive_multiplier = 0.0;
            let mut additive = 0.0;
            // find effectors affecting this stat
            for e in self.effectors.iter() {
                let def = effector_defs
                    .defs
                    .get(&e.effector_key)
                    .expect("Tried to get unknown stat key.");

                // Algo:
                // - Apply all multiplicative multipliers
                // - Apply all additive multipliers
                // - Apply all additives

                // look into the effect of each effector
                for (key, ty) in def.effects.iter() {
                    // if any matches
                    if *key == s.key {
                        // Apply Effector
                        match ty {
                            EffectorType::Additive(v) => additive += v,
                            EffectorType::AdditiveMultiplier(v) => additive_multiplier += v,
                            EffectorType::MultiplicativeMultiplier(v) => {
                                multiplicative_multiplier *= v
                            }
                        }
                    }
                }
            }
            let multiplier = multiplicative_multiplier + additive_multiplier;
            new_value += additive;
            new_value *= multiplier;
            s.value_with_effectors = new_value;
        }
    }
}

//impl<K: Hash+Eq> StatSet<K> {
//    pub fn update(&mut self, delta_time: f64, stat_set: &mut StatSet<K>) {
//        let mut rm_idx = vec![];
//        for (idx, stat) in self.effectors.iter_mut().enumerate() {
//            // TODO: apply modifier rules and ordering.
//
//            if let Some(left) = stat.disable_in.as_mut() {
//                *left -= delta_time;
//                if *left <= 0.0 {
//                    rm_idx.push(idx);
//                }
//            }
//        }
//
//        rm_idx.reverse();
//        for idx in rm_idx {
//            self.effectors.swap_remove(idx);
//        }
//    }
//}

/// Condition based on a stat to activate something.
#[derive(Clone, Debug, Serialize, Deserialize, new)]
pub struct StatCondition<K> {
    /// The key of the stat.
    pub stat_key: K,
    /// The type of condition.
    pub condition: StatConditionType,
}

impl<K: Hash + Eq + Debug> StatCondition<K> {
    /// Checks if this stat condition is met using for the provided `StatSet` using the known
    /// `StatDefinitions`.
    pub fn check(&self, stats: &StatSet<K>, stat_defs: &StatDefinitions<K>) -> bool {
        let v = stats.stats.get(&self.stat_key).expect(&format!(
            "Requested stat key {:?} is not in provided StatSet.",
            self.stat_key
        ));
        let def = stat_defs.defs.get(&self.stat_key).expect(&format!(
            "Requested stat key {:?} is not in provided StatDefinitions.",
            self.stat_key
        ));
        self.condition
            .is_true(v.value, def.min_value, def.max_value)
    }
}

/// A condition based on a stat's value.
#[derive(Clone, Serialize, Deserialize, new, Derivative)]
#[derivative(Debug)]
pub enum StatConditionType {
    /// The stat value must be higher or equal to this value.
    MinValue(f64),
    /// The stat value must be between these values.
    BetweenValue(f64, f64),
    /// The stat value must be lower or equal to this value.
    MaxValue(f64),
    /// The minimum progress of the value between its minimum and maximum.
    /// This calculates the distance between the minimum and maximum values, then assigns
    /// a value between 0.0 and 1.0 that correspond to the absolute distance from the minimum.
    /// If the minimum value is 10 and the maximum is 20 and we have a value of 15, then this
    /// corresponds to a "distance" of 0.5 (50%!) of the way between 10 and 20.
    MinPercent(f64),
    /// The minimum progress of the value between its minimum and maximum.
    /// This calculates the distance between the minimum and maximum values, then assigns
    /// a value between 0.0 and 1.0 that correspond to the absolute distance from the minimum.
    /// If the minimum value is 10 and the maximum is 20 and we have a value of 15, then this
    /// corresponds to a "distance" of 0.5 (50%!) of the way between 10 and 20.
    BetweenPercent(f64, f64),
    /// The minimum progress of the value between its minimum and maximum.
    /// This calculates the distance between the minimum and maximum values, then assigns
    /// a value between 0.0 and 1.0 that correspond to the absolute distance from the minimum.
    /// If the minimum value is 10 and the maximum is 20 and we have a value of 15, then this
    /// corresponds to a "distance" of 0.5 (50%!) of the way between 10 and 20.
    MaxPercent(f64),
    /// The value is divisible by this value.
    /// DivisibleBy(2) is equivalent to (value % 2 == 0).
    DivisibleBy(i32),
    /// A custom function that takes the value and returns whether the condition passed or not.
    #[serde(skip)]
    //Custom(#[derivative(Debug = "ignore")] std::sync::Arc<Box<dyn Fn(f64) -> bool>>),
    Custom(#[derivative(Debug = "ignore")] fn(f64) -> bool),
}

impl StatConditionType {
    /// Checks if the condition is true using the actual value, as well as the minimum and maximum
    /// values of the stat (found in the `StatDefinition`).
    pub fn is_true(&self, value: f64, min_value: Option<f64>, max_value: Option<f64>) -> bool {
        let percent = if let (Some(min_value), Some(max_value)) = (min_value, max_value) {
            Some((value - min_value) / (max_value - min_value))
        } else {
            None
        };
        match &*self {
            StatConditionType::MinValue(v) => value >= *v,
            StatConditionType::BetweenValue(min, max) => value >= *min && value <= *max,
            StatConditionType::MaxValue(v) => value <= *v,
            StatConditionType::MinPercent(p) => {
                percent.expect("This stat doesn't have min/max values.") >= *p
            }
            StatConditionType::BetweenPercent(min, max) => {
                percent.expect("This stat doesn't have min/max values.") >= *min
                    && percent.expect("This stat doesn't have min/max values.") <= *max
            }
            StatConditionType::MaxPercent(p) => {
                percent.expect("This stat doesn't have min/max values.") <= *p
            }
            StatConditionType::DivisibleBy(p) => value as i32 % p == 0,
            StatConditionType::Custom(e) => e(value),
        }
    }
}

/// The definition of a stat effector.
/// This modifies temporarily the value of a stat.
#[derive(Debug, Clone, Serialize, Deserialize, new)]
pub struct EffectorDefinition<K, E> {
    /// The key of the effector.
    pub key: E,
    /// The duration of the effector.
    /// None means that it does not expire.
    /// Some(0) means that it is applied only once.
    /// Some(n) means that it is applied for n seconds.
    pub duration: Option<f64>,
    /// The effects that cause this effector.
    /// Note that effectors can only cause effects on a single stat.
    /// To affect multiple stats, create multiple effectors.
    // TODO consider using only a single element here? It almost never happens that
    // we want to apply multiple changes to the same stat.
    pub effects: Vec<(K, EffectorType)>,
}

/// The way this effector modifies the stat.
#[derive(Debug, Clone, Serialize, Deserialize, new)]
pub enum EffectorType {
    /// Adds a value to the base value of the stat.
    Additive(f64),
    /// Multiplies the stat by a value.
    /// Stacks additively with other multipliers affecting this same stat.
    AdditiveMultiplier(f64),
    /// Multiplies the stat by a value.
    /// Stacks multiplicatively with other multipliers affecting this same stat.
    MultiplicativeMultiplier(f64),
}

/// An active instance of an effector.
#[derive(Debug, Clone, Serialize, Deserialize, new)]
pub struct EffectorInstance<E> {
    /// The key of the effector.
    pub effector_key: E,
    /// The time before this effector expires.
    pub disable_in: Option<f64>,
}