Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solution spiking #8984

Merged
merged 13 commits into from
Jun 24, 2022
Merged
30 changes: 30 additions & 0 deletions Content.Server/Chemistry/Components/SolutionSpikerComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace Content.Server.Chemistry.Components;

[RegisterComponent]
public sealed class SolutionSpikerComponent : Component
{
/// <summary>
/// The source solution to take the reagents from in order
/// to spike the other solution container.
/// </summary>
[DataField("sourceSolution")]
public string SourceSolution { get; } = string.Empty;

/// <summary>
/// If spiking with this entity should ignore empty containers or not.
/// </summary>
[DataField("ignoreEmpty")]
public bool IgnoreEmpty { get; }

/// <summary>
/// What should pop up when spiking with this entity.
/// </summary>
[DataField("popup")]
public string Popup { get; } = "spike-solution-generic";

/// <summary>
/// What should pop up when spiking fails because the container was empty.
/// </summary>
[DataField("popupEmpty")]
public string PopupEmpty { get; } = "spike-solution-empty-generic";
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public void UpdateAppearance(EntityUid uid, Solution solution,
|| !Resolve(uid, ref appearanceComponent, false))
return;

var filledVolumePercent = solution.CurrentVolume.Float() / solution.MaxVolume.Float();
var filledVolumePercent = Math.Min(1.0f, solution.CurrentVolume.Float() / solution.MaxVolume.Float());
appearanceComponent.SetData(SolutionContainerVisuals.VisualState,
new SolutionContainerVisualState(solution.Color, filledVolumePercent));
}
Expand Down
94 changes: 94 additions & 0 deletions Content.Server/Chemistry/EntitySystems/SolutionSpikableSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Components.SolutionManager;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Popups;
using Content.Shared.Chemistry.Components;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction;
using Robust.Shared.Player;

namespace Content.Server.Chemistry.EntitySystems;

/// <summary>
/// Entity system used to handle when solution containers are 'spiked'
/// with another entity. Triggers the source entity afterwards.
/// Uses refillable solution as the target solution, as that indicates
/// 'easy' refills.
///
/// Examples of spikable entity interactions include pills being dropped into glasses,
/// eggs being cracked into bowls, and so on.
/// </summary>
public sealed class SolutionSpikableSystem : EntitySystem
{
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
[Dependency] private readonly TriggerSystem _triggerSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;

public override void Initialize()
{
SubscribeLocalEvent<RefillableSolutionComponent, InteractUsingEvent>(OnInteractUsing);
}

private void OnInteractUsing(EntityUid uid, RefillableSolutionComponent target, InteractUsingEvent args)
{
TrySpike(args.Used, args.Target, args.User, target);
}

/// <summary>
/// Immediately transfer all reagents from this entity, to the other entity.
/// The source entity will then be acted on by TriggerSystem.
/// </summary>
/// <param name="source">Source of the solution.</param>
/// <param name="target">Target to spike with the solution from source.</param>
/// <param name="user">User spiking the target solution.</param>
private void TrySpike(EntityUid source, EntityUid target, EntityUid user, RefillableSolutionComponent? spikableTarget = null,
SolutionSpikerComponent? spikableSource = null,
SolutionContainerManagerComponent? managerSource = null,
SolutionContainerManagerComponent? managerTarget = null)
{
if (!Resolve(source, ref spikableSource, ref managerSource, false)
|| !Resolve(target, ref spikableTarget, ref managerTarget, false)
|| !_solutionSystem.TryGetRefillableSolution(target, out var targetSolution, managerTarget, spikableTarget)
|| !managerSource.Solutions.TryGetValue(spikableSource.SourceSolution, out var sourceSolution))
{
return;
}

if (targetSolution.CurrentVolume == 0 && !spikableSource.IgnoreEmpty)
{
_popupSystem.PopupEntity(Loc.GetString(spikableSource.PopupEmpty, ("spiked-entity", target), ("spike-entity", source)), user, Filter.Entities(user));
return;
}

if (_solutionSystem.TryMixAndOverflow(target,
targetSolution,
sourceSolution,
targetSolution.MaxVolume,
out var overflow))
{
if (overflow.TotalVolume > 0)
{
RaiseLocalEvent(target, new SolutionSpikeOverflowEvent(overflow));
}

_popupSystem.PopupEntity(Loc.GetString(spikableSource.Popup, ("spiked-entity", target), ("spike-entity", source)), user, Filter.Entities(user));

sourceSolution.RemoveAllSolution();

_triggerSystem.Trigger(source);
}
}
}

public sealed class SolutionSpikeOverflowEvent : HandledEntityEventArgs
{
/// <summary>
/// The solution that's been overflowed from the spike.
/// </summary>
public Solution Overflow { get; }

public SolutionSpikeOverflowEvent(Solution overflow)
{
Overflow = overflow;
}
}
11 changes: 11 additions & 0 deletions Content.Server/Fluids/EntitySystems/SpillableSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ public override void Initialize()
SubscribeLocalEvent<SpillableComponent, LandEvent>(SpillOnLand);
SubscribeLocalEvent<SpillableComponent, GetVerbsEvent<Verb>>(AddSpillVerb);
SubscribeLocalEvent<SpillableComponent, GotEquippedEvent>(OnGotEquipped);
SubscribeLocalEvent<SpillableComponent, SolutionSpikeOverflowEvent>(OnSpikeOverflow);
}

private void OnSpikeOverflow(EntityUid uid, SpillableComponent component, SolutionSpikeOverflowEvent args)
{
if (!args.Handled)
{
SpillAt(args.Overflow, Transform(uid).Coordinates, "PuddleSmear");
}

args.Handled = true;
}

private void OnGotEquipped(EntityUid uid, SpillableComponent component, GotEquippedEvent args)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
spike-solution-generic = You spike {THE($spiked-entity)} with {THE($spike-entity)}.
spike-solution-empty-generic = {THE($spike-entity)} fails to dissolve in {THE($spiked-entity)}.
spike-solution-egg = You crack {THE($spike-entity)} into {THE($spiked-entity)}.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@
maxVol: 6
reagents:
- ReagentId: Egg
Quantity: 5
Quantity: 6
- type: SolutionSpiker
sourceSolution: food
ignoreEmpty: true
popup: spike-solution-egg
- type: DeleteOnTrigger
- type: DamageOnLand
damage:
types:
Expand Down
3 changes: 3 additions & 0 deletions Resources/Prototypes/Entities/Objects/Specific/chemistry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,9 @@
solutions:
food:
maxVol: 50
- type: SolutionSpiker
sourceSolution: food
- type: DeleteOnTrigger
- type: Extractable
grindableSolutionName: food

Expand Down