Files
tbd-station-14/Content.Shared/Fluids/SharedPuddleSystem.Evaporation.cs
Perry Fraser c65f0aeb31 Make a lot more puddle stuff predicted (#38871)
* feat: predict evaporation

* refactor: move puddle update logic to shared

* refactor: move more puddle stuff to Shared

Still can't do stuff that creates puddles :(

* refactor: move puddle transfers to shared

* fix: various style fixes + switch to predicted variants

* style: make some puddle stuff private instead of protected

* refactor: move solution dumping to its own system

* docs: clarify Drainable/Dumpable/Refillable docs

Also whacks unneeded VVAccess's.

* fix: audit usages of drainable+refillable

I'm leaving spear and arrow for now... but I don't love it.

* Added an item query I guess

* Review changes

* You can pour out waterguns

* Review changes

* oops

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
Co-authored-by: SlamBamActionman <slambamactionman@gmail.com>
2025-10-18 17:41:56 +00:00

125 lines
4.6 KiB
C#

using System.Linq;
using Content.Shared.Chemistry.Components;
using Content.Shared.FixedPoint;
using Content.Shared.Fluids.Components;
namespace Content.Shared.Fluids;
public abstract partial class SharedPuddleSystem
{
private static readonly TimeSpan EvaporationCooldown = TimeSpan.FromSeconds(1);
private void OnEvaporationMapInit(Entity<EvaporationComponent> ent, ref MapInitEvent args)
{
ent.Comp.NextTick = _timing.CurTime + EvaporationCooldown;
Dirty(ent);
}
private void UpdateEvaporation(EntityUid uid, Solution solution)
{
if (_evaporationQuery.HasComp(uid))
return;
if (solution.GetTotalPrototypeQuantity(GetEvaporatingReagents(solution)) > FixedPoint2.Zero)
{
var evaporation = AddComp<EvaporationComponent>(uid);
evaporation.NextTick = _timing.CurTime + EvaporationCooldown;
Dirty<EvaporationComponent>((uid, evaporation));
return;
}
RemComp<EvaporationComponent>(uid);
}
private void TickEvaporation()
{
var query = EntityQueryEnumerator<EvaporationComponent, PuddleComponent>();
var curTime = _timing.CurTime;
while (query.MoveNext(out var uid, out var evaporation, out var puddle))
{
if (evaporation.NextTick > curTime)
continue;
// Necessary to keep client and server in sync so they don't drift
evaporation.NextTick += EvaporationCooldown;
Dirty(uid, evaporation);
if (!_solutionContainerSystem.ResolveSolution(uid, puddle.SolutionName, ref puddle.Solution, out var puddleSolution))
continue;
// If we have multiple evaporating reagents in one puddle, just take the average evaporation speed and apply
// that to all of them.
var evaporationSpeeds = GetEvaporationSpeeds(puddleSolution);
if (evaporationSpeeds.Count == 0)
continue;
// Can't use .Average because FixedPoint2
var evaporationSpeed = evaporationSpeeds.Values.Sum() / evaporationSpeeds.Count;
var reagentProportions = evaporationSpeeds.ToDictionary(kv => kv.Key,
kv => puddleSolution.GetTotalPrototypeQuantity(kv.Key) / puddleSolution.Volume);
// Still have to iterate over one-by-one since the full solution could have non-evaporating solutions.
foreach (var (reagent, factor) in reagentProportions)
{
var reagentTick = evaporation.EvaporationAmount * EvaporationCooldown.TotalSeconds * evaporationSpeed * factor;
puddleSolution.SplitSolutionWithOnly(reagentTick, reagent);
}
// Despawn if we're done
if (puddleSolution.Volume == FixedPoint2.Zero)
{
// Spawn a *sparkle*
SpawnAttachedTo(evaporation.EvaporationEffect, Transform(uid).Coordinates);
PredictedQueueDel(uid);
}
_solutionContainerSystem.UpdateChemicals(puddle.Solution.Value);
}
}
public string[] GetEvaporatingReagents(Solution solution)
{
List<string> evaporatingReagents = [];
foreach (var solProto in solution.GetReagentPrototypes(_prototypeManager).Keys)
{
if (solProto.EvaporationSpeed > FixedPoint2.Zero)
evaporatingReagents.Add(solProto.ID);
}
return evaporatingReagents.ToArray();
}
public string[] GetAbsorbentReagents(Solution solution)
{
List<string> absorbentReagents = [];
foreach (var solProto in solution.GetReagentPrototypes(_prototypeManager).Keys)
{
if (solProto.Absorbent)
absorbentReagents.Add(solProto.ID);
}
return absorbentReagents.ToArray();
}
public bool CanFullyEvaporate(Solution solution)
{
return solution.GetTotalPrototypeQuantity(GetEvaporatingReagents(solution)) == solution.Volume;
}
/// <summary>
/// Gets a mapping of evaporating speed of the reagents within a solution.
/// The speed at which a solution evaporates is the average of the speed of all evaporating reagents in it.
/// </summary>
public Dictionary<string, FixedPoint2> GetEvaporationSpeeds(Solution solution)
{
Dictionary<string, FixedPoint2> evaporatingSpeeds = [];
foreach (var solProto in solution.GetReagentPrototypes(_prototypeManager).Keys)
{
if (solProto.EvaporationSpeed > FixedPoint2.Zero)
{
evaporatingSpeeds.Add(solProto.ID, solProto.EvaporationSpeed);
}
}
return evaporatingSpeeds;
}
}