use crate::state::IndividualState;
use crate::types::Duration;
#[must_use]
pub(crate) fn advance_state(state: IndividualState, duration: Duration) -> IndividualState {
let mut new_state = state;
new_state.apply_decay(duration);
new_state
}
#[must_use]
pub(crate) fn regress_state(state: IndividualState, duration: Duration) -> IndividualState {
let mut new_state = state;
reverse_decay_on_state(&mut new_state, duration);
new_state
}
fn reverse_decay_on_state(state: &mut IndividualState, duration: Duration) {
reverse_dimension_decay(state.mood_mut().valence_mut(), duration, Duration::hours(6));
reverse_dimension_decay(state.mood_mut().arousal_mut(), duration, Duration::hours(6));
reverse_dimension_decay(
state.mood_mut().dominance_mut(),
duration,
Duration::hours(6),
);
reverse_dimension_decay(
state.social_cognition_mut().loneliness_mut(),
duration,
Duration::days(1),
);
reverse_dimension_decay(
state
.social_cognition_mut()
.perceived_reciprocal_caring_mut(),
duration,
Duration::days(2),
);
reverse_dimension_decay(
state.social_cognition_mut().perceived_liability_mut(),
duration,
Duration::days(3),
);
reverse_dimension_decay(
state.social_cognition_mut().self_hate_mut(),
duration,
Duration::days(3),
);
reverse_dimension_decay(
state.needs_mut().stress_mut(),
duration,
Duration::hours(12),
);
reverse_dimension_decay(
state.needs_mut().fatigue_mut(),
duration,
Duration::hours(8),
);
reverse_dimension_decay(state.needs_mut().purpose_mut(), duration, Duration::days(3));
reverse_dimension_decay(
state.mental_health_mut().depression_mut(),
duration,
Duration::weeks(2),
);
reverse_dimension_decay(
state.mental_health_mut().interpersonal_hopelessness_mut(),
duration,
Duration::weeks(2),
);
reverse_dimension_decay(
state.disposition_mut().empathy_mut(),
duration,
Duration::weeks(4),
);
reverse_dimension_decay(
state.disposition_mut().aggression_mut(),
duration,
Duration::weeks(1),
);
reverse_dimension_decay(
state.disposition_mut().grievance_mut(),
duration,
Duration::weeks(1),
);
reverse_dimension_decay(
state.person_characteristics_mut().social_capital_mut(),
duration,
Duration::weeks(4),
);
}
fn reverse_dimension_decay(
state_value: &mut crate::state::StateValue,
duration: Duration,
half_life: Duration,
) {
let current_delta = state_value.delta();
if current_delta.abs() < f32::EPSILON {
return;
}
let elapsed_ms = duration.as_millis() as f64;
let half_life_ms = half_life.as_millis() as f64;
if half_life_ms <= 0.0 {
return;
}
let ln2 = std::f64::consts::LN_2;
let exponent = ln2 * elapsed_ms / half_life_ms;
if exponent > 700.0 {
return;
}
let reversal_factor = exponent.exp();
let original_delta = (current_delta as f64) * reversal_factor;
let clamped = original_delta.clamp(-100.0, 100.0) as f32;
state_value.set_delta(clamped);
}
#[must_use]
pub(crate) fn apply_interpreted_event_to_state(
state: IndividualState,
interpreted: &crate::processor::InterpretedEvent,
) -> IndividualState {
let mut new_state = state;
if let Some(deltas) = &interpreted.spec_deltas {
apply_permanent_deltas_to_state(&mut new_state, deltas);
apply_acute_deltas_to_state(&mut new_state, deltas);
apply_chronic_deltas_to_state(&mut new_state, deltas);
}
new_state
}
fn apply_permanent_deltas_to_state(
state: &mut IndividualState,
deltas: &crate::event::AppliedDeltas,
) {
let p = &deltas.permanent;
state.mood_mut().shift_valence_base(p.valence);
state.mood_mut().shift_arousal_base(p.arousal);
state.mood_mut().shift_dominance_base(p.dominance);
state
.social_cognition_mut()
.loneliness_mut()
.shift_base(p.loneliness);
state
.social_cognition_mut()
.perceived_reciprocal_caring_mut()
.shift_base(p.prc);
state
.social_cognition_mut()
.perceived_competence_mut()
.shift_base(p.perceived_competence);
state
.social_cognition_mut()
.perceived_liability_mut()
.shift_base(p.perceived_liability);
state
.social_cognition_mut()
.self_hate_mut()
.shift_base(p.self_hate);
state.needs_mut().purpose_mut().shift_base(p.purpose);
state
.mental_health_mut()
.shift_acquired_capability_base(p.acquired_capability);
state
.mental_health_mut()
.shift_interpersonal_hopelessness_base(p.interpersonal_hopelessness);
state
.mental_health_mut()
.depression_mut()
.shift_base(p.depression);
state
.mental_health_mut()
.hopelessness_mut()
.shift_base(p.hopelessness);
state.mental_health_mut().shift_self_worth_base(p.self_worth);
state.needs_mut().stress_mut().shift_base(p.stress);
state.needs_mut().fatigue_mut().shift_base(p.fatigue);
state
.disposition_mut()
.grievance_mut()
.shift_base(p.grievance);
state
.disposition_mut()
.impulse_control_mut()
.shift_base(p.impulse_control);
state
.disposition_mut()
.empathy_mut()
.shift_base(p.empathy);
state
.disposition_mut()
.aggression_mut()
.shift_base(p.aggression);
state
.disposition_mut()
.reactance_mut()
.shift_base(p.reactance);
state
.disposition_mut()
.trustor_propensity_mut()
.shift_base(p.trustor_propensity);
}
fn apply_acute_deltas_to_state(state: &mut IndividualState, deltas: &crate::event::AppliedDeltas) {
let a = &deltas.acute;
if a.valence.abs() > f32::EPSILON {
state.mood_mut().add_valence_delta(a.valence);
}
if a.arousal.abs() > f32::EPSILON {
state.mood_mut().add_arousal_delta(a.arousal);
}
if a.dominance.abs() > f32::EPSILON {
state.mood_mut().add_dominance_delta(a.dominance);
}
if a.loneliness.abs() > f32::EPSILON {
state
.social_cognition_mut()
.add_loneliness_delta(a.loneliness);
}
if a.prc.abs() > f32::EPSILON {
state
.social_cognition_mut()
.add_perceived_reciprocal_caring_delta(a.prc);
}
if a.perceived_competence.abs() > f32::EPSILON {
state
.social_cognition_mut()
.add_perceived_competence_delta(a.perceived_competence);
}
if a.perceived_liability.abs() > f32::EPSILON {
state
.social_cognition_mut()
.add_perceived_liability_delta(a.perceived_liability);
}
if a.self_hate.abs() > f32::EPSILON {
state.social_cognition_mut().add_self_hate_delta(a.self_hate);
}
if a.purpose.abs() > f32::EPSILON {
state.needs_mut().add_purpose_delta(a.purpose);
}
if a.interpersonal_hopelessness.abs() > f32::EPSILON {
state
.mental_health_mut()
.add_interpersonal_hopelessness_delta(a.interpersonal_hopelessness);
}
if a.depression.abs() > f32::EPSILON {
state
.mental_health_mut()
.add_depression_delta(a.depression);
}
if a.hopelessness.abs() > f32::EPSILON {
state
.mental_health_mut()
.add_hopelessness_delta(a.hopelessness);
}
if a.self_worth.abs() > f32::EPSILON {
state.mental_health_mut().add_self_worth_delta(a.self_worth);
}
if a.stress.abs() > f32::EPSILON {
state.needs_mut().add_stress_delta(a.stress);
}
if a.fatigue.abs() > f32::EPSILON {
state.needs_mut().add_fatigue_delta(a.fatigue);
}
if a.grievance.abs() > f32::EPSILON {
state.disposition_mut().add_grievance_delta(a.grievance);
}
if a.impulse_control.abs() > f32::EPSILON {
state
.disposition_mut()
.add_impulse_control_delta(a.impulse_control);
}
if a.empathy.abs() > f32::EPSILON {
state.disposition_mut().add_empathy_delta(a.empathy);
}
if a.aggression.abs() > f32::EPSILON {
state
.disposition_mut()
.add_aggression_delta(a.aggression);
}
if a.reactance.abs() > f32::EPSILON {
state.disposition_mut().add_reactance_delta(a.reactance);
}
if a.trustor_propensity.abs() > f32::EPSILON {
state
.disposition_mut()
.add_trustor_propensity_delta(a.trustor_propensity);
}
}
fn apply_chronic_deltas_to_state(state: &mut IndividualState, deltas: &crate::event::AppliedDeltas) {
let c = &deltas.chronic;
if c.valence.abs() > f32::EPSILON {
state.mood_mut().add_valence_chronic_delta(c.valence);
}
if c.arousal.abs() > f32::EPSILON {
state.mood_mut().add_arousal_chronic_delta(c.arousal);
}
if c.dominance.abs() > f32::EPSILON {
state.mood_mut().add_dominance_chronic_delta(c.dominance);
}
if c.loneliness.abs() > f32::EPSILON {
state
.social_cognition_mut()
.loneliness_mut()
.add_chronic_delta(c.loneliness);
}
if c.prc.abs() > f32::EPSILON {
state
.social_cognition_mut()
.perceived_reciprocal_caring_mut()
.add_chronic_delta(c.prc);
}
if c.perceived_competence.abs() > f32::EPSILON {
state
.social_cognition_mut()
.perceived_competence_mut()
.add_chronic_delta(c.perceived_competence);
}
if c.perceived_liability.abs() > f32::EPSILON {
state
.social_cognition_mut()
.perceived_liability_mut()
.add_chronic_delta(c.perceived_liability);
}
if c.self_hate.abs() > f32::EPSILON {
state
.social_cognition_mut()
.self_hate_mut()
.add_chronic_delta(c.self_hate);
}
if c.purpose.abs() > f32::EPSILON {
state.needs_mut().purpose_mut().add_chronic_delta(c.purpose);
}
if c.interpersonal_hopelessness.abs() > f32::EPSILON {
state
.mental_health_mut()
.add_interpersonal_hopelessness_chronic_delta(c.interpersonal_hopelessness);
}
if c.depression.abs() > f32::EPSILON {
state
.mental_health_mut()
.depression_mut()
.add_chronic_delta(c.depression);
}
if c.hopelessness.abs() > f32::EPSILON {
state
.mental_health_mut()
.hopelessness_mut()
.add_chronic_delta(c.hopelessness);
}
if c.self_worth.abs() > f32::EPSILON {
state
.mental_health_mut()
.add_self_worth_chronic_delta(c.self_worth);
}
if c.stress.abs() > f32::EPSILON {
state.needs_mut().stress_mut().add_chronic_delta(c.stress);
}
if c.fatigue.abs() > f32::EPSILON {
state.needs_mut().fatigue_mut().add_chronic_delta(c.fatigue);
}
if c.grievance.abs() > f32::EPSILON {
state
.disposition_mut()
.grievance_mut()
.add_chronic_delta(c.grievance);
}
if c.impulse_control.abs() > f32::EPSILON {
state
.disposition_mut()
.impulse_control_mut()
.add_chronic_delta(c.impulse_control);
}
if c.empathy.abs() > f32::EPSILON {
state
.disposition_mut()
.empathy_mut()
.add_chronic_delta(c.empathy);
}
if c.aggression.abs() > f32::EPSILON {
state
.disposition_mut()
.aggression_mut()
.add_chronic_delta(c.aggression);
}
if c.reactance.abs() > f32::EPSILON {
state
.disposition_mut()
.reactance_mut()
.add_chronic_delta(c.reactance);
}
if c.trustor_propensity.abs() > f32::EPSILON {
state
.disposition_mut()
.trustor_propensity_mut()
.add_chronic_delta(c.trustor_propensity);
}
}
#[must_use]
pub(crate) fn reverse_interpreted_event_from_state(
state: IndividualState,
interpreted: &crate::processor::InterpretedEvent,
) -> IndividualState {
let mut new_state = state;
if let Some(deltas) = &interpreted.spec_deltas {
reverse_acute_deltas_from_state(&mut new_state, deltas);
reverse_chronic_deltas_from_state(&mut new_state, deltas);
}
new_state
}
fn reverse_acute_deltas_from_state(
state: &mut IndividualState,
deltas: &crate::event::AppliedDeltas,
) {
let a = &deltas.acute;
if a.valence.abs() > f32::EPSILON {
state.mood_mut().add_valence_delta(-a.valence);
}
if a.arousal.abs() > f32::EPSILON {
state.mood_mut().add_arousal_delta(-a.arousal);
}
if a.dominance.abs() > f32::EPSILON {
state.mood_mut().add_dominance_delta(-a.dominance);
}
if a.loneliness.abs() > f32::EPSILON {
state
.social_cognition_mut()
.add_loneliness_delta(-a.loneliness);
}
if a.prc.abs() > f32::EPSILON {
state
.social_cognition_mut()
.add_perceived_reciprocal_caring_delta(-a.prc);
}
if a.perceived_competence.abs() > f32::EPSILON {
state
.social_cognition_mut()
.add_perceived_competence_delta(-a.perceived_competence);
}
if a.perceived_liability.abs() > f32::EPSILON {
state
.social_cognition_mut()
.add_perceived_liability_delta(-a.perceived_liability);
}
if a.self_hate.abs() > f32::EPSILON {
state
.social_cognition_mut()
.add_self_hate_delta(-a.self_hate);
}
if a.purpose.abs() > f32::EPSILON {
state.needs_mut().add_purpose_delta(-a.purpose);
}
if a.interpersonal_hopelessness.abs() > f32::EPSILON {
state
.mental_health_mut()
.add_interpersonal_hopelessness_delta(-a.interpersonal_hopelessness);
}
if a.depression.abs() > f32::EPSILON {
state
.mental_health_mut()
.add_depression_delta(-a.depression);
}
if a.hopelessness.abs() > f32::EPSILON {
state
.mental_health_mut()
.add_hopelessness_delta(-a.hopelessness);
}
if a.self_worth.abs() > f32::EPSILON {
state.mental_health_mut().add_self_worth_delta(-a.self_worth);
}
if a.stress.abs() > f32::EPSILON {
state.needs_mut().add_stress_delta(-a.stress);
}
if a.fatigue.abs() > f32::EPSILON {
state.needs_mut().add_fatigue_delta(-a.fatigue);
}
if a.grievance.abs() > f32::EPSILON {
state.disposition_mut().add_grievance_delta(-a.grievance);
}
if a.impulse_control.abs() > f32::EPSILON {
state
.disposition_mut()
.add_impulse_control_delta(-a.impulse_control);
}
if a.empathy.abs() > f32::EPSILON {
state.disposition_mut().add_empathy_delta(-a.empathy);
}
if a.aggression.abs() > f32::EPSILON {
state
.disposition_mut()
.add_aggression_delta(-a.aggression);
}
if a.reactance.abs() > f32::EPSILON {
state.disposition_mut().add_reactance_delta(-a.reactance);
}
if a.trustor_propensity.abs() > f32::EPSILON {
state
.disposition_mut()
.add_trustor_propensity_delta(-a.trustor_propensity);
}
}
fn reverse_chronic_deltas_from_state(
state: &mut IndividualState,
deltas: &crate::event::AppliedDeltas,
) {
let c = &deltas.chronic;
if c.valence.abs() > f32::EPSILON {
state.mood_mut().add_valence_chronic_delta(-c.valence);
}
if c.arousal.abs() > f32::EPSILON {
state.mood_mut().add_arousal_chronic_delta(-c.arousal);
}
if c.dominance.abs() > f32::EPSILON {
state.mood_mut().add_dominance_chronic_delta(-c.dominance);
}
if c.loneliness.abs() > f32::EPSILON {
state
.social_cognition_mut()
.loneliness_mut()
.add_chronic_delta(-c.loneliness);
}
if c.prc.abs() > f32::EPSILON {
state
.social_cognition_mut()
.perceived_reciprocal_caring_mut()
.add_chronic_delta(-c.prc);
}
if c.perceived_competence.abs() > f32::EPSILON {
state
.social_cognition_mut()
.perceived_competence_mut()
.add_chronic_delta(-c.perceived_competence);
}
if c.perceived_liability.abs() > f32::EPSILON {
state
.social_cognition_mut()
.perceived_liability_mut()
.add_chronic_delta(-c.perceived_liability);
}
if c.self_hate.abs() > f32::EPSILON {
state
.social_cognition_mut()
.self_hate_mut()
.add_chronic_delta(-c.self_hate);
}
if c.purpose.abs() > f32::EPSILON {
state.needs_mut().purpose_mut().add_chronic_delta(-c.purpose);
}
if c.interpersonal_hopelessness.abs() > f32::EPSILON {
state
.mental_health_mut()
.add_interpersonal_hopelessness_chronic_delta(-c.interpersonal_hopelessness);
}
if c.depression.abs() > f32::EPSILON {
state
.mental_health_mut()
.depression_mut()
.add_chronic_delta(-c.depression);
}
if c.hopelessness.abs() > f32::EPSILON {
state
.mental_health_mut()
.hopelessness_mut()
.add_chronic_delta(-c.hopelessness);
}
if c.self_worth.abs() > f32::EPSILON {
state
.mental_health_mut()
.add_self_worth_chronic_delta(-c.self_worth);
}
if c.stress.abs() > f32::EPSILON {
state.needs_mut().stress_mut().add_chronic_delta(-c.stress);
}
if c.fatigue.abs() > f32::EPSILON {
state.needs_mut().fatigue_mut().add_chronic_delta(-c.fatigue);
}
if c.grievance.abs() > f32::EPSILON {
state
.disposition_mut()
.grievance_mut()
.add_chronic_delta(-c.grievance);
}
if c.impulse_control.abs() > f32::EPSILON {
state
.disposition_mut()
.impulse_control_mut()
.add_chronic_delta(-c.impulse_control);
}
if c.empathy.abs() > f32::EPSILON {
state
.disposition_mut()
.empathy_mut()
.add_chronic_delta(-c.empathy);
}
if c.aggression.abs() > f32::EPSILON {
state
.disposition_mut()
.aggression_mut()
.add_chronic_delta(-c.aggression);
}
if c.reactance.abs() > f32::EPSILON {
state
.disposition_mut()
.reactance_mut()
.add_chronic_delta(-c.reactance);
}
if c.trustor_propensity.abs() > f32::EPSILON {
state
.disposition_mut()
.trustor_propensity_mut()
.add_chronic_delta(-c.trustor_propensity);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::entity::EntityBuilder;
use crate::enums::{EventType, Species};
use crate::event::EventBuilder;
use crate::processor::interpret_event;
#[test]
fn advance_state_applies_decay() {
let mut state = IndividualState::new();
state.mood_mut().add_valence_delta(0.8);
let advanced = advance_state(state, Duration::weeks(1));
assert!(advanced.mood().valence_delta() < 0.01);
}
#[test]
fn advance_state_zero_duration_unchanged() {
let mut state = IndividualState::new();
state.mood_mut().add_valence_delta(0.5);
let advanced = advance_state(state, Duration::zero());
assert!((advanced.mood().valence_delta() - 0.5).abs() < f32::EPSILON);
}
#[test]
fn regress_state_reverses_decay() {
let mut state = IndividualState::new();
state.mood_mut().add_valence_delta(0.25);
let regressed = regress_state(state, Duration::hours(6));
assert!(regressed.mood().valence_delta() > 0.4);
}
#[test]
fn regress_state_zero_duration_unchanged() {
let mut state = IndividualState::new();
state.mood_mut().add_valence_delta(0.5);
let regressed = regress_state(state, Duration::zero());
assert!((regressed.mood().valence_delta() - 0.5).abs() < f32::EPSILON);
}
#[test]
fn apply_interpreted_event_to_state_breakup() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let event = EventBuilder::new(EventType::EndRelationshipRomantic)
.severity(0.8)
.build()
.unwrap();
let interpreted = interpret_event(&event, &entity);
let state = IndividualState::new();
let new_state = apply_interpreted_event_to_state(state, &interpreted);
let valence_change = new_state.mood().valence_delta()
+ (new_state.mood().valence_base() - 0.0); assert!(valence_change < 0.0);
}
#[test]
fn apply_interpreted_event_to_state_combat_increases_ac_base() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let event = EventBuilder::new(EventType::ExperienceCombatMilitary)
.severity(0.9)
.build()
.unwrap();
let interpreted = interpret_event(&event, &entity);
let state = IndividualState::new();
let new_state = apply_interpreted_event_to_state(state, &interpreted);
assert!(new_state.mental_health().acquired_capability().base() > 0.0);
}
#[test]
fn apply_interpreted_event_to_state_achievement_positive() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let event = EventBuilder::new(EventType::AchieveGoalMajor)
.severity(0.8)
.build()
.unwrap();
let interpreted = interpret_event(&event, &entity);
let state = IndividualState::new();
let new_state = apply_interpreted_event_to_state(state, &interpreted);
let valence_change = new_state.mood().valence_delta()
+ (new_state.mood().valence_base() - 0.0);
assert!(valence_change > 0.0);
}
#[test]
fn apply_interpreted_event_to_state_permanent_fields_all_hit() {
use crate::event::event_spec::{ChronicFlags, EventImpact, EventSpec, PermanenceValues};
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let impact = EventImpact {
valence: 0.1,
arousal: 0.1,
dominance: 0.1,
fatigue: 0.1,
stress: 0.1,
purpose: 0.1,
loneliness: 0.1,
prc: 0.1,
perceived_liability: 0.1,
self_hate: 0.1,
perceived_competence: 0.1,
depression: 0.1,
self_worth: 0.1,
hopelessness: 0.1,
interpersonal_hopelessness: 0.1,
acquired_capability: 0.1,
impulse_control: 0.1,
empathy: 0.1,
aggression: 0.1,
grievance: 0.1,
reactance: 0.1,
trustor_propensity: 0.1,
};
let permanence = PermanenceValues {
valence: 1.0,
arousal: 1.0,
dominance: 1.0,
fatigue: 1.0,
stress: 1.0,
purpose: 1.0,
loneliness: 1.0,
prc: 1.0,
perceived_liability: 1.0,
self_hate: 1.0,
perceived_competence: 1.0,
depression: 1.0,
self_worth: 1.0,
hopelessness: 1.0,
interpersonal_hopelessness: 1.0,
impulse_control: 1.0,
empathy: 1.0,
aggression: 1.0,
grievance: 1.0,
reactance: 1.0,
trustor_propensity: 1.0,
};
let custom_spec = EventSpec {
impact,
chronic: ChronicFlags::default(),
permanence,
};
let event = EventBuilder::custom(custom_spec).severity(1.0).build().unwrap();
let interpreted = interpret_event(&event, &entity);
let state = IndividualState::new();
let new_state = apply_interpreted_event_to_state(state, &interpreted);
assert!(new_state.mood().valence_base().abs() > f32::EPSILON);
assert!(new_state.needs().stress().base().abs() > f32::EPSILON);
assert!(new_state.disposition().aggression().base().abs() > f32::EPSILON);
}
#[test]
fn apply_permanent_deltas_to_state_applies_all_fields() {
use crate::event::{AppliedDeltas, EventImpact};
let mut state = IndividualState::new();
let deltas = AppliedDeltas {
permanent: EventImpact {
valence: 0.1,
arousal: 0.1,
dominance: 0.1,
fatigue: 0.1,
stress: 0.1,
purpose: 0.1,
loneliness: 0.1,
prc: 0.1,
perceived_liability: 0.1,
self_hate: 0.1,
perceived_competence: 0.1,
depression: 0.1,
self_worth: 0.1,
hopelessness: 0.1,
interpersonal_hopelessness: 0.1,
acquired_capability: 0.1,
impulse_control: 0.1,
empathy: 0.1,
aggression: 0.1,
grievance: 0.1,
reactance: 0.1,
trustor_propensity: 0.1,
},
acute: EventImpact::default(),
chronic: EventImpact::default(),
};
assert!(deltas.permanent.valence.abs() > f32::EPSILON);
assert!(deltas.permanent.trustor_propensity.abs() > f32::EPSILON);
apply_permanent_deltas_to_state(&mut state, &deltas);
assert!(state.mood().valence_base().abs() > f32::EPSILON);
assert!(state.needs().stress().base().abs() > f32::EPSILON);
assert!(state.disposition().trustor_propensity().base().abs() > f32::EPSILON);
}
#[test]
fn apply_interpreted_event_to_state_skips_when_no_spec_deltas() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let event = EventBuilder::new(EventType::EndRelationshipRomantic)
.severity(0.8)
.build()
.unwrap();
let mut interpreted = interpret_event(&event, &entity);
interpreted.spec_deltas = None;
let state = IndividualState::new();
let before_valence = state.mood().valence_base();
let new_state = apply_interpreted_event_to_state(state, &interpreted);
assert!((new_state.mood().valence_base() - before_valence).abs() < f32::EPSILON);
}
#[test]
fn reverse_interpreted_event_from_state_skips_when_no_spec_deltas() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let event = EventBuilder::new(EventType::EndRelationshipRomantic)
.severity(0.8)
.build()
.unwrap();
let mut interpreted = interpret_event(&event, &entity);
interpreted.spec_deltas = None;
let mut state = IndividualState::new();
state.mood_mut().add_valence_delta(0.4);
let before_delta = state.mood().valence_delta();
let reversed = reverse_interpreted_event_from_state(state, &interpreted);
assert!((reversed.mood().valence_delta() - before_delta).abs() < f32::EPSILON);
}
#[test]
fn reverse_interpreted_event_reverses_acute_deltas() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let event = EventBuilder::new(EventType::EndRelationshipRomantic)
.severity(0.8)
.build()
.unwrap();
let interpreted = interpret_event(&event, &entity);
let state = IndividualState::new();
let after_event = apply_interpreted_event_to_state(state.clone(), &interpreted);
let reversed = reverse_interpreted_event_from_state(after_event.clone(), &interpreted);
assert!(reversed.mood().valence_delta().abs() < 0.01);
}
#[test]
fn reverse_interpreted_event_does_not_reverse_ac() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let event = EventBuilder::new(EventType::ExperienceCombatMilitary)
.severity(0.9)
.build()
.unwrap();
let interpreted = interpret_event(&event, &entity);
let state = IndividualState::new();
let after_event = apply_interpreted_event_to_state(state, &interpreted);
let ac_after = after_event.mental_health().acquired_capability().base();
let reversed = reverse_interpreted_event_from_state(after_event, &interpreted);
let ac_reversed = reversed.mental_health().acquired_capability().base();
assert!((ac_after - ac_reversed).abs() < f32::EPSILON);
}
#[test]
fn reverse_dimension_decay_doubles_after_half_life() {
let mut state = IndividualState::new();
state.mood_mut().add_valence_delta(0.25);
reverse_dimension_decay(state.mood_mut().valence_mut(), Duration::hours(6), Duration::hours(6));
assert!(state.mood().valence_delta() > 0.4);
assert!(state.mood().valence_delta() < 0.6);
}
#[test]
fn reverse_dimension_decay_zero_delta_unchanged() {
let mut state = IndividualState::new();
reverse_dimension_decay(state.mood_mut().valence_mut(), Duration::hours(6), Duration::hours(6));
assert!(state.mood().valence_delta().abs() < f32::EPSILON);
}
#[test]
fn reverse_dimension_decay_zero_half_life_no_change() {
let mut state = IndividualState::new();
state.mood_mut().add_valence_delta(0.5);
let original = state.mood().valence_delta();
reverse_dimension_decay(
state.mood_mut().valence_mut(),
Duration::hours(6),
Duration::seconds(0),
);
assert!((state.mood().valence_delta() - original).abs() < f32::EPSILON);
}
#[test]
fn apply_chronic_deltas_routes_to_chronic_bucket() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let event = EventBuilder::new(EventType::DevelopIllnessChronic)
.severity(0.8)
.build()
.unwrap();
let interpreted = interpret_event(&event, &entity);
let state = IndividualState::new();
let new_state = apply_interpreted_event_to_state(state, &interpreted);
let chronic_stress = new_state.needs().stress().chronic_delta().abs() > f32::EPSILON;
let chronic_fatigue = new_state.needs().fatigue().chronic_delta().abs() > f32::EPSILON;
let chronic_self_worth = new_state
.mental_health()
.self_worth()
.chronic_delta()
.abs()
> f32::EPSILON;
let chronic_count =
(chronic_stress as u8) + (chronic_fatigue as u8) + (chronic_self_worth as u8);
let acute_stress = new_state.needs().stress().delta().abs() > f32::EPSILON;
let impact_total = chronic_count + (acute_stress as u8);
assert!(impact_total > 0);
}
}