Added sink (#14348)
@@ -36,6 +36,13 @@ namespace Content.Client.Chemistry.Visualizers
|
|||||||
[DataField("metamorphicNameFull")]
|
[DataField("metamorphicNameFull")]
|
||||||
public string MetamorphicNameFull = "transformable-container-component-glass";
|
public string MetamorphicNameFull = "transformable-container-component-glass";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Which solution of the SolutionContainerManagerComponent to represent.
|
||||||
|
/// If not set, will work as default.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("solutionName")]
|
||||||
|
public string SolutionName = "";
|
||||||
|
|
||||||
public string InitialName = string.Empty;
|
public string InitialName = string.Empty;
|
||||||
public string InitialDescription = string.Empty;
|
public string InitialDescription = string.Empty;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,16 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
|
|||||||
|
|
||||||
protected override void OnAppearanceChange(EntityUid uid, SolutionContainerVisualsComponent component, ref AppearanceChangeEvent args)
|
protected override void OnAppearanceChange(EntityUid uid, SolutionContainerVisualsComponent component, ref AppearanceChangeEvent args)
|
||||||
{
|
{
|
||||||
|
// Check if the solution that was updated is the one set as represented
|
||||||
|
if (!string.IsNullOrEmpty(component.SolutionName))
|
||||||
|
{
|
||||||
|
if (AppearanceSystem.TryGetData<string>(uid, SolutionContainerVisuals.SolutionName, out var name,
|
||||||
|
args.Component) && name != component.SolutionName)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!AppearanceSystem.TryGetData<float>(uid, SolutionContainerVisuals.FillFraction, out var fraction, args.Component))
|
if (!AppearanceSystem.TryGetData<float>(uid, SolutionContainerVisuals.FillFraction, out var fraction, args.Component))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -152,6 +152,10 @@ public sealed partial class SolutionContainerSystem : EntitySystem
|
|||||||
|
|
||||||
_appearance.SetData(uid, SolutionContainerVisuals.FillFraction, solution.FillFraction, appearanceComponent);
|
_appearance.SetData(uid, SolutionContainerVisuals.FillFraction, solution.FillFraction, appearanceComponent);
|
||||||
_appearance.SetData(uid, SolutionContainerVisuals.Color, solution.GetColor(_prototypeManager), appearanceComponent);
|
_appearance.SetData(uid, SolutionContainerVisuals.Color, solution.GetColor(_prototypeManager), appearanceComponent);
|
||||||
|
if (solution.Name != null)
|
||||||
|
{
|
||||||
|
_appearance.SetData(uid, SolutionContainerVisuals.SolutionName, solution.Name, appearanceComponent);
|
||||||
|
}
|
||||||
|
|
||||||
if (solution.GetPrimaryReagentId() is { } reagent)
|
if (solution.GetPrimaryReagentId() is { } reagent)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,18 +1,106 @@
|
|||||||
using Content.Server.Chemistry.Components.SolutionManager;
|
using Content.Server.Chemistry.Components.SolutionManager;
|
||||||
using Content.Server.Fluids.Components;
|
using Content.Server.Fluids.Components;
|
||||||
using Content.Server.Chemistry.EntitySystems;
|
using Content.Server.Chemistry.EntitySystems;
|
||||||
|
using Content.Server.DoAfter;
|
||||||
|
using Content.Server.Popups;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Audio;
|
using Content.Shared.Audio;
|
||||||
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.DoAfter;
|
||||||
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.Fluids;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Tag;
|
||||||
|
using Content.Shared.Verbs;
|
||||||
using Content.Shared.Fluids.Components;
|
using Content.Shared.Fluids.Components;
|
||||||
using Robust.Shared.Collections;
|
using Robust.Shared.Collections;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Fluids.EntitySystems
|
namespace Content.Server.Fluids.EntitySystems;
|
||||||
|
|
||||||
|
public sealed class DrainSystem : SharedDrainSystem
|
||||||
{
|
{
|
||||||
public sealed class DrainSystem : EntitySystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
|
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
|
||||||
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!;
|
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!;
|
||||||
|
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||||
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||||
|
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||||
|
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||||
|
[Dependency] private readonly PuddleSystem _puddleSystem = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<DrainComponent, GetVerbsEvent<Verb>>(AddEmptyVerb);
|
||||||
|
SubscribeLocalEvent<DrainComponent, ExaminedEvent>(OnExamined);
|
||||||
|
SubscribeLocalEvent<DrainComponent, AfterInteractUsingEvent>(OnInteract);
|
||||||
|
SubscribeLocalEvent<DrainComponent, DrainDoAfterEvent>(OnDoAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddEmptyVerb(EntityUid uid, DrainComponent component, GetVerbsEvent<Verb> args)
|
||||||
|
{
|
||||||
|
if (!args.CanAccess || !args.CanInteract || args.Using == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!TryComp(args.Using, out SpillableComponent? spillable) ||
|
||||||
|
!TryComp(args.Target, out DrainComponent? drain))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Verb verb = new()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("drain-component-empty-verb-inhand", ("object", Name(args.Using.Value))),
|
||||||
|
Act = () =>
|
||||||
|
{
|
||||||
|
Empty(args.Using.Value, spillable, args.Target, drain);
|
||||||
|
},
|
||||||
|
Impact = LogImpact.Low,
|
||||||
|
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/eject.svg.192dpi.png"))
|
||||||
|
|
||||||
|
};
|
||||||
|
args.Verbs.Add(verb);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
_popupSystem.PopupEntity(
|
||||||
|
Loc.GetString("drain-component-empty-verb-using-is-empty-message", ("object", container)),
|
||||||
|
container);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to find the drain's solution
|
||||||
|
if (!_solutionSystem.TryGetSolution(target, DrainComponent.SolutionName, out var drainSolution))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to transfer as much solution as possible to the drain
|
||||||
|
|
||||||
|
var transferSolution = _solutionSystem.SplitSolution(container, containerSolution,
|
||||||
|
FixedPoint2.Min(containerSolution.Volume, drainSolution.AvailableVolume));
|
||||||
|
|
||||||
|
_solutionSystem.TryAddSolution(target, drainSolution, transferSolution);
|
||||||
|
|
||||||
|
_audioSystem.PlayPvs(drain.ManualDrainSound, target);
|
||||||
|
_ambientSoundSystem.SetAmbience(target, true);
|
||||||
|
|
||||||
|
// If drain is full, spill
|
||||||
|
|
||||||
|
if (drainSolution.MaxVolume == drainSolution.Volume)
|
||||||
|
{
|
||||||
|
_puddleSystem.TrySpillAt(Transform(target).Coordinates, containerSolution, out var puddle);
|
||||||
|
_popupSystem.PopupEntity(
|
||||||
|
Loc.GetString("drain-component-empty-verb-target-is-full-message", ("object", target)),
|
||||||
|
container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
@@ -31,6 +119,13 @@ namespace Content.Server.Fluids.EntitySystems
|
|||||||
}
|
}
|
||||||
drain.Accumulator -= drain.DrainFrequency;
|
drain.Accumulator -= drain.DrainFrequency;
|
||||||
|
|
||||||
|
// Disable ambient sound from emptying manually
|
||||||
|
if (!drain.AutoDrain)
|
||||||
|
{
|
||||||
|
_ambientSoundSystem.SetAmbience(drain.Owner, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!managerQuery.TryGetComponent(drain.Owner, out var manager))
|
if (!managerQuery.TryGetComponent(drain.Owner, out var manager))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -40,6 +135,12 @@ namespace Content.Server.Fluids.EntitySystems
|
|||||||
if (drainSolution is null)
|
if (drainSolution is null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (drainSolution.AvailableVolume <= 0)
|
||||||
|
{
|
||||||
|
_ambientSoundSystem.SetAmbience(drain.Owner, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove a bit from the buffer
|
// Remove a bit from the buffer
|
||||||
_solutionSystem.SplitSolution(drain.Owner, drainSolution, (drain.UnitsDestroyedPerSecond * drain.DrainFrequency));
|
_solutionSystem.SplitSolution(drain.Owner, drainSolution, (drain.UnitsDestroyedPerSecond * drain.DrainFrequency));
|
||||||
|
|
||||||
@@ -97,5 +198,68 @@ namespace Content.Server.Fluids.EntitySystems
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnExamined(EntityUid uid, DrainComponent component, ExaminedEvent args)
|
||||||
|
{
|
||||||
|
if (!args.IsInDetailsRange ||
|
||||||
|
!TryComp(uid, out SolutionContainerManagerComponent? solutionComp) ||
|
||||||
|
!_solutionSystem.TryGetSolution(uid, DrainComponent.SolutionName, out var drainSolution))
|
||||||
|
{ return; }
|
||||||
|
|
||||||
|
var text = drainSolution.AvailableVolume != 0 ?
|
||||||
|
Loc.GetString("drain-component-examine-volume", ("volume", drainSolution.AvailableVolume)) :
|
||||||
|
Loc.GetString("drain-component-examine-hint-full");
|
||||||
|
args.Message.AddMarkup($"\n\n{text}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInteract(EntityUid uid, DrainComponent component, InteractEvent args)
|
||||||
|
{
|
||||||
|
if (!args.CanReach || args.Target == null ||
|
||||||
|
!_tagSystem.HasTag(args.Used, DrainComponent.PlungerTag) ||
|
||||||
|
!_solutionSystem.TryGetSolution(args.Target.Value, DrainComponent.SolutionName, out var drainSolution))
|
||||||
|
{ return; }
|
||||||
|
|
||||||
|
if (drainSolution.AvailableVolume > 0)
|
||||||
|
{
|
||||||
|
_popupSystem.PopupEntity(Loc.GetString("drain-component-unclog-notapplicable", ("object", args.Target.Value)), args.Target.Value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_audioSystem.PlayPvs(component.PlungerSound, uid);
|
||||||
|
|
||||||
|
|
||||||
|
var doAfterArgs = new DoAfterArgs(args.User, component.UnclogDuration, new DrainDoAfterEvent(),uid, args.Used)
|
||||||
|
{
|
||||||
|
BreakOnTargetMove = true,
|
||||||
|
BreakOnUserMove = true,
|
||||||
|
BreakOnDamage = true,
|
||||||
|
BreakOnHandChange = true
|
||||||
|
};
|
||||||
|
|
||||||
|
_doAfterSystem.TryStartDoAfter(doAfterArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDoAfter(EntityUid uid, DrainComponent component, DoAfterEvent args)
|
||||||
|
{
|
||||||
|
if (args.Target == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_random.Prob(component.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))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_solutionSystem.RemoveAllSolution(args.Target.Value, drainSolution);
|
||||||
|
_audioSystem.PlayPvs(component.UnclogSound, args.Target.Value);
|
||||||
|
_popupSystem.PopupEntity(Loc.GetString("drain-component-unclog-success", ("object", args.Target.Value)), args.Target.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Content.Server.Fluids.Components;
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
using Content.Shared.DragDrop;
|
using Content.Shared.DragDrop;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace Content.Shared.Chemistry
|
|||||||
Color,
|
Color,
|
||||||
FillFraction,
|
FillFraction,
|
||||||
BaseOverride,
|
BaseOverride,
|
||||||
|
SolutionName
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SolutionContainerLayers : byte
|
public enum SolutionContainerLayers : byte
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
|
|||||||
// I feel like this is somewhat cursed, but its the only way I can think of without having to just send
|
// I feel like this is somewhat cursed, but its the only way I can think of without having to just send
|
||||||
// redundant data over the network and increasing DoAfter boilerplate.
|
// redundant data over the network and increasing DoAfter boilerplate.
|
||||||
var evType = typeof(DoAfterAttemptEvent<>).MakeGenericType(args.Event.GetType());
|
var evType = typeof(DoAfterAttemptEvent<>).MakeGenericType(args.Event.GetType());
|
||||||
doAfter.AttemptEvent = _factory.CreateInstance(evType, new object[] { doAfter, args.Event }, inject: false);
|
doAfter.AttemptEvent = _factory.CreateInstance(evType, new object[] { doAfter, args.Event });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.EventTarget != null)
|
if (args.EventTarget != null)
|
||||||
|
|||||||
@@ -1,15 +1,31 @@
|
|||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.Audio;
|
||||||
|
|
||||||
namespace Content.Shared.Fluids.Components;
|
namespace Content.Shared.Fluids.Components;
|
||||||
|
|
||||||
[RegisterComponent, NetworkedComponent]
|
/// <summary>
|
||||||
|
/// A Drain allows an entity to absorb liquid in a disposal goal. Drains can be filled manually (with the Empty verb)
|
||||||
|
/// or they can absorb puddles of liquid around them when AutoDrain is set to true.
|
||||||
|
/// When the entity also has a SolutionContainerManager attached with a solution named drainBuffer, this solution
|
||||||
|
/// gets filled until the drain is full.
|
||||||
|
/// When the drain is full, it can be unclogged using a plunger (i.e. an entity with a Plunger tag attached).
|
||||||
|
/// Later this can be refactored into a proper Plunger component if needed.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(SharedDrainSystem))]
|
||||||
public sealed class DrainComponent : Component
|
public sealed class DrainComponent : Component
|
||||||
{
|
{
|
||||||
public const string SolutionName = "drainBuffer";
|
public const string SolutionName = "drainBuffer";
|
||||||
|
public const string PlungerTag = "Plunger";
|
||||||
|
|
||||||
[DataField("accumulator")]
|
[DataField("accumulator")]
|
||||||
public float Accumulator = 0f;
|
public float Accumulator = 0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Does this drain automatically absorb surrouding puddles? Or is it a drain designed to empty
|
||||||
|
/// solutions in it manually?
|
||||||
|
/// </summary>
|
||||||
|
[DataField("autoDrain"), ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public bool AutoDrain = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How many units per second the drain can absorb from the surrounding puddles.
|
/// How many units per second the drain can absorb from the surrounding puddles.
|
||||||
/// Divided by puddles, so if there are 5 puddles this will take 1/5 from each puddle.
|
/// Divided by puddles, so if there are 5 puddles this will take 1/5 from each puddle.
|
||||||
@@ -28,7 +44,7 @@ public sealed class DrainComponent : Component
|
|||||||
/// How many (unobstructed) tiles away the drain will
|
/// How many (unobstructed) tiles away the drain will
|
||||||
/// drain puddles from.
|
/// drain puddles from.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("range")]
|
[DataField("range"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float Range = 2f;
|
public float Range = 2f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -37,4 +53,25 @@ public sealed class DrainComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("drainFrequency")]
|
[DataField("drainFrequency")]
|
||||||
public float DrainFrequency = 1f;
|
public float DrainFrequency = 1f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How much time it takes to unclog it with a plunger
|
||||||
|
/// </summary>
|
||||||
|
[DataField("unclogDuration"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float UnclogDuration = 1f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What's the probability of uncloging on each try
|
||||||
|
/// </summary>
|
||||||
|
[DataField("unclogProbability"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float UnclogProbability = 0.3f;
|
||||||
|
|
||||||
|
[DataField("manualDrainSound"), ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public SoundSpecifier ManualDrainSound = new SoundPathSpecifier("/Audio/Effects/Fluids/slosh.ogg");
|
||||||
|
|
||||||
|
[DataField("plungerSound"), ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public SoundSpecifier PlungerSound = new SoundPathSpecifier("/Audio/Items/Janitor/plunger.ogg");
|
||||||
|
|
||||||
|
[DataField("unclogSound"), ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public SoundSpecifier UnclogSound = new SoundPathSpecifier("/Audio/Effects/Fluids/glug.ogg");
|
||||||
}
|
}
|
||||||
|
|||||||
12
Content.Shared/Fluids/SharedDrainSystem.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Content.Shared.DoAfter;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Fluids;
|
||||||
|
|
||||||
|
public class SharedDrainSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class DrainDoAfterEvent : SimpleDoAfterEvent
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
blood1.ogg under CC-0 from https://freesound.org/people/kyles/sounds/453769/
|
|
||||||
blood2.ogg under CC-BY 3.0 from https://freesound.org/people/EminYILDIRIM/sounds/554284/
|
|
||||||
14
Resources/Audio/Effects/Fluids/attributions.yml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
- files: ["blood1.ogg"]
|
||||||
|
license: "CC0-1.0"
|
||||||
|
copyright: "Created by Kyles"
|
||||||
|
source: "https://freesound.org/people/kyles/sounds/453769/"
|
||||||
|
|
||||||
|
- files: ["blood2.ogg"]
|
||||||
|
license: "CC-BY-4.0"
|
||||||
|
copyright: "Created by EminYILDIRIM"
|
||||||
|
source: "https://freesound.org/people/EminYILDIRIM/sounds/554284/"
|
||||||
|
|
||||||
|
- files: ["glug.ogg"]
|
||||||
|
license: "CC0-1.0"
|
||||||
|
copyright: "Created by brittmosel"
|
||||||
|
source: "https://freesound.org/people/brittmosel/sounds/529300/"
|
||||||
BIN
Resources/Audio/Effects/Fluids/glug.ogg
Normal file
4
Resources/Audio/Items/Janitor/attributions.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
- files: ["plunger.ogg"]
|
||||||
|
license: "CC-BY-4.0"
|
||||||
|
copyright: "Created by tosha73"
|
||||||
|
source: "https://freesound.org/people/tosha73/sounds/540784/"
|
||||||
BIN
Resources/Audio/Items/Janitor/plunger.ogg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
drain-component-empty-verb-using-is-empty-message = { CAPITALIZE(THE($object)) } is empty!
|
||||||
|
drain-component-empty-verb-target-is-full-message = { CAPITALIZE(THE($object)) } is full!
|
||||||
|
drain-component-empty-verb-inhand = Empty {$object}
|
||||||
|
drain-component-examine-hint-full = [color="blue"]It is filled to the brim. Maybe a plunger can help?[/color]
|
||||||
|
drain-component-examine-volume = [color="blue"]Remaining space - {$volume}u.[/color]
|
||||||
|
drain-component-unclog-fail = { CAPITALIZE(THE($object)) } is still full.
|
||||||
|
drain-component-unclog-success = { CAPITALIZE(THE($object)) } unclogs.
|
||||||
|
drain-component-unclog-notapplicable = { CAPITALIZE(THE($object)) } isn't clogged.
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
sprite: Objects/Specific/Janitorial/janitorial.rsi
|
sprite: Objects/Specific/Janitorial/janitorial.rsi
|
||||||
state: cleaner
|
state: cleaner
|
||||||
product: CrateServiceJanitorialSupplies
|
product: CrateServiceJanitorialSupplies
|
||||||
cost: 500
|
cost: 560
|
||||||
category: Service
|
category: Service
|
||||||
group: market
|
group: market
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
amount: 2
|
amount: 2
|
||||||
- id: TrashBag
|
- id: TrashBag
|
||||||
amount: 2
|
amount: 2
|
||||||
|
- id: Plunger
|
||||||
|
amount: 2
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: CrateServiceReplacementLights
|
id: CrateServiceReplacementLights
|
||||||
|
|||||||
@@ -71,6 +71,8 @@
|
|||||||
amount: 2
|
amount: 2
|
||||||
- id: FlashlightLantern
|
- id: FlashlightLantern
|
||||||
amount: 2
|
amount: 2
|
||||||
|
- id: Plunger
|
||||||
|
amount: 2
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: ClosetLegalFilled
|
id: ClosetLegalFilled
|
||||||
|
|||||||
@@ -94,6 +94,7 @@
|
|||||||
- SheetSteel
|
- SheetSteel
|
||||||
- SheetPlastic
|
- SheetPlastic
|
||||||
- ResearchDisk
|
- ResearchDisk
|
||||||
|
- Plunger
|
||||||
chance: 0.6
|
chance: 0.6
|
||||||
offset: 0.0
|
offset: 0.0
|
||||||
|
|
||||||
|
|||||||
@@ -317,6 +317,26 @@
|
|||||||
path: /Audio/Effects/metalbreak.ogg
|
path: /Audio/Effects/metalbreak.ogg
|
||||||
- type: ItemMapper
|
- type: ItemMapper
|
||||||
mapLayers:
|
mapLayers:
|
||||||
|
cart_plunger:
|
||||||
|
whitelist:
|
||||||
|
tags:
|
||||||
|
- Plunger
|
||||||
|
cart_mop:
|
||||||
|
whitelist:
|
||||||
|
tags:
|
||||||
|
- Mop
|
||||||
|
cart_garbage:
|
||||||
|
whitelist:
|
||||||
|
tags:
|
||||||
|
- TrashBag
|
||||||
|
cart_replacer:
|
||||||
|
whitelist:
|
||||||
|
components:
|
||||||
|
- LightReplacer
|
||||||
|
cart_spray:
|
||||||
|
whitelist:
|
||||||
|
tags:
|
||||||
|
- Spray
|
||||||
cart_sign1: # this is like stack of floor signs
|
cart_sign1: # this is like stack of floor signs
|
||||||
minCount: 1
|
minCount: 1
|
||||||
whitelist:
|
whitelist:
|
||||||
@@ -337,26 +357,10 @@
|
|||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- WetFloorSign
|
- WetFloorSign
|
||||||
cart_spray:
|
|
||||||
whitelist:
|
|
||||||
tags:
|
|
||||||
- Spray
|
|
||||||
cart_garbage:
|
|
||||||
whitelist:
|
|
||||||
tags:
|
|
||||||
- TrashBag
|
|
||||||
cart_replacer:
|
|
||||||
whitelist:
|
|
||||||
components:
|
|
||||||
- LightReplacer
|
|
||||||
cart_bucket:
|
cart_bucket:
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
- Bucket
|
- Bucket
|
||||||
cart_mop:
|
|
||||||
whitelist:
|
|
||||||
tags:
|
|
||||||
- Mop
|
|
||||||
sprite: Objects/Specific/Janitorial/janitorial_cart.rsi
|
sprite: Objects/Specific/Janitorial/janitorial_cart.rsi
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
- type: SolutionContainerVisuals
|
- type: SolutionContainerVisuals
|
||||||
@@ -392,7 +396,11 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
drawdepth: FloorObjects
|
drawdepth: FloorObjects
|
||||||
sprite: Objects/Specific/Janitorial/drain.rsi
|
sprite: Objects/Specific/Janitorial/drain.rsi
|
||||||
state: icon
|
layers:
|
||||||
|
- state: icon
|
||||||
|
- map: [ "enum.SolutionContainerLayers.Fill" ]
|
||||||
|
state: fill-1
|
||||||
|
visible: false
|
||||||
- type: InteractionOutline
|
- type: InteractionOutline
|
||||||
- type: Clickable
|
- type: Clickable
|
||||||
- type: Transform
|
- type: Transform
|
||||||
@@ -400,13 +408,18 @@
|
|||||||
- type: Physics
|
- type: Physics
|
||||||
bodyType: Static
|
bodyType: Static
|
||||||
canCollide: false
|
canCollide: false
|
||||||
- type: Drain
|
|
||||||
- type: AmbientSound
|
- type: AmbientSound
|
||||||
enabled: false
|
enabled: false
|
||||||
volume: -8
|
volume: -8
|
||||||
range: 8
|
range: 8
|
||||||
sound:
|
sound:
|
||||||
path: /Audio/Ambience/Objects/drain.ogg
|
path: /Audio/Ambience/Objects/drain.ogg
|
||||||
|
- type: Drain
|
||||||
|
- type: Appearance
|
||||||
|
- type: SolutionContainerVisuals
|
||||||
|
maxFillLevels: 1
|
||||||
|
fillBaseName: fill-
|
||||||
|
solutionName: drainBuffer
|
||||||
- type: SolutionContainerManager
|
- type: SolutionContainerManager
|
||||||
solutions:
|
solutions:
|
||||||
drainBuffer:
|
drainBuffer:
|
||||||
@@ -433,6 +446,24 @@
|
|||||||
- !type:PlaySoundBehavior
|
- !type:PlaySoundBehavior
|
||||||
sound:
|
sound:
|
||||||
path: /Audio/Effects/metalbreak.ogg
|
path: /Audio/Effects/metalbreak.ogg
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: plunger
|
||||||
|
id: Plunger
|
||||||
|
parent: BaseItem
|
||||||
|
description: A plunger with a red plastic suction-cup and a wooden handle. Used to unclog drains.
|
||||||
|
components:
|
||||||
|
- type: Tag
|
||||||
|
tags:
|
||||||
|
- Plunger
|
||||||
|
- type: Sprite
|
||||||
|
netsync: false
|
||||||
|
sprite: Objects/Specific/Janitorial/janitorial.rsi
|
||||||
|
state: plunger
|
||||||
|
- type: Item
|
||||||
|
sprite: Objects/Specific/Janitorial/janitorial.rsi
|
||||||
|
heldPrefix: plunger
|
||||||
|
- type: ItemCooldown
|
||||||
- type: GuideHelp
|
- type: GuideHelp
|
||||||
guides:
|
guides:
|
||||||
- Janitorial
|
- Janitorial
|
||||||
|
|||||||
@@ -10,9 +10,21 @@
|
|||||||
- type: InteractionOutline
|
- type: InteractionOutline
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Furniture/sink.rsi
|
sprite: Structures/Furniture/sink.rsi
|
||||||
state: sink_stem
|
layers:
|
||||||
|
- state: sink_stem
|
||||||
|
- map: [ "enum.SolutionContainerLayers.Fill" ]
|
||||||
|
state: sink-fill-1
|
||||||
|
visible: false
|
||||||
|
netsync: false
|
||||||
|
- type: Appearance
|
||||||
|
- type: SolutionContainerVisuals
|
||||||
|
maxFillLevels: 1
|
||||||
|
fillBaseName: sink-fill-
|
||||||
|
solutionName: drainBuffer
|
||||||
- type: SolutionContainerManager
|
- type: SolutionContainerManager
|
||||||
solutions:
|
solutions:
|
||||||
|
drainBuffer:
|
||||||
|
maxVol: 100
|
||||||
tank:
|
tank:
|
||||||
maxVol: 500
|
maxVol: 500
|
||||||
- type: SolutionRegeneration
|
- type: SolutionRegeneration
|
||||||
@@ -24,6 +36,8 @@
|
|||||||
- type: DrainableSolution
|
- type: DrainableSolution
|
||||||
solution: tank
|
solution: tank
|
||||||
- type: ReagentTank
|
- type: ReagentTank
|
||||||
|
- type: Drain
|
||||||
|
autoDrain: false
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: Inorganic
|
damageContainer: Inorganic
|
||||||
damageModifierSet: Metallic
|
damageModifierSet: Metallic
|
||||||
@@ -44,6 +58,13 @@
|
|||||||
- !type:PlaySoundBehavior
|
- !type:PlaySoundBehavior
|
||||||
sound:
|
sound:
|
||||||
path: /Audio/Effects/metalbreak.ogg
|
path: /Audio/Effects/metalbreak.ogg
|
||||||
|
- type: AmbientSound
|
||||||
|
enabled: false
|
||||||
|
volume: -8
|
||||||
|
range: 8
|
||||||
|
sound:
|
||||||
|
path: /Audio/Ambience/Objects/drain.ogg
|
||||||
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: sink
|
name: sink
|
||||||
@@ -53,6 +74,8 @@
|
|||||||
components:
|
components:
|
||||||
- type: SolutionContainerManager
|
- type: SolutionContainerManager
|
||||||
solutions:
|
solutions:
|
||||||
|
drainBuffer:
|
||||||
|
maxVol: 200
|
||||||
tank:
|
tank:
|
||||||
reagents:
|
reagents:
|
||||||
- ReagentId: Water
|
- ReagentId: Water
|
||||||
@@ -65,7 +88,15 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Furniture/sink.rsi
|
sprite: Structures/Furniture/sink.rsi
|
||||||
state: sink_wide
|
layers:
|
||||||
|
- state: sink_wide
|
||||||
|
- map: [ "enum.SolutionContainerLayers.Fill" ]
|
||||||
|
state: sink_wide-fill-1
|
||||||
|
visible: false
|
||||||
|
- type: SolutionContainerVisuals
|
||||||
|
maxFillLevels: 1
|
||||||
|
fillBaseName: sink_wide-fill-
|
||||||
|
solutionName: drainBuffer
|
||||||
|
|
||||||
#Stemless Sink
|
#Stemless Sink
|
||||||
|
|
||||||
@@ -76,7 +107,11 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Furniture/sink.rsi
|
sprite: Structures/Furniture/sink.rsi
|
||||||
state: sink
|
layers:
|
||||||
|
- state: sink
|
||||||
|
- map: [ "enum.SolutionContainerLayers.Fill" ]
|
||||||
|
state: sink-fill-1
|
||||||
|
visible: false
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: sink
|
name: sink
|
||||||
@@ -86,6 +121,8 @@
|
|||||||
components:
|
components:
|
||||||
- type: SolutionContainerManager
|
- type: SolutionContainerManager
|
||||||
solutions:
|
solutions:
|
||||||
|
drainBuffer:
|
||||||
|
maxVol: 100
|
||||||
tank:
|
tank:
|
||||||
reagents:
|
reagents:
|
||||||
- ReagentId: Water
|
- ReagentId: Water
|
||||||
|
|||||||
@@ -22,6 +22,8 @@
|
|||||||
stash: !type:ContainerSlot {}
|
stash: !type:ContainerSlot {}
|
||||||
- type: SolutionContainerManager
|
- type: SolutionContainerManager
|
||||||
solutions:
|
solutions:
|
||||||
|
drainBuffer:
|
||||||
|
maxVol: 500
|
||||||
toilet:
|
toilet:
|
||||||
maxVol: 250
|
maxVol: 250
|
||||||
- type: Transform
|
- type: Transform
|
||||||
@@ -32,6 +34,12 @@
|
|||||||
graph: Toilet
|
graph: Toilet
|
||||||
node: toilet
|
node: toilet
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
|
- type: Drain
|
||||||
|
autoDrain: false
|
||||||
|
- type: SolutionContainerVisuals
|
||||||
|
maxFillLevels: 1
|
||||||
|
fillBaseName: fill-
|
||||||
|
solutionName: drainBuffer
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 25
|
price: 25
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
result: Bucket
|
result: Bucket
|
||||||
completetime: 2
|
completetime: 2
|
||||||
materials:
|
materials:
|
||||||
Plastic: 100
|
Steel: 100
|
||||||
|
|
||||||
- type: latheRecipe
|
- type: latheRecipe
|
||||||
id: WetFloorSign
|
id: WetFloorSign
|
||||||
@@ -81,6 +81,14 @@
|
|||||||
Steel: 100
|
Steel: 100
|
||||||
Glass: 100
|
Glass: 100
|
||||||
|
|
||||||
|
- type: latheRecipe
|
||||||
|
id: Plunger
|
||||||
|
result: Plunger
|
||||||
|
completetime: 2
|
||||||
|
materials:
|
||||||
|
Plastic: 50
|
||||||
|
Wood: 200
|
||||||
|
|
||||||
- type: latheRecipe
|
- type: latheRecipe
|
||||||
id: WeaponSprayNozzle
|
id: WeaponSprayNozzle
|
||||||
result: WeaponSprayNozzle
|
result: WeaponSprayNozzle
|
||||||
|
|||||||
@@ -631,9 +631,13 @@
|
|||||||
- type: Tag
|
- type: Tag
|
||||||
id: Plastic
|
id: Plastic
|
||||||
|
|
||||||
|
- type: Tag
|
||||||
|
id: Plunger
|
||||||
|
|
||||||
- type: Tag
|
- type: Tag
|
||||||
id: PlushieGhost
|
id: PlushieGhost
|
||||||
|
|
||||||
|
|
||||||
- type: Tag
|
- type: Tag
|
||||||
id: Powerdrill
|
id: Powerdrill
|
||||||
|
|
||||||
|
|||||||
|
After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 3.3 KiB |
@@ -5,11 +5,15 @@
|
|||||||
"y":32
|
"y":32
|
||||||
},
|
},
|
||||||
"license":"CC-BY-SA-3.0",
|
"license":"CC-BY-SA-3.0",
|
||||||
"copyright":"Created by EmoGarbage",
|
"copyright":"Created by EmoGarbage, fill-1 created by Topy for SS14",
|
||||||
"states":[
|
"states":[
|
||||||
{
|
{
|
||||||
"name":"icon",
|
"name":"icon",
|
||||||
"directions": 4
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fill-1",
|
||||||
|
"directions": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mopbucket_water-3"
|
"name": "mopbucket_water-3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "plunger"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "inhand-left",
|
"name": "inhand-left",
|
||||||
@@ -36,6 +39,14 @@
|
|||||||
{
|
{
|
||||||
"name": "equipped-BELT",
|
"name": "equipped-BELT",
|
||||||
"directions": 4
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "plunger-inhand-left",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "plunger-inhand-right",
|
||||||
|
"directions": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
@@ -46,6 +46,10 @@
|
|||||||
{
|
{
|
||||||
"name": "cart_spray",
|
"name": "cart_spray",
|
||||||
"directions": 4
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cart_plunger",
|
||||||
|
"directions": 4
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "cart_water-1",
|
"name": "cart_water-1",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"y": 32
|
"y": 32
|
||||||
},
|
},
|
||||||
"license": "CC-BY-SA-3.0",
|
"license": "CC-BY-SA-3.0",
|
||||||
"copyright": "Taken from tgstation https://github.com/tgstation/tgstation/commit/f01afc7edd39b28dd718407d5bbfca3a5dfe995f#diff-378d1b8f0f0a73185e7c82e4ccfdb65102561992a7abb306090ce851f8419780",
|
"copyright": "Taken from tgstation https://github.com/tgstation/tgstation/commit/f01afc7edd39b28dd718407d5bbfca3a5dfe995f#diff-378d1b8f0f0a73185e7c82e4ccfdb65102561992a7abb306090ce851f8419780, sink-fill-1 and sink_wide-fill-1 made by Topy for SS14",
|
||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "sink",
|
"name": "sink",
|
||||||
@@ -18,6 +18,14 @@
|
|||||||
{
|
{
|
||||||
"name": "sink_stem",
|
"name": "sink_stem",
|
||||||
"directions": 4
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sink-fill-1",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sink_wide-fill-1",
|
||||||
|
"directions": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
Resources/Textures/Structures/Furniture/sink.rsi/sink-fill-1.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |