fire extinguisher using item toggle (#29906)

* move SprayAttemptEvent to shared

* add SolutionTransferredEvent

* replace FireExtinguisher with SpraySafety

* update fire extinguisher yml

* invert visuals

* always handle event in solution transfer, it makes popups

* instantly fill it

* untroll

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
This commit is contained in:
deltanedas
2024-07-13 04:17:10 +00:00
committed by GitHub
parent 87fa6075b6
commit 7b590122b6
10 changed files with 118 additions and 209 deletions

View File

@@ -1,7 +0,0 @@
using Content.Shared.Extinguisher;
using Robust.Shared.GameStates;
namespace Content.Client.Extinguisher;
[RegisterComponent]
public sealed partial class FireExtinguisherComponent : SharedFireExtinguisherComponent;

View File

@@ -1,10 +0,0 @@
using Content.Shared.Extinguisher;
using Robust.Shared.GameStates;
namespace Content.Server.Extinguisher;
[RegisterComponent]
[Access(typeof(FireExtinguisherSystem))]
public sealed partial class FireExtinguisherComponent : SharedFireExtinguisherComponent
{
}

View File

@@ -1,139 +0,0 @@
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Fluids.EntitySystems;
using Content.Server.Popups;
using Content.Shared.Chemistry.Components;
using Content.Shared.Extinguisher;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Verbs;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
namespace Content.Server.Extinguisher;
public sealed class FireExtinguisherSystem : EntitySystem
{
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FireExtinguisherComponent, ComponentInit>(OnFireExtinguisherInit);
SubscribeLocalEvent<FireExtinguisherComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<FireExtinguisherComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<FireExtinguisherComponent, GetVerbsEvent<InteractionVerb>>(OnGetInteractionVerbs);
SubscribeLocalEvent<FireExtinguisherComponent, SprayAttemptEvent>(OnSprayAttempt);
}
private void OnFireExtinguisherInit(Entity<FireExtinguisherComponent> entity, ref ComponentInit args)
{
if (entity.Comp.HasSafety)
{
UpdateAppearance((entity.Owner, entity.Comp));
}
}
private void OnUseInHand(Entity<FireExtinguisherComponent> entity, ref UseInHandEvent args)
{
if (args.Handled)
return;
ToggleSafety((entity.Owner, entity.Comp), args.User);
args.Handled = true;
}
private void OnAfterInteract(Entity<FireExtinguisherComponent> entity, ref AfterInteractEvent args)
{
if (args.Target == null || !args.CanReach)
{
return;
}
if (args.Handled)
return;
if (entity.Comp.HasSafety && entity.Comp.Safety)
{
_popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-safety-on-message"), entity.Owner, args.User);
return;
}
if (args.Target is not { Valid: true } target ||
!_solutionContainerSystem.TryGetDrainableSolution(target, out var targetSoln, out var targetSolution) ||
!_solutionContainerSystem.TryGetRefillableSolution(entity.Owner, out var containerSoln, out var containerSolution))
{
return;
}
args.Handled = true;
// TODO: why is this copy paste shit here just have fire extinguisher cancel transfer when safety is on
var transfer = containerSolution.AvailableVolume;
if (TryComp<SolutionTransferComponent>(entity.Owner, out var solTrans))
{
transfer = solTrans.TransferAmount;
}
transfer = FixedPoint2.Min(transfer, targetSolution.Volume);
if (transfer > 0)
{
var drained = _solutionContainerSystem.Drain(target, targetSoln.Value, transfer);
_solutionContainerSystem.TryAddSolution(containerSoln.Value, drained);
_audio.PlayPvs(entity.Comp.RefillSound, entity.Owner);
_popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-after-interact-refilled-message", ("owner", entity.Owner)),
entity.Owner, args.Target.Value);
}
}
private void OnGetInteractionVerbs(Entity<FireExtinguisherComponent> entity, ref GetVerbsEvent<InteractionVerb> args)
{
if (!args.CanAccess || !args.CanInteract)
return;
var user = args.User;
var verb = new InteractionVerb
{
Act = () => ToggleSafety((entity.Owner, entity.Comp), user),
Text = Loc.GetString("fire-extinguisher-component-verb-text"),
};
args.Verbs.Add(verb);
}
private void OnSprayAttempt(Entity<FireExtinguisherComponent> entity, ref SprayAttemptEvent args)
{
if (entity.Comp.HasSafety && entity.Comp.Safety)
{
_popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-safety-on-message"), entity, args.User);
args.Cancel();
}
}
private void UpdateAppearance(Entity<FireExtinguisherComponent, AppearanceComponent?> entity)
{
if (!Resolve(entity, ref entity.Comp2, false))
return;
if (entity.Comp1.HasSafety)
{
_appearance.SetData(entity, FireExtinguisherVisuals.Safety, entity.Comp1.Safety, entity.Comp2);
}
}
public void ToggleSafety(Entity<FireExtinguisherComponent?> extinguisher, EntityUid user)
{
if (!Resolve(extinguisher, ref extinguisher.Comp))
return;
extinguisher.Comp.Safety = !extinguisher.Comp.Safety;
_audio.PlayPvs(extinguisher.Comp.SafetySound, extinguisher, AudioParams.Default.WithVariation(0.125f).WithVolume(-4f));
UpdateAppearance((extinguisher.Owner, extinguisher.Comp));
}
}

