From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com>
Date: Sun, 30 Jun 2024 03:43:43 +0000 (+0200)
Subject: Turn ReagentEffects into generic EntityEffects (#28168)
X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=b9fa941ca6ea1e207f88d0dd21ac51708bd5fa0f;p=space-station-14.git
Turn ReagentEffects into generic EntityEffects (#28168)
* Oh the possibilities
* Merge fixes
* Forgot to remote LavaSystem oops
* Changed EntityEffectArgs to EntityEffectBaseArgs and EntityEffectReagentArgs
* Throw exception for unimplemented effectargs
* Remove Json and overrideable datafields
* Fix test issues
* Actually fix the compiling issue
* Fix comments and remove EntityEffectArgs (no longer used, replaced with EntityEffectBaseArgs)
---
diff --git a/Content.Server/Body/Systems/BloodstreamSystem.cs b/Content.Server/Body/Systems/BloodstreamSystem.cs
index eaa7b62f25..a8a86028d4 100644
--- a/Content.Server/Body/Systems/BloodstreamSystem.cs
+++ b/Content.Server/Body/Systems/BloodstreamSystem.cs
@@ -1,6 +1,6 @@
using Content.Server.Body.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
-using Content.Server.Chemistry.ReactionEffects;
+using Content.Server.EntityEffects.Effects;
using Content.Server.Fluids.EntitySystems;
using Content.Server.Forensics;
using Content.Server.Popups;
diff --git a/Content.Server/Body/Systems/MetabolizerSystem.cs b/Content.Server/Body/Systems/MetabolizerSystem.cs
index 8394d9999b..5ba3c6ee58 100644
--- a/Content.Server/Body/Systems/MetabolizerSystem.cs
+++ b/Content.Server/Body/Systems/MetabolizerSystem.cs
@@ -6,6 +6,7 @@ using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
+using Content.Shared.EntityEffects;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
@@ -193,8 +194,7 @@ namespace Content.Server.Body.Systems
}
var actualEntity = ent.Comp2?.Body ?? solutionEntityUid.Value;
- var args = new ReagentEffectArgs(actualEntity, ent, solution, proto, mostToRemove,
- EntityManager, null, scale);
+ var args = new EntityEffectReagentArgs(actualEntity, EntityManager, ent, solution, mostToRemove, proto, null, scale);
// do all effects, if conditions apply
foreach (var effect in entry.Effects)
diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs
index 5461f68db2..5fbd0c4814 100644
--- a/Content.Server/Body/Systems/RespiratorSystem.cs
+++ b/Content.Server/Body/Systems/RespiratorSystem.cs
@@ -3,8 +3,8 @@ using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Components;
using Content.Server.Chat.Systems;
using Content.Server.Chemistry.Containers.EntitySystems;
-using Content.Server.Chemistry.ReagentEffectConditions;
-using Content.Server.Chemistry.ReagentEffects;
+using Content.Server.EntityEffects.EffectConditions;
+using Content.Server.EntityEffects.Effects;
using Content.Shared.Alert;
using Content.Shared.Atmos;
using Content.Shared.Body.Components;
@@ -13,6 +13,7 @@ using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Damage;
using Content.Shared.Database;
+using Content.Shared.EntityEffects;
using Content.Shared.Mobs.Systems;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
@@ -261,7 +262,7 @@ public sealed class RespiratorSystem : EntitySystem
// TODO generalize condition checks
// this is pretty janky, but I just want to bodge a method that checks if an entity can breathe a gas mixture
// Applying actual reaction effects require a full ReagentEffectArgs struct.
- bool CanMetabolize(ReagentEffect effect)
+ bool CanMetabolize(EntityEffect effect)
{
if (effect.Conditions == null)
return true;
diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs
index 0ae56bc5fc..b2882d8e94 100644
--- a/Content.Server/Botany/SeedPrototype.cs
+++ b/Content.Server/Botany/SeedPrototype.cs
@@ -1,7 +1,7 @@
using Content.Server.Botany.Components;
using Content.Server.Botany.Systems;
using Content.Shared.Atmos;
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
@@ -80,7 +80,7 @@ public partial struct SeedChemQuantity
// TODO reduce the number of friends to a reasonable level. Requires ECS-ing things like plant holder component.
[Virtual, DataDefinition]
-[Access(typeof(BotanySystem), typeof(PlantHolderSystem), typeof(SeedExtractorSystem), typeof(PlantHolderComponent), typeof(ReagentEffect), typeof(MutationSystem))]
+[Access(typeof(BotanySystem), typeof(PlantHolderSystem), typeof(SeedExtractorSystem), typeof(PlantHolderComponent), typeof(EntityEffect), typeof(MutationSystem))]
public partial class SeedData
{
#region Tracking
diff --git a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs
index 7b70497c7d..c5bdb39816 100644
--- a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs
+++ b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs
@@ -1,7 +1,7 @@
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Chemistry;
using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Forensics;
diff --git a/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs
deleted file mode 100644
index 56509a0953..0000000000
--- a/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using Content.Server.Fluids.EntitySystems;
-using Content.Shared.Audio;
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Coordinates.Helpers;
-using Content.Shared.Database;
-using Content.Shared.FixedPoint;
-using Content.Shared.Maps;
-using JetBrains.Annotations;
-using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
-using Robust.Shared.Audio.Systems;
-using Robust.Shared.Map;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.Chemistry.ReactionEffects
-{
- ///
- /// Basically smoke and foam reactions.
- ///
- [UsedImplicitly]
- [DataDefinition]
- public sealed partial class AreaReactionEffect : ReagentEffect
- {
- ///
- /// How many seconds will the effect stay, counting after fully spreading.
- ///
- [DataField("duration")] private float _duration = 10;
-
- ///
- /// How many units of reaction for 1 smoke entity.
- ///
- [DataField] public FixedPoint2 OverflowThreshold = FixedPoint2.New(2.5);
-
- ///
- /// The entity prototype that will be spawned as the effect.
- ///
- [DataField("prototypeId", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))]
- private string _prototypeId = default!;
-
- ///
- /// Sound that will get played when this reaction effect occurs.
- ///
- [DataField("sound", required: true)] private SoundSpecifier _sound = default!;
-
- public override bool ShouldLog => true;
-
- protected override string ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-area-reaction",
- ("duration", _duration)
- );
-
- public override LogImpact LogImpact => LogImpact.High;
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (args.Source == null)
- return;
-
- var spreadAmount = (int) Math.Max(0, Math.Ceiling((args.Quantity / OverflowThreshold).Float()));
- var splitSolution = args.Source.SplitSolution(args.Source.Volume);
- var transform = args.EntityManager.GetComponent(args.SolutionEntity);
- var mapManager = IoCManager.Resolve();
- var mapSys = args.EntityManager.System();
- var sys = args.EntityManager.System();
- var mapCoords = sys.GetMapCoordinates(args.SolutionEntity, xform: transform);
-
- if (!mapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid) ||
- !mapSys.TryGetTileRef(gridUid, grid, transform.Coordinates, out var tileRef) ||
- tileRef.Tile.IsSpace())
- {
- return;
- }
-
- var coords = mapSys.MapToGrid(gridUid, mapCoords);
- var ent = args.EntityManager.SpawnEntity(_prototypeId, coords.SnapToGrid());
-
- var smoke = args.EntityManager.System();
- smoke.StartSmoke(ent, splitSolution, _duration, spreadAmount);
-
- var audio = args.EntityManager.System();
- audio.PlayPvs(_sound, args.SolutionEntity, AudioHelpers.WithVariation(0.125f));
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs
deleted file mode 100644
index 2771f79c3a..0000000000
--- a/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-using Content.Server.Explosion.EntitySystems;
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Database;
-using Content.Shared.Explosion;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using System.Text.Json.Serialization;
-
-namespace Content.Server.Chemistry.ReactionEffects
-{
- [DataDefinition]
- public sealed partial class ExplosionReactionEffect : ReagentEffect
- {
- ///
- /// The type of explosion. Determines damage types and tile break chance scaling.
- ///
- [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer))]
- [JsonIgnore]
- public string ExplosionType = default!;
-
- ///
- /// The max intensity the explosion can have at a given tile. Places an upper limit of damage and tile break
- /// chance.
- ///
- [DataField]
- [JsonIgnore]
- public float MaxIntensity = 5;
-
- ///
- /// How quickly intensity drops off as you move away from the epicenter
- ///
- [DataField]
- [JsonIgnore]
- public float IntensitySlope = 1;
-
- ///
- /// The maximum total intensity that this chemical reaction can achieve. Basically here to prevent people
- /// from creating a nuke by collecting enough potassium and water.
- ///
- ///
- /// A slope of 1 and MaxTotalIntensity of 100 corresponds to a radius of around 4.5 tiles.
- ///
- [DataField]
- [JsonIgnore]
- public float MaxTotalIntensity = 100;
-
- ///
- /// The intensity of the explosion per unit reaction.
- ///
- [DataField]
- [JsonIgnore]
- public float IntensityPerUnit = 1;
-
- public override bool ShouldLog => true;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-explosion-reaction-effect", ("chance", Probability));
- public override LogImpact LogImpact => LogImpact.High;
-
- public override void Effect(ReagentEffectArgs args)
- {
- var intensity = MathF.Min((float) args.Quantity * IntensityPerUnit, MaxTotalIntensity);
-
- args.EntityManager.System()
- .QueueExplosion(
- args.SolutionEntity,
- ExplosionType,
- intensity,
- IntensitySlope,
- MaxIntensity);
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReactionEffects/SolutionTemperatureEffects.cs b/Content.Server/Chemistry/ReactionEffects/SolutionTemperatureEffects.cs
deleted file mode 100644
index ec58754883..0000000000
--- a/Content.Server/Chemistry/ReactionEffects/SolutionTemperatureEffects.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReactionEffects
-{
- ///
- /// Sets the temperature of the solution involved with the reaction to a new value.
- ///
- [DataDefinition]
- public sealed partial class SetSolutionTemperatureEffect : ReagentEffect
- {
- ///
- /// The temperature to set the solution to.
- ///
- [DataField("temperature", required: true)] private float _temperature;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-set-solution-temperature-effect",
- ("chance", Probability), ("temperature", _temperature));
-
- public override void Effect(ReagentEffectArgs args)
- {
- var solution = args.Source;
- if (solution == null)
- return;
-
- solution.Temperature = _temperature;
- }
- }
-
- ///
- /// Adjusts the temperature of the solution involved in the reaction.
- ///
- [DataDefinition]
- public sealed partial class AdjustSolutionTemperatureEffect : ReagentEffect
- {
- ///
- /// The change in temperature.
- ///
- [DataField("delta", required: true)] private float _delta;
-
- ///
- /// The minimum temperature this effect can reach.
- ///
- [DataField("minTemp")] private float _minTemp = 0.0f;
-
- ///
- /// The maximum temperature this effect can reach.
- ///
- [DataField("maxTemp")] private float _maxTemp = float.PositiveInfinity;
-
- ///
- /// If true, then scale ranges by intensity. If not, the ranges are the same regardless of reactant amount.
- ///
- [DataField("scaled")] private bool _scaled;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-adjust-solution-temperature-effect",
- ("chance", Probability), ("deltasign", MathF.Sign(_delta)), ("mintemp", _minTemp), ("maxtemp", _maxTemp));
-
- public override void Effect(ReagentEffectArgs args)
- {
- var solution = args.Source;
- if (solution == null || solution.Volume == 0)
- return;
-
- var deltaT = _scaled ? _delta * (float) args.Quantity : _delta;
- solution.Temperature = Math.Clamp(solution.Temperature + deltaT, _minTemp, _maxTemp);
- }
- }
-
- ///
- /// Adjusts the thermal energy of the solution involved in the reaction.
- ///
- public sealed partial class AdjustSolutionThermalEnergyEffect : ReagentEffect
- {
- ///
- /// The change in energy.
- ///
- [DataField("delta", required: true)] private float _delta;
-
- ///
- /// The minimum temperature this effect can reach.
- ///
- [DataField("minTemp")] private float _minTemp = 0.0f;
-
- ///
- /// The maximum temperature this effect can reach.
- ///
- [DataField("maxTemp")] private float _maxTemp = float.PositiveInfinity;
-
- ///
- /// If true, then scale ranges by intensity. If not, the ranges are the same regardless of reactant amount.
- ///
- [DataField("scaled")] private bool _scaled;
-
- public override void Effect(ReagentEffectArgs args)
- {
- var solution = args.Source;
- if (solution == null || solution.Volume == 0)
- return;
-
- if (_delta > 0 && solution.Temperature >= _maxTemp)
- return;
- if (_delta < 0 && solution.Temperature <= _minTemp)
- return;
-
- var heatCap = solution.GetHeatCapacity(null);
- var deltaT = _scaled
- ? _delta / heatCap * (float) args.Quantity
- : _delta / heatCap;
-
- solution.Temperature = Math.Clamp(solution.Temperature + deltaT, _minTemp, _maxTemp);
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-adjust-solution-temperature-effect",
- ("chance", Probability), ("deltasign", MathF.Sign(_delta)), ("mintemp", _minTemp), ("maxtemp", _maxTemp));
- }
-
-}
diff --git a/Content.Server/Chemistry/ReagentEffectConditions/BodyTemperature.cs b/Content.Server/Chemistry/ReagentEffectConditions/BodyTemperature.cs
deleted file mode 100644
index 9c47bb58ab..0000000000
--- a/Content.Server/Chemistry/ReagentEffectConditions/BodyTemperature.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using Content.Server.Temperature.Components;
-using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffectConditions
-{
- ///
- /// Requires the solution entity to be above or below a certain temperature.
- /// Used for things like cryoxadone and pyroxadone.
- ///
- public sealed partial class Temperature : ReagentEffectCondition
- {
- [DataField]
- public float Min = 0;
-
- [DataField]
- public float Max = float.PositiveInfinity;
- public override bool Condition(ReagentEffectArgs args)
- {
- if (args.EntityManager.TryGetComponent(args.SolutionEntity, out TemperatureComponent? temp))
- {
- if (temp.CurrentTemperature > Min && temp.CurrentTemperature < Max)
- return true;
- }
-
- return false;
- }
-
- public override string GuidebookExplanation(IPrototypeManager prototype)
- {
- return Loc.GetString("reagent-effect-condition-guidebook-body-temperature",
- ("max", float.IsPositiveInfinity(Max) ? (float) int.MaxValue : Max),
- ("min", Min));
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffectConditions/JobCondition.cs b/Content.Server/Chemistry/ReagentEffectConditions/JobCondition.cs
deleted file mode 100644
index 0ede690049..0000000000
--- a/Content.Server/Chemistry/ReagentEffectConditions/JobCondition.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System.Linq;
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Mobs;
-using Content.Shared.Mobs.Components;
-using Content.Shared.Localizations;
-using Robust.Shared.Prototypes;
-using Content.Shared.Mind;
-using Content.Shared.Mind.Components;
-using Content.Shared.Roles;
-using Content.Shared.Roles.Jobs;
-using Content.Shared.Station;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using Robust.Shared.IoC;
-
-namespace Content.Server.Chemistry.ReagentEffectConditions
-{
- public sealed partial class JobCondition : ReagentEffectCondition
- {
- [DataField(required: true)] public List> Job;
-
- public override bool Condition(ReagentEffectArgs args)
- {
- args.EntityManager.TryGetComponent(args.SolutionEntity, out var mindContainer);
- if (mindContainer != null && mindContainer.Mind != null)
- {
- var prototypeManager = IoCManager.Resolve();
- if (args.EntityManager.TryGetComponent(mindContainer?.Mind, out var comp) && prototypeManager.TryIndex(comp.Prototype, out var prototype))
- {
- foreach (var jobId in Job)
- {
- if (prototype.ID == jobId)
- {
- return true;
- }
- }
- }
- }
-
- return false;
- }
-
- public override string GuidebookExplanation(IPrototypeManager prototype)
- {
- var localizedNames = Job.Select(jobId => prototype.Index(jobId).LocalizedName).ToList();
- return Loc.GetString("reagent-effect-condition-guidebook-job-condition", ("job", ContentLocalizationManager.FormatListToOr(localizedNames)));
- }
- }
-}
-
diff --git a/Content.Server/Chemistry/ReagentEffectConditions/MobStateCondition.cs b/Content.Server/Chemistry/ReagentEffectConditions/MobStateCondition.cs
deleted file mode 100644
index 377eb7d906..0000000000
--- a/Content.Server/Chemistry/ReagentEffectConditions/MobStateCondition.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Mobs;
-using Content.Shared.Mobs.Components;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffectConditions
-{
- public sealed partial class MobStateCondition : ReagentEffectCondition
- {
- [DataField]
- public MobState Mobstate = MobState.Alive;
-
- public override bool Condition(ReagentEffectArgs args)
- {
- if (args.EntityManager.TryGetComponent(args.SolutionEntity, out MobStateComponent? mobState))
- {
- if (mobState.CurrentState == Mobstate)
- return true;
- }
-
- return false;
- }
-
- public override string GuidebookExplanation(IPrototypeManager prototype)
- {
- return Loc.GetString("reagent-effect-condition-guidebook-mob-state-condition", ("state", Mobstate));
- }
- }
-}
-
diff --git a/Content.Server/Chemistry/ReagentEffectConditions/OrganType.cs b/Content.Server/Chemistry/ReagentEffectConditions/OrganType.cs
deleted file mode 100644
index 986c3d79c8..0000000000
--- a/Content.Server/Chemistry/ReagentEffectConditions/OrganType.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using Content.Server.Body.Components;
-using Content.Shared.Body.Prototypes;
-using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.Chemistry.ReagentEffectConditions
-{
- ///
- /// Requires that the metabolizing organ is or is not tagged with a certain MetabolizerType
- ///
- public sealed partial class OrganType : ReagentEffectCondition
- {
- [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string Type = default!;
-
- ///
- /// Does this condition pass when the organ has the type, or when it doesn't have the type?
- ///
- [DataField]
- public bool ShouldHave = true;
-
- public override bool Condition(ReagentEffectArgs args)
- {
- if (args.OrganEntity == null)
- return false;
-
- return Condition(args.OrganEntity.Value, args.EntityManager);
- }
-
- public bool Condition(Entity metabolizer, IEntityManager entMan)
- {
- metabolizer.Comp ??= entMan.GetComponentOrNull(metabolizer.Owner);
- if (metabolizer.Comp != null
- && metabolizer.Comp.MetabolizerTypes != null
- && metabolizer.Comp.MetabolizerTypes.Contains(Type))
- return ShouldHave;
- return !ShouldHave;
- }
-
- public override string GuidebookExplanation(IPrototypeManager prototype)
- {
- return Loc.GetString("reagent-effect-condition-guidebook-organ-type",
- ("name", prototype.Index(Type).LocalizedName),
- ("shouldhave", ShouldHave));
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs b/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs
deleted file mode 100644
index 6ac1549ab0..0000000000
--- a/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.FixedPoint;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffectConditions
-{
- ///
- /// Used for implementing reagent effects that require a certain amount of reagent before it should be applied.
- /// For instance, overdoses.
- ///
- /// This can also trigger on -other- reagents, not just the one metabolizing. By default, it uses the
- /// one being metabolized.
- ///
- public sealed partial class ReagentThreshold : ReagentEffectCondition
- {
- [DataField]
- public FixedPoint2 Min = FixedPoint2.Zero;
-
- [DataField]
- public FixedPoint2 Max = FixedPoint2.MaxValue;
-
- // TODO use ReagentId
- [DataField]
- public string? Reagent;
-
- public override bool Condition(ReagentEffectArgs args)
- {
- var reagent = Reagent ?? args.Reagent?.ID;
- if (reagent == null)
- return true; // No condition to apply.
-
- var quant = FixedPoint2.Zero;
- if (args.Source != null)
- quant = args.Source.GetTotalPrototypeQuantity(reagent);
-
- return quant >= Min && quant <= Max;
- }
-
- public override string GuidebookExplanation(IPrototypeManager prototype)
- {
- ReagentPrototype? reagentProto = null;
- if (Reagent is not null)
- prototype.TryIndex(Reagent, out reagentProto);
-
- return Loc.GetString("reagent-effect-condition-guidebook-reagent-threshold",
- ("reagent", reagentProto?.LocalizedName ?? Loc.GetString("reagent-effect-condition-guidebook-this-reagent")),
- ("max", Max == FixedPoint2.MaxValue ? (float) int.MaxValue : Max.Float()),
- ("min", Min.Float()));
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffectConditions/SolutionTemperature.cs b/Content.Server/Chemistry/ReagentEffectConditions/SolutionTemperature.cs
deleted file mode 100644
index 4387f2ba93..0000000000
--- a/Content.Server/Chemistry/ReagentEffectConditions/SolutionTemperature.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffectConditions
-{
- ///
- /// Requires the solution to be above or below a certain temperature.
- /// Used for things like explosives.
- ///
- public sealed partial class SolutionTemperature : ReagentEffectCondition
- {
- [DataField]
- public float Min = 0.0f;
-
- [DataField]
- public float Max = float.PositiveInfinity;
- public override bool Condition(ReagentEffectArgs args)
- {
- if (args.Source == null)
- return false;
- if (args.Source.Temperature < Min)
- return false;
- if (args.Source.Temperature > Max)
- return false;
-
- return true;
- }
-
- public override string GuidebookExplanation(IPrototypeManager prototype)
- {
- return Loc.GetString("reagent-effect-condition-guidebook-solution-temperature",
- ("max", float.IsPositiveInfinity(Max) ? (float) int.MaxValue : Max),
- ("min", Min));
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffectConditions/TotalDamage.cs b/Content.Server/Chemistry/ReagentEffectConditions/TotalDamage.cs
deleted file mode 100644
index 49630ef9ab..0000000000
--- a/Content.Server/Chemistry/ReagentEffectConditions/TotalDamage.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Damage;
-using Content.Shared.FixedPoint;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffectConditions
-{
- public sealed partial class TotalDamage : ReagentEffectCondition
- {
- [DataField]
- public FixedPoint2 Max = FixedPoint2.MaxValue;
-
- [DataField]
- public FixedPoint2 Min = FixedPoint2.Zero;
-
- public override bool Condition(ReagentEffectArgs args)
- {
- if (args.EntityManager.TryGetComponent(args.SolutionEntity, out DamageableComponent? damage))
- {
- var total = damage.TotalDamage;
- if (total > Min && total < Max)
- return true;
- }
-
- return false;
- }
-
- public override string GuidebookExplanation(IPrototypeManager prototype)
- {
- return Loc.GetString("reagent-effect-condition-guidebook-total-damage",
- ("max", Max == FixedPoint2.MaxValue ? (float) int.MaxValue : Max.Float()),
- ("min", Min.Float()));
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffectConditions/TotalHunger.cs b/Content.Server/Chemistry/ReagentEffectConditions/TotalHunger.cs
deleted file mode 100644
index 1dd12e632a..0000000000
--- a/Content.Server/Chemistry/ReagentEffectConditions/TotalHunger.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Nutrition.Components;
-using Content.Shared.FixedPoint;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffectConditions
-{
- public sealed partial class Hunger : ReagentEffectCondition
- {
- [DataField]
- public float Max = float.PositiveInfinity;
-
- [DataField]
- public float Min = 0;
-
- public override bool Condition(ReagentEffectArgs args)
- {
- if (args.EntityManager.TryGetComponent(args.SolutionEntity, out HungerComponent? hunger))
- {
- var total = hunger.CurrentHunger;
- if (total > Min && total < Max)
- return true;
- }
-
- return false;
- }
-
- public override string GuidebookExplanation(IPrototypeManager prototype)
- {
- return Loc.GetString("reagent-effect-condition-guidebook-total-hunger",
- ("max", float.IsPositiveInfinity(Max) ? (float) int.MaxValue : Max),
- ("min", Min));
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/AddToSolutionReaction.cs b/Content.Server/Chemistry/ReagentEffects/AddToSolutionReaction.cs
deleted file mode 100644
index 2447814f94..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/AddToSolutionReaction.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Content.Server.Chemistry.Containers.EntitySystems;
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffects
-{
- [UsedImplicitly]
- public sealed partial class AddToSolutionReaction : ReagentEffect
- {
- [DataField("solution")]
- private string _solution = "reagents";
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (args.Reagent == null)
- return;
-
- // TODO see if this is correct
- var solutionContainerSystem = args.EntityManager.System();
- if (!solutionContainerSystem.TryGetSolution(args.SolutionEntity, _solution, out var solutionContainer))
- return;
-
- if (solutionContainerSystem.TryAddReagent(solutionContainer.Value, args.Reagent.ID, args.Quantity, out var accepted))
- args.Source?.RemoveReagent(args.Reagent.ID, accepted);
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) =>
- Loc.GetString("reagent-effect-guidebook-add-to-solution-reaction", ("chance", Probability));
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/ChemCleanBloodstream.cs b/Content.Server/Chemistry/ReagentEffects/ChemCleanBloodstream.cs
deleted file mode 100644
index ace73c84f3..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/ChemCleanBloodstream.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Content.Server.Body.Systems;
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReactionEffects
-{
- ///
- /// Basically smoke and foam reactions.
- ///
- [UsedImplicitly]
- public sealed partial class ChemCleanBloodstream : ReagentEffect
- {
- [DataField]
- public float CleanseRate = 3.0f;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-chem-clean-bloodstream", ("chance", Probability));
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (args.Source == null || args.Reagent == null)
- return;
-
- var cleanseRate = CleanseRate;
-
- cleanseRate *= args.Scale;
-
- var bloodstreamSys = args.EntityManager.System();
- bloodstreamSys.FlushChemicals(args.SolutionEntity, args.Reagent.ID, cleanseRate);
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/ChemHealEyeDamage.cs b/Content.Server/Chemistry/ReagentEffects/ChemHealEyeDamage.cs
deleted file mode 100644
index 206b8ed04d..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/ChemHealEyeDamage.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Eye.Blinding;
-using Content.Shared.Eye.Blinding.Systems;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffects
-{
- ///
- /// Heal or apply eye damage
- ///
- [UsedImplicitly]
- public sealed partial class ChemHealEyeDamage : ReagentEffect
- {
- ///
- /// How much eye damage to add.
- ///
- [DataField]
- public int Amount = -1;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-cure-eye-damage", ("chance", Probability), ("deltasign", MathF.Sign(Amount)));
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (args.Scale != 1f) // huh?
- return;
-
- args.EntityManager.EntitySysManager.GetEntitySystem().AdjustEyeDamage(args.SolutionEntity, Amount);
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/Electrocute.cs b/Content.Server/Chemistry/ReagentEffects/Electrocute.cs
deleted file mode 100644
index 272a0fce42..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/Electrocute.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Content.Server.Electrocution;
-using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffects;
-
-public sealed partial class Electrocute : ReagentEffect
-{
- [DataField] public int ElectrocuteTime = 2;
-
- [DataField] public int ElectrocuteDamageScale = 5;
-
- ///
- /// true - refresh electrocute time, false - accumulate electrocute time
- ///
- [DataField] public bool Refresh = true;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-electrocute", ("chance", Probability), ("time", ElectrocuteTime));
-
- public override bool ShouldLog => true;
-
- public override void Effect(ReagentEffectArgs args)
- {
- args.EntityManager.System().TryDoElectrocution(args.SolutionEntity, null,
- Math.Max((args.Quantity * ElectrocuteDamageScale).Int(), 1), TimeSpan.FromSeconds(ElectrocuteTime), Refresh, ignoreInsulation: true);
-
- if (args.Reagent != null)
- args.Source?.RemoveReagent(args.Reagent.ID, args.Quantity);
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/ExtinguishReaction.cs b/Content.Server/Chemistry/ReagentEffects/ExtinguishReaction.cs
deleted file mode 100644
index 69aaf5cdcd..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/ExtinguishReaction.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using Content.Server.Atmos.Components;
-using Content.Server.Atmos.EntitySystems;
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffects
-{
- [UsedImplicitly]
- public sealed partial class ExtinguishReaction : ReagentEffect
- {
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-extinguish-reaction", ("chance", Probability));
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out FlammableComponent? flammable)) return;
-
- var flammableSystem = args.EntityManager.System();
- flammableSystem.Extinguish(args.SolutionEntity, flammable);
- flammableSystem.AdjustFireStacks(args.SolutionEntity, -1.5f * (float) args.Quantity, flammable);
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/FlammableReaction.cs b/Content.Server/Chemistry/ReagentEffects/FlammableReaction.cs
deleted file mode 100644
index 5b36967f6a..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/FlammableReaction.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using Content.Server.Atmos.Components;
-using Content.Server.Atmos.EntitySystems;
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Database;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffects
-{
- [UsedImplicitly]
- public sealed partial class FlammableReaction : ReagentEffect
- {
- [DataField]
- public float Multiplier = 0.05f;
-
- public override bool ShouldLog => true;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-flammable-reaction", ("chance", Probability));
-
- public override LogImpact LogImpact => LogImpact.Medium;
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out FlammableComponent? flammable))
- return;
-
- args.EntityManager.System().AdjustFireStacks(args.SolutionEntity, args.Quantity.Float() * Multiplier, flammable);
-
- if (args.Reagent != null)
- args.Source?.RemoveReagent(args.Reagent.ID, args.Quantity);
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/ModifyLungGas.cs b/Content.Server/Chemistry/ReagentEffects/ModifyLungGas.cs
deleted file mode 100644
index e7466fbc85..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/ModifyLungGas.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using Content.Server.Body.Components;
-using Content.Shared.Atmos;
-using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffects;
-
-public sealed partial class ModifyLungGas : ReagentEffect
-{
- [DataField("ratios", required: true)]
- private Dictionary _ratios = default!;
-
- // JUSTIFICATION: This is internal magic that players never directly interact with.
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => null;
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (!args.EntityManager.TryGetComponent(args.OrganEntity, out var lung))
- return;
-
- foreach (var (gas, ratio) in _ratios)
- {
- var quantity = ratio * args.Quantity.Float() / Atmospherics.BreathMolesToReagentMultiplier;
- if (quantity < 0)
- quantity = Math.Max(quantity, -lung.Air[(int)gas]);
- lung.Air.AdjustMoles(gas, quantity);
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs b/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs
deleted file mode 100644
index e538a011f6..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Movement.Systems;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Timing;
-
-namespace Content.Server.Chemistry.ReagentEffects
-{
- ///
- /// Default metabolism for stimulants and tranqs. Attempts to find a MovementSpeedModifier on the target,
- /// adding one if not there and to change the movespeed
- ///
- public sealed partial class MovespeedModifier : ReagentEffect
- {
- ///
- /// How much the entities' walk speed is multiplied by.
- ///
- [DataField]
- public float WalkSpeedModifier { get; set; } = 1;
-
- ///
- /// How much the entities' run speed is multiplied by.
- ///
- [DataField]
- public float SprintSpeedModifier { get; set; } = 1;
-
- ///
- /// How long the modifier applies (in seconds) when metabolized.
- ///
- [DataField]
- public float StatusLifetime = 2f;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- {
- return Loc.GetString("reagent-effect-guidebook-movespeed-modifier",
- ("chance", Probability),
- ("walkspeed", WalkSpeedModifier),
- ("time", StatusLifetime));
- }
-
- ///
- /// Remove reagent at set rate, changes the movespeed modifiers and adds a MovespeedModifierMetabolismComponent if not already there.
- ///
- public override void Effect(ReagentEffectArgs args)
- {
- var status = args.EntityManager.EnsureComponent(args.SolutionEntity);
-
- // Only refresh movement if we need to.
- var modified = !status.WalkSpeedModifier.Equals(WalkSpeedModifier) ||
- !status.SprintSpeedModifier.Equals(SprintSpeedModifier);
-
- status.WalkSpeedModifier = WalkSpeedModifier;
- status.SprintSpeedModifier = SprintSpeedModifier;
-
- // only going to scale application time
- var statusLifetime = StatusLifetime;
- statusLifetime *= args.Scale;
-
- IncreaseTimer(status, statusLifetime);
-
- if (modified)
- args.EntityManager.System().RefreshMovementSpeedModifiers(args.SolutionEntity);
-
- }
- public void IncreaseTimer(MovespeedModifierMetabolismComponent status, float time)
- {
- var gameTiming = IoCManager.Resolve();
-
- var offsetTime = Math.Max(status.ModifierTimer.TotalSeconds, gameTiming.CurTime.TotalSeconds);
-
- status.ModifierTimer = TimeSpan.FromSeconds(offsetTime + time);
- status.Dirty();
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/Oxygenate.cs b/Content.Server/Chemistry/ReagentEffects/Oxygenate.cs
deleted file mode 100644
index 6c5ab155e4..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/Oxygenate.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Content.Server.Body.Components;
-using Content.Server.Body.Systems;
-using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffects;
-
-public sealed partial class Oxygenate : ReagentEffect
-{
- [DataField]
- public float Factor = 1f;
-
- // JUSTIFICATION: This is internal magic that players never directly interact with.
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => null;
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var resp))
- {
- var respSys = args.EntityManager.System();
- respSys.UpdateSaturation(args.SolutionEntity, args.Quantity.Float() * Factor, resp);
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustAttribute.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustAttribute.cs
deleted file mode 100644
index 9bd8fdaa68..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustAttribute.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using Content.Server.Botany.Components;
-using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Random;
-using System.Diagnostics.CodeAnalysis;
-
-namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
-{
- [ImplicitDataDefinitionForInheritors]
- public abstract partial class PlantAdjustAttribute : ReagentEffect
- {
- [DataField]
- public float Amount { get; protected set; } = 1;
-
- ///
- /// Localisation key for the name of the adjusted attribute. Used for guidebook descriptions.
- ///
- [DataField]
- public abstract string GuidebookAttributeName { get; set; }
-
- ///
- /// Whether the attribute in question is a good thing. Used for guidebook descriptions to determine the color of the number.
- ///
- [DataField]
- public virtual bool GuidebookIsAttributePositive { get; protected set; } = true;
-
- ///
- /// Checks if the plant holder can metabolize the reagent or not. Checks if it has an alive plant by default.
- ///
- /// The entity holding the plant
- /// The plant holder component
- /// The entity manager
- /// Whether to check if it has an alive plant or not
- ///
- public bool CanMetabolize(EntityUid plantHolder, [NotNullWhen(true)] out PlantHolderComponent? plantHolderComponent,
- IEntityManager entityManager,
- bool mustHaveAlivePlant = true)
- {
- plantHolderComponent = null;
-
- if (!entityManager.TryGetComponent(plantHolder, out plantHolderComponent)
- || mustHaveAlivePlant && (plantHolderComponent.Seed == null || plantHolderComponent.Dead))
- return false;
-
- return true;
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- {
- string color;
- if (GuidebookIsAttributePositive ^ Amount < 0.0)
- {
- color = "green";
- }
- else
- {
- color = "red";
- }
- return Loc.GetString("reagent-effect-guidebook-plant-attribute", ("attribute", Loc.GetString(GuidebookAttributeName)), ("amount", Amount.ToString("0.00")), ("colorName", color), ("chance", Probability));
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustHealth.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustHealth.cs
deleted file mode 100644
index af74c17de7..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustHealth.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Content.Server.Botany.Systems;
-using Content.Shared.Chemistry.Reagent;
-
-namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
-{
- public sealed partial class PlantAdjustHealth : PlantAdjustAttribute
- {
- public override string GuidebookAttributeName { get; set; } = "plant-attribute-health";
-
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
- return;
-
- var plantHolder = args.EntityManager.System();
-
- plantHolderComp.Health += Amount;
- plantHolder.CheckHealth(args.SolutionEntity, plantHolderComp);
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustMutationLevel.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustMutationLevel.cs
deleted file mode 100644
index cf0983bc51..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustMutationLevel.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-
-namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
-{
- public sealed partial class PlantAdjustMutationLevel : PlantAdjustAttribute
- {
- public override string GuidebookAttributeName { get; set; } = "plant-attribute-mutation-level";
-
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
- return;
-
- plantHolderComp.MutationLevel += Amount * plantHolderComp.MutationMod;
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustMutationMod.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustMutationMod.cs
deleted file mode 100644
index b43e885388..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustMutationMod.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-
-namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
-{
- [UsedImplicitly]
- public sealed partial class PlantAdjustMutationMod : PlantAdjustAttribute
- {
- public override string GuidebookAttributeName { get; set; } = "plant-attribute-mutation-mod";
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
- return;
-
- plantHolderComp.MutationMod += Amount;
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustNutrition.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustNutrition.cs
deleted file mode 100644
index 68bb59870d..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustNutrition.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Content.Server.Botany.Systems;
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-
-namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
-{
- [UsedImplicitly]
- public sealed partial class PlantAdjustNutrition : PlantAdjustAttribute
- {
- public override string GuidebookAttributeName { get; set; } = "plant-attribute-nutrition";
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager, mustHaveAlivePlant: false))
- return;
-
- var plantHolder = args.EntityManager.System();
-
- plantHolder.AdjustNutrient(args.SolutionEntity, Amount, plantHolderComp);
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustPests.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustPests.cs
deleted file mode 100644
index 9e9787d030..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustPests.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-
-namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
-{
- [UsedImplicitly]
- public sealed partial class PlantAdjustPests : PlantAdjustAttribute
- {
- public override string GuidebookAttributeName { get; set; } = "plant-attribute-pests";
- public override bool GuidebookIsAttributePositive { get; protected set; } = false;
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
- return;
-
- plantHolderComp.PestLevel += Amount;
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustToxins.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustToxins.cs
deleted file mode 100644
index 2279f74910..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustToxins.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-
-namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
-{
- [UsedImplicitly]
- public sealed partial class PlantAdjustToxins : PlantAdjustAttribute
- {
- public override string GuidebookAttributeName { get; set; } = "plant-attribute-toxins";
- public override bool GuidebookIsAttributePositive { get; protected set; } = false;
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
- return;
-
- plantHolderComp.Toxins += Amount;
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustWater.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustWater.cs
deleted file mode 100644
index a1c184d3b7..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustWater.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Content.Server.Botany.Systems;
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-
-namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
-{
- [UsedImplicitly]
- public sealed partial class PlantAdjustWater : PlantAdjustAttribute
- {
- public override string GuidebookAttributeName { get; set; } = "plant-attribute-water";
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager, mustHaveAlivePlant: false))
- return;
-
- var plantHolder = args.EntityManager.System();
-
- plantHolder.AdjustWater(args.SolutionEntity, Amount, plantHolderComp);
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustWeeds.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustWeeds.cs
deleted file mode 100644
index 82958e97a1..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustWeeds.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-
-namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
-{
- [UsedImplicitly]
- public sealed partial class PlantAdjustWeeds : PlantAdjustAttribute
- {
- public override string GuidebookAttributeName { get; set; } = "plant-attribute-weeds";
- public override bool GuidebookIsAttributePositive { get; protected set; } = false;
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
- return;
-
- plantHolderComp.WeedLevel += Amount;
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAffectGrowth.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAffectGrowth.cs
deleted file mode 100644
index e92b1954c0..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAffectGrowth.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Content.Server.Botany.Systems;
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-
-namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
-{
- [UsedImplicitly]
- public sealed partial class PlantAffectGrowth : PlantAdjustAttribute
- {
- public override string GuidebookAttributeName { get; set; } = "plant-attribute-growth";
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
- return;
-
- var plantHolder = args.EntityManager.System();
-
- plantHolder.AffectGrowth(args.SolutionEntity, (int) Amount, plantHolderComp);
- }
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantCryoxadone.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantCryoxadone.cs
deleted file mode 100644
index 55a7e5c97b..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantCryoxadone.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Content.Server.Botany.Components;
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Random;
-
-namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
-{
- [UsedImplicitly]
- [DataDefinition]
- public sealed partial class PlantCryoxadone : ReagentEffect
- {
- public override void Effect(ReagentEffectArgs args)
- {
- if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out PlantHolderComponent? plantHolderComp)
- || plantHolderComp.Seed == null || plantHolderComp.Dead)
- return;
-
- var deviation = 0;
- var seed = plantHolderComp.Seed;
- var random = IoCManager.Resolve();
- if (plantHolderComp.Age > seed.Maturation)
- deviation = (int) Math.Max(seed.Maturation - 1, plantHolderComp.Age - random.Next(7, 10));
- else
- deviation = (int) (seed.Maturation / seed.GrowthStages);
- plantHolderComp.Age -= deviation;
- plantHolderComp.SkipAging++;
- plantHolderComp.ForceUpdate = true;
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-cryoxadone", ("chance", Probability));
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantDiethylamine.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantDiethylamine.cs
deleted file mode 100644
index c98f0be78c..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantDiethylamine.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Content.Server.Botany.Components;
-using Content.Server.Botany.Systems;
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Random;
-
-namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
-{
- [UsedImplicitly]
- [DataDefinition]
- public sealed partial class PlantDiethylamine : ReagentEffect
- {
- public override void Effect(ReagentEffectArgs args)
- {
- if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out PlantHolderComponent? plantHolderComp)
- || plantHolderComp.Seed == null || plantHolderComp.Dead ||
- plantHolderComp.Seed.Immutable)
- return;
-
-
- var plantHolder = args.EntityManager.System();
-
- var random = IoCManager.Resolve();
-
- if (random.Prob(0.1f))
- {
- plantHolder.EnsureUniqueSeed(args.SolutionEntity, plantHolderComp);
- plantHolderComp.Seed.Lifespan++;
- }
-
- if (random.Prob(0.1f))
- {
- plantHolder.EnsureUniqueSeed(args.SolutionEntity, plantHolderComp);
- plantHolderComp.Seed.Endurance++;
- }
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-diethylamine", ("chance", Probability));
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantPhalanximine.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantPhalanximine.cs
deleted file mode 100644
index 7aaca63b7d..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantPhalanximine.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using Content.Server.Botany.Components;
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
-{
- [UsedImplicitly]
- [DataDefinition]
- public sealed partial class PlantPhalanximine : ReagentEffect
- {
- public override void Effect(ReagentEffectArgs args)
- {
- if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out PlantHolderComponent? plantHolderComp)
- || plantHolderComp.Seed == null || plantHolderComp.Dead ||
- plantHolderComp.Seed.Immutable)
- return;
-
- plantHolderComp.Seed.Viable = true;
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-phalanximine", ("chance", Probability));
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/RobustHarvest.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/RobustHarvest.cs
deleted file mode 100644
index 4a01cdf51f..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/RobustHarvest.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using Content.Server.Botany.Components;
-using Content.Server.Botany.Systems;
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Random;
-
-namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
-{
- [UsedImplicitly]
- [DataDefinition]
- public sealed partial class RobustHarvest : ReagentEffect
- {
- [DataField]
- public int PotencyLimit = 50;
-
- [DataField]
- public int PotencyIncrease = 3;
-
- [DataField]
- public int PotencySeedlessThreshold = 30;
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out PlantHolderComponent? plantHolderComp)
- || plantHolderComp.Seed == null || plantHolderComp.Dead ||
- plantHolderComp.Seed.Immutable)
- return;
-
-
- var plantHolder = args.EntityManager.System();
- var random = IoCManager.Resolve();
-
- if (plantHolderComp.Seed.Potency < PotencyLimit)
- {
- plantHolder.EnsureUniqueSeed(args.SolutionEntity, plantHolderComp);
- plantHolderComp.Seed.Potency = Math.Min(plantHolderComp.Seed.Potency + PotencyIncrease, PotencyLimit);
-
- if (plantHolderComp.Seed.Potency > PotencySeedlessThreshold)
- {
- plantHolderComp.Seed.Seedless = true;
- }
- }
- else if (plantHolderComp.Seed.Yield > 1 && random.Prob(0.1f))
- {
- // Too much of a good thing reduces yield
- plantHolder.EnsureUniqueSeed(args.SolutionEntity, plantHolderComp);
- plantHolderComp.Seed.Yield--;
- }
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-robust-harvest", ("seedlesstreshold", PotencySeedlessThreshold), ("limit", PotencyLimit), ("increase", PotencyIncrease), ("chance", Probability));
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs b/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs
deleted file mode 100644
index 8813813c24..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Nutrition.Components;
-using Content.Shared.Nutrition.EntitySystems;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffects
-{
- ///
- /// Default metabolism for drink reagents. Attempts to find a ThirstComponent on the target,
- /// and to update it's thirst values.
- ///
- public sealed partial class SatiateThirst : ReagentEffect
- {
- private const float DefaultHydrationFactor = 3.0f;
-
- /// How much thirst is satiated each metabolism tick. Not currently tied to
- /// rate or anything.
- [DataField("factor")]
- public float HydrationFactor { get; set; } = DefaultHydrationFactor;
-
- /// Satiate thirst if a ThirstComponent can be found
- public override void Effect(ReagentEffectArgs args)
- {
- var uid = args.SolutionEntity;
- if (args.EntityManager.TryGetComponent(uid, out ThirstComponent? thirst))
- args.EntityManager.System().ModifyThirst(uid, thirst, HydrationFactor);
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-satiate-thirst", ("chance", Probability), ("relative", HydrationFactor / DefaultHydrationFactor));
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/StatusEffects/GenericStatusEffect.cs b/Content.Server/Chemistry/ReagentEffects/StatusEffects/GenericStatusEffect.cs
deleted file mode 100644
index 66454b25fd..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/StatusEffects/GenericStatusEffect.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.StatusEffect;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffects.StatusEffects
-{
- ///
- /// Adds a generic status effect to the entity,
- /// not worrying about things like how to affect the time it lasts for
- /// or component fields or anything. Just adds a component to an entity
- /// for a given time. Easy.
- ///
- ///
- /// Can be used for things like adding accents or something. I don't know. Go wild.
- ///
- [UsedImplicitly]
- public sealed partial class GenericStatusEffect : ReagentEffect
- {
- [DataField(required: true)]
- public string Key = default!;
-
- [DataField]
- public string Component = String.Empty;
-
- [DataField]
- public float Time = 2.0f;
-
- ///
- /// true - refresh status effect time, false - accumulate status effect time
- ///
- [DataField]
- public bool Refresh = true;
-
- ///
- /// Should this effect add the status effect, remove time from it, or set its cooldown?
- ///
- [DataField]
- public StatusEffectMetabolismType Type = StatusEffectMetabolismType.Add;
-
- public override void Effect(ReagentEffectArgs args)
- {
- var statusSys = args.EntityManager.EntitySysManager.GetEntitySystem();
-
- var time = Time;
- time *= args.Scale;
-
- if (Type == StatusEffectMetabolismType.Add && Component != String.Empty)
- {
- statusSys.TryAddStatusEffect(args.SolutionEntity, Key, TimeSpan.FromSeconds(time), Refresh, Component);
- }
- else if (Type == StatusEffectMetabolismType.Remove)
- {
- statusSys.TryRemoveTime(args.SolutionEntity, Key, TimeSpan.FromSeconds(time));
- }
- else if (Type == StatusEffectMetabolismType.Set)
- {
- statusSys.TrySetTime(args.SolutionEntity, Key, TimeSpan.FromSeconds(time));
- }
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString(
- "reagent-effect-guidebook-status-effect",
- ("chance", Probability),
- ("type", Type),
- ("time", Time),
- ("key", $"reagent-effect-status-effect-{Key}"));
- }
-
- public enum StatusEffectMetabolismType
- {
- Add,
- Remove,
- Set
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/StatusEffects/Jitter.cs b/Content.Server/Chemistry/ReagentEffects/StatusEffects/Jitter.cs
deleted file mode 100644
index 7ee70957b7..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/StatusEffects/Jitter.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Jittering;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffects.StatusEffects
-{
- ///
- /// Adds the jitter status effect to a mob.
- /// This doesn't use generic status effects because it needs to
- /// take in some parameters that JitterSystem needs.
- ///
- public sealed partial class Jitter : ReagentEffect
- {
- [DataField]
- public float Amplitude = 10.0f;
-
- [DataField]
- public float Frequency = 4.0f;
-
- [DataField]
- public float Time = 2.0f;
-
- ///
- /// true - refresh jitter time, false - accumulate jitter time
- ///
- [DataField]
- public bool Refresh = true;
-
- public override void Effect(ReagentEffectArgs args)
- {
- var time = Time;
- time *= args.Scale;
-
- args.EntityManager.EntitySysManager.GetEntitySystem()
- .DoJitter(args.SolutionEntity, TimeSpan.FromSeconds(time), Refresh, Amplitude, Frequency);
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) =>
- Loc.GetString("reagent-effect-guidebook-jittering", ("chance", Probability));
- }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/WashCreamPieReaction.cs b/Content.Server/Chemistry/ReagentEffects/WashCreamPieReaction.cs
deleted file mode 100644
index a71a42c25f..0000000000
--- a/Content.Server/Chemistry/ReagentEffects/WashCreamPieReaction.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Content.Server.Nutrition.EntitySystems;
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Nutrition.Components;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffects
-{
- [UsedImplicitly]
- public sealed partial class WashCreamPieReaction : ReagentEffect
- {
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-wash-cream-pie-reaction", ("chance", Probability));
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out CreamPiedComponent? creamPied)) return;
-
- args.EntityManager.System().SetCreamPied(args.SolutionEntity, creamPied, false);
- }
- }
-}
diff --git a/Content.Server/EntityEffects/EffectConditions/BodyTemperature.cs b/Content.Server/EntityEffects/EffectConditions/BodyTemperature.cs
new file mode 100644
index 0000000000..9f68bec918
--- /dev/null
+++ b/Content.Server/EntityEffects/EffectConditions/BodyTemperature.cs
@@ -0,0 +1,35 @@
+using Content.Server.Temperature.Components;
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.EffectConditions;
+
+///
+/// Requires the target entity to be above or below a certain temperature.
+/// Used for things like cryoxadone and pyroxadone.
+///
+public sealed partial class Temperature : EntityEffectCondition
+{
+ [DataField]
+ public float Min = 0;
+
+ [DataField]
+ public float Max = float.PositiveInfinity;
+ public override bool Condition(EntityEffectBaseArgs args)
+ {
+ if (args.EntityManager.TryGetComponent(args.TargetEntity, out TemperatureComponent? temp))
+ {
+ if (temp.CurrentTemperature > Min && temp.CurrentTemperature < Max)
+ return true;
+ }
+
+ return false;
+ }
+
+ public override string GuidebookExplanation(IPrototypeManager prototype)
+ {
+ return Loc.GetString("reagent-effect-condition-guidebook-body-temperature",
+ ("max", float.IsPositiveInfinity(Max) ? (float) int.MaxValue : Max),
+ ("min", Min));
+ }
+}
diff --git a/Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs b/Content.Server/EntityEffects/EffectConditions/HasTagCondition.cs
similarity index 76%
rename from Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs
rename to Content.Server/EntityEffects/EffectConditions/HasTagCondition.cs
index d185c7e0a7..b0428b41bf 100644
--- a/Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs
+++ b/Content.Server/EntityEffects/EffectConditions/HasTagCondition.cs
@@ -1,13 +1,13 @@
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Content.Shared.Tag;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Server.Chemistry.ReagentEffectConditions;
+namespace Content.Server.EntityEffects.EffectConditions;
[UsedImplicitly]
-public sealed partial class HasTag : ReagentEffectCondition
+public sealed partial class HasTag : EntityEffectCondition
{
[DataField(customTypeSerializer: typeof(PrototypeIdSerializer))]
public string Tag = default!;
@@ -15,9 +15,9 @@ public sealed partial class HasTag : ReagentEffectCondition
[DataField]
public bool Invert = false;
- public override bool Condition(ReagentEffectArgs args)
+ public override bool Condition(EntityEffectBaseArgs args)
{
- if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var tag))
+ if (args.EntityManager.TryGetComponent(args.TargetEntity, out var tag))
return args.EntityManager.System().HasTag(tag, Tag) ^ Invert;
return false;
diff --git a/Content.Server/EntityEffects/EffectConditions/JobCondition.cs b/Content.Server/EntityEffects/EffectConditions/JobCondition.cs
new file mode 100644
index 0000000000..20d67d6de0
--- /dev/null
+++ b/Content.Server/EntityEffects/EffectConditions/JobCondition.cs
@@ -0,0 +1,49 @@
+using System.Linq;
+using Content.Shared.EntityEffects;
+using Content.Shared.Mobs;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Localizations;
+using Robust.Shared.Prototypes;
+using Content.Shared.Mind;
+using Content.Shared.Mind.Components;
+using Content.Shared.Roles;
+using Content.Shared.Roles.Jobs;
+using Content.Shared.Station;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using Robust.Shared.IoC;
+
+namespace Content.Server.EntityEffects.EffectConditions;
+
+public sealed partial class JobCondition : EntityEffectCondition
+{
+ [DataField(required: true)] public List> Job;
+
+ public override bool Condition(EntityEffectBaseArgs args)
+ {
+ args.EntityManager.TryGetComponent(args.TargetEntity, out var mindContainer);
+ if (mindContainer != null && mindContainer.Mind != null)
+ {
+ var prototypeManager = IoCManager.Resolve();
+ if (args.EntityManager.TryGetComponent(mindContainer?.Mind, out var comp) && prototypeManager.TryIndex(comp.Prototype, out var prototype))
+ {
+ foreach (var jobId in Job)
+ {
+ if (prototype.ID == jobId)
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public override string GuidebookExplanation(IPrototypeManager prototype)
+ {
+ var localizedNames = Job.Select(jobId => prototype.Index(jobId).LocalizedName).ToList();
+ return Loc.GetString("reagent-effect-condition-guidebook-job-condition", ("job", ContentLocalizationManager.FormatListToOr(localizedNames)));
+ }
+}
+
+
diff --git a/Content.Server/EntityEffects/EffectConditions/MobStateCondition.cs b/Content.Server/EntityEffects/EffectConditions/MobStateCondition.cs
new file mode 100644
index 0000000000..d676b29bf9
--- /dev/null
+++ b/Content.Server/EntityEffects/EffectConditions/MobStateCondition.cs
@@ -0,0 +1,29 @@
+using Content.Shared.EntityEffects;
+using Content.Shared.Mobs;
+using Content.Shared.Mobs.Components;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.EffectConditions;
+
+public sealed partial class MobStateCondition : EntityEffectCondition
+{
+ [DataField]
+ public MobState Mobstate = MobState.Alive;
+
+ public override bool Condition(EntityEffectBaseArgs args)
+ {
+ if (args.EntityManager.TryGetComponent(args.TargetEntity, out MobStateComponent? mobState))
+ {
+ if (mobState.CurrentState == Mobstate)
+ return true;
+ }
+
+ return false;
+ }
+
+ public override string GuidebookExplanation(IPrototypeManager prototype)
+ {
+ return Loc.GetString("reagent-effect-condition-guidebook-mob-state-condition", ("state", Mobstate));
+ }
+}
+
diff --git a/Content.Server/EntityEffects/EffectConditions/OrganType.cs b/Content.Server/EntityEffects/EffectConditions/OrganType.cs
new file mode 100644
index 0000000000..fc52608fab
--- /dev/null
+++ b/Content.Server/EntityEffects/EffectConditions/OrganType.cs
@@ -0,0 +1,53 @@
+using Content.Server.Body.Components;
+using Content.Shared.Body.Prototypes;
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Server.EntityEffects.EffectConditions;
+
+///
+/// Requires that the metabolizing organ is or is not tagged with a certain MetabolizerType
+///
+public sealed partial class OrganType : EntityEffectCondition
+{
+ [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string Type = default!;
+
+ ///
+ /// Does this condition pass when the organ has the type, or when it doesn't have the type?
+ ///
+ [DataField]
+ public bool ShouldHave = true;
+
+ public override bool Condition(EntityEffectBaseArgs args)
+ {
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (reagentArgs.OrganEntity == null)
+ return false;
+
+ return Condition(reagentArgs.OrganEntity.Value, reagentArgs.EntityManager);
+ }
+
+ // TODO: Someone needs to figure out how to do this for non-reagent effects.
+ throw new NotImplementedException();
+ }
+
+ public bool Condition(Entity metabolizer, IEntityManager entMan)
+ {
+ metabolizer.Comp ??= entMan.GetComponentOrNull(metabolizer.Owner);
+ if (metabolizer.Comp != null
+ && metabolizer.Comp.MetabolizerTypes != null
+ && metabolizer.Comp.MetabolizerTypes.Contains(Type))
+ return ShouldHave;
+ return !ShouldHave;
+ }
+
+ public override string GuidebookExplanation(IPrototypeManager prototype)
+ {
+ return Loc.GetString("reagent-effect-condition-guidebook-organ-type",
+ ("name", prototype.Index(Type).LocalizedName),
+ ("shouldhave", ShouldHave));
+ }
+}
diff --git a/Content.Server/EntityEffects/EffectConditions/ReagentThreshold.cs b/Content.Server/EntityEffects/EffectConditions/ReagentThreshold.cs
new file mode 100644
index 0000000000..6cbd7b4ea5
--- /dev/null
+++ b/Content.Server/EntityEffects/EffectConditions/ReagentThreshold.cs
@@ -0,0 +1,57 @@
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.EffectConditions;
+
+///
+/// Used for implementing reagent effects that require a certain amount of reagent before it should be applied.
+/// For instance, overdoses.
+///
+/// This can also trigger on -other- reagents, not just the one metabolizing. By default, it uses the
+/// one being metabolized.
+///
+public sealed partial class ReagentThreshold : EntityEffectCondition
+{
+ [DataField]
+ public FixedPoint2 Min = FixedPoint2.Zero;
+
+ [DataField]
+ public FixedPoint2 Max = FixedPoint2.MaxValue;
+
+ // TODO use ReagentId
+ [DataField]
+ public string? Reagent;
+
+ public override bool Condition(EntityEffectBaseArgs args)
+ {
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ var reagent = Reagent ?? reagentArgs.Reagent?.ID;
+ if (reagent == null)
+ return true; // No condition to apply.
+
+ var quant = FixedPoint2.Zero;
+ if (reagentArgs.Source != null)
+ quant = reagentArgs.Source.GetTotalPrototypeQuantity(reagent);
+
+ return quant >= Min && quant <= Max;
+ }
+
+ // TODO: Someone needs to figure out how to do this for non-reagent effects.
+ throw new NotImplementedException();
+ }
+
+ public override string GuidebookExplanation(IPrototypeManager prototype)
+ {
+ ReagentPrototype? reagentProto = null;
+ if (Reagent is not null)
+ prototype.TryIndex(Reagent, out reagentProto);
+
+ return Loc.GetString("reagent-effect-condition-guidebook-reagent-threshold",
+ ("reagent", reagentProto?.LocalizedName ?? Loc.GetString("reagent-effect-condition-guidebook-this-reagent")),
+ ("max", Max == FixedPoint2.MaxValue ? (float) int.MaxValue : Max.Float()),
+ ("min", Min.Float()));
+ }
+}
diff --git a/Content.Server/EntityEffects/EffectConditions/SolutionTemperature.cs b/Content.Server/EntityEffects/EffectConditions/SolutionTemperature.cs
new file mode 100644
index 0000000000..b964435db5
--- /dev/null
+++ b/Content.Server/EntityEffects/EffectConditions/SolutionTemperature.cs
@@ -0,0 +1,40 @@
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.EffectConditions;
+
+///
+/// Requires the solution to be above or below a certain temperature.
+/// Used for things like explosives.
+///
+public sealed partial class SolutionTemperature : EntityEffectCondition
+{
+ [DataField]
+ public float Min = 0.0f;
+
+ [DataField]
+ public float Max = float.PositiveInfinity;
+ public override bool Condition(EntityEffectBaseArgs args)
+ {
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (reagentArgs.Source == null)
+ return false;
+ if (reagentArgs.Source.Temperature < Min)
+ return false;
+ if (reagentArgs.Source.Temperature > Max)
+ return false;
+ return true;
+ }
+
+ // TODO: Someone needs to figure out how to do this for non-reagent effects.
+ throw new NotImplementedException();
+ }
+
+ public override string GuidebookExplanation(IPrototypeManager prototype)
+ {
+ return Loc.GetString("reagent-effect-condition-guidebook-solution-temperature",
+ ("max", float.IsPositiveInfinity(Max) ? (float) int.MaxValue : Max),
+ ("min", Min));
+ }
+}
diff --git a/Content.Server/EntityEffects/EffectConditions/TotalDamage.cs b/Content.Server/EntityEffects/EffectConditions/TotalDamage.cs
new file mode 100644
index 0000000000..2d039a1ac8
--- /dev/null
+++ b/Content.Server/EntityEffects/EffectConditions/TotalDamage.cs
@@ -0,0 +1,34 @@
+using Content.Shared.EntityEffects;
+using Content.Shared.Damage;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.EffectConditions;
+
+public sealed partial class TotalDamage : EntityEffectCondition
+{
+ [DataField]
+ public FixedPoint2 Max = FixedPoint2.MaxValue;
+
+ [DataField]
+ public FixedPoint2 Min = FixedPoint2.Zero;
+
+ public override bool Condition(EntityEffectBaseArgs args)
+ {
+ if (args.EntityManager.TryGetComponent(args.TargetEntity, out DamageableComponent? damage))
+ {
+ var total = damage.TotalDamage;
+ if (total > Min && total < Max)
+ return true;
+ }
+
+ return false;
+ }
+
+ public override string GuidebookExplanation(IPrototypeManager prototype)
+ {
+ return Loc.GetString("reagent-effect-condition-guidebook-total-damage",
+ ("max", Max == FixedPoint2.MaxValue ? (float) int.MaxValue : Max.Float()),
+ ("min", Min.Float()));
+ }
+}
diff --git a/Content.Server/EntityEffects/EffectConditions/TotalHunger.cs b/Content.Server/EntityEffects/EffectConditions/TotalHunger.cs
new file mode 100644
index 0000000000..84ad4c2240
--- /dev/null
+++ b/Content.Server/EntityEffects/EffectConditions/TotalHunger.cs
@@ -0,0 +1,34 @@
+using Content.Shared.EntityEffects;
+using Content.Shared.Nutrition.Components;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.EffectConditions;
+
+public sealed partial class Hunger : EntityEffectCondition
+{
+ [DataField]
+ public float Max = float.PositiveInfinity;
+
+ [DataField]
+ public float Min = 0;
+
+ public override bool Condition(EntityEffectBaseArgs args)
+ {
+ if (args.EntityManager.TryGetComponent(args.TargetEntity, out HungerComponent? hunger))
+ {
+ var total = hunger.CurrentHunger;
+ if (total > Min && total < Max)
+ return true;
+ }
+
+ return false;
+ }
+
+ public override string GuidebookExplanation(IPrototypeManager prototype)
+ {
+ return Loc.GetString("reagent-effect-condition-guidebook-total-hunger",
+ ("max", float.IsPositiveInfinity(Max) ? (float) int.MaxValue : Max),
+ ("min", Min));
+ }
+}
diff --git a/Content.Server/Chemistry/ReagentEffects/ActivateArtifact.cs b/Content.Server/EntityEffects/Effects/ActivateArtifact.cs
similarity index 56%
rename from Content.Server/Chemistry/ReagentEffects/ActivateArtifact.cs
rename to Content.Server/EntityEffects/Effects/ActivateArtifact.cs
index 41af90f60a..3e97388499 100644
--- a/Content.Server/Chemistry/ReagentEffects/ActivateArtifact.cs
+++ b/Content.Server/EntityEffects/Effects/ActivateArtifact.cs
@@ -1,15 +1,16 @@
-using Content.Server.Xenoarchaeology.XenoArtifacts;
+using Content.Server.Xenoarchaeology.XenoArtifacts;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
+using Content.Shared.EntityEffects;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects;
-public sealed partial class ActivateArtifact : ReagentEffect
+public sealed partial class ActivateArtifact : EntityEffect
{
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
var artifact = args.EntityManager.EntitySysManager.GetEntitySystem();
- artifact.TryActivateArtifact(args.SolutionEntity);
+ artifact.TryActivateArtifact(args.TargetEntity);
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) =>
diff --git a/Content.Server/EntityEffects/Effects/AddToSolutionReaction.cs b/Content.Server/EntityEffects/Effects/AddToSolutionReaction.cs
new file mode 100644
index 0000000000..10fb667ca4
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/AddToSolutionReaction.cs
@@ -0,0 +1,38 @@
+using Content.Server.Chemistry.Containers.EntitySystems;
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.Effects
+{
+ [UsedImplicitly]
+ public sealed partial class AddToSolutionReaction : EntityEffect
+ {
+ [DataField("solution")]
+ private string _solution = "reagents";
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (args is EntityEffectReagentArgs reagentArgs) {
+ if (reagentArgs.Reagent == null)
+ return;
+
+ // TODO see if this is correct
+ var solutionContainerSystem = reagentArgs.EntityManager.System();
+ if (!solutionContainerSystem.TryGetSolution(reagentArgs.TargetEntity, _solution, out var solutionContainer))
+ return;
+
+ if (solutionContainerSystem.TryAddReagent(solutionContainer.Value, reagentArgs.Reagent.ID, reagentArgs.Quantity, out var accepted))
+ reagentArgs.Source?.RemoveReagent(reagentArgs.Reagent.ID, accepted);
+
+ return;
+ }
+
+ // TODO: Someone needs to figure out how to do this for non-reagent effects.
+ throw new NotImplementedException();
+ }
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) =>
+ Loc.GetString("reagent-effect-guidebook-add-to-solution-reaction", ("chance", Probability));
+ }
+}
diff --git a/Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs b/Content.Server/EntityEffects/Effects/AdjustAlert.cs
similarity index 76%
rename from Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs
rename to Content.Server/EntityEffects/Effects/AdjustAlert.cs
index 40858176bd..3bf57b309d 100644
--- a/Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs
+++ b/Content.Server/EntityEffects/Effects/AdjustAlert.cs
@@ -1,11 +1,11 @@
-using Content.Shared.Alert;
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Alert;
+using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects;
-public sealed partial class AdjustAlert : ReagentEffect
+public sealed partial class AdjustAlert : EntityEffect
{
///
/// The specific Alert that will be adjusted
@@ -34,15 +34,15 @@ public sealed partial class AdjustAlert : ReagentEffect
//JUSTIFICATION: This just changes some visuals, doesn't need to be documented.
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => null;
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
var alertSys = args.EntityManager.EntitySysManager.GetEntitySystem();
- if (!args.EntityManager.HasComponent(args.SolutionEntity))
+ if (!args.EntityManager.HasComponent(args.TargetEntity))
return;
if (Clear && Time <= 0)
{
- alertSys.ClearAlert(args.SolutionEntity, AlertType);
+ alertSys.ClearAlert(args.TargetEntity, AlertType);
}
else
{
@@ -52,7 +52,7 @@ public sealed partial class AdjustAlert : ReagentEffect
if ((ShowCooldown || Clear) && Time > 0)
cooldown = (timing.CurTime, timing.CurTime + TimeSpan.FromSeconds(Time));
- alertSys.ShowAlert(args.SolutionEntity, AlertType, cooldown: cooldown, autoRemove: Clear, showCooldown: ShowCooldown);
+ alertSys.ShowAlert(args.TargetEntity, AlertType, cooldown: cooldown, autoRemove: Clear, showCooldown: ShowCooldown);
}
}
diff --git a/Content.Server/Chemistry/ReagentEffects/AdjustReagent.cs b/Content.Server/EntityEffects/Effects/AdjustReagent.cs
similarity index 58%
rename from Content.Server/Chemistry/ReagentEffects/AdjustReagent.cs
rename to Content.Server/EntityEffects/Effects/AdjustReagent.cs
index 16d69edd9a..71117d6ec5 100644
--- a/Content.Server/Chemistry/ReagentEffects/AdjustReagent.cs
+++ b/Content.Server/EntityEffects/Effects/AdjustReagent.cs
@@ -1,14 +1,15 @@
using Content.Shared.Body.Prototypes;
using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Server.Chemistry.ReagentEffects
+namespace Content.Server.EntityEffects.Effects
{
[UsedImplicitly]
- public sealed partial class AdjustReagent : ReagentEffect
+ public sealed partial class AdjustReagent : EntityEffect
{
///
/// The reagent ID to remove. Only one of this and should be active.
@@ -27,36 +28,43 @@ namespace Content.Server.Chemistry.ReagentEffects
[DataField(required: true)]
public FixedPoint2 Amount = default!;
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
- if (args.Source == null)
- return;
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (reagentArgs.Source == null)
+ return;
- var amount = Amount;
- amount *= args.Scale;
+ var amount = Amount;
+ amount *= reagentArgs.Scale;
- if (Reagent != null)
- {
- if (amount < 0 && args.Source.ContainsPrototype(Reagent))
- args.Source.RemoveReagent(Reagent, -amount);
- if (amount > 0)
- args.Source.AddReagent(Reagent, amount);
- }
- else if (Group != null)
- {
- var prototypeMan = IoCManager.Resolve();
- foreach (var quant in args.Source.Contents.ToArray())
+ if (Reagent != null)
{
- var proto = prototypeMan.Index(quant.Reagent.Prototype);
- if (proto.Metabolisms != null && proto.Metabolisms.ContainsKey(Group))
+ if (amount < 0 && reagentArgs.Source.ContainsPrototype(Reagent))
+ reagentArgs.Source.RemoveReagent(Reagent, -amount);
+ if (amount > 0)
+ reagentArgs.Source.AddReagent(Reagent, amount);
+ }
+ else if (Group != null)
+ {
+ var prototypeMan = IoCManager.Resolve();
+ foreach (var quant in reagentArgs.Source.Contents.ToArray())
{
- if (amount < 0)
- args.Source.RemoveReagent(quant.Reagent, amount);
- if (amount > 0)
- args.Source.AddReagent(quant.Reagent, amount);
+ var proto = prototypeMan.Index(quant.Reagent.Prototype);
+ if (proto.Metabolisms != null && proto.Metabolisms.ContainsKey(Group))
+ {
+ if (amount < 0)
+ reagentArgs.Source.RemoveReagent(quant.Reagent, amount);
+ if (amount > 0)
+ reagentArgs.Source.AddReagent(quant.Reagent, amount);
+ }
}
}
+ return;
}
+
+ // TODO: Someone needs to figure out how to do this for non-reagent effects.
+ throw new NotImplementedException();
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
diff --git a/Content.Server/Chemistry/ReagentEffects/AdjustTemperature.cs b/Content.Server/EntityEffects/Effects/AdjustTemperature.cs
similarity index 56%
rename from Content.Server/Chemistry/ReagentEffects/AdjustTemperature.cs
rename to Content.Server/EntityEffects/Effects/AdjustTemperature.cs
index 9b97572067..3c5c77c309 100644
--- a/Content.Server/Chemistry/ReagentEffects/AdjustTemperature.cs
+++ b/Content.Server/EntityEffects/Effects/AdjustTemperature.cs
@@ -1,11 +1,11 @@
using Content.Server.Temperature.Components;
using Content.Server.Temperature.Systems;
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
-namespace Content.Server.Chemistry.ReagentEffects
+namespace Content.Server.EntityEffects.Effects
{
- public sealed partial class AdjustTemperature : ReagentEffect
+ public sealed partial class AdjustTemperature : EntityEffect
{
[DataField]
public float Amount;
@@ -16,16 +16,19 @@ namespace Content.Server.Chemistry.ReagentEffects
("deltasign", MathF.Sign(Amount)),
("amount", MathF.Abs(Amount)));
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
- if (args.EntityManager.TryGetComponent(args.SolutionEntity, out TemperatureComponent? temp))
+ if (args.EntityManager.TryGetComponent(args.TargetEntity, out TemperatureComponent? temp))
{
var sys = args.EntityManager.EntitySysManager.GetEntitySystem();
var amount = Amount;
- amount *= args.Scale;
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ amount *= reagentArgs.Scale.Float();
+ }
- sys.ChangeHeat(args.SolutionEntity, amount, true, temp);
+ sys.ChangeHeat(args.TargetEntity, amount, true, temp);
}
}
}
diff --git a/Content.Server/EntityEffects/Effects/AreaReactionEffect.cs b/Content.Server/EntityEffects/Effects/AreaReactionEffect.cs
new file mode 100644
index 0000000000..481f1fc27c
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/AreaReactionEffect.cs
@@ -0,0 +1,91 @@
+using Content.Server.Fluids.EntitySystems;
+using Content.Shared.Audio;
+using Content.Shared.Coordinates.Helpers;
+using Content.Shared.Database;
+using Content.Shared.EntityEffects;
+using Content.Shared.FixedPoint;
+using Content.Shared.Maps;
+using JetBrains.Annotations;
+using Robust.Server.GameObjects;
+using Robust.Shared.Audio;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Map;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Server.EntityEffects.Effects;
+
+///
+/// Basically smoke and foam reactions.
+///
+[UsedImplicitly]
+[DataDefinition]
+public sealed partial class AreaReactionEffect : EntityEffect
+{
+ ///
+ /// How many seconds will the effect stay, counting after fully spreading.
+ ///
+ [DataField("duration")] private float _duration = 10;
+
+ ///
+ /// How many units of reaction for 1 smoke entity.
+ ///
+ [DataField] public FixedPoint2 OverflowThreshold = FixedPoint2.New(2.5);
+
+ ///
+ /// The entity prototype that will be spawned as the effect.
+ ///
+ [DataField("prototypeId", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))]
+ private string _prototypeId = default!;
+
+ ///
+ /// Sound that will get played when this reaction effect occurs.
+ ///
+ [DataField("sound", required: true)] private SoundSpecifier _sound = default!;
+
+ public override bool ShouldLog => true;
+
+ protected override string ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-area-reaction",
+ ("duration", _duration)
+ );
+
+ public override LogImpact LogImpact => LogImpact.High;
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (reagentArgs.Source == null)
+ return;
+
+ var spreadAmount = (int) Math.Max(0, Math.Ceiling((reagentArgs.Quantity / OverflowThreshold).Float()));
+ var splitSolution = reagentArgs.Source.SplitSolution(reagentArgs.Source.Volume);
+ var transform = reagentArgs.EntityManager.GetComponent(reagentArgs.TargetEntity);
+ var mapManager = IoCManager.Resolve();
+ var mapSys = reagentArgs.EntityManager.System();
+ var sys = reagentArgs.EntityManager.System();
+ var mapCoords = sys.GetMapCoordinates(reagentArgs.TargetEntity, xform: transform);
+
+ if (!mapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid) ||
+ !mapSys.TryGetTileRef(gridUid, grid, transform.Coordinates, out var tileRef) ||
+ tileRef.Tile.IsSpace())
+ {
+ return;
+ }
+
+ var coords = mapSys.MapToGrid(gridUid, mapCoords);
+ var ent = reagentArgs.EntityManager.SpawnEntity(_prototypeId, coords.SnapToGrid());
+
+ var smoke = reagentArgs.EntityManager.System();
+ smoke.StartSmoke(ent, splitSolution, _duration, spreadAmount);
+
+ var audio = reagentArgs.EntityManager.System();
+ audio.PlayPvs(_sound, reagentArgs.TargetEntity, AudioHelpers.WithVariation(0.125f));
+ return;
+ }
+
+ // TODO: Someone needs to figure out how to do this for non-reagent effects.
+ throw new NotImplementedException();
+ }
+}
diff --git a/Content.Server/Chemistry/ReagentEffects/CauseZombieInfection.cs b/Content.Server/EntityEffects/Effects/CauseZombieInfection.cs
similarity index 65%
rename from Content.Server/Chemistry/ReagentEffects/CauseZombieInfection.cs
rename to Content.Server/EntityEffects/Effects/CauseZombieInfection.cs
index 96c57f7465..b71e6a30e0 100644
--- a/Content.Server/Chemistry/ReagentEffects/CauseZombieInfection.cs
+++ b/Content.Server/EntityEffects/Effects/CauseZombieInfection.cs
@@ -1,22 +1,21 @@
using Content.Server.Zombies;
-using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Configuration;
+using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
using Content.Shared.Zombies;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects;
-public sealed partial class CauseZombieInfection : ReagentEffect
+public sealed partial class CauseZombieInfection : EntityEffect
{
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-cause-zombie-infection", ("chance", Probability));
// Adds the Zombie Infection Components
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
var entityManager = args.EntityManager;
- entityManager.EnsureComponent(args.SolutionEntity);
- entityManager.EnsureComponent(args.SolutionEntity);
+ entityManager.EnsureComponent(args.TargetEntity);
+ entityManager.EnsureComponent(args.TargetEntity);
}
}
diff --git a/Content.Server/EntityEffects/Effects/ChemCleanBloodstream.cs b/Content.Server/EntityEffects/Effects/ChemCleanBloodstream.cs
new file mode 100644
index 0000000000..b7f6f0dc88
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/ChemCleanBloodstream.cs
@@ -0,0 +1,40 @@
+using Content.Server.Body.Systems;
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.Effects;
+
+///
+/// Basically smoke and foam reactions.
+///
+[UsedImplicitly]
+public sealed partial class ChemCleanBloodstream : EntityEffect
+{
+ [DataField]
+ public float CleanseRate = 3.0f;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-chem-clean-bloodstream", ("chance", Probability));
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+
+ var cleanseRate = CleanseRate;
+
+ var bloodstreamSys = args.EntityManager.System();
+
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (reagentArgs.Source == null || reagentArgs.Reagent == null)
+ return;
+
+ cleanseRate *= reagentArgs.Scale.Float();
+ bloodstreamSys.FlushChemicals(args.TargetEntity, reagentArgs.Reagent.ID, cleanseRate);
+ }
+ else
+ {
+ bloodstreamSys.FlushChemicals(args.TargetEntity, "", cleanseRate);
+ }
+ }
+}
diff --git a/Content.Server/EntityEffects/Effects/ChemHealEyeDamage.cs b/Content.Server/EntityEffects/Effects/ChemHealEyeDamage.cs
new file mode 100644
index 0000000000..42fc9ffa9f
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/ChemHealEyeDamage.cs
@@ -0,0 +1,31 @@
+using Content.Shared.EntityEffects;
+using Content.Shared.Eye.Blinding.Systems;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.Effects;
+
+///
+/// Heal or apply eye damage
+///
+[UsedImplicitly]
+public sealed partial class ChemHealEyeDamage : EntityEffect
+{
+ ///
+ /// How much eye damage to add.
+ ///
+ [DataField]
+ public int Amount = -1;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-cure-eye-damage", ("chance", Probability), ("deltasign", MathF.Sign(Amount)));
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (args is EntityEffectReagentArgs reagentArgs)
+ if (reagentArgs.Scale != 1f) // huh?
+ return;
+
+ args.EntityManager.EntitySysManager.GetEntitySystem().AdjustEyeDamage(args.TargetEntity, Amount);
+ }
+}
diff --git a/Content.Server/Chemistry/ReagentEffects/ChemVomit.cs b/Content.Server/EntityEffects/Effects/ChemVomit.cs
similarity index 65%
rename from Content.Server/Chemistry/ReagentEffects/ChemVomit.cs
rename to Content.Server/EntityEffects/Effects/ChemVomit.cs
index 851c0adf5f..0d1bac87d1 100644
--- a/Content.Server/Chemistry/ReagentEffects/ChemVomit.cs
+++ b/Content.Server/EntityEffects/Effects/ChemVomit.cs
@@ -1,15 +1,15 @@
using Content.Server.Medical;
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
-namespace Content.Server.Chemistry.ReagentEffects
+namespace Content.Server.EntityEffects.Effects
{
///
/// Forces you to vomit.
///
[UsedImplicitly]
- public sealed partial class ChemVomit : ReagentEffect
+ public sealed partial class ChemVomit : EntityEffect
{
/// How many units of thirst to add each time we vomit
[DataField]
@@ -21,14 +21,15 @@ namespace Content.Server.Chemistry.ReagentEffects
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-chem-vomit", ("chance", Probability));
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
- if (args.Scale != 1f)
- return;
+ if (args is EntityEffectReagentArgs reagentArgs)
+ if (reagentArgs.Scale != 1f)
+ return;
var vomitSys = args.EntityManager.EntitySysManager.GetEntitySystem();
- vomitSys.Vomit(args.SolutionEntity, ThirstAmount, HungerAmount);
+ vomitSys.Vomit(args.TargetEntity, ThirstAmount, HungerAmount);
}
}
}
diff --git a/Content.Server/Chemistry/ReactionEffects/CreateEntityReactionEffect.cs b/Content.Server/EntityEffects/Effects/CreateEntityReactionEffect.cs
similarity index 80%
rename from Content.Server/Chemistry/ReactionEffects/CreateEntityReactionEffect.cs
rename to Content.Server/EntityEffects/Effects/CreateEntityReactionEffect.cs
index 0d5acc1722..5288cb587d 100644
--- a/Content.Server/Chemistry/ReactionEffects/CreateEntityReactionEffect.cs
+++ b/Content.Server/EntityEffects/Effects/CreateEntityReactionEffect.cs
@@ -1,12 +1,11 @@
-using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.GameObjects;
+using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Server.Chemistry.ReactionEffects;
+namespace Content.Server.EntityEffects.Effects;
[DataDefinition]
-public sealed partial class CreateEntityReactionEffect : ReagentEffect
+public sealed partial class CreateEntityReactionEffect : EntityEffect
{
///
/// What entity to create.
@@ -26,15 +25,17 @@ public sealed partial class CreateEntityReactionEffect : ReagentEffect
("entname", IoCManager.Resolve().Index(Entity).Name),
("amount", Number));
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
- var transform = args.EntityManager.GetComponent(args.SolutionEntity);
+ var transform = args.EntityManager.GetComponent(args.TargetEntity);
var transformSystem = args.EntityManager.System();
- var quantity = Number * args.Quantity.Int();
+ var quantity = (int)Number;
+ if (args is EntityEffectReagentArgs reagentArgs)
+ quantity *= reagentArgs.Quantity.Int();
for (var i = 0; i < quantity; i++)
{
- var uid = args.EntityManager.SpawnEntity(Entity, transformSystem.GetMapCoordinates(args.SolutionEntity, xform: transform));
+ var uid = args.EntityManager.SpawnEntity(Entity, transformSystem.GetMapCoordinates(args.TargetEntity, xform: transform));
transformSystem.AttachToGridOrMap(uid);
// TODO figure out how to properly spawn inside of containers
diff --git a/Content.Server/Chemistry/ReagentEffects/CreateGas.cs b/Content.Server/EntityEffects/Effects/CreateGas.cs
similarity index 62%
rename from Content.Server/Chemistry/ReagentEffects/CreateGas.cs
rename to Content.Server/EntityEffects/Effects/CreateGas.cs
index c1da3c48db..fb57a43c48 100644
--- a/Content.Server/Chemistry/ReagentEffects/CreateGas.cs
+++ b/Content.Server/EntityEffects/Effects/CreateGas.cs
@@ -1,12 +1,12 @@
-using Content.Server.Atmos.EntitySystems;
+using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos;
-using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
+using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects;
-public sealed partial class CreateGas : ReagentEffect
+public sealed partial class CreateGas : EntityEffect
{
[DataField(required: true)]
public Gas Gas = default!;
@@ -31,15 +31,22 @@ public sealed partial class CreateGas : ReagentEffect
public override LogImpact LogImpact => LogImpact.High;
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
var atmosSys = args.EntityManager.EntitySysManager.GetEntitySystem();
- var tileMix = atmosSys.GetContainingMixture(args.SolutionEntity, false, true);
+ var tileMix = atmosSys.GetContainingMixture(args.TargetEntity, false, true);
if (tileMix != null)
{
- tileMix.AdjustMoles(Gas, args.Quantity.Float() * Multiplier);
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ tileMix.AdjustMoles(Gas, reagentArgs.Quantity.Float() * Multiplier);
+ }
+ else
+ {
+ tileMix.AdjustMoles(Gas, Multiplier);
+ }
}
}
}
diff --git a/Content.Server/Chemistry/ReagentEffects/CureZombieInfection.cs b/Content.Server/EntityEffects/Effects/CureZombieInfection.cs
similarity index 73%
rename from Content.Server/Chemistry/ReagentEffects/CureZombieInfection.cs
rename to Content.Server/EntityEffects/Effects/CureZombieInfection.cs
index 20e2c015c4..8dfc2de055 100644
--- a/Content.Server/Chemistry/ReagentEffects/CureZombieInfection.cs
+++ b/Content.Server/EntityEffects/Effects/CureZombieInfection.cs
@@ -1,12 +1,11 @@
using Content.Server.Zombies;
-using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Configuration;
+using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
using Content.Shared.Zombies;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects;
-public sealed partial class CureZombieInfection : ReagentEffect
+public sealed partial class CureZombieInfection : EntityEffect
{
[DataField]
public bool Innoculate;
@@ -20,18 +19,18 @@ public sealed partial class CureZombieInfection : ReagentEffect
}
// Removes the Zombie Infection Components
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
var entityManager = args.EntityManager;
- if (entityManager.HasComponent(args.SolutionEntity))
+ if (entityManager.HasComponent(args.TargetEntity))
return;
- entityManager.RemoveComponent(args.SolutionEntity);
- entityManager.RemoveComponent(args.SolutionEntity);
+ entityManager.RemoveComponent(args.TargetEntity);
+ entityManager.RemoveComponent(args.TargetEntity);
if (Innoculate)
{
- entityManager.EnsureComponent(args.SolutionEntity);
+ entityManager.EnsureComponent(args.TargetEntity);
}
}
}
diff --git a/Content.Server/Chemistry/ReagentEffects/Drunk.cs b/Content.Server/EntityEffects/Effects/Drunk.cs
similarity index 64%
rename from Content.Server/Chemistry/ReagentEffects/Drunk.cs
rename to Content.Server/EntityEffects/Effects/Drunk.cs
index dbce995ca2..493560e5b9 100644
--- a/Content.Server/Chemistry/ReagentEffects/Drunk.cs
+++ b/Content.Server/EntityEffects/Effects/Drunk.cs
@@ -1,10 +1,10 @@
-using Content.Shared.Chemistry.Reagent;
using Content.Shared.Drunk;
+using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects;
-public sealed partial class Drunk : ReagentEffect
+public sealed partial class Drunk : EntityEffect
{
///
/// BoozePower is how long each metabolism cycle will make the drunk effect last for.
@@ -21,13 +21,15 @@ public sealed partial class Drunk : ReagentEffect
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-drunk", ("chance", Probability));
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
var boozePower = BoozePower;
- boozePower *= args.Scale;
-
+ if (args is EntityEffectReagentArgs reagentArgs) {
+ boozePower *= reagentArgs.Scale.Float();
+ }
+
var drunkSys = args.EntityManager.EntitySysManager.GetEntitySystem();
- drunkSys.TryApplyDrunkenness(args.SolutionEntity, boozePower, SlurSpeech);
+ drunkSys.TryApplyDrunkenness(args.TargetEntity, boozePower, SlurSpeech);
}
}
diff --git a/Content.Server/EntityEffects/Effects/Electrocute.cs b/Content.Server/EntityEffects/Effects/Electrocute.cs
new file mode 100644
index 0000000000..f6a5f1a2da
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/Electrocute.cs
@@ -0,0 +1,38 @@
+using Content.Server.Electrocution;
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.Effects;
+
+public sealed partial class Electrocute : EntityEffect
+{
+ [DataField] public int ElectrocuteTime = 2;
+
+ [DataField] public int ElectrocuteDamageScale = 5;
+
+ ///
+ /// true - refresh electrocute time, false - accumulate electrocute time
+ ///
+ [DataField] public bool Refresh = true;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-electrocute", ("chance", Probability), ("time", ElectrocuteTime));
+
+ public override bool ShouldLog => true;
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ reagentArgs.EntityManager.System().TryDoElectrocution(reagentArgs.TargetEntity, null,
+ Math.Max((reagentArgs.Quantity * ElectrocuteDamageScale).Int(), 1), TimeSpan.FromSeconds(ElectrocuteTime), Refresh, ignoreInsulation: true);
+
+ if (reagentArgs.Reagent != null)
+ reagentArgs.Source?.RemoveReagent(reagentArgs.Reagent.ID, reagentArgs.Quantity);
+ } else
+ {
+ args.EntityManager.System().TryDoElectrocution(args.TargetEntity, null,
+ Math.Max(ElectrocuteDamageScale, 1), TimeSpan.FromSeconds(ElectrocuteTime), Refresh, ignoreInsulation: true);
+ }
+ }
+}
diff --git a/Content.Server/Chemistry/ReagentEffects/Emote.cs b/Content.Server/EntityEffects/Effects/Emote.cs
similarity index 72%
rename from Content.Server/Chemistry/ReagentEffects/Emote.cs
rename to Content.Server/EntityEffects/Effects/Emote.cs
index db6de6cc75..00bdaec455 100644
--- a/Content.Server/Chemistry/ReagentEffects/Emote.cs
+++ b/Content.Server/EntityEffects/Effects/Emote.cs
@@ -1,17 +1,17 @@
using Content.Server.Chat.Systems;
using Content.Shared.Chat.Prototypes;
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects;
///
/// Tries to force someone to emote (scream, laugh, etc). Still respects whitelists/blacklists and other limits of the specified emote unless forced.
///
[UsedImplicitly]
-public sealed partial class Emote : ReagentEffect
+public sealed partial class Emote : EntityEffect
{
[DataField("emote", customTypeSerializer: typeof(PrototypeIdSerializer))]
public string? EmoteId;
@@ -26,16 +26,16 @@ public sealed partial class Emote : ReagentEffect
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> null;
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
if (EmoteId == null)
return;
var chatSys = args.EntityManager.System();
if (ShowInChat)
- chatSys.TryEmoteWithChat(args.SolutionEntity, EmoteId, ChatTransmitRange.GhostRangeLimit, forceEmote: Force);
+ chatSys.TryEmoteWithChat(args.TargetEntity, EmoteId, ChatTransmitRange.GhostRangeLimit, forceEmote: Force);
else
- chatSys.TryEmoteWithoutChat(args.SolutionEntity, EmoteId);
+ chatSys.TryEmoteWithoutChat(args.TargetEntity, EmoteId);
}
}
diff --git a/Content.Server/Chemistry/ReactionEffects/EmpReactionEffect.cs b/Content.Server/EntityEffects/Effects/EmpReactionEffect.cs
similarity index 67%
rename from Content.Server/Chemistry/ReactionEffects/EmpReactionEffect.cs
rename to Content.Server/EntityEffects/Effects/EmpReactionEffect.cs
index 9a320ffc39..ec7801f083 100644
--- a/Content.Server/Chemistry/ReactionEffects/EmpReactionEffect.cs
+++ b/Content.Server/EntityEffects/Effects/EmpReactionEffect.cs
@@ -1,16 +1,17 @@
using Content.Server.Emp;
+using Content.Shared.EntityEffects;
using Content.Shared.Chemistry.Reagent;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
-namespace Content.Server.Chemistry.ReactionEffects;
+namespace Content.Server.EntityEffects.Effects;
[DataDefinition]
-public sealed partial class EmpReactionEffect : ReagentEffect
+public sealed partial class EmpReactionEffect : EntityEffect
{
///
- /// Impulse range per unit of reagent
+ /// Impulse range per unit of quantity
///
[DataField("rangePerUnit")]
public float EmpRangePerUnit = 0.5f;
@@ -36,14 +37,20 @@ public sealed partial class EmpReactionEffect : ReagentEffect
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-emp-reaction-effect", ("chance", Probability));
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
var tSys = args.EntityManager.System();
- var transform = args.EntityManager.GetComponent(args.SolutionEntity);
- var range = MathF.Min((float) (args.Quantity*EmpRangePerUnit), EmpMaxRange);
+ var transform = args.EntityManager.GetComponent(args.TargetEntity);
+
+ var range = EmpRangePerUnit;
+
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ range = MathF.Min((float) (reagentArgs.Quantity * EmpRangePerUnit), EmpMaxRange);
+ }
args.EntityManager.System()
- .EmpPulse(tSys.GetMapCoordinates(args.SolutionEntity, xform: transform),
+ .EmpPulse(tSys.GetMapCoordinates(args.TargetEntity, xform: transform),
range,
EnergyConsumption,
DisableDuration);
diff --git a/Content.Server/EntityEffects/Effects/ExplosionReactionEffect.cs b/Content.Server/EntityEffects/Effects/ExplosionReactionEffect.cs
new file mode 100644
index 0000000000..689757d044
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/ExplosionReactionEffect.cs
@@ -0,0 +1,77 @@
+using Content.Server.Explosion.EntitySystems;
+using Content.Shared.Database;
+using Content.Shared.EntityEffects;
+using Content.Shared.Explosion;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using System.Text.Json.Serialization;
+
+namespace Content.Server.EntityEffects.Effects;
+
+[DataDefinition]
+public sealed partial class ExplosionReactionEffect : EntityEffect
+{
+ ///
+ /// The type of explosion. Determines damage types and tile break chance scaling.
+ ///
+ [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer))]
+ [JsonIgnore]
+ public string ExplosionType = default!;
+
+ ///
+ /// The max intensity the explosion can have at a given tile. Places an upper limit of damage and tile break
+ /// chance.
+ ///
+ [DataField]
+ [JsonIgnore]
+ public float MaxIntensity = 5;
+
+ ///
+ /// How quickly intensity drops off as you move away from the epicenter
+ ///
+ [DataField]
+ [JsonIgnore]
+ public float IntensitySlope = 1;
+
+ ///
+ /// The maximum total intensity that this chemical reaction can achieve. Basically here to prevent people
+ /// from creating a nuke by collecting enough potassium and water.
+ ///
+ ///
+ /// A slope of 1 and MaxTotalIntensity of 100 corresponds to a radius of around 4.5 tiles.
+ ///
+ [DataField]
+ [JsonIgnore]
+ public float MaxTotalIntensity = 100;
+
+ ///
+ /// The intensity of the explosion per unit reaction.
+ ///
+ [DataField]
+ [JsonIgnore]
+ public float IntensityPerUnit = 1;
+
+ public override bool ShouldLog => true;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-explosion-reaction-effect", ("chance", Probability));
+ public override LogImpact LogImpact => LogImpact.High;
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ var intensity = IntensityPerUnit;
+
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ intensity = MathF.Min((float) reagentArgs.Quantity * IntensityPerUnit, MaxTotalIntensity);
+ }
+
+ args.EntityManager.System()
+ .QueueExplosion(
+ args.TargetEntity,
+ ExplosionType,
+ intensity,
+ IntensitySlope,
+ MaxIntensity);
+ }
+}
diff --git a/Content.Server/EntityEffects/Effects/ExtinguishReaction.cs b/Content.Server/EntityEffects/Effects/ExtinguishReaction.cs
new file mode 100644
index 0000000000..6d7e7c2fcd
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/ExtinguishReaction.cs
@@ -0,0 +1,36 @@
+using Content.Server.Atmos.Components;
+using Content.Server.Atmos.EntitySystems;
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.Effects
+{
+ [UsedImplicitly]
+ public sealed partial class ExtinguishReaction : EntityEffect
+ {
+ ///
+ /// Amount of firestacks reduced.
+ ///
+ [DataField]
+ public float FireStacksAdjustment = -1.5f;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-extinguish-reaction", ("chance", Probability));
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!args.EntityManager.TryGetComponent(args.TargetEntity, out FlammableComponent? flammable)) return;
+
+ var flammableSystem = args.EntityManager.System();
+ flammableSystem.Extinguish(args.TargetEntity, flammable);
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ flammableSystem.AdjustFireStacks(reagentArgs.TargetEntity, FireStacksAdjustment * (float) reagentArgs.Quantity, flammable);
+ } else
+ {
+ flammableSystem.AdjustFireStacks(args.TargetEntity, FireStacksAdjustment, flammable);
+ }
+ }
+ }
+}
diff --git a/Content.Server/EntityEffects/Effects/FlammableReaction.cs b/Content.Server/EntityEffects/Effects/FlammableReaction.cs
new file mode 100644
index 0000000000..edd499ab4a
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/FlammableReaction.cs
@@ -0,0 +1,45 @@
+using Content.Server.Atmos.Components;
+using Content.Server.Atmos.EntitySystems;
+using Content.Shared.Database;
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.Effects
+{
+ [UsedImplicitly]
+ public sealed partial class FlammableReaction : EntityEffect
+ {
+ [DataField]
+ public float Multiplier = 0.05f;
+
+ [DataField]
+ public float MultiplierOnExisting = 1f;
+
+ public override bool ShouldLog => true;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-flammable-reaction", ("chance", Probability));
+
+ public override LogImpact LogImpact => LogImpact.Medium;
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!args.EntityManager.TryGetComponent(args.TargetEntity, out FlammableComponent? flammable))
+ return;
+
+ var multiplier = flammable.FireStacks == 0f ? Multiplier : MultiplierOnExisting;
+ var quantity = 1f;
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ quantity = reagentArgs.Quantity.Float();
+ reagentArgs.EntityManager.System().AdjustFireStacks(args.TargetEntity, quantity * multiplier, flammable);
+ if (reagentArgs.Reagent != null)
+ reagentArgs.Source?.RemoveReagent(reagentArgs.Reagent.ID, reagentArgs.Quantity);
+ } else
+ {
+ args.EntityManager.System().AdjustFireStacks(args.TargetEntity, multiplier, flammable);
+ }
+ }
+ }
+}
diff --git a/Content.Server/Chemistry/ReagentEffects/HealthChange.cs b/Content.Server/EntityEffects/Effects/HealthChange.cs
similarity index 84%
rename from Content.Server/Chemistry/ReagentEffects/HealthChange.cs
rename to Content.Server/EntityEffects/Effects/HealthChange.cs
index 24880cfd37..fd2a658745 100644
--- a/Content.Server/Chemistry/ReagentEffects/HealthChange.cs
+++ b/Content.Server/EntityEffects/Effects/HealthChange.cs
@@ -1,6 +1,6 @@
-using Content.Shared.Chemistry.Reagent;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
+using Content.Shared.EntityEffects;
using Content.Shared.FixedPoint;
using Content.Shared.Localizations;
using JetBrains.Annotations;
@@ -8,16 +8,16 @@ using Robust.Shared.Prototypes;
using System.Linq;
using System.Text.Json.Serialization;
-namespace Content.Server.Chemistry.ReagentEffects
+namespace Content.Server.EntityEffects.Effects
{
///
- /// Default metabolism for medicine reagents.
+ /// Default metabolism used for medicine reagents.
///
[UsedImplicitly]
- public sealed partial class HealthChange : ReagentEffect
+ public sealed partial class HealthChange : EntityEffect
{
///
- /// Damage to apply every metabolism cycle. Damage Ignores resistances.
+ /// Damage to apply every cycle. Damage Ignores resistances.
///
[DataField(required: true)]
[JsonPropertyName("damage")]
@@ -26,6 +26,7 @@ namespace Content.Server.Chemistry.ReagentEffects
///
/// Should this effect scale the damage by the amount of chemical in the solution?
/// Useful for touch reactions, like styptic powder or acid.
+ /// Only usable if the EntityEffectBaseArgs is an EntityEffectReagentArgs.
///
[DataField]
[JsonPropertyName("scaleByQuantity")]
@@ -110,13 +111,17 @@ namespace Content.Server.Chemistry.ReagentEffects
("healsordeals", healsordeals));
}
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
- var scale = ScaleByQuantity ? args.Quantity : FixedPoint2.New(1);
- scale *= args.Scale;
+ var scale = FixedPoint2.New(1);
+
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ scale = ScaleByQuantity ? reagentArgs.Quantity * reagentArgs.Scale : reagentArgs.Scale;
+ }
args.EntityManager.System().TryChangeDamage(
- args.SolutionEntity,
+ args.TargetEntity,
Damage * scale,
IgnoreResistances,
interruptsDoAfters: false);
diff --git a/Content.Server/Chemistry/ReagentEffects/Ignite.cs b/Content.Server/EntityEffects/Effects/Ignite.cs
similarity index 50%
rename from Content.Server/Chemistry/ReagentEffects/Ignite.cs
rename to Content.Server/EntityEffects/Effects/Ignite.cs
index 6c5d47711f..cca2a301fd 100644
--- a/Content.Server/Chemistry/ReagentEffects/Ignite.cs
+++ b/Content.Server/EntityEffects/Effects/Ignite.cs
@@ -1,14 +1,14 @@
-using Content.Server.Atmos.EntitySystems;
-using Content.Shared.Chemistry.Reagent;
+using Content.Server.Atmos.EntitySystems;
using Content.Shared.Database;
+using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects;
///
/// Ignites a mob.
///
-public sealed partial class Ignite : ReagentEffect
+public sealed partial class Ignite : EntityEffect
{
public override bool ShouldLog => true;
@@ -17,9 +17,15 @@ public sealed partial class Ignite : ReagentEffect
public override LogImpact LogImpact => LogImpact.Medium;
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
var flamSys = args.EntityManager.System();
- flamSys.Ignite(args.SolutionEntity, args.OrganEntity ?? args.SolutionEntity);
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ flamSys.Ignite(reagentArgs.TargetEntity, reagentArgs.OrganEntity ?? reagentArgs.TargetEntity);
+ } else
+ {
+ flamSys.Ignite(args.TargetEntity, args.TargetEntity);
+ }
}
}
diff --git a/Content.Server/Chemistry/ReagentEffects/MakeSentient.cs b/Content.Server/EntityEffects/Effects/MakeSentient.cs
similarity index 87%
rename from Content.Server/Chemistry/ReagentEffects/MakeSentient.cs
rename to Content.Server/EntityEffects/Effects/MakeSentient.cs
index be8dbbaf52..c487043848 100644
--- a/Content.Server/Chemistry/ReagentEffects/MakeSentient.cs
+++ b/Content.Server/EntityEffects/Effects/MakeSentient.cs
@@ -1,20 +1,20 @@
using Content.Server.Ghost.Roles.Components;
using Content.Server.Speech.Components;
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Content.Shared.Mind.Components;
using Robust.Shared.Prototypes;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects;
-public sealed partial class MakeSentient : ReagentEffect
+public sealed partial class MakeSentient : EntityEffect
{
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-make-sentient", ("chance", Probability));
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
var entityManager = args.EntityManager;
- var uid = args.SolutionEntity;
+ var uid = args.TargetEntity;
// Let affected entities speak normally to make this effect different from, say, the "random sentience" event
// This also works on entities that already have a mind
diff --git a/Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs b/Content.Server/EntityEffects/Effects/ModifyBleedAmount.cs
similarity index 50%
rename from Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs
rename to Content.Server/EntityEffects/Effects/ModifyBleedAmount.cs
index ecd9c86255..58bc304f5e 100644
--- a/Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs
+++ b/Content.Server/EntityEffects/Effects/ModifyBleedAmount.cs
@@ -1,11 +1,11 @@
-using Content.Server.Body.Components;
+using Content.Server.Body.Components;
using Content.Server.Body.Systems;
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects;
-public sealed partial class ModifyBleedAmount : ReagentEffect
+public sealed partial class ModifyBleedAmount : EntityEffect
{
[DataField]
public bool Scaled = false;
@@ -17,15 +17,19 @@ public sealed partial class ModifyBleedAmount : ReagentEffect
=> Loc.GetString("reagent-effect-guidebook-modify-bleed-amount", ("chance", Probability),
("deltasign", MathF.Sign(Amount)));
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
- if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var blood))
+ if (args.EntityManager.TryGetComponent(args.TargetEntity, out var blood))
{
var sys = args.EntityManager.System();
- var amt = Scaled ? Amount * args.Quantity.Float() : Amount;
- amt *= args.Scale;
+ var amt = Amount;
+ if (args is EntityEffectReagentArgs reagentArgs) {
+ if (Scaled)
+ amt *= reagentArgs.Quantity.Float();
+ amt *= reagentArgs.Scale.Float();
+ }
- sys.TryModifyBleedAmount(args.SolutionEntity, amt, blood);
+ sys.TryModifyBleedAmount(args.TargetEntity, amt, blood);
}
}
}
diff --git a/Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs b/Content.Server/EntityEffects/Effects/ModifyBloodLevel.cs
similarity index 52%
rename from Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs
rename to Content.Server/EntityEffects/Effects/ModifyBloodLevel.cs
index b136ff9bb8..d8aca7d284 100644
--- a/Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs
+++ b/Content.Server/EntityEffects/Effects/ModifyBloodLevel.cs
@@ -1,12 +1,12 @@
-using Content.Server.Body.Components;
+using Content.Server.Body.Components;
using Content.Server.Body.Systems;
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects;
-public sealed partial class ModifyBloodLevel : ReagentEffect
+public sealed partial class ModifyBloodLevel : EntityEffect
{
[DataField]
public bool Scaled = false;
@@ -18,15 +18,20 @@ public sealed partial class ModifyBloodLevel : ReagentEffect
=> Loc.GetString("reagent-effect-guidebook-modify-blood-level", ("chance", Probability),
("deltasign", MathF.Sign(Amount.Float())));
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
- if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var blood))
+ if (args.EntityManager.TryGetComponent(args.TargetEntity, out var blood))
{
var sys = args.EntityManager.System();
- var amt = Scaled ? Amount * args.Quantity : Amount;
- amt *= args.Scale;
+ var amt = Amount;
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (Scaled)
+ amt *= reagentArgs.Quantity;
+ amt *= reagentArgs.Scale;
+ }
- sys.TryModifyBloodLevel(args.SolutionEntity, amt, blood);
+ sys.TryModifyBloodLevel(args.TargetEntity, amt, blood);
}
}
}
diff --git a/Content.Server/EntityEffects/Effects/ModifyLungGas.cs b/Content.Server/EntityEffects/Effects/ModifyLungGas.cs
new file mode 100644
index 0000000000..b4f35e3d56
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/ModifyLungGas.cs
@@ -0,0 +1,49 @@
+using Content.Server.Body.Components;
+using Content.Shared.Atmos;
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+using Content.Shared.EntityEffects;
+
+namespace Content.Server.EntityEffects.Effects;
+
+public sealed partial class ModifyLungGas : EntityEffect
+{
+ [DataField("ratios", required: true)]
+ private Dictionary _ratios = default!;
+
+ // JUSTIFICATION: This is internal magic that players never directly interact with.
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => null;
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+
+ LungComponent? lung;
+ float amount = 1f;
+
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (!args.EntityManager.TryGetComponent(reagentArgs.OrganEntity, out var organLung))
+ return;
+ lung = organLung;
+ amount = reagentArgs.Quantity.Float();
+ }
+ else
+ {
+ if (!args.EntityManager.TryGetComponent(args.TargetEntity, out var organLung)) //Likely needs to be modified to ensure it works correctly
+ return;
+ lung = organLung;
+ }
+
+ if (lung != null)
+ {
+ foreach (var (gas, ratio) in _ratios)
+ {
+ var quantity = ratio * amount / Atmospherics.BreathMolesToReagentMultiplier;
+ if (quantity < 0)
+ quantity = Math.Max(quantity, -lung.Air[(int) gas]);
+ lung.Air.AdjustMoles(gas, quantity);
+ }
+ }
+ }
+}
diff --git a/Content.Server/EntityEffects/Effects/MovespeedModifier.cs b/Content.Server/EntityEffects/Effects/MovespeedModifier.cs
new file mode 100644
index 0000000000..ac1f143e9f
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/MovespeedModifier.cs
@@ -0,0 +1,78 @@
+using Content.Shared.Chemistry.Components;
+using Content.Shared.EntityEffects;
+using Content.Shared.Movement.Systems;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
+
+namespace Content.Server.EntityEffects.Effects;
+
+///
+/// Default metabolism for stimulants and tranqs. Attempts to find a MovementSpeedModifier on the target,
+/// adding one if not there and to change the movespeed
+///
+public sealed partial class MovespeedModifier : EntityEffect
+{
+ ///
+ /// How much the entities' walk speed is multiplied by.
+ ///
+ [DataField]
+ public float WalkSpeedModifier { get; set; } = 1;
+
+ ///
+ /// How much the entities' run speed is multiplied by.
+ ///
+ [DataField]
+ public float SprintSpeedModifier { get; set; } = 1;
+
+ ///
+ /// How long the modifier applies (in seconds).
+ /// Is scaled by reagent amount if used with an EntityEffectReagentArgs.
+ ///
+ [DataField]
+ public float StatusLifetime = 2f;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ {
+ return Loc.GetString("reagent-effect-guidebook-movespeed-modifier",
+ ("chance", Probability),
+ ("walkspeed", WalkSpeedModifier),
+ ("time", StatusLifetime));
+ }
+
+ ///
+ /// Remove reagent at set rate, changes the movespeed modifiers and adds a MovespeedModifierMetabolismComponent if not already there.
+ ///
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ var status = args.EntityManager.EnsureComponent(args.TargetEntity);
+
+ // Only refresh movement if we need to.
+ var modified = !status.WalkSpeedModifier.Equals(WalkSpeedModifier) ||
+ !status.SprintSpeedModifier.Equals(SprintSpeedModifier);
+
+ status.WalkSpeedModifier = WalkSpeedModifier;
+ status.SprintSpeedModifier = SprintSpeedModifier;
+
+ // only going to scale application time
+ var statusLifetime = StatusLifetime;
+
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ statusLifetime *= reagentArgs.Scale.Float();
+ }
+
+ IncreaseTimer(status, statusLifetime);
+
+ if (modified)
+ args.EntityManager.System().RefreshMovementSpeedModifiers(args.TargetEntity);
+ }
+ public void IncreaseTimer(MovespeedModifierMetabolismComponent status, float time)
+ {
+ var gameTiming = IoCManager.Resolve();
+
+ var offsetTime = Math.Max(status.ModifierTimer.TotalSeconds, gameTiming.CurTime.TotalSeconds);
+
+ status.ModifierTimer = TimeSpan.FromSeconds(offsetTime + time);
+ status.Dirty();
+ }
+}
diff --git a/Content.Server/EntityEffects/Effects/Oxygenate.cs b/Content.Server/EntityEffects/Effects/Oxygenate.cs
new file mode 100644
index 0000000000..60383188c9
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/Oxygenate.cs
@@ -0,0 +1,32 @@
+using Content.Server.Body.Components;
+using Content.Server.Body.Systems;
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.Effects;
+
+public sealed partial class Oxygenate : EntityEffect
+{
+ [DataField]
+ public float Factor = 1f;
+
+ // JUSTIFICATION: This is internal magic that players never directly interact with.
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => null;
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+
+ var multiplier = 1f;
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ multiplier = reagentArgs.Quantity.Float();
+ }
+
+ if (args.EntityManager.TryGetComponent(args.TargetEntity, out var resp))
+ {
+ var respSys = args.EntityManager.System();
+ respSys.UpdateSaturation(args.TargetEntity, multiplier * Factor, resp);
+ }
+ }
+}
diff --git a/Content.Server/Chemistry/ReagentEffects/Paralyze.cs b/Content.Server/EntityEffects/Effects/Paralyze.cs
similarity index 62%
rename from Content.Server/Chemistry/ReagentEffects/Paralyze.cs
rename to Content.Server/EntityEffects/Effects/Paralyze.cs
index d5d913f37d..ba020dd39b 100644
--- a/Content.Server/Chemistry/ReagentEffects/Paralyze.cs
+++ b/Content.Server/EntityEffects/Effects/Paralyze.cs
@@ -1,10 +1,10 @@
using Content.Server.Stunnable;
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects;
-public sealed partial class Paralyze : ReagentEffect
+public sealed partial class Paralyze : EntityEffect
{
[DataField] public double ParalyzeTime = 2;
@@ -18,12 +18,16 @@ public sealed partial class Paralyze : ReagentEffect
("chance", Probability),
("time", ParalyzeTime));
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
var paralyzeTime = ParalyzeTime;
- paralyzeTime *= args.Scale;
- args.EntityManager.System().TryParalyze(args.SolutionEntity, TimeSpan.FromSeconds(paralyzeTime), Refresh);
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ paralyzeTime *= (double)reagentArgs.Scale;
+ }
+
+ args.EntityManager.System().TryParalyze(args.TargetEntity, TimeSpan.FromSeconds(paralyzeTime), Refresh);
}
}
diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustAttribute.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustAttribute.cs
new file mode 100644
index 0000000000..5133fe502f
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustAttribute.cs
@@ -0,0 +1,61 @@
+using Content.Server.Botany.Components;
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
+
+[ImplicitDataDefinitionForInheritors]
+public abstract partial class PlantAdjustAttribute : EntityEffect
+{
+ [DataField]
+ public float Amount { get; protected set; } = 1;
+
+ ///
+ /// Localisation key for the name of the adjusted attribute. Used for guidebook descriptions.
+ ///
+ [DataField]
+ public abstract string GuidebookAttributeName { get; set; }
+
+ ///
+ /// Whether the attribute in question is a good thing. Used for guidebook descriptions to determine the color of the number.
+ ///
+ [DataField]
+ public virtual bool GuidebookIsAttributePositive { get; protected set; } = true;
+
+ ///
+ /// Checks if the plant holder can metabolize the reagent or not. Checks if it has an alive plant by default.
+ ///
+ /// The entity holding the plant
+ /// The plant holder component
+ /// The entity manager
+ /// Whether to check if it has an alive plant or not
+ ///
+ public bool CanMetabolize(EntityUid plantHolder, [NotNullWhen(true)] out PlantHolderComponent? plantHolderComponent,
+ IEntityManager entityManager,
+ bool mustHaveAlivePlant = true)
+ {
+ plantHolderComponent = null;
+
+ if (!entityManager.TryGetComponent(plantHolder, out plantHolderComponent)
+ || mustHaveAlivePlant && (plantHolderComponent.Seed == null || plantHolderComponent.Dead))
+ return false;
+
+ return true;
+ }
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ {
+ string color;
+ if (GuidebookIsAttributePositive ^ Amount < 0.0)
+ {
+ color = "green";
+ }
+ else
+ {
+ color = "red";
+ }
+ return Loc.GetString("reagent-effect-guidebook-plant-attribute", ("attribute", Loc.GetString(GuidebookAttributeName)), ("amount", Amount.ToString("0.00")), ("colorName", color), ("chance", Probability));
+ }
+}
diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustHealth.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustHealth.cs
new file mode 100644
index 0000000000..774f6ec48c
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustHealth.cs
@@ -0,0 +1,21 @@
+using Content.Server.Botany.Systems;
+using Content.Shared.EntityEffects;
+
+namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantAdjustHealth : PlantAdjustAttribute
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-health";
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!CanMetabolize(args.TargetEntity, out var plantHolderComp, args.EntityManager))
+ return;
+
+ var plantHolder = args.EntityManager.System();
+
+ plantHolderComp.Health += Amount;
+ plantHolder.CheckHealth(args.TargetEntity, plantHolderComp);
+ }
+}
+
diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustMutationLevel.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustMutationLevel.cs
new file mode 100644
index 0000000000..bf41a21bcb
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustMutationLevel.cs
@@ -0,0 +1,16 @@
+using Content.Shared.EntityEffects;
+
+namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantAdjustMutationLevel : PlantAdjustAttribute
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-mutation-level";
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!CanMetabolize(args.TargetEntity, out var plantHolderComp, args.EntityManager))
+ return;
+
+ plantHolderComp.MutationLevel += Amount * plantHolderComp.MutationMod;
+ }
+}
diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustMutationMod.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustMutationMod.cs
new file mode 100644
index 0000000000..6ed793e614
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustMutationMod.cs
@@ -0,0 +1,19 @@
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+
+namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
+
+[UsedImplicitly]
+public sealed partial class PlantAdjustMutationMod : PlantAdjustAttribute
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-mutation-mod";
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!CanMetabolize(args.TargetEntity, out var plantHolderComp, args.EntityManager))
+ return;
+
+ plantHolderComp.MutationMod += Amount;
+ }
+}
+
diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustNutrition.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustNutrition.cs
new file mode 100644
index 0000000000..b9389afacd
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustNutrition.cs
@@ -0,0 +1,21 @@
+using Content.Server.Botany.Systems;
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+
+namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
+
+[UsedImplicitly]
+public sealed partial class PlantAdjustNutrition : PlantAdjustAttribute
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-nutrition";
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!CanMetabolize(args.TargetEntity, out var plantHolderComp, args.EntityManager, mustHaveAlivePlant: false))
+ return;
+
+ var plantHolder = args.EntityManager.System();
+
+ plantHolder.AdjustNutrient(args.TargetEntity, Amount, plantHolderComp);
+ }
+}
diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustPests.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustPests.cs
new file mode 100644
index 0000000000..219529bfc4
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustPests.cs
@@ -0,0 +1,20 @@
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+
+namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
+
+[UsedImplicitly]
+public sealed partial class PlantAdjustPests : PlantAdjustAttribute
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-pests";
+ public override bool GuidebookIsAttributePositive { get; protected set; } = false;
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!CanMetabolize(args.TargetEntity, out var plantHolderComp, args.EntityManager))
+ return;
+
+ plantHolderComp.PestLevel += Amount;
+ }
+}
+
diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustToxins.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustToxins.cs
new file mode 100644
index 0000000000..ab1baa4285
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustToxins.cs
@@ -0,0 +1,20 @@
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+
+namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
+
+[UsedImplicitly]
+public sealed partial class PlantAdjustToxins : PlantAdjustAttribute
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-toxins";
+ public override bool GuidebookIsAttributePositive { get; protected set; } = false;
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!CanMetabolize(args.TargetEntity, out var plantHolderComp, args.EntityManager))
+ return;
+
+ plantHolderComp.Toxins += Amount;
+ }
+}
+
diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustWater.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustWater.cs
new file mode 100644
index 0000000000..41774977d5
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustWater.cs
@@ -0,0 +1,22 @@
+using Content.Server.Botany.Systems;
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+
+namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
+
+[UsedImplicitly]
+public sealed partial class PlantAdjustWater : PlantAdjustAttribute
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-water";
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!CanMetabolize(args.TargetEntity, out var plantHolderComp, args.EntityManager, mustHaveAlivePlant: false))
+ return;
+
+ var plantHolder = args.EntityManager.System();
+
+ plantHolder.AdjustWater(args.TargetEntity, Amount, plantHolderComp);
+ }
+}
+
diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustWeeds.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustWeeds.cs
new file mode 100644
index 0000000000..421e31998d
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustWeeds.cs
@@ -0,0 +1,19 @@
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+
+namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
+
+[UsedImplicitly]
+public sealed partial class PlantAdjustWeeds : PlantAdjustAttribute
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-weeds";
+ public override bool GuidebookIsAttributePositive { get; protected set; } = false;
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!CanMetabolize(args.TargetEntity, out var plantHolderComp, args.EntityManager))
+ return;
+
+ plantHolderComp.WeedLevel += Amount;
+ }
+}
diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAffectGrowth.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAffectGrowth.cs
new file mode 100644
index 0000000000..397eace399
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAffectGrowth.cs
@@ -0,0 +1,22 @@
+using Content.Server.Botany.Systems;
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+
+namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
+
+[UsedImplicitly]
+public sealed partial class PlantAffectGrowth : PlantAdjustAttribute
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-growth";
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!CanMetabolize(args.TargetEntity, out var plantHolderComp, args.EntityManager))
+ return;
+
+ var plantHolder = args.EntityManager.System();
+
+ plantHolder.AffectGrowth(args.TargetEntity, (int) Amount, plantHolderComp);
+ }
+}
+
diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantCryoxadone.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantCryoxadone.cs
new file mode 100644
index 0000000000..7fe4b7a170
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantCryoxadone.cs
@@ -0,0 +1,32 @@
+using Content.Server.Botany.Components;
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+
+namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
+
+[UsedImplicitly]
+[DataDefinition]
+public sealed partial class PlantCryoxadone : EntityEffect
+{
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!args.EntityManager.TryGetComponent(args.TargetEntity, out PlantHolderComponent? plantHolderComp)
+ || plantHolderComp.Seed == null || plantHolderComp.Dead)
+ return;
+
+ var deviation = 0;
+ var seed = plantHolderComp.Seed;
+ var random = IoCManager.Resolve();
+ if (plantHolderComp.Age > seed.Maturation)
+ deviation = (int) Math.Max(seed.Maturation - 1, plantHolderComp.Age - random.Next(7, 10));
+ else
+ deviation = (int) (seed.Maturation / seed.GrowthStages);
+ plantHolderComp.Age -= deviation;
+ plantHolderComp.SkipAging++;
+ plantHolderComp.ForceUpdate = true;
+ }
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-cryoxadone", ("chance", Probability));
+}
diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantDiethylamine.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantDiethylamine.cs
new file mode 100644
index 0000000000..36b6a83342
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantDiethylamine.cs
@@ -0,0 +1,41 @@
+using Content.Server.Botany.Components;
+using Content.Server.Botany.Systems;
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+
+namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
+
+[UsedImplicitly]
+[DataDefinition]
+public sealed partial class PlantDiethylamine : EntityEffect
+{
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!args.EntityManager.TryGetComponent(args.TargetEntity, out PlantHolderComponent? plantHolderComp)
+ || plantHolderComp.Seed == null || plantHolderComp.Dead ||
+ plantHolderComp.Seed.Immutable)
+ return;
+
+
+ var plantHolder = args.EntityManager.System();
+
+ var random = IoCManager.Resolve();
+
+ if (random.Prob(0.1f))
+ {
+ plantHolder.EnsureUniqueSeed(args.TargetEntity, plantHolderComp);
+ plantHolderComp.Seed.Lifespan++;
+ }
+
+ if (random.Prob(0.1f))
+ {
+ plantHolder.EnsureUniqueSeed(args.TargetEntity, plantHolderComp);
+ plantHolderComp.Seed.Endurance++;
+ }
+ }
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-diethylamine", ("chance", Probability));
+}
+
diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantPhalanximine.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantPhalanximine.cs
new file mode 100644
index 0000000000..96d98bfbf2
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantPhalanximine.cs
@@ -0,0 +1,23 @@
+using Content.Server.Botany.Components;
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
+
+[UsedImplicitly]
+[DataDefinition]
+public sealed partial class PlantPhalanximine : EntityEffect
+{
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!args.EntityManager.TryGetComponent(args.TargetEntity, out PlantHolderComponent? plantHolderComp)
+ || plantHolderComp.Seed == null || plantHolderComp.Dead ||
+ plantHolderComp.Seed.Immutable)
+ return;
+
+ plantHolderComp.Seed.Viable = true;
+ }
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-phalanximine", ("chance", Probability));
+}
diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/RobustHarvest.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/RobustHarvest.cs
new file mode 100644
index 0000000000..695cb96675
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/RobustHarvest.cs
@@ -0,0 +1,53 @@
+using Content.Server.Botany.Components;
+using Content.Server.Botany.Systems;
+using Content.Shared.EntityEffects;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+
+namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
+
+[UsedImplicitly]
+[DataDefinition]
+public sealed partial class RobustHarvest : EntityEffect
+{
+ [DataField]
+ public int PotencyLimit = 50;
+
+ [DataField]
+ public int PotencyIncrease = 3;
+
+ [DataField]
+ public int PotencySeedlessThreshold = 30;
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!args.EntityManager.TryGetComponent(args.TargetEntity, out PlantHolderComponent? plantHolderComp)
+ || plantHolderComp.Seed == null || plantHolderComp.Dead ||
+ plantHolderComp.Seed.Immutable)
+ return;
+
+
+ var plantHolder = args.EntityManager.System();
+ var random = IoCManager.Resolve();
+
+ if (plantHolderComp.Seed.Potency < PotencyLimit)
+ {
+ plantHolder.EnsureUniqueSeed(args.TargetEntity, plantHolderComp);
+ plantHolderComp.Seed.Potency = Math.Min(plantHolderComp.Seed.Potency + PotencyIncrease, PotencyLimit);
+
+ if (plantHolderComp.Seed.Potency > PotencySeedlessThreshold)
+ {
+ plantHolderComp.Seed.Seedless = true;
+ }
+ }
+ else if (plantHolderComp.Seed.Yield > 1 && random.Prob(0.1f))
+ {
+ // Too much of a good thing reduces yield
+ plantHolder.EnsureUniqueSeed(args.TargetEntity, plantHolderComp);
+ plantHolderComp.Seed.Yield--;
+ }
+ }
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-robust-harvest", ("seedlesstreshold", PotencySeedlessThreshold), ("limit", PotencyLimit), ("increase", PotencyIncrease), ("chance", Probability));
+}
diff --git a/Content.Server/Chemistry/ReagentEffects/Polymorph.cs b/Content.Server/EntityEffects/Effects/Polymorph.cs
similarity index 80%
rename from Content.Server/Chemistry/ReagentEffects/Polymorph.cs
rename to Content.Server/EntityEffects/Effects/Polymorph.cs
index fea372125e..00a5c6613d 100644
--- a/Content.Server/Chemistry/ReagentEffects/Polymorph.cs
+++ b/Content.Server/EntityEffects/Effects/Polymorph.cs
@@ -1,13 +1,14 @@
using Content.Server.Polymorph.Components;
using Content.Server.Polymorph.Systems;
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Content.Shared.Polymorph;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using Content.Shared.EntityEffects;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects;
-public sealed partial class Polymorph : ReagentEffect
+public sealed partial class Polymorph : EntityEffect
{
///
/// What polymorph prototype is used on effect
@@ -20,10 +21,10 @@ public sealed partial class Polymorph : ReagentEffect
("chance", Probability), ("entityname",
prototype.Index(prototype.Index(PolymorphPrototype).Configuration.Entity).Name));
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
var entityManager = args.EntityManager;
- var uid = args.SolutionEntity;
+ var uid = args.TargetEntity;
var polySystem = entityManager.System();
// Make it into a prototype
diff --git a/Content.Server/Chemistry/ReagentEffects/PopupMessage.cs b/Content.Server/EntityEffects/Effects/PopupMessage.cs
similarity index 60%
rename from Content.Server/Chemistry/ReagentEffects/PopupMessage.cs
rename to Content.Server/EntityEffects/Effects/PopupMessage.cs
index 8278e95c1d..ac22b13051 100644
--- a/Content.Server/Chemistry/ReagentEffects/PopupMessage.cs
+++ b/Content.Server/EntityEffects/Effects/PopupMessage.cs
@@ -1,12 +1,11 @@
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Content.Shared.Popups;
-using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
-namespace Content.Server.Chemistry.ReagentEffects
+namespace Content.Server.EntityEffects.Effects
{
- public sealed partial class PopupMessage : ReagentEffect
+ public sealed partial class PopupMessage : EntityEffect
{
[DataField(required: true)]
public string[] Messages = default!;
@@ -21,20 +20,30 @@ namespace Content.Server.Chemistry.ReagentEffects
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> null;
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
var popupSys = args.EntityManager.EntitySysManager.GetEntitySystem();
var random = IoCManager.Resolve();
var msg = random.Pick(Messages);
- var msgArgs = new (string, object)[] {
- ("entity", args.SolutionEntity),
- ("organ", args.OrganEntity.GetValueOrDefault()),
+ var msgArgs = new (string, object)[]
+ {
+ ("entity", args.TargetEntity),
};
+
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ msgArgs = new (string, object)[]
+ {
+ ("entity", reagentArgs.TargetEntity),
+ ("organ", reagentArgs.OrganEntity.GetValueOrDefault()),
+ };
+ }
+
if (Type == PopupRecipients.Local)
- popupSys.PopupEntity(Loc.GetString(msg, msgArgs), args.SolutionEntity, args.SolutionEntity, VisualType);
+ popupSys.PopupEntity(Loc.GetString(msg, msgArgs), args.TargetEntity, args.TargetEntity, VisualType);
else if (Type == PopupRecipients.Pvs)
- popupSys.PopupEntity(Loc.GetString(msg, msgArgs), args.SolutionEntity, VisualType);
+ popupSys.PopupEntity(Loc.GetString(msg, msgArgs), args.TargetEntity, VisualType);
}
}
diff --git a/Content.Server/Chemistry/ReagentEffects/ReduceRotting.cs b/Content.Server/EntityEffects/Effects/ReduceRotting.cs
similarity index 61%
rename from Content.Server/Chemistry/ReagentEffects/ReduceRotting.cs
rename to Content.Server/EntityEffects/Effects/ReduceRotting.cs
index cea4f85332..5c00492744 100644
--- a/Content.Server/Chemistry/ReagentEffects/ReduceRotting.cs
+++ b/Content.Server/EntityEffects/Effects/ReduceRotting.cs
@@ -1,15 +1,16 @@
using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Content.Server.Atmos.Rotting;
-namespace Content.Server.Chemistry.ReagentEffects
+namespace Content.Server.EntityEffects.Effects
{
///
/// Reduces the rotting accumulator on the patient, making them revivable.
///
[UsedImplicitly]
- public sealed partial class ReduceRotting : ReagentEffect
+ public sealed partial class ReduceRotting : EntityEffect
{
[DataField("seconds")]
public double RottingAmount = 10;
@@ -18,14 +19,17 @@ namespace Content.Server.Chemistry.ReagentEffects
=> Loc.GetString("reagent-effect-guidebook-reduce-rotting",
("chance", Probability),
("time", RottingAmount));
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
- if (args.Scale != 1f)
- return;
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (reagentArgs.Scale != 1f)
+ return;
+ }
var rottingSys = args.EntityManager.EntitySysManager.GetEntitySystem();
- rottingSys.ReduceAccumulator(args.SolutionEntity, TimeSpan.FromSeconds(RottingAmount));
+ rottingSys.ReduceAccumulator(args.TargetEntity, TimeSpan.FromSeconds(RottingAmount));
}
}
}
diff --git a/Content.Server/Chemistry/ReagentEffects/ResetNarcolepsy.cs b/Content.Server/EntityEffects/Effects/ResetNarcolepsy.cs
similarity index 63%
rename from Content.Server/Chemistry/ReagentEffects/ResetNarcolepsy.cs
rename to Content.Server/EntityEffects/Effects/ResetNarcolepsy.cs
index fa4f910891..f9564712ad 100644
--- a/Content.Server/Chemistry/ReagentEffects/ResetNarcolepsy.cs
+++ b/Content.Server/EntityEffects/Effects/ResetNarcolepsy.cs
@@ -1,15 +1,16 @@
using Content.Server.Traits.Assorted;
using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects;
///
/// Reset narcolepsy timer
///
[UsedImplicitly]
-public sealed partial class ResetNarcolepsy : ReagentEffect
+public sealed partial class ResetNarcolepsy : EntityEffect
{
///
/// The # of seconds the effect resets the narcolepsy timer to
@@ -20,11 +21,12 @@ public sealed partial class ResetNarcolepsy : ReagentEffect
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-reset-narcolepsy", ("chance", Probability));
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
- if (args.Scale != 1f)
- return;
+ if (args is EntityEffectReagentArgs reagentArgs)
+ if (reagentArgs.Scale != 1f)
+ return;
- args.EntityManager.EntitySysManager.GetEntitySystem().AdjustNarcolepsyTimer(args.SolutionEntity, TimerReset);
+ args.EntityManager.EntitySysManager.GetEntitySystem().AdjustNarcolepsyTimer(args.TargetEntity, TimerReset);
}
}
diff --git a/Content.Server/Chemistry/ReagentEffects/SatiateHunger.cs b/Content.Server/EntityEffects/Effects/SatiateHunger.cs
similarity index 53%
rename from Content.Server/Chemistry/ReagentEffects/SatiateHunger.cs
rename to Content.Server/EntityEffects/Effects/SatiateHunger.cs
index be77202255..dd58654dff 100644
--- a/Content.Server/Chemistry/ReagentEffects/SatiateHunger.cs
+++ b/Content.Server/EntityEffects/Effects/SatiateHunger.cs
@@ -1,31 +1,40 @@
-using Content.Server.Nutrition.Components;
+using Content.Server.Nutrition.Components;
using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
using Robust.Shared.Prototypes;
-namespace Content.Server.Chemistry.ReagentEffects
+namespace Content.Server.EntityEffects.Effects
{
///
/// Attempts to find a HungerComponent on the target,
/// and to update it's hunger values.
///
- public sealed partial class SatiateHunger : ReagentEffect
+ public sealed partial class SatiateHunger : EntityEffect
{
private const float DefaultNutritionFactor = 3.0f;
///
- /// How much hunger is satiated when 1u of the reagent is metabolized
+ /// How much hunger is satiated.
+ /// Is multiplied by quantity if used with EntityEffectReagentArgs.
///
[DataField("factor")] public float NutritionFactor { get; set; } = DefaultNutritionFactor;
//Remove reagent at set rate, satiate hunger if a HungerComponent can be found
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
var entman = args.EntityManager;
- if (!entman.TryGetComponent(args.SolutionEntity, out HungerComponent? hunger))
+ if (!entman.TryGetComponent(args.TargetEntity, out HungerComponent? hunger))
return;
- entman.System().ModifyHunger(args.SolutionEntity, NutritionFactor * (float) args.Quantity, hunger);
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ entman.System().ModifyHunger(reagentArgs.TargetEntity, NutritionFactor * (float) reagentArgs.Quantity, hunger);
+ }
+ else
+ {
+ entman.System().ModifyHunger(args.TargetEntity, NutritionFactor, hunger);
+ }
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
diff --git a/Content.Server/EntityEffects/Effects/SatiateThirst.cs b/Content.Server/EntityEffects/Effects/SatiateThirst.cs
new file mode 100644
index 0000000000..d0dbe4371b
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/SatiateThirst.cs
@@ -0,0 +1,32 @@
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
+using Content.Shared.Nutrition.Components;
+using Content.Shared.Nutrition.EntitySystems;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.Effects;
+
+///
+/// Default metabolism for drink reagents. Attempts to find a ThirstComponent on the target,
+/// and to update it's thirst values.
+///
+public sealed partial class SatiateThirst : EntityEffect
+{
+ private const float DefaultHydrationFactor = 3.0f;
+
+ /// How much thirst is satiated each tick. Not currently tied to
+ /// rate or anything.
+ [DataField("factor")]
+ public float HydrationFactor { get; set; } = DefaultHydrationFactor;
+
+ /// Satiate thirst if a ThirstComponent can be found
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ var uid = args.TargetEntity;
+ if (args.EntityManager.TryGetComponent(uid, out ThirstComponent? thirst))
+ args.EntityManager.System().ModifyThirst(uid, thirst, HydrationFactor);
+ }
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-satiate-thirst", ("chance", Probability), ("relative", HydrationFactor / DefaultHydrationFactor));
+}
diff --git a/Content.Server/EntityEffects/Effects/SolutionTemperatureEffects.cs b/Content.Server/EntityEffects/Effects/SolutionTemperatureEffects.cs
new file mode 100644
index 0000000000..85c20dbcd1
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/SolutionTemperatureEffects.cs
@@ -0,0 +1,144 @@
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.Effects;
+
+///
+/// Sets the temperature of the solution involved with the reaction to a new value.
+///
+[DataDefinition]
+public sealed partial class SetSolutionTemperatureEffect : EntityEffect
+{
+ ///
+ /// The temperature to set the solution to.
+ ///
+ [DataField("temperature", required: true)] private float _temperature;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-set-solution-temperature-effect",
+ ("chance", Probability), ("temperature", _temperature));
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ var solution = reagentArgs.Source;
+ if (solution == null)
+ return;
+
+ solution.Temperature = _temperature;
+
+ return;
+ }
+
+ // TODO: Someone needs to figure out how to do this for non-reagent effects.
+ throw new NotImplementedException();
+ }
+}
+
+///
+/// Adjusts the temperature of the solution involved in the reaction.
+///
+[DataDefinition]
+public sealed partial class AdjustSolutionTemperatureEffect : EntityEffect
+{
+ ///
+ /// The change in temperature.
+ ///
+ [DataField("delta", required: true)] private float _delta;
+
+ ///
+ /// The minimum temperature this effect can reach.
+ ///
+ [DataField("minTemp")] private float _minTemp = 0.0f;
+
+ ///
+ /// The maximum temperature this effect can reach.
+ ///
+ [DataField("maxTemp")] private float _maxTemp = float.PositiveInfinity;
+
+ ///
+ /// If true, then scale ranges by intensity. If not, the ranges are the same regardless of reactant amount.
+ ///
+ [DataField("scaled")] private bool _scaled;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-adjust-solution-temperature-effect",
+ ("chance", Probability), ("deltasign", MathF.Sign(_delta)), ("mintemp", _minTemp), ("maxtemp", _maxTemp));
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ var solution = reagentArgs.Source;
+ if (solution == null || solution.Volume == 0)
+ return;
+
+ var deltaT = _scaled ? _delta * (float) reagentArgs.Quantity : _delta;
+ solution.Temperature = Math.Clamp(solution.Temperature + deltaT, _minTemp, _maxTemp);
+
+ return;
+ }
+
+ // TODO: Someone needs to figure out how to do this for non-reagent effects.
+ throw new NotImplementedException();
+ }
+}
+
+///
+/// Adjusts the thermal energy of the solution involved in the reaction.
+///
+public sealed partial class AdjustSolutionThermalEnergyEffect : EntityEffect
+{
+ ///
+ /// The change in energy.
+ ///
+ [DataField("delta", required: true)] private float _delta;
+
+ ///
+ /// The minimum temperature this effect can reach.
+ ///
+ [DataField("minTemp")] private float _minTemp = 0.0f;
+
+ ///
+ /// The maximum temperature this effect can reach.
+ ///
+ [DataField("maxTemp")] private float _maxTemp = float.PositiveInfinity;
+
+ ///
+ /// If true, then scale ranges by intensity. If not, the ranges are the same regardless of reactant amount.
+ ///
+ [DataField("scaled")] private bool _scaled;
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ var solution = reagentArgs.Source;
+ if (solution == null || solution.Volume == 0)
+ return;
+
+ if (_delta > 0 && solution.Temperature >= _maxTemp)
+ return;
+ if (_delta < 0 && solution.Temperature <= _minTemp)
+ return;
+
+ var heatCap = solution.GetHeatCapacity(null);
+ var deltaT = _scaled
+ ? _delta / heatCap * (float) reagentArgs.Quantity
+ : _delta / heatCap;
+
+ solution.Temperature = Math.Clamp(solution.Temperature + deltaT, _minTemp, _maxTemp);
+
+ return;
+ }
+
+ // TODO: Someone needs to figure out how to do this for non-reagent effects.
+ throw new NotImplementedException();
+ }
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-adjust-solution-temperature-effect",
+ ("chance", Probability), ("deltasign", MathF.Sign(_delta)), ("mintemp", _minTemp), ("maxtemp", _maxTemp));
+}
diff --git a/Content.Server/EntityEffects/Effects/StatusEffects/GenericStatusEffect.cs b/Content.Server/EntityEffects/Effects/StatusEffects/GenericStatusEffect.cs
new file mode 100644
index 0000000000..bb9bbf3446
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/StatusEffects/GenericStatusEffect.cs
@@ -0,0 +1,77 @@
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
+using Content.Shared.StatusEffect;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.Effects.StatusEffects;
+
+///
+/// Adds a generic status effect to the entity,
+/// not worrying about things like how to affect the time it lasts for
+/// or component fields or anything. Just adds a component to an entity
+/// for a given time. Easy.
+///
+///
+/// Can be used for things like adding accents or something. I don't know. Go wild.
+///
+[UsedImplicitly]
+public sealed partial class GenericStatusEffect : EntityEffect
+{
+ [DataField(required: true)]
+ public string Key = default!;
+
+ [DataField]
+ public string Component = String.Empty;
+
+ [DataField]
+ public float Time = 2.0f;
+
+ ///
+ /// true - refresh status effect time, false - accumulate status effect time
+ ///
+ [DataField]
+ public bool Refresh = true;
+
+ ///
+ /// Should this effect add the status effect, remove time from it, or set its cooldown?
+ ///
+ [DataField]
+ public StatusEffectMetabolismType Type = StatusEffectMetabolismType.Add;
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ var statusSys = args.EntityManager.EntitySysManager.GetEntitySystem();
+
+ var time = Time;
+ if (args is EntityEffectReagentArgs reagentArgs)
+ time *= reagentArgs.Scale.Float();
+
+ if (Type == StatusEffectMetabolismType.Add && Component != String.Empty)
+ {
+ statusSys.TryAddStatusEffect(args.TargetEntity, Key, TimeSpan.FromSeconds(time), Refresh, Component);
+ }
+ else if (Type == StatusEffectMetabolismType.Remove)
+ {
+ statusSys.TryRemoveTime(args.TargetEntity, Key, TimeSpan.FromSeconds(time));
+ }
+ else if (Type == StatusEffectMetabolismType.Set)
+ {
+ statusSys.TrySetTime(args.TargetEntity, Key, TimeSpan.FromSeconds(time));
+ }
+ }
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString(
+ "reagent-effect-guidebook-status-effect",
+ ("chance", Probability),
+ ("type", Type),
+ ("time", Time),
+ ("key", $"reagent-effect-status-effect-{Key}"));
+}
+
+public enum StatusEffectMetabolismType
+{
+ Add,
+ Remove,
+ Set
+}
diff --git a/Content.Server/EntityEffects/Effects/StatusEffects/Jitter.cs b/Content.Server/EntityEffects/Effects/StatusEffects/Jitter.cs
new file mode 100644
index 0000000000..dbca4fad58
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/StatusEffects/Jitter.cs
@@ -0,0 +1,42 @@
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
+using Content.Shared.Jittering;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.Effects.StatusEffects;
+
+///
+/// Adds the jitter status effect to a mob.
+/// This doesn't use generic status effects because it needs to
+/// take in some parameters that JitterSystem needs.
+///
+public sealed partial class Jitter : EntityEffect
+{
+ [DataField]
+ public float Amplitude = 10.0f;
+
+ [DataField]
+ public float Frequency = 4.0f;
+
+ [DataField]
+ public float Time = 2.0f;
+
+ ///
+ /// true - refresh jitter time, false - accumulate jitter time
+ ///
+ [DataField]
+ public bool Refresh = true;
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ var time = Time;
+ if (args is EntityEffectReagentArgs reagentArgs)
+ time *= reagentArgs.Scale.Float();
+
+ args.EntityManager.EntitySysManager.GetEntitySystem()
+ .DoJitter(args.TargetEntity, TimeSpan.FromSeconds(time), Refresh, Amplitude, Frequency);
+ }
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) =>
+ Loc.GetString("reagent-effect-guidebook-jittering", ("chance", Probability));
+}
diff --git a/Content.Server/EntityEffects/Effects/WashCreamPieReaction.cs b/Content.Server/EntityEffects/Effects/WashCreamPieReaction.cs
new file mode 100644
index 0000000000..67bfac6335
--- /dev/null
+++ b/Content.Server/EntityEffects/Effects/WashCreamPieReaction.cs
@@ -0,0 +1,22 @@
+using Content.Server.Nutrition.EntitySystems;
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
+using Content.Shared.Nutrition.Components;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.Effects;
+
+[UsedImplicitly]
+public sealed partial class WashCreamPieReaction : EntityEffect
+{
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-wash-cream-pie-reaction", ("chance", Probability));
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (!args.EntityManager.TryGetComponent(args.TargetEntity, out CreamPiedComponent? creamPied)) return;
+
+ args.EntityManager.System().SetCreamPied(args.TargetEntity, creamPied, false);
+ }
+}
diff --git a/Content.Server/Chemistry/ReagentEffects/WearableReaction.cs b/Content.Server/EntityEffects/Effects/WearableReaction.cs
similarity index 50%
rename from Content.Server/Chemistry/ReagentEffects/WearableReaction.cs
rename to Content.Server/EntityEffects/Effects/WearableReaction.cs
index d9f8414995..9655bbc073 100644
--- a/Content.Server/Chemistry/ReagentEffects/WearableReaction.cs
+++ b/Content.Server/EntityEffects/Effects/WearableReaction.cs
@@ -1,17 +1,19 @@
using Content.Shared.Inventory;
using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
-namespace Content.Server.Chemistry.ReagentEffects;
+namespace Content.Server.EntityEffects.Effects.Effects;
///
-/// A reaction effect that consumes the required amount of reagent and spawns PrototypeID in the
-/// entity's Slot. Used to implement the water droplet effect for arachnids.
+/// A reaction effect that spawns a PrototypeID in the entity's Slot, and attempts to consume the reagent if EntityEffectReagentArgs.
+/// Used to implement the water droplet effect for arachnids.
///
-public sealed partial class WearableReaction : ReagentEffect
+public sealed partial class WearableReaction : EntityEffect
{
///
/// Minimum quantity of reagent required to trigger this effect.
+ /// Only used with EntityEffectReagentArgs.
///
[DataField]
public float AmountThreshold = 1f;
@@ -30,13 +32,17 @@ public sealed partial class WearableReaction : ReagentEffect
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => null;
- public override void Effect(ReagentEffectArgs args)
+ public override void Effect(EntityEffectBaseArgs args)
{
- if (args.Reagent == null || args.Quantity < AmountThreshold)
- return;
-
// SpawnItemInSlot returns false if slot is already occupied
- if (args.EntityManager.System().SpawnItemInSlot(args.SolutionEntity, Slot, PrototypeID))
- args.Source?.RemoveReagent(args.Reagent.ID, AmountThreshold);
+ if (args.EntityManager.System().SpawnItemInSlot(args.TargetEntity, Slot, PrototypeID))
+ {
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (reagentArgs.Reagent == null || reagentArgs.Quantity < AmountThreshold)
+ return;
+ reagentArgs.Source?.RemoveReagent(reagentArgs.Reagent.ID, AmountThreshold);
+ }
+ }
}
}
diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs
index 2eaf19170b..8cdae84a93 100644
--- a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs
+++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs
@@ -2,7 +2,7 @@ using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reaction;
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Chemistry;
using Content.Shared.Clothing;
using Content.Shared.CombatMode.Pacification;
using Content.Shared.Database;
diff --git a/Content.Server/Fluids/EntitySystems/SmokeSystem.cs b/Content.Server/Fluids/EntitySystems/SmokeSystem.cs
index 9638dabf28..78693bfdbc 100644
--- a/Content.Server/Fluids/EntitySystems/SmokeSystem.cs
+++ b/Content.Server/Fluids/EntitySystems/SmokeSystem.cs
@@ -2,7 +2,7 @@ using Content.Server.Administration.Logs;
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Chemistry.Containers.EntitySystems;
-using Content.Server.Chemistry.ReactionEffects;
+using Content.Server.EntityEffects.Effects;
using Content.Server.Spreader;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;
diff --git a/Content.Server/GuideGenerator/ChemistryJsonGenerator.cs b/Content.Server/GuideGenerator/ChemistryJsonGenerator.cs
index 7e107ce1a5..8fd088408c 100644
--- a/Content.Server/GuideGenerator/ChemistryJsonGenerator.cs
+++ b/Content.Server/GuideGenerator/ChemistryJsonGenerator.cs
@@ -5,6 +5,7 @@ using System.Text.Json.Serialization;
using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Damage;
+using Content.Shared.EntityEffects;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
@@ -40,8 +41,8 @@ public sealed class ChemistryJsonGenerator
WriteIndented = true,
Converters =
{
- new UniversalJsonConverter(),
- new UniversalJsonConverter(),
+ new UniversalJsonConverter(),
+ new UniversalJsonConverter(),
new UniversalJsonConverter(),
new UniversalJsonConverter(),
new FixedPointJsonConverter()
diff --git a/Content.Server/GuideGenerator/ReactionJsonGenerator.cs b/Content.Server/GuideGenerator/ReactionJsonGenerator.cs
index 123dd5f8eb..06c230d607 100644
--- a/Content.Server/GuideGenerator/ReactionJsonGenerator.cs
+++ b/Content.Server/GuideGenerator/ReactionJsonGenerator.cs
@@ -2,7 +2,7 @@ using System.IO;
using System.Linq;
using System.Text.Json;
using Content.Shared.Chemistry.Reaction;
-using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
namespace Content.Server.GuideGenerator;
@@ -24,7 +24,7 @@ public sealed class ReactionJsonGenerator
WriteIndented = true,
Converters =
{
- new UniversalJsonConverter(),
+ new UniversalJsonConverter(),
}
};
diff --git a/Content.Server/GuideGenerator/ReagentEntry.cs b/Content.Server/GuideGenerator/ReagentEntry.cs
index ab9e758206..8b597ad61b 100644
--- a/Content.Server/GuideGenerator/ReagentEntry.cs
+++ b/Content.Server/GuideGenerator/ReagentEntry.cs
@@ -4,6 +4,7 @@ using Content.Server.Body.Components;
using Content.Shared.Body.Prototypes;
using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
namespace Content.Server.GuideGenerator;
@@ -61,7 +62,7 @@ public sealed class ReactionEntry
public Dictionary Products { get; }
[JsonPropertyName("effects")]
- public List Effects { get; }
+ public List Effects { get; }
public ReactionEntry(ReactionPrototype proto)
{
diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs
index aa2ed71d8f..b8e334fb0d 100644
--- a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs
+++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs
@@ -1,7 +1,7 @@
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Chemistry.Containers.EntitySystems;
-using Content.Server.Chemistry.ReagentEffects;
+using Content.Server.EntityEffects.Effects;
using Content.Server.Fluids.EntitySystems;
using Content.Server.Forensics;
using Content.Server.Inventory;
@@ -15,6 +15,7 @@ using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
using Content.Shared.DoAfter;
+using Content.Shared.EntityEffects;
using Content.Shared.FixedPoint;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
diff --git a/Content.Server/Tiles/LavaComponent.cs b/Content.Server/Tiles/LavaComponent.cs
deleted file mode 100644
index 1f36e8ffb0..0000000000
--- a/Content.Server/Tiles/LavaComponent.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using Robust.Shared.Audio;
-using Robust.Shared.GameStates;
-
-namespace Content.Server.Tiles;
-
-///
-/// Applies flammable and damage while vaulting.
-///
-[RegisterComponent, Access(typeof(LavaSystem))]
-public sealed partial class LavaComponent : Component
-{
- ///
- /// Sound played if something disintegrates in lava.
- ///
- [ViewVariables(VVAccess.ReadWrite), DataField("soundDisintegration")]
- public SoundSpecifier DisintegrationSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");
-
- ///
- /// How many fire stacks are applied per second.
- ///
- [ViewVariables(VVAccess.ReadWrite), DataField("fireStacks")]
- public float FireStacks = 1.25f;
-}
diff --git a/Content.Server/Tiles/LavaSystem.cs b/Content.Server/Tiles/LavaSystem.cs
deleted file mode 100644
index 51bd6f8475..0000000000
--- a/Content.Server/Tiles/LavaSystem.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using Content.Server.Atmos.Components;
-using Content.Server.Atmos.EntitySystems;
-using Content.Shared.StepTrigger.Systems;
-
-namespace Content.Server.Tiles;
-
-public sealed class LavaSystem : EntitySystem
-{
- [Dependency] private readonly FlammableSystem _flammable = default!;
-
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent(OnLavaStepTriggered);
- SubscribeLocalEvent(OnLavaStepTriggerAttempt);
- }
-
- private void OnLavaStepTriggerAttempt(EntityUid uid, LavaComponent component, ref StepTriggerAttemptEvent args)
- {
- if (!HasComp(args.Tripper))
- return;
-
- args.Continue = true;
- }
-
- private void OnLavaStepTriggered(EntityUid uid, LavaComponent component, ref StepTriggeredOffEvent args)
- {
- var otherUid = args.Tripper;
-
- if (TryComp(otherUid, out var flammable))
- {
- // Apply the fury of a thousand suns
- var multiplier = flammable.FireStacks == 0f ? 5f : 1f;
- _flammable.AdjustFireStacks(otherUid, component.FireStacks * multiplier, flammable);
- _flammable.Ignite(otherUid, uid, flammable);
- }
- }
-}
diff --git a/Content.Server/Tiles/TileEntityEffectComponent.cs b/Content.Server/Tiles/TileEntityEffectComponent.cs
new file mode 100644
index 0000000000..4201af47f9
--- /dev/null
+++ b/Content.Server/Tiles/TileEntityEffectComponent.cs
@@ -0,0 +1,19 @@
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+
+namespace Content.Server.Tiles;
+
+///
+/// Applies effects upon stepping onto a tile.
+///
+[RegisterComponent, Access(typeof(TileEntityEffectSystem))]
+public sealed partial class TileEntityEffectComponent : Component
+{
+ ///
+ /// List of effects that should be applied.
+ ///
+ [ViewVariables(VVAccess.ReadWrite), DataField]
+ public List Effects = default!;
+}
diff --git a/Content.Server/Tiles/TileEntityEffectSystem.cs b/Content.Server/Tiles/TileEntityEffectSystem.cs
new file mode 100644
index 0000000000..7149f16e1a
--- /dev/null
+++ b/Content.Server/Tiles/TileEntityEffectSystem.cs
@@ -0,0 +1,32 @@
+using Content.Server.Atmos.Components;
+using Content.Server.Atmos.EntitySystems;
+using Content.Shared.StepTrigger.Systems;
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
+
+namespace Content.Server.Tiles;
+
+public sealed class TileEntityEffectSystem : EntitySystem
+{
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnTileStepTriggered);
+ SubscribeLocalEvent(OnTileStepTriggerAttempt);
+ }
+ private void OnTileStepTriggerAttempt(Entity ent, ref StepTriggerAttemptEvent args)
+ {
+ args.Continue = true;
+ }
+
+ private void OnTileStepTriggered(Entity ent, ref StepTriggeredOffEvent args)
+ {
+ var otherUid = args.Tripper;
+
+ foreach (var effect in ent.Comp.Effects)
+ {
+ effect.Effect(new EntityEffectBaseArgs(otherUid, EntityManager));
+ }
+ }
+}
diff --git a/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs b/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs
index f2b13d3488..f9dfa9b273 100644
--- a/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs
+++ b/Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs
@@ -2,6 +2,7 @@ using Content.Shared.Administration.Logs;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
+using Content.Shared.EntityEffects;
using Content.Shared.FixedPoint;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes;
@@ -197,9 +198,7 @@ namespace Content.Shared.Chemistry.Reaction
private void OnReaction(Entity soln, ReactionPrototype reaction, ReagentPrototype? reagent, FixedPoint2 unitReactions)
{
- var args = new ReagentEffectArgs(soln, null, soln.Comp.Solution,
- reagent,
- unitReactions, EntityManager, null, 1f);
+ var args = new EntityEffectReagentArgs(soln, EntityManager, null, soln.Comp.Solution, unitReactions, reagent, null, 1f);
var posFound = _transformSystem.TryGetMapOrGridCoordinates(soln, out var gridPos);
@@ -213,7 +212,7 @@ namespace Content.Shared.Chemistry.Reaction
if (effect.ShouldLog)
{
- var entity = args.SolutionEntity;
+ var entity = args.TargetEntity;
_adminLogger.Add(LogType.ReagentEffect, effect.LogImpact,
$"Reaction effect {effect.GetType().Name:effect} of reaction {reaction.ID:reaction} applied on entity {ToPrettyString(entity):entity} at Pos:{(posFound ? $"{gridPos:coordinates}" : "[Grid or Map not Found")}");
}
diff --git a/Content.Shared/Chemistry/Reaction/ReactionPrototype.cs b/Content.Shared/Chemistry/Reaction/ReactionPrototype.cs
index f4e9619b17..351aa50b0f 100644
--- a/Content.Shared/Chemistry/Reaction/ReactionPrototype.cs
+++ b/Content.Shared/Chemistry/Reaction/ReactionPrototype.cs
@@ -1,5 +1,6 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
+using Content.Shared.EntityEffects;
using Content.Shared.FixedPoint;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
@@ -59,7 +60,7 @@ namespace Content.Shared.Chemistry.Reaction
///
/// Effects to be triggered when the reaction occurs.
///
- [DataField("effects", serverOnly: true)] public List Effects = new();
+ [DataField("effects", serverOnly: true)] public List Effects = new();
///
/// How dangerous is this effect? Stuff like bicaridine should be low, while things like methamphetamine
diff --git a/Content.Shared/Chemistry/Reaction/ReactiveComponent.cs b/Content.Shared/Chemistry/Reaction/ReactiveComponent.cs
index 310129c6fc..cabdee93c1 100644
--- a/Content.Shared/Chemistry/Reaction/ReactiveComponent.cs
+++ b/Content.Shared/Chemistry/Reaction/ReactiveComponent.cs
@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
@@ -33,7 +34,7 @@ public sealed partial class ReactiveReagentEffectEntry
public HashSet? Reagents = null;
[DataField("effects", required: true)]
- public List Effects = default!;
+ public List Effects = default!;
[DataField("groups", readOnly: true, serverOnly: true,
customTypeSerializer:typeof(PrototypeIdDictionarySerializer, ReactiveGroupPrototype>))]
diff --git a/Content.Shared/Chemistry/ReactiveSystem.cs b/Content.Shared/Chemistry/ReactiveSystem.cs
index 57b008de0c..edd75bb0b4 100644
--- a/Content.Shared/Chemistry/ReactiveSystem.cs
+++ b/Content.Shared/Chemistry/ReactiveSystem.cs
@@ -3,102 +3,107 @@ using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
+using Content.Shared.EntityEffects;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
-namespace Content.Shared.Chemistry
+namespace Content.Shared.Chemistry;
+
+[UsedImplicitly]
+public sealed class ReactiveSystem : EntitySystem
{
- [UsedImplicitly]
- public sealed class ReactiveSystem : EntitySystem
- {
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- [Dependency] private readonly IRobustRandom _robustRandom = default!;
- [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly IRobustRandom _robustRandom = default!;
+ [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
- public void DoEntityReaction(EntityUid uid, Solution solution, ReactionMethod method)
+ public void DoEntityReaction(EntityUid uid, Solution solution, ReactionMethod method)
+ {
+ foreach (var reagent in solution.Contents.ToArray())
{
- foreach (var reagent in solution.Contents.ToArray())
- {
- ReactionEntity(uid, method, reagent, solution);
- }
+ ReactionEntity(uid, method, reagent, solution);
}
+ }
- public void ReactionEntity(EntityUid uid, ReactionMethod method, ReagentQuantity reagentQuantity, Solution? source)
- {
- // We throw if the reagent specified doesn't exist.
- var proto = _prototypeManager.Index(reagentQuantity.Reagent.Prototype);
- ReactionEntity(uid, method, proto, reagentQuantity, source);
- }
+ public void ReactionEntity(EntityUid uid, ReactionMethod method, ReagentQuantity reagentQuantity, Solution? source)
+ {
+ // We throw if the reagent specified doesn't exist.
+ var proto = _prototypeManager.Index(reagentQuantity.Reagent.Prototype);
+ ReactionEntity(uid, method, proto, reagentQuantity, source);
+ }
- public void ReactionEntity(EntityUid uid, ReactionMethod method, ReagentPrototype proto,
- ReagentQuantity reagentQuantity, Solution? source)
- {
- if (!TryComp(uid, out ReactiveComponent? reactive))
- return;
+ public void ReactionEntity(EntityUid uid, ReactionMethod method, ReagentPrototype proto,
+ ReagentQuantity reagentQuantity, Solution? source)
+ {
+ if (!TryComp(uid, out ReactiveComponent? reactive))
+ return;
- // If we have a source solution, use the reagent quantity we have left. Otherwise, use the reaction volume specified.
- var args = new ReagentEffectArgs(uid, null, source, proto,
- source?.GetReagentQuantity(reagentQuantity.Reagent) ?? reagentQuantity.Quantity, EntityManager, method, 1f);
+ // If we have a source solution, use the reagent quantity we have left. Otherwise, use the reaction volume specified.
+ var args = new EntityEffectReagentArgs(uid, EntityManager, null, source, source?.GetReagentQuantity(reagentQuantity.Reagent) ?? reagentQuantity.Quantity, proto, method, 1f);
- // First, check if the reagent wants to apply any effects.
- if (proto.ReactiveEffects != null && reactive.ReactiveGroups != null)
+ // First, check if the reagent wants to apply any effects.
+ if (proto.ReactiveEffects != null && reactive.ReactiveGroups != null)
+ {
+ foreach (var (key, val) in proto.ReactiveEffects)
{
- foreach (var (key, val) in proto.ReactiveEffects)
- {
- if (!val.Methods.Contains(method))
- continue;
+ if (!val.Methods.Contains(method))
+ continue;
- if (!reactive.ReactiveGroups.ContainsKey(key))
- continue;
+ if (!reactive.ReactiveGroups.ContainsKey(key))
+ continue;
- if (!reactive.ReactiveGroups[key].Contains(method))
+ if (!reactive.ReactiveGroups[key].Contains(method))
+ continue;
+
+ foreach (var effect in val.Effects)
+ {
+ if (!effect.ShouldApply(args, _robustRandom))
continue;
- foreach (var effect in val.Effects)
+ if (effect.ShouldLog)
{
- if (!effect.ShouldApply(args, _robustRandom))
- continue;
-
- if (effect.ShouldLog)
- {
- var entity = args.SolutionEntity;
- _adminLogger.Add(LogType.ReagentEffect, effect.LogImpact,
- $"Reactive effect {effect.GetType().Name:effect} of reagent {proto.ID:reagent} with method {method} applied on entity {ToPrettyString(entity):entity} at {Transform(entity).Coordinates:coordinates}");
- }
-
- effect.Effect(args);
+ var entity = args.TargetEntity;
+ _adminLogger.Add(LogType.ReagentEffect, effect.LogImpact,
+ $"Reactive effect {effect.GetType().Name:effect} of reagent {proto.ID:reagent} with method {method} applied on entity {ToPrettyString(entity):entity} at {Transform(entity).Coordinates:coordinates}");
}
+
+ effect.Effect(args);
}
}
+ }
- // Then, check if the prototype has any effects it can apply as well.
- if (reactive.Reactions != null)
+ // Then, check if the prototype has any effects it can apply as well.
+ if (reactive.Reactions != null)
+ {
+ foreach (var entry in reactive.Reactions)
{
- foreach (var entry in reactive.Reactions)
- {
- if (!entry.Methods.Contains(method))
- continue;
+ if (!entry.Methods.Contains(method))
+ continue;
+
+ if (entry.Reagents != null && !entry.Reagents.Contains(proto.ID))
+ continue;
- if (entry.Reagents != null && !entry.Reagents.Contains(proto.ID))
+ foreach (var effect in entry.Effects)
+ {
+ if (!effect.ShouldApply(args, _robustRandom))
continue;
- foreach (var effect in entry.Effects)
+ if (effect.ShouldLog)
{
- if (!effect.ShouldApply(args, _robustRandom))
- continue;
-
- if (effect.ShouldLog)
- {
- var entity = args.SolutionEntity;
- _adminLogger.Add(LogType.ReagentEffect, effect.LogImpact,
- $"Reactive effect {effect.GetType().Name:effect} of {ToPrettyString(entity):entity} using reagent {proto.ID:reagent} with method {method} at {Transform(entity).Coordinates:coordinates}");
- }
-
- effect.Effect(args);
+ var entity = args.TargetEntity;
+ _adminLogger.Add(LogType.ReagentEffect, effect.LogImpact,
+ $"Reactive effect {effect.GetType().Name:effect} of {ToPrettyString(entity):entity} using reagent {proto.ID:reagent} with method {method} at {Transform(entity).Coordinates:coordinates}");
}
+
+ effect.Effect(args);
}
}
}
}
}
+public enum ReactionMethod
+{
+Touch,
+Injection,
+Ingestion,
+}
diff --git a/Content.Shared/Chemistry/Reagent/ReagentEffect.cs b/Content.Shared/Chemistry/Reagent/ReagentEffect.cs
deleted file mode 100644
index 6d0e85df37..0000000000
--- a/Content.Shared/Chemistry/Reagent/ReagentEffect.cs
+++ /dev/null
@@ -1,112 +0,0 @@
-using System.Linq;
-using System.Text.Json.Serialization;
-using Content.Shared.Chemistry.Components;
-using Content.Shared.Database;
-using Content.Shared.FixedPoint;
-using Content.Shared.Localizations;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Random;
-
-namespace Content.Shared.Chemistry.Reagent
-{
- ///
- /// Reagent effects describe behavior that occurs when a reagent is ingested and metabolized by some
- /// organ. They only trigger when all of are satisfied.
- ///
- [ImplicitDataDefinitionForInheritors]
- [MeansImplicitUse]
- public abstract partial class ReagentEffect
- {
- [JsonPropertyName("id")] private protected string _id => this.GetType().Name;
- ///
- /// The list of conditions required for the effect to activate. Not required.
- ///
- [JsonPropertyName("conditions")]
- [DataField("conditions")]
- public ReagentEffectCondition[]? Conditions;
-
- public virtual string ReagentEffectFormat => "guidebook-reagent-effect-description";
-
- protected abstract string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys);
-
- ///
- /// What's the chance, from 0 to 1, that this effect will occur?
- ///
- [JsonPropertyName("probability")]
- [DataField("probability")]
- public float Probability = 1.0f;
-
- [JsonIgnore]
- [DataField("logImpact")]
- public virtual LogImpact LogImpact { get; private set; } = LogImpact.Low;
-
- ///
- /// Should this reagent effect log at all?
- ///
- [JsonIgnore]
- [DataField("shouldLog")]
- public virtual bool ShouldLog { get; private set; } = false;
-
- public abstract void Effect(ReagentEffectArgs args);
-
- ///
- /// Produces a localized, bbcode'd guidebook description for this effect.
- ///
- ///
- public string? GuidebookEffectDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
- {
- var effect = ReagentEffectGuidebookText(prototype, entSys);
- if (effect is null)
- return null;
-
- return Loc.GetString(ReagentEffectFormat, ("effect", effect), ("chance", Probability),
- ("conditionCount", Conditions?.Length ?? 0),
- ("conditions",
- ContentLocalizationManager.FormatList(Conditions?.Select(x => x.GuidebookExplanation(prototype)).ToList() ??
- new List())));
- }
- }
-
- public static class ReagentEffectExt
- {
- public static bool ShouldApply(this ReagentEffect effect, ReagentEffectArgs args,
- IRobustRandom? random = null)
- {
- if (random == null)
- random = IoCManager.Resolve();
-
- if (effect.Probability < 1.0f && !random.Prob(effect.Probability))
- return false;
-
- if (effect.Conditions != null)
- {
- foreach (var cond in effect.Conditions)
- {
- if (!cond.Condition(args))
- return false;
- }
- }
-
- return true;
- }
- }
-
- public enum ReactionMethod
- {
- Touch,
- Injection,
- Ingestion,
- }
-
- public readonly record struct ReagentEffectArgs(
- EntityUid SolutionEntity,
- EntityUid? OrganEntity,
- Solution? Source,
- ReagentPrototype? Reagent,
- FixedPoint2 Quantity,
- IEntityManager EntityManager,
- ReactionMethod? Method,
- float Scale
- );
-}
diff --git a/Content.Shared/Chemistry/Reagent/ReagentEffectCondition.cs b/Content.Shared/Chemistry/Reagent/ReagentEffectCondition.cs
deleted file mode 100644
index a32b4fa8e3..0000000000
--- a/Content.Shared/Chemistry/Reagent/ReagentEffectCondition.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System.Text.Json.Serialization;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-
-namespace Content.Shared.Chemistry.Reagent
-{
- [ImplicitDataDefinitionForInheritors]
- [MeansImplicitUse]
- public abstract partial class ReagentEffectCondition
- {
- [JsonPropertyName("id")] private protected string _id => this.GetType().Name;
-
- public abstract bool Condition(ReagentEffectArgs args);
-
- ///
- /// Effect explanations are of the form "[chance to] [action] when [condition] and [condition]"
- ///
- ///
- ///
- public abstract string GuidebookExplanation(IPrototypeManager prototype);
- }
-}
diff --git a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs
index 7e96332ebe..fe937b9de4 100644
--- a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs
+++ b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs
@@ -1,10 +1,11 @@
-using System.Collections.Frozen;
+using System.Collections.Frozen;
using System.Linq;
using System.Text.Json.Serialization;
using Content.Shared.Administration.Logs;
using Content.Shared.Body.Prototypes;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reaction;
+using Content.Shared.EntityEffects;
using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Nutrition;
@@ -134,7 +135,7 @@ namespace Content.Shared.Chemistry.Reagent
public List TileReactions = new(0);
[DataField("plantMetabolism", serverOnly: true)]
- public List PlantMetabolisms = new(0);
+ public List PlantMetabolisms = new(0);
[DataField]
public float PricePerUnit;
@@ -170,7 +171,7 @@ namespace Content.Shared.Chemistry.Reagent
var entMan = IoCManager.Resolve();
var random = IoCManager.Resolve();
- var args = new ReagentEffectArgs(plantHolder.Value, null, solution, this, amount.Quantity, entMan, null, 1f);
+ var args = new EntityEffectReagentArgs(plantHolder.Value, entMan, null, solution, amount.Quantity, this, null, 1f);
foreach (var plantMetabolizable in PlantMetabolisms)
{
if (!plantMetabolizable.ShouldApply(args, random))
@@ -178,7 +179,7 @@ namespace Content.Shared.Chemistry.Reagent
if (plantMetabolizable.ShouldLog)
{
- var entity = args.SolutionEntity;
+ var entity = args.TargetEntity;
entMan.System().Add(LogType.ReagentEffect, plantMetabolizable.LogImpact,
$"Plant metabolism effect {plantMetabolizable.GetType().Name:effect} of reagent {ID:reagent} applied on entity {entMan.ToPrettyString(entity):entity} at {entMan.GetComponent(entity).Coordinates:coordinates}");
}
@@ -230,7 +231,7 @@ namespace Content.Shared.Chemistry.Reagent
///
[JsonPropertyName("effects")]
[DataField("effects", required: true)]
- public ReagentEffect[] Effects = default!;
+ public EntityEffect[] Effects = default!;
public ReagentEffectsGuideEntry MakeGuideEntry(IPrototypeManager prototype, IEntitySystemManager entSys)
{
@@ -264,6 +265,6 @@ namespace Content.Shared.Chemistry.Reagent
public HashSet Methods = default!;
[DataField("effects", required: true)]
- public ReagentEffect[] Effects = default!;
+ public EntityEffect[] Effects = default!;
}
}
diff --git a/Content.Shared/EntityEffects/EntityEffect.cs b/Content.Shared/EntityEffects/EntityEffect.cs
new file mode 100644
index 0000000000..21e79f224d
--- /dev/null
+++ b/Content.Shared/EntityEffects/EntityEffect.cs
@@ -0,0 +1,132 @@
+using System.Linq;
+using System.Text.Json.Serialization;
+using Content.Shared.Chemistry;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Database;
+using Content.Shared.FixedPoint;
+using Content.Shared.Localizations;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+using Content.Shared.Chemistry.Reagent;
+using Robust.Shared.Toolshed.TypeParsers;
+
+namespace Content.Shared.EntityEffects;
+
+///
+/// Entity effects describe behavior that occurs on different kinds of triggers, e.g. when a reagent is ingested and metabolized by some
+/// organ. They only trigger when all of are satisfied.
+///
+[ImplicitDataDefinitionForInheritors]
+[MeansImplicitUse]
+public abstract partial class EntityEffect
+{
+ private protected string _id => this.GetType().Name;
+ ///
+ /// The list of conditions required for the effect to activate. Not required.
+ ///
+ [DataField("conditions")]
+ public EntityEffectCondition[]? Conditions;
+
+ public virtual string ReagentEffectFormat => "guidebook-reagent-effect-description";
+
+ protected abstract string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys);
+
+ ///
+ /// What's the chance, from 0 to 1, that this effect will occur?
+ ///
+ [DataField("probability")]
+ public float Probability = 1.0f;
+
+ public virtual LogImpact LogImpact { get; private set; } = LogImpact.Low;
+
+ ///
+ /// Should this entity effect log at all?
+ ///
+ public virtual bool ShouldLog { get; private set; } = false;
+
+ public abstract void Effect(EntityEffectBaseArgs args);
+
+ ///
+ /// Produces a localized, bbcode'd guidebook description for this effect.
+ ///
+ ///
+ public string? GuidebookEffectDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
+ {
+ var effect = ReagentEffectGuidebookText(prototype, entSys);
+ if (effect is null)
+ return null;
+
+ return Loc.GetString(ReagentEffectFormat, ("effect", effect), ("chance", Probability),
+ ("conditionCount", Conditions?.Length ?? 0),
+ ("conditions",
+ ContentLocalizationManager.FormatList(Conditions?.Select(x => x.GuidebookExplanation(prototype)).ToList() ??
+ new List())));
+ }
+}
+
+public static class EntityEffectExt
+{
+ public static bool ShouldApply(this EntityEffect effect, EntityEffectBaseArgs args,
+ IRobustRandom? random = null)
+ {
+ if (random == null)
+ random = IoCManager.Resolve();
+
+ if (effect.Probability < 1.0f && !random.Prob(effect.Probability))
+ return false;
+
+ if (effect.Conditions != null)
+ {
+ foreach (var cond in effect.Conditions)
+ {
+ if (!cond.Condition(args))
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+
+///
+/// EntityEffectBaseArgs only contains the target of an effect.
+/// If a trigger wants to include more info (e.g. the quantity of the chemical triggering the effect), it can be extended (see EntityEffectReagentArgs).
+///
+public record class EntityEffectBaseArgs
+{
+ public EntityUid TargetEntity;
+
+ public IEntityManager EntityManager = default!;
+
+ public EntityEffectBaseArgs(EntityUid targetEntity, IEntityManager entityManager)
+ {
+ TargetEntity = targetEntity;
+ EntityManager = entityManager;
+ }
+}
+
+public record class EntityEffectReagentArgs : EntityEffectBaseArgs
+{
+ public EntityUid? OrganEntity;
+
+ public Solution? Source;
+
+ public FixedPoint2 Quantity;
+
+ public ReagentPrototype? Reagent;
+
+ public ReactionMethod? Method;
+
+ public FixedPoint2 Scale;
+
+ public EntityEffectReagentArgs(EntityUid targetEntity, IEntityManager entityManager, EntityUid? organEntity, Solution? source, FixedPoint2 quantity, ReagentPrototype? reagent, ReactionMethod? method, FixedPoint2 scale) : base(targetEntity, entityManager)
+ {
+ OrganEntity = organEntity;
+ Source = source;
+ Quantity = quantity;
+ Reagent = reagent;
+ Method = method;
+ Scale = scale;
+ }
+}
diff --git a/Content.Shared/EntityEffects/EntityEffectCondition.cs b/Content.Shared/EntityEffects/EntityEffectCondition.cs
new file mode 100644
index 0000000000..d6028b9f2c
--- /dev/null
+++ b/Content.Shared/EntityEffects/EntityEffectCondition.cs
@@ -0,0 +1,22 @@
+using System.Text.Json.Serialization;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects;
+
+[ImplicitDataDefinitionForInheritors]
+[MeansImplicitUse]
+public abstract partial class EntityEffectCondition
+{
+ [JsonPropertyName("id")] private protected string _id => this.GetType().Name;
+
+ public abstract bool Condition(EntityEffectBaseArgs args);
+
+ ///
+ /// Effect explanations are of the form "[chance to] [action] when [condition] and [condition]"
+ ///
+ ///
+ ///
+ public abstract string GuidebookExplanation(IPrototypeManager prototype);
+}
+
diff --git a/Resources/Prototypes/Entities/Tiles/lava.yml b/Resources/Prototypes/Entities/Tiles/lava.yml
index 72641309b3..36c7b80b81 100644
--- a/Resources/Prototypes/Entities/Tiles/lava.yml
+++ b/Resources/Prototypes/Entities/Tiles/lava.yml
@@ -13,8 +13,12 @@
blacklist:
tags:
- Catwalk
- - type: Lava
- fireStacks: 0.75
+ - type: TileEntityEffect
+ effects:
+ - !type:FlammableReaction
+ multiplier: 3.75
+ multiplierOnExisting: 0.75
+ - !type:Ignite
- type: Transform
anchored: true
- type: SyncSprite
diff --git a/Resources/Prototypes/Entities/Tiles/liquid_plasma.yml b/Resources/Prototypes/Entities/Tiles/liquid_plasma.yml
index 08f4653863..869db08597 100644
--- a/Resources/Prototypes/Entities/Tiles/liquid_plasma.yml
+++ b/Resources/Prototypes/Entities/Tiles/liquid_plasma.yml
@@ -1,4 +1,4 @@
-- type: entity
+- type: entity
id: FloorLiquidPlasmaEntity
name: liquid plasma
description: Sweet, expensive nectar. Don't consume.
@@ -13,8 +13,12 @@
blacklist:
tags:
- Catwalk
- - type: Lava
- fireStacks: 0.75
+ - type: TileEntityEffect
+ effects:
+ - !type:FlammableReaction
+ multiplier: 3.75
+ multiplierOnExisting: 0.75
+ - !type:Ignite
- type: Transform
anchored: true
- type: SyncSprite