using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// GAME 1303 Tier 7 — One need: hunger, energy, cleanliness, happiness. Cozy Critters.
///
/// SETUP
/// 1. Add ONE of these per need to the pet. Rename the Stat Name field ("Hunger").
/// 2. Decay Per Minute is how fast it falls. Rhys's pitch is explicit that the pet must
/// NOT die from two seconds of inattention, so keep these low: 2-5 per minute means
/// a full bar lasts 20-50 minutes of play. Tune by feel, not by realism.
/// 3. Higher Is Better: ON for happiness/energy (100 = great), OFF for a stat where a
/// high number is bad. Everything below assumes higher = better.
/// 4. Low Threshold fires OnBecameLow once when it drops past it — that's the cue for a
/// sad animation or a thought bubble, not a punishment.
/// 5. Affects Happiness: if ON, this need being low drags happiness down (wire the
/// happiness NeedStat into Happiness Stat). That's how "care for it, increase
/// happiness" from the pitch actually works.
///
/// NO DEATH: there is deliberately no "hits zero = game over" here. The worst that happens
/// is the pet is unhappy and its nature drifts (PersonalityTracker). That's the whole point
/// of a cozy sim.
/// </summary>
public class NeedStat : MonoBehaviour
{
[Header("Identity")]
public string statName = "Hunger";
public Sprite icon;
[Header("Value")]
[Range(0f, 100f)] public float value = 100f;
public float maxValue = 100f;
[Tooltip("Points lost per minute of real time. Keep this gentle.")]
public float decayPerMinute = 3f;
public bool higherIsBetter = true;
[Header("Thresholds")]
[Range(0f, 100f)] public float lowThreshold = 30f;
[Range(0f, 100f)] public float fullThreshold = 90f;
[Header("Knock-on effect")]
[Tooltip("While this need is low, drain the happiness stat too.")]
public bool affectsHappiness = true;
public NeedStat happinessStat;
public float happinessDrainPerMinute = 2f;
[Header("Events")]
public UnityEvent<float> OnValueChanged; // normalized 0-1
public UnityEvent OnBecameLow;
public UnityEvent OnBecameFull;
public float Normalized => maxValue > 0f ? Mathf.Clamp01(value / maxValue) : 0f;
public bool IsLow => value <= lowThreshold;
public bool IsFull => value >= fullThreshold;
private bool wasLow, wasFull;
void Start() => OnValueChanged?.Invoke(Normalized);
void Update()
{
float before = value;
value = Mathf.Clamp(value - decayPerMinute / 60f * Time.deltaTime, 0f, maxValue);
if (affectsHappiness && IsLow && happinessStat != null && happinessStat != this)
happinessStat.Add(-happinessDrainPerMinute / 60f * Time.deltaTime);
if (!Mathf.Approximately(before, value)) OnValueChanged?.Invoke(Normalized);
if (IsLow && !wasLow) OnBecameLow?.Invoke();
if (IsFull && !wasFull) OnBecameFull?.Invoke();
wasLow = IsLow;
wasFull = IsFull;
}
/// <summary>Add (or subtract) points. CareAction calls this.</summary>
public void Add(float amount)
{
value = Mathf.Clamp(value + amount, 0f, maxValue);
OnValueChanged?.Invoke(Normalized);
}
public void SetValue(float v)
{
value = Mathf.Clamp(v, 0f, maxValue);
OnValueChanged?.Invoke(Normalized);
}
/// <summary>Apply offline decay when the game reloads. SaveSystem calls this.</summary>
public void ApplyOfflineTime(float minutesAway)
{
Add(-decayPerMinute * minutesAway);
}
}