Revert 'Revert 'Solution Entities'' (#23168)

This commit is contained in:
TemporalOroboros
2023-12-29 04:47:43 -08:00
committed by GitHub
parent 93e1af2f8d
commit d23c8d5c19
180 changed files with 3541 additions and 2956 deletions

View File

@@ -1,3 +1,4 @@
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Popups;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
@@ -24,7 +25,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
[Dependency] private readonly PuddleSystem _puddleSystem = default!;
[Dependency] private readonly SharedMeleeWeaponSystem _melee = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly UseDelaySystem _useDelay = default!;
public override void Initialize()
@@ -33,7 +34,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
SubscribeLocalEvent<AbsorbentComponent, ComponentInit>(OnAbsorbentInit);
SubscribeLocalEvent<AbsorbentComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<AbsorbentComponent, InteractNoHandEvent>(OnInteractNoHand);
SubscribeLocalEvent<AbsorbentComponent, SolutionChangedEvent>(OnAbsorbentSolutionChange);
SubscribeLocalEvent<AbsorbentComponent, SolutionContainerChangedEvent>(OnAbsorbentSolutionChange);
}
private void OnAbsorbentInit(EntityUid uid, AbsorbentComponent component, ComponentInit args)
@@ -42,14 +43,14 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
UpdateAbsorbent(uid, component);
}
private void OnAbsorbentSolutionChange(EntityUid uid, AbsorbentComponent component, SolutionChangedEvent args)
private void OnAbsorbentSolutionChange(EntityUid uid, AbsorbentComponent component, ref SolutionContainerChangedEvent args)
{
UpdateAbsorbent(uid, component);
}
private void UpdateAbsorbent(EntityUid uid, AbsorbentComponent component)
{
if (!_solutionSystem.TryGetSolution(uid, AbsorbentComponent.SolutionName, out var solution))
if (!_solutionContainerSystem.TryGetSolution(uid, AbsorbentComponent.SolutionName, out _, out var solution))
return;
var oldProgress = component.Progress.ShallowClone();
@@ -102,17 +103,17 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
public void Mop(EntityUid user, EntityUid target, EntityUid used, AbsorbentComponent component)
{
if (!_solutionSystem.TryGetSolution(used, AbsorbentComponent.SolutionName, out var absorbentSolution))
if (!_solutionContainerSystem.TryGetSolution(used, AbsorbentComponent.SolutionName, out var absorberSoln))
return;
if (_useDelay.ActiveDelay(used))
return;
// If it's a puddle try to grab from
if (!TryPuddleInteract(user, used, target, component, absorbentSolution))
if (!TryPuddleInteract(user, used, target, component, absorberSoln.Value))
{
// If it's refillable try to transfer
if (!TryRefillableInteract(user, used, target, component, absorbentSolution))
if (!TryRefillableInteract(user, used, target, component, absorberSoln.Value))
return;
}
}
@@ -120,24 +121,24 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
/// <summary>
/// Logic for an absorbing entity interacting with a refillable.
/// </summary>
private bool TryRefillableInteract(EntityUid user, EntityUid used, EntityUid target, AbsorbentComponent component, Solution absorbentSolution)
private bool TryRefillableInteract(EntityUid user, EntityUid used, EntityUid target, AbsorbentComponent component, Entity<SolutionComponent> absorbentSoln)
{
if (!TryComp(target, out RefillableSolutionComponent? refillable))
return false;
if (!_solutionSystem.TryGetRefillableSolution(target, out var refillableSolution, refillable: refillable))
if (!_solutionContainerSystem.TryGetRefillableSolution((target, refillable, null), out var refillableSoln, out var refillableSolution))
return false;
if (refillableSolution.Volume <= 0)
{
// Target empty - only transfer absorbent contents into refillable
if (!TryTransferFromAbsorbentToRefillable(user, used, target, component, absorbentSolution, refillableSolution))
if (!TryTransferFromAbsorbentToRefillable(user, used, target, component, absorbentSoln, refillableSoln.Value))
return false;
}
else
{
// Target non-empty - do a two-way transfer
if (!TryTwoWayAbsorbentRefillableTransfer(user, used, target, component, absorbentSolution, refillableSolution))
if (!TryTwoWayAbsorbentRefillableTransfer(user, used, target, component, absorbentSoln, refillableSoln.Value))
return false;
}
@@ -154,15 +155,17 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
EntityUid used,
EntityUid target,
AbsorbentComponent component,
Solution absorbentSolution,
Solution refillableSolution)
Entity<SolutionComponent> absorbentSoln,
Entity<SolutionComponent> refillableSoln)
{
var absorbentSolution = absorbentSoln.Comp.Solution;
if (absorbentSolution.Volume <= 0)
{
_popups.PopupEntity(Loc.GetString("mopping-system-target-container-empty", ("target", target)), user, user);
return false;
}
var refillableSolution = refillableSoln.Comp.Solution;
var transferAmount = component.PickupAmount < refillableSolution.AvailableVolume ?
component.PickupAmount :
refillableSolution.AvailableVolume;
@@ -174,17 +177,15 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
}
// Prioritize transferring non-evaporatives if absorbent has any
var contaminants = absorbentSolution.SplitSolutionWithout(transferAmount, PuddleSystem.EvaporationReagents);
var contaminants = _solutionContainerSystem.SplitSolutionWithout(absorbentSoln, transferAmount, PuddleSystem.EvaporationReagents);
if (contaminants.Volume > 0)
{
_solutionSystem.UpdateChemicals(used, absorbentSolution, true);
_solutionSystem.TryAddSolution(target, refillableSolution, contaminants);
_solutionContainerSystem.TryAddSolution(refillableSoln, contaminants);
}
else
{
var evaporatives = absorbentSolution.SplitSolution(transferAmount);
_solutionSystem.UpdateChemicals(used, absorbentSolution, true);
_solutionSystem.TryAddSolution(target, refillableSolution, evaporatives);
var evaporatives = _solutionContainerSystem.SplitSolution(absorbentSoln, transferAmount);
_solutionContainerSystem.TryAddSolution(refillableSoln, evaporatives);
}
return true;
@@ -198,12 +199,12 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
EntityUid used,
EntityUid target,
AbsorbentComponent component,
Solution absorbentSolution,
Solution refillableSolution)
Entity<SolutionComponent> absorbentSoln,
Entity<SolutionComponent> refillableSoln)
{
var contaminantsFromAbsorbent = absorbentSolution.SplitSolutionWithout(component.PickupAmount, PuddleSystem.EvaporationReagents);
_solutionSystem.UpdateChemicals(used, absorbentSolution, true);
var contaminantsFromAbsorbent = _solutionContainerSystem.SplitSolutionWithout(absorbentSoln, component.PickupAmount, PuddleSystem.EvaporationReagents);
var absorbentSolution = absorbentSoln.Comp.Solution;
if (contaminantsFromAbsorbent.Volume == FixedPoint2.Zero && absorbentSolution.AvailableVolume == FixedPoint2.Zero)
{
// Nothing to transfer to refillable and no room to absorb anything extra
@@ -217,8 +218,9 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
component.PickupAmount :
absorbentSolution.AvailableVolume;
var refillableSolution = refillableSoln.Comp.Solution;
var waterFromRefillable = refillableSolution.SplitSolutionWithOnly(waterPulled, PuddleSystem.EvaporationReagents);
_solutionSystem.UpdateChemicals(target, refillableSolution);
_solutionContainerSystem.UpdateChemicals(refillableSoln);
if (waterFromRefillable.Volume == FixedPoint2.Zero && contaminantsFromAbsorbent.Volume == FixedPoint2.Zero)
{
@@ -234,7 +236,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
if (waterFromRefillable.Volume > FixedPoint2.Zero)
{
// transfer water to absorbent
_solutionSystem.TryAddSolution(used, absorbentSolution, waterFromRefillable);
_solutionContainerSystem.TryAddSolution(absorbentSoln, waterFromRefillable);
anyTransferOccurred = true;
}
@@ -248,12 +250,12 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
{
// transfer as much contaminants to refillable as will fit
var contaminantsForRefillable = contaminantsFromAbsorbent.SplitSolution(refillableSolution.AvailableVolume);
_solutionSystem.TryAddSolution(target, refillableSolution, contaminantsForRefillable);
_solutionContainerSystem.TryAddSolution(refillableSoln, contaminantsForRefillable);
anyTransferOccurred = true;
}
// absorb everything that did not fit in the refillable back by the absorbent
_solutionSystem.TryAddSolution(used, absorbentSolution, contaminantsFromAbsorbent);
_solutionContainerSystem.TryAddSolution(absorbentSoln, contaminantsFromAbsorbent);
}
return anyTransferOccurred;
@@ -262,23 +264,24 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
/// <summary>
/// Logic for an absorbing entity interacting with a puddle.
/// </summary>
private bool TryPuddleInteract(EntityUid user, EntityUid used, EntityUid target, AbsorbentComponent absorber, Solution absorberSoln)
private bool TryPuddleInteract(EntityUid user, EntityUid used, EntityUid target, AbsorbentComponent absorber, Entity<SolutionComponent> absorberSoln)
{
if (!TryComp(target, out PuddleComponent? puddle))
return false;
if (!_solutionSystem.TryGetSolution(target, puddle.SolutionName, out var puddleSoln) || puddleSoln.Volume <= 0)
if (!_solutionContainerSystem.ResolveSolution(target, puddle.SolutionName, ref puddle.Solution, out var puddleSolution) || puddleSolution.Volume <= 0)
return false;
// Check if the puddle has any non-evaporative reagents
if (_puddleSystem.CanFullyEvaporate(puddleSoln))
if (_puddleSystem.CanFullyEvaporate(puddleSolution))
{
_popups.PopupEntity(Loc.GetString("mopping-system-puddle-evaporate", ("target", target)), user, user);
return true;
}
// Check if we have any evaporative reagents on our absorber to transfer
var available = absorberSoln.GetTotalPrototypeQuantity(PuddleSystem.EvaporationReagents);
var absorberSolution = absorberSoln.Comp.Solution;
var available = absorberSolution.GetTotalPrototypeQuantity(PuddleSystem.EvaporationReagents);
// No material
if (available == FixedPoint2.Zero)
@@ -290,8 +293,8 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
var transferMax = absorber.PickupAmount;
var transferAmount = available > transferMax ? transferMax : available;
var puddleSplit = puddleSoln.SplitSolutionWithout(transferAmount, PuddleSystem.EvaporationReagents);
var absorberSplit = absorberSoln.SplitSolutionWithOnly(puddleSplit.Volume, PuddleSystem.EvaporationReagents);
var puddleSplit = puddleSolution.SplitSolutionWithout(transferAmount, PuddleSystem.EvaporationReagents);
var absorberSplit = absorberSolution.SplitSolutionWithOnly(puddleSplit.Volume, PuddleSystem.EvaporationReagents);
// Do tile reactions first
var coordinates = Transform(target).Coordinates;
@@ -300,11 +303,9 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem
_puddleSystem.DoTileReactions(mapGrid.GetTileRef(coordinates), absorberSplit);
}
puddleSoln.AddSolution(absorberSplit, _prototype);
absorberSoln.AddSolution(puddleSplit, _prototype);
_solutionContainerSystem.AddSolution(puddle.Solution.Value, absorberSplit);
_solutionContainerSystem.AddSolution(absorberSoln, puddleSplit);
_solutionSystem.UpdateChemicals(used, absorberSoln);
_solutionSystem.UpdateChemicals(target, puddleSoln);
_audio.PlayPvs(absorber.PickupSound, target);
_useDelay.BeginDelay(used);

View File

@@ -1,9 +1,9 @@
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.DoAfter;
using Content.Server.Fluids.Components;
using Content.Server.Popups;
using Content.Shared.Audio;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
@@ -13,10 +13,9 @@ using Content.Shared.Fluids.Components;
using Content.Shared.Interaction;
using Content.Shared.Tag;
using Content.Shared.Verbs;
using Content.Shared.Fluids.Components;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Collections;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility;
@@ -25,7 +24,7 @@ namespace Content.Server.Fluids.EntitySystems;
public sealed class DrainSystem : SharedDrainSystem
{
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
@@ -33,6 +32,7 @@ public sealed class DrainSystem : SharedDrainSystem
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PuddleSystem _puddleSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override void Initialize()
{
@@ -43,7 +43,7 @@ public sealed class DrainSystem : SharedDrainSystem
SubscribeLocalEvent<DrainComponent, DrainDoAfterEvent>(OnDoAfter);
}
private void AddEmptyVerb(EntityUid uid, DrainComponent component, GetVerbsEvent<Verb> args)
private void AddEmptyVerb(Entity<DrainComponent> entity, ref GetVerbsEvent<Verb> args)
{
if (!args.CanAccess || !args.CanInteract || args.Using == null)
return;
@@ -52,12 +52,14 @@ public sealed class DrainSystem : SharedDrainSystem
!TryComp(args.Target, out DrainComponent? drain))
return;
var used = args.Using.Value;
var target = args.Target;
Verb verb = new()
{
Text = Loc.GetString("drain-component-empty-verb-inhand", ("object", Name(args.Using.Value))),
Text = Loc.GetString("drain-component-empty-verb-inhand", ("object", Name(used))),
Act = () =>
{
Empty(args.Using.Value, spillable, args.Target, drain);
Empty(used, spillable, target, drain);
},
Impact = LogImpact.Low,
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/eject.svg.192dpi.png"))
@@ -69,8 +71,7 @@ public sealed class DrainSystem : SharedDrainSystem
private void Empty(EntityUid container, SpillableComponent spillable, EntityUid target, DrainComponent drain)
{
// Find the solution in the container that is emptied
if (!_solutionSystem.TryGetDrainableSolution(container, out var containerSolution) ||
containerSolution.Volume == FixedPoint2.Zero)
if (!_solutionContainerSystem.TryGetDrainableSolution(container, out var containerSoln, out var containerSolution) || containerSolution.Volume == FixedPoint2.Zero)
{
_popupSystem.PopupEntity(
Loc.GetString("drain-component-empty-verb-using-is-empty-message", ("object", container)),
@@ -79,17 +80,17 @@ public sealed class DrainSystem : SharedDrainSystem
}
// try to find the drain's solution
if (!_solutionSystem.TryGetSolution(target, DrainComponent.SolutionName, out var drainSolution))
if (!_solutionContainerSystem.ResolveSolution(target, DrainComponent.SolutionName, ref drain.Solution, out var drainSolution))
{
return;
}
// Try to transfer as much solution as possible to the drain
var transferSolution = _solutionSystem.SplitSolution(container, containerSolution,
var transferSolution = _solutionContainerSystem.SplitSolution(containerSoln.Value,
FixedPoint2.Min(containerSolution.Volume, drainSolution.AvailableVolume));
_solutionSystem.TryAddSolution(target, drainSolution, transferSolution);
_solutionContainerSystem.TryAddSolution(drain.Solution.Value, transferSolution);
_audioSystem.PlayPvs(drain.ManualDrainSound, target);
_ambientSoundSystem.SetAmbience(target, true);
@@ -111,7 +112,7 @@ public sealed class DrainSystem : SharedDrainSystem
var managerQuery = GetEntityQuery<SolutionContainerManagerComponent>();
var xformQuery = GetEntityQuery<TransformComponent>();
var puddleQuery = GetEntityQuery<PuddleComponent>();
var puddles = new ValueList<(EntityUid Entity, string Solution)>();
var puddles = new ValueList<(Entity<PuddleComponent> Entity, string Solution)>();
var query = EntityQueryEnumerator<DrainComponent>();
while (query.MoveNext(out var uid, out var drain))
@@ -134,9 +135,7 @@ public sealed class DrainSystem : SharedDrainSystem
continue;
// Best to do this one every second rather than once every tick...
_solutionSystem.TryGetSolution(uid, DrainComponent.SolutionName, out var drainSolution, manager);
if (drainSolution is null)
if (!_solutionContainerSystem.ResolveSolution((uid, manager), DrainComponent.SolutionName, ref drain.Solution, out var drainSolution))
continue;
if (drainSolution.AvailableVolume <= 0)
@@ -146,7 +145,7 @@ public sealed class DrainSystem : SharedDrainSystem
}
// Remove a bit from the buffer
_solutionSystem.SplitSolution(uid, drainSolution, (drain.UnitsDestroyedPerSecond * drain.DrainFrequency));
_solutionContainerSystem.SplitSolution(drain.Solution.Value, (drain.UnitsDestroyedPerSecond * drain.DrainFrequency));
// This will ensure that UnitsPerSecond is per second...
var amount = drain.UnitsPerSecond * drain.DrainFrequency;
@@ -162,7 +161,7 @@ public sealed class DrainSystem : SharedDrainSystem
// and these are placed by mappers and not buildable/movable so shouldnt really be a problem...
if (puddleQuery.TryGetComponent(entity, out var puddle))
{
puddles.Add((entity, puddle.SolutionName));
puddles.Add(((entity, puddle), puddle.SolutionName));
}
}
@@ -180,7 +179,7 @@ public sealed class DrainSystem : SharedDrainSystem
{
// Queue the solution deletion if it's empty. EvaporationSystem might also do this
// but queuedelete should be pretty safe.
if (!_solutionSystem.TryGetSolution(puddle, solution, out var puddleSolution))
if (!_solutionContainerSystem.ResolveSolution(puddle.Owner, solution, ref puddle.Comp.Solution, out var puddleSolution))
{
EntityManager.QueueDeleteEntity(puddle);
continue;
@@ -190,24 +189,26 @@ public sealed class DrainSystem : SharedDrainSystem
// the drain component's units per second adjusted for # of puddles
// the puddle's remaining volume (making it cleanly zero)
// the drain's remaining volume in its buffer.
var transferSolution = _solutionSystem.SplitSolution(puddle, puddleSolution,
var transferSolution = _solutionContainerSystem.SplitSolution(puddle.Comp.Solution.Value,
FixedPoint2.Min(FixedPoint2.New(amount), puddleSolution.Volume, drainSolution.AvailableVolume));
_solutionSystem.TryAddSolution(uid, drainSolution, transferSolution);
drainSolution.AddSolution(transferSolution, _prototypeManager);
if (puddleSolution.Volume <= 0)
{
QueueDel(puddle);
}
}
_solutionContainerSystem.UpdateChemicals(drain.Solution.Value);
}
}
private void OnExamined(EntityUid uid, DrainComponent component, ExaminedEvent args)
private void OnExamined(Entity<DrainComponent> entity, ref ExaminedEvent args)
{
if (!args.IsInDetailsRange ||
!HasComp<SolutionContainerManagerComponent>(uid) ||
!_solutionSystem.TryGetSolution(uid, DrainComponent.SolutionName, out var drainSolution))
!HasComp<SolutionContainerManagerComponent>(entity) ||
!_solutionContainerSystem.ResolveSolution(entity.Owner, DrainComponent.SolutionName, ref entity.Comp.Solution, out var drainSolution))
{
return;
}
@@ -218,11 +219,11 @@ public sealed class DrainSystem : SharedDrainSystem
args.Message.AddMarkup($"\n\n{text}");
}
private void OnInteract(EntityUid uid, DrainComponent component, InteractEvent args)
private void OnInteract(Entity<DrainComponent> entity, ref AfterInteractUsingEvent args)
{
if (!args.CanReach || args.Target == null ||
!_tagSystem.HasTag(args.Used, DrainComponent.PlungerTag) ||
!_solutionSystem.TryGetSolution(args.Target.Value, DrainComponent.SolutionName, out var drainSolution))
!_solutionContainerSystem.ResolveSolution(args.Target.Value, DrainComponent.SolutionName, ref entity.Comp.Solution, out var drainSolution))
{
return;
}
@@ -233,10 +234,10 @@ public sealed class DrainSystem : SharedDrainSystem
return;
}
_audioSystem.PlayPvs(component.PlungerSound, uid);
_audioSystem.PlayPvs(entity.Comp.PlungerSound, entity);
var doAfterArgs = new DoAfterArgs(EntityManager, args.User, component.UnclogDuration, new DrainDoAfterEvent(),uid, args.Target, args.Used)
var doAfterArgs = new DoAfterArgs(EntityManager, args.User, entity.Comp.UnclogDuration, new DrainDoAfterEvent(), entity, args.Target, args.Used)
{
BreakOnTargetMove = true,
BreakOnUserMove = true,
@@ -247,27 +248,26 @@ public sealed class DrainSystem : SharedDrainSystem
_doAfterSystem.TryStartDoAfter(doAfterArgs);
}
private void OnDoAfter(EntityUid uid, DrainComponent component, DoAfterEvent args)
private void OnDoAfter(Entity<DrainComponent> entity, ref DrainDoAfterEvent args)
{
if (args.Target == null)
return;
if (!_random.Prob(component.UnclogProbability))
if (!_random.Prob(entity.Comp.UnclogProbability))
{
_popupSystem.PopupEntity(Loc.GetString("drain-component-unclog-fail", ("object", args.Target.Value)), args.Target.Value);
return;
}
if (!_solutionSystem.TryGetSolution(args.Target.Value, DrainComponent.SolutionName,
out var drainSolution))
if (!_solutionContainerSystem.ResolveSolution(args.Target.Value, DrainComponent.SolutionName, ref entity.Comp.Solution))
{
return;
}
_solutionSystem.RemoveAllSolution(args.Target.Value, drainSolution);
_audioSystem.PlayPvs(component.UnclogSound, args.Target.Value);
_solutionContainerSystem.RemoveAllSolution(entity.Comp.Solution.Value);
_audioSystem.PlayPvs(entity.Comp.UnclogSound, args.Target.Value);
_popupSystem.PopupEntity(Loc.GetString("drain-component-unclog-success", ("object", args.Target.Value)), args.Target.Value);
}
}

View File

@@ -18,9 +18,9 @@ public sealed partial class PuddleSystem
public static string[] EvaporationReagents = new[] { Water, SoapyWater };
private void OnEvaporationMapInit(EntityUid uid, EvaporationComponent component, MapInitEvent args)
private void OnEvaporationMapInit(Entity<EvaporationComponent> entity, ref MapInitEvent args)
{
component.NextTick = _timing.CurTime + EvaporationCooldown;
entity.Comp.NextTick = _timing.CurTime + EvaporationCooldown;
}
private void UpdateEvaporation(EntityUid uid, Solution solution)
@@ -52,7 +52,7 @@ public sealed partial class PuddleSystem
evaporation.NextTick += EvaporationCooldown;
if (!_solutionContainerSystem.TryGetSolution(uid, puddle.SolutionName, out var puddleSolution))
if (!_solutionContainerSystem.ResolveSolution(uid, puddle.SolutionName, ref puddle.Solution, out var puddleSolution))
continue;
var reagentTick = evaporation.EvaporationAmount * EvaporationCooldown.TotalSeconds;

View File

@@ -1,4 +1,4 @@
using Content.Server.Chemistry.EntitySystems;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Fluids.Components;
using Content.Server.Nutrition.EntitySystems;
using Content.Shared.Chemistry.Components;
@@ -36,29 +36,29 @@ public sealed partial class PuddleSystem
SubscribeLocalEvent<SpillableComponent, MeleeHitEvent>(SplashOnMeleeHit, after: new[] { typeof(OpenableSystem) });
SubscribeLocalEvent<SpillableComponent, GetVerbsEvent<Verb>>(AddSpillVerb);
SubscribeLocalEvent<SpillableComponent, GotEquippedEvent>(OnGotEquipped);
SubscribeLocalEvent<SpillableComponent, SolutionOverflowEvent>(OnOverflow);
SubscribeLocalEvent<SpillableComponent, SolutionContainerOverflowEvent>(OnOverflow);
SubscribeLocalEvent<SpillableComponent, SpillDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<SpillableComponent, AttemptPacifiedThrowEvent>(OnAttemptPacifiedThrow);
}
private void OnExamined(EntityUid uid, SpillableComponent component, ExaminedEvent args)
private void OnExamined(Entity<SpillableComponent> entity, ref ExaminedEvent args)
{
args.PushMarkup(Loc.GetString("spill-examine-is-spillable"));
if (HasComp<MeleeWeaponComponent>(uid))
if (HasComp<MeleeWeaponComponent>(entity))
args.PushMarkup(Loc.GetString("spill-examine-spillable-weapon"));
}
private void OnOverflow(EntityUid uid, SpillableComponent component, ref SolutionOverflowEvent args)
private void OnOverflow(Entity<SpillableComponent> entity, ref SolutionContainerOverflowEvent args)
{
if (args.Handled)
return;
TrySpillAt(Transform(uid).Coordinates, args.Overflow, out _);
TrySpillAt(Transform(entity).Coordinates, args.Overflow, out _);
args.Handled = true;
}
private void SplashOnMeleeHit(EntityUid uid, SpillableComponent component, MeleeHitEvent args)
private void SplashOnMeleeHit(Entity<SpillableComponent> entity, ref MeleeHitEvent args)
{
if (args.Handled)
return;
@@ -68,20 +68,20 @@ public sealed partial class PuddleSystem
// If this also has solution transfer, then assume the transfer amount is how much we want to spill.
// Otherwise let's say they want to spill a quarter of its max volume.
if (!_solutionContainerSystem.TryGetDrainableSolution(uid, out var solution))
if (!_solutionContainerSystem.TryGetDrainableSolution(entity.Owner, out var soln, out var solution))
return;
var hitCount = args.HitEntities.Count;
var totalSplit = FixedPoint2.Min(solution.MaxVolume * 0.25, solution.Volume);
if (TryComp<SolutionTransferComponent>(uid, out var transfer))
if (TryComp<SolutionTransferComponent>(entity, out var transfer))
{
totalSplit = FixedPoint2.Min(transfer.TransferAmount, solution.Volume);
}
// a little lame, but reagent quantity is not very balanced and we don't want people
// spilling like 100u of reagent on someone at once!
totalSplit = FixedPoint2.Min(totalSplit, component.MaxMeleeSpillAmount);
totalSplit = FixedPoint2.Min(totalSplit, entity.Comp.MaxMeleeSpillAmount);
if (totalSplit == 0)
return;
@@ -95,29 +95,29 @@ public sealed partial class PuddleSystem
continue;
}
var splitSolution = _solutionContainerSystem.SplitSolution(uid, solution, totalSplit / hitCount);
var splitSolution = _solutionContainerSystem.SplitSolution(soln.Value, totalSplit / hitCount);
_adminLogger.Add(LogType.MeleeHit, $"{ToPrettyString(args.User)} splashed {SolutionContainerSystem.ToPrettyString(splitSolution):solution} from {ToPrettyString(uid):entity} onto {ToPrettyString(hit):target}");
_adminLogger.Add(LogType.MeleeHit, $"{ToPrettyString(args.User)} splashed {SolutionContainerSystem.ToPrettyString(splitSolution):solution} from {ToPrettyString(entity.Owner):entity} onto {ToPrettyString(hit):target}");
_reactive.DoEntityReaction(hit, splitSolution, ReactionMethod.Touch);
_popups.PopupEntity(
Loc.GetString("spill-melee-hit-attacker", ("amount", totalSplit / hitCount), ("spillable", uid),
Loc.GetString("spill-melee-hit-attacker", ("amount", totalSplit / hitCount), ("spillable", entity.Owner),
("target", Identity.Entity(hit, EntityManager))),
hit, args.User);
_popups.PopupEntity(
Loc.GetString("spill-melee-hit-others", ("attacker", args.User), ("spillable", uid),
Loc.GetString("spill-melee-hit-others", ("attacker", args.User), ("spillable", entity.Owner),
("target", Identity.Entity(hit, EntityManager))),
hit, Filter.PvsExcept(args.User), true, PopupType.SmallCaution);
}
}
private void OnGotEquipped(EntityUid uid, SpillableComponent component, GotEquippedEvent args)
private void OnGotEquipped(Entity<SpillableComponent> entity, ref GotEquippedEvent args)
{
if (!component.SpillWorn)
if (!entity.Comp.SpillWorn)
return;
if (!TryComp(uid, out ClothingComponent? clothing))
if (!TryComp(entity, out ClothingComponent? clothing))
return;
// check if entity was actually used as clothing
@@ -126,33 +126,33 @@ public sealed partial class PuddleSystem
if (!isCorrectSlot)
return;
if (!_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var solution))
if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution))
return;
if (solution.Volume == 0)
return;
// spill all solution on the player
var drainedSolution = _solutionContainerSystem.Drain(uid, solution, solution.Volume);
TrySplashSpillAt(uid, Transform(args.Equipee).Coordinates, drainedSolution, out _);
var drainedSolution = _solutionContainerSystem.Drain(entity.Owner, soln.Value, solution.Volume);
TrySplashSpillAt(entity.Owner, Transform(args.Equipee).Coordinates, drainedSolution, out _);
}
private void SpillOnLand(EntityUid uid, SpillableComponent component, ref LandEvent args)
private void SpillOnLand(Entity<SpillableComponent> entity, ref LandEvent args)
{
if (!_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var solution))
if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution))
return;
if (_openable.IsClosed(uid))
if (_openable.IsClosed(entity.Owner))
return;
if (args.User != null)
{
_adminLogger.Add(LogType.Landed,
$"{ToPrettyString(uid):entity} spilled a solution {SolutionContainerSystem.ToPrettyString(solution):solution} on landing");
$"{ToPrettyString(entity.Owner):entity} spilled a solution {SolutionContainerSystem.ToPrettyString(solution):solution} on landing");
}
var drainedSolution = _solutionContainerSystem.Drain(uid, solution, solution.Volume);
TrySplashSpillAt(uid, Transform(uid).Coordinates, drainedSolution, out _);
var drainedSolution = _solutionContainerSystem.Drain(entity.Owner, soln.Value, solution.Volume);
TrySplashSpillAt(entity.Owner, Transform(entity).Coordinates, drainedSolution, out _);
}
/// <summary>
@@ -165,18 +165,18 @@ public sealed partial class PuddleSystem
return;
// Dont care about empty containers.
if (!_solutionContainerSystem.TryGetSolution(ent, ent.Comp.SolutionName, out var solution))
if (!_solutionContainerSystem.TryGetSolution(ent.Owner, ent.Comp.SolutionName, out _, out var solution) || solution.Volume <= 0)
return;
args.Cancel("pacified-cannot-throw-spill");
}
private void AddSpillVerb(EntityUid uid, SpillableComponent component, GetVerbsEvent<Verb> args)
private void AddSpillVerb(Entity<SpillableComponent> entity, ref GetVerbsEvent<Verb> args)
{
if (!args.CanAccess || !args.CanInteract)
return;
if (!_solutionContainerSystem.TryGetSolution(args.Target, component.SolutionName, out var solution))
if (!_solutionContainerSystem.TryGetSolution(args.Target, entity.Comp.SolutionName, out var soln, out var solution))
return;
if (_openable.IsClosed(args.Target))
@@ -195,20 +195,21 @@ public sealed partial class PuddleSystem
};
// TODO VERB ICONS spill icon? pouring out a glass/beaker?
if (component.SpillDelay == null)
if (entity.Comp.SpillDelay == null)
{
var target = args.Target;
verb.Act = () =>
{
var puddleSolution = _solutionContainerSystem.SplitSolution(args.Target,
solution, solution.Volume);
TrySpillAt(Transform(args.Target).Coordinates, puddleSolution, out _);
var puddleSolution = _solutionContainerSystem.SplitSolution(soln.Value, solution.Volume);
TrySpillAt(Transform(target).Coordinates, puddleSolution, out _);
};
}
else
{
var user = args.User;
verb.Act = () =>
{
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.SpillDelay ?? 0, new SpillDoAfterEvent(), uid, target: uid)
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, entity.Comp.SpillDelay ?? 0, new SpillDoAfterEvent(), entity.Owner, target: entity.Owner)
{
BreakOnTargetMove = true,
BreakOnUserMove = true,
@@ -222,17 +223,17 @@ public sealed partial class PuddleSystem
args.Verbs.Add(verb);
}
private void OnDoAfter(EntityUid uid, SpillableComponent component, DoAfterEvent args)
private void OnDoAfter(Entity<SpillableComponent> entity, ref SpillDoAfterEvent args)
{
if (args.Handled || args.Cancelled || args.Args.Target == null)
return;
//solution gone by other means before doafter completes
if (!_solutionContainerSystem.TryGetDrainableSolution(uid, out var solution) || solution.Volume == 0)
if (!_solutionContainerSystem.TryGetDrainableSolution(entity.Owner, out var soln, out var solution) || solution.Volume == 0)
return;
var puddleSolution = _solutionContainerSystem.SplitSolution(uid, solution, solution.Volume);
TrySpillAt(Transform(uid).Coordinates, puddleSolution, out _);
var puddleSolution = _solutionContainerSystem.SplitSolution(soln.Value, solution.Volume);
TrySpillAt(Transform(entity).Coordinates, puddleSolution, out _);
args.Handled = true;
}
}