View File

@@ -1,11 +1,11 @@
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Chemistry.EntitySystems;
using Content.Server.Extinguisher;
using Content.Server.Fluids.Components;
using Content.Server.Gravity;
using Content.Server.Popups;
using Content.Shared.FixedPoint;
using Content.Shared.Fluids;
using Content.Shared.Interaction;
using Content.Shared.Timing;
using Content.Shared.Vapor;
@@ -34,7 +34,7 @@ public sealed class SpraySystem : EntitySystem
{
base.Initialize();
SubscribeLocalEvent<SprayComponent, AfterInteractEvent>(OnAfterInteract, after: new[] { typeof(FireExtinguisherSystem) });
SubscribeLocalEvent<SprayComponent, AfterInteractEvent>(OnAfterInteract);
}
private void OnAfterInteract(Entity<SprayComponent> entity, ref AfterInteractEvent args)
@@ -48,7 +48,7 @@ public sealed class SpraySystem : EntitySystem
return;
var ev = new SprayAttemptEvent(args.User);
RaiseLocalEvent(entity, ev);
RaiseLocalEvent(entity, ref ev);
if (ev.Cancelled)
return;
@@ -148,13 +148,3 @@ public sealed class SpraySystem : EntitySystem
_useDelay.TryResetDelay((entity, useDelay));
}
}
public sealed class SprayAttemptEvent : CancellableEntityEventArgs
{
public EntityUid User;
public SprayAttemptEvent(EntityUid user)
{
User = user;
}
}

View File

@@ -117,6 +117,7 @@ public sealed class SolutionTransferSystem : EntitySystem
transferAmount = FixedPoint2.Min(transferAmount, maxRefill);
var transferred = Transfer(args.User, target, targetSoln.Value, uid, ownerSoln.Value, transferAmount);
args.Handled = true;
if (transferred > 0)
{
var toTheBrim = ownerRefill.AvailableVolume == 0;
@@ -125,8 +126,6 @@ public sealed class SolutionTransferSystem : EntitySystem
: "comp-solution-transfer-fill-normal";
_popup.PopupClient(Loc.GetString(msg, ("owner", args.Target), ("amount", transferred), ("target", uid)), uid, args.User);
args.Handled = true;
return;
}
}
@@ -143,13 +142,11 @@ public sealed class SolutionTransferSystem : EntitySystem
transferAmount = FixedPoint2.Min(transferAmount, maxRefill);
var transferred = Transfer(args.User, uid, ownerSoln.Value, target, targetSoln.Value, transferAmount);
args.Handled = true;
if (transferred > 0)
{
var message = Loc.GetString("comp-solution-transfer-transfer-solution", ("amount", transferred), ("target", target));
_popup.PopupClient(message, uid, args.User);
args.Handled = true;
}
}
}
@@ -202,6 +199,9 @@ public sealed class SolutionTransferSystem : EntitySystem
var solution = _solution.SplitSolution(source, actualAmount);
_solution.AddSolution(target, solution);
var ev = new SolutionTransferredEvent(sourceEntity, targetEntity, user, actualAmount);
RaiseLocalEvent(targetEntity, ref ev);
_adminLogger.Add(LogType.Action, LogImpact.Medium,
$"{ToPrettyString(user):player} transferred {SharedSolutionContainerSystem.ToPrettyString(solution)} to {ToPrettyString(targetEntity):target}, which now contains {SharedSolutionContainerSystem.ToPrettyString(targetSolution)}");
@@ -225,3 +225,9 @@ public record struct SolutionTransferAttemptEvent(EntityUid From, EntityUid To,
CancelReason = reason;
}
}
/// <summary>
/// Raised on the target entity when a non-zero amount of solution gets transferred.
/// </summary>
[ByRefEvent]
public record struct SolutionTransferredEvent(EntityUid From, EntityUid To, EntityUid User, FixedPoint2 Amount);

View File

