Fire extinguishers can now extinguish items, including when held/worn (#36267)
* Fire extinguishers now put out candles This did not actually require any changes to flammable or extinguishers directly, the only necessary changes were to make the collision actually work. Vapor entities (also used for fire extinguishers) now have a collision layer, so they can hit items. Added a new FlammableSetCollisionWake component to actually enable collision on candles while they are lit, because otherwise CollisionWake on entities gets in the way too. * Extinguishing items is now relayed to held/worn items This means held candles get extinguished too. Involved moving the core logic of ExtinguishReaction into an event so that it can be relayed via the existing hand/inventory relay logic. * Add helper functions for subscribing to relayed events. Use these in FlammableSystem * Make extinguishers work on cigarettes too A bunch of renaming to make the rest of my code work with SmokableComponent --------- Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
committed by
GitHub
parent
d0b4f4744c
commit
843d79be5f
@@ -23,6 +23,7 @@ using Content.Shared.Timing;
|
||||
using Content.Shared.Toggleable;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Hands;
|
||||
using Robust.Server.Audio;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Events;
|
||||
@@ -73,6 +74,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
SubscribeLocalEvent<FlammableComponent, TileFireEvent>(OnTileFire);
|
||||
SubscribeLocalEvent<FlammableComponent, RejuvenateEvent>(OnRejuvenate);
|
||||
SubscribeLocalEvent<FlammableComponent, ResistFireAlertEvent>(OnResistFireAlert);
|
||||
Subs.SubscribeWithRelay<FlammableComponent, ExtinguishEvent>(OnExtinguishEvent);
|
||||
|
||||
SubscribeLocalEvent<IgniteOnCollideComponent, StartCollideEvent>(IgniteOnCollide);
|
||||
SubscribeLocalEvent<IgniteOnCollideComponent, LandEvent>(OnIgniteLand);
|
||||
@@ -84,6 +86,14 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
SubscribeLocalEvent<IgniteOnHeatDamageComponent, DamageChangedEvent>(OnDamageChanged);
|
||||
}
|
||||
|
||||
private void OnExtinguishEvent(Entity<FlammableComponent> ent, ref ExtinguishEvent args)
|
||||
{
|
||||
// You know I'm really not sure if having AdjustFireStacks *after* Extinguish,
|
||||
// but I'm just moving this code, not questioning it.
|
||||
Extinguish(ent, ent.Comp);
|
||||
AdjustFireStacks(ent, args.FireStacksAdjustment, ent.Comp);
|
||||
}
|
||||
|
||||
private void OnMeleeHit(EntityUid uid, IgniteOnMeleeHitComponent component, MeleeHitEvent args)
|
||||
{
|
||||
foreach (var entity in args.HitEntities)
|
||||
@@ -315,6 +325,9 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
_ignitionSourceSystem.SetIgnited(uid, false);
|
||||
|
||||
var extinguished = new ExtinguishedEvent();
|
||||
RaiseLocalEvent(uid, ref extinguished);
|
||||
|
||||
UpdateAppearance(uid, flammable);
|
||||
}
|
||||
|
||||
@@ -336,6 +349,9 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
else
|
||||
_adminLogger.Add(LogType.Flammable, $"{ToPrettyString(uid):target} set on fire by {ToPrettyString(ignitionSource):actor}");
|
||||
flammable.OnFire = true;
|
||||
|
||||
var extinguished = new IgnitedEvent();
|
||||
RaiseLocalEvent(uid, ref extinguished);
|
||||
}
|
||||
|
||||
UpdateAppearance(uid, flammable);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.EntityEffects;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -20,17 +19,17 @@ namespace Content.Server.EntityEffects.Effects
|
||||
|
||||
public override void Effect(EntityEffectBaseArgs args)
|
||||
{
|
||||
if (!args.EntityManager.TryGetComponent(args.TargetEntity, out FlammableComponent? flammable)) return;
|
||||
var ev = new ExtinguishEvent
|
||||
{
|
||||
FireStacksAdjustment = FireStacksAdjustment,
|
||||
};
|
||||
|
||||
var flammableSystem = args.EntityManager.System<FlammableSystem>();
|
||||
flammableSystem.Extinguish(args.TargetEntity, flammable);
|
||||
if (args is EntityEffectReagentArgs reagentArgs)
|
||||
{
|
||||
flammableSystem.AdjustFireStacks(reagentArgs.TargetEntity, FireStacksAdjustment * (float) reagentArgs.Quantity, flammable);
|
||||
} else
|
||||
{
|
||||
flammableSystem.AdjustFireStacks(args.TargetEntity, FireStacksAdjustment, flammable);
|
||||
ev.FireStacksAdjustment *= (float)reagentArgs.Quantity;
|
||||
}
|
||||
|
||||
args.EntityManager.EventBus.RaiseLocalEvent(args.TargetEntity, ref ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ using Content.Shared.Temperature;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using System.Linq;
|
||||
using Content.Shared.Atmos;
|
||||
|
||||
namespace Content.Server.Nutrition.EntitySystems
|
||||
{
|
||||
@@ -48,12 +49,19 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
SubscribeLocalEvent<SmokableComponent, IsHotEvent>(OnSmokableIsHotEvent);
|
||||
SubscribeLocalEvent<SmokableComponent, ComponentShutdown>(OnSmokableShutdownEvent);
|
||||
SubscribeLocalEvent<SmokableComponent, GotEquippedEvent>(OnSmokeableEquipEvent);
|
||||
Subs.SubscribeWithRelay<SmokableComponent, ExtinguishEvent>(OnExtinguishEvent);
|
||||
|
||||
InitializeCigars();
|
||||
InitializePipes();
|
||||
InitializeVapes();
|
||||
}
|
||||
|
||||
private void OnExtinguishEvent(Entity<SmokableComponent> ent, ref ExtinguishEvent args)
|
||||
{
|
||||
if (ent.Comp.State == SmokableState.Lit)
|
||||
SetSmokableState(ent, SmokableState.Burnt, ent);
|
||||
}
|
||||
|
||||
public void SetSmokableState(EntityUid uid, SmokableState state, SmokableComponent? smokable = null,
|
||||
AppearanceComponent? appearance = null, ClothingComponent? clothing = null)
|
||||
{
|
||||
@@ -74,10 +82,20 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
_items.SetHeldPrefix(uid, newState);
|
||||
|
||||
if (state == SmokableState.Lit)
|
||||
{
|
||||
var igniteEvent = new IgnitedEvent();
|
||||
RaiseLocalEvent(uid, ref igniteEvent);
|
||||
|
||||
_active.Add(uid);
|
||||
}
|
||||
else
|
||||
{
|
||||
var igniteEvent = new ExtinguishedEvent();
|
||||
RaiseLocalEvent(uid, ref igniteEvent);
|
||||
|
||||
_active.Remove(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSmokableIsHotEvent(Entity<SmokableComponent> entity, ref IsHotEvent args)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Atmos.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Makes entities with extinguishing behavior automatically enable/disable <see cref="CollisionWakeComponent"/>,
|
||||
/// so they can be extinguished with fire extinguishers.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent]
|
||||
public sealed partial class ExtinguishableSetCollisionWakeComponent : Component;
|
||||
@@ -0,0 +1,30 @@
|
||||
using Content.Shared.Atmos.Components;
|
||||
|
||||
namespace Content.Shared.Atmos.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// Implements <see cref="ExtinguishableSetCollisionWakeComponent"/>.
|
||||
/// </summary>
|
||||
public sealed class ExtinguishableSetCollisionWakeSystem : EntitySystem
|
||||
{
|
||||
[Dependency]
|
||||
private readonly CollisionWakeSystem _collisionWake = null!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ExtinguishableSetCollisionWakeComponent, ExtinguishedEvent>(HandleExtinguished);
|
||||
SubscribeLocalEvent<ExtinguishableSetCollisionWakeComponent, IgnitedEvent>(HandleIgnited);
|
||||
}
|
||||
|
||||
private void HandleExtinguished(Entity<ExtinguishableSetCollisionWakeComponent> ent, ref ExtinguishedEvent args)
|
||||
{
|
||||
_collisionWake.SetEnabled(ent, true);
|
||||
}
|
||||
|
||||
private void HandleIgnited(Entity<ExtinguishableSetCollisionWakeComponent> ent, ref IgnitedEvent args)
|
||||
{
|
||||
_collisionWake.SetEnabled(ent, false);
|
||||
}
|
||||
}
|
||||
42
Content.Shared/Atmos/FireEvents.cs
Normal file
42
Content.Shared/Atmos/FireEvents.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
|
||||
namespace Content.Shared.Atmos;
|
||||
|
||||
// NOTE: These components are currently not raised on the client, only on the server.
|
||||
|
||||
/// <summary>
|
||||
/// An entity has had an existing effect applied to it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This does not necessarily mean the effect is strong enough to fully extinguish the entity in one go.
|
||||
/// </remarks>
|
||||
[ByRefEvent]
|
||||
public struct ExtinguishEvent : IInventoryRelayEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Amount of firestacks changed. Should be a negative number.
|
||||
/// </summary>
|
||||
public float FireStacksAdjustment;
|
||||
|
||||
SlotFlags IInventoryRelayEvent.TargetSlots => SlotFlags.WITHOUT_POCKET;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A flammable entity has been extinguished.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This can occur on both <c>Flammable</c> entities as well as <see cref="SmokableComponent"/>.
|
||||
/// </remarks>
|
||||
/// <seealso cref="ExtinguishEvent"/>
|
||||
[ByRefEvent]
|
||||
public struct ExtinguishedEvent;
|
||||
|
||||
/// <summary>
|
||||
/// A flammable entity has been ignited.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This can occur on both <c>Flammable</c> entities as well as <see cref="SmokableComponent"/>.
|
||||
/// </remarks>
|
||||
[ByRefEvent]
|
||||
public struct IgnitedEvent;
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Camera;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
@@ -11,14 +12,31 @@ public abstract partial class SharedHandsSystem
|
||||
SubscribeLocalEvent<HandsComponent, GetEyeOffsetRelayedEvent>(RelayEvent);
|
||||
SubscribeLocalEvent<HandsComponent, GetEyePvsScaleRelayedEvent>(RelayEvent);
|
||||
SubscribeLocalEvent<HandsComponent, RefreshMovementSpeedModifiersEvent>(RelayEvent);
|
||||
|
||||
// By-ref events.
|
||||
SubscribeLocalEvent<HandsComponent, ExtinguishEvent>(RefRelayEvent);
|
||||
}
|
||||
|
||||
private void RelayEvent<T>(Entity<HandsComponent> entity, ref T args) where T : EntityEventArgs
|
||||
{
|
||||
CoreRelayEvent(entity, ref args);
|
||||
}
|
||||
|
||||
private void RefRelayEvent<T>(Entity<HandsComponent> entity, ref T args)
|
||||
{
|
||||
var ev = CoreRelayEvent(entity, ref args);
|
||||
args = ev.Args;
|
||||
}
|
||||
|
||||
private HeldRelayedEvent<T> CoreRelayEvent<T>(Entity<HandsComponent> entity, ref T args)
|
||||
{
|
||||
var ev = new HeldRelayedEvent<T>(args);
|
||||
|
||||
foreach (var held in EnumerateHeld(entity, entity.Comp))
|
||||
{
|
||||
RaiseLocalEvent(held, ref ev);
|
||||
}
|
||||
|
||||
return ev;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.Armor;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Hypospray.Events;
|
||||
@@ -49,6 +50,7 @@ public partial class InventorySystem
|
||||
SubscribeLocalEvent<InventoryComponent, GetSpeedModifierContactCapEvent>(RefRelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, GetSlowedOverSlipperyModifierEvent>(RefRelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, ModifySlowOnDamageSpeedEvent>(RefRelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, ExtinguishEvent>(RefRelayInventoryEvent);
|
||||
|
||||
// Eye/vision events
|
||||
SubscribeLocalEvent<InventoryComponent, CanSeeAttemptEvent>(RelayInventoryEvent);
|
||||
|
||||
123
Content.Shared/Inventory/RelaySubscriptionHelpers.cs
Normal file
123
Content.Shared/Inventory/RelaySubscriptionHelpers.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using Content.Shared.Hands;
|
||||
|
||||
namespace Content.Shared.Inventory;
|
||||
|
||||
/// <summary>
|
||||
/// Helper functions for subscribing to component events that are also relayed via hands/inventory.
|
||||
/// </summary>
|
||||
public static class RelaySubscriptionHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Subscribe to an event, along with different relayed event wrappers, in one call.
|
||||
/// </summary>
|
||||
/// <param name="subs">Subscriptions for the entity system we're subscribing on.</param>
|
||||
/// <param name="handler">The event handler to be called for the event.</param>
|
||||
/// <param name="baseEvent">Whether to subscribe the base event type.</param>
|
||||
/// <param name="inventory">Whether to subscribe for <see cref="T:Content.Shared.Inventory.InventoryRelayedEvent`1"/>.</param>
|
||||
/// <param name="held">Whether to subscribe for <see cref="T:Content.Shared.Hands.HeldRelayedEvent`1"/>.</param>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.EntitySystem.SubscribeLocalEvent``2(Robust.Shared.GameObjects.EntityEventRefHandler{``0,``1},System.Type[],System.Type[])"/>
|
||||
public static void SubscribeWithRelay<TComp, TEvent>(
|
||||
this EntitySystem.Subscriptions subs,
|
||||
EntityEventRefHandler<TComp, TEvent> handler,
|
||||
bool baseEvent = true,
|
||||
bool inventory = true,
|
||||
bool held = true)
|
||||
where TEvent : notnull
|
||||
where TComp : IComponent
|
||||
{
|
||||
if (baseEvent)
|
||||
subs.SubscribeLocalEvent(handler);
|
||||
|
||||
if (inventory)
|
||||
{
|
||||
subs.SubscribeLocalEvent((Entity<TComp> ent, ref InventoryRelayedEvent<TEvent> ev) =>
|
||||
{
|
||||
handler(ent, ref ev.Args);
|
||||
});
|
||||
}
|
||||
|
||||
if (held)
|
||||
{
|
||||
subs.SubscribeLocalEvent((Entity<TComp> ent, ref HeldRelayedEvent<TEvent> ev) =>
|
||||
{
|
||||
handler(ent, ref ev.Args);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to an event, along with different relayed event wrappers, in one call.
|
||||
/// </summary>
|
||||
/// <param name="subs">Subscriptions for the entity system we're subscribing on.</param>
|
||||
/// <param name="handler">The event handler to be called for the event.</param>
|
||||
/// <param name="baseEvent">Whether to subscribe the base event type.</param>
|
||||
/// <param name="inventory">Whether to subscribe for <see cref="T:Content.Shared.Inventory.InventoryRelayedEvent`1"/>.</param>
|
||||
/// <param name="held">Whether to subscribe for <see cref="T:Content.Shared.Hands.HeldRelayedEvent`1"/>.</param>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.EntitySystem.SubscribeLocalEvent``2(Robust.Shared.GameObjects.ComponentEventHandler{``0,``1},System.Type[],System.Type[])"/>
|
||||
public static void SubscribeWithRelay<TComp, TEvent>(
|
||||
this EntitySystem.Subscriptions subs,
|
||||
ComponentEventHandler<TComp, TEvent> handler,
|
||||
bool baseEvent = true,
|
||||
bool inventory = true,
|
||||
bool held = true)
|
||||
where TEvent : notnull
|
||||
where TComp : IComponent
|
||||
{
|
||||
if (baseEvent)
|
||||
subs.SubscribeLocalEvent(handler);
|
||||
|
||||
if (inventory)
|
||||
{
|
||||
subs.SubscribeLocalEvent((EntityUid uid, TComp component, InventoryRelayedEvent<TEvent> args) =>
|
||||
{
|
||||
handler(uid, component, args.Args);
|
||||
});
|
||||
}
|
||||
|
||||
if (held)
|
||||
{
|
||||
subs.SubscribeLocalEvent((EntityUid uid, TComp component, HeldRelayedEvent<TEvent> args) =>
|
||||
{
|
||||
handler(uid, component, args.Args);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to an event, along with different relayed event wrappers, in one call.
|
||||
/// </summary>
|
||||
/// <param name="subs">Subscriptions for the entity system we're subscribing on.</param>
|
||||
/// <param name="handler">The event handler to be called for the event.</param>
|
||||
/// <param name="baseEvent">Whether to subscribe the base event type.</param>
|
||||
/// <param name="inventory">Whether to subscribe for <see cref="T:Content.Shared.Inventory.InventoryRelayedEvent`1"/>.</param>
|
||||
/// <param name="held">Whether to subscribe for <see cref="T:Content.Shared.Hands.HeldRelayedEvent`1"/>.</param>
|
||||
/// <seealso cref="M:Robust.Shared.GameObjects.EntitySystem.SubscribeLocalEvent``2(Robust.Shared.GameObjects.ComponentEventRefHandler{``0,``1},System.Type[],System.Type[])"/>
|
||||
public static void SubscribeWithRelay<TComp, TEvent>(
|
||||
this EntitySystem.Subscriptions subs,
|
||||
ComponentEventRefHandler<TComp, TEvent> handler,
|
||||
bool baseEvent = true,
|
||||
bool inventory = true,
|
||||
bool held = true)
|
||||
where TEvent : notnull
|
||||
where TComp : IComponent
|
||||
{
|
||||
if (baseEvent)
|
||||
subs.SubscribeLocalEvent(handler);
|
||||
|
||||
if (inventory)
|
||||
{
|
||||
subs.SubscribeLocalEvent((EntityUid uid, TComp component, ref InventoryRelayedEvent<TEvent> args) =>
|
||||
{
|
||||
handler(uid, component, ref args.Args);
|
||||
});
|
||||
}
|
||||
|
||||
if (held)
|
||||
{
|
||||
subs.SubscribeLocalEvent((EntityUid uid, TComp component, ref HeldRelayedEvent<TEvent> args) =>
|
||||
{
|
||||
handler(uid, component, ref args.Args);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,10 @@
|
||||
parent: BaseItem
|
||||
abstract: true
|
||||
components:
|
||||
- type: Reactive
|
||||
groups:
|
||||
Extinguish: [ Touch ]
|
||||
- type: ExtinguishableSetCollisionWake
|
||||
- type: Smokable
|
||||
- type: Sprite
|
||||
- type: Appearance
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
variation: 0.05
|
||||
volume: 10
|
||||
- type: UseDelay
|
||||
- type: ExtinguishableSetCollisionWake
|
||||
- type: Flammable
|
||||
fireSpread: false
|
||||
canResistFire: false
|
||||
|
||||
@@ -130,6 +130,8 @@
|
||||
mask:
|
||||
- FullTileMask
|
||||
- Opaque
|
||||
layer:
|
||||
- ItemMask
|
||||
- type: Appearance
|
||||
- type: VaporVisuals
|
||||
|
||||
|
||||
Reference in New Issue
Block a user