using Content.Server.Body.Components;
-using Content.Server.EntityEffects.Effects;
+using Content.Shared.EntityEffects.Effects;
using Content.Server.Fluids.EntitySystems;
using Content.Server.Popups;
using Content.Shared.Alert;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Components;
using Content.Server.Chat.Systems;
-using Content.Server.EntityEffects.EffectConditions;
-using Content.Server.EntityEffects.Effects;
+using Content.Server.EntityEffects;
+using Content.Shared.EntityEffects.EffectConditions;
+using Content.Shared.EntityEffects.Effects;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Alert;
using Content.Shared.Atmos;
[Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly ChatSystem _chat = default!;
+ [Dependency] private readonly EntityEffectSystem _entityEffect = default!;
private static readonly ProtoId<MetabolismGroupPrototype> GasId = new("Gas");
foreach (var cond in effect.Conditions)
{
- if (cond is OrganType organ && !organ.Condition(lung, EntityManager))
+ if (cond is OrganType organ && !_entityEffect.OrganCondition(organ, lung))
return false;
}
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
using Robust.Shared.Utility;
+using Content.Server.EntityEffects;
+
namespace Content.Server.Botany;
[Prototype]
// 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(EntityEffect), typeof(MutationSystem))]
+[Access(typeof(BotanySystem), typeof(PlantHolderSystem), typeof(SeedExtractorSystem), typeof(PlantHolderComponent), typeof(EntityEffectSystem), typeof(MutationSystem))]
public partial class SeedData
{
#region Tracking
+++ /dev/null
-using Content.Server.Body.Components;
-using Content.Server.Body.Systems;
-using Content.Shared.EntityEffects;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.EffectConditions;
-
-/// <summary>
-/// Condition for if the entity is successfully breathing.
-/// </summary>
-public sealed partial class Breathing : EntityEffectCondition
-{
- /// <summary>
- /// If true, the entity must not have trouble breathing to pass.
- /// </summary>
- [DataField]
- public bool IsBreathing = true;
-
- public override bool Condition(EntityEffectBaseArgs args)
- {
- if (!args.EntityManager.TryGetComponent(args.TargetEntity, out RespiratorComponent? respiratorComp))
- return !IsBreathing; // They do not breathe.
-
- var breathingState = args.EntityManager.System<RespiratorSystem>().IsBreathing((args.TargetEntity, respiratorComp));
- return IsBreathing == breathingState;
- }
-
- public override string GuidebookExplanation(IPrototypeManager prototype)
- {
- return Loc.GetString("reagent-effect-condition-guidebook-breathing",
- ("isBreathing", IsBreathing));
- }
-}
+++ /dev/null
-using Content.Server.Body.Components;
-using Content.Shared.Body.Prototypes;
-using Content.Shared.EntityEffects;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.EntityEffects.EffectConditions;
-
-/// <summary>
-/// Requires that the metabolizing organ is or is not tagged with a certain MetabolizerType
-/// </summary>
-public sealed partial class OrganType : EntityEffectCondition
-{
- [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<MetabolizerTypePrototype>))]
- public string Type = default!;
-
- /// <summary>
- /// Does this condition pass when the organ has the type, or when it doesn't have the type?
- /// </summary>
- [DataField]
- public bool ShouldHave = true;
-
- public override bool Condition(EntityEffectBaseArgs args)
- {
- if (args is EntityEffectReagentArgs reagentArgs)
- {
- if (reagentArgs.OrganEntity == null)
- return false;
-
- return Condition(reagentArgs.OrganEntity.Value, reagentArgs.EntityManager);
- }
-
- // TODO: Someone needs to figure out how to do this for non-reagent effects.
- throw new NotImplementedException();
- }
-
- public bool Condition(Entity<MetabolizerComponent?> metabolizer, IEntityManager entMan)
- {
- metabolizer.Comp ??= entMan.GetComponentOrNull<MetabolizerComponent>(metabolizer.Owner);
- if (metabolizer.Comp != null
- && metabolizer.Comp.MetabolizerTypes != null
- && metabolizer.Comp.MetabolizerTypes.Contains(Type))
- return ShouldHave;
- return !ShouldHave;
- }
-
- public override string GuidebookExplanation(IPrototypeManager prototype)
- {
- return Loc.GetString("reagent-effect-condition-guidebook-organ-type",
- ("name", prototype.Index<MetabolizerTypePrototype>(Type).LocalizedName),
- ("shouldhave", ShouldHave));
- }
-}
+++ /dev/null
-using Content.Server.Temperature.Components;
-using Content.Server.Temperature.Systems;
-using Content.Shared.EntityEffects;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects
-{
- public sealed partial class AdjustTemperature : EntityEffect
- {
- [DataField]
- public float Amount;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-adjust-temperature",
- ("chance", Probability),
- ("deltasign", MathF.Sign(Amount)),
- ("amount", MathF.Abs(Amount)));
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (args.EntityManager.TryGetComponent(args.TargetEntity, out TemperatureComponent? temp))
- {
- var sys = args.EntityManager.EntitySysManager.GetEntitySystem<TemperatureSystem>();
- var amount = Amount;
-
- if (args is EntityEffectReagentArgs reagentArgs)
- {
- amount *= reagentArgs.Scale.Float();
- }
-
- sys.ChangeHeat(args.TargetEntity, amount, true, temp);
- }
- }
- }
-}
+++ /dev/null
-using Content.Server.Fluids.EntitySystems;
-using Content.Server.Spreader;
-using Content.Shared.Audio;
-using Content.Shared.Coordinates.Helpers;
-using Content.Shared.Database;
-using Content.Shared.EntityEffects;
-using Content.Shared.FixedPoint;
-using Content.Shared.Maps;
-using JetBrains.Annotations;
-using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
-using Robust.Shared.Audio.Systems;
-using Robust.Shared.Map;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.EntityEffects.Effects;
-
-/// <summary>
-/// Basically smoke and foam reactions.
-/// </summary>
-[UsedImplicitly]
-[DataDefinition]
-public sealed partial class AreaReactionEffect : EntityEffect
-{
- /// <summary>
- /// How many seconds will the effect stay, counting after fully spreading.
- /// </summary>
- [DataField("duration")] private float _duration = 10;
-
- /// <summary>
- /// How many units of reaction for 1 smoke entity.
- /// </summary>
- [DataField] public FixedPoint2 OverflowThreshold = FixedPoint2.New(2.5);
-
- /// <summary>
- /// The entity prototype that will be spawned as the effect.
- /// </summary>
- [DataField("prototypeId", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
- private string _prototypeId = default!;
-
- /// <summary>
- /// Sound that will get played when this reaction effect occurs.
- /// </summary>
- [DataField("sound", required: true)] private SoundSpecifier _sound = default!;
-
- public override bool ShouldLog => true;
-
- protected override string ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-area-reaction",
- ("duration", _duration)
- );
-
- public override LogImpact LogImpact => LogImpact.High;
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (args is EntityEffectReagentArgs reagentArgs)
- {
- if (reagentArgs.Source == null)
- return;
-
- var spreadAmount = (int) Math.Max(0, Math.Ceiling((reagentArgs.Quantity / OverflowThreshold).Float()));
- var splitSolution = reagentArgs.Source.SplitSolution(reagentArgs.Source.Volume);
- var transform = reagentArgs.EntityManager.GetComponent<TransformComponent>(reagentArgs.TargetEntity);
- var mapManager = IoCManager.Resolve<IMapManager>();
- var mapSys = reagentArgs.EntityManager.System<MapSystem>();
- var spreaderSys = args.EntityManager.System<SpreaderSystem>();
- var sys = args.EntityManager.System<TransformSystem>();
- var mapCoords = sys.GetMapCoordinates(reagentArgs.TargetEntity, xform: transform);
-
- if (!mapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid) ||
- !mapSys.TryGetTileRef(gridUid, grid, transform.Coordinates, out var tileRef))
- {
- return;
- }
-
- if (spreaderSys.RequiresFloorToSpread(_prototypeId) && tileRef.Tile.IsSpace())
- return;
-
- var coords = mapSys.MapToGrid(gridUid, mapCoords);
- var ent = reagentArgs.EntityManager.SpawnEntity(_prototypeId, coords.SnapToGrid());
-
- var smoke = reagentArgs.EntityManager.System<SmokeSystem>();
- smoke.StartSmoke(ent, splitSolution, _duration, spreadAmount);
-
- var audio = reagentArgs.EntityManager.System<SharedAudioSystem>();
- audio.PlayPvs(_sound, reagentArgs.TargetEntity, AudioParams.Default.WithVariation(0.25f));
- return;
- }
-
- // TODO: Someone needs to figure out how to do this for non-reagent effects.
- throw new NotImplementedException();
- }
-}
+++ /dev/null
-using Content.Server.Zombies;
-using Content.Shared.EntityEffects;
-using Robust.Shared.Prototypes;
-using Content.Shared.Zombies;
-
-namespace Content.Server.EntityEffects.Effects;
-
-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(EntityEffectBaseArgs args)
- {
- var entityManager = args.EntityManager;
- entityManager.EnsureComponent<ZombifyOnDeathComponent>(args.TargetEntity);
- entityManager.EnsureComponent<PendingZombieComponent>(args.TargetEntity);
- }
-}
-
+++ /dev/null
-using Content.Server.Body.Systems;
-using Content.Shared.EntityEffects;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects;
-
-/// <summary>
-/// Basically smoke and foam reactions.
-/// </summary>
-[UsedImplicitly]
-public sealed partial class ChemCleanBloodstream : EntityEffect
-{
- [DataField]
- public float CleanseRate = 3.0f;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-chem-clean-bloodstream", ("chance", Probability));
-
- public override void Effect(EntityEffectBaseArgs args)
- {
-
- var cleanseRate = CleanseRate;
-
- var bloodstreamSys = args.EntityManager.System<BloodstreamSystem>();
-
- if (args is EntityEffectReagentArgs reagentArgs)
- {
- if (reagentArgs.Source == null || reagentArgs.Reagent == null)
- return;
-
- cleanseRate *= reagentArgs.Scale.Float();
- bloodstreamSys.FlushChemicals(args.TargetEntity, reagentArgs.Reagent.ID, cleanseRate);
- }
- else
- {
- bloodstreamSys.FlushChemicals(args.TargetEntity, "", cleanseRate);
- }
- }
-}
+++ /dev/null
-using Content.Server.Medical;
-using Content.Shared.EntityEffects;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects
-{
- /// <summary>
- /// Forces you to vomit.
- /// </summary>
- [UsedImplicitly]
- public sealed partial class ChemVomit : EntityEffect
- {
- /// How many units of thirst to add each time we vomit
- [DataField]
- public float ThirstAmount = -8f;
- /// How many units of hunger to add each time we vomit
- [DataField]
- public float HungerAmount = -8f;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-chem-vomit", ("chance", Probability));
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (args is EntityEffectReagentArgs reagentArgs)
- if (reagentArgs.Scale != 1f)
- return;
-
- var vomitSys = args.EntityManager.EntitySysManager.GetEntitySystem<VomitSystem>();
-
- vomitSys.Vomit(args.TargetEntity, ThirstAmount, HungerAmount);
- }
- }
-}
+++ /dev/null
-using Content.Shared.EntityEffects;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.EntityEffects.Effects;
-
-[DataDefinition]
-public sealed partial class CreateEntityReactionEffect : EntityEffect
-{
- /// <summary>
- /// What entity to create.
- /// </summary>
- [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
- public string Entity = default!;
-
- /// <summary>
- /// How many entities to create per unit reaction.
- /// </summary>
- [DataField]
- public uint Number = 1;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-create-entity-reaction-effect",
- ("chance", Probability),
- ("entname", IoCManager.Resolve<IPrototypeManager>().Index<EntityPrototype>(Entity).Name),
- ("amount", Number));
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- var transform = args.EntityManager.GetComponent<TransformComponent>(args.TargetEntity);
- var transformSystem = args.EntityManager.System<SharedTransformSystem>();
- 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.TargetEntity, xform: transform));
- transformSystem.AttachToGridOrMap(uid);
-
- // TODO figure out how to properly spawn inside of containers
- // e.g. cheese:
- // if the user is holding a bowl milk & enzyme, should drop to floor, not attached to the user.
- // if reaction happens in a backpack, should insert cheese into backpack.
- // --> if it doesn't fit, iterate through parent storage until it attaches to the grid (again, DON'T attach to players).
- // if the reaction happens INSIDE a stomach? the bloodstream? I have no idea how to handle that.
- // presumably having cheese materialize inside of your blood would have "disadvantages".
- }
- }
-}
+++ /dev/null
-using Content.Server.Atmos.EntitySystems;
-using Content.Shared.Atmos;
-using Content.Shared.Database;
-using Content.Shared.EntityEffects;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects;
-
-public sealed partial class CreateGas : EntityEffect
-{
- [DataField(required: true)]
- public Gas Gas = default!;
-
- /// <summary>
- /// For each unit consumed, how many moles of gas should be created?
- /// </summary>
- [DataField]
- public float Multiplier = 3f;
-
- public override bool ShouldLog => true;
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- {
- var atmos = entSys.GetEntitySystem<AtmosphereSystem>();
- var gasProto = atmos.GetGas(Gas);
-
- return Loc.GetString("reagent-effect-guidebook-create-gas",
- ("chance", Probability),
- ("moles", Multiplier),
- ("gas", gasProto.Name));
- }
-
- public override LogImpact LogImpact => LogImpact.High;
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- var atmosSys = args.EntityManager.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
-
- var tileMix = atmosSys.GetContainingMixture(args.TargetEntity, false, true);
-
- if (tileMix != null)
- {
- if (args is EntityEffectReagentArgs reagentArgs)
- {
- tileMix.AdjustMoles(Gas, reagentArgs.Quantity.Float() * Multiplier);
- }
- else
- {
- tileMix.AdjustMoles(Gas, Multiplier);
- }
- }
- }
-}
+++ /dev/null
-using Content.Server.Zombies;
-using Content.Shared.EntityEffects;
-using Robust.Shared.Prototypes;
-using Content.Shared.Zombies;
-
-namespace Content.Server.EntityEffects.Effects;
-
-public sealed partial class CureZombieInfection : EntityEffect
-{
- [DataField]
- public bool Innoculate;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- {
- if(Innoculate)
- return Loc.GetString("reagent-effect-guidebook-innoculate-zombie-infection", ("chance", Probability));
-
- return Loc.GetString("reagent-effect-guidebook-cure-zombie-infection", ("chance", Probability));
- }
-
- // Removes the Zombie Infection Components
- public override void Effect(EntityEffectBaseArgs args)
- {
- var entityManager = args.EntityManager;
- if (entityManager.HasComponent<IncurableZombieComponent>(args.TargetEntity))
- return;
-
- entityManager.RemoveComponent<ZombifyOnDeathComponent>(args.TargetEntity);
- entityManager.RemoveComponent<PendingZombieComponent>(args.TargetEntity);
-
- if (Innoculate)
- {
- entityManager.EnsureComponent<ZombieImmuneComponent>(args.TargetEntity);
- }
- }
-}
-
+++ /dev/null
-using Content.Server.Emp;
-using Content.Shared.EntityEffects;
-using Content.Shared.Chemistry.Reagent;
-using Robust.Server.GameObjects;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects;
-
-
-[DataDefinition]
-public sealed partial class EmpReactionEffect : EntityEffect
-{
- /// <summary>
- /// Impulse range per unit of quantity
- /// </summary>
- [DataField("rangePerUnit")]
- public float EmpRangePerUnit = 0.5f;
-
- /// <summary>
- /// Maximum impulse range
- /// </summary>
- [DataField("maxRange")]
- public float EmpMaxRange = 10;
-
- /// <summary>
- /// How much energy will be drain from sources
- /// </summary>
- [DataField]
- public float EnergyConsumption = 12500;
-
- /// <summary>
- /// Amount of time entities will be disabled
- /// </summary>
- [DataField("duration")]
- public float DisableDuration = 15;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-emp-reaction-effect", ("chance", Probability));
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- var tSys = args.EntityManager.System<TransformSystem>();
- var transform = args.EntityManager.GetComponent<TransformComponent>(args.TargetEntity);
-
- var range = EmpRangePerUnit;
-
- if (args is EntityEffectReagentArgs reagentArgs)
- {
- range = MathF.Min((float) (reagentArgs.Quantity * EmpRangePerUnit), EmpMaxRange);
- }
-
- args.EntityManager.System<EmpSystem>()
- .EmpPulse(tSys.GetMapCoordinates(args.TargetEntity, xform: transform),
- range,
- EnergyConsumption,
- DisableDuration);
- }
-}
+++ /dev/null
-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;
-
- // The fire stack multiplier if fire stacks already exist on target, only works if 0 or greater
- [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;
-
- // Sets the multiplier for FireStacks to MultiplierOnExisting is 0 or greater and target already has FireStacks
- var multiplier = flammable.FireStacks != 0f && MultiplierOnExisting >= 0 ? MultiplierOnExisting : Multiplier;
- var quantity = 1f;
- if (args is EntityEffectReagentArgs reagentArgs)
- {
- quantity = reagentArgs.Quantity.Float();
- reagentArgs.EntityManager.System<FlammableSystem>().AdjustFireStacks(args.TargetEntity, quantity * multiplier, flammable);
- if (reagentArgs.Reagent != null)
- reagentArgs.Source?.RemoveReagent(reagentArgs.Reagent.ID, reagentArgs.Quantity);
- }
- else
- {
- args.EntityManager.System<FlammableSystem>().AdjustFireStacks(args.TargetEntity, multiplier, flammable);
- }
- }
- }
-}
+++ /dev/null
-using Content.Shared.EntityEffects;
-using Content.Server.Flash;
-using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects;
-
-[DataDefinition]
-public sealed partial class FlashReactionEffect : EntityEffect
-{
- /// <summary>
- /// Flash range per unit of reagent.
- /// </summary>
- [DataField]
- public float RangePerUnit = 0.2f;
-
- /// <summary>
- /// Maximum flash range.
- /// </summary>
- [DataField]
- public float MaxRange = 10f;
-
- /// <summary>
- /// How much to entities are slowed down.
- /// </summary>
- [DataField]
- public float SlowTo = 0.5f;
-
- /// <summary>
- /// The time entities will be flashed in seconds.
- /// The default is chosen to be better than the hand flash so it is worth using it for grenades etc.
- /// </summary>
- [DataField]
- public float Duration = 4f;
-
- /// <summary>
- /// The prototype ID used for the visual effect.
- /// </summary>
- [DataField]
- public EntProtoId? FlashEffectPrototype = "ReactionFlash";
-
- /// <summary>
- /// The sound the flash creates.
- /// </summary>
- [DataField]
- public SoundSpecifier? Sound = new SoundPathSpecifier("/Audio/Weapons/flash.ogg");
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-flash-reaction-effect", ("chance", Probability));
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- var transform = args.EntityManager.GetComponent<TransformComponent>(args.TargetEntity);
- var transformSystem = args.EntityManager.System<SharedTransformSystem>();
-
- var range = 1f;
-
- if (args is EntityEffectReagentArgs reagentArgs)
- range = MathF.Min((float)(reagentArgs.Quantity * RangePerUnit), MaxRange);
-
- args.EntityManager.System<FlashSystem>().FlashArea(
- args.TargetEntity,
- null,
- range,
- Duration * 1000,
- slowTo: SlowTo,
- sound: Sound);
-
- if (FlashEffectPrototype == null)
- return;
-
- var uid = args.EntityManager.SpawnEntity(FlashEffectPrototype, transformSystem.GetMapCoordinates(transform));
- transformSystem.AttachToGridOrMap(uid);
-
- if (!args.EntityManager.TryGetComponent<PointLightComponent>(uid, out var pointLightComp))
- return;
- var pointLightSystem = args.EntityManager.System<SharedPointLightSystem>();
- // PointLights with a radius lower than 1.1 are too small to be visible, so this is hardcoded
- pointLightSystem.SetRadius(uid, MathF.Max(1.1f, range), pointLightComp);
- }
-}
+++ /dev/null
-using Content.Server.Atmos.Components;
-using Content.Server.Atmos.EntitySystems;
-using Content.Shared.Database;
-using Content.Shared.EntityEffects;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects;
-
-/// <summary>
-/// Ignites a mob.
-/// </summary>
-public sealed partial class Ignite : EntityEffect
-{
- public override bool ShouldLog => true;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-ignite", ("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 flamSys = args.EntityManager.System<FlammableSystem>();
- if (args is EntityEffectReagentArgs reagentArgs)
- {
- flamSys.Ignite(reagentArgs.TargetEntity, reagentArgs.OrganEntity ?? reagentArgs.TargetEntity, flammable: flammable);
- }
- else
- {
- flamSys.Ignite(args.TargetEntity, args.TargetEntity, flammable: flammable);
- }
- }
-}
+++ /dev/null
-using Content.Server.Ghost.Roles.Components;
-using Content.Server.Speech.Components;
-using Content.Shared.EntityEffects;
-using Content.Shared.Mind.Components;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects;
-
-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(EntityEffectBaseArgs args)
- {
- var entityManager = args.EntityManager;
- 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
- // We call this before the mind check to allow things like player-controlled mice to be able to benefit from the effect
- entityManager.RemoveComponent<ReplacementAccentComponent>(uid);
- entityManager.RemoveComponent<MonkeyAccentComponent>(uid);
-
- // Stops from adding a ghost role to things like people who already have a mind
- if (entityManager.TryGetComponent<MindContainerComponent>(uid, out var mindContainer) && mindContainer.HasMind)
- {
- return;
- }
-
- // Don't add a ghost role to things that already have ghost roles
- if (entityManager.TryGetComponent(uid, out GhostRoleComponent? ghostRole))
- {
- return;
- }
-
- ghostRole = entityManager.AddComponent<GhostRoleComponent>(uid);
- entityManager.EnsureComponent<GhostTakeoverAvailableComponent>(uid);
-
- var entityData = entityManager.GetComponent<MetaDataComponent>(uid);
- ghostRole.RoleName = entityData.EntityName;
- ghostRole.RoleDescription = Loc.GetString("ghost-role-information-cognizine-description");
- }
-}
+++ /dev/null
-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 ModifyBleedAmount : EntityEffect
-{
- [DataField]
- public bool Scaled = false;
-
- [DataField]
- public float Amount = -1.0f;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-modify-bleed-amount", ("chance", Probability),
- ("deltasign", MathF.Sign(Amount)));
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (args.EntityManager.TryGetComponent<BloodstreamComponent>(args.TargetEntity, out var blood))
- {
- var sys = args.EntityManager.System<BloodstreamSystem>();
- var amt = Amount;
- if (args is EntityEffectReagentArgs reagentArgs) {
- if (Scaled)
- amt *= reagentArgs.Quantity.Float();
- amt *= reagentArgs.Scale.Float();
- }
-
- sys.TryModifyBleedAmount(args.TargetEntity, amt, blood);
- }
- }
-}
+++ /dev/null
-using Content.Server.Body.Components;
-using Content.Server.Body.Systems;
-using Content.Shared.EntityEffects;
-using Content.Shared.FixedPoint;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects;
-
-public sealed partial class ModifyBloodLevel : EntityEffect
-{
- [DataField]
- public bool Scaled = false;
-
- [DataField]
- public FixedPoint2 Amount = 1.0f;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-modify-blood-level", ("chance", Probability),
- ("deltasign", MathF.Sign(Amount.Float())));
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (args.EntityManager.TryGetComponent<BloodstreamComponent>(args.TargetEntity, out var blood))
- {
- var sys = args.EntityManager.System<BloodstreamSystem>();
- var amt = Amount;
- if (args is EntityEffectReagentArgs reagentArgs)
- {
- if (Scaled)
- amt *= reagentArgs.Quantity;
- amt *= reagentArgs.Scale;
- }
-
- sys.TryModifyBloodLevel(args.TargetEntity, amt, blood);
- }
- }
-}
+++ /dev/null
-using Content.Server.Body.Components;
-using Content.Shared.Atmos;
-using Content.Shared.EntityEffects;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects;
-
-public sealed partial class ModifyLungGas : EntityEffect
-{
- [DataField("ratios", required: true)]
- private Dictionary<Gas, float> _ratios = default!;
-
- // JUSTIFICATION: This is internal magic that players never directly interact with.
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => null;
-
- public override void Effect(EntityEffectBaseArgs args)
- {
-
- LungComponent? lung;
- float amount = 1f;
-
- if (args is EntityEffectReagentArgs reagentArgs)
- {
- if (!args.EntityManager.TryGetComponent<LungComponent>(reagentArgs.OrganEntity, out var organLung))
- return;
- lung = organLung;
- amount = reagentArgs.Quantity.Float();
- }
- else
- {
- if (!args.EntityManager.TryGetComponent<LungComponent>(args.TargetEntity, out var organLung)) //Likely needs to be modified to ensure it works correctly
- return;
- lung = organLung;
- }
-
- if (lung != null)
- {
- foreach (var (gas, ratio) in _ratios)
- {
- var quantity = ratio * amount / Atmospherics.BreathMolesToReagentMultiplier;
- if (quantity < 0)
- quantity = Math.Max(quantity, -lung.Air[(int) gas]);
- lung.Air.AdjustMoles(gas, quantity);
- }
- }
- }
-}
+++ /dev/null
-using Content.Server.Body.Components;
-using Content.Server.Body.Systems;
-using Content.Shared.EntityEffects;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects;
-
-public sealed partial class Oxygenate : EntityEffect
-{
- [DataField]
- public float Factor = 1f;
-
- // JUSTIFICATION: This is internal magic that players never directly interact with.
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => null;
-
- public override void Effect(EntityEffectBaseArgs args)
- {
-
- var multiplier = 1f;
- if (args is EntityEffectReagentArgs reagentArgs)
- {
- multiplier = reagentArgs.Quantity.Float();
- }
-
- if (args.EntityManager.TryGetComponent<RespiratorComponent>(args.TargetEntity, out var resp))
- {
- var respSys = args.EntityManager.System<RespiratorSystem>();
- respSys.UpdateSaturation(args.TargetEntity, multiplier * Factor, resp);
- }
- }
-}
+++ /dev/null
-using Content.Server.Botany;
-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]
-public sealed partial class PlantChangeStat : EntityEffect
-{
- [DataField]
- public string TargetValue;
-
- [DataField]
- public float MinValue;
-
- [DataField]
- public float MaxValue;
-
- [DataField]
- public int Steps;
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- var plantHolder = args.EntityManager.GetComponent<PlantHolderComponent>(args.TargetEntity);
- if (plantHolder == null || plantHolder.Seed == null)
- return;
-
- var member = plantHolder.Seed.GetType().GetField(TargetValue);
- var mutationSys = args.EntityManager.System<MutationSystem>();
-
- if (member == null)
- {
- mutationSys.Log.Error(this.GetType().Name + " Error: Member " + TargetValue + " not found on " + plantHolder.GetType().Name + ". Did you misspell it?");
- return;
- }
-
- var currentValObj = member.GetValue(plantHolder.Seed);
- if (currentValObj == null)
- return;
-
- if (member.FieldType == typeof(float))
- {
- var floatVal = (float)currentValObj;
- MutateFloat(ref floatVal, MinValue, MaxValue, Steps);
- member.SetValue(plantHolder.Seed, floatVal);
- }
- else if (member.FieldType == typeof(int))
- {
- var intVal = (int)currentValObj;
- MutateInt(ref intVal, (int)MinValue, (int)MaxValue, Steps);
- member.SetValue(plantHolder.Seed, intVal);
- }
- else if (member.FieldType == typeof(bool))
- {
- var boolVal = (bool)currentValObj;
- boolVal = !boolVal;
- member.SetValue(plantHolder.Seed, boolVal);
- }
- }
-
- // Mutate reference 'val' between 'min' and 'max' by pretending the value
- // is representable by a thermometer code with 'bits' number of bits and
- // randomly flipping some of them.
- private void MutateFloat(ref float val, float min, float max, int bits)
- {
- if (min == max)
- {
- val = min;
- return;
- }
-
- // Starting number of bits that are high, between 0 and bits.
- // In other words, it's val mapped linearly from range [min, max] to range [0, bits], and then rounded.
- int valInt = (int)MathF.Round((val - min) / (max - min) * bits);
- // val may be outside the range of min/max due to starting prototype values, so clamp.
- valInt = Math.Clamp(valInt, 0, bits);
-
- // Probability that the bit flip increases n.
- // The higher the current value is, the lower the probability of increasing value is, and the higher the probability of decreasive it it.
- // In other words, it tends to go to the middle.
- float probIncrease = 1 - (float)valInt / bits;
- int valIntMutated;
- if (Random(probIncrease))
- {
- valIntMutated = valInt + 1;
- }
- else
- {
- valIntMutated = valInt - 1;
- }
-
- // Set value based on mutated thermometer code.
- float valMutated = Math.Clamp((float)valIntMutated / bits * (max - min) + min, min, max);
- val = valMutated;
- }
-
- private void MutateInt(ref int val, int min, int max, int bits)
- {
- if (min == max)
- {
- val = min;
- return;
- }
-
- // Starting number of bits that are high, between 0 and bits.
- // In other words, it's val mapped linearly from range [min, max] to range [0, bits], and then rounded.
- int valInt = (int)MathF.Round((val - min) / (max - min) * bits);
- // val may be outside the range of min/max due to starting prototype values, so clamp.
- valInt = Math.Clamp(valInt, 0, bits);
-
- // Probability that the bit flip increases n.
- // The higher the current value is, the lower the probability of increasing value is, and the higher the probability of decreasing it.
- // In other words, it tends to go to the middle.
- float probIncrease = 1 - (float)valInt / bits;
- int valMutated;
- if (Random(probIncrease))
- {
- valMutated = val + 1;
- }
- else
- {
- valMutated = val - 1;
- }
-
- valMutated = Math.Clamp(valMutated, min, max);
- val = valMutated;
- }
-
- private bool Random(float odds)
- {
- var random = IoCManager.Resolve<IRobustRandom>();
- return random.Prob(odds);
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- {
- throw new NotImplementedException();
- }
-}
+++ /dev/null
-using Content.Server.Botany.Systems;
-using Content.Shared.EntityEffects;
-
-namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
-
-public sealed partial class PlantAdjustHealth : PlantAdjustAttribute
-{
- public override string GuidebookAttributeName { get; set; } = "plant-attribute-health";
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (!CanMetabolize(args.TargetEntity, out var plantHolderComp, args.EntityManager))
- return;
-
- var plantHolder = args.EntityManager.System<PlantHolderSystem>();
-
- plantHolderComp.Health += Amount;
- plantHolder.CheckHealth(args.TargetEntity, plantHolderComp);
- }
-}
-
+++ /dev/null
-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;
- }
-}
+++ /dev/null
-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;
- }
-}
-
+++ /dev/null
-using Content.Server.Botany.Systems;
-using Content.Shared.EntityEffects;
-using JetBrains.Annotations;
-
-namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
-
-[UsedImplicitly]
-public sealed partial class PlantAdjustNutrition : PlantAdjustAttribute
-{
- public override string GuidebookAttributeName { get; set; } = "plant-attribute-nutrition";
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (!CanMetabolize(args.TargetEntity, out var plantHolderComp, args.EntityManager, mustHaveAlivePlant: false))
- return;
-
- var plantHolder = args.EntityManager.System<PlantHolderSystem>();
-
- plantHolder.AdjustNutrient(args.TargetEntity, Amount, plantHolderComp);
- }
-}
+++ /dev/null
-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;
- }
-}
-
+++ /dev/null
-using Content.Server.Botany.Systems;
-using Content.Shared.EntityEffects;
-
-namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
-
-/// <summary>
-/// Handles increase or decrease of plant potency.
-/// </summary>
-
-public sealed partial class PlantAdjustPotency : PlantAdjustAttribute
-{
- public override string GuidebookAttributeName { get; set; } = "plant-attribute-potency";
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (!CanMetabolize(args.TargetEntity, out var plantHolderComp, args.EntityManager))
- return;
-
- if (plantHolderComp.Seed == null)
- return;
-
- var plantHolder = args.EntityManager.System<PlantHolderSystem>();
-
- plantHolder.EnsureUniqueSeed(args.TargetEntity, plantHolderComp);
-
- plantHolderComp.Seed.Potency = Math.Max(plantHolderComp.Seed.Potency + Amount, 1);
- }
-}
+++ /dev/null
-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;
- }
-}
-
+++ /dev/null
-using Content.Server.Botany.Systems;
-using Content.Shared.EntityEffects;
-using JetBrains.Annotations;
-
-namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
-
-[UsedImplicitly]
-public sealed partial class PlantAdjustWater : PlantAdjustAttribute
-{
- public override string GuidebookAttributeName { get; set; } = "plant-attribute-water";
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (!CanMetabolize(args.TargetEntity, out var plantHolderComp, args.EntityManager, mustHaveAlivePlant: false))
- return;
-
- var plantHolder = args.EntityManager.System<PlantHolderSystem>();
-
- plantHolder.AdjustWater(args.TargetEntity, Amount, plantHolderComp);
- }
-}
-
+++ /dev/null
-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;
- }
-}
+++ /dev/null
-using Content.Server.Botany.Systems;
-using Content.Shared.EntityEffects;
-using JetBrains.Annotations;
-
-namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
-
-[UsedImplicitly]
-public sealed partial class PlantAffectGrowth : PlantAdjustAttribute
-{
- public override string GuidebookAttributeName { get; set; } = "plant-attribute-growth";
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (!CanMetabolize(args.TargetEntity, out var plantHolderComp, args.EntityManager))
- return;
-
- var plantHolder = args.EntityManager.System<PlantHolderSystem>();
-
- plantHolder.AffectGrowth(args.TargetEntity, (int) Amount, plantHolderComp);
- }
-}
-
+++ /dev/null
-using Content.Server.Botany.Components;
-using Content.Shared.EntityEffects;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Random;
-
-namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
-
-[UsedImplicitly]
-[DataDefinition]
-public sealed partial class PlantCryoxadone : EntityEffect
-{
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (!args.EntityManager.TryGetComponent(args.TargetEntity, out PlantHolderComponent? plantHolderComp)
- || plantHolderComp.Seed == null || plantHolderComp.Dead)
- return;
-
- var deviation = 0;
- var seed = plantHolderComp.Seed;
- var random = IoCManager.Resolve<IRobustRandom>();
- if (plantHolderComp.Age > seed.Maturation)
- deviation = (int) Math.Max(seed.Maturation - 1, plantHolderComp.Age - random.Next(7, 10));
- else
- deviation = (int) (seed.Maturation / seed.GrowthStages);
- plantHolderComp.Age -= deviation;
- plantHolderComp.LastProduce = plantHolderComp.Age;
- plantHolderComp.SkipAging++;
- plantHolderComp.ForceUpdate = true;
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-cryoxadone", ("chance", Probability));
-}
+++ /dev/null
-using Content.Server.Botany.Components;
-using Content.Server.Botany.Systems;
-using Content.Shared.EntityEffects;
-using Content.Shared.Popups;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
-
-/// <summary>
-/// Handles removal of seeds on a plant.
-/// </summary>
-
-public sealed partial class PlantDestroySeeds : EntityEffect
-{
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (
- !args.EntityManager.TryGetComponent(args.TargetEntity, out PlantHolderComponent? plantHolderComp)
- || plantHolderComp.Seed == null
- || plantHolderComp.Dead
- || plantHolderComp.Seed.Immutable
- )
- return;
-
- var plantHolder = args.EntityManager.System<PlantHolderSystem>();
- var popupSystem = args.EntityManager.System<SharedPopupSystem>();
-
- if (plantHolderComp.Seed.Seedless == false)
- {
- plantHolder.EnsureUniqueSeed(args.TargetEntity, plantHolderComp);
- popupSystem.PopupEntity(
- Loc.GetString("botany-plant-seedsdestroyed"),
- args.TargetEntity,
- PopupType.SmallCaution
- );
- plantHolderComp.Seed.Seedless = true;
- }
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) =>
- Loc.GetString("reagent-effect-guidebook-plant-seeds-remove", ("chance", Probability));
-}
+++ /dev/null
-using Content.Server.Botany.Components;
-using Content.Server.Botany.Systems;
-using Content.Shared.EntityEffects;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Random;
-
-namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
-
-[UsedImplicitly]
-[DataDefinition]
-public sealed partial class PlantDiethylamine : EntityEffect
-{
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (!args.EntityManager.TryGetComponent(args.TargetEntity, out PlantHolderComponent? plantHolderComp)
- || plantHolderComp.Seed == null || plantHolderComp.Dead ||
- plantHolderComp.Seed.Immutable)
- return;
-
-
- var plantHolder = args.EntityManager.System<PlantHolderSystem>();
-
- var random = IoCManager.Resolve<IRobustRandom>();
-
- if (random.Prob(0.1f))
- {
- plantHolder.EnsureUniqueSeed(args.TargetEntity, plantHolderComp);
- plantHolderComp.Seed.Lifespan++;
- }
-
- if (random.Prob(0.1f))
- {
- plantHolder.EnsureUniqueSeed(args.TargetEntity, plantHolderComp);
- plantHolderComp.Seed.Endurance++;
- }
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-diethylamine", ("chance", Probability));
-}
-
+++ /dev/null
-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));
-}
+++ /dev/null
-using Content.Server.Botany.Components;
-using Content.Server.Botany.Systems;
-using Content.Shared.EntityEffects;
-using Content.Shared.Popups;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
-
-/// <summary>
-/// Handles restoral of seeds on a plant.
-/// </summary>
-
-public sealed partial class PlantRestoreSeeds : EntityEffect
-{
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (
- !args.EntityManager.TryGetComponent(args.TargetEntity, out PlantHolderComponent? plantHolderComp)
- || plantHolderComp.Seed == null
- || plantHolderComp.Dead
- || plantHolderComp.Seed.Immutable
- )
- return;
-
- var plantHolder = args.EntityManager.System<PlantHolderSystem>();
- var popupSystem = args.EntityManager.System<SharedPopupSystem>();
-
- if (plantHolderComp.Seed.Seedless)
- {
- plantHolder.EnsureUniqueSeed(args.TargetEntity, plantHolderComp);
- popupSystem.PopupEntity(Loc.GetString("botany-plant-seedsrestored"), args.TargetEntity);
- plantHolderComp.Seed.Seedless = false;
- }
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) =>
- Loc.GetString("reagent-effect-guidebook-plant-seeds-add", ("chance", Probability));
-}
+++ /dev/null
-using Content.Server.Botany.Components;
-using Content.Server.Botany.Systems;
-using Content.Shared.EntityEffects;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Random;
-
-namespace Content.Server.EntityEffects.Effects.PlantMetabolism;
-
-[UsedImplicitly]
-[DataDefinition]
-public sealed partial class RobustHarvest : EntityEffect
-{
- [DataField]
- public int PotencyLimit = 50;
-
- [DataField]
- public int PotencyIncrease = 3;
-
- [DataField]
- public int PotencySeedlessThreshold = 30;
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (!args.EntityManager.TryGetComponent(args.TargetEntity, out PlantHolderComponent? plantHolderComp)
- || plantHolderComp.Seed == null || plantHolderComp.Dead ||
- plantHolderComp.Seed.Immutable)
- return;
-
-
- var plantHolder = args.EntityManager.System<PlantHolderSystem>();
- var random = IoCManager.Resolve<IRobustRandom>();
-
- if (plantHolderComp.Seed.Potency < PotencyLimit)
- {
- plantHolder.EnsureUniqueSeed(args.TargetEntity, plantHolderComp);
- plantHolderComp.Seed.Potency = Math.Min(plantHolderComp.Seed.Potency + PotencyIncrease, PotencyLimit);
-
- if (plantHolderComp.Seed.Potency > PotencySeedlessThreshold)
- {
- plantHolderComp.Seed.Seedless = true;
- }
- }
- else if (plantHolderComp.Seed.Yield > 1 && random.Prob(0.1f))
- {
- // Too much of a good thing reduces yield
- plantHolder.EnsureUniqueSeed(args.TargetEntity, plantHolderComp);
- plantHolderComp.Seed.Yield--;
- }
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-robust-harvest", ("seedlesstreshold", PotencySeedlessThreshold), ("limit", PotencyLimit), ("increase", PotencyIncrease), ("chance", Probability));
-}
+++ /dev/null
-using Content.Server.Botany;
-using Content.Server.Botany.Components;
-using Content.Shared.EntityEffects;
-using Content.Shared.Random;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Random;
-
-namespace Content.Server.EntityEffects.Effects;
-
-/// <summary>
-/// changes the chemicals available in a plant's produce
-/// </summary>
-public sealed partial class PlantMutateChemicals : EntityEffect
-{
- public override void Effect(EntityEffectBaseArgs args)
- {
- var plantholder = args.EntityManager.GetComponent<PlantHolderComponent>(args.TargetEntity);
-
- if (plantholder.Seed == null)
- return;
-
- var random = IoCManager.Resolve<IRobustRandom>();
- var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
- var chemicals = plantholder.Seed.Chemicals;
- var randomChems = prototypeManager.Index<WeightedRandomFillSolutionPrototype>("RandomPickBotanyReagent").Fills;
-
- // Add a random amount of a random chemical to this set of chemicals
- if (randomChems != null)
- {
- var pick = random.Pick<RandomFillSolution>(randomChems);
- var chemicalId = random.Pick(pick.Reagents);
- var amount = random.Next(1, (int)pick.Quantity);
- var seedChemQuantity = new SeedChemQuantity();
- if (chemicals.ContainsKey(chemicalId))
- {
- seedChemQuantity.Min = chemicals[chemicalId].Min;
- seedChemQuantity.Max = chemicals[chemicalId].Max + amount;
- }
- else
- {
- seedChemQuantity.Min = 1;
- seedChemQuantity.Max = 1 + amount;
- seedChemQuantity.Inherent = false;
- }
- var potencyDivisor = (int)Math.Ceiling(100.0f / seedChemQuantity.Max);
- seedChemQuantity.PotencyDivisor = potencyDivisor;
- chemicals[chemicalId] = seedChemQuantity;
- }
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- {
- return "TODO";
- }
-}
+++ /dev/null
-using Content.Server.Botany.Components;
-using Content.Shared.Atmos;
-using Content.Shared.EntityEffects;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Random;
-using System.Linq;
-
-namespace Content.Server.EntityEffects.Effects;
-
-/// <summary>
-/// changes the gases that a plant or produce create.
-/// </summary>
-public sealed partial class PlantMutateExudeGasses : EntityEffect
-{
- [DataField]
- public float MinValue = 0.01f;
-
- [DataField]
- public float MaxValue = 0.5f;
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- var plantholder = args.EntityManager.GetComponent<PlantHolderComponent>(args.TargetEntity);
-
- if (plantholder.Seed == null)
- return;
-
- var random = IoCManager.Resolve<IRobustRandom>();
- var gasses = plantholder.Seed.ExudeGasses;
-
- // Add a random amount of a random gas to this gas dictionary
- float amount = random.NextFloat(MinValue, MaxValue);
- Gas gas = random.Pick(Enum.GetValues(typeof(Gas)).Cast<Gas>().ToList());
- if (gasses.ContainsKey(gas))
- {
- gasses[gas] += amount;
- }
- else
- {
- gasses.Add(gas, amount);
- }
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- {
- return "TODO";
- }
-}
-
-/// <summary>
-/// changes the gases that a plant or produce consumes.
-/// </summary>
-public sealed partial class PlantMutateConsumeGasses : EntityEffect
-{
- [DataField]
- public float MinValue = 0.01f;
-
- [DataField]
- public float MaxValue = 0.5f;
- public override void Effect(EntityEffectBaseArgs args)
- {
- var plantholder = args.EntityManager.GetComponent<PlantHolderComponent>(args.TargetEntity);
-
- if (plantholder.Seed == null)
- return;
-
- var random = IoCManager.Resolve<IRobustRandom>();
- var gasses = plantholder.Seed.ConsumeGasses;
-
- // Add a random amount of a random gas to this gas dictionary
- float amount = random.NextFloat(MinValue, MaxValue);
- Gas gas = random.Pick(Enum.GetValues(typeof(Gas)).Cast<Gas>().ToList());
- if (gasses.ContainsKey(gas))
- {
- gasses[gas] += amount;
- }
- else
- {
- gasses.Add(gas, amount);
- }
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- {
- return "TODO";
- }
-}
+++ /dev/null
-using Content.Server.Botany;
-using Content.Server.Botany.Components;
-using Content.Shared.EntityEffects;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects;
-
-/// <summary>
-/// Upgrades a plant's harvest type.
-/// </summary>
-public sealed partial class PlantMutateHarvest : EntityEffect
-{
- public override void Effect(EntityEffectBaseArgs args)
- {
- var plantholder = args.EntityManager.GetComponent<PlantHolderComponent>(args.TargetEntity);
-
- if (plantholder.Seed == null)
- return;
-
- if (plantholder.Seed.HarvestRepeat == HarvestType.NoRepeat)
- plantholder.Seed.HarvestRepeat = HarvestType.Repeat;
- else if (plantholder.Seed.HarvestRepeat == HarvestType.Repeat)
- plantholder.Seed.HarvestRepeat = HarvestType.SelfHarvest;
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- {
- return "TODO";
- }
-}
+++ /dev/null
-using Content.Server.Botany;
-using Content.Server.Botany.Components;
-using Content.Shared.EntityEffects;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Random;
-using Serilog;
-
-namespace Content.Server.EntityEffects.Effects;
-
-/// <summary>
-/// Changes a plant into one of the species its able to mutate into.
-/// </summary>
-public sealed partial class PlantSpeciesChange : EntityEffect
-{
- public override void Effect(EntityEffectBaseArgs args)
- {
- var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
- var plantholder = args.EntityManager.GetComponent<PlantHolderComponent>(args.TargetEntity);
-
- if (plantholder.Seed == null)
- return;
-
- if (plantholder.Seed.MutationPrototypes.Count == 0)
- return;
-
- var random = IoCManager.Resolve<IRobustRandom>();
- var targetProto = random.Pick(plantholder.Seed.MutationPrototypes);
- prototypeManager.TryIndex(targetProto, out SeedPrototype? protoSeed);
-
- if (protoSeed == null)
- {
- Log.Error($"Seed prototype could not be found: {targetProto}!");
- return;
- }
-
- plantholder.Seed = plantholder.Seed.SpeciesChange(protoSeed);
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- {
- return "TODO";
- }
-}
+++ /dev/null
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.EntityEffects;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-using Content.Server.Atmos.Rotting;
-
-namespace Content.Server.EntityEffects.Effects
-{
- /// <summary>
- /// Reduces the rotting accumulator on the patient, making them revivable.
- /// </summary>
- [UsedImplicitly]
- public sealed partial class ReduceRotting : EntityEffect
- {
- [DataField("seconds")]
- public double RottingAmount = 10;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-reduce-rotting",
- ("chance", Probability),
- ("time", RottingAmount));
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (args is EntityEffectReagentArgs reagentArgs)
- {
- if (reagentArgs.Scale != 1f)
- return;
- }
-
- var rottingSys = args.EntityManager.EntitySysManager.GetEntitySystem<RottingSystem>();
-
- rottingSys.ReduceAccumulator(args.TargetEntity, TimeSpan.FromSeconds(RottingAmount));
- }
- }
-}
+++ /dev/null
-using Content.Server.Traits.Assorted;
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.EntityEffects;
-using JetBrains.Annotations;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.EntityEffects.Effects;
-
-/// <summary>
-/// Reset narcolepsy timer
-/// </summary>
-[UsedImplicitly]
-public sealed partial class ResetNarcolepsy : EntityEffect
-{
- /// <summary>
- /// The # of seconds the effect resets the narcolepsy timer to
- /// </summary>
- [DataField("TimerReset")]
- public int TimerReset = 600;
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-reset-narcolepsy", ("chance", Probability));
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- if (args is EntityEffectReagentArgs reagentArgs)
- if (reagentArgs.Scale != 1f)
- return;
-
- args.EntityManager.EntitySysManager.GetEntitySystem<NarcolepsySystem>().AdjustNarcolepsyTimer(args.TargetEntity, TimerReset);
- }
-}
+++ /dev/null
-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.EntityEffects.Effects
-{
- /// <summary>
- /// Attempts to find a HungerComponent on the target,
- /// and to update it's hunger values.
- /// </summary>
- public sealed partial class SatiateHunger : EntityEffect
- {
- private const float DefaultNutritionFactor = 3.0f;
-
- /// <summary>
- /// How much hunger is satiated.
- /// Is multiplied by quantity if used with EntityEffectReagentArgs.
- /// </summary>
- [DataField("factor")] public float NutritionFactor { get; set; } = DefaultNutritionFactor;
-
- //Remove reagent at set rate, satiate hunger if a HungerComponent can be found
- public override void Effect(EntityEffectBaseArgs args)
- {
- var entman = args.EntityManager;
- if (!entman.TryGetComponent(args.TargetEntity, out HungerComponent? hunger))
- return;
- if (args is EntityEffectReagentArgs reagentArgs)
- {
- entman.System<HungerSystem>().ModifyHunger(reagentArgs.TargetEntity, NutritionFactor * (float) reagentArgs.Quantity, hunger);
- }
- else
- {
- entman.System<HungerSystem>().ModifyHunger(args.TargetEntity, NutritionFactor, hunger);
- }
- }
-
- protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("reagent-effect-guidebook-satiate-hunger", ("chance", Probability), ("relative", NutritionFactor / DefaultNutritionFactor));
- }
-}
--- /dev/null
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Content.Server.Atmos.Components;
+using Content.Server.Atmos.EntitySystems;
+using Content.Server.Body.Components;
+using Content.Server.Body.Systems;
+using Content.Server.Botany.Components;
+using Content.Server.Botany.Systems;
+using Content.Server.Botany;
+using Content.Server.Chat.Systems;
+using Content.Server.Emp;
+using Content.Server.Explosion.EntitySystems;
+using Content.Server.Flash;
+using Content.Server.Fluids.EntitySystems;
+using Content.Server.Ghost.Roles.Components;
+using Content.Server.Medical;
+using Content.Server.Polymorph.Components;
+using Content.Server.Polymorph.Systems;
+using Content.Server.Speech.Components;
+using Content.Server.Spreader;
+using Content.Server.Temperature.Components;
+using Content.Server.Temperature.Systems;
+using Content.Server.Traits.Assorted;
+using Content.Server.Zombies;
+using Content.Shared.Atmos;
+using Content.Shared.Audio;
+using Content.Shared.Coordinates.Helpers;
+using Content.Shared.EntityEffects.EffectConditions;
+using Content.Shared.EntityEffects.Effects.PlantMetabolism;
+using Content.Shared.EntityEffects.Effects.StatusEffects;
+using Content.Shared.EntityEffects.Effects;
+using Content.Shared.EntityEffects;
+using Content.Shared.Maps;
+using Content.Shared.Mind.Components;
+using Content.Shared.Popups;
+using Content.Shared.Random;
+using Content.Shared.Zombies;
+using Robust.Server.GameObjects;
+using Robust.Shared.Audio;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Map;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+
+using TemperatureCondition = Content.Shared.EntityEffects.EffectConditions.Temperature; // disambiguate the namespace
+using PolymorphEffect = Content.Shared.EntityEffects.Effects.Polymorph;
+
+namespace Content.Server.EntityEffects;
+
+public sealed class EntityEffectSystem : EntitySystem
+{
+ [Dependency] private readonly AtmosphereSystem _atmosphere = default!;
+ [Dependency] private readonly BloodstreamSystem _bloodstream = default!;
+ [Dependency] private readonly ChatSystem _chat = default!;
+ [Dependency] private readonly EmpSystem _emp = default!;
+ [Dependency] private readonly ExplosionSystem _explosion = default!;
+ [Dependency] private readonly FlammableSystem _flammable = default!;
+ [Dependency] private readonly FlashSystem _flash = default!;
+ [Dependency] private readonly IMapManager _mapManager = default!;
+ [Dependency] private readonly IPrototypeManager _protoManager = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly SharedMapSystem _map = default!;
+ [Dependency] private readonly MutationSystem _mutation = default!;
+ [Dependency] private readonly NarcolepsySystem _narcolepsy = default!;
+ [Dependency] private readonly PlantHolderSystem _plantHolder = default!;
+ [Dependency] private readonly PolymorphSystem _polymorph = default!;
+ [Dependency] private readonly RespiratorSystem _respirator = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly SharedPointLightSystem _pointLight = default!;
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly SmokeSystem _smoke = default!;
+ [Dependency] private readonly SpreaderSystem _spreader = default!;
+ [Dependency] private readonly TemperatureSystem _temperature = default!;
+ [Dependency] private readonly SharedTransformSystem _xform = default!;
+ [Dependency] private readonly VomitSystem _vomit = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<CheckEntityEffectConditionEvent<TemperatureCondition>>(OnCheckTemperature);
+ SubscribeLocalEvent<CheckEntityEffectConditionEvent<Breathing>>(OnCheckBreathing);
+ SubscribeLocalEvent<CheckEntityEffectConditionEvent<OrganType>>(OnCheckOrganType);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustHealth>>(OnExecutePlantAdjustHealth);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustMutationLevel>>(OnExecutePlantAdjustMutationLevel);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustMutationMod>>(OnExecutePlantAdjustMutationMod);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustPests>>(OnExecutePlantAdjustPests);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustPotency>>(OnExecutePlantAdjustPotency);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustToxins>>(OnExecutePlantAdjustToxins);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustWater>>(OnExecutePlantAdjustWater);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustWeeds>>(OnExecutePlantAdjustWeeds);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAffectGrowth>>(OnExecutePlantAffectGrowth);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantChangeStat>>(OnExecutePlantChangeStat);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantCryoxadone>>(OnExecutePlantCryoxadone);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantDestroySeeds>>(OnExecutePlantDestroySeeds);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantDiethylamine>>(OnExecutePlantDiethylamine);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantPhalanximine>>(OnExecutePlantPhalanximine);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantRestoreSeeds>>(OnExecutePlantRestoreSeeds);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<AdjustTemperature>>(OnExecuteAdjustTemperature);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<AreaReactionEffect>>(OnExecuteAreaReactionEffect);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<CauseZombieInfection>>(OnExecuteCauseZombieInfection);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<ChemCleanBloodstream>>(OnExecuteChemCleanBloodstream);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<ChemVomit>>(OnExecuteChemVomit);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<CreateEntityReactionEffect>>(OnExecuteCreateEntityReactionEffect);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<CreateGas>>(OnExecuteCreateGas);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<CureZombieInfection>>(OnExecuteCureZombieInfection);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<Emote>>(OnExecuteEmote);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<EmpReactionEffect>>(OnExecuteEmpReactionEffect);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<ExplosionReactionEffect>>(OnExecuteExplosionReactionEffect);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<FlammableReaction>>(OnExecuteFlammableReaction);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<FlashReactionEffect>>(OnExecuteFlashReactionEffect);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<Ignite>>(OnExecuteIgnite);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<MakeSentient>>(OnExecuteMakeSentient);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<ModifyBleedAmount>>(OnExecuteModifyBleedAmount);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<ModifyBloodLevel>>(OnExecuteModifyBloodLevel);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<ModifyLungGas>>(OnExecuteModifyLungGas);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<Oxygenate>>(OnExecuteOxygenate);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantMutateChemicals>>(OnExecutePlantMutateChemicals);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantMutateConsumeGasses>>(OnExecutePlantMutateConsumeGasses);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantMutateExudeGasses>>(OnExecutePlantMutateExudeGasses);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantMutateHarvest>>(OnExecutePlantMutateHarvest);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantSpeciesChange>>(OnExecutePlantSpeciesChange);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<PolymorphEffect>>(OnExecutePolymorph);
+ SubscribeLocalEvent<ExecuteEntityEffectEvent<ResetNarcolepsy>>(OnExecuteResetNarcolepsy);
+ }
+
+ private void OnCheckTemperature(ref CheckEntityEffectConditionEvent<TemperatureCondition> args)
+ {
+ args.Result = false;
+ if (TryComp(args.Args.TargetEntity, out TemperatureComponent? temp))
+ {
+ if (temp.CurrentTemperature > args.Condition.Min && temp.CurrentTemperature < args.Condition.Max)
+ args.Result = true;
+ }
+ }
+
+ private void OnCheckBreathing(ref CheckEntityEffectConditionEvent<Breathing> args)
+ {
+ if (!TryComp(args.Args.TargetEntity, out RespiratorComponent? respiratorComp))
+ {
+ args.Result = !args.Condition.IsBreathing;
+ return;
+ }
+
+ var breathingState = _respirator.IsBreathing((args.Args.TargetEntity, respiratorComp));
+ args.Result = args.Condition.IsBreathing == breathingState;
+ }
+
+ private void OnCheckOrganType(ref CheckEntityEffectConditionEvent<OrganType> args)
+ {
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (reagentArgs.OrganEntity == null)
+ {
+ args.Result = false;
+ return;
+ }
+
+ args.Result = OrganCondition(args.Condition, reagentArgs.OrganEntity.Value);
+ return;
+ }
+
+ // TODO: Someone needs to figure out how to do this for non-reagent effects.
+ throw new NotImplementedException();
+ }
+
+ public bool OrganCondition(OrganType condition, Entity<MetabolizerComponent?> metabolizer)
+ {
+ metabolizer.Comp ??= EntityManager.GetComponentOrNull<MetabolizerComponent>(metabolizer.Owner);
+ if (metabolizer.Comp != null
+ && metabolizer.Comp.MetabolizerTypes != null
+ && metabolizer.Comp.MetabolizerTypes.Contains(condition.Type))
+ return condition.ShouldHave;
+ return !condition.ShouldHave;
+ }
+
+ /// <summary>
+ /// Checks if the plant holder can metabolize the reagent or not. Checks if it has an alive plant by default.
+ /// </summary>
+ /// <param name="plantHolder">The entity holding the plant</param>
+ /// <param name="plantHolderComponent">The plant holder component</param>
+ /// <param name="entityManager">The entity manager</param>
+ /// <param name="mustHaveAlivePlant">Whether to check if it has an alive plant or not</param>
+ /// <returns></returns>
+ private bool CanMetabolizePlant(EntityUid plantHolder, [NotNullWhen(true)] out PlantHolderComponent? plantHolderComponent,
+ bool mustHaveAlivePlant = true, bool mustHaveMutableSeed = false)
+ {
+ plantHolderComponent = null;
+
+ if (!TryComp(plantHolder, out plantHolderComponent))
+ return false;
+
+ if (mustHaveAlivePlant && (plantHolderComponent.Seed == null || plantHolderComponent.Dead))
+ return false;
+
+ if (mustHaveMutableSeed && (plantHolderComponent.Seed == null || plantHolderComponent.Seed.Immutable))
+ return false;
+
+ return true;
+ }
+
+ private void OnExecutePlantAdjustHealth(ref ExecuteEntityEffectEvent<PlantAdjustHealth> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
+ return;
+
+ plantHolderComp.Health += args.Effect.Amount;
+ _plantHolder.CheckHealth(args.Args.TargetEntity, plantHolderComp);
+ }
+
+ private void OnExecutePlantAdjustMutationLevel(ref ExecuteEntityEffectEvent<PlantAdjustMutationLevel> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
+ return;
+
+ plantHolderComp.MutationLevel += args.Effect.Amount * plantHolderComp.MutationMod;
+ }
+
+ private void OnExecutePlantAdjustMutationMod(ref ExecuteEntityEffectEvent<PlantAdjustMutationMod> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
+ return;
+
+ plantHolderComp.MutationMod += args.Effect.Amount;
+ }
+
+ private void OnExecutePlantAdjustNutrition(ref ExecuteEntityEffectEvent<PlantAdjustNutrition> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveAlivePlant: false))
+ return;
+
+ _plantHolder.AdjustNutrient(args.Args.TargetEntity, args.Effect.Amount, plantHolderComp);
+ }
+
+ private void OnExecutePlantAdjustPests(ref ExecuteEntityEffectEvent<PlantAdjustPests> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
+ return;
+
+ plantHolderComp.PestLevel += args.Effect.Amount;
+ }
+
+ private void OnExecutePlantAdjustPotency(ref ExecuteEntityEffectEvent<PlantAdjustPotency> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
+ return;
+
+ if (plantHolderComp.Seed == null)
+ return;
+
+ _plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp);
+ plantHolderComp.Seed.Potency = Math.Max(plantHolderComp.Seed.Potency + args.Effect.Amount, 1);
+ }
+
+ private void OnExecutePlantAdjustToxins(ref ExecuteEntityEffectEvent<PlantAdjustToxins> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
+ return;
+
+ plantHolderComp.Toxins += args.Effect.Amount;
+ }
+
+ private void OnExecutePlantAdjustWater(ref ExecuteEntityEffectEvent<PlantAdjustWater> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveAlivePlant: false))
+ return;
+
+ _plantHolder.AdjustWater(args.Args.TargetEntity, args.Effect.Amount, plantHolderComp);
+ }
+
+ private void OnExecutePlantAdjustWeeds(ref ExecuteEntityEffectEvent<PlantAdjustWeeds> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
+ return;
+
+ plantHolderComp.WeedLevel += args.Effect.Amount;
+ }
+
+ private void OnExecutePlantAffectGrowth(ref ExecuteEntityEffectEvent<PlantAffectGrowth> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
+ return;
+
+ _plantHolder.AffectGrowth(args.Args.TargetEntity, (int) args.Effect.Amount, plantHolderComp);
+ }
+
+ // Mutate reference 'val' between 'min' and 'max' by pretending the value
+ // is representable by a thermometer code with 'bits' number of bits and
+ // randomly flipping some of them.
+ private void MutateFloat(ref float val, float min, float max, int bits)
+ {
+ if (min == max)
+ {
+ val = min;
+ return;
+ }
+
+ // Starting number of bits that are high, between 0 and bits.
+ // In other words, it's val mapped linearly from range [min, max] to range [0, bits], and then rounded.
+ int valInt = (int)MathF.Round((val - min) / (max - min) * bits);
+ // val may be outside the range of min/max due to starting prototype values, so clamp.
+ valInt = Math.Clamp(valInt, 0, bits);
+
+ // Probability that the bit flip increases n.
+ // The higher the current value is, the lower the probability of increasing value is, and the higher the probability of decreasive it it.
+ // In other words, it tends to go to the middle.
+ float probIncrease = 1 - (float)valInt / bits;
+ int valIntMutated;
+ if (_random.Prob(probIncrease))
+ {
+ valIntMutated = valInt + 1;
+ }
+ else
+ {
+ valIntMutated = valInt - 1;
+ }
+
+ // Set value based on mutated thermometer code.
+ float valMutated = Math.Clamp((float)valIntMutated / bits * (max - min) + min, min, max);
+ val = valMutated;
+ }
+
+ private void MutateInt(ref int val, int min, int max, int bits)
+ {
+ if (min == max)
+ {
+ val = min;
+ return;
+ }
+
+ // Starting number of bits that are high, between 0 and bits.
+ // In other words, it's val mapped linearly from range [min, max] to range [0, bits], and then rounded.
+ int valInt = (int)MathF.Round((val - min) / (max - min) * bits);
+ // val may be outside the range of min/max due to starting prototype values, so clamp.
+ valInt = Math.Clamp(valInt, 0, bits);
+
+ // Probability that the bit flip increases n.
+ // The higher the current value is, the lower the probability of increasing value is, and the higher the probability of decreasing it.
+ // In other words, it tends to go to the middle.
+ float probIncrease = 1 - (float)valInt / bits;
+ int valMutated;
+ if (_random.Prob(probIncrease))
+ {
+ valMutated = val + 1;
+ }
+ else
+ {
+ valMutated = val - 1;
+ }
+
+ valMutated = Math.Clamp(valMutated, min, max);
+ val = valMutated;
+ }
+
+ private void OnExecutePlantChangeStat(ref ExecuteEntityEffectEvent<PlantChangeStat> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
+ return;
+
+ if (plantHolderComp.Seed == null)
+ return;
+
+ var member = plantHolderComp.Seed.GetType().GetField(args.Effect.TargetValue);
+
+ if (member == null)
+ {
+ _mutation.Log.Error(args.Effect.GetType().Name + " Error: Member " + args.Effect.TargetValue + " not found on " + plantHolderComp.Seed.GetType().Name + ". Did you misspell it?");
+ return;
+ }
+
+ var currentValObj = member.GetValue(plantHolderComp.Seed);
+ if (currentValObj == null)
+ return;
+
+ if (member.FieldType == typeof(float))
+ {
+ var floatVal = (float)currentValObj;
+ MutateFloat(ref floatVal, args.Effect.MinValue, args.Effect.MaxValue, args.Effect.Steps);
+ member.SetValue(plantHolderComp.Seed, floatVal);
+ }
+ else if (member.FieldType == typeof(int))
+ {
+ var intVal = (int)currentValObj;
+ MutateInt(ref intVal, (int)args.Effect.MinValue, (int)args.Effect.MaxValue, args.Effect.Steps);
+ member.SetValue(plantHolderComp.Seed, intVal);
+ }
+ else if (member.FieldType == typeof(bool))
+ {
+ var boolVal = (bool)currentValObj;
+ boolVal = !boolVal;
+ member.SetValue(plantHolderComp.Seed, boolVal);
+ }
+ }
+
+ private void OnExecutePlantCryoxadone(ref ExecuteEntityEffectEvent<PlantCryoxadone> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
+ return;
+
+ var deviation = 0;
+ var seed = plantHolderComp.Seed;
+ if (seed == null)
+ return;
+ 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.LastProduce = plantHolderComp.Age;
+ plantHolderComp.SkipAging++;
+ plantHolderComp.ForceUpdate = true;
+ }
+
+ private void OnExecutePlantDestroySeeds(ref ExecuteEntityEffectEvent<PlantDestroySeeds> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveMutableSeed: true))
+ return;
+
+ if (plantHolderComp.Seed!.Seedless == false)
+ {
+ _plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp);
+ _popup.PopupEntity(
+ Loc.GetString("botany-plant-seedsdestroyed"),
+ args.Args.TargetEntity,
+ PopupType.SmallCaution
+ );
+ plantHolderComp.Seed.Seedless = true;
+ }
+ }
+
+ private void OnExecutePlantDiethylamine(ref ExecuteEntityEffectEvent<PlantDiethylamine> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveMutableSeed: true))
+ return;
+
+ if (_random.Prob(0.1f))
+ {
+ _plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp);
+ plantHolderComp.Seed!.Lifespan++;
+ }
+
+ if (_random.Prob(0.1f))
+ {
+ _plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp);
+ plantHolderComp.Seed!.Endurance++;
+ }
+ }
+
+ private void OnExecutePlantPhalanximine(ref ExecuteEntityEffectEvent<PlantPhalanximine> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveMutableSeed: true))
+ return;
+
+ plantHolderComp.Seed!.Viable = true;
+ }
+
+ private void OnExecutePlantRestoreSeeds(ref ExecuteEntityEffectEvent<PlantRestoreSeeds> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveMutableSeed: true))
+ return;
+
+ if (plantHolderComp.Seed!.Seedless)
+ {
+ _plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp);
+ _popup.PopupEntity(Loc.GetString("botany-plant-seedsrestored"), args.Args.TargetEntity);
+ plantHolderComp.Seed.Seedless = false;
+ }
+ }
+
+ private void OnExecuteRobustHarvest(ref ExecuteEntityEffectEvent<RobustHarvest> args)
+ {
+ if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
+ return;
+
+ if (plantHolderComp.Seed == null)
+ return;
+
+ if (plantHolderComp.Seed.Potency < args.Effect.PotencyLimit)
+ {
+ _plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp);
+ plantHolderComp.Seed.Potency = Math.Min(plantHolderComp.Seed.Potency + args.Effect.PotencyIncrease, args.Effect.PotencyLimit);
+
+ if (plantHolderComp.Seed.Potency > args.Effect.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.Args.TargetEntity, plantHolderComp);
+ plantHolderComp.Seed.Yield--;
+ }
+ }
+
+ private void OnExecuteAdjustTemperature(ref ExecuteEntityEffectEvent<AdjustTemperature> args)
+ {
+ if (TryComp(args.Args.TargetEntity, out TemperatureComponent? temp))
+ {
+ var amount = args.Effect.Amount;
+
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ {
+ amount *= reagentArgs.Scale.Float();
+ }
+
+ _temperature.ChangeHeat(args.Args.TargetEntity, amount, true, temp);
+ }
+ }
+
+ private void OnExecuteAreaReactionEffect(ref ExecuteEntityEffectEvent<AreaReactionEffect> args)
+ {
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (reagentArgs.Source == null)
+ return;
+
+ var spreadAmount = (int) Math.Max(0, Math.Ceiling((reagentArgs.Quantity / args.Effect.OverflowThreshold).Float()));
+ var splitSolution = reagentArgs.Source.SplitSolution(reagentArgs.Source.Volume);
+ var transform = EntityManager.GetComponent<TransformComponent>(reagentArgs.TargetEntity);
+ var mapCoords = _xform.GetMapCoordinates(reagentArgs.TargetEntity, xform: transform);
+
+ if (!_mapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid) ||
+ !_map.TryGetTileRef(gridUid, grid, transform.Coordinates, out var tileRef))
+ {
+ return;
+ }
+
+ if (_spreader.RequiresFloorToSpread(args.Effect.PrototypeId) && tileRef.Tile.IsSpace())
+ return;
+
+ var coords = _map.MapToGrid(gridUid, mapCoords);
+ var ent = EntityManager.SpawnEntity(args.Effect.PrototypeId, coords.SnapToGrid());
+
+ _smoke.StartSmoke(ent, splitSolution, args.Effect.Duration, spreadAmount);
+
+ _audio.PlayPvs(args.Effect.Sound, reagentArgs.TargetEntity, AudioParams.Default.WithVariation(0.25f));
+ return;
+ }
+
+ // TODO: Someone needs to figure out how to do this for non-reagent effects.
+ throw new NotImplementedException();
+ }
+
+ private void OnExecuteCauseZombieInfection(ref ExecuteEntityEffectEvent<CauseZombieInfection> args)
+ {
+ EnsureComp<ZombifyOnDeathComponent>(args.Args.TargetEntity);
+ EnsureComp<PendingZombieComponent>(args.Args.TargetEntity);
+ }
+
+ private void OnExecuteChemCleanBloodstream(ref ExecuteEntityEffectEvent<ChemCleanBloodstream> args)
+ {
+ var cleanseRate = args.Effect.CleanseRate;
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (reagentArgs.Source == null || reagentArgs.Reagent == null)
+ return;
+
+ cleanseRate *= reagentArgs.Scale.Float();
+ _bloodstream.FlushChemicals(args.Args.TargetEntity, reagentArgs.Reagent.ID, cleanseRate);
+ }
+ else
+ {
+ _bloodstream.FlushChemicals(args.Args.TargetEntity, "", cleanseRate);
+ }
+ }
+
+ private void OnExecuteChemVomit(ref ExecuteEntityEffectEvent<ChemVomit> args)
+ {
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ if (reagentArgs.Scale != 1f)
+ return;
+
+ _vomit.Vomit(args.Args.TargetEntity, args.Effect.ThirstAmount, args.Effect.HungerAmount);
+ }
+
+ private void OnExecuteCreateEntityReactionEffect(ref ExecuteEntityEffectEvent<CreateEntityReactionEffect> args)
+ {
+ var transform = Comp<TransformComponent>(args.Args.TargetEntity);
+ var quantity = (int)args.Effect.Number;
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ quantity *= reagentArgs.Quantity.Int();
+
+ for (var i = 0; i < quantity; i++)
+ {
+ var uid = Spawn(args.Effect.Entity, _xform.GetMapCoordinates(args.Args.TargetEntity, xform: transform));
+ _xform.AttachToGridOrMap(uid);
+
+ // TODO figure out how to properly spawn inside of containers
+ // e.g. cheese:
+ // if the user is holding a bowl milk & enzyme, should drop to floor, not attached to the user.
+ // if reaction happens in a backpack, should insert cheese into backpack.
+ // --> if it doesn't fit, iterate through parent storage until it attaches to the grid (again, DON'T attach to players).
+ // if the reaction happens INSIDE a stomach? the bloodstream? I have no idea how to handle that.
+ // presumably having cheese materialize inside of your blood would have "disadvantages".
+ }
+ }
+
+ private void OnExecuteCreateGas(ref ExecuteEntityEffectEvent<CreateGas> args)
+ {
+ var tileMix = _atmosphere.GetContainingMixture(args.Args.TargetEntity, false, true);
+
+ if (tileMix != null)
+ {
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ {
+ tileMix.AdjustMoles(args.Effect.Gas, reagentArgs.Quantity.Float() * args.Effect.Multiplier);
+ }
+ else
+ {
+ tileMix.AdjustMoles(args.Effect.Gas, args.Effect.Multiplier);
+ }
+ }
+ }
+
+ private void OnExecuteCureZombieInfection(ref ExecuteEntityEffectEvent<CureZombieInfection> args)
+ {
+ if (HasComp<IncurableZombieComponent>(args.Args.TargetEntity))
+ return;
+
+ RemComp<ZombifyOnDeathComponent>(args.Args.TargetEntity);
+ RemComp<PendingZombieComponent>(args.Args.TargetEntity);
+
+ if (args.Effect.Innoculate)
+ {
+ EnsureComp<ZombieImmuneComponent>(args.Args.TargetEntity);
+ }
+ }
+
+ private void OnExecuteEmote(ref ExecuteEntityEffectEvent<Emote> args)
+ {
+ if (args.Effect.EmoteId == null)
+ return;
+
+ if (args.Effect.ShowInChat)
+ _chat.TryEmoteWithChat(args.Args.TargetEntity, args.Effect.EmoteId, ChatTransmitRange.GhostRangeLimit, forceEmote: args.Effect.Force);
+ else
+ _chat.TryEmoteWithoutChat(args.Args.TargetEntity, args.Effect.EmoteId);
+ }
+
+ private void OnExecuteEmpReactionEffect(ref ExecuteEntityEffectEvent<EmpReactionEffect> args)
+ {
+ var transform = EntityManager.GetComponent<TransformComponent>(args.Args.TargetEntity);
+
+ var range = args.Effect.EmpRangePerUnit;
+
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ {
+ range = MathF.Min((float) (reagentArgs.Quantity * args.Effect.EmpRangePerUnit), args.Effect.EmpMaxRange);
+ }
+
+ _emp.EmpPulse(_xform.GetMapCoordinates(args.Args.TargetEntity, xform: transform),
+ range,
+ args.Effect.EnergyConsumption,
+ args.Effect.DisableDuration);
+ }
+
+ private void OnExecuteExplosionReactionEffect(ref ExecuteEntityEffectEvent<ExplosionReactionEffect> args)
+ {
+ var intensity = args.Effect.IntensityPerUnit;
+
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ {
+ intensity = MathF.Min((float) reagentArgs.Quantity * args.Effect.IntensityPerUnit, args.Effect.MaxTotalIntensity);
+ }
+
+ _explosion.QueueExplosion(
+ args.Args.TargetEntity,
+ args.Effect.ExplosionType,
+ intensity,
+ args.Effect.IntensitySlope,
+ args.Effect.MaxIntensity,
+ args.Effect.TileBreakScale);
+ }
+
+ private void OnExecuteFlammableReaction(ref ExecuteEntityEffectEvent<FlammableReaction> args)
+ {
+ if (!TryComp(args.Args.TargetEntity, out FlammableComponent? flammable))
+ return;
+
+ // Sets the multiplier for FireStacks to MultiplierOnExisting is 0 or greater and target already has FireStacks
+ var multiplier = flammable.FireStacks != 0f && args.Effect.MultiplierOnExisting >= 0 ? args.Effect.MultiplierOnExisting : args.Effect.Multiplier;
+ var quantity = 1f;
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ {
+ quantity = reagentArgs.Quantity.Float();
+ _flammable.AdjustFireStacks(args.Args.TargetEntity, quantity * multiplier, flammable);
+ if (reagentArgs.Reagent != null)
+ reagentArgs.Source?.RemoveReagent(reagentArgs.Reagent.ID, reagentArgs.Quantity);
+ }
+ else
+ {
+ _flammable.AdjustFireStacks(args.Args.TargetEntity, multiplier, flammable);
+ }
+ }
+
+ private void OnExecuteFlashReactionEffect(ref ExecuteEntityEffectEvent<FlashReactionEffect> args)
+ {
+ var transform = EntityManager.GetComponent<TransformComponent>(args.Args.TargetEntity);
+
+ var range = 1f;
+
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ range = MathF.Min((float)(reagentArgs.Quantity * args.Effect.RangePerUnit), args.Effect.MaxRange);
+
+ _flash.FlashArea(
+ args.Args.TargetEntity,
+ null,
+ range,
+ args.Effect.Duration * 1000,
+ slowTo: args.Effect.SlowTo,
+ sound: args.Effect.Sound);
+
+ if (args.Effect.FlashEffectPrototype == null)
+ return;
+
+ var uid = EntityManager.SpawnEntity(args.Effect.FlashEffectPrototype, _xform.GetMapCoordinates(transform));
+ _xform.AttachToGridOrMap(uid);
+
+ if (!TryComp<PointLightComponent>(uid, out var pointLightComp))
+ return;
+
+ _pointLight.SetRadius(uid, MathF.Max(1.1f, range), pointLightComp);
+ }
+
+ private void OnExecuteIgnite(ref ExecuteEntityEffectEvent<Ignite> args)
+ {
+ if (!TryComp(args.Args.TargetEntity, out FlammableComponent? flammable))
+ return;
+
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ {
+ _flammable.Ignite(reagentArgs.TargetEntity, reagentArgs.OrganEntity ?? reagentArgs.TargetEntity, flammable: flammable);
+ }
+ else
+ {
+ _flammable.Ignite(args.Args.TargetEntity, args.Args.TargetEntity, flammable: flammable);
+ }
+ }
+
+ private void OnExecuteMakeSentient(ref ExecuteEntityEffectEvent<MakeSentient> args)
+ {
+ var uid = args.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
+ // We call this before the mind check to allow things like player-controlled mice to be able to benefit from the effect
+ RemComp<ReplacementAccentComponent>(uid);
+ RemComp<MonkeyAccentComponent>(uid);
+
+ // Stops from adding a ghost role to things like people who already have a mind
+ if (TryComp<MindContainerComponent>(uid, out var mindContainer) && mindContainer.HasMind)
+ {
+ return;
+ }
+
+ // Don't add a ghost role to things that already have ghost roles
+ if (TryComp(uid, out GhostRoleComponent? ghostRole))
+ {
+ return;
+ }
+
+ ghostRole = AddComp<GhostRoleComponent>(uid);
+ EnsureComp<GhostTakeoverAvailableComponent>(uid);
+
+ var entityData = EntityManager.GetComponent<MetaDataComponent>(uid);
+ ghostRole.RoleName = entityData.EntityName;
+ ghostRole.RoleDescription = Loc.GetString("ghost-role-information-cognizine-description");
+ }
+
+ private void OnExecuteModifyBleedAmount(ref ExecuteEntityEffectEvent<ModifyBleedAmount> args)
+ {
+ if (TryComp<BloodstreamComponent>(args.Args.TargetEntity, out var blood))
+ {
+ var amt = args.Effect.Amount;
+ if (args.Args is EntityEffectReagentArgs reagentArgs) {
+ if (args.Effect.Scaled)
+ amt *= reagentArgs.Quantity.Float();
+ amt *= reagentArgs.Scale.Float();
+ }
+
+ _bloodstream.TryModifyBleedAmount(args.Args.TargetEntity, amt, blood);
+ }
+ }
+
+ private void OnExecuteModifyBloodLevel(ref ExecuteEntityEffectEvent<ModifyBloodLevel> args)
+ {
+ if (TryComp<BloodstreamComponent>(args.Args.TargetEntity, out var blood))
+ {
+ var amt = args.Effect.Amount;
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (args.Effect.Scaled)
+ amt *= reagentArgs.Quantity;
+ amt *= reagentArgs.Scale;
+ }
+
+ _bloodstream.TryModifyBloodLevel(args.Args.TargetEntity, amt, blood);
+ }
+ }
+
+ private void OnExecuteModifyLungGas(ref ExecuteEntityEffectEvent<ModifyLungGas> args)
+ {
+ LungComponent? lung;
+ float amount = 1f;
+
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (!TryComp<LungComponent>(reagentArgs.OrganEntity, out var organLung))
+ return;
+ lung = organLung;
+ amount = reagentArgs.Quantity.Float();
+ }
+ else
+ {
+ if (!TryComp<LungComponent>(args.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 args.Effect.Ratios)
+ {
+ var quantity = ratio * amount / Atmospherics.BreathMolesToReagentMultiplier;
+ if (quantity < 0)
+ quantity = Math.Max(quantity, -lung.Air[(int) gas]);
+ lung.Air.AdjustMoles(gas, quantity);
+ }
+ }
+ }
+
+ private void OnExecuteOxygenate(ref ExecuteEntityEffectEvent<Oxygenate> args)
+ {
+ var multiplier = 1f;
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ {
+ multiplier = reagentArgs.Quantity.Float();
+ }
+
+ if (TryComp<RespiratorComponent>(args.Args.TargetEntity, out var resp))
+ {
+ _respirator.UpdateSaturation(args.Args.TargetEntity, multiplier * args.Effect.Factor, resp);
+ }
+ }
+
+ private void OnExecutePlantMutateChemicals(ref ExecuteEntityEffectEvent<PlantMutateChemicals> args)
+ {
+ var plantholder = EntityManager.GetComponent<PlantHolderComponent>(args.Args.TargetEntity);
+
+ if (plantholder.Seed == null)
+ return;
+
+ var chemicals = plantholder.Seed.Chemicals;
+ var randomChems = _protoManager.Index<WeightedRandomFillSolutionPrototype>("RandomPickBotanyReagent").Fills;
+
+ // Add a random amount of a random chemical to this set of chemicals
+ if (randomChems != null)
+ {
+ var pick = _random.Pick<RandomFillSolution>(randomChems);
+ var chemicalId = _random.Pick(pick.Reagents);
+ var amount = _random.Next(1, (int)pick.Quantity);
+ var seedChemQuantity = new SeedChemQuantity();
+ if (chemicals.ContainsKey(chemicalId))
+ {
+ seedChemQuantity.Min = chemicals[chemicalId].Min;
+ seedChemQuantity.Max = chemicals[chemicalId].Max + amount;
+ }
+ else
+ {
+ seedChemQuantity.Min = 1;
+ seedChemQuantity.Max = 1 + amount;
+ seedChemQuantity.Inherent = false;
+ }
+ var potencyDivisor = (int)Math.Ceiling(100.0f / seedChemQuantity.Max);
+ seedChemQuantity.PotencyDivisor = potencyDivisor;
+ chemicals[chemicalId] = seedChemQuantity;
+ }
+ }
+
+ private void OnExecutePlantMutateConsumeGasses(ref ExecuteEntityEffectEvent<PlantMutateConsumeGasses> args)
+ {
+ var plantholder = EntityManager.GetComponent<PlantHolderComponent>(args.Args.TargetEntity);
+
+ if (plantholder.Seed == null)
+ return;
+
+ var gasses = plantholder.Seed.ExudeGasses;
+
+ // Add a random amount of a random gas to this gas dictionary
+ float amount = _random.NextFloat(args.Effect.MinValue, args.Effect.MaxValue);
+ Gas gas = _random.Pick(Enum.GetValues(typeof(Gas)).Cast<Gas>().ToList());
+ if (gasses.ContainsKey(gas))
+ {
+ gasses[gas] += amount;
+ }
+ else
+ {
+ gasses.Add(gas, amount);
+ }
+ }
+
+ private void OnExecutePlantMutateExudeGasses(ref ExecuteEntityEffectEvent<PlantMutateExudeGasses> args)
+ {
+ var plantholder = EntityManager.GetComponent<PlantHolderComponent>(args.Args.TargetEntity);
+
+ if (plantholder.Seed == null)
+ return;
+
+ var gasses = plantholder.Seed.ConsumeGasses;
+
+ // Add a random amount of a random gas to this gas dictionary
+ float amount = _random.NextFloat(args.Effect.MinValue, args.Effect.MaxValue);
+ Gas gas = _random.Pick(Enum.GetValues(typeof(Gas)).Cast<Gas>().ToList());
+ if (gasses.ContainsKey(gas))
+ {
+ gasses[gas] += amount;
+ }
+ else
+ {
+ gasses.Add(gas, amount);
+ }
+ }
+
+ private void OnExecutePlantMutateHarvest(ref ExecuteEntityEffectEvent<PlantMutateHarvest> args)
+ {
+ var plantholder = EntityManager.GetComponent<PlantHolderComponent>(args.Args.TargetEntity);
+
+ if (plantholder.Seed == null)
+ return;
+
+ if (plantholder.Seed.HarvestRepeat == HarvestType.NoRepeat)
+ plantholder.Seed.HarvestRepeat = HarvestType.Repeat;
+ else if (plantholder.Seed.HarvestRepeat == HarvestType.Repeat)
+ plantholder.Seed.HarvestRepeat = HarvestType.SelfHarvest;
+ }
+
+ private void OnExecutePlantSpeciesChange(ref ExecuteEntityEffectEvent<PlantSpeciesChange> args)
+ {
+ var plantholder = EntityManager.GetComponent<PlantHolderComponent>(args.Args.TargetEntity);
+ if (plantholder.Seed == null)
+ return;
+
+ if (plantholder.Seed.MutationPrototypes.Count == 0)
+ return;
+
+ var targetProto = _random.Pick(plantholder.Seed.MutationPrototypes);
+ _protoManager.TryIndex(targetProto, out SeedPrototype? protoSeed);
+
+ if (protoSeed == null)
+ {
+ Log.Error($"Seed prototype could not be found: {targetProto}!");
+ return;
+ }
+
+ plantholder.Seed = plantholder.Seed.SpeciesChange(protoSeed);
+ }
+
+ private void OnExecutePolymorph(ref ExecuteEntityEffectEvent<PolymorphEffect> args)
+ {
+ // Make it into a prototype
+ EnsureComp<PolymorphableComponent>(args.Args.TargetEntity);
+ _polymorph.PolymorphEntity(args.Args.TargetEntity, args.Effect.PolymorphPrototype);
+ }
+
+ private void OnExecuteResetNarcolepsy(ref ExecuteEntityEffectEvent<ResetNarcolepsy> args)
+ {
+ if (args.Args is EntityEffectReagentArgs reagentArgs)
+ if (reagentArgs.Scale != 1f)
+ return;
+
+ _narcolepsy.AdjustNarcolepsyTimer(args.Args.TargetEntity, args.Effect.TimerReset);
+ }
+}
using Content.Server.Administration.Logs;
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
-using Content.Server.EntityEffects.Effects;
+using Content.Shared.EntityEffects.Effects;
using Content.Server.Spreader;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
-using Content.Server.EntityEffects.Effects;
+using Content.Shared.EntityEffects.Effects;
using Content.Server.Fluids.EntitySystems;
using Content.Server.Forensics;
using Content.Server.Inventory;
/// <summary>
/// Effects to be triggered when the reaction occurs.
/// </summary>
- [DataField("effects", serverOnly: true)] public List<EntityEffect> Effects = new();
+ [DataField("effects")] public List<EntityEffect> Effects = new();
/// <summary>
/// How dangerous is this effect? Stuff like bicaridine should be low, while things like methamphetamine
[DataField]
public bool WorksOnTheDead;
- [DataField(serverOnly: true)]
+ [DataField]
public FrozenDictionary<ProtoId<MetabolismGroupPrototype>, ReagentEffectsEntry>? Metabolisms;
- [DataField(serverOnly: true)]
+ [DataField]
public Dictionary<ProtoId<ReactiveGroupPrototype>, ReactiveReagentEffectEntry>? ReactiveEffects;
[DataField(serverOnly: true)]
public List<ITileReaction> TileReactions = new(0);
- [DataField("plantMetabolism", serverOnly: true)]
+ [DataField("plantMetabolism")]
public List<EntityEffect> PlantMetabolisms = new(0);
[DataField]
-using Content.Server.Temperature.Components;
-using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.EffectConditions;
+namespace Content.Shared.EntityEffects.EffectConditions;
/// <summary>
/// Requires the target entity to be above or below a certain temperature.
/// Used for things like cryoxadone and pyroxadone.
/// </summary>
-public sealed partial class Temperature : EntityEffectCondition
+public sealed partial class Temperature : EventEntityEffectCondition<Temperature>
{
[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)
{
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.EffectConditions;
+
+/// <summary>
+/// Condition for if the entity is successfully breathing.
+/// </summary>
+public sealed partial class Breathing : EventEntityEffectCondition<Breathing>
+{
+ /// <summary>
+ /// If true, the entity must not have trouble breathing to pass.
+ /// </summary>
+ [DataField]
+ public bool IsBreathing = true;
+
+ public override string GuidebookExplanation(IPrototypeManager prototype)
+ {
+ return Loc.GetString("reagent-effect-condition-guidebook-breathing",
+ ("isBreathing", IsBreathing));
+ }
+}
-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.EntityEffects.EffectConditions;
+namespace Content.Shared.EntityEffects.EffectConditions;
-[UsedImplicitly]
public sealed partial class HasTag : EntityEffectCondition
{
[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<TagPrototype>))]
using System.Linq;
-using Content.Shared.EntityEffects;
using Content.Shared.Localizations;
using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Content.Shared.Roles.Jobs;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.EffectConditions;
+namespace Content.Shared.EntityEffects.EffectConditions;
public sealed partial class JobCondition : EntityEffectCondition
{
-using Content.Shared.EntityEffects;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.EffectConditions;
+namespace Content.Shared.EntityEffects.EffectConditions;
public sealed partial class MobStateCondition : EntityEffectCondition
{
--- /dev/null
+// using Content.Server.Body.Components;
+using Content.Shared.Body.Prototypes;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Shared.EntityEffects.EffectConditions;
+
+/// <summary>
+/// Requires that the metabolizing organ is or is not tagged with a certain MetabolizerType
+/// </summary>
+public sealed partial class OrganType : EventEntityEffectCondition<OrganType>
+{
+ [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<MetabolizerTypePrototype>))]
+ public string Type = default!;
+
+ /// <summary>
+ /// Does this condition pass when the organ has the type, or when it doesn't have the type?
+ /// </summary>
+ [DataField]
+ public bool ShouldHave = true;
+
+ public override string GuidebookExplanation(IPrototypeManager prototype)
+ {
+ return Loc.GetString("reagent-effect-condition-guidebook-organ-type",
+ ("name", prototype.Index<MetabolizerTypePrototype>(Type).LocalizedName),
+ ("shouldhave", ShouldHave));
+ }
+}
using Content.Shared.Chemistry.Reagent;
-using Content.Shared.EntityEffects;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.EffectConditions;
+namespace Content.Shared.EntityEffects.EffectConditions;
/// <summary>
/// Used for implementing reagent effects that require a certain amount of reagent before it should be applied.
-using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.EffectConditions;
+namespace Content.Shared.EntityEffects.EffectConditions;
/// <summary>
/// Requires the solution to be above or below a certain temperature.
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.EffectConditions;
+namespace Content.Shared.EntityEffects.EffectConditions;
public sealed partial class TotalDamage : EntityEffectCondition
{
using Content.Shared.Nutrition.EntitySystems;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.EffectConditions;
+namespace Content.Shared.EntityEffects.EffectConditions;
public sealed partial class Hunger : EntityEffectCondition
{
using Content.Shared.Chemistry.EntitySystems;
-using Content.Shared.EntityEffects;
-using JetBrains.Annotations;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.Effects
+namespace Content.Shared.EntityEffects.Effects
{
- [UsedImplicitly]
public sealed partial class AddToSolutionReaction : EntityEffect
{
[DataField("solution")]
using Content.Shared.Alert;
-using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
-namespace Content.Server.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects;
public sealed partial class AdjustAlert : EntityEffect
{
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.EntityEffects.Effects
+namespace Content.Shared.EntityEffects.Effects
{
- [UsedImplicitly]
public sealed partial class AdjustReagent : EntityEffect
{
/// <summary>
}
}
}
+
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+public sealed partial class AdjustTemperature : EventEntityEffect<AdjustTemperature>
+{
+ [DataField]
+ public float Amount;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-adjust-temperature",
+ ("chance", Probability),
+ ("deltasign", MathF.Sign(Amount)),
+ ("amount", MathF.Abs(Amount)));
+}
--- /dev/null
+using Content.Shared.Database;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+/// <summary>
+/// Basically smoke and foam reactions.
+/// </summary>
+public sealed partial class AreaReactionEffect : EventEntityEffect<AreaReactionEffect>
+{
+ /// <summary>
+ /// How many seconds will the effect stay, counting after fully spreading.
+ /// </summary>
+ [DataField("duration")] public float Duration = 10;
+
+ /// <summary>
+ /// How many units of reaction for 1 smoke entity.
+ /// </summary>
+ [DataField] public FixedPoint2 OverflowThreshold = FixedPoint2.New(2.5);
+
+ /// <summary>
+ /// The entity prototype that will be spawned as the effect.
+ /// </summary>
+ [DataField("prototypeId", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
+ public string PrototypeId = default!;
+
+ /// <summary>
+ /// Sound that will get played when this reaction effect occurs.
+ /// </summary>
+ [DataField("sound", required: true)] public 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;
+}
-using Content.Server.Popups;
-using Content.Server.Xenoarchaeology.Artifact;
+using Content.Shared.Xenoarchaeology.Artifact;
using Content.Shared.EntityEffects;
using Content.Shared.Popups;
using Content.Shared.Xenoarchaeology.Artifact.Components;
-using JetBrains.Annotations;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects;
/// <summary>
/// Sets an artifact into the unlocking state and marks the artifexium effect as true.
/// This is a very specific behavior intended for a specific chem.
/// </summary>
-[UsedImplicitly]
public sealed partial class ArtifactUnlock : EntityEffect
{
public override void Effect(EntityEffectBaseArgs args)
{
var entMan = args.EntityManager;
- var xenoArtifactSys = entMan.System<XenoArtifactSystem>();
- var popupSys = entMan.System<PopupSystem>();
+ var xenoArtifactSys = entMan.System<SharedXenoArtifactSystem>();
+ var popupSys = entMan.System<SharedPopupSystem>();
if (!entMan.TryGetComponent<XenoArtifactComponent>(args.TargetEntity, out var xenoArtifact))
return;
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+public sealed partial class CauseZombieInfection : EventEntityEffect<CauseZombieInfection>
+{
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-cause-zombie-infection", ("chance", Probability));
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+/// <summary>
+/// Basically smoke and foam reactions.
+/// </summary>
+public sealed partial class ChemCleanBloodstream : EventEntityEffect<ChemCleanBloodstream>
+{
+ [DataField]
+ public float CleanseRate = 3.0f;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-chem-clean-bloodstream", ("chance", Probability));
+}
-using Content.Shared.EntityEffects;
using Content.Shared.Eye.Blinding.Systems;
-using JetBrains.Annotations;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects;
/// <summary>
/// Heal or apply eye damage
/// </summary>
-[UsedImplicitly]
public sealed partial class ChemHealEyeDamage : EntityEffect
{
/// <summary>
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+/// <summary>
+/// Forces you to vomit.
+/// </summary>
+public sealed partial class ChemVomit : EventEntityEffect<ChemVomit>
+{
+ /// How many units of thirst to add each time we vomit
+ [DataField]
+ public float ThirstAmount = -8f;
+ /// How many units of hunger to add each time we vomit
+ [DataField]
+ public float HungerAmount = -8f;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-chem-vomit", ("chance", Probability));
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+[DataDefinition]
+public sealed partial class CreateEntityReactionEffect : EventEntityEffect<CreateEntityReactionEffect>
+{
+ /// <summary>
+ /// What entity to create.
+ /// </summary>
+ [DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
+ public string Entity = default!;
+
+ /// <summary>
+ /// How many entities to create per unit reaction.
+ /// </summary>
+ [DataField]
+ public uint Number = 1;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-create-entity-reaction-effect",
+ ("chance", Probability),
+ ("entname", IoCManager.Resolve<IPrototypeManager>().Index<EntityPrototype>(Entity).Name),
+ ("amount", Number));
+}
--- /dev/null
+using Content.Shared.Atmos;
+using Content.Shared.Atmos.EntitySystems;
+using Content.Shared.Database;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+public sealed partial class CreateGas : EventEntityEffect<CreateGas>
+{
+ [DataField(required: true)]
+ public Gas Gas = default!;
+
+ /// <summary>
+ /// For each unit consumed, how many moles of gas should be created?
+ /// </summary>
+ [DataField]
+ public float Multiplier = 3f;
+
+ public override bool ShouldLog => true;
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ {
+ var atmos = entSys.GetEntitySystem<SharedAtmosphereSystem>();
+ var gasProto = atmos.GetGas(Gas);
+
+ return Loc.GetString("reagent-effect-guidebook-create-gas",
+ ("chance", Probability),
+ ("moles", Multiplier),
+ ("gas", gasProto.Name));
+ }
+
+ public override LogImpact LogImpact => LogImpact.High;
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+public sealed partial class CureZombieInfection : EventEntityEffect<CureZombieInfection>
+{
+ [DataField]
+ public bool Innoculate;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ {
+ if(Innoculate)
+ return Loc.GetString("reagent-effect-guidebook-innoculate-zombie-infection", ("chance", Probability));
+
+ return Loc.GetString("reagent-effect-guidebook-cure-zombie-infection", ("chance", Probability));
+ }
+}
+
using Content.Shared.Drunk;
-using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects;
public sealed partial class Drunk : EntityEffect
{
if (args is EntityEffectReagentArgs reagentArgs) {
boozePower *= reagentArgs.Scale.Float();
}
-
+
var drunkSys = args.EntityManager.EntitySysManager.GetEntitySystem<SharedDrunkSystem>();
drunkSys.TryApplyDrunkenness(args.TargetEntity, boozePower, SlurSpeech);
}
-using Content.Server.Electrocution;
-using Content.Shared.EntityEffects;
+using Content.Shared.Electrocution;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects;
public sealed partial class Electrocute : EntityEffect
{
{
if (args is EntityEffectReagentArgs reagentArgs)
{
- reagentArgs.EntityManager.System<ElectrocutionSystem>().TryDoElectrocution(reagentArgs.TargetEntity, null,
+ reagentArgs.EntityManager.System<SharedElectrocutionSystem>().TryDoElectrocution(reagentArgs.TargetEntity, null,
Math.Max((reagentArgs.Quantity * ElectrocuteDamageScale).Int(), 1), TimeSpan.FromSeconds(ElectrocuteTime), Refresh, ignoreInsulation: true);
if (reagentArgs.Reagent != null)
reagentArgs.Source?.RemoveReagent(reagentArgs.Reagent.ID, reagentArgs.Quantity);
} else
{
- args.EntityManager.System<ElectrocutionSystem>().TryDoElectrocution(args.TargetEntity, null,
+ args.EntityManager.System<SharedElectrocutionSystem>().TryDoElectrocution(args.TargetEntity, null,
Math.Max(ElectrocuteDamageScale, 1), TimeSpan.FromSeconds(ElectrocuteTime), Refresh, ignoreInsulation: true);
}
}
-using Content.Server.Chat.Systems;
using Content.Shared.Chat.Prototypes;
-using Content.Shared.EntityEffects;
-using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Server.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects;
/// <summary>
/// Tries to force someone to emote (scream, laugh, etc). Still respects whitelists/blacklists and other limits unless specially forced.
/// </summary>
-[UsedImplicitly]
-public sealed partial class Emote : EntityEffect
+public sealed partial class Emote : EventEntityEffect<Emote>
{
/// <summary>
/// The emote the entity will preform.
return Loc.GetString("reagent-effect-guidebook-emote", ("chance", Probability), ("emote", EmoteId));
}
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- var chatSys = args.EntityManager.System<ChatSystem>();
- if (ShowInChat)
- chatSys.TryEmoteWithChat(args.TargetEntity, EmoteId, ChatTransmitRange.GhostRangeLimit, forceEmote: Force);
- else
- chatSys.TryEmoteWithoutChat(args.TargetEntity, EmoteId);
- }
}
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+[DataDefinition]
+public sealed partial class EmpReactionEffect : EventEntityEffect<EmpReactionEffect>
+{
+ /// <summary>
+ /// Impulse range per unit of quantity
+ /// </summary>
+ [DataField("rangePerUnit")]
+ public float EmpRangePerUnit = 0.5f;
+
+ /// <summary>
+ /// Maximum impulse range
+ /// </summary>
+ [DataField("maxRange")]
+ public float EmpMaxRange = 10;
+
+ /// <summary>
+ /// How much energy will be drain from sources
+ /// </summary>
+ [DataField]
+ public float EnergyConsumption = 12500;
+
+ /// <summary>
+ /// Amount of time entities will be disabled
+ /// </summary>
+ [DataField("duration")]
+ public float DisableDuration = 15;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-emp-reaction-effect", ("chance", Probability));
+}
using Content.Shared.EntityEffects;
using Content.Shared.FixedPoint;
using Content.Shared.Localizations;
-using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
-namespace Content.Server.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects;
/// <summary>
/// Version of <see cref="HealthChange"/> that distributes the healing to groups
/// </summary>
-[UsedImplicitly]
public sealed partial class EvenHealthChange : EntityEffect
{
/// <summary>
-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;
+namespace Content.Shared.EntityEffects.Effects;
[DataDefinition]
-public sealed partial class ExplosionReactionEffect : EntityEffect
+public sealed partial class ExplosionReactionEffect : EventEntityEffect<ExplosionReactionEffect>
{
/// <summary>
/// The type of explosion. Determines damage types and tile break chance scaling.
/// </summary>
[DataField]
public float IntensityPerUnit = 1;
-
+
/// <summary>
/// Factor used to scale the explosion intensity when calculating tile break chances. Allows for stronger
/// explosives that don't space tiles, without having to create a new explosion-type prototype.
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-explosion-reaction-effect", ("chance", Probability));
public override LogImpact LogImpact => LogImpact.High;
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- var intensity = IntensityPerUnit;
-
- if (args is EntityEffectReagentArgs reagentArgs)
- {
- intensity = MathF.Min((float) reagentArgs.Quantity * IntensityPerUnit, MaxTotalIntensity);
- }
-
- args.EntityManager.System<ExplosionSystem>()
- .QueueExplosion(
- args.TargetEntity,
- ExplosionType,
- intensity,
- IntensitySlope,
- MaxIntensity,
- TileBreakScale);
- }
}
using Content.Shared.Atmos;
-using Content.Shared.EntityEffects;
-using JetBrains.Annotations;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.Effects
+namespace Content.Shared.EntityEffects.Effects
{
- [UsedImplicitly]
public sealed partial class ExtinguishReaction : EntityEffect
{
/// <summary>
--- /dev/null
+using Content.Shared.Database;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+public sealed partial class FlammableReaction : EventEntityEffect<FlammableReaction>
+{
+ [DataField]
+ public float Multiplier = 0.05f;
+
+ // The fire stack multiplier if fire stacks already exist on target, only works if 0 or greater
+ [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;
+}
--- /dev/null
+using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+[DataDefinition]
+public sealed partial class FlashReactionEffect : EventEntityEffect<FlashReactionEffect>
+{
+ /// <summary>
+ /// Flash range per unit of reagent.
+ /// </summary>
+ [DataField]
+ public float RangePerUnit = 0.2f;
+
+ /// <summary>
+ /// Maximum flash range.
+ /// </summary>
+ [DataField]
+ public float MaxRange = 10f;
+
+ /// <summary>
+ /// How much to entities are slowed down.
+ /// </summary>
+ [DataField]
+ public float SlowTo = 0.5f;
+
+ /// <summary>
+ /// The time entities will be flashed in seconds.
+ /// The default is chosen to be better than the hand flash so it is worth using it for grenades etc.
+ /// </summary>
+ [DataField]
+ public float Duration = 4f;
+
+ /// <summary>
+ /// The prototype ID used for the visual effect.
+ /// </summary>
+ [DataField]
+ public EntProtoId? FlashEffectPrototype = "ReactionFlash";
+
+ /// <summary>
+ /// The sound the flash creates.
+ /// </summary>
+ [DataField]
+ public SoundSpecifier? Sound = new SoundPathSpecifier("/Audio/Weapons/flash.ogg");
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-flash-reaction-effect", ("chance", Probability));
+}
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
-namespace Content.Server.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects;
/// <summary>
/// Makes a mob glow.
using Content.Shared.EntityEffects;
using Content.Shared.FixedPoint;
using Content.Shared.Localizations;
-using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using System.Linq;
using System.Text.Json.Serialization;
-namespace Content.Server.EntityEffects.Effects
+namespace Content.Shared.EntityEffects.Effects
{
/// <summary>
/// Default metabolism used for medicine reagents.
/// </summary>
- [UsedImplicitly]
public sealed partial class HealthChange : EntityEffect
{
/// <summary>
--- /dev/null
+using Content.Shared.Database;
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+/// <summary>
+/// Ignites a mob.
+/// </summary>
+public sealed partial class Ignite : EventEntityEffect<Ignite>
+{
+ public override bool ShouldLog => true;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-ignite", ("chance", Probability));
+
+ public override LogImpact LogImpact => LogImpact.Medium;
+}
--- /dev/null
+using Content.Shared.Mind.Components;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+public sealed partial class MakeSentient : EventEntityEffect<MakeSentient>
+{
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-make-sentient", ("chance", Probability));
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+public sealed partial class ModifyBleedAmount : EventEntityEffect<ModifyBleedAmount>
+{
+ [DataField]
+ public bool Scaled = false;
+
+ [DataField]
+ public float Amount = -1.0f;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-modify-bleed-amount", ("chance", Probability),
+ ("deltasign", MathF.Sign(Amount)));
+}
--- /dev/null
+using Content.Shared.FixedPoint;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+public sealed partial class ModifyBloodLevel : EventEntityEffect<ModifyBloodLevel>
+{
+ [DataField]
+ public bool Scaled = false;
+
+ [DataField]
+ public FixedPoint2 Amount = 1.0f;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-modify-blood-level", ("chance", Probability),
+ ("deltasign", MathF.Sign(Amount.Float())));
+}
--- /dev/null
+using Content.Shared.Atmos;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+public sealed partial class ModifyLungGas : EventEntityEffect<ModifyLungGas>
+{
+ [DataField("ratios", required: true)]
+ public Dictionary<Gas, float> Ratios = default!;
+
+ // JUSTIFICATION: This is internal magic that players never directly interact with.
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => null;
+}
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;
+namespace Content.Shared.EntityEffects.Effects;
/// <summary>
/// Default metabolism for stimulants and tranqs. Attempts to find a MovementSpeedModifier on the target,
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+public sealed partial class Oxygenate : EventEntityEffect<Oxygenate>
+{
+ [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;
+}
-using Content.Server.Stunnable;
using Content.Shared.EntityEffects;
+using Content.Shared.Stunnable;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects;
public sealed partial class Paralyze : EntityEffect
{
paralyzeTime *= (double)reagentArgs.Scale;
}
- args.EntityManager.System<StunSystem>().TryParalyze(args.TargetEntity, TimeSpan.FromSeconds(paralyzeTime), Refresh);
+ args.EntityManager.System<SharedStunSystem>().TryParalyze(args.TargetEntity, TimeSpan.FromSeconds(paralyzeTime), Refresh);
}
}
-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;
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
[ImplicitDataDefinitionForInheritors]
-public abstract partial class PlantAdjustAttribute : EntityEffect
+public abstract partial class PlantAdjustAttribute<T> : EventEntityEffect<T> where T : PlantAdjustAttribute<T>
{
[DataField]
public float Amount { get; protected set; } = 1;
[DataField]
public virtual bool GuidebookIsAttributePositive { get; protected set; } = true;
- /// <summary>
- /// Checks if the plant holder can metabolize the reagent or not. Checks if it has an alive plant by default.
- /// </summary>
- /// <param name="plantHolder">The entity holding the plant</param>
- /// <param name="plantHolderComponent">The plant holder component</param>
- /// <param name="entityManager">The entity manager</param>
- /// <param name="mustHaveAlivePlant">Whether to check if it has an alive plant or not</param>
- /// <returns></returns>
- public bool CanMetabolize(EntityUid plantHolder, [NotNullWhen(true)] out PlantHolderComponent? plantHolderComponent,
- IEntityManager entityManager,
- bool mustHaveAlivePlant = true)
- {
- plantHolderComponent = null;
-
- if (!entityManager.TryGetComponent(plantHolder, out plantHolderComponent)
- || mustHaveAlivePlant && (plantHolderComponent.Seed == null || plantHolderComponent.Dead))
- return false;
-
- return true;
- }
-
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
{
string color;
--- /dev/null
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantAdjustHealth : PlantAdjustAttribute<PlantAdjustHealth>
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-health";
+}
+
--- /dev/null
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantAdjustMutationLevel : PlantAdjustAttribute<PlantAdjustMutationLevel>
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-mutation-level";
+}
--- /dev/null
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantAdjustMutationMod : PlantAdjustAttribute<PlantAdjustMutationMod>
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-mutation-mod";
+}
--- /dev/null
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantAdjustNutrition : PlantAdjustAttribute<PlantAdjustNutrition>
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-nutrition";
+}
--- /dev/null
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantAdjustPests : PlantAdjustAttribute<PlantAdjustPests>
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-pests";
+ public override bool GuidebookIsAttributePositive { get; protected set; } = false;
+}
--- /dev/null
+// using Content.Server.Botany.Systems;
+
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+/// <summary>
+/// Handles increase or decrease of plant potency.
+/// </summary>
+
+public sealed partial class PlantAdjustPotency : PlantAdjustAttribute<PlantAdjustPotency>
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-potency";
+}
--- /dev/null
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantAdjustToxins : PlantAdjustAttribute<PlantAdjustToxins>
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-toxins";
+ public override bool GuidebookIsAttributePositive { get; protected set; } = false;
+}
+
--- /dev/null
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantAdjustWater : PlantAdjustAttribute<PlantAdjustWater>
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-water";
+}
+
--- /dev/null
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantAdjustWeeds : PlantAdjustAttribute<PlantAdjustWeeds>
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-weeds";
+ public override bool GuidebookIsAttributePositive { get; protected set; } = false;
+}
--- /dev/null
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantAffectGrowth : PlantAdjustAttribute<PlantAffectGrowth>
+{
+ public override string GuidebookAttributeName { get; set; } = "plant-attribute-growth";
+}
+
--- /dev/null
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantChangeStat : EventEntityEffect<PlantChangeStat>
+{
+ [DataField]
+ public string TargetValue;
+
+ [DataField]
+ public float MinValue;
+
+ [DataField]
+ public float MaxValue;
+
+ [DataField]
+ public int Steps;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ {
+ throw new NotImplementedException();
+ }
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantCryoxadone : EventEntityEffect<PlantCryoxadone>
+{
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-cryoxadone", ("chance", Probability));
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+/// <summary>
+/// Handles removal of seeds on a plant.
+/// </summary>
+
+public sealed partial class PlantDestroySeeds : EventEntityEffect<PlantDestroySeeds>
+{
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) =>
+ Loc.GetString("reagent-effect-guidebook-plant-seeds-remove", ("chance", Probability));
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantDiethylamine : EventEntityEffect<PlantDiethylamine>
+{
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-diethylamine", ("chance", Probability));
+}
+
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class PlantPhalanximine : EventEntityEffect<PlantPhalanximine>
+{
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-phalanximine", ("chance", Probability));
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+/// <summary>
+/// Handles restoral of seeds on a plant.
+/// </summary>
+public sealed partial class PlantRestoreSeeds : EventEntityEffect<PlantRestoreSeeds>
+{
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) =>
+ Loc.GetString("reagent-effect-guidebook-plant-seeds-add", ("chance", Probability));
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+
+namespace Content.Shared.EntityEffects.Effects.PlantMetabolism;
+
+public sealed partial class RobustHarvest : EventEntityEffect<RobustHarvest>
+{
+ [DataField]
+ public int PotencyLimit = 50;
+
+ [DataField]
+ public int PotencyIncrease = 3;
+
+ [DataField]
+ public int PotencySeedlessThreshold = 30;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-robust-harvest", ("seedlesstreshold", PotencySeedlessThreshold), ("limit", PotencyLimit), ("increase", PotencyIncrease), ("chance", Probability));
+}
--- /dev/null
+using Content.Shared.Random;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+/// <summary>
+/// changes the chemicals available in a plant's produce
+/// </summary>
+public sealed partial class PlantMutateChemicals : EventEntityEffect<PlantMutateChemicals>
+{
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ {
+ return "TODO";
+ }
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+using System.Linq;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+/// <summary>
+/// changes the gases that a plant or produce create.
+/// </summary>
+public sealed partial class PlantMutateExudeGasses : EventEntityEffect<PlantMutateExudeGasses>
+{
+ [DataField]
+ public float MinValue = 0.01f;
+
+ [DataField]
+ public float MaxValue = 0.5f;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ {
+ return "TODO";
+ }
+}
+
+/// <summary>
+/// changes the gases that a plant or produce consumes.
+/// </summary>
+public sealed partial class PlantMutateConsumeGasses : EventEntityEffect<PlantMutateConsumeGasses>
+{
+ [DataField]
+ public float MinValue = 0.01f;
+
+ [DataField]
+ public float MaxValue = 0.5f;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ {
+ return "TODO";
+ }
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+/// <summary>
+/// Upgrades a plant's harvest type.
+/// </summary>
+public sealed partial class PlantMutateHarvest : EventEntityEffect<PlantMutateHarvest>
+{
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ {
+ return "TODO";
+ }
+}
--- /dev/null
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+/// <summary>
+/// Changes a plant into one of the species its able to mutate into.
+/// </summary>
+public sealed partial class PlantSpeciesChange : EventEntityEffect<PlantSpeciesChange>
+{
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ {
+ return "TODO";
+ }
+}
-using Content.Server.Polymorph.Components;
-using Content.Server.Polymorph.Systems;
-using Content.Shared.EntityEffects;
using Content.Shared.Polymorph;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Server.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects;
-public sealed partial class Polymorph : EntityEffect
+public sealed partial class Polymorph : EventEntityEffect<Polymorph>
{
/// <summary>
/// What polymorph prototype is used on effect
=> Loc.GetString("reagent-effect-guidebook-make-polymorph",
("chance", Probability), ("entityname",
prototype.Index<EntityPrototype>(prototype.Index<PolymorphPrototype>(PolymorphPrototype).Configuration.Entity).Name));
-
- public override void Effect(EntityEffectBaseArgs args)
- {
- var entityManager = args.EntityManager;
- var uid = args.TargetEntity;
- var polySystem = entityManager.System<PolymorphSystem>();
-
- // Make it into a prototype
- entityManager.EnsureComponent<PolymorphableComponent>(uid);
- polySystem.PolymorphEntity(uid, PolymorphPrototype);
- }
}
-using Content.Shared.EntityEffects;
using Content.Shared.Popups;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
-namespace Content.Server.EntityEffects.Effects
+namespace Content.Shared.EntityEffects.Effects
{
public sealed partial class PopupMessage : EntityEffect
{
--- /dev/null
+using Content.Shared.Chemistry.Reagent;
+using Robust.Shared.Prototypes;
+using Content.Shared.Atmos.Rotting;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+/// <summary>
+/// Reduces the rotting accumulator on the patient, making them revivable.
+/// </summary>
+public sealed partial class ReduceRotting : EntityEffect
+{
+ [DataField("seconds")]
+ public double RottingAmount = 10;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-reduce-rotting",
+ ("chance", Probability),
+ ("time", RottingAmount));
+
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ if (reagentArgs.Scale != 1f)
+ return;
+ }
+
+ var rottingSys = args.EntityManager.EntitySysManager.GetEntitySystem<SharedRottingSystem>();
+
+ rottingSys.ReduceAccumulator(args.TargetEntity, TimeSpan.FromSeconds(RottingAmount));
+ }
+}
--- /dev/null
+using Content.Shared.Chemistry.Reagent;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+/// <summary>
+/// Reset narcolepsy timer
+/// </summary>
+public sealed partial class ResetNarcolepsy : EventEntityEffect<ResetNarcolepsy>
+{
+ /// <summary>
+ /// The # of seconds the effect resets the narcolepsy timer to
+ /// </summary>
+ [DataField("TimerReset")]
+ public int TimerReset = 600;
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-reset-narcolepsy", ("chance", Probability));
+}
--- /dev/null
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Nutrition.Components;
+using Content.Shared.Nutrition.EntitySystems;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+/// <summary>
+/// Attempts to find a HungerComponent on the target,
+/// and to update it's hunger values.
+/// </summary>
+public sealed partial class SatiateHunger : EntityEffect
+{
+ private const float DefaultNutritionFactor = 3.0f;
+
+ /// <summary>
+ /// How much hunger is satiated.
+ /// Is multiplied by quantity if used with EntityEffectReagentArgs.
+ /// </summary>
+ [DataField("factor")] public float NutritionFactor { get; set; } = DefaultNutritionFactor;
+
+ //Remove reagent at set rate, satiate hunger if a HungerComponent can be found
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ var entman = args.EntityManager;
+ if (!entman.TryGetComponent(args.TargetEntity, out HungerComponent? hunger))
+ return;
+ if (args is EntityEffectReagentArgs reagentArgs)
+ {
+ entman.System<HungerSystem>().ModifyHunger(reagentArgs.TargetEntity, NutritionFactor * (float) reagentArgs.Quantity, hunger);
+ }
+ else
+ {
+ entman.System<HungerSystem>().ModifyHunger(args.TargetEntity, NutritionFactor, hunger);
+ }
+ }
+
+ protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("reagent-effect-guidebook-satiate-hunger", ("chance", Probability), ("relative", NutritionFactor / DefaultNutritionFactor));
+}
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;
+namespace Content.Shared.EntityEffects.Effects;
/// <summary>
/// Default metabolism for drink reagents. Attempts to find a ThirstComponent on the target,
-using Content.Shared.EntityEffects;
using Content.Shared.Physics;
using Content.Shared.Slippery;
using Content.Shared.StepTrigger.Components;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects;
/// <summary>
/// Makes a mob slippery.
using Content.Shared.Chemistry.Reagent;
-using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects;
/// <summary>
/// Sets the temperature of the solution involved with the reaction to a new value.
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;
+namespace Content.Shared.EntityEffects.Effects.StatusEffects;
/// <summary>
/// Adds a generic status effect to the entity,
/// <remarks>
/// Can be used for things like adding accents or something. I don't know. Go wild.
/// </remarks>
-[UsedImplicitly]
public sealed partial class GenericStatusEffect : EntityEffect
{
[DataField(required: true)]
using Content.Shared.Chemistry.Reagent;
-using Content.Shared.EntityEffects;
using Content.Shared.Jittering;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.Effects.StatusEffects;
+namespace Content.Shared.EntityEffects.Effects.StatusEffects;
/// <summary>
/// Adds the jitter status effect to a mob.
-using Content.Server.Nutrition.EntitySystems;
using Content.Shared.Chemistry.Reagent;
-using Content.Shared.EntityEffects;
using Content.Shared.Nutrition.Components;
-using JetBrains.Annotations;
+using Content.Shared.Nutrition.EntitySystems;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.Effects;
+namespace Content.Shared.EntityEffects.Effects;
-[UsedImplicitly]
public sealed partial class WashCreamPieReaction : EntityEffect
{
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
{
if (!args.EntityManager.TryGetComponent(args.TargetEntity, out CreamPiedComponent? creamPied)) return;
- args.EntityManager.System<CreamPieSystem>().SetCreamPied(args.TargetEntity, creamPied, false);
+ args.EntityManager.System<SharedCreamPieSystem>().SetCreamPied(args.TargetEntity, creamPied, false);
}
}
using Content.Shared.Inventory;
using Content.Shared.Chemistry.Reagent;
-using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
-namespace Content.Server.EntityEffects.Effects.Effects;
+namespace Content.Shared.EntityEffects.Effects;
/// <summary>
/// A reaction effect that spawns a PrototypeID in the entity's Slot, and attempts to consume the reagent if EntityEffectReagentArgs.
}
}
+[ByRefEvent]
+public struct ExecuteEntityEffectEvent<T> where T : EntityEffect
+{
+ public T Effect;
+ public EntityEffectBaseArgs Args;
+
+ public ExecuteEntityEffectEvent(T effect, EntityEffectBaseArgs args)
+ {
+ Effect = effect;
+ Args = args;
+ }
+}
+
/// <summary>
/// EntityEffectBaseArgs only contains the target of an effect.
/// If a trigger wants to include more info (e.g. the quantity of the chemical triggering the effect), it can be extended (see EntityEffectReagentArgs).
public abstract string GuidebookExplanation(IPrototypeManager prototype);
}
+[ByRefEvent]
+public struct CheckEntityEffectConditionEvent<T> where T : EntityEffectCondition
+{
+ public T Condition;
+ public EntityEffectBaseArgs Args;
+ public bool Result;
+}
--- /dev/null
+namespace Content.Shared.EntityEffects;
+
+public abstract partial class EventEntityEffect<T> : EntityEffect where T : EntityEffect
+{
+ public override void Effect(EntityEffectBaseArgs args)
+ {
+ if (this is not T type)
+ return;
+ var ev = new ExecuteEntityEffectEvent<T>(type, args);
+ args.EntityManager.EventBus.RaiseEvent(EventSource.Local, ref ev);
+ }
+}
--- /dev/null
+namespace Content.Shared.EntityEffects;
+
+public abstract partial class EventEntityEffectCondition<T> : EntityEffectCondition where T : EventEntityEffectCondition<T>
+{
+ public override bool Condition(EntityEffectBaseArgs args)
+ {
+ if (this is not T type)
+ return false;
+
+ var evt = new CheckEntityEffectConditionEvent<T> { Condition = type, Args = args };
+ args.EntityManager.EventBus.RaiseEvent(EventSource.Local, ref evt);
+ return evt.Result;
+ }
+}