use std::{collections::HashMap, hash::Hash};
#[derive(Clone, Debug, Default)]
pub(crate) struct EntityPriorityData {
pub(crate) accumulated: f32,
pub(crate) gain_override: Option<f32>,
pub(crate) last_sent_tick: Option<u32>,
}
pub struct EntityPriorityRef<'a, E: Copy + Eq + Hash> {
pub(crate) state: Option<&'a EntityPriorityData>,
pub(crate) entity: E,
}
impl<'a, E: Copy + Eq + Hash> EntityPriorityRef<'a, E> {
pub fn empty(entity: E) -> Self {
Self {
state: None,
entity,
}
}
pub fn entity(&self) -> E {
self.entity
}
pub fn accumulated(&self) -> f32 {
self.state.map(|s| s.accumulated).unwrap_or(0.0)
}
pub fn gain(&self) -> Option<f32> {
self.state.and_then(|s| s.gain_override)
}
pub fn is_overridden(&self) -> bool {
self.gain().is_some()
}
}
pub struct EntityPriorityMut<'a, E: Copy + Eq + Hash> {
pub(crate) entries: &'a mut HashMap<E, EntityPriorityData>,
pub(crate) entity: E,
}
impl<'a, E: Copy + Eq + Hash> EntityPriorityMut<'a, E> {
pub fn entity(&self) -> E {
self.entity
}
pub fn accumulated(&self) -> f32 {
self.entries
.get(&self.entity)
.map(|s| s.accumulated)
.unwrap_or(0.0)
}
pub fn gain(&self) -> Option<f32> {
self.entries
.get(&self.entity)
.and_then(|s| s.gain_override)
}
pub fn is_overridden(&self) -> bool {
self.gain().is_some()
}
pub fn set_gain(&mut self, gain: f32) -> &mut Self {
self.entries
.entry(self.entity)
.or_default()
.gain_override = Some(gain);
self
}
pub fn boost_once(&mut self, amount: f32) -> &mut Self {
self.entries
.entry(self.entity)
.or_default()
.accumulated += amount;
self
}
pub fn reset(&mut self) -> &mut Self {
if let Some(data) = self.entries.get_mut(&self.entity) {
data.gain_override = None;
}
self
}
}
#[cfg(test)]
mod tests {
use super::*;
fn fresh() -> HashMap<u32, EntityPriorityData> {
HashMap::new()
}
#[test]
fn set_gain_lazy_creates_entry() {
let mut entries = fresh();
let mut m = EntityPriorityMut {
entries: &mut entries,
entity: 7u32,
};
assert_eq!(m.gain(), None);
m.set_gain(5.0);
assert_eq!(m.gain(), Some(5.0));
assert!(m.is_overridden());
}
#[test]
fn set_gain_then_reset_returns_to_default() {
let mut entries = fresh();
let mut m = EntityPriorityMut {
entries: &mut entries,
entity: 7u32,
};
m.set_gain(5.0);
m.reset();
assert_eq!(m.gain(), None);
assert!(!m.is_overridden());
assert!(entries.contains_key(&7u32));
}
#[test]
fn boost_once_is_additive_and_preserves_gain() {
let mut entries = fresh();
let mut m = EntityPriorityMut {
entries: &mut entries,
entity: 7u32,
};
m.set_gain(3.0);
m.boost_once(10.0);
m.boost_once(5.0);
assert_eq!(m.accumulated(), 15.0);
assert_eq!(m.gain(), Some(3.0));
}
#[test]
fn boost_once_lazy_creates_entry() {
let mut entries = fresh();
let mut m = EntityPriorityMut {
entries: &mut entries,
entity: 7u32,
};
m.boost_once(4.0);
assert_eq!(m.accumulated(), 4.0);
assert_eq!(m.gain(), None);
}
#[test]
fn reset_on_absent_entry_is_noop() {
let mut entries = fresh();
let mut m = EntityPriorityMut {
entries: &mut entries,
entity: 7u32,
};
m.reset();
assert!(!entries.contains_key(&7u32));
}
#[test]
fn b_bdd_4_set_gain_then_reset_yields_default() {
let mut entries = fresh();
let mut m = EntityPriorityMut {
entries: &mut entries,
entity: 7u32,
};
m.set_gain(5.0);
assert!(m.is_overridden());
m.reset();
assert_eq!(m.gain(), None);
assert!(!m.is_overridden());
assert!(entries.contains_key(&7u32));
}
#[test]
fn b_bdd_5_boost_once_does_not_mutate_gain() {
let mut entries = fresh();
let mut m = EntityPriorityMut {
entries: &mut entries,
entity: 7u32,
};
m.set_gain(3.0);
m.boost_once(100.0);
assert_eq!(m.accumulated(), 100.0);
assert_eq!(m.gain(), Some(3.0));
}
#[test]
fn b_bdd_6_set_gain_persists_across_mutations() {
let mut entries = fresh();
{
let mut m = EntityPriorityMut {
entries: &mut entries,
entity: 7u32,
};
m.set_gain(5.0);
m.boost_once(10.0);
}
let m = EntityPriorityMut {
entries: &mut entries,
entity: 7u32,
};
assert_eq!(m.gain(), Some(5.0));
assert_eq!(m.accumulated(), 10.0);
}
#[test]
fn ref_reads_match_mut_reads() {
let mut entries = fresh();
{
let mut m = EntityPriorityMut {
entries: &mut entries,
entity: 7u32,
};
m.set_gain(2.0);
m.boost_once(7.0);
}
let r = EntityPriorityRef {
state: entries.get(&7u32),
entity: 7u32,
};
assert_eq!(r.gain(), Some(2.0));
assert_eq!(r.accumulated(), 7.0);
assert!(r.is_overridden());
}
#[test]
fn empty_ref_reads_defaults() {
let r = EntityPriorityRef::<u32>::empty(42);
assert_eq!(r.entity(), 42);
assert_eq!(r.gain(), None);
assert_eq!(r.accumulated(), 0.0);
assert!(!r.is_overridden());
}
}