diff --git a/Content.Shared/Damage/Components/PassiveDamageComponent.cs b/Content.Shared/Damage/Components/PassiveDamageComponent.cs new file mode 100644 index 0000000000..269960adac --- /dev/null +++ b/Content.Shared/Damage/Components/PassiveDamageComponent.cs @@ -0,0 +1,40 @@ +using Content.Shared.Mobs; +using Content.Shared.FixedPoint; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; +using Robust.Shared.GameStates; + +namespace Content.Shared.Damage.Components; + +/// +/// Passively damages the entity on a specified interval. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class PassiveDamageComponent : Component +{ + /// + /// The entitys' states that passive damage will apply in + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public List AllowedStates = new(); + + /// + /// Damage / Healing per interval dealt to the entity every interval + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public DamageSpecifier Damage = new(); + + /// + /// Delay between damage events in seconds + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float Interval = 1f; + + /// + /// The maximum HP the damage will be given to. If 0, disabled. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public FixedPoint2 DamageCap = 0; + + [DataField("nextDamage", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan NextDamage = TimeSpan.Zero; +} diff --git a/Content.Shared/Damage/Systems/PassiveDamageSystem.cs b/Content.Shared/Damage/Systems/PassiveDamageSystem.cs new file mode 100644 index 0000000000..5a37d6a6e6 --- /dev/null +++ b/Content.Shared/Damage/Systems/PassiveDamageSystem.cs @@ -0,0 +1,55 @@ +using Content.Shared.Damage.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.Mobs.Components; +using Content.Shared.FixedPoint; +using Robust.Shared.Timing; + +namespace Content.Shared.Damage; + +public sealed class PassiveDamageSystem : EntitySystem +{ + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPendingMapInit); + } + + private void OnPendingMapInit(EntityUid uid, PassiveDamageComponent component, MapInitEvent args) + { + component.NextDamage = _timing.CurTime + TimeSpan.FromSeconds(1f); + } + + // Every tick, attempt to damage entities + public override void Update(float frameTime) + { + base.Update(frameTime); + var curTime = _timing.CurTime; + + // Go through every entity with the component + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp, out var damage, out var mobState)) + { + // Make sure they're up for a damage tick + if (comp.NextDamage > curTime) + continue; + + if (comp.DamageCap != 0 && damage.TotalDamage >= comp.DamageCap) + continue; + + // Set the next time they can take damage + comp.NextDamage = curTime + TimeSpan.FromSeconds(1f); + + // Damage them + foreach (var allowedState in comp.AllowedStates) + { + if(allowedState == mobState.CurrentState) + _damageable.TryChangeDamage(uid, comp.Damage, true, false, damage); + } + } + } +} diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 422e1aa4e1..74c676eb68 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -237,6 +237,15 @@ damage: types: Blunt: 0.55 #per second, scales with pressure and other constants. + - type: PassiveDamage # Slight passive regen. Assuming one damage type, comes out to about 4 damage a minute. + allowedStates: + - Alive + damageCap: 20 + damage: + types: + Heat: -0.07 + groups: + Brute: -0.07 # Organs - type: StatusEffects allowed: