Spray nozzle can suck puddles into tank directly! (#30600)
* feat: now vacuum cleaner can suck solutions from floor * refactor using AbsorbentSystem instead of separate vacuum cleaner * refactor: remove unused vacuum cleaner files * refactor: renamed ConnectedContainerComponent to SlotBasedConnectedContainerComponent (and system) * fix: fix invalid comp name * fix: no more spray nozzle messaging about water inside bottles etc. * refactor: minor refactor in SlotBasedConnectedContainerSystem and adjustments after merge * refactor: cleanups * refactor: renaming * refactor: update to use _puddleSystem.GetAbsorbentReagents * refactor: changed interactions with SlotBasedConnectedContainerSystem into events * refactor: new sound and action delay adjusted to sound (amount tweaked a bit accordingly, almost) * refactor: added networking for SlotBasedConnectedContainerComponent * fix attribution for vacuum-cleaner-fast.ogg * trying to fix multi-license for mix sound file * remove empty line * refactor: remove trailing whitespace * by ref struct, brother --------- Co-authored-by: pa.pecherskij <pa.pecherskij@interfax.ru> Co-authored-by: EmoGarbage404 <retron404@gmail.com>
This commit is contained in:
@@ -33,6 +33,7 @@ public sealed class AbsorbentTest
|
|||||||
id: {AbsorbentDummyId}
|
id: {AbsorbentDummyId}
|
||||||
components:
|
components:
|
||||||
- type: Absorbent
|
- type: Absorbent
|
||||||
|
useAbsorberSolution: true
|
||||||
- type: SolutionContainerManager
|
- type: SolutionContainerManager
|
||||||
solutions:
|
solutions:
|
||||||
absorbed:
|
absorbed:
|
||||||
@@ -94,7 +95,7 @@ public sealed class AbsorbentTest
|
|||||||
refillable = entityManager.SpawnEntity(RefillableDummyId, coordinates);
|
refillable = entityManager.SpawnEntity(RefillableDummyId, coordinates);
|
||||||
|
|
||||||
entityManager.TryGetComponent(absorbent, out component);
|
entityManager.TryGetComponent(absorbent, out component);
|
||||||
solutionContainerSystem.TryGetSolution(absorbent, AbsorbentComponent.SolutionName, out var absorbentSoln, out var absorbentSolution);
|
solutionContainerSystem.TryGetSolution(absorbent, component.SolutionName, out var absorbentSoln, out var absorbentSolution);
|
||||||
solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSoln, out var refillableSolution);
|
solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSoln, out var refillableSolution);
|
||||||
|
|
||||||
// Arrange
|
// Arrange
|
||||||
@@ -152,7 +153,7 @@ public sealed class AbsorbentTest
|
|||||||
refillable = entityManager.SpawnEntity(SmallRefillableDummyId, coordinates);
|
refillable = entityManager.SpawnEntity(SmallRefillableDummyId, coordinates);
|
||||||
|
|
||||||
entityManager.TryGetComponent(absorbent, out component);
|
entityManager.TryGetComponent(absorbent, out component);
|
||||||
solutionContainerSystem.TryGetSolution(absorbent, AbsorbentComponent.SolutionName, out var absorbentSoln, out var absorbentSolution);
|
solutionContainerSystem.TryGetSolution(absorbent, component.SolutionName, out var absorbentSoln, out var absorbentSolution);
|
||||||
solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSoln, out var refillableSolution);
|
solutionContainerSystem.TryGetRefillableSolution(refillable, out var refillableSoln, out var refillableSolution);
|
||||||
|
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public sealed class InjectorSystem : SharedInjectorSystem
|
|||||||
// Is the target a mob? If yes, use a do-after to give them time to respond.
|
// Is the target a mob? If yes, use a do-after to give them time to respond.
|
||||||
if (HasComp<MobStateComponent>(target) || HasComp<BloodstreamComponent>(target))
|
if (HasComp<MobStateComponent>(target) || HasComp<BloodstreamComponent>(target))
|
||||||
{
|
{
|
||||||
// Are use using an injector capible of targeting a mob?
|
// Are use using an injector capable of targeting a mob?
|
||||||
if (entity.Comp.IgnoreMobs)
|
if (entity.Comp.IgnoreMobs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ namespace Content.Server.Fluids.EntitySystems;
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public sealed class AbsorbentSystem : SharedAbsorbentSystem
|
public sealed class AbsorbentSystem : SharedAbsorbentSystem
|
||||||
{
|
{
|
||||||
|
private static readonly EntProtoId Sparkles = "PuddleSparkle";
|
||||||
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||||
[Dependency] private readonly AudioSystem _audio = default!;
|
[Dependency] private readonly AudioSystem _audio = default!;
|
||||||
[Dependency] private readonly PopupSystem _popups = default!;
|
[Dependency] private readonly PopupSystem _popups = default!;
|
||||||
@@ -51,7 +53,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
|
|||||||
|
|
||||||
private void UpdateAbsorbent(EntityUid uid, AbsorbentComponent component)
|
private void UpdateAbsorbent(EntityUid uid, AbsorbentComponent component)
|
||||||
{
|
{
|
||||||
if (!_solutionContainerSystem.TryGetSolution(uid, AbsorbentComponent.SolutionName, out _, out var solution))
|
if (!_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out _, out var solution))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var oldProgress = component.Progress.ShallowClone();
|
var oldProgress = component.Progress.ShallowClone();
|
||||||
@@ -104,7 +106,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
|
|||||||
|
|
||||||
public void Mop(EntityUid user, EntityUid target, EntityUid used, AbsorbentComponent component)
|
public void Mop(EntityUid user, EntityUid target, EntityUid used, AbsorbentComponent component)
|
||||||
{
|
{
|
||||||
if (!_solutionContainerSystem.TryGetSolution(used, AbsorbentComponent.SolutionName, out var absorberSoln))
|
if (!_solutionContainerSystem.TryGetSolution(used, component.SolutionName, out var absorberSoln))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (TryComp<UseDelayComponent>(used, out var useDelay)
|
if (TryComp<UseDelayComponent>(used, out var useDelay)
|
||||||
@@ -112,7 +114,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// If it's a puddle try to grab from
|
// If it's a puddle try to grab from
|
||||||
if (!TryPuddleInteract(user, used, target, component, useDelay, absorberSoln.Value))
|
if (!TryPuddleInteract(user, used, target, component, useDelay, absorberSoln.Value) && component.UseAbsorberSolution)
|
||||||
{
|
{
|
||||||
// If it's refillable try to transfer
|
// If it's refillable try to transfer
|
||||||
if (!TryRefillableInteract(user, used, target, component, useDelay, absorberSoln.Value))
|
if (!TryRefillableInteract(user, used, target, component, useDelay, absorberSoln.Value))
|
||||||
@@ -282,6 +284,10 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Solution puddleSplit;
|
||||||
|
var isRemoved = false;
|
||||||
|
if (absorber.UseAbsorberSolution)
|
||||||
|
{
|
||||||
// Check if we have any evaporative reagents on our absorber to transfer
|
// Check if we have any evaporative reagents on our absorber to transfer
|
||||||
var absorberSolution = absorberSoln.Comp.Solution;
|
var absorberSolution = absorberSoln.Comp.Solution;
|
||||||
var available = absorberSolution.GetTotalPrototypeQuantity(_puddleSystem.GetAbsorbentReagents(absorberSolution));
|
var available = absorberSolution.GetTotalPrototypeQuantity(_puddleSystem.GetAbsorbentReagents(absorberSolution));
|
||||||
@@ -296,7 +302,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
|
|||||||
var transferMax = absorber.PickupAmount;
|
var transferMax = absorber.PickupAmount;
|
||||||
var transferAmount = available > transferMax ? transferMax : available;
|
var transferAmount = available > transferMax ? transferMax : available;
|
||||||
|
|
||||||
var puddleSplit = puddleSolution.SplitSolutionWithout(transferAmount, _puddleSystem.GetAbsorbentReagents(puddleSolution));
|
puddleSplit = puddleSolution.SplitSolutionWithout(transferAmount, _puddleSystem.GetAbsorbentReagents(puddleSolution));
|
||||||
var absorberSplit = absorberSolution.SplitSolutionWithOnly(puddleSplit.Volume, _puddleSystem.GetAbsorbentReagents(absorberSolution));
|
var absorberSplit = absorberSolution.SplitSolutionWithOnly(puddleSplit.Volume, _puddleSystem.GetAbsorbentReagents(absorberSolution));
|
||||||
|
|
||||||
// Do tile reactions first
|
// Do tile reactions first
|
||||||
@@ -307,11 +313,24 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
|
|||||||
var tileRef = _mapSystem.GetTileRef(gridUid.Value, mapGrid, transform.Coordinates);
|
var tileRef = _mapSystem.GetTileRef(gridUid.Value, mapGrid, transform.Coordinates);
|
||||||
_puddleSystem.DoTileReactions(tileRef, absorberSplit);
|
_puddleSystem.DoTileReactions(tileRef, absorberSplit);
|
||||||
}
|
}
|
||||||
|
|
||||||
_solutionContainerSystem.AddSolution(puddle.Solution.Value, absorberSplit);
|
_solutionContainerSystem.AddSolution(puddle.Solution.Value, absorberSplit);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puddleSplit = puddleSolution.SplitSolutionWithout(absorber.PickupAmount, _puddleSystem.GetAbsorbentReagents(puddleSolution));
|
||||||
|
// Despawn if we're done
|
||||||
|
if (puddleSolution.Volume == FixedPoint2.Zero)
|
||||||
|
{
|
||||||
|
// Spawn a *sparkle*
|
||||||
|
Spawn(Sparkles, GetEntityQuery<TransformComponent>().GetComponent(target).Coordinates);
|
||||||
|
QueueDel(target);
|
||||||
|
isRemoved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_solutionContainerSystem.AddSolution(absorberSoln, puddleSplit);
|
_solutionContainerSystem.AddSolution(absorberSoln, puddleSplit);
|
||||||
|
|
||||||
_audio.PlayPvs(absorber.PickupSound, target);
|
_audio.PlayPvs(absorber.PickupSound, isRemoved ? used : target);
|
||||||
if (useDelay != null)
|
if (useDelay != null)
|
||||||
_useDelay.TryResetDelay((used, useDelay));
|
_useDelay.TryResetDelay((used, useDelay));
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using Content.Shared.Containers;
|
||||||
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.Whitelist;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Chemistry.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Component for marking linked container in character slot, to which entity is bound.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(SlotBasedConnectedContainerSystem)), NetworkedComponent]
|
||||||
|
public sealed partial class SlotBasedConnectedContainerComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The slot in which target container should be.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public SlotFlags TargetSlot;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A whitelist for determining whether container is valid or not .
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public EntityWhitelist? ContainerWhitelist;
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ using System.Linq;
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Content.Shared.Containers;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
@@ -162,6 +163,12 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
|||||||
[NotNullWhen(true)] out Entity<SolutionComponent>? entity,
|
[NotNullWhen(true)] out Entity<SolutionComponent>? entity,
|
||||||
bool errorOnMissing = false)
|
bool errorOnMissing = false)
|
||||||
{
|
{
|
||||||
|
// use connected container instead of entity from arguments, if it exists.
|
||||||
|
var ev = new GetConnectedContainerEvent();
|
||||||
|
RaiseLocalEvent(container, ref ev);
|
||||||
|
if (ev.ContainerEntity.HasValue)
|
||||||
|
container = ev.ContainerEntity.Value;
|
||||||
|
|
||||||
EntityUid uid;
|
EntityUid uid;
|
||||||
if (name is null)
|
if (name is null)
|
||||||
uid = container;
|
uid = container;
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Content.Shared.Chemistry.Components;
|
||||||
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.Whitelist;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
|
||||||
|
namespace Content.Shared.Containers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// System for getting container that is linked to subject entity. Container is supposed to be present in certain character slot.
|
||||||
|
/// Can be used for linking ammo feeder, solution source for spray nozzle, etc.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class SlotBasedConnectedContainerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedContainerSystem _containers = default!;
|
||||||
|
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||||
|
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<SlotBasedConnectedContainerComponent, GetConnectedContainerEvent>(OnGettingConnectedContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try get connected container entity in character slots for <see cref="uid"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">
|
||||||
|
/// Entity for which connected container is required. If <see cref="SlotBasedConnectedContainerComponent"/>
|
||||||
|
/// is used - tries to find container in slot, returns false and null <see cref="slotEntity"/> otherwise.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="slotEntity">Found connected container entity or null.</param>
|
||||||
|
/// <returns>True if connected container was found, false otherwise.</returns>
|
||||||
|
public bool TryGetConnectedContainer(EntityUid uid, [NotNullWhen(true)] out EntityUid? slotEntity)
|
||||||
|
{
|
||||||
|
if (!TryComp<SlotBasedConnectedContainerComponent>(uid, out var component))
|
||||||
|
{
|
||||||
|
slotEntity = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TryGetConnectedContainer(uid, component.TargetSlot, component.ContainerWhitelist, out slotEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGettingConnectedContainer(Entity<SlotBasedConnectedContainerComponent> ent, ref GetConnectedContainerEvent args)
|
||||||
|
{
|
||||||
|
if (TryGetConnectedContainer(ent, ent.Comp.TargetSlot, ent.Comp.ContainerWhitelist, out var val))
|
||||||
|
args.ContainerEntity = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetConnectedContainer(EntityUid uid, SlotFlags slotFlag, EntityWhitelist? providerWhitelist, [NotNullWhen(true)] out EntityUid? slotEntity)
|
||||||
|
{
|
||||||
|
slotEntity = null;
|
||||||
|
|
||||||
|
if (!_containers.TryGetContainingContainer((uid, null, null), out var container))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var user = container.Owner;
|
||||||
|
if (!_inventory.TryGetContainerSlotEnumerator(user, out var enumerator, slotFlag))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (enumerator.NextItem(out var item))
|
||||||
|
{
|
||||||
|
if (_whitelistSystem.IsWhitelistFailOrNull(providerWhitelist, item))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
slotEntity = item;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event for an attempt of getting container, connected to entity on which event was raised.
|
||||||
|
/// Fills <see cref="ContainerEntity"/> if connected container exists.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public struct GetConnectedContainerEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Container entity, if it exists, or null.
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid? ContainerEntity;
|
||||||
|
}
|
||||||
@@ -11,23 +11,28 @@ namespace Content.Shared.Fluids;
|
|||||||
[RegisterComponent, NetworkedComponent]
|
[RegisterComponent, NetworkedComponent]
|
||||||
public sealed partial class AbsorbentComponent : Component
|
public sealed partial class AbsorbentComponent : Component
|
||||||
{
|
{
|
||||||
public const string SolutionName = "absorbed";
|
|
||||||
|
|
||||||
public Dictionary<Color, float> Progress = new();
|
public Dictionary<Color, float> Progress = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name for solution container, that should be used for absorbed solution storage and as source of absorber solution.
|
||||||
|
/// Default is 'absorbed'.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public string SolutionName = "absorbed";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How much solution we can transfer in one interaction.
|
/// How much solution we can transfer in one interaction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("pickupAmount")]
|
[DataField]
|
||||||
public FixedPoint2 PickupAmount = FixedPoint2.New(100);
|
public FixedPoint2 PickupAmount = FixedPoint2.New(100);
|
||||||
|
|
||||||
[DataField("pickupSound")]
|
[DataField]
|
||||||
public SoundSpecifier PickupSound = new SoundPathSpecifier("/Audio/Effects/Fluids/watersplash.ogg")
|
public SoundSpecifier PickupSound = new SoundPathSpecifier("/Audio/Effects/Fluids/watersplash.ogg")
|
||||||
{
|
{
|
||||||
Params = AudioParams.Default.WithVariation(SharedContentAudioSystem.DefaultVariation),
|
Params = AudioParams.Default.WithVariation(SharedContentAudioSystem.DefaultVariation),
|
||||||
};
|
};
|
||||||
|
|
||||||
[DataField("transferSound")] public SoundSpecifier TransferSound =
|
[DataField] public SoundSpecifier TransferSound =
|
||||||
new SoundPathSpecifier("/Audio/Effects/Fluids/slosh.ogg")
|
new SoundPathSpecifier("/Audio/Effects/Fluids/slosh.ogg")
|
||||||
{
|
{
|
||||||
Params = AudioParams.Default.WithVariation(SharedContentAudioSystem.DefaultVariation).WithVolume(-3f),
|
Params = AudioParams.Default.WithVariation(SharedContentAudioSystem.DefaultVariation).WithVolume(-3f),
|
||||||
@@ -38,4 +43,11 @@ public sealed partial class AbsorbentComponent : Component
|
|||||||
{
|
{
|
||||||
Params = AudioParams.Default.WithVariation(SharedContentAudioSystem.DefaultVariation).WithVolume(-3f),
|
Params = AudioParams.Default.WithVariation(SharedContentAudioSystem.DefaultVariation).WithVolume(-3f),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marker that absorbent component owner should try to use 'absorber solution' to replace solution to be absorbed.
|
||||||
|
/// Target solution will be simply consumed into container if set to false.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool UseAbsorberSolution = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ using Robust.Shared.GameStates;
|
|||||||
namespace Content.Shared.Weapons.Ranged.Components;
|
namespace Content.Shared.Weapons.Ranged.Components;
|
||||||
|
|
||||||
[NetworkedComponent]
|
[NetworkedComponent]
|
||||||
public abstract partial class AmmoProviderComponent : Component {}
|
public abstract partial class AmmoProviderComponent : Component;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using Content.Shared.Inventory;
|
|
||||||
using Content.Shared.Weapons.Ranged.Systems;
|
using Content.Shared.Weapons.Ranged.Systems;
|
||||||
using Content.Shared.Whitelist;
|
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
namespace Content.Shared.Weapons.Ranged.Components;
|
namespace Content.Shared.Weapons.Ranged.Components;
|
||||||
@@ -10,17 +8,4 @@ namespace Content.Shared.Weapons.Ranged.Components;
|
|||||||
/// to an entity in the user's clothing slot.
|
/// to an entity in the user's clothing slot.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedGunSystem))]
|
[RegisterComponent, NetworkedComponent, Access(typeof(SharedGunSystem))]
|
||||||
public sealed partial class ClothingSlotAmmoProviderComponent : AmmoProviderComponent
|
public sealed partial class ClothingSlotAmmoProviderComponent : AmmoProviderComponent;
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The slot that the ammo provider should be located in.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("targetSlot", required: true)]
|
|
||||||
public SlotFlags TargetSlot;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A whitelist for determining whether or not an ammo provider is valid.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("providerWhitelist")]
|
|
||||||
public EntityWhitelist? ProviderWhitelist;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using Content.Shared.Containers;
|
||||||
using Content.Shared.Inventory;
|
|
||||||
using Content.Shared.Weapons.Ranged.Components;
|
using Content.Shared.Weapons.Ranged.Components;
|
||||||
using Content.Shared.Weapons.Ranged.Events;
|
using Content.Shared.Weapons.Ranged.Events;
|
||||||
|
|
||||||
@@ -7,8 +6,6 @@ namespace Content.Shared.Weapons.Ranged.Systems;
|
|||||||
|
|
||||||
public partial class SharedGunSystem
|
public partial class SharedGunSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
|
||||||
|
|
||||||
private void InitializeClothing()
|
private void InitializeClothing()
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<ClothingSlotAmmoProviderComponent, TakeAmmoEvent>(OnClothingTakeAmmo);
|
SubscribeLocalEvent<ClothingSlotAmmoProviderComponent, TakeAmmoEvent>(OnClothingTakeAmmo);
|
||||||
@@ -17,38 +14,21 @@ public partial class SharedGunSystem
|
|||||||
|
|
||||||
private void OnClothingTakeAmmo(EntityUid uid, ClothingSlotAmmoProviderComponent component, TakeAmmoEvent args)
|
private void OnClothingTakeAmmo(EntityUid uid, ClothingSlotAmmoProviderComponent component, TakeAmmoEvent args)
|
||||||
{
|
{
|
||||||
if (!TryGetClothingSlotEntity(uid, component, out var entity))
|
var getConnectedContainerEvent = new GetConnectedContainerEvent();
|
||||||
|
RaiseLocalEvent(uid, ref getConnectedContainerEvent);
|
||||||
|
if(!getConnectedContainerEvent.ContainerEntity.HasValue)
|
||||||
return;
|
return;
|
||||||
RaiseLocalEvent(entity.Value, args);
|
|
||||||
|
RaiseLocalEvent(getConnectedContainerEvent.ContainerEntity.Value, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClothingAmmoCount(EntityUid uid, ClothingSlotAmmoProviderComponent component, ref GetAmmoCountEvent args)
|
private void OnClothingAmmoCount(EntityUid uid, ClothingSlotAmmoProviderComponent component, ref GetAmmoCountEvent args)
|
||||||
{
|
{
|
||||||
if (!TryGetClothingSlotEntity(uid, component, out var entity))
|
var getConnectedContainerEvent = new GetConnectedContainerEvent();
|
||||||
|
RaiseLocalEvent(uid, ref getConnectedContainerEvent);
|
||||||
|
if (!getConnectedContainerEvent.ContainerEntity.HasValue)
|
||||||
return;
|
return;
|
||||||
RaiseLocalEvent(entity.Value, ref args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryGetClothingSlotEntity(EntityUid uid, ClothingSlotAmmoProviderComponent component, [NotNullWhen(true)] out EntityUid? slotEntity)
|
RaiseLocalEvent(getConnectedContainerEvent.ContainerEntity.Value, ref args);
|
||||||
{
|
|
||||||
slotEntity = null;
|
|
||||||
|
|
||||||
if (!Containers.TryGetContainingContainer((uid, null, null), out var container))
|
|
||||||
return false;
|
|
||||||
var user = container.Owner;
|
|
||||||
|
|
||||||
if (!_inventory.TryGetContainerSlotEnumerator(user, out var enumerator, component.TargetSlot))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
while (enumerator.NextItem(out var item))
|
|
||||||
{
|
|
||||||
if (_whitelistSystem.IsWhitelistFailOrNull(component.ProviderWhitelist, item))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
slotEntity = item;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,3 +22,13 @@
|
|||||||
license: "CC-BY-SA-3.0"
|
license: "CC-BY-SA-3.0"
|
||||||
copyright: "Created by the_toilet_guy"
|
copyright: "Created by the_toilet_guy"
|
||||||
source: "https://freesound.org/people/the_toilet_guy/sounds/98770/"
|
source: "https://freesound.org/people/the_toilet_guy/sounds/98770/"
|
||||||
|
|
||||||
|
- files: ["vacuum-cleaner-fast.ogg"]
|
||||||
|
license: "CC0-1.0"
|
||||||
|
copyright: "Created by kyles"
|
||||||
|
source: "https://freesound.org/people/kyles/sounds/637927/"
|
||||||
|
|
||||||
|
- files: ["vacuum-cleaner-fast.ogg"]
|
||||||
|
license: "CC0-1.0"
|
||||||
|
copyright: "Created by BrendanSound12 mixed by Fildrance"
|
||||||
|
source: "https://freesound.org/people/BrendanSound12/sounds/445165/"
|
||||||
|
|||||||
BIN
Resources/Audio/Effects/Fluids/vacuum-cleaner-fast.ogg
Normal file
BIN
Resources/Audio/Effects/Fluids/vacuum-cleaner-fast.ogg
Normal file
Binary file not shown.
@@ -34,6 +34,7 @@
|
|||||||
size: Large
|
size: Large
|
||||||
sprite: Objects/Specific/Janitorial/mop.rsi
|
sprite: Objects/Specific/Janitorial/mop.rsi
|
||||||
- type: Absorbent
|
- type: Absorbent
|
||||||
|
useAbsorberSolution: true
|
||||||
- type: SolutionContainerManager
|
- type: SolutionContainerManager
|
||||||
solutions:
|
solutions:
|
||||||
absorbed:
|
absorbed:
|
||||||
@@ -89,6 +90,7 @@
|
|||||||
sprite: Objects/Specific/Janitorial/advmop.rsi
|
sprite: Objects/Specific/Janitorial/advmop.rsi
|
||||||
- type: Absorbent
|
- type: Absorbent
|
||||||
pickupAmount: 100
|
pickupAmount: 100
|
||||||
|
useAbsorberSolution: true
|
||||||
- type: UseDelay
|
- type: UseDelay
|
||||||
delay: 1.0
|
delay: 1.0
|
||||||
- type: SolutionRegeneration
|
- type: SolutionRegeneration
|
||||||
@@ -322,6 +324,7 @@
|
|||||||
sprite: Objects/Specific/Janitorial/rag.rsi
|
sprite: Objects/Specific/Janitorial/rag.rsi
|
||||||
- type: Absorbent
|
- type: Absorbent
|
||||||
pickupAmount: 15
|
pickupAmount: 15
|
||||||
|
useAbsorberSolution: true
|
||||||
- type: Construction
|
- type: Construction
|
||||||
graph: Rag
|
graph: Rag
|
||||||
node: rag
|
node: rag
|
||||||
|
|||||||
@@ -21,9 +21,18 @@
|
|||||||
- FullAuto
|
- FullAuto
|
||||||
soundGunshot:
|
soundGunshot:
|
||||||
path: /Audio/Weapons/Guns/Gunshots/water_spray.ogg
|
path: /Audio/Weapons/Guns/Gunshots/water_spray.ogg
|
||||||
|
- type: Absorbent
|
||||||
|
pickupAmount: 35
|
||||||
|
solutionName: tank
|
||||||
|
useAbsorberSolution: false
|
||||||
|
pickupSound:
|
||||||
|
path: /Audio/Effects/Fluids/vacuum-cleaner-fast.ogg
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
- type: ClothingSlotAmmoProvider
|
- type: ClothingSlotAmmoProvider
|
||||||
|
- type: SlotBasedConnectedContainer
|
||||||
targetSlot: BACK
|
targetSlot: BACK
|
||||||
providerWhitelist:
|
containerWhitelist:
|
||||||
tags:
|
tags:
|
||||||
- NozzleBackTank
|
- NozzleBackTank
|
||||||
|
- type: UseDelay
|
||||||
|
delay: 1.0
|
||||||
|
|||||||
Reference in New Issue
Block a user