]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Turn ReagentEffects into generic EntityEffects (#28168)
authorSlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com>
Sun, 30 Jun 2024 03:43:43 +0000 (05:43 +0200)
committerGitHub <noreply@github.com>
Sun, 30 Jun 2024 03:43:43 +0000 (13:43 +1000)
* 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)

127 files changed:
Content.Server/Body/Systems/BloodstreamSystem.cs
Content.Server/Body/Systems/MetabolizerSystem.cs
Content.Server/Body/Systems/RespiratorSystem.cs
Content.Server/Botany/SeedPrototype.cs
Content.Server/Chemistry/EntitySystems/HypospraySystem.cs
Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs [deleted file]
Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs [deleted file]
Content.Server/Chemistry/ReactionEffects/SolutionTemperatureEffects.cs [deleted file]
Content.Server/Chemistry/ReagentEffectConditions/BodyTemperature.cs [deleted file]
Content.Server/Chemistry/ReagentEffectConditions/JobCondition.cs [deleted file]
Content.Server/Chemistry/ReagentEffectConditions/MobStateCondition.cs [deleted file]
Content.Server/Chemistry/ReagentEffectConditions/OrganType.cs [deleted file]
Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs [deleted file]
Content.Server/Chemistry/ReagentEffectConditions/SolutionTemperature.cs [deleted file]
Content.Server/Chemistry/ReagentEffectConditions/TotalDamage.cs [deleted file]
Content.Server/Chemistry/ReagentEffectConditions/TotalHunger.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/AddToSolutionReaction.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/ChemCleanBloodstream.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/ChemHealEyeDamage.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/Electrocute.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/ExtinguishReaction.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/FlammableReaction.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/ModifyLungGas.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/Oxygenate.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustAttribute.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustHealth.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustMutationLevel.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustMutationMod.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustNutrition.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustPests.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustToxins.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustWater.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustWeeds.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAffectGrowth.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantCryoxadone.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantDiethylamine.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantPhalanximine.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/PlantMetabolism/RobustHarvest.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/StatusEffects/GenericStatusEffect.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/StatusEffects/Jitter.cs [deleted file]
Content.Server/Chemistry/ReagentEffects/WashCreamPieReaction.cs [deleted file]
Content.Server/EntityEffects/EffectConditions/BodyTemperature.cs [new file with mode: 0644]
Content.Server/EntityEffects/EffectConditions/HasTagCondition.cs [moved from Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs with 76% similarity]
Content.Server/EntityEffects/EffectConditions/JobCondition.cs [new file with mode: 0644]
Content.Server/EntityEffects/EffectConditions/MobStateCondition.cs [new file with mode: 0644]
Content.Server/EntityEffects/EffectConditions/OrganType.cs [new file with mode: 0644]
Content.Server/EntityEffects/EffectConditions/ReagentThreshold.cs [new file with mode: 0644]
Content.Server/EntityEffects/EffectConditions/SolutionTemperature.cs [new file with mode: 0644]
Content.Server/EntityEffects/EffectConditions/TotalDamage.cs [new file with mode: 0644]
Content.Server/EntityEffects/EffectConditions/TotalHunger.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/ActivateArtifact.cs [moved from Content.Server/Chemistry/ReagentEffects/ActivateArtifact.cs with 56% similarity]
Content.Server/EntityEffects/Effects/AddToSolutionReaction.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/AdjustAlert.cs [moved from Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs with 76% similarity]
Content.Server/EntityEffects/Effects/AdjustReagent.cs [moved from Content.Server/Chemistry/ReagentEffects/AdjustReagent.cs with 58% similarity]
Content.Server/EntityEffects/Effects/AdjustTemperature.cs [moved from Content.Server/Chemistry/ReagentEffects/AdjustTemperature.cs with 56% similarity]
Content.Server/EntityEffects/Effects/AreaReactionEffect.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/CauseZombieInfection.cs [moved from Content.Server/Chemistry/ReagentEffects/CauseZombieInfection.cs with 65% similarity]
Content.Server/EntityEffects/Effects/ChemCleanBloodstream.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/ChemHealEyeDamage.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/ChemVomit.cs [moved from Content.Server/Chemistry/ReagentEffects/ChemVomit.cs with 65% similarity]
Content.Server/EntityEffects/Effects/CreateEntityReactionEffect.cs [moved from Content.Server/Chemistry/ReactionEffects/CreateEntityReactionEffect.cs with 80% similarity]
Content.Server/EntityEffects/Effects/CreateGas.cs [moved from Content.Server/Chemistry/ReagentEffects/CreateGas.cs with 62% similarity]
Content.Server/EntityEffects/Effects/CureZombieInfection.cs [moved from Content.Server/Chemistry/ReagentEffects/CureZombieInfection.cs with 73% similarity]
Content.Server/EntityEffects/Effects/Drunk.cs [moved from Content.Server/Chemistry/ReagentEffects/Drunk.cs with 64% similarity]
Content.Server/EntityEffects/Effects/Electrocute.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/Emote.cs [moved from Content.Server/Chemistry/ReagentEffects/Emote.cs with 72% similarity]
Content.Server/EntityEffects/Effects/EmpReactionEffect.cs [moved from Content.Server/Chemistry/ReactionEffects/EmpReactionEffect.cs with 67% similarity]
Content.Server/EntityEffects/Effects/ExplosionReactionEffect.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/ExtinguishReaction.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/FlammableReaction.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/HealthChange.cs [moved from Content.Server/Chemistry/ReagentEffects/HealthChange.cs with 84% similarity]
Content.Server/EntityEffects/Effects/Ignite.cs [moved from Content.Server/Chemistry/ReagentEffects/Ignite.cs with 50% similarity]
Content.Server/EntityEffects/Effects/MakeSentient.cs [moved from Content.Server/Chemistry/ReagentEffects/MakeSentient.cs with 87% similarity]
Content.Server/EntityEffects/Effects/ModifyBleedAmount.cs [moved from Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs with 50% similarity]
Content.Server/EntityEffects/Effects/ModifyBloodLevel.cs [moved from Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs with 52% similarity]
Content.Server/EntityEffects/Effects/ModifyLungGas.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/MovespeedModifier.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/Oxygenate.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/Paralyze.cs [moved from Content.Server/Chemistry/ReagentEffects/Paralyze.cs with 62% similarity]
Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustAttribute.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustHealth.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustMutationLevel.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustMutationMod.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustNutrition.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustPests.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustToxins.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustWater.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAdjustWeeds.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/PlantMetabolism/PlantAffectGrowth.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/PlantMetabolism/PlantCryoxadone.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/PlantMetabolism/PlantDiethylamine.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/PlantMetabolism/PlantPhalanximine.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/PlantMetabolism/RobustHarvest.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/Polymorph.cs [moved from Content.Server/Chemistry/ReagentEffects/Polymorph.cs with 80% similarity]
Content.Server/EntityEffects/Effects/PopupMessage.cs [moved from Content.Server/Chemistry/ReagentEffects/PopupMessage.cs with 60% similarity]
Content.Server/EntityEffects/Effects/ReduceRotting.cs [moved from Content.Server/Chemistry/ReagentEffects/ReduceRotting.cs with 61% similarity]
Content.Server/EntityEffects/Effects/ResetNarcolepsy.cs [moved from Content.Server/Chemistry/ReagentEffects/ResetNarcolepsy.cs with 63% similarity]
Content.Server/EntityEffects/Effects/SatiateHunger.cs [moved from Content.Server/Chemistry/ReagentEffects/SatiateHunger.cs with 53% similarity]
Content.Server/EntityEffects/Effects/SatiateThirst.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/SolutionTemperatureEffects.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/StatusEffects/GenericStatusEffect.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/StatusEffects/Jitter.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/WashCreamPieReaction.cs [new file with mode: 0644]
Content.Server/EntityEffects/Effects/WearableReaction.cs [moved from Content.Server/Chemistry/ReagentEffects/WearableReaction.cs with 50% similarity]
Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs
Content.Server/Fluids/EntitySystems/SmokeSystem.cs
Content.Server/GuideGenerator/ChemistryJsonGenerator.cs
Content.Server/GuideGenerator/ReactionJsonGenerator.cs
Content.Server/GuideGenerator/ReagentEntry.cs
Content.Server/Nutrition/EntitySystems/DrinkSystem.cs
Content.Server/Tiles/LavaComponent.cs [deleted file]
Content.Server/Tiles/LavaSystem.cs [deleted file]
Content.Server/Tiles/TileEntityEffectComponent.cs [new file with mode: 0644]
Content.Server/Tiles/TileEntityEffectSystem.cs [new file with mode: 0644]
Content.Shared/Chemistry/Reaction/ChemicalReactionSystem.cs
Content.Shared/Chemistry/Reaction/ReactionPrototype.cs
Content.Shared/Chemistry/Reaction/ReactiveComponent.cs
Content.Shared/Chemistry/ReactiveSystem.cs
Content.Shared/Chemistry/Reagent/ReagentEffect.cs [deleted file]
Content.Shared/Chemistry/Reagent/ReagentEffectCondition.cs [deleted file]
Content.Shared/Chemistry/Reagent/ReagentPrototype.cs
Content.Shared/EntityEffects/EntityEffect.cs [new file with mode: 0644]
Content.Shared/EntityEffects/EntityEffectCondition.cs [new file with mode: 0644]
Resources/Prototypes/Entities/Tiles/lava.yml
Resources/Prototypes/Entities/Tiles/liquid_plasma.yml

