diff --git a/Content.Server/Atmos/Components/FlammableComponent.cs b/Content.Server/Atmos/Components/FlammableComponent.cs index 94bc78318c..679b551058 100644 --- a/Content.Server/Atmos/Components/FlammableComponent.cs +++ b/Content.Server/Atmos/Components/FlammableComponent.cs @@ -6,16 +6,15 @@ namespace Content.Server.Atmos.Components [RegisterComponent] public sealed partial class FlammableComponent : Component { - [ViewVariables] - public bool Resisting = false; - - [ViewVariables] - public readonly List Collided = new(); + [DataField] + public bool Resisting; [ViewVariables(VVAccess.ReadWrite)] + [DataField] public bool OnFire { get; set; } [ViewVariables(VVAccess.ReadWrite)] + [DataField] public float FireStacks { get; set; } [ViewVariables(VVAccess.ReadWrite)] diff --git a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs index 716c5b88e5..e045b552a2 100644 --- a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs +++ b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs @@ -56,6 +56,7 @@ namespace Content.Server.Atmos.EntitySystems private float _timer; private readonly Dictionary, float> _fireEvents = new(); + private readonly List _toRemove = new(); public override void Initialize() { @@ -73,7 +74,6 @@ namespace Content.Server.Atmos.EntitySystems SubscribeLocalEvent(OnMeleeHit); - SubscribeLocalEvent(OnExtinguishUsingInHand); SubscribeLocalEvent(OnExtinguishActivateInWorld); } @@ -142,29 +142,26 @@ namespace Content.Server.Atmos.EntitySystems private void OnExtinguishActivateInWorld(EntityUid uid, ExtinguishOnInteractComponent component, ActivateInWorldEvent args) { - TryHandExtinguish(uid, component); - } + if (args.Handled) + return; - private void OnExtinguishUsingInHand(EntityUid uid, ExtinguishOnInteractComponent component, UseInHandEvent args) - { - TryHandExtinguish(uid, component); - } - - private void TryHandExtinguish(EntityUid uid, ExtinguishOnInteractComponent component) - { if (!TryComp(uid, out FlammableComponent? flammable)) return; + if (!flammable.OnFire) return; - if (TryComp(uid, out UseDelayComponent? useDelay) && _useDelay.ActiveDelay(uid, useDelay)) + + args.Handled = true; + + if (!_useDelay.BeginDelay(uid)) return; - _useDelay.BeginDelay(uid); _audio.PlayPvs(component.ExtinguishAttemptSound, uid); if (_random.Prob(component.Probability)) { AdjustFireStacks(uid, component.StackDelta, flammable); - } else + } + else { _popup.PopupEntity(Loc.GetString(component.ExtinguishFailed), uid); } @@ -173,52 +170,56 @@ namespace Content.Server.Atmos.EntitySystems { var otherUid = args.OtherEntity; + // Collisions cause events to get raised directed at both entities. We only want to handle this collision + // once, hence the uid check. + if (otherUid.Id < uid.Id) + return; + // Normal hard collisions, though this isn't generally possible since most flammable things are mobs // which don't collide with one another, shouldn't work here. if (args.OtherFixtureId != FlammableFixtureID && args.OurFixtureId != FlammableFixtureID) return; - if (!EntityManager.TryGetComponent(otherUid, out FlammableComponent? otherFlammable)) + if (!flammable.FireSpread) return; - if (!flammable.FireSpread || !otherFlammable.FireSpread) + if (!TryComp(otherUid, out FlammableComponent? otherFlammable) || !otherFlammable.FireSpread) return; + if (!flammable.OnFire && !otherFlammable.OnFire) + return; // Neither are on fire + + if (flammable.OnFire && otherFlammable.OnFire) + { + // Both are on fire -> equalize fire stacks. + var avg = (flammable.FireStacks + otherFlammable.FireStacks) / 2; + flammable.FireStacks = flammable.CanExtinguish ? avg : Math.Max(flammable.FireStacks, avg); + otherFlammable.FireStacks = otherFlammable.CanExtinguish ? avg : Math.Max(otherFlammable.FireStacks, avg); + UpdateAppearance(uid, flammable); + UpdateAppearance(otherUid, otherFlammable); + return; + } + + // Only one is on fire -> attempt to spread the fire. if (flammable.OnFire) { - if (otherFlammable.OnFire) + otherFlammable.FireStacks += flammable.FireStacks / 2; + Ignite(otherUid, uid, otherFlammable); + if (flammable.CanExtinguish) { - if (flammable.CanExtinguish) - { - var fireSplit = (flammable.FireStacks + otherFlammable.FireStacks) / 2; - flammable.FireStacks = fireSplit; - otherFlammable.FireStacks = fireSplit; - } - else - { - otherFlammable.FireStacks = flammable.FireStacks / 2; - } - } - else - { - if (!flammable.CanExtinguish) - { - otherFlammable.FireStacks += flammable.FireStacks / 2; - Ignite(otherUid, uid, otherFlammable); - } - else - { - flammable.FireStacks /= 2; - otherFlammable.FireStacks += flammable.FireStacks; - Ignite(otherUid, uid, otherFlammable); - } + flammable.FireStacks /= 2; + UpdateAppearance(uid, flammable); } } - else if (otherFlammable.OnFire) + else { - otherFlammable.FireStacks /= 2; - flammable.FireStacks += otherFlammable.FireStacks; + flammable.FireStacks += otherFlammable.FireStacks / 2; Ignite(uid, otherUid, flammable); + if (otherFlammable.CanExtinguish) + { + otherFlammable.FireStacks /= 2; + UpdateAppearance(otherUid, otherFlammable); + } } } @@ -254,7 +255,7 @@ namespace Content.Server.Atmos.EntitySystems // This is intended so that matches & candles can re-use code for un-shaded layers on in-hand sprites. // However, this could cause conflicts if something is ACTUALLY both a toggleable light and flammable. // if that ever happens, then fire visuals will need to implement their own in-hand sprite management. - _appearance.SetData(uid, ToggleableLightVisuals.Enabled, true, appearance); + _appearance.SetData(uid, ToggleableLightVisuals.Enabled, flammable.OnFire, appearance); } public void AdjustFireStacks(EntityUid uid, float relativeFireStacks, FlammableComponent? flammable = null) @@ -266,8 +267,8 @@ namespace Content.Server.Atmos.EntitySystems if (flammable.OnFire && flammable.FireStacks <= 0) Extinguish(uid, flammable); - - UpdateAppearance(uid, flammable); + else + UpdateAppearance(uid, flammable); } public void Extinguish(EntityUid uid, FlammableComponent? flammable = null) @@ -282,8 +283,6 @@ namespace Content.Server.Atmos.EntitySystems flammable.OnFire = false; flammable.FireStacks = 0; - flammable.Collided.Clear(); - UpdateAppearance(uid, flammable); } @@ -409,24 +408,6 @@ namespace Content.Server.Atmos.EntitySystems 700f, 50f, uid, true); } - - for (var i = flammable.Collided.Count - 1; i >= 0; i--) - { - var otherUid = flammable.Collided[i]; - - if (!otherUid.IsValid() || !EntityManager.EntityExists(otherUid)) - { - flammable.Collided.RemoveAt(i); - continue; - } - - // TODO: Sloth, please save our souls! - // no - if (!_lookup.GetWorldAABB(uid, transform).Intersects(_lookup.GetWorldAABB(otherUid))) - { - flammable.Collided.RemoveAt(i); - } - } } } } diff --git a/Content.Shared/Timing/UseDelaySystem.cs b/Content.Shared/Timing/UseDelaySystem.cs index 1d1d636b89..17a806f094 100644 --- a/Content.Shared/Timing/UseDelaySystem.cs +++ b/Content.Shared/Timing/UseDelaySystem.cs @@ -77,13 +77,20 @@ public sealed class UseDelaySystem : EntitySystem } } - public void BeginDelay(EntityUid uid, UseDelayComponent? component = null) + /// + /// Attempts tp start a use-delay for some entity. Returns true unless there is already an active delay. + /// + /// + /// Note that this will always return true if the entity does not have a use delay component, as in that case there + /// is no reason to block/prevent an interaction. + /// + public bool BeginDelay(EntityUid uid, UseDelayComponent? component = null) { if (!Resolve(uid, ref component, false)) - return; + return true; if (component.ActiveDelay) - return; + return false; DebugTools.Assert(!_activeDelays.Contains(component)); _activeDelays.Add(component); @@ -91,12 +98,12 @@ public sealed class UseDelaySystem : EntitySystem var currentTime = _gameTiming.CurTime; component.LastUseTime = currentTime; component.DelayEndTime = currentTime + component.Delay; - Dirty(component); + Dirty(uid, component); - // TODO just merge these components? - var cooldown = EnsureComp(component.Owner); + var cooldown = EnsureComp(uid); cooldown.CooldownStart = currentTime; cooldown.CooldownEnd = component.DelayEndTime; + return true; } public bool ActiveDelay(EntityUid uid, UseDelayComponent? component = null)