Replace BlockSolutionAccessComponent with an attempt event (#26988)

* BlockSolutionAccessComponent now only blocks one specified solution.

* Significant overhaul
Separated spilling when worn functionality into its own component/system.
Removed BlockSolutionAccessComponent.
Added an event for solution access.
This commit is contained in:
Tayrtahn
2024-06-20 23:04:02 -04:00
committed by GitHub
parent f94d663452
commit 880fef507d
7 changed files with 104 additions and 56 deletions

View File

@@ -27,8 +27,6 @@ public sealed partial class PuddleSystem
SubscribeLocalEvent<SpillableComponent, LandEvent>(SpillOnLand);
// Openable handles the event if it's closed
SubscribeLocalEvent<SpillableComponent, MeleeHitEvent>(SplashOnMeleeHit, after: [typeof(OpenableSystem)]);
SubscribeLocalEvent<SpillableComponent, ClothingGotEquippedEvent>(OnGotEquipped);
SubscribeLocalEvent<SpillableComponent, ClothingGotUnequippedEvent>(OnGotUnequipped);
SubscribeLocalEvent<SpillableComponent, SolutionContainerOverflowEvent>(OnOverflow);
SubscribeLocalEvent<SpillableComponent, SpillDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<SpillableComponent, AttemptPacifiedThrowEvent>(OnAttemptPacifiedThrow);
@@ -97,33 +95,6 @@ public sealed partial class PuddleSystem
}
}
private void OnGotEquipped(Entity<SpillableComponent> entity, ref ClothingGotEquippedEvent args)
{
if (!entity.Comp.SpillWorn)
return;
if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution))
return;
// block access to the solution while worn
AddComp<BlockSolutionAccessComponent>(entity);
if (solution.Volume == 0)
return;
// spill all solution on the player
var drainedSolution = _solutionContainerSystem.Drain(entity.Owner, soln.Value, solution.Volume);
TrySplashSpillAt(entity.Owner, Transform(args.Wearer).Coordinates, drainedSolution, out _);
}
private void OnGotUnequipped(Entity<SpillableComponent> entity, ref ClothingGotUnequippedEvent args)
{
if (!entity.Comp.SpillWorn)
return;
RemCompDeferred<BlockSolutionAccessComponent>(entity);
}
private void SpillOnLand(Entity<SpillableComponent> entity, ref LandEvent args)
{
if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution))

View File

@@ -1,11 +0,0 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Chemistry.Components;
/// <summary>
/// Blocks all attempts to access solutions contained by this entity.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class BlockSolutionAccessComponent : Component
{
}

View File

@@ -48,6 +48,12 @@ public partial record struct SolutionOverflowEvent(Entity<SolutionComponent> Sol
public bool Handled = false;
}
[ByRefEvent]
public partial record struct SolutionAccessAttemptEvent(string SolutionName)
{
public bool Cancelled;
}
/// <summary>
/// Part of Chemistry system deal with SolutionContainers
/// </summary>
@@ -156,12 +162,6 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
[NotNullWhen(true)] out Entity<SolutionComponent>? entity,
bool errorOnMissing = false)
{
if (TryComp(container, out BlockSolutionAccessComponent? blocker))
{
entity = null;
return false;
}
EntityUid uid;
if (name is null)
uid = container;
@@ -170,7 +170,18 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
solutionContainer is ContainerSlot solutionSlot &&
solutionSlot.ContainedEntity is { } containedSolution
)
{
var attemptEv = new SolutionAccessAttemptEvent(name);
RaiseLocalEvent(container, ref attemptEv);
if (attemptEv.Cancelled)
{
entity = null;
return false;
}
uid = containedSolution;
}
else
{
entity = null;
@@ -218,11 +229,14 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
if (!Resolve(container, ref container.Comp, logMissing: false))
yield break;
if (HasComp<BlockSolutionAccessComponent>(container))
yield break;
foreach (var name in container.Comp.Containers)
{
var attemptEv = new SolutionAccessAttemptEvent(name);
RaiseLocalEvent(container, ref attemptEv);
if (attemptEv.Cancelled)
continue;
if (ContainerSystem.GetContainer(container, $"solution@{name}") is ContainerSlot slot && slot.ContainedEntity is { } solutionId)
yield return (name, (solutionId, Comp<SolutionComponent>(solutionId)));
}

View File

@@ -0,0 +1,24 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Fluids.Components;
/// <summary>
/// This entity will spill its contained solution onto the wearer when worn, and its
/// (empty) contents will be inaccessible while still worn.
/// </summary>
[RegisterComponent]
[NetworkedComponent, AutoGenerateComponentState]
public sealed partial class SpillWhenWornComponent : Component
{
/// <summary>
/// Name of the solution to spill.
/// </summary>
[DataField]
public string Solution = "default";
/// <summary>
/// Tracks if this item is currently being worn.
/// </summary>
[DataField, AutoNetworkedField]
public bool IsWorn;
}

View File

@@ -14,13 +14,6 @@ public sealed partial class SpillableComponent : Component
[DataField("solution")]
public string SolutionName = "puddle";
/// <summary>
/// Should this item be spilled when worn as clothing?
/// Doesn't count for pockets or hands.
/// </summary>
[DataField]
public bool SpillWorn = true;
[DataField]
public float? SpillDelay;

View File

@@ -0,0 +1,55 @@
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Clothing;
using Content.Shared.Fluids.Components;
namespace Content.Shared.Fluids.EntitySystems;
/// <inheritdoc cref="SpillWhenWornComponent"/>
public sealed class SpillWhenWornSystem : EntitySystem
{
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
[Dependency] private readonly SharedPuddleSystem _puddle = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SpillWhenWornComponent, ClothingGotEquippedEvent>(OnGotEquipped);
SubscribeLocalEvent<SpillWhenWornComponent, ClothingGotUnequippedEvent>(OnGotUnequipped);
SubscribeLocalEvent<SpillWhenWornComponent, SolutionAccessAttemptEvent>(OnSolutionAccessAttempt);
}
private void OnGotEquipped(Entity<SpillWhenWornComponent> ent, ref ClothingGotEquippedEvent args)
{
if (_solutionContainer.TryGetSolution(ent.Owner, ent.Comp.Solution, out var soln, out var solution)
&& solution.Volume > 0)
{
// Spill all solution on the player
var drainedSolution = _solutionContainer.Drain(ent.Owner, soln.Value, solution.Volume);
_puddle.TrySplashSpillAt(ent.Owner, Transform(args.Wearer).Coordinates, drainedSolution, out _);
}
// Flag as worn after draining, otherwise we'll block ourself from accessing!
ent.Comp.IsWorn = true;
Dirty(ent);
}
private void OnGotUnequipped(Entity<SpillWhenWornComponent> ent, ref ClothingGotUnequippedEvent args)
{
ent.Comp.IsWorn = false;
Dirty(ent);
}
private void OnSolutionAccessAttempt(Entity<SpillWhenWornComponent> ent, ref SolutionAccessAttemptEvent args)
{
// If we're not being worn right now, we don't care
if (!ent.Comp.IsWorn)
return;
// Make sure it's the right solution
if (ent.Comp.Solution != args.SolutionName)
return;
args.Cancelled = true;
}
}

View File

@@ -45,6 +45,8 @@
Blunt: 0
- type: Spillable
solution: bucket
- type: SpillWhenWorn
solution: bucket
- type: DrawableSolution
solution: bucket
- type: RefillableSolution