index eaa7b62f25ae9e40683d5090d69c898ef28ffa4a..a8a86028d40b66c7411d3f3d8db444c7d7665668 100644 (file)
@@ -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;
index 8394d9999bcf162b5d9c2f7b9a23bfe8c1f77cad..5ba3c6ee58228b35a57a0f57c3eb78faf8e13201 100644 (file)
@@ -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)
index 5461f68db2f85f8e62debc683b3a301925fad9cb..5fbd0c48147eda45eb0aa9831afffc044759d911 100644 (file)
@@ -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;
index 0ae56bc5fc652845b17c100284b951ccd1f39b3b..b2882d8e944102bd5aa9fd1c2d7feedea9b6624e 100644 (file)
@@ -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
index 7b70497c7d3fab9e3b6f186db3eccff823177862..c5bdb39816ec44fd5de2eb6e4eabe35e4db301c3 100644 (file)
@@ -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 (file)
index 56509a0..0000000
+++ /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
-{
-    /// <summary>
-    /// Basically smoke and foam reactions.
-    /// </summary>
-    [UsedImplicitly]
-    [DataDefinition]
-    public sealed partial class AreaReactionEffect : ReagentEffect
-    {
-        /// <summary>
-        /// How many seconds will the effect stay, counting after fully spreading.
-        /// </summary>
-        [DataField("duration")] private float _duration = 10;
-
-        /// <summary>
-        /// How many units of reaction for 1 smoke entity.
-        /// </summary>
-        [DataField] public FixedPoint2 OverflowThreshold = FixedPoint2.New(2.5);
-
-        /// <summary>
-        /// The entity prototype that will be spawned as the effect.
-        /// </summary>
-        [DataField("prototypeId", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
-        private string _prototypeId = default!;
-
-        /// <summary>
-        /// Sound that will get played when this reaction effect occurs.
-        /// </summary>
-        [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<TransformComponent>(args.SolutionEntity);
-            var mapManager = IoCManager.Resolve<IMapManager>();
-            var mapSys = args.EntityManager.System<MapSystem>();
-            var sys = args.EntityManager.System<TransformSystem>();
-            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<SmokeSystem>();
-            smoke.StartSmoke(ent, splitSolution, _duration, spreadAmount);
-
-            var audio = args.EntityManager.System<SharedAudioSystem>();
-            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 (file)
index 2771f79..0000000
+++ /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
-    {
-        /// <summary>
-        ///     The type of explosion. Determines damage types and tile break chance scaling.
-        /// </summary>
-        [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<ExplosionPrototype>))]
-        [JsonIgnore]
-        public string ExplosionType = default!;
-
-        /// <summary>
-        ///     The max intensity the explosion can have at a given tile. Places an upper limit of damage and tile break
-        ///     chance.
-        /// </summary>
-        [DataField]
-        [JsonIgnore]
-        public float MaxIntensity = 5;
-
-        /// <summary>
-        ///     How quickly intensity drops off as you move away from the epicenter
-        /// </summary>
-        [DataField]
-        [JsonIgnore]
-        public float IntensitySlope = 1;
-
-        /// <summary>
-        ///     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.
-        /// </summary>
-        /// <remarks>
-        ///     A slope of 1 and MaxTotalIntensity of 100 corresponds to a radius of around 4.5 tiles.
-        /// </remarks>
-        [DataField]
-        [JsonIgnore]
-        public float MaxTotalIntensity = 100;
-
-        /// <summary>
-        ///     The intensity of the explosion per unit reaction.
-        /// </summary>
-        [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<ExplosionSystem>()
-                .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 (file)
index ec58754..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReactionEffects
-{
-    /// <summary>
-    ///     Sets the temperature of the solution involved with the reaction to a new value.
-    /// </summary>
-    [DataDefinition]
-    public sealed partial class SetSolutionTemperatureEffect : ReagentEffect
-    {
-        /// <summary>
-        ///     The temperature to set the solution to.
-        /// </summary>
-        [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;
-        }
-    }
-
-    /// <summary>
-    ///     Adjusts the temperature of the solution involved in the reaction.
-    /// </summary>
-    [DataDefinition]
-    public sealed partial class AdjustSolutionTemperatureEffect : ReagentEffect
-    {
-        /// <summary>
-        ///     The change in temperature.
-        /// </summary>
-        [DataField("delta", required: true)] private float _delta;
-
-        /// <summary>
-        ///     The minimum temperature this effect can reach.
-        /// </summary>
-        [DataField("minTemp")] private float _minTemp = 0.0f;
-
-        /// <summary>
-        ///     The maximum temperature this effect can reach.
-        /// </summary>
-        [DataField("maxTemp")] private float _maxTemp = float.PositiveInfinity;
-
-        /// <summary>
-        ///     If true, then scale ranges by intensity. If not, the ranges are the same regardless of reactant amount.
-        /// </summary>
-        [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);
-        }
-    }
-
-    /// <summary>
-    ///     Adjusts the thermal energy of the solution involved in the reaction.
-    /// </summary>
-    public sealed partial class AdjustSolutionThermalEnergyEffect : ReagentEffect
-    {
-        /// <summary>
-        ///     The change in energy.
-        /// </summary>
-        [DataField("delta", required: true)] private float _delta;
-
-        /// <summary>
-        ///     The minimum temperature this effect can reach.
-        /// </summary>
-        [DataField("minTemp")] private float _minTemp = 0.0f;
-
-        /// <summary>
-        ///     The maximum temperature this effect can reach.
-        /// </summary>
-        [DataField("maxTemp")] private float _maxTemp = float.PositiveInfinity;
-
-        /// <summary>
-        ///     If true, then scale ranges by intensity. If not, the ranges are the same regardless of reactant amount.
-        /// </summary>
-        [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 (file)
index 9c47bb5..0000000
+++ /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
-{
-    /// <summary>
-    ///     Requires the solution entity to be above or below a certain temperature.
-    ///     Used for things like cryoxadone and pyroxadone.
-    /// </summary>
-    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 (file)
index 0ede690..0000000
+++ /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<ProtoId<JobPrototype>> Job;
-                
-        public override bool Condition(ReagentEffectArgs args)
-        {   
-            args.EntityManager.TryGetComponent<MindContainerComponent>(args.SolutionEntity, out var mindContainer);
-            if (mindContainer != null && mindContainer.Mind != null)
-            {
-                var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
-                if (args.EntityManager.TryGetComponent<JobComponent>(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 (file)
index 377eb7d..0000000
+++ /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 (file)
index 986c3d7..0000000
+++ /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
-{
-    /// <summary>
-    ///     Requires that the metabolizing organ is or is not tagged with a certain MetabolizerType
-    /// </summary>
-    public sealed partial class OrganType : ReagentEffectCondition
-    {
-        [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<MetabolizerTypePrototype>))]
-        public string Type = default!;
-
-        /// <summary>
-        ///     Does this condition pass when the organ has the type, or when it doesn't have the type?
-        /// </summary>
-        [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<MetabolizerComponent?> metabolizer, IEntityManager entMan)
-        {
-            metabolizer.Comp ??= entMan.GetComponentOrNull<MetabolizerComponent>(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<MetabolizerTypePrototype>(Type).LocalizedName),
-                ("shouldhave", ShouldHave));
-        }
-    }
-}
diff --git a/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs b/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs
deleted file mode 100644 (file)
index 6ac1549..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.FixedPoint;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffectConditions
-{
-    /// <summary>
-    ///     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.
-    /// </summary>
-    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 (file)
index 4387f2b..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Chemistry.ReagentEffectConditions
-{
-    /// <summary>
-    ///     Requires the solution to be above or below a certain temperature.
-    ///     Used for things like explosives.
-    /// </summary>
-    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 (file)
index 49630ef..0000000
+++ /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 (file)
index 1dd12e6..0000000
+++ /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 (file)
index 2447814..0000000
+++ /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<SolutionContainerSystem>();
-            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 (file)
index ace73c8..0000000
+++ /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
-{
-    /// <summary>
-    /// Basically smoke and foam reactions.
-    /// </summary>
-    [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<BloodstreamSystem>();
-            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 (file)
index 206b8ed..0000000
+++ /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
-{
-    /// <summary>
-    /// Heal or apply eye damage
-    /// </summary>
-    [UsedImplicitly]
-    public sealed partial class ChemHealEyeDamage : ReagentEffect
-    {
-        /// <summary>
-        /// How much eye damage to add.
-        /// </summary>
-        [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<BlindableSystem>().AdjustEyeDamage(args.SolutionEntity, Amount);
-        }
-    }
-}
diff --git a/Content.Server/Chemistry/ReagentEffects/Electrocute.cs b/Content.Server/Chemistry/ReagentEffects/Electrocute.cs
deleted file mode 100644 (file)
index 272a0fc..0000000
+++ /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;
-
-    /// <remarks>
-    ///     true - refresh electrocute time,  false - accumulate electrocute time
-    /// </remarks>
-    [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<ElectrocutionSystem>().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 (file)
index 69aaf5c..0000000
+++ /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>();
-            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 (file)
index 5b36967..0000000
+++ /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<FlammableSystem>().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 (file)
index e7466fb..0000000
+++ /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<Gas, float> _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<LungComponent>(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 (file)
index e538a01..0000000
+++ /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
-{
-    /// <summary>
-    /// Default metabolism for stimulants and tranqs. Attempts to find a MovementSpeedModifier on the target,
-    /// adding one if not there and to change the movespeed
-    /// </summary>
-    public sealed partial class MovespeedModifier : ReagentEffect
-    {
-        /// <summary>
-        /// How much the entities' walk speed is multiplied by.
-        /// </summary>
-        [DataField]
-        public float WalkSpeedModifier { get; set; } = 1;
-
-        /// <summary>
-        /// How much the entities' run speed is multiplied by.
-        /// </summary>
-        [DataField]
-        public float SprintSpeedModifier { get; set; } = 1;
-
-        /// <summary>
-        /// How long the modifier applies (in seconds) when metabolized.
-        /// </summary>
-        [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));
-        }
-
-        /// <summary>
-        /// Remove reagent at set rate, changes the movespeed modifiers and adds a MovespeedModifierMetabolismComponent if not already there.
-        /// </summary>
-        public override void Effect(ReagentEffectArgs args)
-        {
-            var status = args.EntityManager.EnsureComponent<MovespeedModifierMetabolismComponent>(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<MovementSpeedModifierSystem>().RefreshMovementSpeedModifiers(args.SolutionEntity);
-
-        }
-        public void IncreaseTimer(MovespeedModifierMetabolismComponent status, float time)
-        {
-            var gameTiming = IoCManager.Resolve<IGameTiming>();
-
-            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 (file)
index 6c5ab15..0000000
+++ /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<RespiratorComponent>(args.SolutionEntity, out var resp))
-        {
-            var respSys = args.EntityManager.System<RespiratorSystem>();
-            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 (file)
index 9bd8fda..0000000
+++ /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;
-
-        /// <summary>
-        /// Localisation key for the name of the adjusted attribute. Used for guidebook descriptions.
-        /// </summary>
-        [DataField]
-        public abstract string GuidebookAttributeName { get; set; }
-
-        /// <summary>
-        /// Whether the attribute in question is a good thing. Used for guidebook descriptions to determine the color of the number.
-        /// </summary>
-        [DataField]
-        public virtual bool GuidebookIsAttributePositive { get; protected set; } = true;
-
-        /// <summary>
-        ///     Checks if the plant holder can metabolize the reagent or not. Checks if it has an alive plant by default.
-        /// </summary>
-        /// <param name="plantHolder">The entity holding the plant</param>
-        /// <param name="plantHolderComponent">The plant holder component</param>
-        /// <param name="entityManager">The entity manager</param>
-        /// <param name="mustHaveAlivePlant">Whether to check if it has an alive plant or not</param>
-        /// <returns></returns>
-        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 (file)
index af74c17..0000000
+++ /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<PlantHolderSystem>();
-
-            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 (file)
index cf0983b..0000000
+++ /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 (file)
index b43e885..0000000
+++ /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 (file)
index 68bb598..0000000
+++ /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<PlantHolderSystem>();
-
-            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 (file)
index 9e9787d..0000000
+++ /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 (file)
index 2279f74..0000000
+++ /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 (file)
index a1c184d..0000000
+++ /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<PlantHolderSystem>();
-
-            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 (file)
index 82958e9..0000000
+++ /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 (file)
index e92b195..0000000
+++ /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<PlantHolderSystem>();
-
-            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 (file)
index 55a7e5c..0000000
+++ /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<IRobustRandom>();
-            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 (file)
index c98f0be..0000000
+++ /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<PlantHolderSystem>();
-
-            var random = IoCManager.Resolve<IRobustRandom>();
-
-            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 (file)
index 7aaca63..0000000
+++ /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 (file)
index 4a01cdf..0000000
+++ /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<PlantHolderSystem>();
-            var random = IoCManager.Resolve<IRobustRandom>();
-
-            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 (file)
index 8813813..0000000
+++ /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
-{
-    /// <summary>
-    /// Default metabolism for drink reagents. Attempts to find a ThirstComponent on the target,
-    /// and to update it's thirst values.
-    /// </summary>
-    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<ThirstSystem>().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 (file)
index 66454b2..0000000
+++ /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
-{
-    /// <summary>
-    ///     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.
-    /// </summary>
-    /// <remarks>
-    ///     Can be used for things like adding accents or something. I don't know. Go wild.
-    /// </remarks>
-    [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;
-
-        /// <remarks>
-        ///     true - refresh status effect time,  false - accumulate status effect time
-        /// </remarks>
-        [DataField]
-        public bool Refresh = true;
-
-        /// <summary>
-        ///     Should this effect add the status effect, remove time from it, or set its cooldown?
-        /// </summary>
-        [DataField]
-        public StatusEffectMetabolismType Type = StatusEffectMetabolismType.Add;
-
-        public override void Effect(ReagentEffectArgs args)
-        {
-            var statusSys = args.EntityManager.EntitySysManager.GetEntitySystem<StatusEffectsSystem>();
-
-            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 (file)
index 7ee7095..0000000
+++ /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
-{
-    /// <summary>
-    ///     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.
-    /// </summary>
-    public sealed partial class Jitter : ReagentEffect
-    {
-        [DataField]
-        public float Amplitude = 10.0f;
-
-        [DataField]
-        public float Frequency = 4.0f;
-
-        [DataField]
-        public float Time = 2.0f;
-
-        /// <remarks>
-        ///     true - refresh jitter time,  false - accumulate jitter time
-        /// </remarks>
-        [DataField]
-        public bool Refresh = true;
-
-        public override void Effect(ReagentEffectArgs args)
-        {
-            var time = Time;
-            time *= args.Scale;
-
-            args.EntityManager.EntitySysManager.GetEntitySystem<SharedJitteringSystem>()
-                .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 (file)
index a71a42c..0000000
+++ /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<CreamPieSystem>().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 (file)
index 0000000..9f68bec
--- /dev/null
@@ -0,0 +1,35 @@
+using Content.Server.Temperature.Components;
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.EffectConditions;
+
+/// <summary>
+///     Requires the target entity to be above or below a certain temperature.
+///     Used for things like cryoxadone and pyroxadone.
+/// </summary>
+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));
+    }
+}
similarity index 76%
rename from Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs
rename to Content.Server/EntityEffects/EffectConditions/HasTagCondition.cs
index d185c7e0a764b62fd1c3da81aeeadc357d8868c7..b0428b41bfc23257e274beb5a6ffc55c5e290770 100644 (file)
@@ -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<TagPrototype>))]
     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<TagComponent>(args.SolutionEntity, out var tag))
+        if (args.EntityManager.TryGetComponent<TagComponent>(args.TargetEntity, out var tag))
             return args.EntityManager.System<TagSystem>().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 (file)
index 0000000..20d67d6
--- /dev/null
@@ -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<ProtoId<JobPrototype>> Job;
+                
+    public override bool Condition(EntityEffectBaseArgs args)
+    {   
+        args.EntityManager.TryGetComponent<MindContainerComponent>(args.TargetEntity, out var mindContainer);
+        if (mindContainer != null && mindContainer.Mind != null)
+        {
+            var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
+            if (args.EntityManager.TryGetComponent<JobComponent>(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 (file)
index 0000000..d676b29
--- /dev/null
@@ -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 (file)
index 0000000..fc52608
--- /dev/null
@@ -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;
+
+/// <summary>
+///     Requires that the metabolizing organ is or is not tagged with a certain MetabolizerType
+/// </summary>
+public sealed partial class OrganType : EntityEffectCondition
+{
+    [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<MetabolizerTypePrototype>))]
+    public string Type = default!;
+
+    /// <summary>
+    ///     Does this condition pass when the organ has the type, or when it doesn't have the type?
+    /// </summary>
+    [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<MetabolizerComponent?> metabolizer, IEntityManager entMan)
+    {
+        metabolizer.Comp ??= entMan.GetComponentOrNull<MetabolizerComponent>(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<MetabolizerTypePrototype>(Type).LocalizedName),
+            ("shouldhave", ShouldHave));
+    }
+}
diff --git a/Content.Server/EntityEffects/EffectConditions/ReagentThreshold.cs b/Content.Server/EntityEffects/EffectConditions/ReagentThreshold.cs
new file mode 100644 (file)
index 0000000..6cbd7b4
--- /dev/null
@@ -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;
+
+/// <summary>
+///     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.
+/// </summary>
+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 (file)
index 0000000..b964435
--- /dev/null
@@ -0,0 +1,40 @@
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.EffectConditions;
+
+/// <summary>
+///     Requires the solution to be above or below a certain temperature.
+///     Used for things like explosives.
+/// </summary>
+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 (file)
index 0000000..2d039a1
--- /dev/null
@@ -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 (file)
index 0000000..84ad4c2
--- /dev/null
@@ -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));
+    }
+}
similarity index 56%
rename from Content.Server/Chemistry/ReagentEffects/ActivateArtifact.cs
rename to Content.Server/EntityEffects/Effects/ActivateArtifact.cs
index 41af90f60a1e76243d50835258dea4b047d2a385..3e97388499567b90a91af46c08df500d0296c3c2 100644 (file)
@@ -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<ArtifactSystem>();
-        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 (file)
index 0000000..10fb667
--- /dev/null
@@ -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<SolutionContainerSystem>();
+                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));
+    }
+}
similarity index 76%
rename from Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs
rename to Content.Server/EntityEffects/Effects/AdjustAlert.cs
index 40858176bd15bd5c7b5d7b35e795da01e82fe02f..3bf57b309d1ee9cc9d37b9d1c40820d21aaf168b 100644 (file)
@@ -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
 {
     /// <summary>
     /// 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<AlertsSystem>();
-        if (!args.EntityManager.HasComponent<AlertsComponent>(args.SolutionEntity))
+        if (!args.EntityManager.HasComponent<AlertsComponent>(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);
         }
 
     }
similarity index 58%
rename from Content.Server/Chemistry/ReagentEffects/AdjustReagent.cs
rename to Content.Server/EntityEffects/Effects/AdjustReagent.cs
index 16d69edd9aac26b5c7e55d0691c3f29be2357c8d..71117d6ec5377a1579f197959702c984a82ed0e7 100644 (file)
@@ -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
     {
         /// <summary>
         ///     The reagent ID to remove. Only one of this and <see cref="Group"/> 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<IPrototypeManager>();
-                foreach (var quant in args.Source.Contents.ToArray())
+                if (Reagent != null)
                 {
-                    var proto = prototypeMan.Index<ReagentPrototype>(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<IPrototypeManager>();
+                    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<ReagentPrototype>(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)
similarity index 56%
rename from Content.Server/Chemistry/ReagentEffects/AdjustTemperature.cs
rename to Content.Server/EntityEffects/Effects/AdjustTemperature.cs
index 9b97572067805764798d0f9367c2d5825270f684..3c5c77c309d4bed4c47a4c69032c9c7bf55aa152 100644 (file)
@@ -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<TemperatureSystem>();
                 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 (file)
index 0000000..481f1fc
--- /dev/null
@@ -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;
+
+/// <summary>
+/// Basically smoke and foam reactions.
+/// </summary>
+[UsedImplicitly]
+[DataDefinition]
+public sealed partial class AreaReactionEffect : EntityEffect
+{
+    /// <summary>
+    /// How many seconds will the effect stay, counting after fully spreading.
+    /// </summary>
+    [DataField("duration")] private float _duration = 10;
+
+    /// <summary>
+    /// How many units of reaction for 1 smoke entity.
+    /// </summary>
+    [DataField] public FixedPoint2 OverflowThreshold = FixedPoint2.New(2.5);
+
+    /// <summary>
+    /// The entity prototype that will be spawned as the effect.
+    /// </summary>
+    [DataField("prototypeId", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
+    private string _prototypeId = default!;
+
+    /// <summary>
+    /// Sound that will get played when this reaction effect occurs.
+    /// </summary>
+    [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<TransformComponent>(reagentArgs.TargetEntity);
+            var mapManager = IoCManager.Resolve<IMapManager>();
+            var mapSys = reagentArgs.EntityManager.System<MapSystem>();
+            var sys = reagentArgs.EntityManager.System<TransformSystem>();
+            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<SmokeSystem>();
+            smoke.StartSmoke(ent, splitSolution, _duration, spreadAmount);
+
+            var audio = reagentArgs.EntityManager.System<SharedAudioSystem>();
+            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();
+    }
+}
similarity index 65%
rename from Content.Server/Chemistry/ReagentEffects/CauseZombieInfection.cs
rename to Content.Server/EntityEffects/Effects/CauseZombieInfection.cs
index 96c57f74653e0357e558f357d155e7a9df768900..b71e6a30e027d8ae1b20bde8ea88454a1e71a753 100644 (file)
@@ -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<ZombifyOnDeathComponent>(args.SolutionEntity);
-        entityManager.EnsureComponent<PendingZombieComponent>(args.SolutionEntity);
+        entityManager.EnsureComponent<ZombifyOnDeathComponent>(args.TargetEntity);
+        entityManager.EnsureComponent<PendingZombieComponent>(args.TargetEntity);
     }
 }
 
diff --git a/Content.Server/EntityEffects/Effects/ChemCleanBloodstream.cs b/Content.Server/EntityEffects/Effects/ChemCleanBloodstream.cs
new file mode 100644 (file)
index 0000000..b7f6f0d
--- /dev/null
@@ -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;
+
+/// <summary>
+/// Basically smoke and foam reactions.
+/// </summary>
+[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<BloodstreamSystem>();
+
+        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 (file)
index 0000000..42fc9ff
--- /dev/null
@@ -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;
+
+/// <summary>
+/// Heal or apply eye damage
+/// </summary>
+[UsedImplicitly]
+public sealed partial class ChemHealEyeDamage : EntityEffect
+{
+    /// <summary>
+    /// How much eye damage to add.
+    /// </summary>
+    [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<BlindableSystem>().AdjustEyeDamage(args.TargetEntity, Amount);
+    }
+}
similarity index 65%
rename from Content.Server/Chemistry/ReagentEffects/ChemVomit.cs
rename to Content.Server/EntityEffects/Effects/ChemVomit.cs
index 851c0adf5f25320ad3c6daed5a956738c2796527..0d1bac87d1c139adbccdc19517b0aef99251bb80 100644 (file)
@@ -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
 {
     /// <summary>
     /// Forces you to vomit.
     /// </summary>
     [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<VomitSystem>();
 
-            vomitSys.Vomit(args.SolutionEntity, ThirstAmount, HungerAmount);
+            vomitSys.Vomit(args.TargetEntity, ThirstAmount, HungerAmount);
         }
     }
 }
similarity index 80%
rename from Content.Server/Chemistry/ReactionEffects/CreateEntityReactionEffect.cs
rename to Content.Server/EntityEffects/Effects/CreateEntityReactionEffect.cs
index 0d5acc172203fe6cc30a1f6fe31b42d41afe1bb0..5288cb587daef9552ad0c2b548920647af585328 100644 (file)
@@ -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
 {
     /// <summary>
     ///     What entity to create.
@@ -26,15 +25,17 @@ public sealed partial class CreateEntityReactionEffect : ReagentEffect
             ("entname", IoCManager.Resolve<IPrototypeManager>().Index<EntityPrototype>(Entity).Name),
             ("amount", Number));
 
-    public override void Effect(ReagentEffectArgs args)
+    public override void Effect(EntityEffectBaseArgs args)
     {
-        var transform = args.EntityManager.GetComponent<TransformComponent>(args.SolutionEntity);
+        var transform = args.EntityManager.GetComponent<TransformComponent>(args.TargetEntity);
         var transformSystem = args.EntityManager.System<SharedTransformSystem>();
-        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
similarity index 62%
rename from Content.Server/Chemistry/ReagentEffects/CreateGas.cs
rename to Content.Server/EntityEffects/Effects/CreateGas.cs
index c1da3c48db32d04e648d330c7c6ee87cd7071375..fb57a43c48d6e84488c284d447a6b8ba5a19bed2 100644 (file)
@@ -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<AtmosphereSystem>();
 
-        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);
+            }
         }
     }
 }
similarity index 73%
rename from Content.Server/Chemistry/ReagentEffects/CureZombieInfection.cs
rename to Content.Server/EntityEffects/Effects/CureZombieInfection.cs
index 20e2c015c46fe9580aeb5d75fc752a22dd40f0dc..8dfc2de05580c255ee46dfd7d343888a605b4b52 100644 (file)
@@ -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<IncurableZombieComponent>(args.SolutionEntity))
+        if (entityManager.HasComponent<IncurableZombieComponent>(args.TargetEntity))
             return;
 
-        entityManager.RemoveComponent<ZombifyOnDeathComponent>(args.SolutionEntity);
-        entityManager.RemoveComponent<PendingZombieComponent>(args.SolutionEntity);
+        entityManager.RemoveComponent<ZombifyOnDeathComponent>(args.TargetEntity);
+        entityManager.RemoveComponent<PendingZombieComponent>(args.TargetEntity);
 
         if (Innoculate)
         {
-            entityManager.EnsureComponent<ZombieImmuneComponent>(args.SolutionEntity);
+            entityManager.EnsureComponent<ZombieImmuneComponent>(args.TargetEntity);
         }
     }
 }
similarity index 64%
rename from Content.Server/Chemistry/ReagentEffects/Drunk.cs
rename to Content.Server/EntityEffects/Effects/Drunk.cs
index dbce995ca2e6798c0486c109ea2915738f5b5f38..493560e5b98a619064e66e35b922592b61f648f4 100644 (file)
@@ -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
 {
     /// <summary>
     ///     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<SharedDrunkSystem>();
-        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 (file)
index 0000000..f6a5f1a
--- /dev/null
@@ -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;
+
+    /// <remarks>
+    ///     true - refresh electrocute time,  false - accumulate electrocute time
+    /// </remarks>
+    [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<ElectrocutionSystem>().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<ElectrocutionSystem>().TryDoElectrocution(args.TargetEntity, null,
+                Math.Max(ElectrocuteDamageScale, 1), TimeSpan.FromSeconds(ElectrocuteTime), Refresh, ignoreInsulation: true);
+        }
+    }
+}
similarity index 72%
rename from Content.Server/Chemistry/ReagentEffects/Emote.cs
rename to Content.Server/EntityEffects/Effects/Emote.cs
index db6de6cc75be9246010f9d4dc0ea744a671a8fc1..00bdaec455c30491856f0ab1ce0cf4d6316eff92 100644 (file)
@@ -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;
 
 /// <summary>
 ///     Tries to force someone to emote (scream, laugh, etc). Still respects whitelists/blacklists and other limits of the specified emote unless forced.
 /// </summary>
 [UsedImplicitly]
-public sealed partial class Emote : ReagentEffect
+public sealed partial class Emote : EntityEffect
 {
     [DataField("emote", customTypeSerializer: typeof(PrototypeIdSerializer<EmotePrototype>))]
     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<ChatSystem>();
         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);
 
     }
 }
similarity index 67%
rename from Content.Server/Chemistry/ReactionEffects/EmpReactionEffect.cs
rename to Content.Server/EntityEffects/Effects/EmpReactionEffect.cs
index 9a320ffc39820a77aa5c2df3d0176b0c5eb1e816..ec7801f08321514080e3500d58618ef169c2e671 100644 (file)
@@ -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
 {
     /// <summary>
-    ///     Impulse range per unit of reagent
+    ///     Impulse range per unit of quantity
     /// </summary>
     [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<TransformSystem>();
-        var transform = args.EntityManager.GetComponent<TransformComponent>(args.SolutionEntity);
-        var range = MathF.Min((float) (args.Quantity*EmpRangePerUnit), EmpMaxRange);
+        var transform = args.EntityManager.GetComponent<TransformComponent>(args.TargetEntity);
+
+        var range = EmpRangePerUnit;
+
+        if (args is EntityEffectReagentArgs reagentArgs)
+        {
+            range = MathF.Min((float) (reagentArgs.Quantity * EmpRangePerUnit), EmpMaxRange);
+        }
 
         args.EntityManager.System<EmpSystem>()
-            .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 (file)
index 0000000..689757d
--- /dev/null
@@ -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
+{
+    /// <summary>
+    ///     The type of explosion. Determines damage types and tile break chance scaling.
+    /// </summary>
+    [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<ExplosionPrototype>))]
+    [JsonIgnore]
+    public string ExplosionType = default!;
+
+    /// <summary>
+    ///     The max intensity the explosion can have at a given tile. Places an upper limit of damage and tile break
+    ///     chance.
+    /// </summary>
+    [DataField]
+    [JsonIgnore]
+    public float MaxIntensity = 5;
+
+    /// <summary>
+    ///     How quickly intensity drops off as you move away from the epicenter
+    /// </summary>
+    [DataField]
+    [JsonIgnore]
+    public float IntensitySlope = 1;
+
+    /// <summary>
+    ///     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.
+    /// </summary>
+    /// <remarks>
+    ///     A slope of 1 and MaxTotalIntensity of 100 corresponds to a radius of around 4.5 tiles.
+    /// </remarks>
+    [DataField]
+    [JsonIgnore]
+    public float MaxTotalIntensity = 100;
+
+    /// <summary>
+    ///     The intensity of the explosion per unit reaction.
+    /// </summary>
+    [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<ExplosionSystem>()
+            .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 (file)
index 0000000..6d7e7c2
--- /dev/null
@@ -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
+    {
+        /// <summary>
+        ///     Amount of firestacks reduced.
+        /// </summary>
+        [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>();
+            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 (file)
index 0000000..edd499a
--- /dev/null
@@ -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<FlammableSystem>().AdjustFireStacks(args.TargetEntity, quantity * multiplier, flammable);
+                if (reagentArgs.Reagent != null)
+                    reagentArgs.Source?.RemoveReagent(reagentArgs.Reagent.ID, reagentArgs.Quantity);
+            } else
+            {
+                args.EntityManager.System<FlammableSystem>().AdjustFireStacks(args.TargetEntity, multiplier, flammable);
+            }
+        }
+    }
+}
similarity index 84%
rename from Content.Server/Chemistry/ReagentEffects/HealthChange.cs
rename to Content.Server/EntityEffects/Effects/HealthChange.cs
index 24880cfd371ded96d3ffba1895668e48c2e346a0..fd2a658745b678e36cb115cbf11c09dce5f7d8de 100644 (file)
@@ -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
 {
     /// <summary>
-    /// Default metabolism for medicine reagents.
+    /// Default metabolism used for medicine reagents.
     /// </summary>
     [UsedImplicitly]
-    public sealed partial class HealthChange : ReagentEffect
+    public sealed partial class HealthChange : EntityEffect
     {
         /// <summary>
-        /// Damage to apply every metabolism cycle. Damage Ignores resistances.
+        /// Damage to apply every cycle. Damage Ignores resistances.
         /// </summary>
         [DataField(required: true)]
         [JsonPropertyName("damage")]
@@ -26,6 +26,7 @@ namespace Content.Server.Chemistry.ReagentEffects
         /// <summary>
         ///     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.
         /// </summary>
         [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<DamageableSystem>().TryChangeDamage(
-                args.SolutionEntity,
+                args.TargetEntity,
                 Damage * scale,
                 IgnoreResistances,
                 interruptsDoAfters: false);
similarity index 50%
rename from Content.Server/Chemistry/ReagentEffects/Ignite.cs
rename to Content.Server/EntityEffects/Effects/Ignite.cs
index 6c5d47711f35e5863778dcf3461bbb0b8a98d775..cca2a301fdf63d2204168befba4e928c87dfd8a6 100644 (file)
@@ -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;
 
 /// <summary>
 ///     Ignites a mob.
 /// </summary>
-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<FlammableSystem>();
-        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);
+        }
     }
 }
similarity index 87%
rename from Content.Server/Chemistry/ReagentEffects/MakeSentient.cs
rename to Content.Server/EntityEffects/Effects/MakeSentient.cs
index be8dbbaf52acbe3d29d08087b13ad1116ec6a717..c4870438486f61a574cdfa4b60a99ec7a36cc85a 100644 (file)
@@ -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
similarity index 50%
rename from Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs
rename to Content.Server/EntityEffects/Effects/ModifyBleedAmount.cs
index ecd9c86255fcec1c40defe61ff78fa215077abd4..58bc304f5eae7480ccb3d89dcf46d4f4741bf069 100644 (file)
@@ -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<BloodstreamComponent>(args.SolutionEntity, out var blood))
+        if (args.EntityManager.TryGetComponent<BloodstreamComponent>(args.TargetEntity, out var blood))
         {
             var sys = args.EntityManager.System<BloodstreamSystem>();
-            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);
         }
     }
 }