@@ -1,25 +0,0 @@
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Extinguisher;
[NetworkedComponent]
public abstract partial class SharedFireExtinguisherComponent : Component
{
[DataField("refillSound")] public SoundSpecifier RefillSound = new SoundPathSpecifier("/Audio/Effects/refill.ogg");
[DataField("hasSafety")] public bool HasSafety = true;
[DataField("safety")] public bool Safety = true;
[DataField("safetySound")]
public SoundSpecifier SafetySound { get; private set; } = new SoundPathSpecifier("/Audio/Machines/button.ogg");
}
[Serializable, NetSerializable]
public enum FireExtinguisherVisuals : byte
{
Safety
}

View File

@@ -0,0 +1,24 @@
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
namespace Content.Shared.Fluids.Components;
/// <summary>
/// Uses <c>ItemToggle</c> to control safety for a spray item.
/// You can't spray or refill it while safety is on.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(SpraySafetySystem))]
public sealed partial class SpraySafetyComponent : Component
{
/// <summary>
/// Popup shown when trying to spray or refill with safety on.
/// </summary>
[DataField]
public LocId Popup = "fire-extinguisher-component-safety-on-message";
/// <summary>
/// Sound to play after refilling.
/// </summary>
[DataField]
public SoundSpecifier RefillSound = new SoundPathSpecifier("/Audio/Effects/refill.ogg");
}

View File

@@ -34,3 +34,15 @@ public sealed partial class AbsorbantDoAfterEvent : DoAfterEvent
public override DoAfterEvent Clone() => this;
}
/// <summary>
/// Raised when trying to spray something, for example a fire extinguisher.
/// </summary>
[ByRefEvent]
public record struct SprayAttemptEvent(EntityUid User, bool Cancelled = false)
{
public void Cancel()
{
Cancelled = true;
}
}

View File

@@ -0,0 +1,44 @@
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Fluids.Components;
using Content.Shared.Item.ItemToggle;
using Content.Shared.Popups;
using Robust.Shared.Audio.Systems;
namespace Content.Shared.Fluids;
public sealed class SpraySafetySystem : EntitySystem
{
[Dependency] private readonly ItemToggleSystem _toggle = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SpraySafetyComponent, SolutionTransferAttemptEvent>(OnTransferAttempt);
SubscribeLocalEvent<SpraySafetyComponent, SolutionTransferredEvent>(OnTransferred);
SubscribeLocalEvent<SpraySafetyComponent, SprayAttemptEvent>(OnSprayAttempt);
}
private void OnTransferAttempt(Entity<SpraySafetyComponent> ent, ref SolutionTransferAttemptEvent args)
{
var (uid, comp) = ent;
if (uid == args.To && !_toggle.IsActivated(uid))
args.Cancel(Loc.GetString(comp.Popup));
}
private void OnTransferred(Entity<SpraySafetyComponent> ent, ref SolutionTransferredEvent args)
{
_audio.PlayPredicted(ent.Comp.RefillSound, ent, args.User);
}
private void OnSprayAttempt(Entity<SpraySafetyComponent> ent, ref SprayAttemptEvent args)
{
if (!_toggle.IsActivated(ent.Owner))
{
_popup.PopupEntity(Loc.GetString(ent.Comp.Popup), ent, args.User);
args.Cancel();
}
}
}

View File

@@ -8,7 +8,7 @@
sprite: Objects/Misc/fire_extinguisher.rsi
layers:
- state: fire_extinguisher_closed
map: [ "enabled" ]
map: [ "enum.ToggleVisuals.Layer" ]
- type: Item
sprite: Objects/Misc/fire_extinguisher.rsi
size: Normal
@@ -24,6 +24,8 @@
- type: DrainableSolution
solution: spray
- type: SolutionTransfer
maxTransferAmount: 100
transferAmount: 100
- type: UseDelay
- type: Spray
transferAmount: 10
@@ -34,8 +36,20 @@
vaporAmount: 3
vaporSpread: 90
sprayVelocity: 2.0
- type: FireExtinguisher
hasSafety: true
- type: ItemToggle
soundActivate:
path: /Audio/Machines/button.ogg
params:
variation: 0.125
volume: -4
soundDeactivate:
path: /Audio/Machines/button.ogg
params:
variation: 0.125
volume: -4
- type: ToggleVerb
text: fire-extinguisher-component-verb-text
- type: SpraySafety
- type: MeleeWeapon
wideAnimationRotation: 180
damage:
@@ -46,14 +60,14 @@
- type: Tool
qualities:
- Rolling
speedModifier: 0.5 # its very big, akward to use
speedModifier: 0.5 # its very big, awkward to use
- type: Appearance
- type: GenericVisualizer
visuals:
enum.FireExtinguisherVisuals.Safety:
enabled:
True: { state: fire_extinguisher_closed }
False: { state: fire_extinguisher_open }
enum.ToggleVisuals.Toggled:
enum.ToggleVisuals.Layer:
True: { state: fire_extinguisher_open }
False: { state: fire_extinguisher_closed }
- type: PhysicalComposition
materialComposition:
Steel: 100