Emergent Sanitation Gameplay (#1378)

* Emergent Sanitation Gameplay

* Fix the map

* Address review

* Mention if it's slippery in the description
This commit is contained in:
ike709
2020-07-11 16:49:54 -05:00
committed by GitHub
parent 531d9626ad
commit 203a835264
20 changed files with 1011 additions and 45 deletions

View File

@@ -65,17 +65,16 @@ namespace Content.Server.GameObjects.Components.Fluids
{
if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return;
Solution solution;
if (CurrentVolume <= 0)
{
return;
}
//Solution solution;
if (eventArgs.Target == null)
{
if (CurrentVolume <= 0)
{
return;
}
// Drop the liquid on the mop on to the ground I guess? Potentially change by design
// Maybe even use a toggle mode instead of "Pickup" and "dropoff"
solution = _contents.SplitSolution(CurrentVolume);
SpillHelper.SpillAt(eventArgs.ClickLocation, solution, "PuddleSmear");
// Drop the liquid on the mop on to the ground
SpillHelper.SpillAt(eventArgs.ClickLocation, _contents.SplitSolution(CurrentVolume), "PuddleSmear");
return;
}
@@ -88,18 +87,34 @@ namespace Content.Server.GameObjects.Components.Fluids
// - _pickupAmount,
// - whatever's left in the puddle, or
// - whatever we can still hold (whichever's smallest)
var transferAmount = ReagentUnit.Min(ReagentUnit.New(5), puddleComponent.CurrentVolume, MaxVolume - CurrentVolume);
var transferAmount = ReagentUnit.Min(ReagentUnit.New(5), puddleComponent.CurrentVolume, CurrentVolume);
bool puddleCleaned = puddleComponent.CurrentVolume - transferAmount <= 0;
if (transferAmount == 0)
{
return;
if(puddleComponent.EmptyHolder) //The puddle doesn't actually *have* reagents, for example vomit because there's no "vomit" reagent.
{
puddleComponent.Owner.Delete();
transferAmount = ReagentUnit.Min(ReagentUnit.New(5), CurrentVolume);
puddleCleaned = true;
}
else
{
return;
}
}
else
{
puddleComponent.SplitSolution(transferAmount);
}
solution = puddleComponent.SplitSolution(transferAmount);
// Probably don't recolor a mop? Could work, if we layered it maybe
if (!_contents.TryAddSolution(solution, false, true))
if (puddleCleaned) //After cleaning the puddle, make a new puddle with solution from the mop as a "wet floor". Then evaporate it slowly.
{
// I can't imagine why this would happen
throw new InvalidOperationException();
SpillHelper.SpillAt(eventArgs.ClickLocation, _contents.SplitSolution(transferAmount), "PuddleSmear");
}
else
{
_contents.SplitSolution(transferAmount);
}
// Give some visual feedback shit's happening (for anyone who can't hear sound)

View File

@@ -2,7 +2,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Content.Server.GameObjects.Components.Movement;
using Content.Server.GameObjects.Components.Chemistry;
using Content.Server.GameObjects.EntitySystems.Click;
using Content.Shared.Chemistry;
using Content.Shared.Physics;
using Robust.Server.GameObjects;
@@ -15,6 +17,7 @@ using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
@@ -27,7 +30,7 @@ namespace Content.Server.GameObjects.Components.Fluids
/// Puddle on a floor
/// </summary>
[RegisterComponent]
public class PuddleComponent : Component
public class PuddleComponent : Component, IExamine
{
// Current design: Something calls the SpillHelper.Spill, that will either
// A) Add to an existing puddle at the location (normalised to tile-center) or
@@ -44,12 +47,25 @@ namespace Content.Server.GameObjects.Components.Fluids
#pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager;
[Dependency] private readonly ILocalizationManager _loc;
#pragma warning restore 649
public override string Name => "Puddle";
private CancellationTokenSource _evaporationToken;
private ReagentUnit _evaporateThreshold; // How few <Solution Quantity> we can hold prior to self-destructing
public ReagentUnit EvaporateThreshold
{
get => _evaporateThreshold;
set => _evaporateThreshold = value;
}
private ReagentUnit _slipThreshold = ReagentUnit.New(3);
public ReagentUnit SlipThreshold
{
get => _slipThreshold;
set => _slipThreshold = value;
}
private bool _slippery = false;
private float _evaporateTime;
private string _spillSound;
@@ -78,6 +94,7 @@ namespace Content.Server.GameObjects.Components.Fluids
private ReagentUnit OverflowLeft => CurrentVolume - OverflowVolume;
private SolutionComponent _contents;
public bool EmptyHolder => _contents.ReagentList.Count == 0;
private int _spriteVariants;
// Whether the underlying solution color should be used
private bool _recolor;
@@ -87,11 +104,12 @@ namespace Content.Server.GameObjects.Components.Fluids
{
serializer.DataFieldCached(ref _spillSound, "spill_sound", "/Audio/Effects/Fluids/splat.ogg");
serializer.DataField(ref _overflowVolume, "overflow_volume", ReagentUnit.New(20));
serializer.DataField(ref _evaporateTime, "evaporate_time", 600.0f);
serializer.DataField(ref _evaporateTime, "evaporate_time", 5.0f);
// Long-term probably have this based on the underlying reagents
serializer.DataField(ref _evaporateThreshold, "evaporate_threshold", ReagentUnit.New(2));
serializer.DataField(ref _evaporateThreshold, "evaporate_threshold", ReagentUnit.New(20));
serializer.DataField(ref _spriteVariants, "variants", 1);
serializer.DataField(ref _recolor, "recolor", false);
}
public override void Initialize()
@@ -121,6 +139,16 @@ namespace Content.Server.GameObjects.Components.Fluids
_spriteComponent.LayerSetState(0, $"{baseName}-{randomVariant}"); // TODO: Remove hardcode
_spriteComponent.Rotation = Angle.FromDegrees(robustRandom.Next(0, 359));
// UpdateAppearance should get called soon after this so shouldn't need to call Dirty() here
UpdateStatus();
}
void IExamine.Examine(FormattedMessage message, bool inDetailsRange)
{
if(_slippery)
{
message.AddText(_loc.GetString("It looks slippery."));
}
}
// Flow rate should probably be controlled globally so this is it for now
@@ -174,12 +202,28 @@ namespace Content.Server.GameObjects.Components.Fluids
}
}
private void UpdateStatus()
public void Evaporate()
{
// If UpdateStatus is getting called again it means more fluid has been updated so let's just wait
_evaporationToken?.Cancel();
_contents.SplitSolution(ReagentUnit.Min(ReagentUnit.New(1), _contents.CurrentVolume));
if (CurrentVolume == 0)
{
Owner.Delete();
}
else
{
UpdateStatus();
}
}
if (CurrentVolume > _evaporateThreshold)
public void UpdateStatus()
{
_evaporationToken?.Cancel();
if(Owner.Deleted) return;
UpdateAppearance();
UpdateSlip();
if (_evaporateThreshold == ReagentUnit.New(-1) || CurrentVolume > _evaporateThreshold)
{
return;
}
@@ -187,12 +231,26 @@ namespace Content.Server.GameObjects.Components.Fluids
_evaporationToken = new CancellationTokenSource();
// KYS to evaporate
Timer.Spawn(TimeSpan.FromSeconds(_evaporateTime), CheckEvaporate, _evaporationToken.Token);
Timer.Spawn(TimeSpan.FromSeconds(_evaporateTime), Evaporate, _evaporationToken.Token);
}
private void UpdateSlip()
{
if ((_slipThreshold == ReagentUnit.New(-1) || CurrentVolume < _slipThreshold) && Owner.TryGetComponent(out SlipperyComponent existingSlipperyComponent))
{
Owner.RemoveComponent<SlipperyComponent>();
_slippery = false;
}
else if (CurrentVolume >= _slipThreshold && !Owner.TryGetComponent(out SlipperyComponent newSlipperyComponent))
{
Owner.AddComponent<SlipperyComponent>();
_slippery = true;
}
}
private void UpdateAppearance()
{
if (Owner.Deleted)
if (Owner.Deleted || EmptyHolder)
{
return;
}

View File

@@ -1,3 +1,4 @@
#nullable enable
using Content.Shared.Chemistry;
using Robust.Server.Interfaces.GameObjects;
@@ -31,11 +32,11 @@ namespace Content.Server.GameObjects.Components.Fluids
/// <param name="gridCoordinates"></param>
/// <param name="solution">Initial solution for the prototype</param>
/// <param name="prototype">Prototype to use</param>
internal static void SpillAt(GridCoordinates gridCoordinates, Solution solution, string prototype)
internal static PuddleComponent? SpillAt(GridCoordinates gridCoordinates, Solution solution, string prototype)
{
if (solution.TotalVolume == 0)
{
return;
return null;
}
var mapManager = IoCManager.Resolve<IMapManager>();
@@ -48,7 +49,7 @@ namespace Content.Server.GameObjects.Components.Fluids
var tileRef = mapGrid.GetTileRef(gridCoordinates);
if (tileRef.Tile.IsEmpty)
{
return;
return null;
}
// Get normalized co-ordinate for spill location and spill it in the centre
@@ -78,11 +79,13 @@ namespace Content.Server.GameObjects.Components.Fluids
// Did we add to an existing puddle
if (spilt)
{
return;
return null;
}
var puddle = serverEntityManager.SpawnEntity(prototype, spillGridCoords);
puddle.GetComponent<PuddleComponent>().TryAddSolution(solution);
var newPuddleComponent = puddle.GetComponent<PuddleComponent>();
newPuddleComponent.TryAddSolution(solution);
return newPuddleComponent;
}
}