similarity index 52%
rename from Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs
rename to Content.Server/EntityEffects/Effects/ModifyBloodLevel.cs
index b136ff9bb8c1566525e374bfe117d287fb2f16ee..d8aca7d28428a3d44a4f4a47e2ee8360720fa2d2 100644 (file)
@@ -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<BloodstreamComponent>(args.SolutionEntity, out var blood))
+        if (args.EntityManager.TryGetComponent<BloodstreamComponent>(args.TargetEntity, out var blood))
         {
             var sys = args.EntityManager.System<BloodstreamSystem>();
-            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 (file)
index 0000000..b4f35e3
--- /dev/null
@@ -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<Gas, float> _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<LungComponent>(reagentArgs.OrganEntity, out var organLung))
+                return;
+            lung = organLung;
+            amount = reagentArgs.Quantity.Float();
+        }
+        else
+        {
+            if (!args.EntityManager.TryGetComponent<LungComponent>(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 (file)
index 0000000..ac1f143
--- /dev/null
@@ -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;
+
+/// <summary>
+/// Default metabolism for stimulants and tranqs. Attempts to find a MovementSpeedModifier on the target,
+/// adding one if not there and to change the movespeed
+/// </summary>
+public sealed partial class MovespeedModifier : EntityEffect
+{
+    /// <summary>
+    /// How much the entities' walk speed is multiplied by.
+    /// </summary>
+    [DataField]
+    public float WalkSpeedModifier { get; set; } = 1;
+
+    /// <summary>
+    /// How much the entities' run speed is multiplied by.
+    /// </summary>
+    [DataField]
+    public float SprintSpeedModifier { get; set; } = 1;
+
+    /// <summary>
+    /// How long the modifier applies (in seconds).
+    /// Is scaled by reagent amount if used with an EntityEffectReagentArgs.
+    /// </summary>
+    [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));
+    }
+
+    /// <summary>
+    /// Remove reagent at set rate, changes the movespeed modifiers and adds a MovespeedModifierMetabolismComponent if not already there.
+    /// </summary>
+    public override void Effect(EntityEffectBaseArgs args)
+    {
+        var status = args.EntityManager.EnsureComponent<MovespeedModifierMetabolismComponent>(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<MovementSpeedModifierSystem>().RefreshMovementSpeedModifiers(args.TargetEntity);
+    }
+    public void IncreaseTimer(MovespeedModifierMetabolismComponent status, float time)
+    {
+        var gameTiming = IoCManager.Resolve<IGameTiming>();
+
+        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 (file)
index 0000000..6038318
--- /dev/null
@@ -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<RespiratorComponent>(args.TargetEntity, out var resp))
+        {
+            var respSys = args.EntityManager.System<RespiratorSystem>();
+            respSys.UpdateSaturation(args.TargetEntity, multiplier * Factor, resp);
+        }
+    }
+}
similarity index 62%
rename from Content.Server/Chemistry/ReagentEffects/Paralyze.cs
rename to Content.Server/EntityEffects/Effects/Paralyze.cs
index d5d913f37d2c36a7c98d2eb27b54db3780239c63..ba020dd39b7accfdf6d71e6ceb9028888ec215a9 100644 (file)
@@ -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<StunSystem>().TryParalyze(args.SolutionEntity, TimeSpan.FromSeconds(paralyzeTime), Refresh);
+        if (args is EntityEffectReagentArgs reagentArgs)
+        {
+            paralyzeTime *= (double)reagentArgs.Scale;
+        }
+
+        args.EntityManager.System<StunSystem>().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 (file)
index 0000000..5133fe5
--- /dev/null
@@ -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;
+
+    /// <summary>
+    /// Localisation key for the name of the adjusted attribute. Used for guidebook descriptions.
+    /// </summary>
+    [DataField]
+    public abstract string GuidebookAttributeName { get; set; }
+
+    /// <summary>
+    /// Whether the attribute in question is a good thing. Used for guidebook descriptions to determine the color of the number.
+    /// </summary>
+    [DataField]
+    public virtual bool GuidebookIsAttributePositive { get; protected set; } = true;
+
+    /// <summary>
+    ///     Checks if the plant holder can metabolize the reagent or not. Checks if it has an alive plant by default.
+    /// </summary>
+    /// <param name="plantHolder">The entity holding the plant</param>
+    /// <param name="plantHolderComponent">The plant holder component</param>
+    /// <param name="entityManager">The entity manager</param>
+    /// <param name="mustHaveAlivePlant">Whether to check if it has an alive plant or not</param>
+    /// <returns></returns>
+    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 (file)
index 0000000..774f6ec
--- /dev/null
@@ -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<PlantHolderSystem>();
+
+        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 (file)
index 0000000..bf41a21
--- /dev/null
@@ -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 (file)
index 0000000..6ed793e
--- /dev/null
@@ -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 (file)
index 0000000..b9389af
--- /dev/null
@@ -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<PlantHolderSystem>();
+
+        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 (file)
index 0000000..219529b
--- /dev/null
@@ -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 (file)
index 0000000..ab1baa4
--- /dev/null
@@ -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 (file)
index 0000000..4177497
--- /dev/null
@@ -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<PlantHolderSystem>();
+
+        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 (file)
index 0000000..421e319
--- /dev/null
@@ -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 (file)
index 0000000..397eace
--- /dev/null
@@ -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<PlantHolderSystem>();
+
+        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 (file)
index 0000000..7fe4b7a
--- /dev/null
@@ -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<IRobustRandom>();
+        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 (file)
index 0000000..36b6a83
--- /dev/null
@@ -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<PlantHolderSystem>();
+
+        var random = IoCManager.Resolve<IRobustRandom>();
+
+        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 (file)
index 0000000..96d98bf
--- /dev/null
@@ -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 (file)
index 0000000..695cb96
--- /dev/null
@@ -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<PlantHolderSystem>();
+        var random = IoCManager.Resolve<IRobustRandom>();
+
+        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));
+}
similarity index 80%
rename from Content.Server/Chemistry/ReagentEffects/Polymorph.cs
rename to Content.Server/EntityEffects/Effects/Polymorph.cs
index fea372125eebe227a5840a80c8046c594ee920f0..00a5c6613d02eb7fbbdeec3717a32e49c0beda4b 100644 (file)
@@ -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
 {
     /// <summary>
     ///     What polymorph prototype is used on effect
@@ -20,10 +21,10 @@ public sealed partial class Polymorph : ReagentEffect
             ("chance", Probability), ("entityname",
                 prototype.Index<EntityPrototype>(prototype.Index<PolymorphPrototype>(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<PolymorphSystem>();
 
         // Make it into a prototype
similarity index 60%
rename from Content.Server/Chemistry/ReagentEffects/PopupMessage.cs
rename to Content.Server/EntityEffects/Effects/PopupMessage.cs
index 8278e95c1da9f10d640b3dcaf3ab19719eda50f2..ac22b13051b5f5efc6de7113c6946e44602f6e62 100644 (file)
@@ -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<SharedPopupSystem>();
             var random = IoCManager.Resolve<IRobustRandom>();
 
             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);
         }
     }
 
similarity index 61%
rename from Content.Server/Chemistry/ReagentEffects/ReduceRotting.cs
rename to Content.Server/EntityEffects/Effects/ReduceRotting.cs
index cea4f85332183a50adbc7b88a0b3fc2f5c14a23f..5c00492744dce27b0040e5f596a60d45b5b7ad4a 100644 (file)
@@ -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
 {
     /// <summary>
     /// Reduces the rotting accumulator on the patient, making them revivable.
     /// </summary>
     [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<RottingSystem>();
 
-            rottingSys.ReduceAccumulator(args.SolutionEntity, TimeSpan.FromSeconds(RottingAmount));
+            rottingSys.ReduceAccumulator(args.TargetEntity, TimeSpan.FromSeconds(RottingAmount));
         }
     }
 }
