Mopping enhancements (#3298)

* Bucket starts with water
* Mop has do_after
* Bucket has do_after (user allowed to move while using it as long as they're in range at the end for maximum jannie skill)
* Random cleanup

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
metalgearsloth
2021-02-25 11:32:08 +11:00
committed by GitHub
parent 48b1336e3c
commit adbcfbb951
3 changed files with 115 additions and 77 deletions

View File

@@ -1,13 +1,17 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Chemistry;
using Content.Server.GameObjects.EntitySystems.DoAfter;
using Content.Shared.Chemistry;
using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Utility;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.Components.Fluids
@@ -20,6 +24,8 @@ namespace Content.Server.GameObjects.Components.Fluids
{
public override string Name => "Bucket";
private List<EntityUid> _currentlyUsing = new();
public ReagentUnit MaxVolume
{
get => Owner.TryGetComponent(out SolutionContainerComponent? solution) ? solution.MaxVolume : ReagentUnit.Zero;
@@ -51,64 +57,50 @@ namespace Content.Server.GameObjects.Components.Fluids
Owner.EnsureComponentWarn<SolutionContainerComponent>();
}
private bool TryGiveToMop(MopComponent mopComponent)
{
if (!Owner.TryGetComponent(out SolutionContainerComponent? contents))
{
return false;
}
var mopContents = mopComponent.Contents;
if (mopContents == null)
{
return false;
}
// Let's fill 'er up
// If this is called the mop should be empty but just in case we'll do Max - Current
var transferAmount = ReagentUnit.Min(mopComponent.MaxVolume - mopComponent.CurrentVolume, CurrentVolume);
var solution = contents.SplitSolution(transferAmount);
if (!mopContents.TryAddSolution(solution) || mopComponent.CurrentVolume == 0)
{
return false;
}
if (_sound == null)
{
return true;
}
EntitySystem.Get<AudioSystem>().PlayFromEntity(_sound, Owner);
return true;
}
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
{
if (!Owner.TryGetComponent(out SolutionContainerComponent? contents))
if (!Owner.TryGetComponent(out SolutionContainerComponent? contents) ||
_currentlyUsing.Contains(eventArgs.Using.Uid) ||
!eventArgs.Using.TryGetComponent(out MopComponent? mopComponent) ||
mopComponent.Mopping)
{
return false;
}
if (!eventArgs.Using.TryGetComponent(out MopComponent? mopComponent))
if (CurrentVolume <= 0)
{
Owner.PopupMessage(eventArgs.User, Loc.GetString("Bucket is empty"));
return false;
}
// Give to the mop if it's empty
if (mopComponent.CurrentVolume == 0)
{
if (!TryGiveToMop(mopComponent))
if (mopComponent.CurrentVolume == mopComponent.MaxVolume)
{
Owner.PopupMessage(eventArgs.User, Loc.GetString("Mop is full"));
return false;
}
Owner.PopupMessage(eventArgs.User, Loc.GetString("Splish"));
return true;
}
_currentlyUsing.Add(eventArgs.Using.Uid);
var transferAmount = ReagentUnit.Min(mopComponent.CurrentVolume, MaxVolume - CurrentVolume);
// IMO let em move while doing it.
var doAfterArgs = new DoAfterEventArgs(eventArgs.User, 1.0f, target: eventArgs.Target)
{
BreakOnStun = true,
BreakOnDamage = true,
};
var result = await EntitySystem.Get<DoAfterSystem>().DoAfter(doAfterArgs);
_currentlyUsing.Remove(eventArgs.Using.Uid);
if (result == DoAfterStatus.Cancelled ||
Owner.Deleted ||
mopComponent.Deleted ||
CurrentVolume <= 0 ||
!Owner.InRangeUnobstructed(mopComponent.Owner))
return false;
// Top up mops solution given it needs it to annihilate puddles I guess
var transferAmount = ReagentUnit.Min(mopComponent.MaxVolume - mopComponent.CurrentVolume, CurrentVolume);
if (transferAmount == 0)
{
return false;
@@ -121,22 +113,16 @@ namespace Content.Server.GameObjects.Components.Fluids
return false;
}
var solution = mopContents.SplitSolution(transferAmount);
if (!contents.TryAddSolution(solution))
var solution = contents.SplitSolution(transferAmount);
if (!mopContents.TryAddSolution(solution))
{
//This really shouldn't happen
throw new InvalidOperationException();
return false;
}
// Give some visual feedback shit's happening (for anyone who can't hear sound)
Owner.PopupMessage(eventArgs.User, Loc.GetString("Sploosh"));
if (_sound == null)
if (_sound != null)
{
return true;
}
EntitySystem.Get<AudioSystem>().PlayFromEntity(_sound, Owner);
}
return true;
}

View File

@@ -8,7 +8,9 @@ using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Serialization;
using System.Threading.Tasks;
using Content.Server.GameObjects.EntitySystems.DoAfter;
using Robust.Server.GameObjects;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Fluids
{
@@ -20,6 +22,13 @@ namespace Content.Server.GameObjects.Components.Fluids
{
public override string Name => "Mop";
public bool Mopping => _mopping;
/// <summary>
/// Used to prevent do_after spam if we're currently mopping.
/// </summary>
private bool _mopping;
public SolutionContainerComponent? Contents => Owner.GetComponentOrNull<SolutionContainerComponent>();
public ReagentUnit MaxVolume
@@ -44,7 +53,13 @@ namespace Content.Server.GameObjects.Components.Fluids
public ReagentUnit PickupAmount => _pickupAmount;
private ReagentUnit _pickupAmount;
private string _pickupSound = "";
private string? _pickupSound;
/// <summary>
/// Multiplier for the do_after delay for how fast the mop works.
/// </summary>
[ViewVariables]
private float _mopSpeed;
/// <inheritdoc />
public override void ExposeData(ObjectSerializer serializer)
@@ -52,6 +67,7 @@ namespace Content.Server.GameObjects.Components.Fluids
serializer.DataFieldCached(ref _pickupSound, "pickup_sound", "/Audio/Effects/Fluids/slosh.ogg");
// The turbo mop will pickup more
serializer.DataFieldCached(ref _pickupAmount, "pickup_amount", ReagentUnit.New(5));
serializer.DataField(ref _mopSpeed, "speed", 1.0f);
}
public override void Initialize()
@@ -63,34 +79,69 @@ namespace Content.Server.GameObjects.Components.Fluids
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
{
if (!Owner.TryGetComponent(out SolutionContainerComponent? contents))
return false;
if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true))
return false;
/*
* Functionality:
* Essentially if we click on an empty tile spill our contents there
* Otherwise, try to mop up the puddle (if it is a puddle).
* It will try to destroy solution on the mop to do so, and if it is successful
* will spill some of the mop's solution onto the puddle which will evaporate eventually.
*/
if (CurrentVolume <= 0)
if (!Owner.TryGetComponent(out SolutionContainerComponent? contents) ||
_mopping ||
!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true))
{
return true;
return false;
}
var currentVolume = CurrentVolume;
if (eventArgs.Target == null)
{
if (currentVolume > 0)
{
// Drop the liquid on the mop on to the ground
contents.SplitSolution(CurrentVolume).SpillAt(eventArgs.ClickLocation, "PuddleSmear");
return true;
}
return false;
}
if (!eventArgs.Target.TryGetComponent(out PuddleComponent? puddleComponent))
{
return true;
return false;
}
// Essentially pickup either:
// - _pickupAmount,
// - whatever's left in the puddle, or
// - whatever we can still hold (whichever's smallest)
var puddleVolume = puddleComponent.CurrentVolume;
if (currentVolume <= 0)
{
Owner.PopupMessage(eventArgs.User, Loc.GetString("Mop needs to be wet!"));
return false;
}
_mopping = true;
// So if the puddle has 20 units we mop in 2 seconds. Don't just store CurrentVolume given it can change so need to re-calc it anyway.
var doAfterArgs = new DoAfterEventArgs(eventArgs.User, _mopSpeed * puddleVolume.Float() / 10.0f, target: eventArgs.Target)
{
BreakOnUserMove = true,
BreakOnStun = true,
BreakOnDamage = true,
};
var result = await EntitySystem.Get<DoAfterSystem>().DoAfter(doAfterArgs);
_mopping = false;
if (result == DoAfterStatus.Cancelled ||
Owner.Deleted ||
puddleComponent.Deleted)
return false;
// Annihilate the puddle
var transferAmount = ReagentUnit.Min(ReagentUnit.New(5), puddleComponent.CurrentVolume, CurrentVolume);
bool puddleCleaned = puddleComponent.CurrentVolume - transferAmount <= 0;
var puddleCleaned = puddleComponent.CurrentVolume - transferAmount <= 0;
if (transferAmount == 0)
{
@@ -119,9 +170,6 @@ namespace Content.Server.GameObjects.Components.Fluids
contents.SplitSolution(transferAmount);
}
// Give some visual feedback shit's happening (for anyone who can't hear sound)
Owner.PopupMessage(eventArgs.User, Loc.GetString("Swish"));
if (!string.IsNullOrWhiteSpace(_pickupSound))
{
EntitySystem.Get<AudioSystem>().PlayFromEntity(_pickupSound, Owner);

View File

@@ -14,12 +14,12 @@
- type: Mop
- type: SolutionContainer
maxVol: 10
- type: LoopingSound
- type: entity
name: mop bucket
id: MopBucket
description: Holds water and the tears of the janitor.
suffix: Full
components:
- type: Clickable
- type: Sprite
@@ -33,6 +33,11 @@
- type: LoopingSound
- type: SolutionContainer
maxVol: 500
contents:
reagents:
- ReagentId: chem.Water
Quantity: 500
- type: Physics
mass: 5
anchored: false
@@ -44,7 +49,6 @@
- Opaque
layer:
- Opaque
- type: Spillable
- type: Pullable
- type: entity