let animals drink things (#18236)

* thirst .Owner removal

* thirst file scope

* drink update + minor refactor

* drink file scope

* let animals drink things

* dont eat/drink when dont need to

* admeme mouse override

* moo

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
This commit is contained in:
deltanedas
2023-07-25 21:32:10 +00:00
committed by GitHub
parent d55cd23b0a
commit 063f75ca29
8 changed files with 705 additions and 594 deletions

View File

@@ -6,167 +6,170 @@ using Content.Shared.Alert;
using Content.Shared.Movement.Systems;
using Content.Shared.Rejuvenate;
namespace Content.Server.Nutrition.EntitySystems
namespace Content.Server.Nutrition.EntitySystems;
[UsedImplicitly]
public sealed class ThirstSystem : EntitySystem
{
[UsedImplicitly]
public sealed class ThirstSystem : EntitySystem
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
[Dependency] private readonly SharedJetpackSystem _jetpack = default!;
private ISawmill _sawmill = default!;
private float _accumulatedFrameTime;
public override void Initialize()
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
[Dependency] private readonly SharedJetpackSystem _jetpack = default!;
base.Initialize();
private ISawmill _sawmill = default!;
private float _accumulatedFrameTime;
_sawmill = Logger.GetSawmill("thirst");
public override void Initialize()
SubscribeLocalEvent<ThirstComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
SubscribeLocalEvent<ThirstComponent, ComponentStartup>(OnComponentStartup);
SubscribeLocalEvent<ThirstComponent, RejuvenateEvent>(OnRejuvenate);
}
private void OnComponentStartup(EntityUid uid, ThirstComponent component, ComponentStartup args)
{
// Do not change behavior unless starting value is explicitly defined
if (component.CurrentThirst < 0)
{
base.Initialize();
_sawmill = Logger.GetSawmill("thirst");
SubscribeLocalEvent<ThirstComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
SubscribeLocalEvent<ThirstComponent, ComponentStartup>(OnComponentStartup);
SubscribeLocalEvent<ThirstComponent, RejuvenateEvent>(OnRejuvenate);
component.CurrentThirst = _random.Next(
(int) component.ThirstThresholds[ThirstThreshold.Thirsty] + 10,
(int) component.ThirstThresholds[ThirstThreshold.Okay] - 1);
}
private void OnComponentStartup(EntityUid uid, ThirstComponent component, ComponentStartup args)
component.CurrentThirstThreshold = GetThirstThreshold(component, component.CurrentThirst);
component.LastThirstThreshold = ThirstThreshold.Okay; // TODO: Potentially change this -> Used Okay because no effects.
// TODO: Check all thresholds make sense and throw if they don't.
UpdateEffects(uid, component);
}
private void OnRefreshMovespeed(EntityUid uid, ThirstComponent component, RefreshMovementSpeedModifiersEvent args)
{
// TODO: This should really be taken care of somewhere else
if (_jetpack.IsUserFlying(uid))
return;
var mod = component.CurrentThirstThreshold <= ThirstThreshold.Parched ? 0.75f : 1.0f;
args.ModifySpeed(mod, mod);
}
private void OnRejuvenate(EntityUid uid, ThirstComponent component, RejuvenateEvent args)
{
ResetThirst(component);
}
private ThirstThreshold GetThirstThreshold(ThirstComponent component, float amount)
{
ThirstThreshold result = ThirstThreshold.Dead;
var value = component.ThirstThresholds[ThirstThreshold.OverHydrated];
foreach (var threshold in component.ThirstThresholds)
{
// Do not change behavior unless starting value is explicitly defined
if (component.CurrentThirst < 0)
if (threshold.Value <= value && threshold.Value >= amount)
{
component.CurrentThirst = _random.Next(
(int) component.ThirstThresholds[ThirstThreshold.Thirsty] + 10,
(int) component.ThirstThresholds[ThirstThreshold.Okay] - 1);
result = threshold.Key;
value = threshold.Value;
}
component.CurrentThirstThreshold = GetThirstThreshold(component, component.CurrentThirst);
component.LastThirstThreshold = ThirstThreshold.Okay; // TODO: Potentially change this -> Used Okay because no effects.
// TODO: Check all thresholds make sense and throw if they don't.
UpdateEffects(component);
}
private void OnRefreshMovespeed(EntityUid uid, ThirstComponent component, RefreshMovementSpeedModifiersEvent args)
return result;
}
public void UpdateThirst(ThirstComponent component, float amount)
{
component.CurrentThirst = Math.Clamp(component.CurrentThirst + amount, component.ThirstThresholds[ThirstThreshold.Dead], component.ThirstThresholds[ThirstThreshold.OverHydrated]);
}
public void ResetThirst(ThirstComponent component)
{
component.CurrentThirst = component.ThirstThresholds[ThirstThreshold.Okay];
}
private bool IsMovementThreshold(ThirstThreshold threshold)
{
switch (threshold)
{
// TODO: This should really be taken care of somewhere else
if (_jetpack.IsUserFlying(component.Owner))
case ThirstThreshold.Dead:
case ThirstThreshold.Parched:
return true;
case ThirstThreshold.Thirsty:
case ThirstThreshold.Okay:
case ThirstThreshold.OverHydrated:
return false;
default:
throw new ArgumentOutOfRangeException(nameof(threshold), threshold, null);
}
}
private void UpdateEffects(EntityUid uid, ThirstComponent component)
{
if (IsMovementThreshold(component.LastThirstThreshold) != IsMovementThreshold(component.CurrentThirstThreshold) &&
TryComp(uid, out MovementSpeedModifierComponent? movementSlowdownComponent))
{
_movement.RefreshMovementSpeedModifiers(uid, movementSlowdownComponent);
}
// Update UI
if (ThirstComponent.ThirstThresholdAlertTypes.TryGetValue(component.CurrentThirstThreshold, out var alertId))
{
_alerts.ShowAlert(uid, alertId);
}
else
{
_alerts.ClearAlertCategory(uid, AlertCategory.Thirst);
}
switch (component.CurrentThirstThreshold)
{
case ThirstThreshold.OverHydrated:
component.LastThirstThreshold = component.CurrentThirstThreshold;
component.ActualDecayRate = component.BaseDecayRate * 1.2f;
return;
var mod = component.CurrentThirstThreshold <= ThirstThreshold.Parched ? 0.75f : 1.0f;
args.ModifySpeed(mod, mod);
}
case ThirstThreshold.Okay:
component.LastThirstThreshold = component.CurrentThirstThreshold;
component.ActualDecayRate = component.BaseDecayRate;
return;
private void OnRejuvenate(EntityUid uid, ThirstComponent component, RejuvenateEvent args)
{
ResetThirst(component);
}
case ThirstThreshold.Thirsty:
// Same as okay except with UI icon saying drink soon.
component.LastThirstThreshold = component.CurrentThirstThreshold;
component.ActualDecayRate = component.BaseDecayRate * 0.8f;
return;
case ThirstThreshold.Parched:
_movement.RefreshMovementSpeedModifiers(uid);
component.LastThirstThreshold = component.CurrentThirstThreshold;
component.ActualDecayRate = component.BaseDecayRate * 0.6f;
return;
private ThirstThreshold GetThirstThreshold(ThirstComponent component, float amount)
case ThirstThreshold.Dead:
return;
default:
_sawmill.Error($"No thirst threshold found for {component.CurrentThirstThreshold}");
throw new ArgumentOutOfRangeException($"No thirst threshold found for {component.CurrentThirstThreshold}");
}
}
public override void Update(float frameTime)
{
_accumulatedFrameTime += frameTime;
if (_accumulatedFrameTime > 1)
{
ThirstThreshold result = ThirstThreshold.Dead;
var value = component.ThirstThresholds[ThirstThreshold.OverHydrated];
foreach (var threshold in component.ThirstThresholds)
var query = EntityManager.EntityQueryEnumerator<ThirstComponent>();
while (query.MoveNext(out var uid, out var comp))
{
if (threshold.Value <= value && threshold.Value >= amount)
UpdateThirst(comp, - comp.ActualDecayRate);
var calculatedThirstThreshold = GetThirstThreshold(comp, comp.CurrentThirst);
if (calculatedThirstThreshold != comp.CurrentThirstThreshold)
{
result = threshold.Key;
value = threshold.Value;
comp.CurrentThirstThreshold = calculatedThirstThreshold;
UpdateEffects(uid, comp);
}
}
return result;
}
public void UpdateThirst(ThirstComponent component, float amount)
{
component.CurrentThirst = Math.Clamp(component.CurrentThirst + amount, component.ThirstThresholds[ThirstThreshold.Dead], component.ThirstThresholds[ThirstThreshold.OverHydrated]);
}
public void ResetThirst(ThirstComponent component)
{
component.CurrentThirst = component.ThirstThresholds[ThirstThreshold.Okay];
}
private bool IsMovementThreshold(ThirstThreshold threshold)
{
switch (threshold)
{
case ThirstThreshold.Dead:
case ThirstThreshold.Parched:
return true;
case ThirstThreshold.Thirsty:
case ThirstThreshold.Okay:
case ThirstThreshold.OverHydrated:
return false;
default:
throw new ArgumentOutOfRangeException(nameof(threshold), threshold, null);
}
}
private void UpdateEffects(ThirstComponent component)
{
if (IsMovementThreshold(component.LastThirstThreshold) != IsMovementThreshold(component.CurrentThirstThreshold) &&
TryComp(component.Owner, out MovementSpeedModifierComponent? movementSlowdownComponent))
{
_movement.RefreshMovementSpeedModifiers(component.Owner, movementSlowdownComponent);
}
// Update UI
if (ThirstComponent.ThirstThresholdAlertTypes.TryGetValue(component.CurrentThirstThreshold, out var alertId))
{
_alerts.ShowAlert(component.Owner, alertId);
}
else
{
_alerts.ClearAlertCategory(component.Owner, AlertCategory.Thirst);
}
switch (component.CurrentThirstThreshold)
{
case ThirstThreshold.OverHydrated:
component.LastThirstThreshold = component.CurrentThirstThreshold;
component.ActualDecayRate = component.BaseDecayRate * 1.2f;
return;
case ThirstThreshold.Okay:
component.LastThirstThreshold = component.CurrentThirstThreshold;
component.ActualDecayRate = component.BaseDecayRate;
return;
case ThirstThreshold.Thirsty:
// Same as okay except with UI icon saying drink soon.
component.LastThirstThreshold = component.CurrentThirstThreshold;
component.ActualDecayRate = component.BaseDecayRate * 0.8f;
return;
case ThirstThreshold.Parched:
_movement.RefreshMovementSpeedModifiers(component.Owner);
component.LastThirstThreshold = component.CurrentThirstThreshold;
component.ActualDecayRate = component.BaseDecayRate * 0.6f;
return;
case ThirstThreshold.Dead:
return;
default:
_sawmill.Error($"No thirst threshold found for {component.CurrentThirstThreshold}");
throw new ArgumentOutOfRangeException($"No thirst threshold found for {component.CurrentThirstThreshold}");
}
}
public override void Update(float frameTime)
{
_accumulatedFrameTime += frameTime;
if (_accumulatedFrameTime > 1)
{
foreach (var component in EntityManager.EntityQuery<ThirstComponent>())
{
UpdateThirst(component, - component.ActualDecayRate);
var calculatedThirstThreshold = GetThirstThreshold(component, component.CurrentThirst);
if (calculatedThirstThreshold != component.CurrentThirstThreshold)
{
component.CurrentThirstThreshold = calculatedThirstThreshold;
UpdateEffects(component);
}
}
_accumulatedFrameTime -= 1;
}
_accumulatedFrameTime -= 1;
}
}
}