similarity index 63%
rename from Content.Server/Chemistry/ReagentEffects/ResetNarcolepsy.cs
rename to Content.Server/EntityEffects/Effects/ResetNarcolepsy.cs
index fa4f9108918d729f068ee15acba2483ccd3026b7..f9564712adc6064639d4fb7886fb19ced24a4d02 100644 (file)
@@ -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;
 
 /// <summary>
 /// Reset narcolepsy timer
 /// </summary>
 [UsedImplicitly]
-public sealed partial class ResetNarcolepsy : ReagentEffect
+public sealed partial class ResetNarcolepsy : EntityEffect
 {
     /// <summary>
     /// 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<NarcolepsySystem>().AdjustNarcolepsyTimer(args.SolutionEntity, TimerReset);
+        args.EntityManager.EntitySysManager.GetEntitySystem<NarcolepsySystem>().AdjustNarcolepsyTimer(args.TargetEntity, TimerReset);
     }
 }
similarity index 53%
rename from Content.Server/Chemistry/ReagentEffects/SatiateHunger.cs
rename to Content.Server/EntityEffects/Effects/SatiateHunger.cs
index be77202255654a3ee4ab7d26a04e1ff17eb7c001..dd58654dffc29d22b256d069a1bef22a9b66dd9c 100644 (file)
@@ -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
 {
     /// <summary>
     /// Attempts to find a HungerComponent on the target,
     /// and to update it's hunger values.
     /// </summary>
-    public sealed partial class SatiateHunger : ReagentEffect
+    public sealed partial class SatiateHunger : EntityEffect
     {
         private const float DefaultNutritionFactor = 3.0f;
 
         /// <summary>
-        ///     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.
         /// </summary>
         [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<HungerSystem>().ModifyHunger(args.SolutionEntity, NutritionFactor * (float) args.Quantity, hunger);
+            if (args is EntityEffectReagentArgs reagentArgs)
+            {
+                entman.System<HungerSystem>().ModifyHunger(reagentArgs.TargetEntity, NutritionFactor * (float) reagentArgs.Quantity, hunger);
+            }
+            else
+            {
+                entman.System<HungerSystem>().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 (file)
index 0000000..d0dbe43
--- /dev/null
@@ -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;
+
+/// <summary>
+/// Default metabolism for drink reagents. Attempts to find a ThirstComponent on the target,
+/// and to update it's thirst values.
+/// </summary>
+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<ThirstSystem>().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 (file)
index 0000000..85c20db
--- /dev/null
@@ -0,0 +1,144 @@
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.EntityEffects.Effects;
+
+/// <summary>
+///     Sets the temperature of the solution involved with the reaction to a new value.
+/// </summary>
+[DataDefinition]
+public sealed partial class SetSolutionTemperatureEffect : EntityEffect
+{
+    /// <summary>
+    ///     The temperature to set the solution to.
+    /// </summary>
+    [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();
+    }
+}
+
+/// <summary>
+///     Adjusts the temperature of the solution involved in the reaction.
+/// </summary>
+[DataDefinition]
+public sealed partial class AdjustSolutionTemperatureEffect : EntityEffect
+{
+    /// <summary>
+    ///     The change in temperature.
+    /// </summary>
+    [DataField("delta", required: true)] private float _delta;
+
+    /// <summary>
+    ///     The minimum temperature this effect can reach.
+    /// </summary>
+    [DataField("minTemp")] private float _minTemp = 0.0f;
+
+    /// <summary>
+    ///     The maximum temperature this effect can reach.
+    /// </summary>
+    [DataField("maxTemp")] private float _maxTemp = float.PositiveInfinity;
+
+    /// <summary>
+    ///     If true, then scale ranges by intensity. If not, the ranges are the same regardless of reactant amount.
+    /// </summary>
+    [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();
+    }
+}
+
+/// <summary>
+///     Adjusts the thermal energy of the solution involved in the reaction.
+/// </summary>
+public sealed partial class AdjustSolutionThermalEnergyEffect : EntityEffect
+{
+    /// <summary>
+    ///     The change in energy.
+    /// </summary>
+    [DataField("delta", required: true)] private float _delta;
+
+    /// <summary>
+    ///     The minimum temperature this effect can reach.
+    /// </summary>
+    [DataField("minTemp")] private float _minTemp = 0.0f;
+
+    /// <summary>
+    ///     The maximum temperature this effect can reach.
+    /// </summary>
+    [DataField("maxTemp")] private float _maxTemp = float.PositiveInfinity;
+
+    /// <summary>
+    ///     If true, then scale ranges by intensity. If not, the ranges are the same regardless of reactant amount.
+    /// </summary>
+    [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 (file)
index 0000000..bb9bbf3
--- /dev/null
@@ -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;
+
+/// <summary>
+///     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.
+/// </summary>
+/// <remarks>
+///     Can be used for things like adding accents or something. I don't know. Go wild.
+/// </remarks>
+[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;
+
+    /// <remarks>
+    ///     true - refresh status effect time,  false - accumulate status effect time
+    /// </remarks>
+    [DataField]
+    public bool Refresh = true;
+
+    /// <summary>
+    ///     Should this effect add the status effect, remove time from it, or set its cooldown?
+    /// </summary>
+    [DataField]
+    public StatusEffectMetabolismType Type = StatusEffectMetabolismType.Add;
+
+    public override void Effect(EntityEffectBaseArgs args)
+    {
+        var statusSys = args.EntityManager.EntitySysManager.GetEntitySystem<StatusEffectsSystem>();
+
+        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 (file)
index 0000000..dbca4fa
--- /dev/null
@@ -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;
+
+/// <summary>
+///     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.
+/// </summary>
+public sealed partial class Jitter : EntityEffect
+{
+    [DataField]
+    public float Amplitude = 10.0f;
+
+    [DataField]
+    public float Frequency = 4.0f;
+
+    [DataField]
+    public float Time = 2.0f;
+
+    /// <remarks>
+    ///     true - refresh jitter time,  false - accumulate jitter time
+    /// </remarks>
+    [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<SharedJitteringSystem>()
+            .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 (file)
index 0000000..67bfac6
--- /dev/null
@@ -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<CreamPieSystem>().SetCreamPied(args.TargetEntity, creamPied, false);
+    }
+}
similarity index 50%
rename from Content.Server/Chemistry/ReagentEffects/WearableReaction.cs
rename to Content.Server/EntityEffects/Effects/WearableReaction.cs
index d9f8414995da825f9625cdd8f06b86502e7d7b36..9655bbc073f099d88abd0d57b81d0ed963b6ae2c 100644 (file)
@@ -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;
 
 /// <summary>
-/// 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.
 /// </summary>
-public sealed partial class WearableReaction : ReagentEffect
+public sealed partial class WearableReaction : EntityEffect
 {
     /// <summary>
     /// Minimum quantity of reagent required to trigger this effect.
+    /// Only used with EntityEffectReagentArgs.
     /// </summary>
     [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<InventorySystem>().SpawnItemInSlot(args.SolutionEntity, Slot, PrototypeID))
-            args.Source?.RemoveReagent(args.Reagent.ID, AmountThreshold);
+        if (args.EntityManager.System<InventorySystem>().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);
+            }
+        }
     }
 }
index 2eaf19170b761302f4163f63d7ab9155ce2bc6c3..8cdae84a9362c7db575a5427f84bc7591ad6e379 100644 (file)
@@ -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;
index 9638dabf28ea694fb50bbcdf1c041ce06f463549..78693bfdbcc7ad54b6e4b6050cbb0d48ace03022 100644 (file)
@@ -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;
index 7e107ce1a5af4655f3331b15ba60bfb10e1ae7da..8fd088408c0f2e10c334d31474384c130537a144 100644 (file)
@@ -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<ReagentEffect>(),
-                new UniversalJsonConverter<ReagentEffectCondition>(),
+                new UniversalJsonConverter<EntityEffect>(),
+                new UniversalJsonConverter<EntityEffectCondition>(),
                 new UniversalJsonConverter<ReagentEffectsEntry>(),
                 new UniversalJsonConverter<DamageSpecifier>(),
                 new FixedPointJsonConverter()
index 123dd5f8eb3189d83e65f8cc77455c13839f1465..06c230d6071a0d3d586ce95f291862c7d1bf2ae6 100644 (file)
@@ -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<ReagentEffect>(),
+                new UniversalJsonConverter<EntityEffect>(),
             }
         };
 
index ab9e75820607767e33e719ebd14c69cbf78e8e36..8b597ad61bc8c1b17d8146443c24ffb16e18fe32 100644 (file)
@@ -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<string, float> Products { get; }
 
     [JsonPropertyName("effects")]
-    public List<ReagentEffect> Effects { get; }
+    public List<EntityEffect> Effects { get; }
 
     public ReactionEntry(ReactionPrototype proto)
     {
index aa2ed71d8f36ecfd4be76dc665e5cf54e9742933..b8e334fb0dafc5f931bb88ea322a7b12f9b8bf51 100644 (file)
@@ -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 (file)
index 1f36e8f..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-using Robust.Shared.Audio;
-using Robust.Shared.GameStates;
-
-namespace Content.Server.Tiles;
-
-/// <summary>
-/// Applies flammable and damage while vaulting.
-/// </summary>
-[RegisterComponent, Access(typeof(LavaSystem))]
-public sealed partial class LavaComponent : Component
-{
-    /// <summary>
-    /// Sound played if something disintegrates in lava.
-    /// </summary>
-    [ViewVariables(VVAccess.ReadWrite), DataField("soundDisintegration")]
-    public SoundSpecifier DisintegrationSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");
-
-    /// <summary>
-    /// How many fire stacks are applied per second.
-    /// </summary>
-    [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 (file)
index 51bd6f8..0000000
+++ /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<LavaComponent, StepTriggeredOffEvent>(OnLavaStepTriggered);
-        SubscribeLocalEvent<LavaComponent, StepTriggerAttemptEvent>(OnLavaStepTriggerAttempt);
-    }
-
-    private void OnLavaStepTriggerAttempt(EntityUid uid, LavaComponent component, ref StepTriggerAttemptEvent args)
-    {
-        if (!HasComp<FlammableComponent>(args.Tripper))
-            return;
-
-        args.Continue = true;
-    }
-
-    private void OnLavaStepTriggered(EntityUid uid, LavaComponent component, ref StepTriggeredOffEvent args)
-    {
-        var otherUid = args.Tripper;
-
-        if (TryComp<FlammableComponent>(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 (file)
index 0000000..4201af4
--- /dev/null
@@ -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;
+
+/// <summary>
+/// Applies effects upon stepping onto a tile.
+/// </summary>
+[RegisterComponent, Access(typeof(TileEntityEffectSystem))]
+public sealed partial class TileEntityEffectComponent : Component
+{
+    /// <summary>
+    /// List of effects that should be applied.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField]
+    public List<EntityEffect> Effects = default!;
+}
diff --git a/Content.Server/Tiles/TileEntityEffectSystem.cs b/Content.Server/Tiles/TileEntityEffectSystem.cs
new file mode 100644 (file)
index 0000000..7149f16
--- /dev/null
@@ -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<TileEntityEffectComponent, StepTriggeredOffEvent>(OnTileStepTriggered);
+        SubscribeLocalEvent<TileEntityEffectComponent, StepTriggerAttemptEvent>(OnTileStepTriggerAttempt);
+    }
+    private void OnTileStepTriggerAttempt(Entity<TileEntityEffectComponent> ent, ref StepTriggerAttemptEvent args)
+    {
+        args.Continue = true;
+    }
+
+    private void OnTileStepTriggered(Entity<TileEntityEffectComponent> ent, ref StepTriggeredOffEvent args)
+    {
+        var otherUid = args.Tripper;
+
+        foreach (var effect in ent.Comp.Effects)
+        {
+            effect.Effect(new EntityEffectBaseArgs(otherUid, EntityManager));
+        }
+    }
+}
index f2b13d3488158061d7f99211b015b93c4d75a78b..f9dfa9b2734572af966ea46c865921fe896becfd 100644 (file)
@@ -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<SolutionComponent> 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")}");
                 }
index f4e9619b172f100ca9b17ec337df4f17aa2d1fed..351aa50b0f3fe8e524e7631277817d0192a3d8fa 100644 (file)
@@ -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
         /// <summary>
         /// Effects to be triggered when the reaction occurs.
         /// </summary>
-        [DataField("effects", serverOnly: true)] public List<ReagentEffect> Effects = new();
+        [DataField("effects", serverOnly: true)] public List<EntityEffect> Effects = new();
 
         /// <summary>
         /// How dangerous is this effect? Stuff like bicaridine should be low, while things like methamphetamine
index 310129c6fc7846edd5f98cf6778e0bcfe69e0011..cabdee93c1a30af799d54f7d38685d1d3d8dee8d 100644 (file)
@@ -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<string>? Reagents = null;
 
     [DataField("effects", required: true)]
-    public List<ReagentEffect> Effects = default!;
+    public List<EntityEffect> Effects = default!;
 
     [DataField("groups", readOnly: true, serverOnly: true,
         customTypeSerializer:typeof(PrototypeIdDictionarySerializer<HashSet<ReactionMethod>, ReactiveGroupPrototype>))]
index 57b008de0c7a519fcd1d12b86f9e50b041d58958..edd75bb0b4c54cc49d931ee9f15b9d28f1a5492f 100644 (file)
@@ -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<ReagentPrototype>(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<ReagentPrototype>(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 (file)
index 6d0e85d..0000000
+++ /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
-{
-    /// <summary>
-    ///     Reagent effects describe behavior that occurs when a reagent is ingested and metabolized by some
-    ///     organ. They only trigger when all of <see cref="Conditions"/> are satisfied.
-    /// </summary>
-    [ImplicitDataDefinitionForInheritors]
-    [MeansImplicitUse]
-    public abstract partial class ReagentEffect
-    {
-        [JsonPropertyName("id")] private protected string _id => this.GetType().Name;
-        /// <summary>
-        ///     The list of conditions required for the effect to activate. Not required.
-        /// </summary>
-        [JsonPropertyName("conditions")]
-        [DataField("conditions")]
-        public ReagentEffectCondition[]? Conditions;
-
-        public virtual string ReagentEffectFormat => "guidebook-reagent-effect-description";
-
-        protected abstract string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys);
-
-        /// <summary>
-        ///     What's the chance, from 0 to 1, that this effect will occur?
-        /// </summary>
-        [JsonPropertyName("probability")]
-        [DataField("probability")]
-        public float Probability = 1.0f;
-
-        [JsonIgnore]
-        [DataField("logImpact")]
-        public virtual LogImpact LogImpact { get; private set; } = LogImpact.Low;
-
-        /// <summary>
-        ///     Should this reagent effect log at all?
-        /// </summary>
-        [JsonIgnore]
-        [DataField("shouldLog")]
-        public virtual bool ShouldLog { get; private set; } = false;
-
-        public abstract void Effect(ReagentEffectArgs args);
-
-        /// <summary>
-        /// Produces a localized, bbcode'd guidebook description for this effect.
-        /// </summary>
-        /// <returns></returns>
-        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<string>())));
-        }
-    }
-
-    public static class ReagentEffectExt
-    {
-        public static bool ShouldApply(this ReagentEffect effect, ReagentEffectArgs args,
-            IRobustRandom? random = null)
-        {
-            if (random == null)
-                random = IoCManager.Resolve<IRobustRandom>();
-
-            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 (file)
index a32b4fa..0000000
+++ /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);
-
-        /// <summary>
-        /// Effect explanations are of the form "[chance to] [action] when [condition] and [condition]"
-        /// </summary>
-        /// <param name="prototype"></param>
-        /// <returns></returns>
-        public abstract string GuidebookExplanation(IPrototypeManager prototype);
-    }
-}
index 7e96332ebe97569de945d95286e9e8e1700047c7..fe937b9de4301fe276b6e3c3cf88e33b9d60a606 100644 (file)
@@ -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<ITileReaction> TileReactions = new(0);
 
         [DataField("plantMetabolism", serverOnly: true)]
-        public List<ReagentEffect> PlantMetabolisms = new(0);
+        public List<EntityEffect> PlantMetabolisms = new(0);
 
         [DataField]
         public float PricePerUnit;
@@ -170,7 +171,7 @@ namespace Content.Shared.Chemistry.Reagent
 
             var entMan = IoCManager.Resolve<IEntityManager>();
             var random = IoCManager.Resolve<IRobustRandom>();
-            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<SharedAdminLogSystem>().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<TransformComponent>(entity).Coordinates:coordinates}");
                 }
