diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index 0ce540980a..2a51408f8b 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -63,6 +63,7 @@ namespace Content.Client.Entry "AtmosFixMarker", "CablePlacer", "Drink", + "Drain", "Food", "DeployableBarrier", "MagicMirror", diff --git a/Content.Server/Fluids/Components/DrainComponent.cs b/Content.Server/Fluids/Components/DrainComponent.cs new file mode 100644 index 0000000000..174a820381 --- /dev/null +++ b/Content.Server/Fluids/Components/DrainComponent.cs @@ -0,0 +1,39 @@ +namespace Content.Server.Fluids.Components +{ + [RegisterComponent] + public sealed class DrainComponent : Component + { + public const string SolutionName = "drainBuffer"; + + [DataField("accumulator")] + public float Accumulator = 0f; + + /// + /// 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. + /// This will stay fixed to 1 second no matter what DrainFrequency is. + /// + [DataField("unitsPerSecond")] + public float UnitsPerSecond = 6f; + + /// + /// How many units are ejected from the buffer per second. + /// + [DataField("unitsDestroyedPerSecond")] + public float UnitsDestroyedPerSecond = 1f; + + /// + /// How many (unobstructed) tiles away the drain will + /// drain puddles from. + /// + [DataField("range")] + public float Range = 2f; + + /// + /// How often in seconds the drain checks for puddles around it. + /// If the EntityQuery seems a bit unperformant this can be increased. + /// + [DataField("drainFrequency")] + public float DrainFrequency = 1f; + } +} diff --git a/Content.Server/Fluids/Components/EvaporationComponent.cs b/Content.Server/Fluids/Components/EvaporationComponent.cs index 94f9832586..8aaaf122c0 100644 --- a/Content.Server/Fluids/Components/EvaporationComponent.cs +++ b/Content.Server/Fluids/Components/EvaporationComponent.cs @@ -1,9 +1,5 @@ using Content.Server.Fluids.EntitySystems; -using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; -using Robust.Shared.Analyzers; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization.Manager.Attributes; namespace Content.Server.Fluids.Components { diff --git a/Content.Server/Fluids/EntitySystems/DrainSystem.cs b/Content.Server/Fluids/EntitySystems/DrainSystem.cs new file mode 100644 index 0000000000..072a2b0e66 --- /dev/null +++ b/Content.Server/Fluids/EntitySystems/DrainSystem.cs @@ -0,0 +1,89 @@ +using Content.Server.Fluids.Components; +using Content.Server.Chemistry.EntitySystems; +using Content.Shared.FixedPoint; +using Content.Shared.Interaction; +using Content.Shared.Audio; + +namespace Content.Server.Fluids.EntitySystems +{ + public sealed class DrainSystem : EntitySystem + { + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SolutionContainerSystem _solutionSystem = default!; + [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; + + + public override void Update(float frameTime) + { + base.Update(frameTime); + foreach (var drain in EntityQuery()) + { + drain.Accumulator += frameTime; + if (drain.Accumulator < drain.DrainFrequency) + { + continue; + } + drain.Accumulator -= drain.DrainFrequency; + + /// Best to do this one every second rather than once every tick... + _solutionSystem.TryGetSolution(drain.Owner, DrainComponent.SolutionName, out var drainSolution); + + if (drainSolution is null) + return; + + /// Remove a bit from the buffer + _solutionSystem.SplitSolution(drain.Owner, drainSolution, (drain.UnitsDestroyedPerSecond * drain.DrainFrequency)); + + /// This will ensure that UnitsPerSecond is per second... + var amount = drain.UnitsPerSecond * drain.DrainFrequency; + var xform = Transform(drain.Owner); + List puddles = new(); + + foreach (var entity in _lookup.GetEntitiesInRange(xform.MapPosition, drain.Range)) + { + /// No InRangeUnobstructed because there's no collision group that fits right now + /// and these are placed by mappers and not buildable/movable so shouldnt really be a problem... + if (TryComp(entity, out var puddleComp)) + { + puddles.Add(puddleComp); + } + } + + if (puddles.Count == 0) + { + _ambientSoundSystem.SetAmbience(drain.Owner, false); + continue; + } + + _ambientSoundSystem.SetAmbience(drain.Owner, true); + + amount /= puddles.Count; + + foreach (var puddle in puddles) + { + /// Queue the solution deletion if it's empty. EvaporationSystem might also do this + /// but queuedelete should be pretty safe. + if (!_solutionSystem.TryGetSolution(puddle.Owner, puddle.SolutionName, out var puddleSolution)) + { + EntityManager.QueueDeleteEntity(puddle.Owner); + continue; + } + + /// Removes the lowest of: + /// 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.Owner, puddleSolution, + FixedPoint2.Min(FixedPoint2.New(amount), puddleSolution.CurrentVolume, drainSolution.AvailableVolume)); + + _solutionSystem.TryAddSolution(drain.Owner, drainSolution, transferSolution); + + if (puddleSolution.CurrentVolume <= 0) + { + EntityManager.QueueDeleteEntity(puddle.Owner); + } + } + } + } + } +} diff --git a/Resources/Audio/Ambience/Objects/drain.ogg b/Resources/Audio/Ambience/Objects/drain.ogg new file mode 100644 index 0000000000..bc27cd181b Binary files /dev/null and b/Resources/Audio/Ambience/Objects/drain.ogg differ diff --git a/Resources/Audio/Ambience/Objects/license.txt b/Resources/Audio/Ambience/Objects/license.txt index 36750672e0..f315c60331 100644 --- a/Resources/Audio/Ambience/Objects/license.txt +++ b/Resources/Audio/Ambience/Objects/license.txt @@ -4,3 +4,4 @@ gas_hiss - https://freesound.org/people/geodylabs/sounds/122803/ - CC-BY-3.0 gas_vent - https://freesound.org/people/kyles/sounds/453642/ - CC0-1.0 flowing_water_open - https://freesound.org/people/sterferny/sounds/382322/ - CC0-1.0 server_fans - https://freesound.org/people/DeVern/sounds/610761/ - CC-BY-3.0 +drain.ogg - https://freesound.org/people/PhreaKsAccount/sounds/46266/ - CC-BY-3.0 (by PhreaKsAccount) diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index ff9f09dabf..8971b4382b 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -18,7 +18,7 @@ maxVol: 50 - type: Tag tags: - - DroneUsable #No bucket because it holds chems, they can drag the thing around instead + - DroneUsable #No bucket because it holds chems, they can drag the cart or use a drain - Mop - type: entity @@ -42,6 +42,7 @@ reagents: - ReagentId: Water Quantity: 250 # half-full at roundstart to leave room for puddles + - type: Spillable - type: DrainableSolution solution: bucket - type: RefillableSolution @@ -128,6 +129,7 @@ mask: - VaultImpassable mass: 100 + - type: Spillable - type: SolutionContainerManager solutions: bucket: @@ -203,3 +205,37 @@ maxFillLevels: 3 fillBaseName: cart_water_ changeColor: false + + +- type: entity + id: FloorDrain + name: drain + description: Drains puddles around it. Useful for dumping mop buckets or keeping certain rooms clean. + placement: + mode: SnapgridCenter + components: + - type: Sprite + netsync: false + drawdepth: FloorObjects + sprite: Objects/Specific/Janitorial/drain.rsi + state: icon + - type: InteractionOutline + - type: Clickable + - type: Transform + anchored: true + - type: Physics + bodyType: Static + - type: Drain + - type: AmbientSound + enabled: false + volume: -8 + range: 8 + sound: + path: /Audio/Ambience/Objects/drain.ogg + - type: SolutionContainerManager + solutions: + drainBuffer: + maxVol: 500 + - type: DrainableSolution + solution: drainBuffer + diff --git a/Resources/Textures/Objects/Specific/Janitorial/drain.rsi/icon.png b/Resources/Textures/Objects/Specific/Janitorial/drain.rsi/icon.png new file mode 100644 index 0000000000..de9a244bdf Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/drain.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Specific/Janitorial/drain.rsi/meta.json b/Resources/Textures/Objects/Specific/Janitorial/drain.rsi/meta.json new file mode 100644 index 0000000000..7835a4c7bf --- /dev/null +++ b/Resources/Textures/Objects/Specific/Janitorial/drain.rsi/meta.json @@ -0,0 +1,15 @@ +{ + "version":1, + "size":{ + "x":32, + "y":32 + }, + "license":"CC-BY-SA-3.0", + "copyright":"Created by EmoGarbage", + "states":[ + { + "name":"icon", + "directions": 4 + } + ] +}