From 0670b56205028843c72d63efe445cfa303a5a7ce Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Thu, 26 Oct 2023 22:52:11 -0400 Subject: [PATCH] A return to foam (foam rework) (#20831) --- .../Visualizers/FoamVisualizerSystem.cs | 23 +- .../Visualizers/FoamVisualsComponent.cs | 12 +- .../Body/Systems/InternalsSystem.cs | 8 + .../Chemistry/Components/SmokeComponent.cs | 26 -- .../ReactionEffects/AreaReactionEffect.cs | 14 +- .../Fluids/EntitySystems/SmokeSystem.cs | 263 ++++++++++++------ .../Components/VentClogRuleComponent.cs | 28 +- .../StationEvents/Events/VentClogRule.cs | 7 +- .../Effects/Systems/FoamArtifactSystem.cs | 6 +- .../Components/SmokeAffectedComponent.cs | 24 ++ .../Chemistry/Components/SmokeComponent.cs | 34 +++ 11 files changed, 294 insertions(+), 151 deletions(-) delete mode 100644 Content.Server/Chemistry/Components/SmokeComponent.cs create mode 100644 Content.Shared/Chemistry/Components/SmokeAffectedComponent.cs create mode 100644 Content.Shared/Chemistry/Components/SmokeComponent.cs diff --git a/Content.Client/Chemistry/Visualizers/FoamVisualizerSystem.cs b/Content.Client/Chemistry/Visualizers/FoamVisualizerSystem.cs index ec582ee094..2ee88956ff 100644 --- a/Content.Client/Chemistry/Visualizers/FoamVisualizerSystem.cs +++ b/Content.Client/Chemistry/Visualizers/FoamVisualizerSystem.cs @@ -1,8 +1,6 @@ -using Content.Shared.Smoking; -using Robust.Shared.Spawners; +using Content.Shared.Chemistry.Components; using Robust.Client.Animations; using Robust.Client.GameObjects; -using Robust.Shared.Network; using Robust.Shared.Timing; namespace Content.Client.Chemistry.Visualizers; @@ -18,6 +16,7 @@ public sealed class FoamVisualizerSystem : VisualizerSystem(OnComponentInit); + SubscribeLocalEvent(OnAnimationComplete); } public override void Update(float frameTime) @@ -27,11 +26,11 @@ public sealed class FoamVisualizerSystem : VisualizerSystem(); + var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp, out var despawn)) + while (query.MoveNext(out var uid, out var comp, out var smoke)) { - if (despawn.Lifetime > 1f) + if (_timing.CurTime < comp.StartTime + TimeSpan.FromSeconds(smoke.Duration) - TimeSpan.FromSeconds(comp.AnimationTime)) continue; // Despawn animation. @@ -48,6 +47,7 @@ public sealed class FoamVisualizerSystem : VisualizerSystem private void OnComponentInit(EntityUid uid, FoamVisualsComponent comp, ComponentInit args) { + comp.StartTime = _timing.CurTime; comp.Animation = new Animation { Length = TimeSpan.FromSeconds(comp.AnimationTime), @@ -58,12 +58,21 @@ public sealed class FoamVisualizerSystem : VisualizerSystem(uid, out var sprite)) + sprite.Visible = false; + } } public enum FoamVisualLayers : byte diff --git a/Content.Client/Chemistry/Visualizers/FoamVisualsComponent.cs b/Content.Client/Chemistry/Visualizers/FoamVisualsComponent.cs index b09c74aa0f..8199efa42e 100644 --- a/Content.Client/Chemistry/Visualizers/FoamVisualsComponent.cs +++ b/Content.Client/Chemistry/Visualizers/FoamVisualsComponent.cs @@ -1,5 +1,6 @@ using Robust.Client.Animations; using Robust.Client.Graphics; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Client.Chemistry.Visualizers; @@ -15,18 +16,21 @@ public sealed partial class FoamVisualsComponent : Component /// public const string AnimationKey = "foamdissolve_animation"; + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan StartTime; + /// /// How long the foam visually dissolves for. /// - [DataField("animationTime")] - public float AnimationTime = 0.6f; + [DataField] + public float AnimationTime = 0.5f; /// /// The state of the entities base sprite RSI that is displayed when the foam dissolves. /// Cannot use because it does not have and I am not making an engine PR at this time. /// - [DataField("animationState")] - public string State = "foam-dissolve"; + [DataField] + public string AnimationState = "foam-dissolve"; /// /// The animation used while the foam dissolves. diff --git a/Content.Server/Body/Systems/InternalsSystem.cs b/Content.Server/Body/Systems/InternalsSystem.cs index ec4faa345c..17a6544976 100644 --- a/Content.Server/Body/Systems/InternalsSystem.cs +++ b/Content.Server/Body/Systems/InternalsSystem.cs @@ -197,6 +197,14 @@ public sealed class InternalsSystem : EntitySystem return true; } + public bool AreInternalsWorking(EntityUid uid, InternalsComponent? component = null) + { + if (!Resolve(uid, ref component, false)) + return false; + + return AreInternalsWorking(component); + } + public bool AreInternalsWorking(InternalsComponent component) { return TryComp(component.BreathToolEntity, out BreathToolComponent? breathTool) && diff --git a/Content.Server/Chemistry/Components/SmokeComponent.cs b/Content.Server/Chemistry/Components/SmokeComponent.cs deleted file mode 100644 index 133ad41f13..0000000000 --- a/Content.Server/Chemistry/Components/SmokeComponent.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Content.Shared.Fluids.Components; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; - -namespace Content.Server.Chemistry.Components; - -/// -/// Stores solution on an anchored entity that has touch and ingestion reactions -/// to entities that collide with it. Similar to -/// -[RegisterComponent] -public sealed partial class SmokeComponent : Component -{ - public const string SolutionName = "solutionArea"; - - [DataField("nextReact", customTypeSerializer:typeof(TimeOffsetSerializer))] - public TimeSpan NextReact = TimeSpan.Zero; - - [DataField("spreadAmount")] - public int SpreadAmount = 0; - - /// - /// Have we reacted with our tile yet? - /// - [DataField("reactedTile")] - public bool ReactedTile = false; -} diff --git a/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs index e6eaab8a59..291a654422 100644 --- a/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs +++ b/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs @@ -1,4 +1,3 @@ -using Content.Server.Chemistry.Components; using Content.Server.Fluids.EntitySystems; using Content.Shared.Audio; using Content.Shared.Chemistry.EntitySystems; @@ -10,7 +9,6 @@ using Content.Shared.Maps; using JetBrains.Annotations; using Robust.Shared.Audio; using Robust.Shared.Map; -using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -71,18 +69,10 @@ namespace Content.Server.Chemistry.ReactionEffects var coords = grid.MapToGrid(transform.MapPosition); var ent = args.EntityManager.SpawnEntity(_prototypeId, coords.SnapToGrid()); - if (!args.EntityManager.TryGetComponent(ent, out var smokeComponent)) - { - Logger.Error("Couldn't get AreaEffectComponent from " + _prototypeId); - args.EntityManager.QueueDeleteEntity(ent); - return; - } - var smoke = args.EntityManager.System(); - smokeComponent.SpreadAmount = spreadAmount; - smoke.Start(ent, smokeComponent, splitSolution, _duration); + smoke.StartSmoke(ent, splitSolution, _duration, spreadAmount); - SoundSystem.Play(_sound.GetSound(), Filter.Pvs(args.SolutionEntity), args.SolutionEntity, AudioHelpers.WithVariation(0.125f)); + args.EntityManager.System().PlayPvs(_sound, args.SolutionEntity, AudioHelpers.WithVariation(0.125f)); } } } diff --git a/Content.Server/Fluids/EntitySystems/SmokeSystem.cs b/Content.Server/Fluids/EntitySystems/SmokeSystem.cs index f7732fec62..5459dacf0b 100644 --- a/Content.Server/Fluids/EntitySystems/SmokeSystem.cs +++ b/Content.Server/Fluids/EntitySystems/SmokeSystem.cs @@ -1,14 +1,23 @@ using System.Linq; -using Content.Server.Chemistry.Components; +using Content.Server.Administration.Logs; +using Content.Server.Body.Components; +using Content.Server.Body.Systems; using Content.Server.Chemistry.ReactionEffects; using Content.Server.Spreader; +using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reaction; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Database; using Content.Shared.FixedPoint; using Content.Shared.Smoking; using Robust.Server.GameObjects; using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Events; +using Robust.Shared.Physics.Systems; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -22,70 +31,140 @@ namespace Content.Server.Fluids.EntitySystems; public sealed class SmokeSystem : EntitySystem { // If I could do it all again this could probably use a lot more of puddles. + [Dependency] private readonly IAdminLogManager _logger = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly AppearanceSystem _appearance = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly BloodstreamSystem _blood = default!; + [Dependency] private readonly InternalsSystem _internals = default!; + [Dependency] private readonly ReactiveSystem _reactive = default!; + [Dependency] private readonly SharedBroadphaseSystem _broadphase = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SolutionContainerSystem _solutionSystem = default!; - [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly TransformSystem _transform = default!; + + private EntityQuery _smokeQuery; + private EntityQuery _smokeAffectedQuery; /// public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnSmokeUnpaused); + + _smokeQuery = GetEntityQuery(); + _smokeAffectedQuery = GetEntityQuery(); + + SubscribeLocalEvent(OnStartCollide); + SubscribeLocalEvent(OnEndCollide); SubscribeLocalEvent(OnReactionAttempt); SubscribeLocalEvent(OnSmokeSpread); + SubscribeLocalEvent(OnAffectedUnpaused); + } + + /// + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + var curTime = _timing.CurTime; + while (query.MoveNext(out var uid, out var smoke)) + { + if (curTime < smoke.NextSecond) + continue; + + smoke.NextSecond += TimeSpan.FromSeconds(1); + SmokeReact(uid, smoke.SmokeEntity); + } + } + + private void OnStartCollide(EntityUid uid, SmokeComponent component, ref StartCollideEvent args) + { + if (_smokeAffectedQuery.HasComponent(args.OtherEntity)) + return; + + var smokeAffected = AddComp(args.OtherEntity); + smokeAffected.SmokeEntity = uid; + smokeAffected.NextSecond = _timing.CurTime + TimeSpan.FromSeconds(1); + } + + private void OnEndCollide(EntityUid uid, SmokeComponent component, ref EndCollideEvent args) + { + // if we are already in smoke, make sure the thing we are exiting is the current smoke we are in. + if (_smokeAffectedQuery.TryGetComponent(args.OtherEntity, out var smokeAffectedComponent)) + { + if (smokeAffectedComponent.SmokeEntity != uid) + return; + } + + var exists = Exists(uid); + + if (!TryComp(args.OtherEntity, out var body)) + return; + + foreach (var ent in _physics.GetContactingEntities(args.OtherEntity, body)) + { + if (exists && ent == uid) + continue; + + if (!_smokeQuery.HasComponent(ent)) + continue; + + smokeAffectedComponent ??= EnsureComp(args.OtherEntity); + smokeAffectedComponent.SmokeEntity = ent; + return; // exit the function so we don't remove the component. + } + + if (smokeAffectedComponent != null) + RemComp(args.OtherEntity, smokeAffectedComponent); + } + + private void OnAffectedUnpaused(EntityUid uid, SmokeAffectedComponent component, ref EntityUnpausedEvent args) + { + component.NextSecond += args.PausedTime; } private void OnSmokeSpread(EntityUid uid, SmokeComponent component, ref SpreadNeighborsEvent args) { - if (component.SpreadAmount == 0 - || !_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution)) + if (component.SpreadAmount == 0 || !_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution)) { RemCompDeferred(uid); return; } - var prototype = MetaData(uid).EntityPrototype; - - if (prototype == null) + if (Prototype(uid) is not { } prototype) { RemCompDeferred(uid); return; } + if (!args.NeighborFreeTiles.Any()) + return; + TryComp(uid, out var timer); - _appearance.TryGetData(uid, SmokeVisuals.Color, out var color); // wtf is the logic behind any of this. - var smokePerSpread = 1 + component.SpreadAmount / Math.Max(1, args.NeighborFreeTiles.Count); + var smokePerSpread = component.SpreadAmount / Math.Max(1, args.NeighborFreeTiles.Count); foreach (var neighbor in args.NeighborFreeTiles) { var coords = neighbor.Grid.GridTileToLocal(neighbor.Tile); var ent = Spawn(prototype.ID, coords); - var neighborSmoke = EnsureComp(ent); - neighborSmoke.SpreadAmount = Math.Max(0, smokePerSpread - 2); // why - 2? who the fuck knows. - component.SpreadAmount--; - args.Updates--; - - // Listen this is the old behaviour iunno - Start(ent, neighborSmoke, solution.Clone(), timer?.Lifetime ?? 10f); + var spreadAmount = Math.Max(0, smokePerSpread); + component.SpreadAmount -= args.NeighborFreeTiles.Count(); - if (color != null) - _appearance.SetData(ent, SmokeVisuals.Color, color); + StartSmoke(ent, solution.Clone(), timer?.Lifetime ?? component.Duration, spreadAmount); if (component.SpreadAmount == 0) { RemCompDeferred(uid); break; } - - if (args.Updates <= 0) - break; } + args.Updates--; + if (args.NeighborFreeTiles.Count > 0 || args.Neighbors.Count == 0 || component.SpreadAmount < 1) return; @@ -100,7 +179,6 @@ public sealed class SmokeSystem : EntitySystem continue; smoke.SpreadAmount++; - args.Updates--; component.SpreadAmount--; EnsureComp(neighbor); @@ -110,6 +188,7 @@ public sealed class SmokeSystem : EntitySystem break; } } + } private void OnReactionAttempt(EntityUid uid, SmokeComponent component, ReactionAttemptEvent args) @@ -128,101 +207,117 @@ public sealed class SmokeSystem : EntitySystem } } - private void OnSmokeUnpaused(EntityUid uid, SmokeComponent component, ref EntityUnpausedEvent args) + /// + /// Sets up a smoke component for spreading. + /// + public void StartSmoke(EntityUid uid, Solution solution, float duration, int spreadAmount, SmokeComponent? component = null) { - component.NextReact += args.PausedTime; - } + if (!Resolve(uid, ref component)) + return; - /// - public override void Update(float frameTime) - { - base.Update(frameTime); - var query = EntityQueryEnumerator(); - var curTime = _timing.CurTime; + component.SpreadAmount = spreadAmount; + component.Duration = duration; + component.TransferRate = solution.Volume / duration; + TryAddSolution(uid, solution); + Dirty(uid, component); + EnsureComp(uid); - while (query.MoveNext(out var uid, out var smoke)) + if (TryComp(uid, out var body) && TryComp(uid, out var fixtures)) { - if (smoke.NextReact > curTime) - continue; + var xform = Transform(uid); + _physics.SetBodyType(uid, BodyType.Dynamic, fixtures, body, xform); + _physics.SetCanCollide(uid, true, manager: fixtures, body: body); + _broadphase.RegenerateContacts(uid, body, fixtures, xform); + } - smoke.NextReact += TimeSpan.FromSeconds(1.5); + var timer = EnsureComp(uid); + timer.Lifetime = duration; - SmokeReact(uid, 1f, smoke); - } + // The tile reaction happens here because it only occurs once. + ReactOnTile(uid, component); } /// - /// Does the relevant smoke reactions for an entity for the specified exposure duration. + /// Does the relevant smoke reactions for an entity. /// - public void SmokeReact(EntityUid uid, float frameTime, SmokeComponent? component = null, TransformComponent? xform = null) + public void SmokeReact(EntityUid entity, EntityUid smokeUid, SmokeComponent? component = null) { - if (!Resolve(uid, ref component, ref xform)) + if (!Resolve(smokeUid, ref component)) return; - if (!_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution) || + if (!_solutionSystem.TryGetSolution(smokeUid, SmokeComponent.SolutionName, out var solution) || solution.Contents.Count == 0) { return; } - if (!_mapManager.TryGetGrid(xform.GridUid, out var mapGrid)) - return; + ReactWithEntity(entity, smokeUid, solution, component); + UpdateVisuals(smokeUid); + } - var tile = mapGrid.GetTileRef(xform.Coordinates.ToVector2i(EntityManager, _mapManager)); + private void ReactWithEntity(EntityUid entity, EntityUid smokeUid, Solution solution, SmokeComponent? component = null) + { + if (!Resolve(smokeUid, ref component)) + return; - var solutionFraction = 1 / Math.Floor(frameTime); - var ents = _lookup.GetEntitiesIntersecting(tile, 0f, flags: LookupFlags.Uncontained).ToArray(); + if (!TryComp(entity, out var bloodstream)) + return; - foreach (var reagentQuantity in solution.Contents.ToArray()) - { - if (reagentQuantity.Quantity == FixedPoint2.Zero) - continue; + var blockIngestion = _internals.AreInternalsWorking(entity); - // NOOP, react with entities on the tile or whatever. - } + var cloneSolution = solution.Clone(); + var availableTransfer = FixedPoint2.Min(cloneSolution.Volume, component.TransferRate); + var transferAmount = FixedPoint2.Min(availableTransfer, bloodstream.ChemicalSolution.AvailableVolume); + var transferSolution = cloneSolution.SplitSolution(transferAmount); - foreach (var entity in ents) + foreach (var reagentQuantity in transferSolution.Contents.ToArray()) { - if (entity == uid) + if (reagentQuantity.Quantity == FixedPoint2.Zero) continue; + var reagentProto = _prototype.Index(reagentQuantity.Reagent.Prototype); - ReactWithEntity(entity, solution, solutionFraction); + _reactive.ReactionEntity(entity, ReactionMethod.Touch, reagentProto, reagentQuantity, transferSolution); + if (!blockIngestion) + _reactive.ReactionEntity(entity, ReactionMethod.Ingestion, reagentProto, reagentQuantity, transferSolution); } - UpdateVisuals(uid); - } + if (blockIngestion) + return; - private void UpdateVisuals(EntityUid uid) - { - if (TryComp(uid, out AppearanceComponent? appearance) && - _solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution)) + if (_blood.TryAddToChemicals(entity, transferSolution, bloodstream)) { - var color = solution.GetColor(_prototype); - _appearance.SetData(uid, SmokeVisuals.Color, color, appearance); + // Log solution addition by smoke + _logger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(entity):target} ingested smoke {SolutionContainerSystem.ToPrettyString(transferSolution)}"); } } - private void ReactWithEntity(EntityUid entity, Solution solution, double solutionFraction) + private void ReactOnTile(EntityUid uid, SmokeComponent? component = null, TransformComponent? xform = null) { - // NOOP due to people complaining constantly. - return; - } + if (!Resolve(uid, ref component, ref xform)) + return; - /// - /// Sets up a smoke component for spreading. - /// - public void Start(EntityUid uid, SmokeComponent component, Solution solution, float duration) - { - TryAddSolution(uid, component, solution); - EnsureComp(uid); - var timer = EnsureComp(uid); - timer.Lifetime = duration; + if (!_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution) || !solution.Any()) + return; + + if (!_mapManager.TryGetGrid(xform.GridUid, out var mapGrid)) + return; + + var tile = mapGrid.GetTileRef(xform.Coordinates.ToVector2i(EntityManager, _mapManager, _transform)); + + foreach (var reagentQuantity in solution.Contents.ToArray()) + { + if (reagentQuantity.Quantity == FixedPoint2.Zero) + continue; + + var reagent = _prototype.Index(reagentQuantity.Reagent.Prototype); + reagent.ReactionTile(tile, reagentQuantity.Quantity); + } } /// /// Adds the specified solution to the relevant smoke solution. /// - public void TryAddSolution(EntityUid uid, SmokeComponent component, Solution solution) + private void TryAddSolution(EntityUid uid, Solution solution) { if (solution.Volume == FixedPoint2.Zero) return; @@ -237,4 +332,14 @@ public sealed class SmokeSystem : EntitySystem UpdateVisuals(uid); } + + private void UpdateVisuals(EntityUid uid) + { + if (!TryComp(uid, out AppearanceComponent? appearance) || + !_solutionSystem.TryGetSolution(uid, SmokeComponent.SolutionName, out var solution)) + return; + + var color = solution.GetColor(_prototype); + _appearance.SetData(uid, SmokeVisuals.Color, color, appearance); + } } diff --git a/Content.Server/StationEvents/Components/VentClogRuleComponent.cs b/Content.Server/StationEvents/Components/VentClogRuleComponent.cs index 79ebc520c8..afb3a36326 100644 --- a/Content.Server/StationEvents/Components/VentClogRuleComponent.cs +++ b/Content.Server/StationEvents/Components/VentClogRuleComponent.cs @@ -12,7 +12,7 @@ public sealed partial class VentClogRuleComponent : Component /// Somewhat safe chemicals to put in foam that probably won't instantly kill you. /// There is a small chance of using any reagent, ignoring this. /// - [DataField("safeishVentChemicals", customTypeSerializer: typeof(PrototypeIdListSerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdListSerializer))] public IReadOnlyList SafeishVentChemicals = new[] { "Water", "Blood", "Slime", "SpaceDrugs", "SpaceCleaner", "Nutriment", "Sugar", "SpaceLube", "Ephedrine", "Ale", "Beer", "SpaceGlue" @@ -21,31 +21,31 @@ public sealed partial class VentClogRuleComponent : Component /// /// Sound played when foam is being created. /// - [DataField("sound")] + [DataField] public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Effects/extinguish.ogg"); /// - /// The standard reagent quantity to put in the foam, modfied by event severity. + /// The standard reagent quantity to put in the foam, modified by event severity. /// - [DataField("reagentQuantity"), ViewVariables(VVAccess.ReadWrite)] - public int ReagentQuantity = 200; + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int ReagentQuantity = 100; /// - /// The standard spreading of the foam, not modfied by event severity. + /// The standard spreading of the foam, not modified by event severity. /// - [DataField("spread"), ViewVariables(VVAccess.ReadWrite)] - public int Spread = 20; + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int Spread = 16; /// /// How long the foam lasts for /// - [DataField("time"), ViewVariables(VVAccess.ReadWrite)] + [DataField, ViewVariables(VVAccess.ReadWrite)] public float Time = 20f; /// /// Reagents that gets the weak numbers used instead of regular ones. /// - [DataField("weakReagents", customTypeSerializer: typeof(PrototypeIdListSerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdListSerializer))] public IReadOnlyList WeakReagents = new[] { "SpaceLube", "SpaceGlue" @@ -54,12 +54,12 @@ public sealed partial class VentClogRuleComponent : Component /// /// Quantity of weak reagents to put in the foam. /// - [DataField("weakReagentQuantity"), ViewVariables(VVAccess.ReadWrite)] - public int WeakReagentQuantity = 60; + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int WeakReagentQuantity = 50; /// /// Spread of the foam for weak reagents. /// - [DataField("weakSpread"), ViewVariables(VVAccess.ReadWrite)] - public int WeakSpread = 2; + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int WeakSpread = 3; } diff --git a/Content.Server/StationEvents/Events/VentClogRule.cs b/Content.Server/StationEvents/Events/VentClogRule.cs index 5ef28304e7..f378aec3fb 100644 --- a/Content.Server/StationEvents/Events/VentClogRule.cs +++ b/Content.Server/StationEvents/Events/VentClogRule.cs @@ -3,10 +3,8 @@ using Content.Server.Station.Components; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using JetBrains.Annotations; -using Robust.Shared.Audio; using Robust.Shared.Random; using System.Linq; -using Content.Server.Chemistry.Components; using Content.Server.Fluids.EntitySystems; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; @@ -53,9 +51,8 @@ public sealed class VentClogRule : StationEventSystem solution.AddReagent(reagent, quantity); var foamEnt = Spawn("Foam", transform.Coordinates); - var smoke = EnsureComp(foamEnt); - smoke.SpreadAmount = weak ? component.WeakSpread : component.Spread; - _smoke.Start(foamEnt, smoke, solution, component.Time); + var spreadAmount = weak ? component.WeakSpread : component.Spread; + _smoke.StartSmoke(foamEnt, solution, component.Time, spreadAmount); Audio.PlayPvs(component.Sound, transform.Coordinates); } } diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/FoamArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/FoamArtifactSystem.cs index 93d9d5a01b..2d2c230b12 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/FoamArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/FoamArtifactSystem.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.Chemistry.Components; using Content.Server.Fluids.EntitySystems; using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components; using Content.Server.Xenoarchaeology.XenoArtifacts.Events; @@ -38,8 +37,7 @@ public sealed class FoamArtifactSystem : EntitySystem var range = (int) MathF.Round(MathHelper.Lerp(component.MinFoamAmount, component.MaxFoamAmount, _random.NextFloat(0, 1f))); sol.AddReagent(component.SelectedReagent, component.ReagentAmount); var foamEnt = Spawn("Foam", xform.Coordinates); - var smoke = EnsureComp(foamEnt); - smoke.SpreadAmount = range * 4; - _smoke.Start(foamEnt, smoke, sol, component.Duration); + var spreadAmount = range * 4; + _smoke.StartSmoke(foamEnt, sol, component.Duration, spreadAmount); } } diff --git a/Content.Shared/Chemistry/Components/SmokeAffectedComponent.cs b/Content.Shared/Chemistry/Components/SmokeAffectedComponent.cs new file mode 100644 index 0000000000..def6940ee1 --- /dev/null +++ b/Content.Shared/Chemistry/Components/SmokeAffectedComponent.cs @@ -0,0 +1,24 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Shared.Chemistry.Components; + +/// +/// This is used for entities which are currently being affected by smoke. +/// Manages the gradual metabolism every second. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class SmokeAffectedComponent : Component +{ + /// + /// The time at which the next smoke metabolism will occur. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan NextSecond; + + /// + /// The smoke that is currently affecting this entity. + /// + [DataField] + public EntityUid SmokeEntity; +} diff --git a/Content.Shared/Chemistry/Components/SmokeComponent.cs b/Content.Shared/Chemistry/Components/SmokeComponent.cs new file mode 100644 index 0000000000..9d88fcac94 --- /dev/null +++ b/Content.Shared/Chemistry/Components/SmokeComponent.cs @@ -0,0 +1,34 @@ +using Content.Shared.FixedPoint; +using Content.Shared.Fluids.Components; +using Robust.Shared.GameStates; + +namespace Content.Shared.Chemistry.Components; + +/// +/// Stores solution on an anchored entity that has touch and ingestion reactions +/// to entities that collide with it. Similar to +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class SmokeComponent : Component +{ + public const string SolutionName = "solutionArea"; + + /// + /// The max amount of tiles this smoke cloud can spread to. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int SpreadAmount; + + /// + /// The max rate at which chemicals are transferred from the smoke to the person inhaling it. + /// Calculated as (total volume of chemicals in smoke) / () + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public FixedPoint2 TransferRate; + + /// + /// The total lifespan of the smoke. + /// + [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public float Duration = 10; +} -- 2.51.2