View File

@@ -1,9 +1,7 @@
using Content.Server.Fluids.Components;
using Content.Shared.Chemistry.Components;
using Content.Shared.DragDrop;
using Content.Shared.FixedPoint;
using Content.Shared.Fluids;
using Content.Shared.Fluids.Components;
namespace Content.Server.Fluids.EntitySystems;
@@ -14,33 +12,30 @@ public sealed partial class PuddleSystem
SubscribeLocalEvent<RefillableSolutionComponent, DragDropDraggedEvent>(OnRefillableDragged);
}
private void OnRefillableDragged(EntityUid uid, RefillableSolutionComponent component, ref DragDropDraggedEvent args)
private void OnRefillableDragged(Entity<RefillableSolutionComponent> entity, ref DragDropDraggedEvent args)
{
_solutionContainerSystem.TryGetSolution(uid, component.Solution, out var solution);
if (solution?.Volume == FixedPoint2.Zero)
if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.Solution, out var soln, out var solution) || solution.Volume == FixedPoint2.Zero)
{
_popups.PopupEntity(Loc.GetString("mopping-system-empty", ("used", uid)), uid, args.User);
_popups.PopupEntity(Loc.GetString("mopping-system-empty", ("used", entity.Owner)), entity, args.User);
return;
}
// Dump reagents into DumpableSolution
if (TryComp<DumpableSolutionComponent>(args.Target, out var dump))
{
_solutionContainerSystem.TryGetDumpableSolution(args.Target, out var dumpableSolution, dump);
if (dumpableSolution == null || solution == null)
if (!_solutionContainerSystem.TryGetDumpableSolution((args.Target, dump, null), out var dumpableSoln, out var dumpableSolution))
return;
bool success = true;
if (dump.Unlimited)
{
var split = _solutionContainerSystem.SplitSolution(uid, solution, solution.Volume);
var split = _solutionContainerSystem.SplitSolution(soln.Value, solution.Volume);
dumpableSolution.AddSolution(split, _prototypeManager);
}
else
{
var split = _solutionContainerSystem.SplitSolution(uid, solution, dumpableSolution.AvailableVolume);
success = _solutionContainerSystem.TryAddSolution(args.Target, dumpableSolution, split);
var split = _solutionContainerSystem.SplitSolution(soln.Value, dumpableSolution.AvailableVolume);
success = _solutionContainerSystem.TryAddSolution(dumpableSoln.Value, split);
}
if (success)
@@ -55,25 +50,21 @@ public sealed partial class PuddleSystem
return;
}
TryComp<DrainableSolutionComponent>(args.Target, out var drainable);
_solutionContainerSystem.TryGetDrainableSolution(args.Target, out var drainableSolution, drainable);
// Take reagents from target
if (drainable != null)
if (!TryComp<DrainableSolutionComponent>(args.Target, out var drainable))
{
if (drainableSolution == null || solution == null)
if (!_solutionContainerSystem.TryGetDrainableSolution((args.Target, drainable, null), out var drainableSolution, out _))
return;
var split = _solutionContainerSystem.SplitSolution(args.Target, drainableSolution, solution.AvailableVolume);
var split = _solutionContainerSystem.SplitSolution(drainableSolution.Value, solution.AvailableVolume);
if (_solutionContainerSystem.TryAddSolution(uid, solution, split))
if (_solutionContainerSystem.TryAddSolution(soln.Value, split))
{
_audio.PlayPvs(AbsorbentComponent.DefaultTransferSound, uid);
_audio.PlayPvs(AbsorbentComponent.DefaultTransferSound, entity);
}
else
{
_popups.PopupEntity(Loc.GetString("mopping-system-full", ("used", uid)), uid, args.User);
_popups.PopupEntity(Loc.GetString("mopping-system-full", ("used", entity.Owner)), entity, args.User);
}
}
}