@@ -230,7 +231,7 @@ namespace Content.Shared.Chemistry.Reagent
         /// </summary>
         [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<ReactionMethod> 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 (file)
index 0000000..21e79f2
--- /dev/null
@@ -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;
+
+/// <summary>
+///     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 <see cref="Conditions"/> are satisfied.
+/// </summary>
+[ImplicitDataDefinitionForInheritors]
+[MeansImplicitUse]
+public abstract partial class EntityEffect
+{
+    private protected string _id => this.GetType().Name;
+    /// <summary>
+    ///     The list of conditions required for the effect to activate. Not required.
+    /// </summary>
+    [DataField("conditions")]
+    public EntityEffectCondition[]? Conditions;
+
+    public virtual string ReagentEffectFormat => "guidebook-reagent-effect-description";
+
+    protected abstract string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys);
+
+    /// <summary>
+    ///     What's the chance, from 0 to 1, that this effect will occur?
+    /// </summary>
+    [DataField("probability")]
+    public float Probability = 1.0f;
+
+    public virtual LogImpact LogImpact { get; private set; } = LogImpact.Low;
+
+    /// <summary>
+    ///     Should this entity effect log at all?
+    /// </summary>
+    public virtual bool ShouldLog { get; private set; } = false;
+
+    public abstract void Effect(EntityEffectBaseArgs args);
+
+    /// <summary>
+    /// Produces a localized, bbcode'd guidebook description for this effect.
+    /// </summary>
+    /// <returns></returns>
+    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<string>())));
+    }
+}
+
+public static class EntityEffectExt
+{
+    public static bool ShouldApply(this EntityEffect effect, EntityEffectBaseArgs args,
+        IRobustRandom? random = null)
+    {
+        if (random == null)
+            random = IoCManager.Resolve<IRobustRandom>();
+
+        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;
+    }
+}
+
+/// <summary>
+///     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).
+/// </summary>
+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 (file)
index 0000000..d6028b9
--- /dev/null
@@ -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);
+
+    /// <summary>
+    /// Effect explanations are of the form "[chance to] [action] when [condition] and [condition]"
+    /// </summary>
+    /// <param name="prototype"></param>
+    /// <returns></returns>
+    public abstract string GuidebookExplanation(IPrototypeManager prototype);
+}
+
index 72641309b318e82426d64ee150c45b4f453273d3..36c7b80b81bfff773bdccff98927f40f77a0e279 100644 (file)
     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
index 08f465386313ed97a6b05b23e3916101a66d1633..869db085970b2f3542bdb5cd38f489afe56e05a5 100644 (file)
@@ -1,4 +1,4 @@
-- type: entity
+- type: entity
   id: FloorLiquidPlasmaEntity
   name: liquid plasma
   description: Sweet, expensive nectar. Don't consume.
     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