From 195bf86fe23c0ec6a74d3fb42876b94c8c720bd5 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sun, 18 Dec 2022 13:12:28 -0500 Subject: [PATCH] PlantholderComponent ECS (#12871) --- .../Botany/PlantHolderVisualizerSystem.cs | 1 - .../Botany/Components/PlantHolderComponent.cs | 611 +--------------- .../Botany/Systems/BotanySwabSystem.cs | 2 +- .../Botany/Systems/BotanySystem.Plant.cs | 43 -- .../Botany/Systems/BotanySystem.Seed.cs | 25 +- .../Botany/Systems/PlantHolderSystem.cs | 674 +++++++++++++++++- .../PlantMetabolism/PlantAdjustHealth.cs | 7 +- .../PlantMetabolism/PlantAdjustNutrition.cs | 8 +- .../PlantMetabolism/PlantAdjustWater.cs | 7 +- .../PlantMetabolism/PlantAffectGrowth.cs | 7 +- .../PlantMetabolism/PlantDiethylamine.cs | 8 +- .../PlantMetabolism/RobustHarvest.cs | 7 +- 12 files changed, 720 insertions(+), 680 deletions(-) delete mode 100644 Content.Server/Botany/Systems/BotanySystem.Plant.cs diff --git a/Content.Client/Botany/PlantHolderVisualizerSystem.cs b/Content.Client/Botany/PlantHolderVisualizerSystem.cs index b94ca28ddf..ba2d5923c6 100644 --- a/Content.Client/Botany/PlantHolderVisualizerSystem.cs +++ b/Content.Client/Botany/PlantHolderVisualizerSystem.cs @@ -1,7 +1,6 @@ using Content.Client.Botany.Components; using Content.Shared.Botany; using Robust.Client.GameObjects; -using Robust.Shared.Utility; namespace Content.Client.Botany; diff --git a/Content.Server/Botany/Components/PlantHolderComponent.cs b/Content.Server/Botany/Components/PlantHolderComponent.cs index bc95c41726..97af11328d 100644 --- a/Content.Server/Botany/Components/PlantHolderComponent.cs +++ b/Content.Server/Botany/Components/PlantHolderComponent.cs @@ -1,50 +1,34 @@ -using Content.Server.Atmos; -using Content.Server.Atmos.EntitySystems; -using Content.Server.Botany.Systems; -using Content.Server.Chemistry.EntitySystems; -using Content.Server.Hands.Components; -using Content.Server.Ghost.Roles.Components; -using Content.Shared.Botany; -using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.Reagent; -using Content.Shared.FixedPoint; -using Robust.Server.GameObjects; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Timing; - namespace Content.Server.Botany.Components { [RegisterComponent] public sealed class PlantHolderComponent : Component { - public const float HydroponicsSpeedMultiplier = 1f; - public const float HydroponicsConsumptionMultiplier = 4f; + [ViewVariables] + public TimeSpan NextUpdate = TimeSpan.Zero; + public TimeSpan UpdateDelay = TimeSpan.FromSeconds(3); - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly IEntityManager _entMan = default!; - - [ViewVariables] private int _lastProduce; - - [ViewVariables(VVAccess.ReadWrite)] public int MissingGas; - - private readonly TimeSpan _cycleDelay = TimeSpan.FromSeconds(15f); - - [ViewVariables] public TimeSpan LastCycle = TimeSpan.Zero; - - [ViewVariables(VVAccess.ReadWrite)] private bool _updateSpriteAfterUpdate; + [ViewVariables] + public int LastProduce; [ViewVariables(VVAccess.ReadWrite)] - [DataField("drawWarnings")] - public bool DrawWarnings { get; private set; } = false; + public int MissingGas; + + public readonly TimeSpan CycleDelay = TimeSpan.FromSeconds(15f); + + [ViewVariables] + public TimeSpan LastCycle = TimeSpan.Zero; [ViewVariables(VVAccess.ReadWrite)] - public float WaterLevel { get; private set; } = 100f; + public bool UpdateSpriteAfterUpdate; + + [ViewVariables(VVAccess.ReadWrite)] [DataField("drawWarnings")] + public bool DrawWarnings = false; [ViewVariables(VVAccess.ReadWrite)] - public float NutritionLevel { get; private set; } = 100f; + public float WaterLevel = 100f; + + [ViewVariables(VVAccess.ReadWrite)] + public float NutritionLevel = 100f; [ViewVariables(VVAccess.ReadWrite)] public float PestLevel { get; set; } @@ -102,562 +86,5 @@ namespace Content.Server.Botany.Components [DataField("solution")] public string SoilSolutionName { get; set; } = "soil"; - - public void WeedInvasion() - { - // TODO - } - - public void Update() - { - UpdateReagents(); - - var curTime = _gameTiming.CurTime; - - if (ForceUpdate) - ForceUpdate = false; - else if (curTime < (LastCycle + _cycleDelay)) - { - if (_updateSpriteAfterUpdate) - UpdateSprite(); - return; - } - - LastCycle = curTime; - - // todo ecs. - var botanySystem = EntitySystem.Get(); - - // Process mutations - if (MutationLevel > 0) - { - Mutate(Math.Min(MutationLevel, 25)); - MutationLevel = 0; - } - - // Weeds like water and nutrients! They may appear even if there's not a seed planted. - if (WaterLevel > 10 && NutritionLevel > 2 && _random.Prob(Seed == null ? 0.05f : 0.01f)) - { - WeedLevel += 1 * HydroponicsSpeedMultiplier * WeedCoefficient; - - if (DrawWarnings) - _updateSpriteAfterUpdate = true; - } - - // There's a chance for a weed explosion to happen if weeds take over. - // Plants that are themselves weeds (WeedTolerance > 8) are unaffected. - if (WeedLevel >= 10 && _random.Prob(0.1f)) - { - if (Seed == null || WeedLevel >= Seed.WeedTolerance + 2) - WeedInvasion(); - } - - // If we have no seed planted, or the plant is dead, stop processing here. - if (Seed == null || Dead) - { - if (_updateSpriteAfterUpdate) - UpdateSprite(); - - return; - } - - // There's a small chance the pest population increases. - // Can only happen when there's a live seed planted. - if (_random.Prob(0.01f)) - { - PestLevel += 0.5f * HydroponicsSpeedMultiplier; - if (DrawWarnings) - _updateSpriteAfterUpdate = true; - } - - // Advance plant age here. - if (SkipAging > 0) - SkipAging--; - else - { - if (_random.Prob(0.8f)) - Age += (int) (1 * HydroponicsSpeedMultiplier); - - _updateSpriteAfterUpdate = true; - } - - // Nutrient consumption. - if (Seed.NutrientConsumption > 0 && NutritionLevel > 0 && _random.Prob(0.75f)) - { - NutritionLevel -= MathF.Max(0f, Seed.NutrientConsumption * HydroponicsSpeedMultiplier); - if (DrawWarnings) - _updateSpriteAfterUpdate = true; - } - - // Water consumption. - if (Seed.WaterConsumption > 0 && WaterLevel > 0 && _random.Prob(0.75f)) - { - WaterLevel -= MathF.Max(0f, - Seed.NutrientConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); - if (DrawWarnings) - _updateSpriteAfterUpdate = true; - } - - var healthMod = _random.Next(1, 3) * HydroponicsSpeedMultiplier; - - // Make sure genetics are viable. - if (!Seed.Viable) - { - AffectGrowth(-1); - Health -= 6*healthMod; - } - - // Make sure the plant is not starving. - if (_random.Prob(0.35f)) - { - if (NutritionLevel > 2) - { - Health += healthMod; - } - else - { - AffectGrowth(-1); - Health -= healthMod; - } - - if (DrawWarnings) - _updateSpriteAfterUpdate = true; - } - - // Make sure the plant is not thirsty. - if (_random.Prob(0.35f)) - { - if (WaterLevel > 10) - { - Health += healthMod; - } - else - { - AffectGrowth(-1); - Health -= healthMod; - } - - if (DrawWarnings) - _updateSpriteAfterUpdate = true; - } - - var atmosphereSystem = _entMan.EntitySysManager.GetEntitySystem(); - var environment = atmosphereSystem.GetContainingMixture(Owner, true, true) ?? - GasMixture.SpaceGas; - - if (Seed.ConsumeGasses.Count > 0) - { - MissingGas = 0; - - foreach (var (gas, amount) in Seed.ConsumeGasses) - { - if (environment.GetMoles(gas) < amount) - { - MissingGas++; - continue; - } - - environment.AdjustMoles(gas, -amount); - } - - if (MissingGas > 0) - { - Health -= MissingGas * HydroponicsSpeedMultiplier; - if (DrawWarnings) - _updateSpriteAfterUpdate = true; - } - } - - // SeedPrototype pressure resistance. - var pressure = environment.Pressure; - if (pressure < Seed.LowPressureTolerance || pressure > Seed.HighPressureTolerance) - { - Health -= healthMod; - ImproperPressure = true; - if (DrawWarnings) - _updateSpriteAfterUpdate = true; - } - else - { - ImproperPressure = false; - } - - // SeedPrototype ideal temperature. - if (MathF.Abs(environment.Temperature - Seed.IdealHeat) > Seed.HeatTolerance) - { - Health -= healthMod; - ImproperHeat = true; - if (DrawWarnings) - _updateSpriteAfterUpdate = true; - } - else - { - ImproperHeat = false; - } - - // Gas production. - var exudeCount = Seed.ExudeGasses.Count; - if (exudeCount > 0) - { - foreach (var (gas, amount) in Seed.ExudeGasses) - { - environment.AdjustMoles(gas, - MathF.Max(1f, MathF.Round((amount * MathF.Round(Seed.Potency)) / exudeCount))); - } - } - - // Toxin levels beyond the plant's tolerance cause damage. - // They are, however, slowly reduced over time. - if (Toxins > 0) - { - var toxinUptake = MathF.Max(1, MathF.Round(Toxins / 10f)); - if (Toxins > Seed.ToxinsTolerance) - { - Health -= toxinUptake; - } - - Toxins -= toxinUptake; - if (DrawWarnings) - _updateSpriteAfterUpdate = true; - } - - // Weed levels. - if (PestLevel > 0) - { - // TODO: Carnivorous plants? - if (PestLevel > Seed.PestTolerance) - { - Health -= HydroponicsSpeedMultiplier; - } - - if (DrawWarnings) - _updateSpriteAfterUpdate = true; - } - - // Weed levels. - if (WeedLevel > 0) - { - // TODO: Parasitic plants. - if (WeedLevel >= Seed.WeedTolerance) - { - Health -= HydroponicsSpeedMultiplier; - } - - if (DrawWarnings) - _updateSpriteAfterUpdate = true; - } - - if (Age > Seed.Lifespan) - { - Health -= _random.Next(3, 5) * HydroponicsSpeedMultiplier; - if (DrawWarnings) - _updateSpriteAfterUpdate = true; - } - else if (Age < 0) // Revert back to seed packet! - { - botanySystem.SpawnSeedPacket(Seed, _entMan.GetComponent(Owner).Coordinates); - RemovePlant(); - ForceUpdate = true; - Update(); - } - - CheckHealth(); - - if (Harvest && Seed.HarvestRepeat == HarvestType.SelfHarvest) - AutoHarvest(); - - // If enough time has passed since the plant was harvested, we're ready to harvest again! - if (!Dead && Seed.ProductPrototypes.Count > 0) - { - if (Age > Seed.Production) - { - if ((Age - _lastProduce) > Seed.Production && !Harvest) - { - Harvest = true; - _lastProduce = Age; - } - } - else - { - if (Harvest) - { - Harvest = false; - _lastProduce = Age; - } - } - } - - CheckLevelSanity(); - - if (Seed.Sentient) - { - var comp = _entMan.EnsureComponent(Owner); - comp.RoleName = _entMan.GetComponent(Owner).EntityName; - comp.RoleDescription = Loc.GetString("station-event-random-sentience-role-description", ("name", comp.RoleName)); - } - - if (_updateSpriteAfterUpdate) - UpdateSprite(); - } - - public void CheckLevelSanity() - { - if (Seed != null) - Health = MathHelper.Clamp(Health, 0, Seed.Endurance); - else - { - Health = 0f; - Dead = false; - } - - MutationLevel = MathHelper.Clamp(MutationLevel, 0f, 100f); - NutritionLevel = MathHelper.Clamp(NutritionLevel, 0f, 100f); - WaterLevel = MathHelper.Clamp(WaterLevel, 0f, 100f); - PestLevel = MathHelper.Clamp(PestLevel, 0f, 10f); - WeedLevel = MathHelper.Clamp(WeedLevel, 0f, 10f); - Toxins = MathHelper.Clamp(Toxins, 0f, 100f); - YieldMod = MathHelper.Clamp(YieldMod, 0, 2); - MutationMod = MathHelper.Clamp(MutationMod, 0f, 3f); - } - - public bool DoHarvest(EntityUid user) - { - if (Seed == null || _entMan.Deleted(user)) - return false; - - var botanySystem = EntitySystem.Get(); - - if (Harvest && !Dead) - { - if (_entMan.TryGetComponent(user, out HandsComponent? hands)) - { - if (!botanySystem.CanHarvest(Seed, hands.ActiveHandEntity)) - return false; - } - else if (!botanySystem.CanHarvest(Seed)) - { - return false; - } - - botanySystem.Harvest(Seed, user, YieldMod); - AfterHarvest(); - return true; - } - - if (!Dead) return false; - - RemovePlant(); - AfterHarvest(); - return true; - } - - public void AutoHarvest() - { - if (Seed == null || !Harvest) - return; - - var botanySystem = EntitySystem.Get(); - - botanySystem.AutoHarvest(Seed, _entMan.GetComponent(Owner).Coordinates); - AfterHarvest(); - } - - private void AfterHarvest() - { - Harvest = false; - _lastProduce = Age; - - if (Seed?.HarvestRepeat == HarvestType.NoRepeat) - RemovePlant(); - - CheckLevelSanity(); - UpdateSprite(); - } - - public void CheckHealth() - { - if (Health <= 0) - { - Die(); - } - } - - public void Die() - { - Dead = true; - Harvest = false; - MutationLevel = 0; - YieldMod = 1; - MutationMod = 1; - ImproperLight = false; - ImproperHeat = false; - ImproperPressure = false; - WeedLevel += 1 * HydroponicsSpeedMultiplier; - PestLevel = 0; - UpdateSprite(); - } - - public void RemovePlant() - { - YieldMod = 1; - MutationMod = 1; - PestLevel = 0; - Seed = null; - Dead = false; - Age = 0; - Sampled = false; - Harvest = false; - ImproperLight = false; - ImproperPressure = false; - ImproperHeat = false; - - UpdateSprite(); - } - - public void AffectGrowth(int amount) - { - if (Seed == null) - return; - - if (amount > 0) - { - if (Age < Seed.Maturation) - Age += amount; - else if (!Harvest && Seed.Yield <= 0f) - _lastProduce -= amount; - } - else - { - if (Age < Seed.Maturation) - SkipAging++; - else if (!Harvest && Seed.Yield <= 0f) - _lastProduce += amount; - } - } - - public void AdjustNutrient(float amount) - { - NutritionLevel += amount; - } - - public void AdjustWater(float amount) - { - WaterLevel += amount; - - // Water dilutes toxins. - if (amount > 0) - { - Toxins -= amount * 4f; - } - } - - public void UpdateReagents() - { - var solutionSystem = EntitySystem.Get(); - if (!solutionSystem.TryGetSolution(Owner, SoilSolutionName, out var solution)) - return; - - if (solution.TotalVolume > 0 && MutationLevel < 25) - { - var amt = FixedPoint2.New(1); - foreach (var (reagentId, quantity) in solutionSystem.RemoveEachReagent(Owner, solution, amt)) - { - var reagentProto = _prototypeManager.Index(reagentId); - reagentProto.ReactionPlant(Owner, new Solution.ReagentQuantity(reagentId, quantity), solution); - } - } - - CheckLevelSanity(); - } - - private void Mutate(float severity) - { - if (Seed != null) - { - EnsureUniqueSeed(); - _entMan.System().MutateSeed(Seed, severity); - } - } - - public void UpdateSprite() - { - _updateSpriteAfterUpdate = false; - - if (Seed != null && Seed.Bioluminescent) - { - var light = _entMan.EnsureComponent(Owner); - light.Radius = Seed.BioluminescentRadius; - light.Color = Seed.BioluminescentColor; - light.CastShadows = false; // this is expensive, and botanists make lots of plants - light.Dirty(); - } - else - { - _entMan.RemoveComponent(Owner); - } - - if (!_entMan.TryGetComponent(Owner, out var appearanceComponent)) - return; - - if (Seed != null) - { - if (DrawWarnings) - appearanceComponent.SetData(PlantHolderVisuals.HealthLight, Health <= (Seed.Endurance / 2f)); - - if (Dead) - { - appearanceComponent.SetData(PlantHolderVisuals.PlantRsi, Seed.PlantRsi.ToString()); - appearanceComponent.SetData(PlantHolderVisuals.PlantState, "dead"); - } - else if (Harvest) - { - appearanceComponent.SetData(PlantHolderVisuals.PlantRsi, Seed.PlantRsi.ToString()); - appearanceComponent.SetData(PlantHolderVisuals.PlantState, "harvest"); - } - else if (Age < Seed.Maturation) - { - var growthStage = Math.Max(1, (int) ((Age * Seed.GrowthStages) / Seed.Maturation)); - - appearanceComponent.SetData(PlantHolderVisuals.PlantRsi, Seed.PlantRsi.ToString()); - appearanceComponent.SetData(PlantHolderVisuals.PlantState, $"stage-{growthStage}"); - _lastProduce = Age; - } - else - { - appearanceComponent.SetData(PlantHolderVisuals.PlantRsi, Seed.PlantRsi.ToString()); - appearanceComponent.SetData(PlantHolderVisuals.PlantState, $"stage-{Seed.GrowthStages}"); - } - } - else - { - appearanceComponent.SetData(PlantHolderVisuals.PlantState, ""); - appearanceComponent.SetData(PlantHolderVisuals.HealthLight, false); - } - - if (!DrawWarnings) return; - appearanceComponent.SetData(PlantHolderVisuals.WaterLight, WaterLevel <= 10); - appearanceComponent.SetData(PlantHolderVisuals.NutritionLight, NutritionLevel <= 2); - appearanceComponent.SetData(PlantHolderVisuals.AlertLight, - WeedLevel >= 5 || PestLevel >= 5 || Toxins >= 40 || ImproperHeat || ImproperLight || ImproperPressure || - MissingGas > 0); - appearanceComponent.SetData(PlantHolderVisuals.HarvestLight, Harvest); - } - - /// - /// Check if the currently contained seed is unique. If it is not, clone it so that we have a unique seed. - /// Necessary to avoid modifying global seeds. - /// - public void EnsureUniqueSeed() - { - if (Seed != null && !Seed.Unique) - Seed = Seed.Clone(); - } - - public void ForceUpdateByExternalCause() - { - SkipAging++; // We're forcing an update cycle, so one age hasn't passed. - ForceUpdate = true; - Update(); - } } } diff --git a/Content.Server/Botany/Systems/BotanySwabSystem.cs b/Content.Server/Botany/Systems/BotanySwabSystem.cs index e372726c66..75caf351c7 100644 --- a/Content.Server/Botany/Systems/BotanySwabSystem.cs +++ b/Content.Server/Botany/Systems/BotanySwabSystem.cs @@ -13,7 +13,7 @@ using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Utility; -namespace Content.Server.Botany +namespace Content.Server.Botany.Systems { public sealed class BotanySwabSystem : EntitySystem { diff --git a/Content.Server/Botany/Systems/BotanySystem.Plant.cs b/Content.Server/Botany/Systems/BotanySystem.Plant.cs deleted file mode 100644 index 590fbd58e1..0000000000 --- a/Content.Server/Botany/Systems/BotanySystem.Plant.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Content.Server.Botany.Components; -using Content.Server.Chemistry.EntitySystems; -using Content.Server.Popups; -using JetBrains.Annotations; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; - -namespace Content.Server.Botany.Systems -{ - [UsedImplicitly] - public sealed partial class BotanySystem : EntitySystem - { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; - [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; - - private float _timer = 0f; - - public override void Initialize() - { - base.Initialize(); - - InitializeSeeds(); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - _timer += frameTime; - if (_timer < 3f) - return; - - _timer -= 3f; - - foreach (var plantHolder in EntityManager.EntityQuery()) - { - plantHolder.Update(); - } - } - } -} diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index 0e5a1ad52c..47ebb97d4d 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -1,8 +1,9 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Server.Botany.Components; -using Content.Server.Mind.Commands; +using Content.Server.Chemistry.EntitySystems; using Content.Server.Kitchen.Components; +using Content.Server.Popups; using Content.Shared.Botany; using Content.Shared.Examine; using Content.Shared.Popups; @@ -12,15 +13,24 @@ using Content.Shared.StepTrigger.Components; using Robust.Server.GameObjects; using Robust.Shared.Map; using Robust.Shared.Player; +using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; namespace Content.Server.Botany.Systems; -public sealed partial class BotanySystem +public sealed partial class BotanySystem : EntitySystem { - public void InitializeSeeds() + [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + + public override void Initialize() { + base.Initialize(); + SubscribeLocalEvent(OnExamined); } @@ -90,7 +100,7 @@ public sealed partial class BotanySystem sprite.LayerSetSprite(0, new SpriteSpecifier.Rsi(proto.PlantRsi, "seed")); } - string val = Loc.GetString("botany-seed-packet-name", ("seedName", proto.Name), ("seedNoun", proto.Noun)); + var val = Loc.GetString("botany-seed-packet-name", ("seedName", proto.Name), ("seedNoun", proto.Noun)); MetaData(seed).EntityName = val; return seed; @@ -150,10 +160,7 @@ public sealed partial class BotanySystem produce.Seed = proto; ProduceGrown(entity, produce); - if (TryComp(entity, out var appearance)) - { - appearance.SetData(ProduceVisuals.Potency, proto.Potency); - } + _appearance.SetData(entity, ProduceVisuals.Potency, proto.Potency); if (proto.Mysterious) { @@ -168,7 +175,7 @@ public sealed partial class BotanySystem light.Radius = proto.BioluminescentRadius; light.Color = proto.BioluminescentColor; light.CastShadows = false; // this is expensive, and botanists make lots of plants - light.Dirty(); + Dirty(light); } if (proto.Slip) diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 35d96c4b9a..29a45fc0e3 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -1,19 +1,27 @@ +using Content.Server.Atmos; +using Content.Server.Atmos.EntitySystems; using Content.Server.Botany.Components; using Content.Server.Popups; using Content.Server.Chemistry.EntitySystems; using Content.Server.Fluids.Components; +using Content.Server.Ghost.Roles.Components; +using Content.Server.Hands.Components; using Content.Server.Kitchen.Components; using Content.Shared.Interaction; using Content.Shared.Examine; using Content.Shared.Tag; using Content.Shared.FixedPoint; -using Content.Shared.Audio; +using Content.Shared.Botany; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; using Content.Shared.IdentityManagement; using Content.Shared.Popups; using Content.Shared.Random.Helpers; +using Robust.Server.GameObjects; using Robust.Shared.Player; using Robust.Shared.Timing; using Robust.Shared.Audio; +using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.Server.Botany.Systems @@ -21,11 +29,19 @@ namespace Content.Server.Botany.Systems public sealed class PlantHolderSystem : EntitySystem { [Dependency] private readonly BotanySystem _botanySystem = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly MutationSystem _mutation = default!; + [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly AudioSystem _audio = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionSystem = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly AtmosphereSystem _atmosphere = default!; + + public const float HydroponicsSpeedMultiplier = 1f; + public const float HydroponicsConsumptionMultiplier = 4f; public override void Initialize() { @@ -35,6 +51,20 @@ namespace Content.Server.Botany.Systems SubscribeLocalEvent(OnInteractHand); } + public override void Update(float frameTime) + { + base.Update(frameTime); + + foreach (var plantHolder in EntityQuery()) + { + if (plantHolder.NextUpdate > _gameTiming.CurTime) + continue; + plantHolder.NextUpdate = _gameTiming.CurTime + plantHolder.UpdateDelay; + + Update(plantHolder.Owner, plantHolder); + } + } + private void OnExamine(EntityUid uid, PlantHolderComponent component, ExaminedEvent args) { if (!args.IsInDetailsRange) @@ -51,12 +81,14 @@ namespace Content.Server.Botany.Systems ("toBeForm", component.Seed.DisplayName.EndsWith('s') ? "are" : "is"))); if (component.Health <= component.Seed.Endurance / 2) + { args.PushMarkup(Loc.GetString( - "plant-holder-component-something-already-growing-low-health-message", - ("healthState", - Loc.GetString(component.Age > component.Seed.Lifespan - ? "plant-holder-component-plant-old-adjective" - : "plant-holder-component-plant-unhealthy-adjective")))); + "plant-holder-component-something-already-growing-low-health-message", + ("healthState", + Loc.GetString(component.Age > component.Seed.Lifespan + ? "plant-holder-component-plant-old-adjective" + : "plant-holder-component-plant-unhealthy-adjective")))); + } } else { @@ -114,8 +146,8 @@ namespace Content.Server.Botany.Systems EntityManager.QueueDeleteEntity(args.Used); - component.CheckLevelSanity(); - component.UpdateSprite(); + CheckLevelSanity(uid, component); + UpdateSprite(uid, component); return; } @@ -134,7 +166,7 @@ namespace Content.Server.Botany.Systems _popupSystem.PopupEntity(Loc.GetString("plant-holder-component-remove-weeds-others-message", ("otherName", Comp(args.User).EntityName)), uid, Filter.PvsExcept(args.User)); component.WeedLevel = 0; - component.UpdateSprite(); + UpdateSprite(uid, component); } else { @@ -152,7 +184,7 @@ namespace Content.Server.Botany.Systems ("name", Comp(uid).EntityName)), Filter.Entities(args.User), PopupType.Medium); _popupSystem.PopupEntity(Loc.GetString("plant-holder-component-remove-plant-others-message", ("name", Comp(args.User).EntityName)), uid, Filter.PvsExcept(args.User)); - component.RemovePlant(); + RemovePlant(uid, component); } else { @@ -164,17 +196,15 @@ namespace Content.Server.Botany.Systems } if (_solutionSystem.TryGetDrainableSolution(args.Used, out var solution) - && _solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var targetSolution) && TryComp(args.Used, out SprayComponent? spray)) + && _solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var targetSolution) + && TryComp(args.Used, out SprayComponent? spray)) { var amount = FixedPoint2.New(1); var targetEntity = uid; var solutionEntity = args.Used; - - SoundSystem.Play(spray.SpraySound.GetSound(), Filter.Pvs(args.Used), - args.Used, AudioHelpers.WithVariation(0.125f)); - + _audio.PlayPvs(spray.SpraySound, args.Used, AudioParams.Default.WithVariation(0.125f)); var split =_solutionSystem.Drain(solutionEntity, solution, amount); @@ -191,7 +221,7 @@ namespace Content.Server.Botany.Systems _solutionSystem.TryAddSolution(targetEntity, targetSolution, split); - component.ForceUpdateByExternalCause(); + ForceUpdateByExternalCause(uid, component); return; } @@ -227,14 +257,14 @@ namespace Content.Server.Botany.Systems component.Sampled = true; // Just in case. - component.CheckLevelSanity(); - component.ForceUpdateByExternalCause(); + CheckLevelSanity(uid, component); + ForceUpdateByExternalCause(uid, component); return; } if (HasComp(args.Used)) - component.DoHarvest(args.User); + DoHarvest(uid, args.User, component); if (TryComp(args.Used, out var produce)) { @@ -252,7 +282,7 @@ namespace Content.Server.Botany.Systems _solutionSystem.TryAddSolution(args.Used, solution2, _solutionSystem.SplitSolution(args.Used, solution2, solution2.TotalVolume)); - component.ForceUpdateByExternalCause(); + ForceUpdateByExternalCause(uid, component); } EntityManager.QueueDeleteEntity(args.Used); @@ -261,7 +291,609 @@ namespace Content.Server.Botany.Systems private void OnInteractHand(EntityUid uid, PlantHolderComponent component, InteractHandEvent args) { - component.DoHarvest(args.User); + DoHarvest(uid, args.User, component); + } + + public void WeedInvasion() + { + // TODO + } + + public void Update(EntityUid uid, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + UpdateReagents(uid, component); + + var curTime = _gameTiming.CurTime; + + if (component.ForceUpdate) + component.ForceUpdate = false; + else if (curTime < (component.LastCycle + component.CycleDelay)) + { + if (component.UpdateSpriteAfterUpdate) + UpdateSprite(uid, component); + return; + } + + component.LastCycle = curTime; + + // Process mutations + if (component.MutationLevel > 0) + { + Mutate(uid, Math.Min(component.MutationLevel, 25), component); + component.MutationLevel = 0; + } + + // Weeds like water and nutrients! They may appear even if there's not a seed planted. + if (component.WaterLevel > 10 && component.NutritionLevel > 2 && _random.Prob(component.Seed == null ? 0.05f : 0.01f)) + { + component.WeedLevel += 1 * HydroponicsSpeedMultiplier * component.WeedCoefficient; + + if (component.DrawWarnings) + component.UpdateSpriteAfterUpdate = true; + } + + // There's a chance for a weed explosion to happen if weeds take over. + // Plants that are themselves weeds (WeedTolerance > 8) are unaffected. + if (component.WeedLevel >= 10 && _random.Prob(0.1f)) + { + if (component.Seed == null || component.WeedLevel >= component.Seed.WeedTolerance + 2) + WeedInvasion(); + } + + // If we have no seed planted, or the plant is dead, stop processing here. + if (component.Seed == null || component.Dead) + { + if (component.UpdateSpriteAfterUpdate) + UpdateSprite(uid, component); + + return; + } + + // There's a small chance the pest population increases. + // Can only happen when there's a live seed planted. + if (_random.Prob(0.01f)) + { + component.PestLevel += 0.5f * HydroponicsSpeedMultiplier; + if (component.DrawWarnings) + component.UpdateSpriteAfterUpdate = true; + } + + // Advance plant age here. + if (component.SkipAging > 0) + component.SkipAging--; + else + { + if (_random.Prob(0.8f)) + component.Age += (int) (1 * HydroponicsSpeedMultiplier); + + component.UpdateSpriteAfterUpdate = true; + } + + // Nutrient consumption. + if (component.Seed.NutrientConsumption > 0 && component.NutritionLevel > 0 && _random.Prob(0.75f)) + { + component.NutritionLevel -= MathF.Max(0f, component.Seed.NutrientConsumption * HydroponicsSpeedMultiplier); + if (component.DrawWarnings) + component.UpdateSpriteAfterUpdate = true; + } + + // Water consumption. + if (component.Seed.WaterConsumption > 0 && component.WaterLevel > 0 && _random.Prob(0.75f)) + { + component.WaterLevel -= MathF.Max(0f, + component.Seed.NutrientConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); + if (component.DrawWarnings) + component.UpdateSpriteAfterUpdate = true; + } + + var healthMod = _random.Next(1, 3) * HydroponicsSpeedMultiplier; + + // Make sure genetics are viable. + if (!component.Seed.Viable) + { + AffectGrowth(uid, -1, component); + component.Health -= 6*healthMod; + } + + // Make sure the plant is not starving. + if (_random.Prob(0.35f)) + { + if (component.NutritionLevel > 2) + { + component.Health += healthMod; + } + else + { + AffectGrowth(uid, -1, component); + component.Health -= healthMod; + } + + if (component.DrawWarnings) + component.UpdateSpriteAfterUpdate = true; + } + + // Make sure the plant is not thirsty. + if (_random.Prob(0.35f)) + { + if (component.WaterLevel > 10) + { + component.Health += healthMod; + } + else + { + AffectGrowth(uid, -1, component); + component.Health -= healthMod; + } + + if (component.DrawWarnings) + component.UpdateSpriteAfterUpdate = true; + } + var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; + + if (component.Seed.ConsumeGasses.Count > 0) + { + component.MissingGas = 0; + + foreach (var (gas, amount) in component.Seed.ConsumeGasses) + { + if (environment.GetMoles(gas) < amount) + { + component.MissingGas++; + continue; + } + + environment.AdjustMoles(gas, -amount); + } + + if (component.MissingGas > 0) + { + component.Health -= component.MissingGas * HydroponicsSpeedMultiplier; + if (component.DrawWarnings) + component.UpdateSpriteAfterUpdate = true; + } + } + + // SeedPrototype pressure resistance. + var pressure = environment.Pressure; + if (pressure < component.Seed.LowPressureTolerance || pressure > component.Seed.HighPressureTolerance) + { + component.Health -= healthMod; + component.ImproperPressure = true; + if (component.DrawWarnings) + component.UpdateSpriteAfterUpdate = true; + } + else + { + component.ImproperPressure = false; + } + + // SeedPrototype ideal temperature. + if (MathF.Abs(environment.Temperature - component.Seed.IdealHeat) > component.Seed.HeatTolerance) + { + component.Health -= healthMod; + component.ImproperHeat = true; + if (component.DrawWarnings) + component.UpdateSpriteAfterUpdate = true; + } + else + { + component.ImproperHeat = false; + } + + // Gas production. + var exudeCount = component.Seed.ExudeGasses.Count; + if (exudeCount > 0) + { + foreach (var (gas, amount) in component.Seed.ExudeGasses) + { + environment.AdjustMoles(gas, + MathF.Max(1f, MathF.Round(amount * MathF.Round(component.Seed.Potency) / exudeCount))); + } + } + + // Toxin levels beyond the plant's tolerance cause damage. + // They are, however, slowly reduced over time. + if (component.Toxins > 0) + { + var toxinUptake = MathF.Max(1, MathF.Round(component.Toxins / 10f)); + if (component.Toxins > component.Seed.ToxinsTolerance) + { + component.Health -= toxinUptake; + } + + component.Toxins -= toxinUptake; + if (component.DrawWarnings) + component.UpdateSpriteAfterUpdate = true; + } + + // Weed levels. + if (component.PestLevel > 0) + { + // TODO: Carnivorous plants? + if (component.PestLevel > component.Seed.PestTolerance) + { + component.Health -= HydroponicsSpeedMultiplier; + } + + if (component.DrawWarnings) + component.UpdateSpriteAfterUpdate = true; + } + + // Weed levels. + if (component.WeedLevel > 0) + { + // TODO: Parasitic plants. + if (component.WeedLevel >= component.Seed.WeedTolerance) + { + component.Health -= HydroponicsSpeedMultiplier; + } + + if (component.DrawWarnings) + component.UpdateSpriteAfterUpdate = true; + } + + if (component.Age > component.Seed.Lifespan) + { + component.Health -= _random.Next(3, 5) * HydroponicsSpeedMultiplier; + if (component.DrawWarnings) + component.UpdateSpriteAfterUpdate = true; + } + else if (component.Age < 0) // Revert back to seed packet! + { + _botanySystem.SpawnSeedPacket(component.Seed, Transform(uid).Coordinates); + RemovePlant(uid, component); + component.ForceUpdate = true; + Update(uid, component); + } + + CheckHealth(uid, component); + + if (component.Harvest && component.Seed.HarvestRepeat == HarvestType.SelfHarvest) + AutoHarvest(uid, component); + + // If enough time has passed since the plant was harvested, we're ready to harvest again! + if (!component.Dead && component.Seed.ProductPrototypes.Count > 0) + { + if (component.Age > component.Seed.Production) + { + if (component.Age - component.LastProduce > component.Seed.Production && !component.Harvest) + { + component.Harvest = true; + component.LastProduce = component.Age; + } + } + else + { + if (component.Harvest) + { + component.Harvest = false; + component.LastProduce = component.Age; + } + } + } + + CheckLevelSanity(uid, component); + + if (component.Seed.Sentient) + { + var comp = EnsureComp(uid); + comp.RoleName = MetaData(uid).EntityName; + comp.RoleDescription = Loc.GetString("station-event-random-sentience-role-description", ("name", comp.RoleName)); + } + + if (component.UpdateSpriteAfterUpdate) + UpdateSprite(uid, component); + } + + //TODO: kill this bullshit + public void CheckLevelSanity(EntityUid uid, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + if (component.Seed != null) + component.Health = MathHelper.Clamp(component.Health, 0, component.Seed.Endurance); + else + { + component.Health = 0f; + component.Dead = false; + } + + component.MutationLevel = MathHelper.Clamp(component.MutationLevel, 0f, 100f); + component.NutritionLevel = MathHelper.Clamp(component.NutritionLevel, 0f, 100f); + component.WaterLevel = MathHelper.Clamp(component.WaterLevel, 0f, 100f); + component.PestLevel = MathHelper.Clamp(component.PestLevel, 0f, 10f); + component.WeedLevel = MathHelper.Clamp(component.WeedLevel, 0f, 10f); + component.Toxins = MathHelper.Clamp(component.Toxins, 0f, 100f); + component.YieldMod = MathHelper.Clamp(component.YieldMod, 0, 2); + component.MutationMod = MathHelper.Clamp(component.MutationMod, 0f, 3f); + } + + public bool DoHarvest(EntityUid plantholder, EntityUid user, PlantHolderComponent? component = null) + { + if (!Resolve(plantholder, ref component)) + return false; + + if (component.Seed == null || Deleted(user)) + return false; + + + if (component.Harvest && !component.Dead) + { + if (TryComp(user, out var hands)) + { + if (!_botanySystem.CanHarvest(component.Seed, hands.ActiveHandEntity)) + return false; + } + else if (!_botanySystem.CanHarvest(component.Seed)) + { + return false; + } + + _botanySystem.Harvest(component.Seed, user, component.YieldMod); + AfterHarvest(plantholder, component); + return true; + } + + if (!component.Dead) + return false; + + RemovePlant(plantholder, component); + AfterHarvest(plantholder, component); + return true; + } + + public void AutoHarvest(EntityUid uid, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + if (component.Seed == null || !component.Harvest) + return; + + _botanySystem.AutoHarvest(component.Seed, Transform(uid).Coordinates); + AfterHarvest(uid, component); + } + + private void AfterHarvest(EntityUid uid, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + component.Harvest = false; + component.LastProduce = component.Age; + + if (component.Seed?.HarvestRepeat == HarvestType.NoRepeat) + RemovePlant(uid, component); + + CheckLevelSanity(uid, component); + UpdateSprite(uid, component); + } + + public void CheckHealth(EntityUid uid, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + if (component.Health <= 0) + { + Die(uid, component); + } + } + + public void Die(EntityUid uid, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + component.Dead = true; + component.Harvest = false; + component.MutationLevel = 0; + component.YieldMod = 1; + component.MutationMod = 1; + component.ImproperLight = false; + component.ImproperHeat = false; + component.ImproperPressure = false; + component.WeedLevel += 1 * HydroponicsSpeedMultiplier; + component.PestLevel = 0; + UpdateSprite(uid, component); + } + + public void RemovePlant(EntityUid uid, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + component.YieldMod = 1; + component.MutationMod = 1; + component.PestLevel = 0; + component.Seed = null; + component.Dead = false; + component.Age = 0; + component.Sampled = false; + component.Harvest = false; + component.ImproperLight = false; + component.ImproperPressure = false; + component.ImproperHeat = false; + + UpdateSprite(uid, component); + } + + public void AffectGrowth(EntityUid uid, int amount, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + if (component.Seed == null) + return; + + if (amount > 0) + { + if (component.Age < component.Seed.Maturation) + component.Age += amount; + else if (!component.Harvest && component.Seed.Yield <= 0f) + component.LastProduce -= amount; + } + else + { + if (component.Age < component.Seed.Maturation) + component.SkipAging++; + else if (!component.Harvest && component.Seed.Yield <= 0f) + component.LastProduce += amount; + } + } + + public void AdjustNutrient(EntityUid uid, float amount, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + component.NutritionLevel += amount; + } + + public void AdjustWater(EntityUid uid, float amount, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + component.WaterLevel += amount; + + // Water dilutes toxins. + if (amount > 0) + { + component.Toxins -= amount * 4f; + } + } + + public void UpdateReagents(EntityUid uid, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + if (!_solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var solution)) + return; + + if (solution.TotalVolume > 0 && component.MutationLevel < 25) + { + var amt = FixedPoint2.New(1); + foreach (var (reagentId, quantity) in _solutionSystem.RemoveEachReagent(uid, solution, amt)) + { + var reagentProto = _prototype.Index(reagentId); + reagentProto.ReactionPlant(uid, new Solution.ReagentQuantity(reagentId, quantity), solution); + } + } + + CheckLevelSanity(uid, component); + } + + private void Mutate(EntityUid uid, float severity, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + if (component.Seed != null) + { + EnsureUniqueSeed(uid, component); + _mutation.MutateSeed(component.Seed, severity); + } + } + + public void UpdateSprite(EntityUid uid, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + component.UpdateSpriteAfterUpdate = false; + + if (component.Seed != null && component.Seed.Bioluminescent) + { + var light = EnsureComp(uid); + light.Radius = component.Seed.BioluminescentRadius; + light.Color = component.Seed.BioluminescentColor; + light.CastShadows = false; // this is expensive, and botanists make lots of plants + Dirty(light); + } + else + { + RemComp(uid); + } + + if (!TryComp(uid, out var app)) + return; + + if (component.Seed != null) + { + if (component.DrawWarnings) + { + _appearance.SetData(uid, PlantHolderVisuals.HealthLight, + component.Health <= component.Seed.Endurance / 2f); + } + + if (component.Dead) + { + _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app); + _appearance.SetData(uid, PlantHolderVisuals.PlantState, "dead", app); + } + else if (component.Harvest) + { + _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app); + _appearance.SetData(uid, PlantHolderVisuals.PlantState, "harvest", app); + } + else if (component.Age < component.Seed.Maturation) + { + var growthStage = Math.Max(1, (int) (component.Age * component.Seed.GrowthStages / component.Seed.Maturation)); + + _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app); + _appearance.SetData(uid, PlantHolderVisuals.PlantState, $"stage-{growthStage}", app); + component.LastProduce = component.Age; + } + else + { + _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app); + _appearance.SetData(uid, PlantHolderVisuals.PlantState, $"stage-{component.Seed.GrowthStages}", app); + } + } + else + { + _appearance.SetData(uid, PlantHolderVisuals.PlantState, "", app); + _appearance.SetData(uid, PlantHolderVisuals.HealthLight, false, app); + } + + if (!component.DrawWarnings) + return; + + _appearance.SetData(uid, PlantHolderVisuals.WaterLight, component.WaterLevel <= 10, app); + _appearance.SetData(uid, PlantHolderVisuals.NutritionLight, component.NutritionLevel <= 2, app); + _appearance.SetData(uid, PlantHolderVisuals.AlertLight, + component.WeedLevel >= 5 || component.PestLevel >= 5 || component.Toxins >= 40 || component.ImproperHeat || + component.ImproperLight || component.ImproperPressure || component.MissingGas > 0, app); + _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, component.Harvest, app); + } + + /// + /// Check if the currently contained seed is unique. If it is not, clone it so that we have a unique seed. + /// Necessary to avoid modifying global seeds. + /// + public void EnsureUniqueSeed(EntityUid uid, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + if (component.Seed is { Unique: false }) + component.Seed = component.Seed.Clone(); + } + + public void ForceUpdateByExternalCause(EntityUid uid, PlantHolderComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + component.SkipAging++; // We're forcing an update cycle, so one age hasn't passed. + component.ForceUpdate = true; + Update(uid, component); } } } diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustHealth.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustHealth.cs index 7f9b4753f0..d6ab8c7963 100644 --- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustHealth.cs +++ b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustHealth.cs @@ -1,4 +1,5 @@ -using Content.Shared.Chemistry.Reagent; +using Content.Server.Botany.Systems; +using Content.Shared.Chemistry.Reagent; namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism { @@ -9,8 +10,10 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager)) return; + var plantHolder = args.EntityManager.System(); + plantHolderComp.Health += Amount; - plantHolderComp.CheckHealth(); + plantHolder.CheckHealth(args.SolutionEntity, plantHolderComp); } } } diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustNutrition.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustNutrition.cs index 4b85c7f002..9d408e3b85 100644 --- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustNutrition.cs +++ b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustNutrition.cs @@ -1,4 +1,5 @@ -using Content.Shared.Chemistry.Reagent; +using Content.Server.Botany.Systems; +using Content.Shared.Chemistry.Reagent; using JetBrains.Annotations; namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism @@ -11,8 +12,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager, mustHaveAlivePlant: false)) return; - plantHolderComp.AdjustNutrient(Amount); - return; + var plantHolder = args.EntityManager.System(); + + plantHolder.AdjustNutrient(args.SolutionEntity, Amount, plantHolderComp); } } } diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustWater.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustWater.cs index b2856330c1..2f4b8d9927 100644 --- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustWater.cs +++ b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAdjustWater.cs @@ -1,4 +1,5 @@ -using Content.Shared.Chemistry.Reagent; +using Content.Server.Botany.Systems; +using Content.Shared.Chemistry.Reagent; using JetBrains.Annotations; namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism @@ -11,7 +12,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager, mustHaveAlivePlant: false)) return; - plantHolderComp.AdjustWater(Amount); + var plantHolder = args.EntityManager.System(); + + plantHolder.AdjustWater(args.SolutionEntity, Amount, plantHolderComp); } } } diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAffectGrowth.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAffectGrowth.cs index 8fa030dd62..32ff4d717a 100644 --- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAffectGrowth.cs +++ b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantAffectGrowth.cs @@ -1,4 +1,5 @@ -using Content.Shared.Chemistry.Reagent; +using Content.Server.Botany.Systems; +using Content.Shared.Chemistry.Reagent; using JetBrains.Annotations; namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism @@ -11,7 +12,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager)) return; - plantHolderComp.AffectGrowth((int) Amount); + var plantHolder = args.EntityManager.System(); + + plantHolder.AffectGrowth(args.SolutionEntity, (int) Amount, plantHolderComp); } } } diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantDiethylamine.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantDiethylamine.cs index 4cf01b63df..b913bd2270 100644 --- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantDiethylamine.cs +++ b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/PlantDiethylamine.cs @@ -1,4 +1,5 @@ using Content.Server.Botany.Components; +using Content.Server.Botany.Systems; using Content.Shared.Chemistry.Reagent; using JetBrains.Annotations; using Robust.Shared.Random; @@ -16,17 +17,20 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism plantHolderComp.Seed.Immutable) return; + + var plantHolder = args.EntityManager.System(); + var random = IoCManager.Resolve(); if (random.Prob(0.1f)) { - plantHolderComp.EnsureUniqueSeed(); + plantHolder.EnsureUniqueSeed(args.SolutionEntity, plantHolderComp); plantHolderComp.Seed.Lifespan++; } if (random.Prob(0.1f)) { - plantHolderComp.EnsureUniqueSeed(); + plantHolder.EnsureUniqueSeed(args.SolutionEntity, plantHolderComp); plantHolderComp.Seed.Endurance++; } } diff --git a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/RobustHarvest.cs b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/RobustHarvest.cs index bc3d985a50..a13edcad5a 100644 --- a/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/RobustHarvest.cs +++ b/Content.Server/Chemistry/ReagentEffects/PlantMetabolism/RobustHarvest.cs @@ -1,4 +1,5 @@ using Content.Server.Botany.Components; +using Content.Server.Botany.Systems; using Content.Shared.Chemistry.Reagent; using JetBrains.Annotations; using Robust.Shared.Random; @@ -25,11 +26,13 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism plantHolderComp.Seed.Immutable) return; + + var plantHolder = args.EntityManager.System(); var random = IoCManager.Resolve(); if (plantHolderComp.Seed.Potency < PotencyLimit) { - plantHolderComp.EnsureUniqueSeed(); + plantHolder.EnsureUniqueSeed(args.SolutionEntity, plantHolderComp); plantHolderComp.Seed.Potency = Math.Min(plantHolderComp.Seed.Potency + PotencyIncrease, PotencyLimit); if (plantHolderComp.Seed.Potency > PotencySeedlessThreshold) @@ -40,7 +43,7 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism else if (plantHolderComp.Seed.Yield > 1 && random.Prob(0.1f)) { // Too much of a good thing reduces yield - plantHolderComp.EnsureUniqueSeed(); + plantHolder.EnsureUniqueSeed(args.SolutionEntity, plantHolderComp); plantHolderComp.Seed.Yield--; } }