View File

@@ -1,4 +1,5 @@
using Content.Server.Administration.Logs;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.DoAfter;
using Content.Server.Fluids.Components;
using Content.Server.Spreader;
@@ -22,19 +23,13 @@ using Content.Shared.Popups;
using Content.Shared.Slippery;
using Content.Shared.StepTrigger.Components;
using Content.Shared.StepTrigger.Systems;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Server.Audio;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Maps;
using Content.Shared.Effects;
using Robust.Server.Audio;
namespace Content.Server.Fluids.EntitySystems;
@@ -91,7 +86,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
// Shouldn't need re-anchoring.
SubscribeLocalEvent<PuddleComponent, AnchorStateChangedEvent>(OnAnchorChanged);
SubscribeLocalEvent<PuddleComponent, ExaminedEvent>(HandlePuddleExamined);
SubscribeLocalEvent<PuddleComponent, SolutionChangedEvent>(OnSolutionUpdate);
SubscribeLocalEvent<PuddleComponent, SolutionContainerChangedEvent>(OnSolutionUpdate);
SubscribeLocalEvent<PuddleComponent, ComponentInit>(OnPuddleInit);
SubscribeLocalEvent<PuddleComponent, SpreadNeighborsEvent>(OnPuddleSpread);
SubscribeLocalEvent<PuddleComponent, SlipEvent>(OnPuddleSlip);
@@ -102,13 +97,13 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
InitializeTransfers();
}
private void OnPuddleSpread(EntityUid uid, PuddleComponent component, ref SpreadNeighborsEvent args)
private void OnPuddleSpread(Entity<PuddleComponent> entity, ref SpreadNeighborsEvent args)
{
var overflow = GetOverflowSolution(uid, component);
var overflow = GetOverflowSolution(entity.Owner, entity.Comp);
if (overflow.Volume == FixedPoint2.Zero)
{
RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
RemCompDeferred<ActiveEdgeSpreaderComponent>(entity);
return;
}
@@ -125,7 +120,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
foreach (var neighbor in args.Neighbors)
{
if (!puddleQuery.TryGetComponent(neighbor, out var puddle) ||
!_solutionContainerSystem.TryGetSolution(neighbor, puddle.SolutionName, out var neighborSolution) ||
!_solutionContainerSystem.ResolveSolution(neighbor, puddle.SolutionName, ref puddle.Solution, out var neighborSolution) ||
CanFullyEvaporate(neighborSolution))
{
continue;
@@ -138,7 +133,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
var split = overflow.SplitSolution(remaining);
if (!_solutionContainerSystem.TryAddSolution(neighbor, neighborSolution, split))
if (!_solutionContainerSystem.TryAddSolution(puddle.Solution.Value, split))
continue;
args.Updates--;
@@ -150,7 +145,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
if (overflow.Volume == FixedPoint2.Zero)
{
RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
RemCompDeferred<ActiveEdgeSpreaderComponent>(entity);
return;
}
}
@@ -173,7 +168,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
break;
}
RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
RemCompDeferred<ActiveEdgeSpreaderComponent>(entity);
return;
}
@@ -186,7 +181,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
{
// Overflow to neighbours (unless it's pure water)
if (!puddleQuery.TryGetComponent(neighbor, out var puddle) ||
!_solutionContainerSystem.TryGetSolution(neighbor, puddle.SolutionName, out var neighborSolution) ||
!_solutionContainerSystem.ResolveSolution(neighbor, puddle.SolutionName, ref puddle.Solution, out var neighborSolution) ||
CanFullyEvaporate(neighborSolution))
{
continue;
@@ -194,7 +189,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
var split = overflow.SplitSolution(spillPerNeighbor);
if (!_solutionContainerSystem.TryAddSolution(neighbor, neighborSolution, split))
if (!_solutionContainerSystem.TryAddSolution(puddle.Solution.Value, split))
continue;
EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
@@ -206,13 +201,13 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
}
// Add the remainder back
if (_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var puddleSolution))
if (_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution))
{
_solutionContainerSystem.TryAddSolution(uid, puddleSolution, overflow);
_solutionContainerSystem.TryAddSolution(entity.Comp.Solution.Value, overflow);
}
}
private void OnPuddleSlip(EntityUid uid, PuddleComponent component, ref SlipEvent args)
private void OnPuddleSlip(Entity<PuddleComponent> entity, ref SlipEvent args)
{
// Reactive entities have a chance to get a touch reaction from slipping on a puddle
// (i.e. it is implied they fell face first onto it or something)
@@ -224,14 +219,14 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
if (!_random.Prob(0.5f))
return;
if (!_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var solution))
if (!_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution))
return;
_popups.PopupEntity(Loc.GetString("puddle-component-slipped-touch-reaction", ("puddle", uid)),
_popups.PopupEntity(Loc.GetString("puddle-component-slipped-touch-reaction", ("puddle", entity.Owner)),
args.Slipped, args.Slipped, PopupType.SmallCaution);
// Take 15% of the puddle solution
var splitSol = _solutionContainerSystem.SplitSolution(uid, solution, solution.Volume * 0.15f);
var splitSol = _solutionContainerSystem.SplitSolution(entity.Comp.Solution.Value, solution.Volume * 0.15f);
_reactive.DoEntityReaction(args.Slipped, splitSol, ReactionMethod.Touch);
}
@@ -248,27 +243,27 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
TickEvaporation();
}
private void OnPuddleInit(EntityUid uid, PuddleComponent component, ComponentInit args)
private void OnPuddleInit(Entity<PuddleComponent> entity, ref ComponentInit args)
{
_solutionContainerSystem.EnsureSolution(uid, component.SolutionName, FixedPoint2.New(PuddleVolume), out _);
_solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.SolutionName, FixedPoint2.New(PuddleVolume), out _);
}
private void OnSolutionUpdate(EntityUid uid, PuddleComponent component, SolutionChangedEvent args)
private void OnSolutionUpdate(Entity<PuddleComponent> entity, ref SolutionContainerChangedEvent args)
{
if (args.Solution.Name != component.SolutionName)
if (args.SolutionId != entity.Comp.SolutionName)
return;
if (args.Solution.Volume <= 0)
{
_deletionQueue.Add(uid);
_deletionQueue.Add(entity);
return;
}
_deletionQueue.Remove(uid);
UpdateSlip(uid, component, args.Solution);
UpdateSlow(uid, args.Solution);
UpdateEvaporation(uid, args.Solution);
UpdateAppearance(uid, component);
_deletionQueue.Remove(entity);
UpdateSlip(entity, entity.Comp, args.Solution);
UpdateSlow(entity, args.Solution);
UpdateEvaporation(entity, args.Solution);
UpdateAppearance(entity, entity.Comp);
}
private void UpdateAppearance(EntityUid uid, PuddleComponent? puddleComponent = null, AppearanceComponent? appearance = null)
@@ -281,7 +276,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
var volume = FixedPoint2.Zero;
Color color = Color.White;
if (_solutionContainerSystem.TryGetSolution(uid, puddleComponent.SolutionName, out var solution))
if (_solutionContainerSystem.ResolveSolution(uid, puddleComponent.SolutionName, ref puddleComponent.Solution, out var solution))
{
volume = solution.Volume / puddleComponent.OverflowVolume;
@@ -364,39 +359,31 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
}
}
private void HandlePuddleExamined(EntityUid uid, PuddleComponent component, ExaminedEvent args)
private void HandlePuddleExamined(Entity<PuddleComponent> entity, ref ExaminedEvent args)
{
if (TryComp<StepTriggerComponent>(uid, out var slippery) && slippery.Active)
if (TryComp<StepTriggerComponent>(entity, out var slippery) && slippery.Active)
{
args.PushMarkup(Loc.GetString("puddle-component-examine-is-slipper-text"));
}
if (HasComp<EvaporationComponent>(uid))
if (HasComp<EvaporationComponent>(entity) &&
_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution))
{
if (_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var solution) &&
CanFullyEvaporate(solution))
{
if (CanFullyEvaporate(solution))
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating"));
}
else if (solution?.GetTotalPrototypeQuantity(EvaporationReagents) > FixedPoint2.Zero)
{
else if (solution.GetTotalPrototypeQuantity(EvaporationReagents) > FixedPoint2.Zero)
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-partial"));
}
else
{
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-no"));
}
}
else
{
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-no"));
}
}
private void OnAnchorChanged(EntityUid uid, PuddleComponent puddle, ref AnchorStateChangedEvent args)
private void OnAnchorChanged(Entity<PuddleComponent> entity, ref AnchorStateChangedEvent args)
{
if (!args.Anchored)
QueueDel(uid);
QueueDel(entity);
}
/// <summary>
@@ -407,8 +394,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
if (!Resolve(uid, ref puddleComponent))
return FixedPoint2.Zero;
return _solutionContainerSystem.TryGetSolution(uid, puddleComponent.SolutionName,
out var solution)
return _solutionContainerSystem.ResolveSolution(uid, puddleComponent.SolutionName, ref puddleComponent.Solution, out var solution)
? solution.Volume
: FixedPoint2.Zero;
}
@@ -432,14 +418,12 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
return false;
if (addedSolution.Volume == 0 ||
!_solutionContainerSystem.TryGetSolution(puddleUid, puddleComponent.SolutionName,
out var solution))
!_solutionContainerSystem.ResolveSolution(puddleUid, puddleComponent.SolutionName, ref puddleComponent.Solution))
{
return false;
}
solution.AddSolution(addedSolution, _prototypeManager);
_solutionContainerSystem.UpdateChemicals(puddleUid, solution, true);
_solutionContainerSystem.AddSolution(puddleComponent.Solution.Value, addedSolution);
if (checkForOverflow && IsOverflowing(puddleUid, puddleComponent))
{
@@ -482,15 +466,14 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
/// </summary>
public Solution GetOverflowSolution(EntityUid uid, PuddleComponent? puddle = null)
{
if (!Resolve(uid, ref puddle) || !_solutionContainerSystem.TryGetSolution(uid, puddle.SolutionName,
out var solution))
if (!Resolve(uid, ref puddle) || !_solutionContainerSystem.ResolveSolution(uid, puddle.SolutionName, ref puddle.Solution))
{
return new Solution(0);
}
// TODO: This is going to fail with struct solutions.
var remaining = puddle.OverflowVolume;
var split = _solutionContainerSystem.SplitSolution(uid, solution, CurrentVolume(uid, puddle) - remaining);
var split = _solutionContainerSystem.SplitSolution(puddle.Solution.Value, CurrentVolume(uid, puddle) - remaining);
return split;
}

View File

@@ -1,7 +1,7 @@
using System.Linq;
using Content.Server.Administration.Logs;
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Chemistry.ReactionEffects;
using Content.Server.Spreader;
using Content.Shared.Chemistry;
@@ -21,6 +21,8 @@ using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using System.Linq;
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
namespace Content.Server.Fluids.EntitySystems;
@@ -42,7 +44,7 @@ public sealed class SmokeSystem : EntitySystem
[Dependency] private readonly ReactiveSystem _reactive = default!;
[Dependency] private readonly SharedBroadphaseSystem _broadphase = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly TransformSystem _transform = default!;
private EntityQuery<SmokeComponent> _smokeQuery;
@@ -59,6 +61,7 @@ public sealed class SmokeSystem : EntitySystem
SubscribeLocalEvent<SmokeComponent, StartCollideEvent>(OnStartCollide);
SubscribeLocalEvent<SmokeComponent, EndCollideEvent>(OnEndCollide);
SubscribeLocalEvent<SmokeComponent, ReactionAttemptEvent>(OnReactionAttempt);
SubscribeLocalEvent<SmokeComponent, SolutionRelayEvent<ReactionAttemptEvent>>(OnReactionAttempt);
SubscribeLocalEvent<SmokeComponent, SpreadNeighborsEvent>(OnSmokeSpread);
SubscribeLocalEvent<SmokeAffectedComponent, EntityUnpausedEvent>(OnAffectedUnpaused);
}
@@ -80,33 +83,33 @@ public sealed class SmokeSystem : EntitySystem
}
}
private void OnStartCollide(EntityUid uid, SmokeComponent component, ref StartCollideEvent args)
private void OnStartCollide(Entity<SmokeComponent> entity, ref StartCollideEvent args)
{
if (_smokeAffectedQuery.HasComponent(args.OtherEntity))
return;
var smokeAffected = AddComp<SmokeAffectedComponent>(args.OtherEntity);
smokeAffected.SmokeEntity = uid;
smokeAffected.SmokeEntity = entity;
smokeAffected.NextSecond = _timing.CurTime + TimeSpan.FromSeconds(1);
}
private void OnEndCollide(EntityUid uid, SmokeComponent component, ref EndCollideEvent args)
private void OnEndCollide(Entity<SmokeComponent> entity, ref EndCollideEvent args)
{
// if we are already in smoke, make sure the thing we are exiting is the current smoke we are in.
if (_smokeAffectedQuery.TryGetComponent(args.OtherEntity, out var smokeAffectedComponent))
{
if (smokeAffectedComponent.SmokeEntity != uid)
if (smokeAffectedComponent.SmokeEntity != entity.Owner)
return;
}
var exists = Exists(uid);
var exists = Exists(entity);
if (!TryComp<PhysicsComponent>(args.OtherEntity, out var body))
return;
foreach (var ent in _physics.GetContactingEntities(args.OtherEntity, body))
{
if (exists && ent == uid)
if (exists && ent == entity.Owner)
continue;
if (!_smokeQuery.HasComponent(ent))
@@ -121,51 +124,51 @@ public sealed class SmokeSystem : EntitySystem
RemComp(args.OtherEntity, smokeAffectedComponent);
}
private void OnAffectedUnpaused(EntityUid uid, SmokeAffectedComponent component, ref EntityUnpausedEvent args)
private void OnAffectedUnpaused(Entity<SmokeAffectedComponent> entity, ref EntityUnpausedEvent args)
{
component.NextSecond += args.PausedTime;
entity.Comp.NextSecond += args.PausedTime;
}
private void OnSmokeSpread(EntityUid uid, SmokeComponent component, ref SpreadNeighborsEvent args)
private void OnSmokeSpread(Entity<SmokeComponent> entity, ref SpreadNeighborsEvent args)
{
if (component.SpreadAmount == 0 || !_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution))
if (entity.Comp.SpreadAmount == 0 || !_solutionContainerSystem.ResolveSolution(entity.Owner, SmokeComponent.SolutionName, ref entity.Comp.Solution, out var solution))
{
RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
RemCompDeferred<ActiveEdgeSpreaderComponent>(entity);
return;
}
if (Prototype(uid) is not { } prototype)
if (Prototype(entity) is not { } prototype)
{
RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
RemCompDeferred<ActiveEdgeSpreaderComponent>(entity);
return;
}
if (!args.NeighborFreeTiles.Any())
return;
TryComp<TimedDespawnComponent>(uid, out var timer);
TryComp<TimedDespawnComponent>(entity, out var timer);
// wtf is the logic behind any of this.
var smokePerSpread = component.SpreadAmount / Math.Max(1, args.NeighborFreeTiles.Count);
var smokePerSpread = entity.Comp.SpreadAmount / Math.Max(1, args.NeighborFreeTiles.Count);
foreach (var neighbor in args.NeighborFreeTiles)
{
var coords = neighbor.Grid.GridTileToLocal(neighbor.Tile);
var ent = Spawn(prototype.ID, coords);
var spreadAmount = Math.Max(0, smokePerSpread);
component.SpreadAmount -= args.NeighborFreeTiles.Count();
entity.Comp.SpreadAmount -= args.NeighborFreeTiles.Count();
StartSmoke(ent, solution.Clone(), timer?.Lifetime ?? component.Duration, spreadAmount);
StartSmoke(ent, solution.Clone(), timer?.Lifetime ?? entity.Comp.Duration, spreadAmount);
if (component.SpreadAmount == 0)
if (entity.Comp.SpreadAmount == 0)
{
RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
RemCompDeferred<ActiveEdgeSpreaderComponent>(entity);
break;
}
}
args.Updates--;
if (args.NeighborFreeTiles.Count > 0 || args.Neighbors.Count == 0 || component.SpreadAmount < 1)
if (args.NeighborFreeTiles.Count > 0 || args.Neighbors.Count == 0 || entity.Comp.SpreadAmount < 1)
return;
// We have no more neighbours to spread to. So instead we will randomly distribute our volume to neighbouring smoke tiles.
@@ -179,21 +182,21 @@ public sealed class SmokeSystem : EntitySystem
continue;
smoke.SpreadAmount++;
component.SpreadAmount--;
entity.Comp.SpreadAmount--;
EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
if (component.SpreadAmount == 0)
if (entity.Comp.SpreadAmount == 0)
{
RemCompDeferred<ActiveEdgeSpreaderComponent>(uid);
RemCompDeferred<ActiveEdgeSpreaderComponent>(entity);
break;
}
}
}
private void OnReactionAttempt(EntityUid uid, SmokeComponent component, ReactionAttemptEvent args)
private void OnReactionAttempt(Entity<SmokeComponent> entity, ref ReactionAttemptEvent args)
{
if (args.Solution.Name != SmokeComponent.SolutionName)
if (args.Cancelled)
return;
// Prevent smoke/foam fork bombs (smoke creating more smoke).
@@ -201,12 +204,18 @@ public sealed class SmokeSystem : EntitySystem
{
if (effect is AreaReactionEffect)
{
args.Cancel();
args.Cancelled = true;
return;
}
}
}
private void OnReactionAttempt(Entity<SmokeComponent> entity, ref SolutionRelayEvent<ReactionAttemptEvent> args)
{
if (args.Name == SmokeComponent.SolutionName)
OnReactionAttempt(entity, ref args.Event);
}
/// <summary>
/// Sets up a smoke component for spreading.
/// </summary>
@@ -245,14 +254,14 @@ public sealed class SmokeSystem : EntitySystem
if (!Resolve(smokeUid, ref component))
return;
if (!_solutionSystem.TryGetSolution(smokeUid, SmokeComponent.SolutionName, out var solution) ||
if (!_solutionContainerSystem.ResolveSolution(smokeUid, SmokeComponent.SolutionName, ref component.Solution, out var solution) ||
solution.Contents.Count == 0)
{
return;
}
ReactWithEntity(entity, smokeUid, solution, component);
UpdateVisuals(smokeUid);
UpdateVisuals((smokeUid, component));
}
private void ReactWithEntity(EntityUid entity, EntityUid smokeUid, Solution solution, SmokeComponent? component = null)
@@ -263,11 +272,14 @@ public sealed class SmokeSystem : EntitySystem
if (!TryComp<BloodstreamComponent>(entity, out var bloodstream))
return;
var blockIngestion = _internals.AreInternalsWorking(entity);
if (!_solutionContainerSystem.ResolveSolution(entity, bloodstream.ChemicalSolutionName, ref bloodstream.ChemicalSolution, out var chemSolution) || chemSolution.AvailableVolume <= 0)
return;
var blockIngestion = _internals.AreInternalsWorking(entity);
var cloneSolution = solution.Clone();
var availableTransfer = FixedPoint2.Min(cloneSolution.Volume, component.TransferRate);
var transferAmount = FixedPoint2.Min(availableTransfer, bloodstream.ChemicalSolution.AvailableVolume);
var transferAmount = FixedPoint2.Min(availableTransfer, chemSolution.AvailableVolume);
var transferSolution = cloneSolution.SplitSolution(transferAmount);
foreach (var reagentQuantity in transferSolution.Contents.ToArray())
@@ -296,7 +308,7 @@ public sealed class SmokeSystem : EntitySystem
if (!Resolve(uid, ref component, ref xform))
return;
if (!_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution) || !solution.Any())
if (!_solutionContainerSystem.ResolveSolution(uid, SmokeComponent.SolutionName, ref component.Solution, out var solution) || !solution.Any())
return;
if (!_mapManager.TryGetGrid(xform.GridUid, out var mapGrid))
@@ -317,29 +329,30 @@ public sealed class SmokeSystem : EntitySystem
/// <summary>
/// Adds the specified solution to the relevant smoke solution.
/// </summary>
private void TryAddSolution(EntityUid uid, Solution solution)
private void TryAddSolution(Entity<SmokeComponent?> smoke, Solution solution)
{
if (solution.Volume == FixedPoint2.Zero)
return;
if (!_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solutionArea))
if (!Resolve(smoke, ref smoke.Comp))
return;
var addSolution =
solution.SplitSolution(FixedPoint2.Min(solution.Volume, solutionArea.AvailableVolume));
if (!_solutionContainerSystem.ResolveSolution(smoke.Owner, SmokeComponent.SolutionName, ref smoke.Comp.Solution, out var solutionArea))
return;
_solutionSystem.TryAddSolution(uid, solutionArea, addSolution);
var addSolution = solution.SplitSolution(FixedPoint2.Min(solution.Volume, solutionArea.AvailableVolume));
_solutionContainerSystem.TryAddSolution(smoke.Comp.Solution.Value, addSolution);
UpdateVisuals(uid);
UpdateVisuals(smoke);
}
private void UpdateVisuals(EntityUid uid)
private void UpdateVisuals(Entity<SmokeComponent?, AppearanceComponent?> smoke)
{
if (!TryComp(uid, out AppearanceComponent? appearance) ||
!_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution))
if (!Resolve(smoke, ref smoke.Comp1, ref smoke.Comp2) ||
!_solutionContainerSystem.ResolveSolution(smoke.Owner, SmokeComponent.SolutionName, ref smoke.Comp1.Solution, out var solution))
return;
var color = solution.GetColor(_prototype);
_appearance.SetData(uid, SmokeVisuals.Color, color, appearance);
_appearance.SetData(smoke.Owner, SmokeVisuals.Color, color, smoke.Comp2);
}
}

