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