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;
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);
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)
_ignitionSourceSystem.SetIgnited(uid, false);
+ var extinguished = new ExtinguishedEvent();
+ RaiseLocalEvent(uid, ref extinguished);
+
UpdateAppearance(uid, flammable);
}
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);
-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;
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);
}
}
}
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using System.Linq;
+using Content.Shared.Atmos;
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)
{
_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)
--- /dev/null
+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;
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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;
+using Content.Shared.Atmos;
using Content.Shared.Camera;
using Content.Shared.Hands.Components;
using Content.Shared.Movement.Systems;
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;
}
}
using Content.Shared.Armor;
+using Content.Shared.Atmos;
using Content.Shared.Chat;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Hypospray.Events;
SubscribeLocalEvent<InventoryComponent, GetSpeedModifierContactCapEvent>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, GetSlowedOverSlipperyModifierEvent>(RefRelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, ModifySlowOnDamageSpeedEvent>(RefRelayInventoryEvent);
+ SubscribeLocalEvent<InventoryComponent, ExtinguishEvent>(RefRelayInventoryEvent);
// Eye/vision events
SubscribeLocalEvent<InventoryComponent, CanSeeAttemptEvent>(RelayInventoryEvent);
--- /dev/null
+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);
+ });
+ }
+ }
+}
parent: BaseItem
abstract: true
components:
+ - type: Reactive
+ groups:
+ Extinguish: [ Touch ]
+ - type: ExtinguishableSetCollisionWake
- type: Smokable
- type: Sprite
- type: Appearance
variation: 0.05
volume: 10
- type: UseDelay
+ - type: ExtinguishableSetCollisionWake
- type: Flammable
fireSpread: false
canResistFire: false
mask:
- FullTileMask
- Opaque
+ layer:
+ - ItemMask
- type: Appearance
- type: VaporVisuals