View File

@@ -1,22 +1,21 @@
using System.Numerics;
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Chemistry.EntitySystems;
using Content.Server.Cooldown;
using Content.Server.Extinguisher;
using Content.Server.Fluids.Components;
using Content.Server.Gravity;
using Content.Server.Popups;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Cooldown;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction;
using Content.Shared.Vapor;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Physics.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using System.Numerics;
namespace Content.Server.Fluids.EntitySystems;
@@ -40,23 +39,23 @@ public sealed class SpraySystem : EntitySystem
SubscribeLocalEvent<SprayComponent, AfterInteractEvent>(OnAfterInteract, after: new[] { typeof(FireExtinguisherSystem) });
}
private void OnAfterInteract(EntityUid uid, SprayComponent component, AfterInteractEvent args)
private void OnAfterInteract(Entity<SprayComponent> entity, ref AfterInteractEvent args)
{
if (args.Handled)
return;
args.Handled = true;
if (!_solutionContainer.TryGetSolution(uid, SprayComponent.SolutionName, out var solution))
if (!_solutionContainer.TryGetSolution(entity.Owner, SprayComponent.SolutionName, out var soln, out var solution))
return;
var ev = new SprayAttemptEvent(args.User);
RaiseLocalEvent(uid, ev);
RaiseLocalEvent(entity, ev);
if (ev.Cancelled)
return;
var curTime = _gameTiming.CurTime;
if (TryComp<ItemCooldownComponent>(uid, out var cooldown)
if (TryComp<ItemCooldownComponent>(entity, out var cooldown)
&& curTime < cooldown.CooldownEnd)
{
return;
@@ -64,8 +63,7 @@ public sealed class SpraySystem : EntitySystem
if (solution.Volume <= 0)
{
_popupSystem.PopupEntity(Loc.GetString("spray-component-is-empty-message"), uid,
args.User);
_popupSystem.PopupEntity(Loc.GetString("spray-component-is-empty-message"), entity.Owner, args.User);
return;
}
@@ -82,9 +80,9 @@ public sealed class SpraySystem : EntitySystem
var diffNorm = diffPos.Normalized();
var diffLength = diffPos.Length();
if (diffLength > component.SprayDistance)
if (diffLength > entity.Comp.SprayDistance)
{
diffLength = component.SprayDistance;
diffLength = entity.Comp.SprayDistance;
}
var diffAngle = diffNorm.ToAngle();
@@ -93,8 +91,8 @@ public sealed class SpraySystem : EntitySystem
var threeQuarters = diffNorm * 0.75f;
var quarter = diffNorm * 0.25f;
var amount = Math.Max(Math.Min((solution.Volume / component.TransferAmount).Int(), component.VaporAmount), 1);
var spread = component.VaporSpread / amount;
var amount = Math.Max(Math.Min((solution.Volume / entity.Comp.TransferAmount).Int(), entity.Comp.VaporAmount), 1);
var spread = entity.Comp.VaporSpread / amount;
// TODO: Just use usedelay homie.
var cooldownTime = 0f;
@@ -108,18 +106,18 @@ public sealed class SpraySystem : EntitySystem
.Offset((diffNorm + rotation.ToVec()).Normalized() * diffLength + quarter);
var distance = (target.Position - userMapPos.Position).Length();
if (distance > component.SprayDistance)
target = userMapPos.Offset(diffNorm * component.SprayDistance);
if (distance > entity.Comp.SprayDistance)
target = userMapPos.Offset(diffNorm * entity.Comp.SprayDistance);
var adjustedSolutionAmount = component.TransferAmount / component.VaporAmount;
var newSolution = _solutionContainer.SplitSolution(uid, solution, adjustedSolutionAmount);
var adjustedSolutionAmount = entity.Comp.TransferAmount / entity.Comp.VaporAmount;
var newSolution = _solutionContainer.SplitSolution(soln.Value, adjustedSolutionAmount);
if (newSolution.Volume <= FixedPoint2.Zero)
break;
// Spawn the vapor cloud onto the grid/map the user is present on. Offset the start position based on how far the target destination is.
var vaporPos = userMapPos.Offset(distance < 1 ? quarter : threeQuarters);
var vapor = Spawn(component.SprayedPrototype, vaporPos);
var vapor = Spawn(entity.Comp.SprayedPrototype, vaporPos);
var vaporXform = xformQuery.GetComponent(vapor);
_transform.SetWorldRotation(vaporXform, rotation);
@@ -137,22 +135,21 @@ public sealed class SpraySystem : EntitySystem
// impulse direction is defined in world-coordinates, not local coordinates
var impulseDirection = rotation.ToVec();
var time = diffLength / component.SprayVelocity;
var time = diffLength / entity.Comp.SprayVelocity;
cooldownTime = MathF.Max(time, cooldownTime);
_vapor.Start(ent, vaporXform, impulseDirection * diffLength, component.SprayVelocity, target, time, args.User);
_vapor.Start(ent, vaporXform, impulseDirection * diffLength, entity.Comp.SprayVelocity, target, time, args.User);
if (TryComp<PhysicsComponent>(args.User, out var body))
{
if (_gravity.IsWeightless(args.User, body))
_physics.ApplyLinearImpulse(args.User, -impulseDirection.Normalized() * component.PushbackAmount, body: body);
_physics.ApplyLinearImpulse(args.User, -impulseDirection.Normalized() * entity.Comp.PushbackAmount, body: body);
}
}
_audio.PlayPvs(component.SpraySound, uid, component.SpraySound.Params.WithVariation(0.125f));
_audio.PlayPvs(entity.Comp.SpraySound, entity, entity.Comp.SpraySound.Params.WithVariation(0.125f));
RaiseLocalEvent(uid,
new RefreshItemCooldownEvent(curTime, curTime + TimeSpan.FromSeconds(cooldownTime)), true);
RaiseLocalEvent(entity, new RefreshItemCooldownEvent(curTime, curTime + TimeSpan.FromSeconds(cooldownTime)), true);
}
}