use crate::state::StateValue;
use crate::types::Duration;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Needs {
fatigue: StateValue,
stress: StateValue,
purpose: StateValue,
}
impl Needs {
const FATIGUE_DECAY_HALF_LIFE: Duration = Duration::hours(8);
const STRESS_DECAY_HALF_LIFE: Duration = Duration::hours(12);
const PURPOSE_DECAY_HALF_LIFE: Duration = Duration::days(3);
#[must_use]
pub fn new() -> Self {
Needs {
fatigue: StateValue::new(0.2)
.with_bounds(0.0, 1.0)
.with_decay_half_life(Self::FATIGUE_DECAY_HALF_LIFE),
stress: StateValue::new(0.2)
.with_bounds(0.0, 1.0)
.with_decay_half_life(Self::STRESS_DECAY_HALF_LIFE),
purpose: StateValue::new(0.7)
.with_bounds(0.0, 1.0)
.with_decay_half_life(Self::PURPOSE_DECAY_HALF_LIFE),
}
}
#[must_use]
pub fn with_fatigue_base(mut self, value: f32) -> Self {
self.fatigue.set_base(value);
self
}
#[must_use]
pub fn with_stress_base(mut self, value: f32) -> Self {
self.stress.set_base(value);
self
}
#[must_use]
pub fn with_purpose_base(mut self, value: f32) -> Self {
self.purpose.set_base(value);
self
}
#[must_use]
pub fn fatigue_effective(&self) -> f32 {
self.fatigue.effective()
}
#[must_use]
pub fn stress_effective(&self) -> f32 {
self.stress.effective()
}
#[must_use]
pub fn purpose_effective(&self) -> f32 {
self.purpose.effective()
}
#[must_use]
pub fn fatigue_base(&self) -> f32 {
self.fatigue.base()
}
#[must_use]
pub fn stress_base(&self) -> f32 {
self.stress.base()
}
#[must_use]
pub fn purpose_base(&self) -> f32 {
self.purpose.base()
}
#[must_use]
pub fn fatigue(&self) -> &StateValue {
&self.fatigue
}
#[must_use]
pub fn stress(&self) -> &StateValue {
&self.stress
}
#[must_use]
pub fn purpose(&self) -> &StateValue {
&self.purpose
}
pub fn fatigue_mut(&mut self) -> &mut StateValue {
&mut self.fatigue
}
pub fn stress_mut(&mut self) -> &mut StateValue {
&mut self.stress
}
pub fn purpose_mut(&mut self) -> &mut StateValue {
&mut self.purpose
}
pub fn add_fatigue_delta(&mut self, amount: f32) {
self.fatigue.add_delta(amount);
}
pub fn add_stress_delta(&mut self, amount: f32) {
self.stress.add_delta(amount);
}
pub fn add_purpose_delta(&mut self, amount: f32) {
self.purpose.add_delta(amount);
}
pub fn apply_decay(&mut self, elapsed: Duration) {
self.fatigue.apply_decay(elapsed);
self.stress.apply_decay(elapsed);
self.purpose.apply_decay(elapsed);
}
pub fn reset_deltas(&mut self) {
self.fatigue.reset_delta();
self.stress.reset_delta();
self.purpose.reset_delta();
}
}
impl Default for Needs {
fn default() -> Self {
Needs::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn needs_contains_fatigue_stress() {
let needs = Needs::new();
let _ = needs.fatigue_effective();
let _ = needs.stress_effective();
}
#[test]
fn purpose_is_need_dimension() {
let needs = Needs::new();
let _ = needs.purpose_effective();
}
#[test]
fn new_creates_healthy_defaults() {
let needs = Needs::new();
assert!(needs.fatigue_effective() < 0.5);
assert!(needs.stress_effective() < 0.5);
assert!(needs.purpose_effective() > 0.5);
}
#[test]
fn builder_methods_set_base_values() {
let needs = Needs::new()
.with_fatigue_base(0.5)
.with_stress_base(0.6)
.with_purpose_base(0.2);
assert!((needs.fatigue_base() - 0.5).abs() < f32::EPSILON);
assert!((needs.stress_base() - 0.6).abs() < f32::EPSILON);
assert!((needs.purpose_base() - 0.2).abs() < f32::EPSILON);
}
#[test]
fn add_delta_modifies_effective() {
let mut needs = Needs::new();
let original = needs.stress_effective();
needs.add_stress_delta(0.3);
assert!((needs.stress_effective() - (original + 0.3)).abs() < f32::EPSILON);
}
#[test]
fn fatigue_decays_over_time() {
let mut needs = Needs::new();
needs.add_fatigue_delta(0.8);
needs.apply_decay(Duration::hours(8));
assert!((needs.fatigue().delta() - 0.4).abs() < 0.01);
}
#[test]
fn stress_decays_over_time() {
let mut needs = Needs::new();
needs.add_stress_delta(0.6);
needs.apply_decay(Duration::hours(12));
assert!((needs.stress().delta() - 0.3).abs() < 0.01);
}
#[test]
fn purpose_decays_over_time() {
let mut needs = Needs::new();
needs.add_purpose_delta(-0.4);
needs.apply_decay(Duration::days(3));
assert!((needs.purpose().delta() - (-0.2)).abs() < 0.01);
}
#[test]
fn reset_deltas_clears_all() {
let mut needs = Needs::new();
needs.add_fatigue_delta(0.5);
needs.add_stress_delta(0.3);
needs.add_purpose_delta(0.2);
needs.reset_deltas();
assert!(needs.fatigue().delta().abs() < f32::EPSILON);
assert!(needs.stress().delta().abs() < f32::EPSILON);
assert!(needs.purpose().delta().abs() < f32::EPSILON);
}
#[test]
fn mutable_references_work() {
let mut needs = Needs::new();
needs.fatigue_mut().add_delta(0.2);
assert!((needs.fatigue().delta() - 0.2).abs() < f32::EPSILON);
}
#[test]
fn default_is_new() {
let needs = Needs::default();
assert!(needs.stress_effective() < 0.5);
}
#[test]
fn clone_and_equality() {
let needs1 = Needs::new().with_stress_base(0.5);
let needs2 = needs1.clone();
assert_eq!(needs1, needs2);
}
#[test]
fn debug_format() {
let needs = Needs::new();
let debug = format!("{:?}", needs);
assert!(debug.contains("Needs"));
}
#[test]
fn all_delta_modifiers() {
let mut n = Needs::new();
n.add_fatigue_delta(0.1);
assert!((n.fatigue().delta() - 0.1).abs() < f32::EPSILON);
n.add_stress_delta(-0.1);
assert!((n.stress().delta() - (-0.1)).abs() < f32::EPSILON);
n.add_purpose_delta(0.2);
assert!((n.purpose().delta() - 0.2).abs() < f32::EPSILON);
}
#[test]
fn all_mutable_refs() {
let mut n = Needs::new();
n.fatigue_mut().add_delta(0.05);
n.stress_mut().add_delta(0.06);
n.purpose_mut().add_delta(-0.1);
assert!((n.fatigue().delta() - 0.05).abs() < f32::EPSILON);
assert!((n.stress().delta() - 0.06).abs() < f32::EPSILON);
assert!((n.purpose().delta() - (-0.1)).abs() < f32::EPSILON);
}
#[test]
fn all_base_accessors() {
let n = Needs::new();
let _ = n.fatigue_base();
let _ = n.stress_base();
let _ = n.purpose_base();
}
}