From dfc54ce04ae6655f512981f5807bb5bab4562fb7 Mon Sep 17 00:00:00 2001 From: PraxisMapper Date: Tue, 17 Sep 2024 23:31:25 -0400 Subject: [PATCH 01/53] Restarting files from scratch --- .../Botany/Components/AgeGrowthComponent.cs | 5 + .../Components/AutoHarvestGrowthComponent.cs | 5 + .../Botany/Components/BotanySwabComponent.cs | 32 +- .../Components/ConsumeGasGrowthComponent.cs | 9 + .../Components/ExudeGasGrowthComponent .cs | 9 + .../Components/NutrientGrowthComponent.cs | 8 + .../Botany/Components/PlantComponent.cs | 5 + .../Botany/Components/PlantGrowthComponent.cs | 5 + .../Botany/Components/PlantHolderComponent.cs | 3 - .../Components/PressureGrowthComponent.cs | 11 + .../Botany/Components/SeedComponent.cs | 43 +- .../Components/TemperatureGrowthComponent.cs | 11 + .../Components/UnviableGrowthComponent.cs | 5 + .../Botany/Components/WaterGrowthComponent.cs | 8 + .../Components/WeedPestGrowthComponent.cs | 11 + Content.Server/Botany/SeedPrototype.cs | 130 +--- .../Botany/Systems/AgeGrowthSystem.cs | 72 ++ .../Botany/Systems/AutoHarvestGrowthSystem.cs | 23 + .../Botany/Systems/BotanySwabSystem.cs | 22 + .../Botany/Systems/BotanySystem.Seed.cs | 2 +- .../Botany/Systems/ConsumeGasGrowthSystem.cs | 47 ++ .../Botany/Systems/ExudeGasGrowthSystem.cs | 36 + .../Botany/Systems/MutationSystem.cs | 14 - .../Botany/Systems/NutrientGrowthSystem.cs | 44 ++ .../Botany/Systems/PlantGrowthSystem.cs | 91 +++ .../Botany/Systems/PlantHolderSystem.cs | 256 +------ .../Botany/Systems/PressureGrowthSystem.cs | 40 + .../Botany/Systems/TemperatureGrowthSystem.cs | 40 + .../Botany/Systems/UnviableGrowthSystem.cs | 22 + .../Botany/Systems/WaterGrowthSystem.cs | 44 ++ .../Botany/Systems/WeedPestGrowthSystem.cs | 37 + .../PlantMetabolism/PlantPhalanximine.cs | 2 +- .../EntityEffects/Effects/PlantMutateGases.cs | 23 +- Resources/Maps/Salvage/vegan-meatball.yml | 6 - Resources/Prototypes/Hydroponics/seeds.yml | 708 +++++++++++++++--- 35 files changed, 1302 insertions(+), 527 deletions(-) create mode 100644 Content.Server/Botany/Components/AgeGrowthComponent.cs create mode 100644 Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs create mode 100644 Content.Server/Botany/Components/ConsumeGasGrowthComponent.cs create mode 100644 Content.Server/Botany/Components/ExudeGasGrowthComponent .cs create mode 100644 Content.Server/Botany/Components/NutrientGrowthComponent.cs create mode 100644 Content.Server/Botany/Components/PlantComponent.cs create mode 100644 Content.Server/Botany/Components/PlantGrowthComponent.cs create mode 100644 Content.Server/Botany/Components/PressureGrowthComponent.cs create mode 100644 Content.Server/Botany/Components/TemperatureGrowthComponent.cs create mode 100644 Content.Server/Botany/Components/UnviableGrowthComponent.cs create mode 100644 Content.Server/Botany/Components/WaterGrowthComponent.cs create mode 100644 Content.Server/Botany/Components/WeedPestGrowthComponent.cs create mode 100644 Content.Server/Botany/Systems/AgeGrowthSystem.cs create mode 100644 Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs create mode 100644 Content.Server/Botany/Systems/ConsumeGasGrowthSystem.cs create mode 100644 Content.Server/Botany/Systems/ExudeGasGrowthSystem.cs create mode 100644 Content.Server/Botany/Systems/NutrientGrowthSystem.cs create mode 100644 Content.Server/Botany/Systems/PlantGrowthSystem.cs create mode 100644 Content.Server/Botany/Systems/PressureGrowthSystem.cs create mode 100644 Content.Server/Botany/Systems/TemperatureGrowthSystem.cs create mode 100644 Content.Server/Botany/Systems/UnviableGrowthSystem.cs create mode 100644 Content.Server/Botany/Systems/WaterGrowthSystem.cs create mode 100644 Content.Server/Botany/Systems/WeedPestGrowthSystem.cs diff --git a/Content.Server/Botany/Components/AgeGrowthComponent.cs b/Content.Server/Botany/Components/AgeGrowthComponent.cs new file mode 100644 index 00000000000..4c7778b702e --- /dev/null +++ b/Content.Server/Botany/Components/AgeGrowthComponent.cs @@ -0,0 +1,5 @@ +namespace Content.Server.Botany.Components; + +[RegisterComponent] +public sealed partial class AgeGrowthComponent : PlantGrowthComponent +{} diff --git a/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs b/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs new file mode 100644 index 00000000000..9d468cc789c --- /dev/null +++ b/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs @@ -0,0 +1,5 @@ +namespace Content.Server.Botany.Components; + +[RegisterComponent] +public sealed partial class AutoHarvestGrowthComponent : PlantGrowthComponent +{} diff --git a/Content.Server/Botany/Components/BotanySwabComponent.cs b/Content.Server/Botany/Components/BotanySwabComponent.cs index 5fad5764b41..91508996c2d 100644 --- a/Content.Server/Botany/Components/BotanySwabComponent.cs +++ b/Content.Server/Botany/Components/BotanySwabComponent.cs @@ -1,19 +1,21 @@ -using System.Threading; +using Content.Server.Botany.Components; -namespace Content.Server.Botany +namespace Content.Server.Botany; + +/// +/// Anything that can be used to cross-pollinate plants. +/// +[RegisterComponent] +public sealed partial class BotanySwabComponent : Component { - /// - /// Anything that can be used to cross-pollinate plants. - /// - [RegisterComponent] - public sealed partial class BotanySwabComponent : Component - { - [DataField("swabDelay")] - public float SwabDelay = 2f; + [DataField("swabDelay")] + public float SwabDelay = 2f; - /// - /// SeedData from the first plant that got swabbed. - /// - public SeedData? SeedData; - } + /// + /// SeedData from the first plant that got swabbed. + /// + public SeedData? SeedData; + + //data from first plant. + public List components; } diff --git a/Content.Server/Botany/Components/ConsumeGasGrowthComponent.cs b/Content.Server/Botany/Components/ConsumeGasGrowthComponent.cs new file mode 100644 index 00000000000..a3ee859160e --- /dev/null +++ b/Content.Server/Botany/Components/ConsumeGasGrowthComponent.cs @@ -0,0 +1,9 @@ +using Content.Shared.Atmos; + +namespace Content.Server.Botany.Components; + +[RegisterComponent] +public sealed partial class ConsumeGasGrowthComponent : PlantGrowthComponent +{ + [DataField] public Dictionary ConsumeGasses = new(); +} diff --git a/Content.Server/Botany/Components/ExudeGasGrowthComponent .cs b/Content.Server/Botany/Components/ExudeGasGrowthComponent .cs new file mode 100644 index 00000000000..280c7988236 --- /dev/null +++ b/Content.Server/Botany/Components/ExudeGasGrowthComponent .cs @@ -0,0 +1,9 @@ +using Content.Shared.Atmos; + +namespace Content.Server.Botany.Components; + +[RegisterComponent] +public sealed partial class ExudeGasGrowthComponent : PlantGrowthComponent +{ + [DataField] public Dictionary ExudeGasses = new(); +} diff --git a/Content.Server/Botany/Components/NutrientGrowthComponent.cs b/Content.Server/Botany/Components/NutrientGrowthComponent.cs new file mode 100644 index 00000000000..bcc14ede195 --- /dev/null +++ b/Content.Server/Botany/Components/NutrientGrowthComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Server.Botany.Components; + +[RegisterComponent] +public sealed partial class NutrientGrowthComponent : PlantGrowthComponent +{ + [DataField] + public float NutrientConsumption = 0.75f; +} diff --git a/Content.Server/Botany/Components/PlantComponent.cs b/Content.Server/Botany/Components/PlantComponent.cs new file mode 100644 index 00000000000..b77edc964fe --- /dev/null +++ b/Content.Server/Botany/Components/PlantComponent.cs @@ -0,0 +1,5 @@ +namespace Content.Server.Botany.Components; + +[RegisterComponent] +public sealed partial class PlantComponent : PlantGrowthComponent +{} diff --git a/Content.Server/Botany/Components/PlantGrowthComponent.cs b/Content.Server/Botany/Components/PlantGrowthComponent.cs new file mode 100644 index 00000000000..df11fc745a8 --- /dev/null +++ b/Content.Server/Botany/Components/PlantGrowthComponent.cs @@ -0,0 +1,5 @@ +namespace Content.Server.Botany.Components; + +[RegisterComponent] +public abstract partial class PlantGrowthComponent : Component +{ } diff --git a/Content.Server/Botany/Components/PlantHolderComponent.cs b/Content.Server/Botany/Components/PlantHolderComponent.cs index f0661e4a301..21444beb8a6 100644 --- a/Content.Server/Botany/Components/PlantHolderComponent.cs +++ b/Content.Server/Botany/Components/PlantHolderComponent.cs @@ -87,9 +87,6 @@ public sealed partial class PlantHolderComponent : Component [DataField] public bool ImproperPressure; - [DataField] - public bool ImproperLight; - [DataField] public bool ForceUpdate; diff --git a/Content.Server/Botany/Components/PressureGrowthComponent.cs b/Content.Server/Botany/Components/PressureGrowthComponent.cs new file mode 100644 index 00000000000..227f1dbcaaf --- /dev/null +++ b/Content.Server/Botany/Components/PressureGrowthComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Server.Botany.Components; + +[RegisterComponent] +public sealed partial class PressureGrowthComponent : PlantGrowthComponent +{ + [DataField] + public float LowPressureTolerance = 81f; + + [DataField] + public float HighPressureTolerance = 121f; +} diff --git a/Content.Server/Botany/Components/SeedComponent.cs b/Content.Server/Botany/Components/SeedComponent.cs index ffa1b7ef4e9..b56361146bb 100644 --- a/Content.Server/Botany/Components/SeedComponent.cs +++ b/Content.Server/Botany/Components/SeedComponent.cs @@ -2,29 +2,28 @@ using Content.Server.Botany.Systems; using Content.Shared.Botany.Components; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -namespace Content.Server.Botany.Components +namespace Content.Server.Botany.Components; + +[RegisterComponent, Access(typeof(BotanySystem))] +public sealed partial class SeedComponent : SharedSeedComponent { - [RegisterComponent, Access(typeof(BotanySystem))] - public sealed partial class SeedComponent : SharedSeedComponent - { - /// - /// Seed data containing information about the plant type & properties that this seed can grow seed. If - /// null, will instead attempt to get data from a seed prototype, if one is defined. See . - /// - [DataField("seed")] - public SeedData? Seed; + /// + /// Seed data containing information about the plant type & properties that this seed can grow seed. If + /// null, will instead attempt to get data from a seed prototype, if one is defined. See . + /// + [DataField("seed")] + public SeedData? Seed; - /// - /// If not null, overrides the plant's initial health. Otherwise, the plant's initial health is set to the Endurance value. - /// - [DataField] - public float? HealthOverride = null; + /// + /// If not null, overrides the plant's initial health. Otherwise, the plant's initial health is set to the Endurance value. + /// + [DataField] + public float? HealthOverride = null; - /// - /// Name of a base seed prototype that is used if is null. - /// - [DataField("seedId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? SeedId; - } + /// + /// Name of a base seed prototype that is used if is null. + /// + [DataField("seedId", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string? SeedId; } diff --git a/Content.Server/Botany/Components/TemperatureGrowthComponent.cs b/Content.Server/Botany/Components/TemperatureGrowthComponent.cs new file mode 100644 index 00000000000..7fcf712d843 --- /dev/null +++ b/Content.Server/Botany/Components/TemperatureGrowthComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Server.Botany.Components; + +[RegisterComponent] +public sealed partial class TemperatureGrowthComponent : PlantGrowthComponent +{ + [DataField] + public float IdealHeat = 293f; + + [DataField] + public float HeatTolerance = 10f; +} diff --git a/Content.Server/Botany/Components/UnviableGrowthComponent.cs b/Content.Server/Botany/Components/UnviableGrowthComponent.cs new file mode 100644 index 00000000000..c7fb0b45f86 --- /dev/null +++ b/Content.Server/Botany/Components/UnviableGrowthComponent.cs @@ -0,0 +1,5 @@ +namespace Content.Server.Botany.Components; + +[RegisterComponent] +public sealed partial class UnviableGrowthComponent : PlantGrowthComponent +{} diff --git a/Content.Server/Botany/Components/WaterGrowthComponent.cs b/Content.Server/Botany/Components/WaterGrowthComponent.cs new file mode 100644 index 00000000000..b073d0cc881 --- /dev/null +++ b/Content.Server/Botany/Components/WaterGrowthComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Server.Botany.Components; + +[RegisterComponent] +public sealed partial class WaterGrowthComponent : PlantGrowthComponent +{ + [DataField] + public float WaterConsumption = 0.5f; +} diff --git a/Content.Server/Botany/Components/WeedPestGrowthComponent.cs b/Content.Server/Botany/Components/WeedPestGrowthComponent.cs new file mode 100644 index 00000000000..b3b63ce8902 --- /dev/null +++ b/Content.Server/Botany/Components/WeedPestGrowthComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Server.Botany.Components; + +[RegisterComponent] +public sealed partial class WeedPestGrowthComponent : PlantGrowthComponent +{ + [DataField] + public float WeedTolerance = 5f; + + [DataField] + public float PestTolerance = 5f; +} diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index 5608338f22e..6bbe83539cb 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -1,6 +1,5 @@ using Content.Server.Botany.Components; using Content.Server.Botany.Systems; -using Content.Shared.Atmos; using Content.Shared.EntityEffects; using Content.Shared.Random; using Robust.Shared.Audio; @@ -24,36 +23,6 @@ public enum HarvestType : byte SelfHarvest } -/* - public enum PlantSpread : byte - { - NoSpread, - Creepers, - Vines, - } - - public enum PlantMutation : byte - { - NoMutation, - Mutable, - HighlyMutable, - } - - public enum PlantCarnivorous : byte - { - NotCarnivorous, - EatPests, - EatLivingBeings, - } - - public enum PlantJuicy : byte - { - NotJuicy, - Juicy, - Slippery, - } -*/ - [DataDefinition] public partial struct SeedChemQuantity { @@ -81,7 +50,7 @@ public partial struct SeedChemQuantity // TODO reduce the number of friends to a reasonable level. Requires ECS-ing things like plant holder component. [Virtual, DataDefinition] -[Access(typeof(BotanySystem), typeof(PlantHolderSystem), typeof(SeedExtractorSystem), typeof(PlantHolderComponent), typeof(EntityEffect), typeof(MutationSystem))] +[Access(typeof(BotanySystem), typeof(PlantHolderSystem), typeof(SeedExtractorSystem), typeof(PlantHolderComponent), typeof(EntityEffect), typeof(MutationSystem), typeof(AgeGrowthSystem))] public partial class SeedData { #region Tracking @@ -89,28 +58,28 @@ public partial class SeedData /// /// The name of this seed. Determines the name of seed packets. /// - [DataField("name")] + [DataField] public string Name { get; private set; } = ""; /// /// The noun for this type of seeds. E.g. for fungi this should probably be "spores" instead of "seeds". Also /// used to determine the name of seed packets. /// - [DataField("noun")] + [DataField] public string Noun { get; private set; } = ""; /// /// Name displayed when examining the hydroponics tray. Describes the actual plant, not the seed itself. /// - [DataField("displayName")] + [DataField] public string DisplayName { get; private set; } = ""; - [DataField("mysterious")] public bool Mysterious; + [DataField] public bool Mysterious; /// /// If true, the properties of this seed cannot be modified. /// - [DataField("immutable")] public bool Immutable; + [DataField] public bool Immutable; /// /// If true, there is only a single reference to this seed and it's properties can be directly modified without @@ -124,44 +93,22 @@ public partial class SeedData /// /// The entity prototype that is spawned when this type of seed is extracted from produce using a seed extractor. /// - [DataField("packetPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string PacketPrototype = "SeedBase"; /// /// The entity prototype this seed spawns when it gets harvested. /// - [DataField("productPrototypes", customTypeSerializer: typeof(PrototypeIdListSerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdListSerializer))] public List ProductPrototypes = new(); [DataField] public Dictionary Chemicals = new(); - [DataField] public Dictionary ConsumeGasses = new(); - - [DataField] public Dictionary ExudeGasses = new(); - #endregion #region Tolerances - - [DataField] public float NutrientConsumption = 0.75f; - - [DataField] public float WaterConsumption = 0.5f; - [DataField] public float IdealHeat = 293f; - [DataField] public float HeatTolerance = 10f; - [DataField] public float IdealLight = 7f; - [DataField] public float LightTolerance = 3f; [DataField] public float ToxinsTolerance = 4f; - [DataField] public float LowPressureTolerance = 81f; - - [DataField] public float HighPressureTolerance = 121f; - - [DataField] public float PestTolerance = 5f; - - [DataField] public float WeedTolerance = 5f; - - [DataField] public float WeedHighLevelThreshold = 10f; - #endregion #region General traits @@ -184,30 +131,11 @@ public partial class SeedData /// [DataField] public bool Seedless = false; - /// - /// If false, rapidly decrease health while growing. Used to kill off - /// plants with "bad" mutations. - /// - [DataField] public bool Viable = true; - /// /// If true, a sharp tool is required to harvest this plant. /// [DataField] public bool Ligneous; - // No, I'm not removing these. - // if you re-add these, make sure that they get cloned. - //public PlantSpread Spread { get; set; } - //public PlantMutation Mutation { get; set; } - //public float AlterTemperature { get; set; } - //public PlantCarnivorous Carnivorous { get; set; } - //public bool Parasite { get; set; } - //public bool Hematophage { get; set; } - //public bool Thorny { get; set; } - //public bool Stinging { get; set; } - // public bool Teleporting { get; set; } - // public PlantJuicy Juicy { get; set; } - #endregion #region Cosmetics @@ -243,6 +171,22 @@ public partial class SeedData [DataField(customTypeSerializer: typeof(PrototypeIdListSerializer))] public List MutationPrototypes = new(); + /// + /// The growth components used by this seed. + /// + [DataField] + public List GrowthComponents = new() { + new PlantComponent(), + new WaterGrowthComponent(), + new NutrientGrowthComponent(), + new AgeGrowthComponent(), + new PressureGrowthComponent(), + new TemperatureGrowthComponent(), + new WeedPestGrowthComponent(), + }; + //TODO: the mutation system should add the missing components when they mutate. + //This would be done with EnsureComp<> + public SeedData Clone() { DebugTools.Assert(!Immutable, "There should be no need to clone an immutable seed."); @@ -258,20 +202,8 @@ public partial class SeedData ProductPrototypes = new List(ProductPrototypes), MutationPrototypes = new List(MutationPrototypes), Chemicals = new Dictionary(Chemicals), - ConsumeGasses = new Dictionary(ConsumeGasses), - ExudeGasses = new Dictionary(ExudeGasses), - NutrientConsumption = NutrientConsumption, - WaterConsumption = WaterConsumption, - IdealHeat = IdealHeat, - HeatTolerance = HeatTolerance, - IdealLight = IdealLight, - LightTolerance = LightTolerance, ToxinsTolerance = ToxinsTolerance, - LowPressureTolerance = LowPressureTolerance, - HighPressureTolerance = HighPressureTolerance, - PestTolerance = PestTolerance, - WeedTolerance = WeedTolerance, Endurance = Endurance, Yield = Yield, @@ -283,7 +215,6 @@ public partial class SeedData Potency = Potency, Seedless = Seedless, - Viable = Viable, Ligneous = Ligneous, PlantRsi = PlantRsi, @@ -319,20 +250,8 @@ public partial class SeedData MutationPrototypes = new List(other.MutationPrototypes), Chemicals = new Dictionary(Chemicals), - ConsumeGasses = new Dictionary(ConsumeGasses), - ExudeGasses = new Dictionary(ExudeGasses), - NutrientConsumption = NutrientConsumption, - WaterConsumption = WaterConsumption, - IdealHeat = IdealHeat, - HeatTolerance = HeatTolerance, - IdealLight = IdealLight, - LightTolerance = LightTolerance, ToxinsTolerance = ToxinsTolerance, - LowPressureTolerance = LowPressureTolerance, - HighPressureTolerance = HighPressureTolerance, - PestTolerance = PestTolerance, - WeedTolerance = WeedTolerance, Endurance = Endurance, Yield = Yield, @@ -346,7 +265,6 @@ public partial class SeedData Mutations = Mutations, Seedless = Seedless, - Viable = Viable, Ligneous = Ligneous, PlantRsi = other.PlantRsi, diff --git a/Content.Server/Botany/Systems/AgeGrowthSystem.cs b/Content.Server/Botany/Systems/AgeGrowthSystem.cs new file mode 100644 index 00000000000..22e42ba3006 --- /dev/null +++ b/Content.Server/Botany/Systems/AgeGrowthSystem.cs @@ -0,0 +1,72 @@ +using Content.Server.Botany.Components; +using Robust.Shared.Random; + +namespace Content.Server.Botany.Systems; +public sealed class AgeGrowthSystem : PlantGrowthSystem +{ + [Dependency] private readonly BotanySystem _botany = default!; + [Dependency] private readonly PlantHolderSystem _plantHolderSystem = default!; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); + } + + private void OnPlantGrow(EntityUid uid, AgeGrowthComponent component, OnPlantGrowEvent args) + { + PlantHolderComponent? holder = null; + Resolve(uid, ref holder); + + if (holder == null || holder.Seed == null || holder.Dead) + return; + + // Advance plant age here. + if (holder.SkipAging > 0) + holder.SkipAging--; + else + { + if (_random.Prob(0.8f)) + { + holder.Age += (int)(1 * HydroponicsSpeedMultiplier); + holder.UpdateSpriteAfterUpdate = true; + } + } + + if (holder.Age > holder.Seed.Lifespan) + { + holder.Health -= _random.Next(3, 5) * HydroponicsSpeedMultiplier; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + else if (holder.Age < 0) // Revert back to seed packet! + { + var packetSeed = holder.Seed; + // will put it in the trays hands if it has any, please do not try doing this + _botany.SpawnSeedPacket(packetSeed, Transform(uid).Coordinates, uid); + _plantHolderSystem.RemovePlant(uid, holder); + holder.ForceUpdate = true; + _plantHolderSystem.Update(uid, holder); + } + + // If enough time has passed since the plant was harvested, we're ready to harvest again! + if (holder.Seed.ProductPrototypes.Count > 0) + { + if (holder.Age > holder.Seed.Production) + { + if (holder.Age - holder.LastProduce > holder.Seed.Production && !holder.Harvest) + { + holder.Harvest = true; + holder.LastProduce = holder.Age; + } + } + else + { + if (holder.Harvest) + { + holder.Harvest = false; + holder.LastProduce = holder.Age; + } + } + } + } +} diff --git a/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs b/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs new file mode 100644 index 00000000000..f589e3f69a7 --- /dev/null +++ b/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs @@ -0,0 +1,23 @@ +using Content.Server.Botany.Components; + +namespace Content.Server.Botany.Systems; +public sealed class AutoHarvestGrowthSystem : PlantGrowthSystem +{ + [Dependency] private readonly PlantHolderSystem _plantHolderSystem = default!; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); + } + + private void OnPlantGrow(EntityUid uid, AutoHarvestGrowthComponent component, OnPlantGrowEvent args) + { + PlantHolderComponent? holder = null; + Resolve(uid, ref holder); + + if (holder == null || holder.Seed == null || holder.Dead || !holder.Harvest) + return; + + _plantHolderSystem.AutoHarvest(uid, holder); + } +} diff --git a/Content.Server/Botany/Systems/BotanySwabSystem.cs b/Content.Server/Botany/Systems/BotanySwabSystem.cs index e8c7af92c27..7b75b8db59f 100644 --- a/Content.Server/Botany/Systems/BotanySwabSystem.cs +++ b/Content.Server/Botany/Systems/BotanySwabSystem.cs @@ -4,6 +4,9 @@ using Content.Shared.DoAfter; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Swab; +using Robust.Shared.Random; +using Robust.Shared.Serialization.Manager; +using System.Linq; namespace Content.Server.Botany.Systems; @@ -12,6 +15,8 @@ public sealed class BotanySwabSystem : EntitySystem [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly MutationSystem _mutationSystem = default!; + [Dependency] private readonly ISerializationManager _serializationManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { @@ -64,6 +69,9 @@ public sealed class BotanySwabSystem : EntitySystem { // Pick up pollen swab.SeedData = plant.Seed; + if (plant.Seed != null) + swab.components = plant.Seed.GrowthComponents.ToList(); //copy list in case plant changes after sampling + _popupSystem.PopupEntity(Loc.GetString("botany-swab-from"), args.Args.Target.Value, args.Args.User); } else @@ -71,6 +79,20 @@ public sealed class BotanySwabSystem : EntitySystem var old = plant.Seed; if (old == null) return; + + for (int index = 0; index < old.GrowthComponents.Count(); index++) + { + var c1 = old.GrowthComponents[index]; + foreach (var c2 in swab.components) + { + if (c1.GetType() == c2.GetType()) + { + if (_random.Prob(0.5f)) + _serializationManager.CopyTo(c2, ref c1, notNullableOverride: true); + } + } + } + plant.Seed = _mutationSystem.Cross(swab.SeedData, old); // Cross-pollenate swab.SeedData = old; // Transfer old plant pollen to swab _popupSystem.PopupEntity(Loc.GetString("botany-swab-to"), args.Args.Target.Value, args.Args.User); diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index 1487ed71d47..dc4847d750c 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -49,7 +49,7 @@ public sealed partial class BotanySystem : EntitySystem if (comp.SeedId != null && _prototypeManager.TryIndex(comp.SeedId, out SeedPrototype? protoSeed)) { - seed = protoSeed; + seed = protoSeed.Clone(); return true; } diff --git a/Content.Server/Botany/Systems/ConsumeGasGrowthSystem.cs b/Content.Server/Botany/Systems/ConsumeGasGrowthSystem.cs new file mode 100644 index 00000000000..63d79e31644 --- /dev/null +++ b/Content.Server/Botany/Systems/ConsumeGasGrowthSystem.cs @@ -0,0 +1,47 @@ +using Content.Server.Atmos.EntitySystems; +using Content.Server.Botany.Components; +using Content.Shared.Atmos; + +namespace Content.Server.Botany.Systems; +public sealed class ConsumeGasGrowthSystem : PlantGrowthSystem +{ + [Dependency] private readonly AtmosphereSystem _atmosphere = default!; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); + } + + private void OnPlantGrow(EntityUid uid, ConsumeGasGrowthComponent component, OnPlantGrowEvent args) + { + PlantHolderComponent? holder = null; + Resolve(uid, ref holder); + + if (holder == null || holder.Seed == null || holder.Dead) + return; + + var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; + + holder.MissingGas = 0; + if (component.ConsumeGasses.Count > 0) + { + foreach (var (gas, amount) in component.ConsumeGasses) + { + if (environment.GetMoles(gas) < amount) + { + holder.MissingGas++; + continue; + } + + environment.AdjustMoles(gas, -amount); + } + + if (holder.MissingGas > 0) + { + holder.Health -= holder.MissingGas * HydroponicsSpeedMultiplier; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + } + } +} diff --git a/Content.Server/Botany/Systems/ExudeGasGrowthSystem.cs b/Content.Server/Botany/Systems/ExudeGasGrowthSystem.cs new file mode 100644 index 00000000000..b33d945f58f --- /dev/null +++ b/Content.Server/Botany/Systems/ExudeGasGrowthSystem.cs @@ -0,0 +1,36 @@ +using Content.Server.Atmos.EntitySystems; +using Content.Server.Botany.Components; +using Content.Shared.Atmos; + +namespace Content.Server.Botany.Systems; +public sealed class ExudeGasGrowthSystem : PlantGrowthSystem +{ + [Dependency] private readonly AtmosphereSystem _atmosphere = default!; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); + } + + private void OnPlantGrow(EntityUid uid, ExudeGasGrowthComponent component, OnPlantGrowEvent args) + { + PlantHolderComponent? holder = null; + Resolve(uid, ref holder); + + if (holder == null || holder.Seed == null || holder.Dead) + return; + + var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; + + // Gas production. + var exudeCount = component.ExudeGasses.Count; + if (exudeCount > 0) + { + foreach (var (gas, amount) in component.ExudeGasses) + { + environment.AdjustMoles(gas, + MathF.Max(1f, MathF.Round(amount * MathF.Round(holder.Seed.Potency) / exudeCount))); + } + } + } +} diff --git a/Content.Server/Botany/Systems/MutationSystem.cs b/Content.Server/Botany/Systems/MutationSystem.cs index 37b68d9475f..8c559d00c71 100644 --- a/Content.Server/Botany/Systems/MutationSystem.cs +++ b/Content.Server/Botany/Systems/MutationSystem.cs @@ -61,17 +61,7 @@ public sealed class MutationSystem : EntitySystem CrossChemicals(ref result.Chemicals, a.Chemicals); - CrossFloat(ref result.NutrientConsumption, a.NutrientConsumption); - CrossFloat(ref result.WaterConsumption, a.WaterConsumption); - CrossFloat(ref result.IdealHeat, a.IdealHeat); - CrossFloat(ref result.HeatTolerance, a.HeatTolerance); - CrossFloat(ref result.IdealLight, a.IdealLight); - CrossFloat(ref result.LightTolerance, a.LightTolerance); CrossFloat(ref result.ToxinsTolerance, a.ToxinsTolerance); - CrossFloat(ref result.LowPressureTolerance, a.LowPressureTolerance); - CrossFloat(ref result.HighPressureTolerance, a.HighPressureTolerance); - CrossFloat(ref result.PestTolerance, a.PestTolerance); - CrossFloat(ref result.WeedTolerance, a.WeedTolerance); CrossFloat(ref result.Endurance, a.Endurance); CrossInt(ref result.Yield, a.Yield); @@ -83,10 +73,6 @@ public sealed class MutationSystem : EntitySystem CrossBool(ref result.Seedless, a.Seedless); CrossBool(ref result.Ligneous, a.Ligneous); CrossBool(ref result.TurnIntoKudzu, a.TurnIntoKudzu); - CrossBool(ref result.CanScream, a.CanScream); - - CrossGasses(ref result.ExudeGasses, a.ExudeGasses); - CrossGasses(ref result.ConsumeGasses, a.ConsumeGasses); // LINQ Explanation // For the list of mutation effects on both plants, use a 50% chance to pick each one. diff --git a/Content.Server/Botany/Systems/NutrientGrowthSystem.cs b/Content.Server/Botany/Systems/NutrientGrowthSystem.cs new file mode 100644 index 00000000000..0be2d099e2b --- /dev/null +++ b/Content.Server/Botany/Systems/NutrientGrowthSystem.cs @@ -0,0 +1,44 @@ +using Content.Server.Botany.Components; +using Robust.Shared.Random; + +namespace Content.Server.Botany.Systems; +public sealed class NutrientGrowthSystem : PlantGrowthSystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); + } + + private void OnPlantGrow(EntityUid uid, NutrientGrowthComponent component, OnPlantGrowEvent args) + { + PlantHolderComponent? holder = null; + Resolve(uid, ref holder); + + if (holder == null || holder.Seed == null || holder.Dead) + return; + + if (component.NutrientConsumption > 0 && holder.NutritionLevel > 0 && _random.Prob(0.75f)) + { + holder.NutritionLevel -= MathF.Max(0f, + component.NutrientConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + + var healthMod = _random.Next(1, 3) * HydroponicsSpeedMultiplier; + if (holder.SkipAging < 10) + { + // Make sure the plant is not hungry + if (holder.NutritionLevel > 5) + { + holder.Health += Convert.ToInt32(_random.Prob(0.35f)) * healthMod; + } + else + { + AffectGrowth(-1, holder); + holder.Health -= healthMod; + } + } + } +} diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs new file mode 100644 index 00000000000..4351621306d --- /dev/null +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -0,0 +1,91 @@ +using Content.Server.Botany.Components; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server.Botany.Systems; + +[ByRefEvent] +public readonly record struct OnPlantGrowEvent; + +//May need to be PlantSystem, since this looks for PlantComponent. +public abstract class PlantGrowthSystem : EntitySystem +{ + [Dependency] protected readonly IRobustRandom _random = default!; + [Dependency] protected readonly IGameTiming _gameTiming = default!; + + public TimeSpan nextUpdate = TimeSpan.Zero; + public TimeSpan updateDelay = TimeSpan.FromSeconds(15); //PlantHolder has a 15 second delay on cycles, but checks every 3 for sprite updates. + + public const float HydroponicsSpeedMultiplier = 1f; + public const float HydroponicsConsumptionMultiplier = 2f; + + + + public override void Initialize() + { + base.Initialize(); + + //This might be overcomplicating things. Maybe I should just create a new event here and + //have each system listen for that instead? + + + //List subsystems = new List(); + //Dictionary subSystemDict = new Dictionary(); + //foreach (var system in EntityManager.EntitySysManager.GetEntitySystemTypes()) + //{ + // if (system is PlantGrowthSystem && system.Name != "PlantGrowthSystem") + // { + // subsystems.Add(system); + + // //find component by name? + // EntityManager.getco + // } + //} + } + + // TO INVESTIGATE: can I figure out some way here to connect all the plant growth systems and components so + // I don't have to give each one of those their own Update() method that listens for gameticks? + // It would be much less code to do it here once instead of in each new system. + // Maybe even if its forcing it by name, to prove its doable? + + public override void Update(float frameTime) + { + if (nextUpdate > _gameTiming.CurTime) + return; + + //This does not work as a universal check. I do need a baseline PlantComponent. + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var plantComponent)) + { + int a = 1; //I want to see each entity once, not each component once. + //Update(uid, unviableGrowthComponent); + var plantGrow = new OnPlantGrowEvent(); + RaiseLocalEvent(uid, ref plantGrow); + } + nextUpdate = _gameTiming.CurTime + updateDelay; + } + + + + + public void AffectGrowth(int amount, PlantHolderComponent? component = null) + { + if (component == null || 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; + } + } +} diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 34d6a75bf25..efac1314a90 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -2,10 +2,9 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Botany.Components; using Content.Server.Kitchen.Components; using Content.Server.Popups; -using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Atmos; using Content.Shared.Botany; using Content.Shared.Burial.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Coordinates.Helpers; using Content.Shared.Examine; @@ -21,6 +20,7 @@ using Robust.Shared.Audio.Systems; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Serialization.Manager; using Robust.Shared.Timing; namespace Content.Server.Botany.Systems; @@ -39,10 +39,9 @@ public sealed class PlantHolderSystem : EntitySystem [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly RandomHelperSystem _randomHelper = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly ISerializationManager _copier = default!; - - public const float HydroponicsSpeedMultiplier = 1f; - public const float HydroponicsConsumptionMultiplier = 2f; + public const float WeedHighLevelThreshold = 10f; public override void Initialize() { @@ -130,9 +129,6 @@ public sealed class PlantHolderSystem : EntitySystem if (component.Toxins > 40f) args.PushMarkup(Loc.GetString("plant-holder-component-toxins-high-warning")); - if (component.ImproperLight) - args.PushMarkup(Loc.GetString("plant-holder-component-light-improper-warning")); - if (component.ImproperHeat) args.PushMarkup(Loc.GetString("plant-holder-component-heat-improper-warning")); @@ -176,6 +172,9 @@ public sealed class PlantHolderSystem : EntitySystem } component.LastCycle = _gameTiming.CurTime; + foreach(var g in seed.GrowthComponents) + EntityManager.AddComponent(uid, _copier.CreateCopy(g, notNullableOverride: true)); + QueueDel(args.Used); CheckLevelSanity(uid, component); @@ -337,11 +336,6 @@ public sealed class PlantHolderSystem : EntitySystem DoHarvest(entity, args.User, entity.Comp); } - public void WeedInvasion() - { - // TODO - } - public void Update(EntityUid uid, PlantHolderComponent? component = null) { @@ -363,7 +357,7 @@ public sealed class PlantHolderSystem : EntitySystem component.LastCycle = curTime; - // Process mutations + // Process mutations. All plants can mutate, so this stays here. if (component.MutationLevel > 0) { Mutate(uid, Math.Min(component.MutationLevel, 25), component); @@ -371,7 +365,7 @@ public sealed class PlantHolderSystem : EntitySystem component.MutationLevel = 0; } - // Weeds like water and nutrients! They may appear even if there's not a seed planted. + // Weeds like water and nutrients! They may appear even if there's not a seed planted. Isnt connected to the plant, stays here in PlantHolder. if (component.WaterLevel > 10 && component.NutritionLevel > 5) { var chance = 0f; @@ -383,27 +377,20 @@ public sealed class PlantHolderSystem : EntitySystem chance = 0.01f; if (_random.Prob(chance)) - component.WeedLevel += 1 + HydroponicsSpeedMultiplier * component.WeedCoefficient; + component.WeedLevel += 1 + component.WeedCoefficient; // * HydroponicsSpeedMultiplier if (component.DrawWarnings) component.UpdateSpriteAfterUpdate = true; } if (component.Seed != null && component.Seed.TurnIntoKudzu - && component.WeedLevel >= component.Seed.WeedHighLevelThreshold) + && component.WeedLevel >= WeedHighLevelThreshold) { Spawn(component.Seed.KudzuPrototype, Transform(uid).Coordinates.SnapToGrid(EntityManager)); component.Seed.TurnIntoKudzu = false; component.Health = 0; } - // 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) @@ -414,144 +401,6 @@ public sealed class PlantHolderSystem : EntitySystem 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.WaterConsumption * 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; - } - - // Prevents the plant from aging when lacking resources. - // Limits the effect on aging so that when resources are added, the plant starts growing in a reasonable amount of time. - if (component.SkipAging < 10) - { - // Make sure the plant is not starving. - if (component.NutritionLevel > 5) - { - component.Health += Convert.ToInt32(_random.Prob(0.35f)) * healthMod; - } - else - { - AffectGrowth(uid, -1, component); - component.Health -= healthMod; - } - - // Make sure the plant is not thirsty. - if (component.WaterLevel > 10) - { - component.Health += Convert.ToInt32(_random.Prob(0.35f)) * healthMod; - } - else - { - AffectGrowth(uid, -1, component); - component.Health -= healthMod; - } - - if (component.DrawWarnings) - component.UpdateSpriteAfterUpdate = true; - } - - var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; - - component.MissingGas = 0; - if (component.Seed.ConsumeGasses.Count > 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) @@ -567,75 +416,7 @@ public sealed class PlantHolderSystem : EntitySystem 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! - { - var packetSeed = component.Seed; - // will put it in the trays hands if it has any, please do not try doing this - _botany.SpawnSeedPacket(packetSeed, Transform(uid).Coordinates, uid); - RemovePlant(uid, component); - component.ForceUpdate = true; - Update(uid, component); - return; - } - 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.UpdateSpriteAfterUpdate) @@ -763,10 +544,8 @@ public sealed class PlantHolderSystem : EntitySystem component.MutationLevel = 0; component.YieldMod = 1; component.MutationMod = 1; - component.ImproperLight = false; - component.ImproperHeat = false; component.ImproperPressure = false; - component.WeedLevel += 1 * HydroponicsSpeedMultiplier; + component.WeedLevel += 1; // * HydroponicsSpeedMultiplier; component.PestLevel = 0; UpdateSprite(uid, component); } @@ -776,6 +555,10 @@ public sealed class PlantHolderSystem : EntitySystem if (!Resolve(uid, ref component)) return; + if (component.Seed != null) + foreach (var g in component.Seed.GrowthComponents) + EntityManager.RemoveComponent(uid, g); + component.YieldMod = 1; component.MutationMod = 1; component.PestLevel = 0; @@ -785,7 +568,6 @@ public sealed class PlantHolderSystem : EntitySystem component.LastProduce = 0; component.Sampled = false; component.Harvest = false; - component.ImproperLight = false; component.ImproperPressure = false; component.ImproperHeat = false; @@ -868,6 +650,10 @@ public sealed class PlantHolderSystem : EntitySystem { EnsureUniqueSeed(uid, component); _mutation.MutateSeed(uid, ref component.Seed, severity); + + //TODO: this is a temp check to apply autoharvest. New mutation system should handle this correctly. + if (component.Seed.HarvestRepeat == HarvestType.SelfHarvest) + Comp(uid); } } @@ -925,7 +711,7 @@ public sealed class PlantHolderSystem : EntitySystem _appearance.SetData(uid, PlantHolderVisuals.NutritionLight, component.NutritionLevel <= 8, 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); + component.ImproperPressure || component.MissingGas > 0, app); _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, component.Harvest, app); } diff --git a/Content.Server/Botany/Systems/PressureGrowthSystem.cs b/Content.Server/Botany/Systems/PressureGrowthSystem.cs new file mode 100644 index 00000000000..45368672bc3 --- /dev/null +++ b/Content.Server/Botany/Systems/PressureGrowthSystem.cs @@ -0,0 +1,40 @@ +using Content.Server.Atmos.EntitySystems; +using Content.Server.Botany.Components; +using Content.Shared.Atmos; + +namespace Content.Server.Botany.Systems; +public sealed class PressureGrowthSystem : PlantGrowthSystem +{ + [Dependency] private readonly BotanySystem _botany = default!; + [Dependency] private readonly PlantHolderSystem _plantHolderSystem = default!; + [Dependency] private readonly AtmosphereSystem _atmosphere = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); + } + + private void OnPlantGrow(EntityUid uid, PressureGrowthComponent component, OnPlantGrowEvent args) + { + PlantHolderComponent? holder = null; + Resolve(uid, ref holder); + + if (holder == null || holder.Seed == null || holder.Dead) + return; + + var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; + var pressure = environment.Pressure; + if (pressure < component.LowPressureTolerance || pressure > component.HighPressureTolerance) + { + holder.Health -= _random.Next(1, 3); + holder.ImproperPressure = true; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + else + { + holder.ImproperPressure = false; + } + } +} diff --git a/Content.Server/Botany/Systems/TemperatureGrowthSystem.cs b/Content.Server/Botany/Systems/TemperatureGrowthSystem.cs new file mode 100644 index 00000000000..8379ad77475 --- /dev/null +++ b/Content.Server/Botany/Systems/TemperatureGrowthSystem.cs @@ -0,0 +1,40 @@ +using Content.Server.Atmos.EntitySystems; +using Content.Server.Botany.Components; +using Content.Shared.Atmos; + +namespace Content.Server.Botany.Systems; +public sealed class TemperatureGrowthSystem : PlantGrowthSystem +{ + [Dependency] private readonly BotanySystem _botany = default!; + [Dependency] private readonly PlantHolderSystem _plantHolderSystem = default!; + [Dependency] private readonly AtmosphereSystem _atmosphere = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); + } + + private void OnPlantGrow(EntityUid uid, TemperatureGrowthComponent component, OnPlantGrowEvent args) + { + + PlantHolderComponent? holder = null; + Resolve(uid, ref holder); + + if (holder == null || holder.Seed == null || holder.Dead) + return; + + var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; + if (MathF.Abs(environment.Temperature - component.IdealHeat) > component.HeatTolerance) + { + holder.Health -= _random.Next(1, 3); + holder.ImproperHeat = true; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + else + { + holder.ImproperHeat = false; + } + } +} diff --git a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs new file mode 100644 index 00000000000..cd2b8d3d762 --- /dev/null +++ b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs @@ -0,0 +1,22 @@ +using Content.Server.Botany.Components; + +namespace Content.Server.Botany.Systems; +public sealed class UnviableGrowthSystem : PlantGrowthSystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); + } + + private void OnPlantGrow(EntityUid uid, UnviableGrowthComponent component, OnPlantGrowEvent args) + { + PlantHolderComponent? holder = null; + Resolve(uid, ref holder); + + if (holder == null || holder.Seed == null || holder.Dead) + return; + + holder.Health -= 6 * _random.Next(1, 3) * HydroponicsSpeedMultiplier; + } +} diff --git a/Content.Server/Botany/Systems/WaterGrowthSystem.cs b/Content.Server/Botany/Systems/WaterGrowthSystem.cs new file mode 100644 index 00000000000..fd2a64a0a19 --- /dev/null +++ b/Content.Server/Botany/Systems/WaterGrowthSystem.cs @@ -0,0 +1,44 @@ +using Content.Server.Botany.Components; +using Robust.Shared.Random; + +namespace Content.Server.Botany.Systems; +public sealed class WaterGrowthSystem : PlantGrowthSystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); + } + + private void OnPlantGrow(EntityUid uid, WaterGrowthComponent component, OnPlantGrowEvent args) + { + PlantHolderComponent? holder = null; + Resolve(uid, ref holder); + + if (holder == null || holder.Seed == null || holder.Dead) + return; + + if (component.WaterConsumption > 0 && holder.WaterLevel > 0 && _random.Prob(0.75f)) + { + holder.WaterLevel -= MathF.Max(0f, + component.WaterConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + + var healthMod = _random.Next(1, 3) * HydroponicsSpeedMultiplier; + if (holder.SkipAging < 10) + { + // Make sure the plant is not thirsty. + if (holder.WaterLevel > 10) + { + holder.Health += Convert.ToInt32(_random.Prob(0.35f)) * healthMod; + } + else + { + AffectGrowth(-1, holder); + holder.Health -= healthMod; + } + } + } +} diff --git a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs new file mode 100644 index 00000000000..1980f18eb0c --- /dev/null +++ b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs @@ -0,0 +1,37 @@ +using Content.Server.Botany.Components; +using Robust.Shared.Random; + +namespace Content.Server.Botany.Systems; +public sealed class WeedPestGrowthSystem : PlantGrowthSystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); + } + + private void OnPlantGrow(EntityUid uid, WeedPestGrowthComponent component, OnPlantGrowEvent args) + { + PlantHolderComponent? holder = null; + Resolve(uid, ref holder); + + if (holder == null || holder.Seed == null || holder.Dead) + return; + + // There's a small chance the pest population increases. Only happens with plants present. + if (_random.Prob(0.01f)) + { + holder.PestLevel += 0.5f * HydroponicsSpeedMultiplier; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + + // Pest levels. + if (holder.PestLevel > component.PestTolerance) + holder.Health -= HydroponicsSpeedMultiplier; + + // Weed levels. + if (holder.WeedLevel >= component.WeedTolerance) + holder.Health -= HydroponicsSpeedMultiplier; + } +} diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantPhalanximine.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantPhalanximine.cs index 96d98bfbf2e..8a2fa559ca9 100644 --- a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantPhalanximine.cs +++ b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantPhalanximine.cs @@ -16,7 +16,7 @@ public sealed partial class PlantPhalanximine : EntityEffect plantHolderComp.Seed.Immutable) return; - plantHolderComp.Seed.Viable = true; + args.EntityManager.RemoveComponent(args.TargetEntity); } protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-phalanximine", ("chance", Probability)); diff --git a/Content.Server/EntityEffects/Effects/PlantMutateGases.cs b/Content.Server/EntityEffects/Effects/PlantMutateGases.cs index 52b9da3a851..de54677d375 100644 --- a/Content.Server/EntityEffects/Effects/PlantMutateGases.cs +++ b/Content.Server/EntityEffects/Effects/PlantMutateGases.cs @@ -20,24 +20,23 @@ public sealed partial class PlantMutateExudeGasses : EntityEffect public override void Effect(EntityEffectBaseArgs args) { - var plantholder = args.EntityManager.GetComponent(args.TargetEntity); + var gasses = args.EntityManager.GetComponent(args.TargetEntity); - if (plantholder.Seed == null) + if (gasses == null) return; var random = IoCManager.Resolve(); - 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().ToList()); - if (gasses.ContainsKey(gas)) + if (gasses.ExudeGasses.ContainsKey(gas)) { - gasses[gas] += amount; + gasses.ExudeGasses[gas] += amount; } else { - gasses.Add(gas, amount); + gasses.ExudeGasses.Add(gas, amount); } } @@ -59,24 +58,22 @@ public sealed partial class PlantMutateConsumeGasses : EntityEffect public float MaxValue = 0.5f; public override void Effect(EntityEffectBaseArgs args) { - var plantholder = args.EntityManager.GetComponent(args.TargetEntity); - - if (plantholder.Seed == null) + var gasses = args.EntityManager.GetComponent(args.TargetEntity); + if (gasses == null) return; var random = IoCManager.Resolve(); - 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().ToList()); - if (gasses.ContainsKey(gas)) + if (gasses.ConsumeGasses.ContainsKey(gas)) { - gasses[gas] += amount; + gasses.ConsumeGasses[gas] += amount; } else { - gasses.Add(gas, amount); + gasses.ConsumeGasses.Add(gas, amount); } } diff --git a/Resources/Maps/Salvage/vegan-meatball.yml b/Resources/Maps/Salvage/vegan-meatball.yml index 824e3d0b7ea..8a0e8d8ef79 100644 --- a/Resources/Maps/Salvage/vegan-meatball.yml +++ b/Resources/Maps/Salvage/vegan-meatball.yml @@ -488,14 +488,11 @@ entities: splatPrototype: null lifespan: 75 endurance: 100 - weedHighLevelThreshold: 10 weedTolerance: 5 pestTolerance: 5 highPressureTolerance: 121 lowPressureTolerance: 81 toxinsTolerance: 4 - lightTolerance: 3 - idealLight: 9 heatTolerance: 10 idealHeat: 298 waterConsumption: 0.4 @@ -561,14 +558,11 @@ entities: splatPrototype: null lifespan: 25 endurance: 100 - weedHighLevelThreshold: 10 weedTolerance: 5 pestTolerance: 5 highPressureTolerance: 121 lowPressureTolerance: 81 toxinsTolerance: 4 - lightTolerance: 3 - idealLight: 7 heatTolerance: 10 idealHeat: 293 waterConsumption: 0.6 diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index 7349f3c3d72..c864e982e26 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -14,8 +14,6 @@ production: 3 yield: 3 potency: 5 - idealLight: 8 - nutrientConsumption: 0.40 chemicals: Nutriment: Min: 1 @@ -40,8 +38,6 @@ production: 3 yield: 3 potency: 5 - idealLight: 8 - nutrientConsumption: 0.40 chemicals: Nutriment: Min: 1 @@ -51,6 +47,15 @@ Min: 5 Max: 20 PotencyDivisor: 20 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.4 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: oat @@ -66,8 +71,6 @@ production: 3 yield: 3 potency: 5 - idealLight: 8 - nutrientConsumption: 0.40 chemicals: Nutriment: Min: 1 @@ -77,6 +80,15 @@ Min: 5 Max: 20 PotencyDivisor: 20 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.4 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: banana @@ -94,8 +106,6 @@ maturation: 6 production: 6 yield: 2 - idealLight: 9 - waterConsumption: 0.60 idealHeat: 298 chemicals: Vitamin: @@ -106,6 +116,15 @@ Min: 1 Max: 2 PotencyDivisor: 50 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: mimana @@ -121,8 +140,6 @@ maturation: 6 production: 6 yield: 2 - idealLight: 9 - waterConsumption: 0.60 idealHeat: 298 chemicals: MuteToxin: @@ -133,6 +150,15 @@ Min: 1 Max: 2 PotencyDivisor: 50 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: carrots @@ -149,7 +175,6 @@ yield: 3 potency: 10 growthStages: 3 - waterConsumption: 0.60 chemicals: JuiceCarrot: Min: 1 @@ -163,6 +188,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: laughinPea @@ -181,10 +215,7 @@ production: 5 yield: 3 potency: 20 - idealLight: 8 harvestRepeat: Repeat - nutrientConsumption: 0.6 - waterConsumption: 0.6 chemicals: Nutriment: Min: 1 @@ -198,6 +229,15 @@ Min: 1 Max: 10 PotencyDivisor: 5 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.6 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: lemon @@ -218,7 +258,6 @@ production: 6 yield: 3 potency: 10 - idealLight: 8 chemicals: Nutriment: Min: 1 @@ -228,6 +267,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: lemoon @@ -244,7 +292,6 @@ production: 6 yield: 4 potency: 1 - idealLight: 8 chemicals: Vitamin: Min: 1 @@ -254,6 +301,15 @@ Min: 8 Max: 20 PotencyDivisor: 5 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: lime @@ -273,7 +329,6 @@ production: 6 yield: 3 potency: 10 - idealLight: 8 chemicals: Nutriment: Min: 1 @@ -283,6 +338,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: orange @@ -303,7 +367,6 @@ production: 6 yield: 3 potency: 10 - idealLight: 8 chemicals: Nutriment: Min: 1 @@ -329,7 +392,6 @@ production: 6 yield: 3 potency: 10 - idealLight: 8 chemicals: Haloperidol: Min: 1 @@ -343,6 +405,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: pineapple @@ -359,7 +430,6 @@ production: 6 yield: 3 potency: 10 - idealLight: 8 growthStages: 3 chemicals: Nutriment: @@ -374,6 +444,15 @@ Min: 1 Max: 2 PotencyDivisor: 50 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: potato @@ -390,7 +469,6 @@ yield: 3 potency: 10 growthStages: 4 - waterConsumption: 0.60 chemicals: Nutriment: Min: 1 @@ -400,6 +478,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: sugarcane @@ -425,6 +512,15 @@ Min: 4 Max: 5 PotencyDivisor: 5 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: teaPlant @@ -442,14 +538,21 @@ yield: 2 potency: 20 growthStages: 5 - waterConsumption: 0.6 - idealLight: 9 idealHeat: 298 chemicals: Vitamin: Min: 1 Max: 5 PotencyDivisor: 20 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: papercane @@ -468,6 +571,15 @@ potency: 10 growthStages: 3 idealHeat: 298 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: towercap @@ -487,10 +599,16 @@ yield: 5 potency: 1 growthStages: 3 - waterConsumption: 0.60 - nutrientConsumption: 0.50 - lightTolerance: 6 idealHeat: 288 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: steelcap @@ -508,10 +626,16 @@ yield: 3 potency: 1 growthStages: 3 - waterConsumption: 0.60 - nutrientConsumption: 0.80 - lightTolerance: 6 idealHeat: 288 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.8 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: tomato @@ -531,9 +655,6 @@ production: 6 yield: 2 potency: 10 - waterConsumption: 0.60 - nutrientConsumption: 0.40 - idealLight: 8 idealHeat: 298 splatPrototype: PuddleSplatter chemicals: @@ -549,6 +670,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.4 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: blueTomato @@ -565,9 +695,6 @@ production: 6 yield: 2 potency: 10 - waterConsumption: 0.60 - nutrientConsumption: 0.70 - idealLight: 8 idealHeat: 298 splatPrototype: PuddleSplatter chemicals: @@ -583,6 +710,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.7 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: bloodTomato @@ -601,9 +737,6 @@ production: 6 yield: 2 potency: 10 - waterConsumption: 0.60 - nutrientConsumption: 0.70 - idealLight: 8 idealHeat: 298 splatPrototype: PuddleSplatter chemicals: @@ -615,6 +748,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.7 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: killerTomato @@ -631,9 +773,6 @@ production: 6 yield: 2 potency: 10 - waterConsumption: 0.60 - nutrientConsumption: 0.70 - idealLight: 8 idealHeat: 298 growthStages: 2 splatPrototype: PuddleSplatter @@ -646,6 +785,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.7 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: eggplant @@ -664,7 +812,6 @@ production: 6 yield: 2 potency: 20 - idealLight: 9 idealHeat: 298 chemicals: Nutriment: @@ -675,6 +822,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: cabbage @@ -700,6 +856,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: garlic @@ -729,6 +894,15 @@ Min: 1 Max: 8 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: apple @@ -747,7 +921,6 @@ production: 6 yield: 3 potency: 10 - idealLight: 6 chemicals: Nutriment: Min: 1 @@ -757,6 +930,16 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent + - type: seed id: goldenApple @@ -773,9 +956,6 @@ production: 6 yield: 3 potency: 10 - idealLight: 6 - waterConsumption: 0.75 - nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -788,7 +968,16 @@ DoctorsDelight: Min: 3 Max: 13 - PotencyDivisor: 10 + PotencyDivisor: 10 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.75 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.75 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: corn @@ -805,8 +994,6 @@ yield: 2 potency: 20 growthStages: 3 - idealLight: 8 - waterConsumption: 0.60 idealHeat: 298 chemicals: Nutriment: @@ -817,6 +1004,15 @@ Min: 5 Max: 15 PotencyDivisor: 10 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: onion @@ -835,8 +1031,6 @@ yield: 2 potency: 20 growthStages: 3 - idealLight: 8 - waterConsumption: 0.60 idealHeat: 298 chemicals: Nutriment: @@ -851,6 +1045,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: onionred @@ -867,8 +1070,6 @@ yield: 2 potency: 20 growthStages: 3 - idealLight: 8 - waterConsumption: 0.60 idealHeat: 298 chemicals: Nutriment: @@ -883,6 +1084,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: chanterelle @@ -899,15 +1109,21 @@ yield: 5 potency: 1 growthStages: 3 - lightTolerance: 6 - waterConsumption: 0.60 - nutrientConsumption: 0.50 idealHeat: 288 chemicals: Nutriment: Min: 1 Max: 25 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: eggy @@ -924,14 +1140,21 @@ production: 12 yield: 2 potency: 20 - nutrientConsumption: 0.50 - idealLight: 9 idealHeat: 298 chemicals: Egg: Min: 1 Max: 10 PotencyDivisor: 10 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: cannabis @@ -951,14 +1174,21 @@ yield: 2 potency: 20 growthStages: 3 - waterConsumption: 0.40 - idealLight: 9 idealHeat: 298 chemicals: THC: Min: 1 Max: 10 PotencyDivisor: 10 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.4 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: rainbowCannabis @@ -976,8 +1206,6 @@ yield: 2 potency: 20 growthStages: 3 - waterConsumption: 0.40 - idealLight: 9 idealHeat: 298 chemicals: SpaceDrugs: @@ -1004,6 +1232,15 @@ Min: 0 Max: 5 PotencyDivisor: 33 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.4 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: tobacco @@ -1021,14 +1258,21 @@ yield: 2 potency: 20 growthStages: 3 - waterConsumption: 0.40 - idealLight: 9 idealHeat: 298 chemicals: Nicotine: Min: 1 Max: 10 PotencyDivisor: 10 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.4 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: nettle @@ -1047,14 +1291,21 @@ yield: 2 potency: 20 growthStages: 5 - idealLight: 8 - waterConsumption: 0.60 idealHeat: 298 chemicals: Histamine: Min: 1 Max: 25 PotencyDivisor: 4 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: deathNettle @@ -1071,9 +1322,6 @@ yield: 2 potency: 20 growthStages: 5 - idealLight: 8 - waterConsumption: 0.70 - nutrientConsumption: 0.80 idealHeat: 298 chemicals: SulfuricAcid: @@ -1084,6 +1332,15 @@ Min: 1 Max: 15 PotencyDivisor: 6 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.7 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.8 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: chili @@ -1102,7 +1359,6 @@ production: 6 yield: 2 potency: 20 - idealLight: 9 idealHeat: 298 chemicals: CapsaicinOil: @@ -1117,6 +1373,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: chilly @@ -1133,7 +1398,6 @@ production: 6 yield: 2 potency: 20 - idealLight: 9 idealHeat: 298 chemicals: Frostoil: @@ -1148,6 +1412,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: poppy @@ -1166,7 +1439,6 @@ yield: 3 potency: 10 growthStages: 3 - waterConsumption: 0.60 chemicals: Nutriment: Min: 1 @@ -1176,6 +1448,15 @@ Min: 1 Max: 20 PotencyDivisor: 5 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: aloe @@ -1192,7 +1473,6 @@ yield: 3 potency: 10 growthStages: 5 - waterConsumption: 0.60 chemicals: Aloe: Min: 1 @@ -1202,6 +1482,15 @@ Min: 1 Max: 10 PotencyDivisor: 10 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: lily @@ -1220,7 +1509,6 @@ yield: 3 potency: 10 growthStages: 3 - waterConsumption: 0.60 chemicals: Nutriment: Min: 1 @@ -1230,6 +1518,15 @@ Min: 1 Max: 20 PotencyDivisor: 5 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: lingzhi @@ -1246,7 +1543,6 @@ yield: 3 potency: 10 growthStages: 3 - waterConsumption: 0.60 chemicals: Ultravasculine: Min: 1 @@ -1256,6 +1552,15 @@ Min: 1 Max: 20 PotencyDivisor: 5 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: ambrosiaVulgaris @@ -1274,7 +1579,6 @@ yield: 3 potency: 10 growthStages: 6 - waterConsumption: 0.60 chemicals: Nutriment: Min: 1 @@ -1296,6 +1600,15 @@ Min: 1 Max: 2 PotencyDivisor: 50 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: ambrosiaDeus @@ -1312,7 +1625,6 @@ yield: 3 potency: 10 growthStages: 6 - waterConsumption: 0.60 chemicals: Nutriment: Min: 1 @@ -1330,6 +1642,15 @@ Min: 1 Max: 10 PotencyDivisor: 10 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: galaxythistle @@ -1348,12 +1669,20 @@ yield: 3 potency: 10 growthStages: 3 - waterConsumption: 0.60 chemicals: Stellibinin: Min: 1 Max: 25 PotencyDivisor: 4 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: glasstle @@ -1370,12 +1699,20 @@ yield: 3 potency: 10 growthStages: 3 - waterConsumption: 0.5 chemicals: Razorium: Min: 1 Max: 25 PotencyDivisor: 4 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: flyAmanita @@ -1392,8 +1729,6 @@ yield: 3 potency: 10 growthStages: 2 - waterConsumption: 0.60 - nutrientConsumption: 0.50 chemicals: Amatoxin: Min: 1 @@ -1403,6 +1738,15 @@ Min: 1 Max: 5 PotencyDivisor: 20 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: gatfruit @@ -1422,7 +1766,6 @@ yield: 1 potency: 10 growthStages: 2 - idealLight: 6 chemicals: Nutriment: Min: 1 @@ -1432,6 +1775,15 @@ Min: 1 Max: 5 PotencyDivisor: 20 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: fakeCapfruit @@ -1448,7 +1800,6 @@ yield: 1 potency: 10 growthStages: 2 - idealLight: 6 chemicals: Nutriment: Min: 1 @@ -1458,6 +1809,15 @@ Min: 1 Max: 5 PotencyDivisor: 20 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: realCapfruit @@ -1474,7 +1834,6 @@ yield: 1 potency: 10 growthStages: 2 - idealLight: 6 chemicals: Nutriment: Min: 1 @@ -1484,6 +1843,15 @@ Min: 1 Max: 5 PotencyDivisor: 20 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: rice @@ -1500,9 +1868,6 @@ yield: 3 potency: 5 growthStages: 4 - idealLight: 5 - nutrientConsumption: 0.40 - waterConsumption: 0.60 chemicals: Nutriment: Min: 1 @@ -1512,6 +1877,15 @@ Min: 5 Max: 20 PotencyDivisor: 20 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.4 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: soybeans @@ -1530,13 +1904,20 @@ production: 6 yield: 3 potency: 5 - idealLight: 7 - nutrientConsumption: 0.40 chemicals: Nutriment: Min: 1 Max: 2 PotencyDivisor: 50 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.4 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: spacemansTrumpet @@ -1553,7 +1934,6 @@ production: 3 yield: 2 potency: 10 - waterConsumption: 0.60 chemicals: Nutriment: Min: 1 @@ -1563,6 +1943,15 @@ Min: 1 Max: 15 PotencyDivisor: 5 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: koibean @@ -1579,8 +1968,6 @@ production: 6 yield: 3 potency: 5 - idealLight: 7 - nutrientConsumption: 0.40 chemicals: Nutriment: Min: 1 @@ -1590,6 +1977,15 @@ Min: 1 Max: 4 PotencyDivisor: 30 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: grape @@ -1615,6 +2011,15 @@ Min: 1 Max: 4 PotencyDivisor: 25 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: watermelon @@ -1632,7 +2037,6 @@ production: 3 yield: 1 potency: 1 - idealLight: 8 chemicals: Nutriment: Min: 1 @@ -1646,6 +2050,15 @@ Min: 1 Max: 5 PotencyDivisor: 20 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: holymelon @@ -1661,7 +2074,6 @@ production: 3 yield: 1 potency: 1 - idealLight: 8 chemicals: Nutriment: Min: 1 @@ -1690,9 +2102,6 @@ maturation: 6 production: 6 yield: 6 - idealLight: 7 - waterConsumption: 1 - nutrientConsumption: 0.8 idealHeat: 298 chemicals: Vitamin: @@ -1703,6 +2112,15 @@ Min: 1 Max: 2 PotencyDivisor: 50 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 1.0 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.8 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: berries @@ -1718,8 +2136,6 @@ maturation: 6 production: 6 yield: 4 - idealLight: 7 - nutrientConsumption: 0.6 chemicals: Nutriment: Min: 2 @@ -1729,6 +2145,15 @@ Min: 1 Max: 4 PotencyDivisor: 40 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.6 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: bungo @@ -1745,10 +2170,8 @@ production: 6 potency: 10 yield: 3 - idealLight: 8 idealHeat: 298 growthStages: 4 - waterConsumption: 0.6 chemicals: Nutriment: Min: 5 @@ -1758,6 +2181,15 @@ Min: 5 Max: 10 PotencyDivisor: 20 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: pea @@ -1776,10 +2208,7 @@ production: 6 yield: 3 potency: 25 - idealLight: 8 harvestRepeat: Repeat - nutrientConsumption: 0.5 - waterConsumption: 0.5 chemicals: Nutriment: Min: 1 @@ -1789,6 +2218,15 @@ Min: 1 Max: 2 PotencyDivisor: 50 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: worldPea @@ -1805,7 +2243,6 @@ production: 6 yield: 3 potency: 25 - idealLight: 8 harvestRepeat: Repeat nutrientConsumption: 0.5 waterConsumption: 0.5 @@ -1822,6 +2259,15 @@ Min: 1 Max: 2 PotencyDivisor: 50 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: pumpkin @@ -1850,6 +2296,15 @@ Min: 1 Max: 5 PotencyDivisor: 20 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: bluePumpkin @@ -1880,6 +2335,15 @@ Min: 1 Max: 10 PotencyDivisor: 3 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: cotton @@ -1897,14 +2361,21 @@ production: 3 yield: 3 potency: 5 - idealLight: 8 growthStages: 3 - waterConsumption: 0.60 chemicals: Fiber: Min: 5 Max: 10 PotencyDivisor: 20 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.6 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: pyrotton @@ -1920,9 +2391,7 @@ production: 3 yield: 2 potency: 5 - idealLight: 8 growthStages: 3 - waterConsumption: 0.80 chemicals: Fiber: Min: 5 @@ -1932,6 +2401,15 @@ Min: 4 Max: 8 PotencyDivisor: 30 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.8 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: cherry @@ -1948,7 +2426,6 @@ production: 6 yield: 5 potency: 10 - idealLight: 6 chemicals: Nutriment: Min: 1 @@ -1958,6 +2435,15 @@ Min: 1 Max: 3 PotencyDivisor: 40 + growthComponents: + - !type:WaterGrowthComponent + waterConsumption: 0.5 + - !type:NutrientGrowthComponent + nutrientConsumption: 0.5 + - !type:AgeGrowthComponent + - !type:WeedPestGrowthComponent + - !type:TemperatureGrowthComponent + - !type:PressureGrowthComponent - type: seed id: anomalyBerry @@ -1988,4 +2474,4 @@ Vitamin: Min: 1 Max: 2 - PotencyDivisor: 40 + PotencyDivisor: 40 \ No newline at end of file From 918932f766e20dd3c76aee014640d63bfaf2e8c2 Mon Sep 17 00:00:00 2001 From: PraxisMapper Date: Thu, 3 Oct 2024 01:01:38 -0400 Subject: [PATCH 02/53] reduce file count, more progress --- .../Botany/Components/AgeGrowthComponent.cs | 5 - ...onent.cs => AtmosphericGrowthComponent.cs} | 8 +- .../Botany/Components/BasicGrowthComponent.cs | 15 +++ ...t.cs => ConsumeExudeGasGrowthComponent.cs} | 3 +- .../Components/ExudeGasGrowthComponent .cs | 9 -- .../Components/NutrientGrowthComponent.cs | 8 -- .../Components/TemperatureGrowthComponent.cs | 11 -- .../Botany/Components/WaterGrowthComponent.cs | 8 -- Content.Server/Botany/SeedPrototype.cs | 19 ++- .../Botany/Systems/AgeGrowthSystem.cs | 72 ----------- ...thSystem.cs => AtmosphericGrowthSystem.cs} | 20 ++- .../Botany/Systems/BasicGrowthSystem.cs | 120 ++++++++++++++++++ ...stem.cs => ConsumeExudeGasGrowthSystem.cs} | 6 +- .../Botany/Systems/ExudeGasGrowthSystem.cs | 36 ------ .../Botany/Systems/NutrientGrowthSystem.cs | 44 ------- .../Botany/Systems/PlantHolderSystem.cs | 12 +- .../Botany/Systems/PressureGrowthSystem.cs | 40 ------ .../Botany/Systems/WaterGrowthSystem.cs | 44 ------- .../EntityEffects/Effects/PlantMutateGases.cs | 11 +- .../Effects/PlantMutateHarvest.cs | 2 +- Resources/Prototypes/Hydroponics/seeds.yml | 29 ++--- 21 files changed, 192 insertions(+), 330 deletions(-) delete mode 100644 Content.Server/Botany/Components/AgeGrowthComponent.cs rename Content.Server/Botany/Components/{PressureGrowthComponent.cs => AtmosphericGrowthComponent.cs} (51%) create mode 100644 Content.Server/Botany/Components/BasicGrowthComponent.cs rename Content.Server/Botany/Components/{ConsumeGasGrowthComponent.cs => ConsumeExudeGasGrowthComponent.cs} (52%) delete mode 100644 Content.Server/Botany/Components/ExudeGasGrowthComponent .cs delete mode 100644 Content.Server/Botany/Components/NutrientGrowthComponent.cs delete mode 100644 Content.Server/Botany/Components/TemperatureGrowthComponent.cs delete mode 100644 Content.Server/Botany/Components/WaterGrowthComponent.cs delete mode 100644 Content.Server/Botany/Systems/AgeGrowthSystem.cs rename Content.Server/Botany/Systems/{TemperatureGrowthSystem.cs => AtmosphericGrowthSystem.cs} (65%) create mode 100644 Content.Server/Botany/Systems/BasicGrowthSystem.cs rename Content.Server/Botany/Systems/{ConsumeGasGrowthSystem.cs => ConsumeExudeGasGrowthSystem.cs} (82%) delete mode 100644 Content.Server/Botany/Systems/ExudeGasGrowthSystem.cs delete mode 100644 Content.Server/Botany/Systems/NutrientGrowthSystem.cs delete mode 100644 Content.Server/Botany/Systems/PressureGrowthSystem.cs delete mode 100644 Content.Server/Botany/Systems/WaterGrowthSystem.cs diff --git a/Content.Server/Botany/Components/AgeGrowthComponent.cs b/Content.Server/Botany/Components/AgeGrowthComponent.cs deleted file mode 100644 index 4c7778b702e..00000000000 --- a/Content.Server/Botany/Components/AgeGrowthComponent.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Content.Server.Botany.Components; - -[RegisterComponent] -public sealed partial class AgeGrowthComponent : PlantGrowthComponent -{} diff --git a/Content.Server/Botany/Components/PressureGrowthComponent.cs b/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs similarity index 51% rename from Content.Server/Botany/Components/PressureGrowthComponent.cs rename to Content.Server/Botany/Components/AtmosphericGrowthComponent.cs index 227f1dbcaaf..dbf6c5728ec 100644 --- a/Content.Server/Botany/Components/PressureGrowthComponent.cs +++ b/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs @@ -1,8 +1,14 @@ namespace Content.Server.Botany.Components; [RegisterComponent] -public sealed partial class PressureGrowthComponent : PlantGrowthComponent +public sealed partial class AtmosphericGrowthComponent : PlantGrowthComponent { + [DataField] + public float IdealHeat = 293f; + + [DataField] + public float HeatTolerance = 10f; + [DataField] public float LowPressureTolerance = 81f; diff --git a/Content.Server/Botany/Components/BasicGrowthComponent.cs b/Content.Server/Botany/Components/BasicGrowthComponent.cs new file mode 100644 index 00000000000..f4a0f2689cb --- /dev/null +++ b/Content.Server/Botany/Components/BasicGrowthComponent.cs @@ -0,0 +1,15 @@ +namespace Content.Server.Botany.Components; + +[RegisterComponent] +public sealed partial class BasicGrowthComponent : PlantGrowthComponent +{ + //Remaining TODOs/TO CHECKS + //Update all of seeds.yml to have condensed component set? or set prototype to have them? + //Ensure mutations work as expected for non-default growth components + //ensure components / values are copied over when seeds are made/clipped. + [DataField] + public float WaterConsumption = 0.5f; + + [DataField] + public float NutrientConsumption = 0.75f; +} diff --git a/Content.Server/Botany/Components/ConsumeGasGrowthComponent.cs b/Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs similarity index 52% rename from Content.Server/Botany/Components/ConsumeGasGrowthComponent.cs rename to Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs index a3ee859160e..1da31d376b1 100644 --- a/Content.Server/Botany/Components/ConsumeGasGrowthComponent.cs +++ b/Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs @@ -3,7 +3,8 @@ using Content.Shared.Atmos; namespace Content.Server.Botany.Components; [RegisterComponent] -public sealed partial class ConsumeGasGrowthComponent : PlantGrowthComponent +public sealed partial class ConsumeExudeGasGrowthComponent : PlantGrowthComponent { [DataField] public Dictionary ConsumeGasses = new(); + [DataField] public Dictionary ExudeGasses = new(); } diff --git a/Content.Server/Botany/Components/ExudeGasGrowthComponent .cs b/Content.Server/Botany/Components/ExudeGasGrowthComponent .cs deleted file mode 100644 index 280c7988236..00000000000 --- a/Content.Server/Botany/Components/ExudeGasGrowthComponent .cs +++ /dev/null @@ -1,9 +0,0 @@ -using Content.Shared.Atmos; - -namespace Content.Server.Botany.Components; - -[RegisterComponent] -public sealed partial class ExudeGasGrowthComponent : PlantGrowthComponent -{ - [DataField] public Dictionary ExudeGasses = new(); -} diff --git a/Content.Server/Botany/Components/NutrientGrowthComponent.cs b/Content.Server/Botany/Components/NutrientGrowthComponent.cs deleted file mode 100644 index bcc14ede195..00000000000 --- a/Content.Server/Botany/Components/NutrientGrowthComponent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Content.Server.Botany.Components; - -[RegisterComponent] -public sealed partial class NutrientGrowthComponent : PlantGrowthComponent -{ - [DataField] - public float NutrientConsumption = 0.75f; -} diff --git a/Content.Server/Botany/Components/TemperatureGrowthComponent.cs b/Content.Server/Botany/Components/TemperatureGrowthComponent.cs deleted file mode 100644 index 7fcf712d843..00000000000 --- a/Content.Server/Botany/Components/TemperatureGrowthComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Content.Server.Botany.Components; - -[RegisterComponent] -public sealed partial class TemperatureGrowthComponent : PlantGrowthComponent -{ - [DataField] - public float IdealHeat = 293f; - - [DataField] - public float HeatTolerance = 10f; -} diff --git a/Content.Server/Botany/Components/WaterGrowthComponent.cs b/Content.Server/Botany/Components/WaterGrowthComponent.cs deleted file mode 100644 index b073d0cc881..00000000000 --- a/Content.Server/Botany/Components/WaterGrowthComponent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Content.Server.Botany.Components; - -[RegisterComponent] -public sealed partial class WaterGrowthComponent : PlantGrowthComponent -{ - [DataField] - public float WaterConsumption = 0.5f; -} diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index 6bbe83539cb..7e09e3f2f7f 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -50,7 +50,7 @@ public partial struct SeedChemQuantity // TODO reduce the number of friends to a reasonable level. Requires ECS-ing things like plant holder component. [Virtual, DataDefinition] -[Access(typeof(BotanySystem), typeof(PlantHolderSystem), typeof(SeedExtractorSystem), typeof(PlantHolderComponent), typeof(EntityEffect), typeof(MutationSystem), typeof(AgeGrowthSystem))] +[Access(typeof(BotanySystem), typeof(PlantHolderSystem), typeof(SeedExtractorSystem), typeof(PlantHolderComponent), typeof(EntityEffect), typeof(MutationSystem))] public partial class SeedData { #region Tracking @@ -175,15 +175,13 @@ public partial class SeedData /// The growth components used by this seed. /// [DataField] - public List GrowthComponents = new() { - new PlantComponent(), - new WaterGrowthComponent(), - new NutrientGrowthComponent(), - new AgeGrowthComponent(), - new PressureGrowthComponent(), - new TemperatureGrowthComponent(), - new WeedPestGrowthComponent(), - }; + public List GrowthComponents = new(); + //{ + // new PlantComponent(), + // new BasicGrowthComponent(), + // new AtmosphericGrowthComponent(), + // new WeedPestGrowthComponent(), + // }; //TODO: the mutation system should add the missing components when they mutate. //This would be done with EnsureComp<> @@ -193,6 +191,7 @@ public partial class SeedData var newSeed = new SeedData { + GrowthComponents = GrowthComponents, Name = Name, Noun = Noun, DisplayName = DisplayName, diff --git a/Content.Server/Botany/Systems/AgeGrowthSystem.cs b/Content.Server/Botany/Systems/AgeGrowthSystem.cs deleted file mode 100644 index 22e42ba3006..00000000000 --- a/Content.Server/Botany/Systems/AgeGrowthSystem.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Content.Server.Botany.Components; -using Robust.Shared.Random; - -namespace Content.Server.Botany.Systems; -public sealed class AgeGrowthSystem : PlantGrowthSystem -{ - [Dependency] private readonly BotanySystem _botany = default!; - [Dependency] private readonly PlantHolderSystem _plantHolderSystem = default!; - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); - } - - private void OnPlantGrow(EntityUid uid, AgeGrowthComponent component, OnPlantGrowEvent args) - { - PlantHolderComponent? holder = null; - Resolve(uid, ref holder); - - if (holder == null || holder.Seed == null || holder.Dead) - return; - - // Advance plant age here. - if (holder.SkipAging > 0) - holder.SkipAging--; - else - { - if (_random.Prob(0.8f)) - { - holder.Age += (int)(1 * HydroponicsSpeedMultiplier); - holder.UpdateSpriteAfterUpdate = true; - } - } - - if (holder.Age > holder.Seed.Lifespan) - { - holder.Health -= _random.Next(3, 5) * HydroponicsSpeedMultiplier; - if (holder.DrawWarnings) - holder.UpdateSpriteAfterUpdate = true; - } - else if (holder.Age < 0) // Revert back to seed packet! - { - var packetSeed = holder.Seed; - // will put it in the trays hands if it has any, please do not try doing this - _botany.SpawnSeedPacket(packetSeed, Transform(uid).Coordinates, uid); - _plantHolderSystem.RemovePlant(uid, holder); - holder.ForceUpdate = true; - _plantHolderSystem.Update(uid, holder); - } - - // If enough time has passed since the plant was harvested, we're ready to harvest again! - if (holder.Seed.ProductPrototypes.Count > 0) - { - if (holder.Age > holder.Seed.Production) - { - if (holder.Age - holder.LastProduce > holder.Seed.Production && !holder.Harvest) - { - holder.Harvest = true; - holder.LastProduce = holder.Age; - } - } - else - { - if (holder.Harvest) - { - holder.Harvest = false; - holder.LastProduce = holder.Age; - } - } - } - } -} diff --git a/Content.Server/Botany/Systems/TemperatureGrowthSystem.cs b/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs similarity index 65% rename from Content.Server/Botany/Systems/TemperatureGrowthSystem.cs rename to Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs index 8379ad77475..c34fbd14c1f 100644 --- a/Content.Server/Botany/Systems/TemperatureGrowthSystem.cs +++ b/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs @@ -3,7 +3,7 @@ using Content.Server.Botany.Components; using Content.Shared.Atmos; namespace Content.Server.Botany.Systems; -public sealed class TemperatureGrowthSystem : PlantGrowthSystem +public sealed class AtmosphericGrowthSystem : PlantGrowthSystem { [Dependency] private readonly BotanySystem _botany = default!; [Dependency] private readonly PlantHolderSystem _plantHolderSystem = default!; @@ -12,12 +12,11 @@ public sealed class TemperatureGrowthSystem : PlantGrowthSystem public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); + SubscribeLocalEvent(OnPlantGrow); } - private void OnPlantGrow(EntityUid uid, TemperatureGrowthComponent component, OnPlantGrowEvent args) + private void OnPlantGrow(EntityUid uid, AtmosphericGrowthComponent component, OnPlantGrowEvent args) { - PlantHolderComponent? holder = null; Resolve(uid, ref holder); @@ -36,5 +35,18 @@ public sealed class TemperatureGrowthSystem : PlantGrowthSystem { holder.ImproperHeat = false; } + + var pressure = environment.Pressure; + if (pressure < component.LowPressureTolerance || pressure > component.HighPressureTolerance) + { + holder.Health -= _random.Next(1, 3); + holder.ImproperPressure = true; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + else + { + holder.ImproperPressure = false; + } } } diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs new file mode 100644 index 00000000000..977c949a95e --- /dev/null +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -0,0 +1,120 @@ +using Content.Server.Botany.Components; +using Robust.Shared.Random; + +namespace Content.Server.Botany.Systems; +// For all the very common stuff all plants are expected to do. + +// TODO: make CO2Boost (add potency if the plant can eat enough CO2). separate PR post-merge +// TOOD: make GrowLight (run bonus ticks if theres a grow light nearby). separate PR post-merge. +public sealed class BasicGrowthSystem : PlantGrowthSystem +{ + [Dependency] private readonly BotanySystem _botany = default!; + [Dependency] private readonly PlantHolderSystem _plantHolder = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); + } + + private void OnPlantGrow(EntityUid uid, BasicGrowthComponent component, OnPlantGrowEvent args) + { + PlantHolderComponent? holder = null; + Resolve(uid, ref holder); + + if (holder == null || holder.Seed == null || holder.Dead) + return; + + // Advance plant age here. + if (holder.SkipAging > 0) + holder.SkipAging--; + else + { + if (_random.Prob(0.8f)) + { + holder.Age += (int)(1 * HydroponicsSpeedMultiplier); + holder.UpdateSpriteAfterUpdate = true; + } + } + + if (holder.Age > holder.Seed.Lifespan) + { + holder.Health -= _random.Next(3, 5) * HydroponicsSpeedMultiplier; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + else if (holder.Age < 0) // Revert back to seed packet! + { + var packetSeed = holder.Seed; + // will put it in the trays hands if it has any, please do not try doing this + _botany.SpawnSeedPacket(packetSeed, Transform(uid).Coordinates, uid); + _plantHolder.RemovePlant(uid, holder); + holder.ForceUpdate = true; + _plantHolder.Update(uid, holder); + } + + // If enough time has passed since the plant was harvested, we're ready to harvest again! + if (holder.Seed.ProductPrototypes.Count > 0) + { + if (holder.Age > holder.Seed.Production) + { + if (holder.Age - holder.LastProduce > holder.Seed.Production && !holder.Harvest) + { + holder.Harvest = true; + holder.LastProduce = holder.Age; + } + } + else + { + if (holder.Harvest) + { + holder.Harvest = false; + holder.LastProduce = holder.Age; + } + } + } + + if (component.WaterConsumption > 0 && holder.WaterLevel > 0 && _random.Prob(0.75f)) + { + holder.WaterLevel -= MathF.Max(0f, + component.WaterConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + + if (component.NutrientConsumption > 0 && holder.NutritionLevel > 0 && _random.Prob(0.75f)) + { + holder.NutritionLevel -= MathF.Max(0f, + component.NutrientConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + + var healthMod = _random.Next(1, 3) * HydroponicsSpeedMultiplier; + if (holder.SkipAging < 10) + { + // Make sure the plant is not thirsty. + if (holder.WaterLevel > 10) + { + holder.Health += Convert.ToInt32(_random.Prob(0.35f)) * healthMod; + } + else + { + AffectGrowth(-1, holder); + holder.Health -= healthMod; + } + + if (holder.NutritionLevel > 5) + { + holder.Health += Convert.ToInt32(_random.Prob(0.35f)) * healthMod; + } + else + { + AffectGrowth(-1, holder); + holder.Health -= healthMod; + } + } + + //TODO: put Viable back on seed and check it here, or split it off to a separate system and check there? + } +} diff --git a/Content.Server/Botany/Systems/ConsumeGasGrowthSystem.cs b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs similarity index 82% rename from Content.Server/Botany/Systems/ConsumeGasGrowthSystem.cs rename to Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs index 63d79e31644..9649888d94c 100644 --- a/Content.Server/Botany/Systems/ConsumeGasGrowthSystem.cs +++ b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs @@ -3,16 +3,16 @@ using Content.Server.Botany.Components; using Content.Shared.Atmos; namespace Content.Server.Botany.Systems; -public sealed class ConsumeGasGrowthSystem : PlantGrowthSystem +public sealed class ConsumeExudeGasGrowthSystem : PlantGrowthSystem { [Dependency] private readonly AtmosphereSystem _atmosphere = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); + SubscribeLocalEvent(OnPlantGrow); } - private void OnPlantGrow(EntityUid uid, ConsumeGasGrowthComponent component, OnPlantGrowEvent args) + private void OnPlantGrow(EntityUid uid, ConsumeExudeGasGrowthComponent component, OnPlantGrowEvent args) { PlantHolderComponent? holder = null; Resolve(uid, ref holder); diff --git a/Content.Server/Botany/Systems/ExudeGasGrowthSystem.cs b/Content.Server/Botany/Systems/ExudeGasGrowthSystem.cs deleted file mode 100644 index b33d945f58f..00000000000 --- a/Content.Server/Botany/Systems/ExudeGasGrowthSystem.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Content.Server.Atmos.EntitySystems; -using Content.Server.Botany.Components; -using Content.Shared.Atmos; - -namespace Content.Server.Botany.Systems; -public sealed class ExudeGasGrowthSystem : PlantGrowthSystem -{ - [Dependency] private readonly AtmosphereSystem _atmosphere = default!; - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); - } - - private void OnPlantGrow(EntityUid uid, ExudeGasGrowthComponent component, OnPlantGrowEvent args) - { - PlantHolderComponent? holder = null; - Resolve(uid, ref holder); - - if (holder == null || holder.Seed == null || holder.Dead) - return; - - var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; - - // Gas production. - var exudeCount = component.ExudeGasses.Count; - if (exudeCount > 0) - { - foreach (var (gas, amount) in component.ExudeGasses) - { - environment.AdjustMoles(gas, - MathF.Max(1f, MathF.Round(amount * MathF.Round(holder.Seed.Potency) / exudeCount))); - } - } - } -} diff --git a/Content.Server/Botany/Systems/NutrientGrowthSystem.cs b/Content.Server/Botany/Systems/NutrientGrowthSystem.cs deleted file mode 100644 index 0be2d099e2b..00000000000 --- a/Content.Server/Botany/Systems/NutrientGrowthSystem.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Content.Server.Botany.Components; -using Robust.Shared.Random; - -namespace Content.Server.Botany.Systems; -public sealed class NutrientGrowthSystem : PlantGrowthSystem -{ - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); - } - - private void OnPlantGrow(EntityUid uid, NutrientGrowthComponent component, OnPlantGrowEvent args) - { - PlantHolderComponent? holder = null; - Resolve(uid, ref holder); - - if (holder == null || holder.Seed == null || holder.Dead) - return; - - if (component.NutrientConsumption > 0 && holder.NutritionLevel > 0 && _random.Prob(0.75f)) - { - holder.NutritionLevel -= MathF.Max(0f, - component.NutrientConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); - if (holder.DrawWarnings) - holder.UpdateSpriteAfterUpdate = true; - } - - var healthMod = _random.Next(1, 3) * HydroponicsSpeedMultiplier; - if (holder.SkipAging < 10) - { - // Make sure the plant is not hungry - if (holder.NutritionLevel > 5) - { - holder.Health += Convert.ToInt32(_random.Prob(0.35f)) * healthMod; - } - else - { - AffectGrowth(-1, holder); - holder.Health -= healthMod; - } - } - } -} diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index efac1314a90..6c573a898d4 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -552,12 +552,12 @@ public sealed class PlantHolderSystem : EntitySystem public void RemovePlant(EntityUid uid, PlantHolderComponent? component = null) { - if (!Resolve(uid, ref component)) + if (!Resolve(uid, ref component) || component.Seed == null) return; - if (component.Seed != null) - foreach (var g in component.Seed.GrowthComponents) - EntityManager.RemoveComponent(uid, g); + //OK, how do i check the entity instead of the seed? + foreach(var g in EntityManager.GetComponents(uid)) + EntityManager.RemoveComponent(uid, g); component.YieldMod = 1; component.MutationMod = 1; @@ -650,10 +650,6 @@ public sealed class PlantHolderSystem : EntitySystem { EnsureUniqueSeed(uid, component); _mutation.MutateSeed(uid, ref component.Seed, severity); - - //TODO: this is a temp check to apply autoharvest. New mutation system should handle this correctly. - if (component.Seed.HarvestRepeat == HarvestType.SelfHarvest) - Comp(uid); } } diff --git a/Content.Server/Botany/Systems/PressureGrowthSystem.cs b/Content.Server/Botany/Systems/PressureGrowthSystem.cs deleted file mode 100644 index 45368672bc3..00000000000 --- a/Content.Server/Botany/Systems/PressureGrowthSystem.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Content.Server.Atmos.EntitySystems; -using Content.Server.Botany.Components; -using Content.Shared.Atmos; - -namespace Content.Server.Botany.Systems; -public sealed class PressureGrowthSystem : PlantGrowthSystem -{ - [Dependency] private readonly BotanySystem _botany = default!; - [Dependency] private readonly PlantHolderSystem _plantHolderSystem = default!; - [Dependency] private readonly AtmosphereSystem _atmosphere = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); - } - - private void OnPlantGrow(EntityUid uid, PressureGrowthComponent component, OnPlantGrowEvent args) - { - PlantHolderComponent? holder = null; - Resolve(uid, ref holder); - - if (holder == null || holder.Seed == null || holder.Dead) - return; - - var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; - var pressure = environment.Pressure; - if (pressure < component.LowPressureTolerance || pressure > component.HighPressureTolerance) - { - holder.Health -= _random.Next(1, 3); - holder.ImproperPressure = true; - if (holder.DrawWarnings) - holder.UpdateSpriteAfterUpdate = true; - } - else - { - holder.ImproperPressure = false; - } - } -} diff --git a/Content.Server/Botany/Systems/WaterGrowthSystem.cs b/Content.Server/Botany/Systems/WaterGrowthSystem.cs deleted file mode 100644 index fd2a64a0a19..00000000000 --- a/Content.Server/Botany/Systems/WaterGrowthSystem.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Content.Server.Botany.Components; -using Robust.Shared.Random; - -namespace Content.Server.Botany.Systems; -public sealed class WaterGrowthSystem : PlantGrowthSystem -{ - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); - } - - private void OnPlantGrow(EntityUid uid, WaterGrowthComponent component, OnPlantGrowEvent args) - { - PlantHolderComponent? holder = null; - Resolve(uid, ref holder); - - if (holder == null || holder.Seed == null || holder.Dead) - return; - - if (component.WaterConsumption > 0 && holder.WaterLevel > 0 && _random.Prob(0.75f)) - { - holder.WaterLevel -= MathF.Max(0f, - component.WaterConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); - if (holder.DrawWarnings) - holder.UpdateSpriteAfterUpdate = true; - } - - var healthMod = _random.Next(1, 3) * HydroponicsSpeedMultiplier; - if (holder.SkipAging < 10) - { - // Make sure the plant is not thirsty. - if (holder.WaterLevel > 10) - { - holder.Health += Convert.ToInt32(_random.Prob(0.35f)) * healthMod; - } - else - { - AffectGrowth(-1, holder); - holder.Health -= healthMod; - } - } - } -} diff --git a/Content.Server/EntityEffects/Effects/PlantMutateGases.cs b/Content.Server/EntityEffects/Effects/PlantMutateGases.cs index de54677d375..b7b7cee998f 100644 --- a/Content.Server/EntityEffects/Effects/PlantMutateGases.cs +++ b/Content.Server/EntityEffects/Effects/PlantMutateGases.cs @@ -20,11 +20,7 @@ public sealed partial class PlantMutateExudeGasses : EntityEffect public override void Effect(EntityEffectBaseArgs args) { - var gasses = args.EntityManager.GetComponent(args.TargetEntity); - - if (gasses == null) - return; - + var gasses = args.EntityManager.EnsureComponent(args.TargetEntity); var random = IoCManager.Resolve(); // Add a random amount of a random gas to this gas dictionary @@ -58,10 +54,7 @@ public sealed partial class PlantMutateConsumeGasses : EntityEffect public float MaxValue = 0.5f; public override void Effect(EntityEffectBaseArgs args) { - var gasses = args.EntityManager.GetComponent(args.TargetEntity); - if (gasses == null) - return; - + var gasses = args.EntityManager.GetComponent(args.TargetEntity); var random = IoCManager.Resolve(); // Add a random amount of a random gas to this gas dictionary diff --git a/Content.Server/EntityEffects/Effects/PlantMutateHarvest.cs b/Content.Server/EntityEffects/Effects/PlantMutateHarvest.cs index e67176ee16d..c92b154c018 100644 --- a/Content.Server/EntityEffects/Effects/PlantMutateHarvest.cs +++ b/Content.Server/EntityEffects/Effects/PlantMutateHarvest.cs @@ -20,7 +20,7 @@ public sealed partial class PlantMutateHarvest : EntityEffect if (plantholder.Seed.HarvestRepeat == HarvestType.NoRepeat) plantholder.Seed.HarvestRepeat = HarvestType.Repeat; else if (plantholder.Seed.HarvestRepeat == HarvestType.Repeat) - plantholder.Seed.HarvestRepeat = HarvestType.SelfHarvest; + args.EntityManager.EnsureComponent(args.TargetEntity); } protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index c864e982e26..1740b81f08b 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -23,6 +23,12 @@ Min: 5 Max: 20 PotencyDivisor: 20 + growthComponents: + - !type:BasicGrowthComponent + waterConsumption: 0.5 + nutrientConsumption: 0.4 + - !type:AtmosphericGrowthComponent + - !type:WeedPestGrowthComponent - type: seed id: meatwheat @@ -48,15 +54,12 @@ Max: 20 PotencyDivisor: 20 growthComponents: - - !type:WaterGrowthComponent + - !type:BasicGrowthComponent waterConsumption: 0.5 - - !type:NutrientGrowthComponent nutrientConsumption: 0.4 - - !type:AgeGrowthComponent + - !type:AtmosphericGrowthComponent - !type:WeedPestGrowthComponent - - !type:TemperatureGrowthComponent - - !type:PressureGrowthComponent - + - type: seed id: oat name: seeds-oat-name @@ -81,14 +84,12 @@ Max: 20 PotencyDivisor: 20 growthComponents: - - !type:WaterGrowthComponent + - !type:BasicGrowthComponent waterConsumption: 0.5 - - !type:NutrientGrowthComponent nutrientConsumption: 0.4 - - !type:AgeGrowthComponent + - !type:AtmosphericGrowthComponent - !type:WeedPestGrowthComponent - - !type:TemperatureGrowthComponent - - !type:PressureGrowthComponent + - type: seed id: banana @@ -1844,14 +1845,10 @@ Max: 5 PotencyDivisor: 20 growthComponents: - - !type:WaterGrowthComponent + - !type:BasicGrowthComponent waterConsumption: 0.5 - - !type:NutrientGrowthComponent nutrientConsumption: 0.5 - !type:AgeGrowthComponent - - !type:WeedPestGrowthComponent - - !type:TemperatureGrowthComponent - - !type:PressureGrowthComponent - type: seed id: rice From 7dad2c388b3e4f6720cb642518ff4594a2a12085 Mon Sep 17 00:00:00 2001 From: PraxisMapper Date: Fri, 4 Oct 2024 14:44:04 -0400 Subject: [PATCH 03/53] Additional partial work --- .../Botany/Components/PlantGrowthComponent.cs | 12 +++++++-- .../Botany/Systems/BasicGrowthSystem.cs | 26 +++++++++++++++++- .../Botany/Systems/BotanySwabSystem.cs | 27 ++++++++++++++++++- .../Botany/Systems/PlantGrowthSystem.cs | 2 -- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/Content.Server/Botany/Components/PlantGrowthComponent.cs b/Content.Server/Botany/Components/PlantGrowthComponent.cs index df11fc745a8..5490ef80f94 100644 --- a/Content.Server/Botany/Components/PlantGrowthComponent.cs +++ b/Content.Server/Botany/Components/PlantGrowthComponent.cs @@ -1,5 +1,13 @@ +using System.Security.Policy; + namespace Content.Server.Botany.Components; [RegisterComponent] -public abstract partial class PlantGrowthComponent : Component -{ } +public abstract partial class PlantGrowthComponent : Component { + public PlantGrowthComponent DupeComponent() + { + return (PlantGrowthComponent)this.MemberwiseClone(); //TODO TEST if this carries all properties or only base class + } +} + + diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index 977c949a95e..f8c627c812f 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -1,10 +1,11 @@ using Content.Server.Botany.Components; +using Content.Shared.Swab; using Robust.Shared.Random; namespace Content.Server.Botany.Systems; // For all the very common stuff all plants are expected to do. -// TODO: make CO2Boost (add potency if the plant can eat enough CO2). separate PR post-merge +// TODO: make CO2Boost (add potency if the plant can eat an increasing amount of CO2). separate PR post-merge // TOOD: make GrowLight (run bonus ticks if theres a grow light nearby). separate PR post-merge. public sealed class BasicGrowthSystem : PlantGrowthSystem { @@ -15,6 +16,29 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem { base.Initialize(); SubscribeLocalEvent(OnPlantGrow); + SubscribeLocalEvent(OnSwab); + } + + private void OnSwab(EntityUid uid, BasicGrowthComponent component, BotanySwabDoAfterEvent args) + { + if (args.Cancelled || args.Handled || !TryComp(args.Args.Target, out var plant) || + args.Used == null || !TryComp(args.Used.Value, out var swab)) + return; + + var swabComp = swab.components.Find(c => c.GetType() == typeof(BasicGrowthComponent)); + if (swabComp == null) + { + swab.components.Add(new BasicGrowthComponent() { + WaterConsumption = component.WaterConsumption, + NutrientConsumption = component.NutrientConsumption + }); + } + else + { + BasicGrowthComponent typedComp = (BasicGrowthComponent)swabComp; + if (_random.Prob(0.5f)) typedComp.WaterConsumption = component.WaterConsumption; + if (_random.Prob(0.5f)) typedComp.NutrientConsumption = component.NutrientConsumption; + } } private void OnPlantGrow(EntityUid uid, BasicGrowthComponent component, OnPlantGrowEvent args) diff --git a/Content.Server/Botany/Systems/BotanySwabSystem.cs b/Content.Server/Botany/Systems/BotanySwabSystem.cs index 7b75b8db59f..ec10b888403 100644 --- a/Content.Server/Botany/Systems/BotanySwabSystem.cs +++ b/Content.Server/Botany/Systems/BotanySwabSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.DoAfter; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Swab; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Robust.Shared.Random; using Robust.Shared.Serialization.Manager; using System.Linq; @@ -70,7 +71,7 @@ public sealed class BotanySwabSystem : EntitySystem // Pick up pollen swab.SeedData = plant.Seed; if (plant.Seed != null) - swab.components = plant.Seed.GrowthComponents.ToList(); //copy list in case plant changes after sampling + swab.components = plant.Seed.GrowthComponents.ToList(); //TODO: copy components in case plant changes after sampling. test this works. _popupSystem.PopupEntity(Loc.GetString("botany-swab-from"), args.Args.Target.Value, args.Args.User); } @@ -93,6 +94,30 @@ public sealed class BotanySwabSystem : EntitySystem } } + //Growth Components mean that those systems listed for the BotanySwabDoAfterEvent and do their stuff. + //Wait, no. If the swab has components the plant doesn't that wont fire off. + //I DO need to check that here (though maybe I can get away with calling an event for it instead of looking up systems? + //Or maybe some override function that only applies the Cross math (since the other effect is copying the component?) + + //OR OR MAYBE THIS: the event fires, this handles ones that aren't on the plant, and those system handle the event for ones that do? + //that might be more work? + var plantcomps = EntityManager.GetComponents(args.Args.Target.Value); + foreach (var gc in swab.components) + { + if (plantcomps.Any(p => p.GetType() == gc.GetType())) + { + //fire the event or override to handle the 50% variable swap test. + } + else + { + //copy the component over on the 50% chance. + if (_random.Prob(0.5f)) { + //EntityManager.add + } + } + } + + plant.Seed = _mutationSystem.Cross(swab.SeedData, old); // Cross-pollenate swab.SeedData = old; // Transfer old plant pollen to swab _popupSystem.PopupEntity(Loc.GetString("botany-swab-to"), args.Args.Target.Value, args.Args.User); diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs index 4351621306d..521fc183448 100644 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -19,8 +19,6 @@ public abstract class PlantGrowthSystem : EntitySystem public const float HydroponicsSpeedMultiplier = 1f; public const float HydroponicsConsumptionMultiplier = 2f; - - public override void Initialize() { base.Initialize(); From 86f4cb1312a09d9eee2bec8ee1af0e00e342b315 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Thu, 31 Jul 2025 05:48:23 +0300 Subject: [PATCH 04/53] update 1 --- .../Botany/Components/PlantGrowthComponent.cs | 5 +- Content.Server/Botany/SeedPrototype.cs | 2 + .../Botany/Systems/BasicGrowthSystem.cs | 13 +++- .../Botany/Systems/BotanySwabSystem.cs | 11 ++- .../Botany/Systems/MutationSystem.cs | 16 +++++ .../Botany/Systems/PlantHolderSystem.cs | 5 +- .../EntityEffects/Effects/PlantMutateGases.cs | 8 ++- .../EntityEffects/EntityEffectSystem.cs | 68 +++++++++++-------- .../SharedSolutionContainerSystem.cs | 57 +++++++++++++--- .../EffectConditions/SolutionTemperature.cs | 18 ++++- .../Tools/Components/WelderComponent.cs | 3 - 11 files changed, 155 insertions(+), 51 deletions(-) diff --git a/Content.Server/Botany/Components/PlantGrowthComponent.cs b/Content.Server/Botany/Components/PlantGrowthComponent.cs index 5490ef80f94..3db30624f30 100644 --- a/Content.Server/Botany/Components/PlantGrowthComponent.cs +++ b/Content.Server/Botany/Components/PlantGrowthComponent.cs @@ -4,9 +4,12 @@ namespace Content.Server.Botany.Components; [RegisterComponent] public abstract partial class PlantGrowthComponent : Component { + /// + /// Creates a copy of this component. + /// public PlantGrowthComponent DupeComponent() { - return (PlantGrowthComponent)this.MemberwiseClone(); //TODO TEST if this carries all properties or only base class + return (PlantGrowthComponent)this.MemberwiseClone(); } } diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index a9b643f6147..fdce7ddf888 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -1,10 +1,12 @@ using Content.Server.Botany.Components; using Content.Server.Botany.Systems; +using Content.Server.EntityEffects; using Content.Shared.EntityEffects; using Content.Shared.Random; using Robust.Shared.Audio; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; using Robust.Shared.Utility; namespace Content.Server.Botany; diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index f8c627c812f..fed24d6d198 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -6,7 +6,7 @@ namespace Content.Server.Botany.Systems; // For all the very common stuff all plants are expected to do. // TODO: make CO2Boost (add potency if the plant can eat an increasing amount of CO2). separate PR post-merge -// TOOD: make GrowLight (run bonus ticks if theres a grow light nearby). separate PR post-merge. +// TODO: make GrowLight (run bonus ticks if theres a grow light nearby). separate PR post-merge. public sealed class BasicGrowthSystem : PlantGrowthSystem { [Dependency] private readonly BotanySystem _botany = default!; @@ -49,6 +49,15 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem if (holder == null || holder.Seed == null || holder.Dead) return; + // Check if the plant is viable + if (holder.Seed.Viable == false) + { + holder.Health -= _random.Next(5, 10) * HydroponicsSpeedMultiplier; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + return; + } + // Advance plant age here. if (holder.SkipAging > 0) holder.SkipAging--; @@ -138,7 +147,5 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem holder.Health -= healthMod; } } - - //TODO: put Viable back on seed and check it here, or split it off to a separate system and check there? } } diff --git a/Content.Server/Botany/Systems/BotanySwabSystem.cs b/Content.Server/Botany/Systems/BotanySwabSystem.cs index ec10b888403..bba7e056734 100644 --- a/Content.Server/Botany/Systems/BotanySwabSystem.cs +++ b/Content.Server/Botany/Systems/BotanySwabSystem.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Robust.Shared.Random; using Robust.Shared.Serialization.Manager; using System.Linq; +using System.Collections.Generic; namespace Content.Server.Botany.Systems; @@ -71,7 +72,15 @@ public sealed class BotanySwabSystem : EntitySystem // Pick up pollen swab.SeedData = plant.Seed; if (plant.Seed != null) - swab.components = plant.Seed.GrowthComponents.ToList(); //TODO: copy components in case plant changes after sampling. test this works. + { + // Deep copy components to prevent changes to the original plant from affecting the swab + swab.components = new List(); + foreach (var component in plant.Seed.GrowthComponents) + { + var copiedComponent = component.DupeComponent(); + swab.components.Add(copiedComponent); + } + } _popupSystem.PopupEntity(Loc.GetString("botany-swab-from"), args.Args.Target.Value, args.Args.User); } diff --git a/Content.Server/Botany/Systems/MutationSystem.cs b/Content.Server/Botany/Systems/MutationSystem.cs index 90698f87c27..d6c9f90d4ca 100644 --- a/Content.Server/Botany/Systems/MutationSystem.cs +++ b/Content.Server/Botany/Systems/MutationSystem.cs @@ -55,6 +55,22 @@ public sealed class MutationSystem : EntitySystem } CheckRandomMutations(plantHolder, ref seed, severity); + EnsureGrowthComponents(plantHolder, seed); + } + + /// + /// Ensures that the plant has all the growth components specified in the seed data. + /// + private void EnsureGrowthComponents(EntityUid plantHolder, SeedData seed) + { + foreach (var component in seed.GrowthComponents) + { + if (!EntityManager.HasComponent(plantHolder, component.GetType())) + { + var newComponent = component.DupeComponent(); + EntityManager.AddComponent(plantHolder, newComponent); + } + } } public SeedData Cross(SeedData a, SeedData b) diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index f8fc1dab347..216461ba91d 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -25,7 +25,6 @@ using Robust.Shared.Serialization.Manager; using Robust.Shared.Timing; using Content.Shared.Administration.Logs; using Content.Shared.Containers.ItemSlots; -using Content.Shared.Database; using Content.Shared.Labels.Components; namespace Content.Server.Botany.Systems; @@ -458,7 +457,9 @@ public sealed class PlantHolderSystem : EntitySystem UpdateSprite(uid, component); } - //TODO: kill this bullshit + /// + /// Ensures all plant holder levels are within valid ranges. + /// public void CheckLevelSanity(EntityUid uid, PlantHolderComponent? component = null) { if (!Resolve(uid, ref component)) diff --git a/Content.Server/EntityEffects/Effects/PlantMutateGases.cs b/Content.Server/EntityEffects/Effects/PlantMutateGases.cs index b7b7cee998f..c47105b1e17 100644 --- a/Content.Server/EntityEffects/Effects/PlantMutateGases.cs +++ b/Content.Server/EntityEffects/Effects/PlantMutateGases.cs @@ -38,7 +38,9 @@ public sealed partial class PlantMutateExudeGasses : EntityEffect protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) { - return "TODO"; + return Loc.GetString("reagent-effect-guidebook-plant-mutate-exude-gasses", + ("minValue", MinValue), + ("maxValue", MaxValue)); } } @@ -72,6 +74,8 @@ public sealed partial class PlantMutateConsumeGasses : EntityEffect protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) { - return "TODO"; + return Loc.GetString("reagent-effect-guidebook-plant-mutate-consume-gasses", + ("minValue", MinValue), + ("maxValue", MaxValue)); } } diff --git a/Content.Server/EntityEffects/EntityEffectSystem.cs b/Content.Server/EntityEffects/EntityEffectSystem.cs index 1277c8df594..b279920737c 100644 --- a/Content.Server/EntityEffects/EntityEffectSystem.cs +++ b/Content.Server/EntityEffects/EntityEffectSystem.cs @@ -164,8 +164,16 @@ public sealed class EntityEffectSystem : EntitySystem return; } - // TODO: Someone needs to figure out how to do this for non-reagent effects. - throw new NotImplementedException(); + // For non-reagent effects, we need to find the organ entity differently + var metabolizer = EntityManager.GetComponentOrNull(args.Args.TargetEntity); + if (metabolizer != null) + { + args.Result = OrganCondition(args.Condition, (args.Args.TargetEntity, metabolizer)); + return; + } + + // If no metabolizer is found, the condition fails + args.Result = false; } public bool OrganCondition(OrganType condition, Entity metabolizer) @@ -514,36 +522,40 @@ public sealed class EntityEffectSystem : EntitySystem private void OnExecuteAreaReactionEffect(ref ExecuteEntityEffectEvent args) { + var transform = Comp(args.Args.TargetEntity); + var coords = _xform.GetMapCoordinates(args.Args.TargetEntity, xform: transform); + var range = args.Effect.Range; + 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 = Comp(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) && _turf.IsSpace(tileRef)) - return; - - var coords = _map.MapToGrid(gridUid, mapCoords); - var ent = Spawn(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; + range *= reagentArgs.Scale.Float(); } - // TODO: Someone needs to figure out how to do this for non-reagent effects. - throw new NotImplementedException(); + var entities = _map.GetEntitiesInRange(coords, range); + var reagent = args.Effect.Reagent; + var quantity = args.Effect.Quantity; + + if (args.Args is EntityEffectReagentArgs reagentArgs2) + { + quantity *= reagentArgs2.Quantity.Int(); + } + + foreach (var entity in entities) + { + if (entity == args.Args.TargetEntity) + continue; + + if (TryComp(entity, out var solutionContainer)) + { + if (SolutionContainerSystem.TryGetSolution(entity, args.Effect.Solution, out var solution)) + { + SolutionContainerSystem.TryAddReagent(solution.Value, reagent, quantity); + } + } + } + + if (args.Effect.Sound != null) + _audio.PlayPvs(args.Effect.Sound, reagentArgs.TargetEntity, AudioParams.Default.WithVariation(0.25f)); } private void OnExecuteCauseZombieInfection(ref ExecuteEntityEffectEvent args) diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs index 5cc12a9b695..dd5865e461b 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs @@ -298,7 +298,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem /// /// Dirties a solution entity that has been modified and prompts updates to chemical reactions and overflow state. - /// Should be invoked whenever a solution entity is modified. + /// Should be invoked whenever a solution entity is changed. /// /// /// 90% of this system is ensuring that this proc is invoked whenever a solution entity is changed. The other 10% is this proc. @@ -572,12 +572,8 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem if (quantity == 0) return false; - // TODO This should be made into a function that directly transfers reagents. - // Currently this is quite inefficient. - solution.AddSolution(source.SplitSolution(quantity), PrototypeManager); - - UpdateChemicals(soln); - return true; + // Use the more efficient direct transfer method + return TryDirectTransferReagents(soln, source, quantity); } /// @@ -815,8 +811,22 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem return; } - var colorHex = solution.GetColor(PrototypeManager) - .ToHexNoAlpha(); //TODO: If the chem has a dark color, the examine text becomes black on a black background, which is unreadable. + var colorHex = primary.SubstanceColor + .ToHexNoAlpha(); + + // Ensure readability for dark colors by checking contrast + // For very dark colors, use a lighter variant + if (primary.SubstanceColor.R + primary.SubstanceColor.G + primary.SubstanceColor.B < 0.3f * 3) + { + // Create a lighter version of the color + var lighterColor = new Color( + Math.Min(1f, primary.SubstanceColor.R + 0.3f), + Math.Min(1f, primary.SubstanceColor.G + 0.3f), + Math.Min(1f, primary.SubstanceColor.B + 0.3f), + primary.SubstanceColor.A + ); + colorHex = lighterColor.ToHexNoAlpha(); + } var messageString = "shared-solution-container-component-on-examine-main-text"; using (args.PushGroup(nameof(ExaminableSolutionComponent))) @@ -1233,4 +1243,33 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem dissolvedReagentAmount += overflow; return dissolvedReagentAmount; } + + /// + /// Directly transfers reagents from one solution to another without creating intermediate solutions. + /// + public bool TryDirectTransferReagents(Entity targetSoln, Solution source, FixedPoint2 quantity) + { + var (uid, comp) = targetSoln; + var solution = comp.Solution; + + if (quantity <= FixedPoint2.Zero || source.Volume <= FixedPoint2.Zero) + return false; + + quantity = FixedPoint2.Min(quantity, solution.AvailableVolume, source.Volume); + if (quantity == 0) + return false; + + // Directly transfer reagents without creating intermediate solutions + foreach (var (reagentId, amount) in source.Contents) + { + var transferAmount = amount * (quantity / source.Volume); + if (transferAmount > FixedPoint2.Zero) + { + solution.AddReagent(reagentId, transferAmount); + } + } + + UpdateChemicals(targetSoln); + return true; + } } diff --git a/Content.Shared/EntityEffects/EffectConditions/SolutionTemperature.cs b/Content.Shared/EntityEffects/EffectConditions/SolutionTemperature.cs index e2febd8f483..dccd6b8ed07 100644 --- a/Content.Shared/EntityEffects/EffectConditions/SolutionTemperature.cs +++ b/Content.Shared/EntityEffects/EffectConditions/SolutionTemperature.cs @@ -1,4 +1,6 @@ +using Content.Shared.EntityEffects; using Robust.Shared.Prototypes; +using Content.Shared.Chemistry.Components.SolutionManager; namespace Content.Shared.EntityEffects.EffectConditions; @@ -23,8 +25,20 @@ public sealed partial class SolutionTemperature : EntityEffectCondition reagentArgs.Source.Temperature <= Max; } - // TODO: Someone needs to figure out how to do this for non-reagent effects. - throw new NotImplementedException(); + // For non-reagent effects, we need to find the solution differently + if (args.EntityManager.TryGetComponent(args.TargetEntity, out SolutionContainerManagerComponent? container) && + container != null && container.Solutions != null) + { + // Check the first available solution for temperature + foreach (var (name, solution) in container.Solutions) + { + if (solution.Temperature >= Min && solution.Temperature <= Max) + return true; + } + } + + // If no suitable solution is found, the condition fails + return false; } public override string GuidebookExplanation(IPrototypeManager prototype) diff --git a/Content.Shared/Tools/Components/WelderComponent.cs b/Content.Shared/Tools/Components/WelderComponent.cs index a9111c7f534..bff55b8e589 100644 --- a/Content.Shared/Tools/Components/WelderComponent.cs +++ b/Content.Shared/Tools/Components/WelderComponent.cs @@ -11,9 +11,6 @@ namespace Content.Shared.Tools.Components; /// /// Handles fuel consumption for the tool and allows it to explode welding fuel tanks. /// -/// -/// TODO: De-hardcode welder bombing. -/// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), AutoGenerateComponentPause] [Access(typeof(SharedToolSystem))] public sealed partial class WelderComponent : Component From 85c25ff639afbea9981c2d7d6b3e3755bb1cb286 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Thu, 31 Jul 2025 06:30:09 +0300 Subject: [PATCH 05/53] update 2 --- Content.Server/Botany/SeedPrototype.cs | 48 +++++++++- .../Botany/Systems/BotanySwabSystem.cs | 48 +++------- .../Botany/Systems/BotanySystem.Seed.cs | 1 + .../Botany/Systems/PlantGrowthSystem.cs | 56 +++++------ .../Botany/Systems/PlantHolderSystem.cs | 1 + .../Effects/PlantMutateHarvest.cs | 30 ------ .../EntityEffects/EntityEffectSystem.cs | 95 ++++++++++--------- .../Effects/PlantMutateHarvest.cs | 9 +- .../en-US/guidebook/chemistry/effects.ftl | 13 +++ 9 files changed, 147 insertions(+), 154 deletions(-) delete mode 100644 Content.Server/EntityEffects/Effects/PlantMutateHarvest.cs diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index fdce7ddf888..c019d13f377 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -1,6 +1,7 @@ using Content.Server.Botany.Components; using Content.Server.Botany.Systems; using Content.Server.EntityEffects; +using Content.Shared.Database; using Content.Shared.EntityEffects; using Content.Shared.Random; using Robust.Shared.Audio; @@ -177,6 +178,24 @@ public partial class SeedData /// [DataField] public List GrowthComponents = new(); + + /// + /// Whether this seed is viable for growth. + /// + [DataField] + public bool Viable = true; + + /// + /// Log impact for harvest operations. + /// + [DataField] + public LogImpact? HarvestLogImpact; + + /// + /// Log impact for plant operations. + /// + [DataField] + public LogImpact? PlantLogImpact; //{ // new PlantComponent(), // new BasicGrowthComponent(), @@ -192,15 +211,18 @@ public partial class SeedData var newSeed = new SeedData { - GrowthComponents = GrowthComponents, + GrowthComponents = new List(), + Viable = Viable, + HarvestLogImpact = HarvestLogImpact, + PlantLogImpact = PlantLogImpact, Name = Name, Noun = Noun, DisplayName = DisplayName, Mysterious = Mysterious, PacketPrototype = PacketPrototype, - ProductPrototypes = new List(ProductPrototypes), - MutationPrototypes = new List>(MutationPrototypes), + ProductPrototypes = new List(ProductPrototypes), + MutationPrototypes = new List(MutationPrototypes), Chemicals = new Dictionary(Chemicals), ToxinsTolerance = ToxinsTolerance, @@ -228,6 +250,12 @@ public partial class SeedData Unique = true, }; + // Deep copy growth components + foreach (var component in GrowthComponents) + { + newSeed.GrowthComponents.Add(component.DupeComponent()); + } + newSeed.Mutations.AddRange(Mutations); return newSeed; } @@ -240,14 +268,18 @@ public partial class SeedData { var newSeed = new SeedData { + GrowthComponents = new List(), + Viable = other.Viable, + HarvestLogImpact = other.HarvestLogImpact, + PlantLogImpact = other.PlantLogImpact, Name = other.Name, Noun = other.Noun, DisplayName = other.DisplayName, Mysterious = other.Mysterious, PacketPrototype = other.PacketPrototype, - ProductPrototypes = new List(other.ProductPrototypes), - MutationPrototypes = new List>(other.MutationPrototypes), + ProductPrototypes = new List(other.ProductPrototypes), + MutationPrototypes = new List(other.MutationPrototypes), Chemicals = new Dictionary(Chemicals), @@ -292,6 +324,12 @@ public partial class SeedData } } + // Deep copy growth components from the new species + foreach (var component in other.GrowthComponents) + { + newSeed.GrowthComponents.Add(component.DupeComponent()); + } + return newSeed; } } diff --git a/Content.Server/Botany/Systems/BotanySwabSystem.cs b/Content.Server/Botany/Systems/BotanySwabSystem.cs index bba7e056734..9231f0c2cd5 100644 --- a/Content.Server/Botany/Systems/BotanySwabSystem.cs +++ b/Content.Server/Botany/Systems/BotanySwabSystem.cs @@ -4,10 +4,8 @@ using Content.Shared.DoAfter; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Swab; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Robust.Shared.Random; using Robust.Shared.Serialization.Manager; -using System.Linq; using System.Collections.Generic; namespace Content.Server.Botany.Systems; @@ -90,45 +88,23 @@ public sealed class BotanySwabSystem : EntitySystem if (old == null) return; - for (int index = 0; index < old.GrowthComponents.Count(); index++) + // Cross-pollenate the plants + plant.Seed = _mutationSystem.Cross(swab.SeedData, old); + + // Transfer old plant pollen to swab + swab.SeedData = old; + + // Copy components from the old plant to the swab + if (old.GrowthComponents != null) { - var c1 = old.GrowthComponents[index]; - foreach (var c2 in swab.components) + swab.components = new List(); + foreach (var component in old.GrowthComponents) { - if (c1.GetType() == c2.GetType()) - { - if (_random.Prob(0.5f)) - _serializationManager.CopyTo(c2, ref c1, notNullableOverride: true); - } + var copiedComponent = component.DupeComponent(); + swab.components.Add(copiedComponent); } } - //Growth Components mean that those systems listed for the BotanySwabDoAfterEvent and do their stuff. - //Wait, no. If the swab has components the plant doesn't that wont fire off. - //I DO need to check that here (though maybe I can get away with calling an event for it instead of looking up systems? - //Or maybe some override function that only applies the Cross math (since the other effect is copying the component?) - - //OR OR MAYBE THIS: the event fires, this handles ones that aren't on the plant, and those system handle the event for ones that do? - //that might be more work? - var plantcomps = EntityManager.GetComponents(args.Args.Target.Value); - foreach (var gc in swab.components) - { - if (plantcomps.Any(p => p.GetType() == gc.GetType())) - { - //fire the event or override to handle the 50% variable swap test. - } - else - { - //copy the component over on the 50% chance. - if (_random.Prob(0.5f)) { - //EntityManager.add - } - } - } - - - plant.Seed = _mutationSystem.Cross(swab.SeedData, old); // Cross-pollenate - swab.SeedData = old; // Transfer old plant pollen to swab _popupSystem.PopupEntity(Loc.GetString("botany-swab-to"), args.Args.Target.Value, args.Args.User); } diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index 912f7d1168a..7e15673ca5e 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -16,6 +16,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.Administration.Logs; using Content.Shared.Database; +using LogType = Content.Shared.Database.LogType; namespace Content.Server.Botany.Systems; diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs index 521fc183448..24cd20f8830 100644 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -7,14 +7,17 @@ namespace Content.Server.Botany.Systems; [ByRefEvent] public readonly record struct OnPlantGrowEvent; -//May need to be PlantSystem, since this looks for PlantComponent. +/// +/// Base system for plant growth mechanics. Handles the core growth cycle and provides +/// common functionality for all plant growth systems. +/// public abstract class PlantGrowthSystem : EntitySystem { [Dependency] protected readonly IRobustRandom _random = default!; [Dependency] protected readonly IGameTiming _gameTiming = default!; public TimeSpan nextUpdate = TimeSpan.Zero; - public TimeSpan updateDelay = TimeSpan.FromSeconds(15); //PlantHolder has a 15 second delay on cycles, but checks every 3 for sprite updates. + public TimeSpan updateDelay = TimeSpan.FromSeconds(15); // PlantHolder has a 15 second delay on cycles public const float HydroponicsSpeedMultiplier = 1f; public const float HydroponicsConsumptionMultiplier = 2f; @@ -22,50 +25,35 @@ public abstract class PlantGrowthSystem : EntitySystem public override void Initialize() { base.Initialize(); - - //This might be overcomplicating things. Maybe I should just create a new event here and - //have each system listen for that instead? - - - //List subsystems = new List(); - //Dictionary subSystemDict = new Dictionary(); - //foreach (var system in EntityManager.EntitySysManager.GetEntitySystemTypes()) - //{ - // if (system is PlantGrowthSystem && system.Name != "PlantGrowthSystem") - // { - // subsystems.Add(system); - - // //find component by name? - // EntityManager.getco - // } - //} } - // TO INVESTIGATE: can I figure out some way here to connect all the plant growth systems and components so - // I don't have to give each one of those their own Update() method that listens for gameticks? - // It would be much less code to do it here once instead of in each new system. - // Maybe even if its forcing it by name, to prove its doable? - public override void Update(float frameTime) { if (nextUpdate > _gameTiming.CurTime) return; - //This does not work as a universal check. I do need a baseline PlantComponent. - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var plantComponent)) + // Query for plant holders that have seeds and are not dead + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var plantHolder)) { - int a = 1; //I want to see each entity once, not each component once. - //Update(uid, unviableGrowthComponent); + // Only process plants that have seeds and are alive + if (plantHolder.Seed == null || plantHolder.Dead) + continue; + + // Check if it's time for this plant to grow + if (_gameTiming.CurTime < plantHolder.LastCycle + plantHolder.CycleDelay) + continue; + var plantGrow = new OnPlantGrowEvent(); RaiseLocalEvent(uid, ref plantGrow); } + nextUpdate = _gameTiming.CurTime + updateDelay; } - - - + /// + /// Affects the growth of a plant by modifying its age or production timing. + /// public void AffectGrowth(int amount, PlantHolderComponent? component = null) { if (component == null || component.Seed == null) @@ -75,14 +63,14 @@ public abstract class PlantGrowthSystem : EntitySystem { if (component.Age < component.Seed.Maturation) component.Age += amount; - else if (!component.Harvest && component.Seed.Yield <= 0f) + 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) + else if (!component.Harvest && component.Seed.Yield > 0f) component.LastProduce += amount; } } diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 216461ba91d..b919e4607f9 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -25,6 +25,7 @@ using Robust.Shared.Serialization.Manager; using Robust.Shared.Timing; using Content.Shared.Administration.Logs; using Content.Shared.Containers.ItemSlots; +using LogType = Content.Shared.Database.LogType; using Content.Shared.Labels.Components; namespace Content.Server.Botany.Systems; diff --git a/Content.Server/EntityEffects/Effects/PlantMutateHarvest.cs b/Content.Server/EntityEffects/Effects/PlantMutateHarvest.cs deleted file mode 100644 index c92b154c018..00000000000 --- a/Content.Server/EntityEffects/Effects/PlantMutateHarvest.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Content.Server.Botany; -using Content.Server.Botany.Components; -using Content.Shared.EntityEffects; -using Robust.Shared.Prototypes; - -namespace Content.Server.EntityEffects.Effects; - -/// -/// Upgrades a plant's harvest type. -/// -public sealed partial class PlantMutateHarvest : EntityEffect -{ - public override void Effect(EntityEffectBaseArgs args) - { - var plantholder = args.EntityManager.GetComponent(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) - args.EntityManager.EnsureComponent(args.TargetEntity); - } - - protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) - { - return "TODO"; - } -} diff --git a/Content.Server/EntityEffects/EntityEffectSystem.cs b/Content.Server/EntityEffects/EntityEffectSystem.cs index b279920737c..87ca8543490 100644 --- a/Content.Server/EntityEffects/EntityEffectSystem.cs +++ b/Content.Server/EntityEffects/EntityEffectSystem.cs @@ -522,40 +522,36 @@ public sealed class EntityEffectSystem : EntitySystem private void OnExecuteAreaReactionEffect(ref ExecuteEntityEffectEvent args) { - var transform = Comp(args.Args.TargetEntity); - var coords = _xform.GetMapCoordinates(args.Args.TargetEntity, xform: transform); - var range = args.Effect.Range; - if (args.Args is EntityEffectReagentArgs reagentArgs) { - range *= reagentArgs.Scale.Float(); - } + if (reagentArgs.Source == null) + return; - var entities = _map.GetEntitiesInRange(coords, range); - var reagent = args.Effect.Reagent; - var quantity = args.Effect.Quantity; + var spreadAmount = (int) Math.Max(0, Math.Ceiling((reagentArgs.Quantity / args.Effect.OverflowThreshold).Float())); + var splitSolution = reagentArgs.Source.SplitSolution(reagentArgs.Source.Volume); + var transform = Comp(reagentArgs.TargetEntity); + var mapCoords = _xform.GetMapCoordinates(reagentArgs.TargetEntity, xform: transform); - if (args.Args is EntityEffectReagentArgs reagentArgs2) - { - quantity *= reagentArgs2.Quantity.Int(); - } - - foreach (var entity in entities) - { - if (entity == args.Args.TargetEntity) - continue; - - if (TryComp(entity, out var solutionContainer)) + if (!_mapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid) || + !_map.TryGetTileRef(gridUid, grid, transform.Coordinates, out var tileRef)) { - if (SolutionContainerSystem.TryGetSolution(entity, args.Effect.Solution, out var solution)) - { - SolutionContainerSystem.TryAddReagent(solution.Value, reagent, quantity); - } + return; } + + if (_spreader.RequiresFloorToSpread(args.Effect.PrototypeId) && _turf.IsSpace(tileRef)) + return; + + var coords = _map.MapToGrid(gridUid, mapCoords); + var ent = Spawn(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; } - if (args.Effect.Sound != null) - _audio.PlayPvs(args.Effect.Sound, reagentArgs.TargetEntity, AudioParams.Default.WithVariation(0.25f)); + // TODO: Someone needs to figure out how to do this for non-reagent effects. + throw new NotImplementedException(); } private void OnExecuteCauseZombieInfection(ref ExecuteEntityEffectEvent args) @@ -896,59 +892,64 @@ public sealed class EntityEffectSystem : EntitySystem private void OnExecutePlantMutateConsumeGasses(ref ExecuteEntityEffectEvent args) { - var plantholder = Comp(args.Args.TargetEntity); - - if (plantholder.Seed == null) + if (!TryComp(args.Args.TargetEntity, out var plantholder) || + plantholder.Seed == null) return; - var gasses = plantholder.Seed.ExudeGasses; + // Get or create the gas growth component + var gasComponent = EnsureComp(args.Args.TargetEntity); // 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().ToList()); - if (gasses.ContainsKey(gas)) + if (gasComponent.ConsumeGasses.ContainsKey(gas)) { - gasses[gas] += amount; + gasComponent.ConsumeGasses[gas] += amount; } else { - gasses.Add(gas, amount); + gasComponent.ConsumeGasses.Add(gas, amount); } } private void OnExecutePlantMutateExudeGasses(ref ExecuteEntityEffectEvent args) { - var plantholder = Comp(args.Args.TargetEntity); - - if (plantholder.Seed == null) + if (!TryComp(args.Args.TargetEntity, out var plantholder) || + plantholder.Seed == null) return; - var gasses = plantholder.Seed.ConsumeGasses; + // Get or create the gas growth component + var gasComponent = EnsureComp(args.Args.TargetEntity); // 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().ToList()); - if (gasses.ContainsKey(gas)) + if (gasComponent.ExudeGasses.ContainsKey(gas)) { - gasses[gas] += amount; + gasComponent.ExudeGasses[gas] += amount; } else { - gasses.Add(gas, amount); + gasComponent.ExudeGasses.Add(gas, amount); } } private void OnExecutePlantMutateHarvest(ref ExecuteEntityEffectEvent args) { - var plantholder = Comp(args.Args.TargetEntity); - - if (plantholder.Seed == null) + if (!TryComp(args.Args.TargetEntity, out var plantholder) || + 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; + // Clone the seed to make it mutable + var clonedSeed = plantholder.Seed.Clone(); + + if (clonedSeed.HarvestRepeat == HarvestType.NoRepeat) + clonedSeed.HarvestRepeat = HarvestType.Repeat; + else if (clonedSeed.HarvestRepeat == HarvestType.Repeat) + clonedSeed.HarvestRepeat = HarvestType.SelfHarvest; + + // Update the plant holder with the cloned seed + plantholder.Seed = clonedSeed; } private void OnExecutePlantSpeciesChange(ref ExecuteEntityEffectEvent args) diff --git a/Content.Shared/EntityEffects/Effects/PlantMutateHarvest.cs b/Content.Shared/EntityEffects/Effects/PlantMutateHarvest.cs index 84d62931852..2a591a300d0 100644 --- a/Content.Shared/EntityEffects/Effects/PlantMutateHarvest.cs +++ b/Content.Shared/EntityEffects/Effects/PlantMutateHarvest.cs @@ -5,10 +5,15 @@ namespace Content.Shared.EntityEffects.Effects; /// /// Upgrades a plant's harvest type. /// -public sealed partial class PlantMutateHarvest : EventEntityEffect +public sealed partial class PlantMutateHarvest : EntityEffect { + public override void Effect(EntityEffectBaseArgs args) + { + // This is handled in EntityEffectSystem.cs + } + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) { - return "TODO"; + return Loc.GetString("reagent-effect-guidebook-plant-mutate-harvest"); } } diff --git a/Resources/Locale/en-US/guidebook/chemistry/effects.ftl b/Resources/Locale/en-US/guidebook/chemistry/effects.ftl index cd7bb21af2d..90032c7166e 100644 --- a/Resources/Locale/en-US/guidebook/chemistry/effects.ftl +++ b/Resources/Locale/en-US/guidebook/chemistry/effects.ftl @@ -428,3 +428,16 @@ reagent-effect-guidebook-plant-seeds-remove = [1] Removes the *[other] remove the } seeds of the plant + +reagent-effect-guidebook-plant-mutate-exude-gasses = + { $chance -> + [1] Mutates + *[other] mutate + } the plant to exude gases between {$minValue} and {$maxValue} moles + +reagent-effect-guidebook-plant-mutate-consume-gasses = + { $chance -> + [1] Mutates + *[other] mutate + } the plant to consume gases between {$minValue} and {$maxValue} moles + From 544614112f57226a8d24803a66b61e0b13b80de5 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Thu, 31 Jul 2025 16:48:01 +0300 Subject: [PATCH 06/53] TODO execution --- .../Botany/Components/BasicGrowthComponent.cs | 10 +- Resources/Maps/Salvage/vegan-meatball.yml | 12 +- Resources/Prototypes/Hydroponics/seeds.yml | 331 ++++++++++++++---- 3 files changed, 282 insertions(+), 71 deletions(-) diff --git a/Content.Server/Botany/Components/BasicGrowthComponent.cs b/Content.Server/Botany/Components/BasicGrowthComponent.cs index f4a0f2689cb..58663c363dd 100644 --- a/Content.Server/Botany/Components/BasicGrowthComponent.cs +++ b/Content.Server/Botany/Components/BasicGrowthComponent.cs @@ -3,13 +3,15 @@ namespace Content.Server.Botany.Components; [RegisterComponent] public sealed partial class BasicGrowthComponent : PlantGrowthComponent { - //Remaining TODOs/TO CHECKS - //Update all of seeds.yml to have condensed component set? or set prototype to have them? - //Ensure mutations work as expected for non-default growth components - //ensure components / values are copied over when seeds are made/clipped. + /// + /// Amount of water consumed per growth tick. + /// [DataField] public float WaterConsumption = 0.5f; + /// + /// Amount of nutrients consumed per growth tick. + /// [DataField] public float NutrientConsumption = 0.75f; } diff --git a/Resources/Maps/Salvage/vegan-meatball.yml b/Resources/Maps/Salvage/vegan-meatball.yml index 8a0e8d8ef79..04770c0678d 100644 --- a/Resources/Maps/Salvage/vegan-meatball.yml +++ b/Resources/Maps/Salvage/vegan-meatball.yml @@ -495,8 +495,10 @@ entities: toxinsTolerance: 4 heatTolerance: 10 idealHeat: 298 - waterConsumption: 0.4 - nutrientConsumption: 0.75 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.4 + NutrientConsumption: 0.75 exudeGasses: {} consumeGasses: {} chemicals: @@ -565,8 +567,10 @@ entities: toxinsTolerance: 4 heatTolerance: 10 idealHeat: 293 - waterConsumption: 0.6 - nutrientConsumption: 0.75 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 exudeGasses: {} consumeGasses: {} chemicals: diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index 55cec210af0..068e59130a7 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -15,7 +15,10 @@ yield: 3 potency: 5 idealLight: 8 - nutrientConsumption: 0.40 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -41,7 +44,10 @@ yield: 3 potency: 5 idealLight: 8 - nutrientConsumption: 0.40 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -67,7 +73,10 @@ yield: 3 potency: 5 idealLight: 8 - nutrientConsumption: 0.40 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -95,8 +104,11 @@ production: 6 yield: 2 idealLight: 9 - waterConsumption: 0.60 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Vitamin: Min: 1 @@ -122,8 +134,11 @@ production: 6 yield: 2 idealLight: 9 - waterConsumption: 0.60 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: MuteToxin: Min: 1 @@ -149,7 +164,10 @@ yield: 3 potency: 10 growthStages: 3 - waterConsumption: 0.60 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: JuiceCarrot: Min: 1 @@ -183,8 +201,10 @@ potency: 20 idealLight: 8 harvestRepeat: Repeat - nutrientConsumption: 0.6 - waterConsumption: 0.6 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.6 chemicals: Nutriment: Min: 1 @@ -219,6 +239,10 @@ yield: 3 potency: 10 idealLight: 8 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -245,6 +269,10 @@ yield: 4 potency: 1 idealLight: 8 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Vitamin: Min: 1 @@ -274,6 +302,10 @@ yield: 3 potency: 10 idealLight: 8 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -304,6 +336,10 @@ yield: 3 potency: 10 idealLight: 8 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -330,6 +366,10 @@ yield: 3 potency: 10 idealLight: 8 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Haloperidol: Min: 1 @@ -361,6 +401,10 @@ potency: 10 idealLight: 8 growthStages: 3 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -390,7 +434,10 @@ yield: 3 potency: 10 growthStages: 4 - waterConsumption: 0.60 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -420,6 +467,10 @@ potency: 10 growthStages: 3 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Sugar: Min: 4 @@ -442,9 +493,12 @@ yield: 2 potency: 20 growthStages: 5 - waterConsumption: 0.6 idealLight: 9 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Vitamin: Min: 1 @@ -487,8 +541,6 @@ yield: 5 potency: 1 growthStages: 3 - waterConsumption: 0.60 - nutrientConsumption: 0.50 lightTolerance: 6 idealHeat: 288 @@ -508,8 +560,6 @@ yield: 3 potency: 1 growthStages: 3 - waterConsumption: 0.60 - nutrientConsumption: 0.80 lightTolerance: 6 idealHeat: 288 @@ -531,11 +581,13 @@ production: 6 yield: 2 potency: 10 - waterConsumption: 0.60 - nutrientConsumption: 0.40 idealLight: 8 idealHeat: 298 splatPrototype: PuddleSplatter + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -565,11 +617,13 @@ production: 6 yield: 2 potency: 10 - waterConsumption: 0.60 - nutrientConsumption: 0.70 idealLight: 8 idealHeat: 298 splatPrototype: PuddleSplatter + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.7 chemicals: Nutriment: Min: 1 @@ -601,11 +655,13 @@ production: 6 yield: 2 potency: 10 - waterConsumption: 0.60 - nutrientConsumption: 0.70 idealLight: 8 idealHeat: 298 splatPrototype: PuddleSplatter + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.7 chemicals: Blood: Min: 1 @@ -633,12 +689,14 @@ production: 6 yield: 2 potency: 10 - waterConsumption: 0.60 - nutrientConsumption: 0.70 idealLight: 8 idealHeat: 298 growthStages: 2 splatPrototype: PuddleSplatter + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.7 chemicals: Blood: Min: 1 @@ -669,6 +727,10 @@ growthStages: 3 idealLight: 9 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -694,6 +756,10 @@ yield: 3 potency: 10 growthStages: 1 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -719,6 +785,10 @@ yield: 3 potency: 25 growthStages: 3 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -751,6 +821,10 @@ yield: 3 potency: 10 idealLight: 6 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -777,8 +851,10 @@ yield: 3 potency: 10 idealLight: 6 - waterConsumption: 0.75 - nutrientConsumption: 0.75 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.75 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -809,8 +885,11 @@ potency: 20 growthStages: 3 idealLight: 8 - waterConsumption: 0.60 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -839,8 +918,11 @@ potency: 20 growthStages: 3 idealLight: 8 - waterConsumption: 0.60 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -871,8 +953,11 @@ potency: 20 growthStages: 3 idealLight: 8 - waterConsumption: 0.60 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -903,9 +988,11 @@ potency: 1 growthStages: 3 lightTolerance: 6 - waterConsumption: 0.60 - nutrientConsumption: 0.50 idealHeat: 288 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.5 chemicals: Nutriment: Min: 1 @@ -927,9 +1014,12 @@ production: 12 yield: 2 potency: 20 - nutrientConsumption: 0.50 idealLight: 9 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.5 chemicals: Egg: Min: 4 @@ -954,9 +1044,12 @@ yield: 2 potency: 20 growthStages: 3 - waterConsumption: 0.40 idealLight: 9 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.4 + NutrientConsumption: 0.75 chemicals: THC: Min: 1 @@ -979,9 +1072,12 @@ yield: 2 potency: 20 growthStages: 3 - waterConsumption: 0.40 idealLight: 9 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.4 + NutrientConsumption: 0.75 chemicals: SpaceDrugs: Min: 1 @@ -1024,9 +1120,12 @@ yield: 2 potency: 20 growthStages: 3 - waterConsumption: 0.40 idealLight: 9 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.4 + NutrientConsumption: 0.75 chemicals: Nicotine: Min: 1 @@ -1051,8 +1150,11 @@ potency: 20 growthStages: 5 idealLight: 8 - waterConsumption: 0.60 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Histamine: Min: 1 @@ -1077,9 +1179,11 @@ potency: 20 growthStages: 5 idealLight: 8 - waterConsumption: 0.70 - nutrientConsumption: 0.80 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.7 + NutrientConsumption: 0.8 chemicals: SulfuricAcid: Min: 1 @@ -1109,6 +1213,10 @@ potency: 20 idealLight: 9 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: CapsaicinOil: Min: 1 @@ -1140,6 +1248,10 @@ potency: 20 idealLight: 9 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: FrostOil: Min: 1 @@ -1171,7 +1283,10 @@ yield: 3 potency: 10 growthStages: 3 - waterConsumption: 0.60 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1197,7 +1312,10 @@ yield: 3 potency: 10 growthStages: 5 - waterConsumption: 0.60 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Aloe: Min: 1 @@ -1225,7 +1343,10 @@ yield: 3 potency: 10 growthStages: 3 - waterConsumption: 0.60 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1251,7 +1372,10 @@ yield: 3 potency: 10 growthStages: 3 - waterConsumption: 0.60 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Ultravasculine: Min: 1 @@ -1279,7 +1403,10 @@ yield: 3 potency: 10 growthStages: 6 - waterConsumption: 0.60 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1317,7 +1444,10 @@ yield: 3 potency: 10 growthStages: 6 - waterConsumption: 0.60 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1353,7 +1483,10 @@ yield: 3 potency: 10 growthStages: 3 - waterConsumption: 0.60 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Stellibinin: Min: 1 @@ -1375,7 +1508,10 @@ yield: 3 potency: 10 growthStages: 3 - waterConsumption: 0.5 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Razorium: Min: 1 @@ -1397,8 +1533,10 @@ yield: 3 potency: 10 growthStages: 2 - waterConsumption: 0.60 - nutrientConsumption: 0.50 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.5 chemicals: Amatoxin: Min: 1 @@ -1428,6 +1566,10 @@ potency: 10 growthStages: 2 idealLight: 6 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1454,6 +1596,10 @@ potency: 10 growthStages: 2 idealLight: 6 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1480,6 +1626,10 @@ potency: 10 growthStages: 2 idealLight: 6 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1506,8 +1656,10 @@ potency: 5 growthStages: 4 idealLight: 5 - nutrientConsumption: 0.40 - waterConsumption: 0.60 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -1536,7 +1688,10 @@ yield: 3 potency: 5 idealLight: 7 - nutrientConsumption: 0.40 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -1558,7 +1713,10 @@ production: 3 yield: 2 potency: 10 - waterConsumption: 0.60 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1585,7 +1743,10 @@ yield: 3 potency: 5 idealLight: 7 - nutrientConsumption: 0.40 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -1611,6 +1772,10 @@ yield: 3 potency: 10 growthStages: 2 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1638,6 +1803,10 @@ yield: 1 potency: 1 idealLight: 8 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1667,6 +1836,10 @@ yield: 1 potency: 1 idealLight: 8 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1696,9 +1869,11 @@ production: 6 yield: 6 idealLight: 7 - waterConsumption: 1 - nutrientConsumption: 0.8 idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 1.0 + NutrientConsumption: 0.8 chemicals: Vitamin: Min: 1 @@ -1724,7 +1899,10 @@ production: 6 yield: 4 idealLight: 7 - nutrientConsumption: 0.6 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.6 chemicals: Nutriment: Min: 2 @@ -1753,7 +1931,10 @@ idealLight: 8 idealHeat: 298 growthStages: 4 - waterConsumption: 0.6 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 5 @@ -1783,8 +1964,10 @@ potency: 25 idealLight: 8 harvestRepeat: Repeat - nutrientConsumption: 0.5 - waterConsumption: 0.5 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.5 chemicals: Nutriment: Min: 1 @@ -1812,8 +1995,10 @@ potency: 25 idealLight: 8 harvestRepeat: Repeat - nutrientConsumption: 0.5 - waterConsumption: 0.5 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.5 chemicals: Happiness: Min: 1 @@ -1846,6 +2031,10 @@ potency: 10 idealHeat: 288 growthStages: 3 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: PumpkinFlesh: Min: 1 @@ -1872,6 +2061,10 @@ potency: 10 idealHeat: 288 growthStages: 3 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Ammonia: Min: 1 @@ -1904,7 +2097,10 @@ potency: 5 idealLight: 8 growthStages: 3 - waterConsumption: 0.60 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.75 chemicals: Fiber: Min: 5 @@ -1927,7 +2123,10 @@ potency: 5 idealLight: 8 growthStages: 3 - waterConsumption: 0.80 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.8 + NutrientConsumption: 0.75 chemicals: Fiber: Min: 5 @@ -1954,6 +2153,10 @@ yield: 5 potency: 10 idealLight: 6 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1979,8 +2182,10 @@ yield: 3 potency: 10 growthStages: 2 - waterConsumption: 0.60 - nutrientConsumption: 0.50 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.6 + NutrientConsumption: 0.5 chemicals: Artifexium: Min: 1 From 6a2bf6337a46ee95c966d49a930721bc44ec4586 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Thu, 31 Jul 2025 18:55:52 +0300 Subject: [PATCH 07/53] fix --- Resources/Maps/Salvage/vegan-meatball.yml | 22 +++-- Resources/Prototypes/Hydroponics/seeds.yml | 96 +++++++++++++++------- 2 files changed, 78 insertions(+), 40 deletions(-) diff --git a/Resources/Maps/Salvage/vegan-meatball.yml b/Resources/Maps/Salvage/vegan-meatball.yml index 04770c0678d..0fc58eaed87 100644 --- a/Resources/Maps/Salvage/vegan-meatball.yml +++ b/Resources/Maps/Salvage/vegan-meatball.yml @@ -490,17 +490,16 @@ entities: endurance: 100 weedTolerance: 5 pestTolerance: 5 - highPressureTolerance: 121 - lowPressureTolerance: 81 toxinsTolerance: 4 - heatTolerance: 10 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.4 NutrientConsumption: 0.75 - exudeGasses: {} - consumeGasses: {} + - type: AtmosphericGrowthComponent + IdealHeat: 298 + HeatTolerance: 10 + LowPressureTolerance: 81 + HighPressureTolerance: 121 chemicals: THC: Inherent: True @@ -562,17 +561,16 @@ entities: endurance: 100 weedTolerance: 5 pestTolerance: 5 - highPressureTolerance: 121 - lowPressureTolerance: 81 toxinsTolerance: 4 - heatTolerance: 10 - idealHeat: 293 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 - exudeGasses: {} - consumeGasses: {} + - type: AtmosphericGrowthComponent + IdealHeat: 293 + HeatTolerance: 10 + LowPressureTolerance: 81 + HighPressureTolerance: 121 chemicals: Stellibinin: Inherent: True diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index 068e59130a7..9ccdcf69f5b 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -104,11 +104,12 @@ production: 6 yield: 2 idealLight: 9 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Vitamin: Min: 1 @@ -134,11 +135,12 @@ production: 6 yield: 2 idealLight: 9 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: MuteToxin: Min: 1 @@ -466,11 +468,12 @@ yield: 3 potency: 10 growthStages: 3 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Sugar: Min: 4 @@ -494,11 +497,12 @@ potency: 20 growthStages: 5 idealLight: 9 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Vitamin: Min: 1 @@ -521,7 +525,12 @@ yield: 3 potency: 10 growthStages: 3 - idealHeat: 298 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 - type: seed id: towercap @@ -542,7 +551,12 @@ potency: 1 growthStages: 3 lightTolerance: 6 - idealHeat: 288 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 288 - type: seed id: steelcap @@ -561,7 +575,12 @@ potency: 1 growthStages: 3 lightTolerance: 6 - idealHeat: 288 + growthComponents: + - type: BasicGrowthComponent + WaterConsumption: 0.5 + NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 288 - type: seed id: tomato @@ -582,12 +601,13 @@ yield: 2 potency: 10 idealLight: 8 - idealHeat: 298 splatPrototype: PuddleSplatter growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.4 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Nutriment: Min: 1 @@ -618,12 +638,13 @@ yield: 2 potency: 10 idealLight: 8 - idealHeat: 298 splatPrototype: PuddleSplatter growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.7 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Nutriment: Min: 1 @@ -656,12 +677,13 @@ yield: 2 potency: 10 idealLight: 8 - idealHeat: 298 splatPrototype: PuddleSplatter growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.7 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Blood: Min: 1 @@ -690,13 +712,14 @@ yield: 2 potency: 10 idealLight: 8 - idealHeat: 298 growthStages: 2 splatPrototype: PuddleSplatter growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.7 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Blood: Min: 1 @@ -726,11 +749,12 @@ potency: 20 growthStages: 3 idealLight: 9 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Nutriment: Min: 1 @@ -885,11 +909,12 @@ potency: 20 growthStages: 3 idealLight: 8 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Nutriment: Min: 1 @@ -918,11 +943,12 @@ potency: 20 growthStages: 3 idealLight: 8 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Nutriment: Min: 1 @@ -953,11 +979,12 @@ potency: 20 growthStages: 3 idealLight: 8 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Nutriment: Min: 1 @@ -988,11 +1015,12 @@ potency: 1 growthStages: 3 lightTolerance: 6 - idealHeat: 288 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.5 + - type: AtmosphericGrowthComponent + IdealHeat: 288 chemicals: Nutriment: Min: 1 @@ -1015,11 +1043,12 @@ yield: 2 potency: 20 idealLight: 9 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.5 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Egg: Min: 4 @@ -1045,11 +1074,12 @@ potency: 20 growthStages: 3 idealLight: 9 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.4 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: THC: Min: 1 @@ -1073,11 +1103,12 @@ potency: 20 growthStages: 3 idealLight: 9 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.4 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: SpaceDrugs: Min: 1 @@ -1121,11 +1152,12 @@ potency: 20 growthStages: 3 idealLight: 9 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.4 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Nicotine: Min: 1 @@ -1150,11 +1182,12 @@ potency: 20 growthStages: 5 idealLight: 8 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Histamine: Min: 1 @@ -1179,11 +1212,12 @@ potency: 20 growthStages: 5 idealLight: 8 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.7 NutrientConsumption: 0.8 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: SulfuricAcid: Min: 1 @@ -1212,11 +1246,12 @@ yield: 2 potency: 20 idealLight: 9 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: CapsaicinOil: Min: 1 @@ -1247,11 +1282,12 @@ yield: 2 potency: 20 idealLight: 9 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: FrostOil: Min: 1 @@ -1869,11 +1905,12 @@ production: 6 yield: 6 idealLight: 7 - idealHeat: 298 growthComponents: - type: BasicGrowthComponent WaterConsumption: 1.0 NutrientConsumption: 0.8 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Vitamin: Min: 1 @@ -1929,12 +1966,13 @@ potency: 10 yield: 3 idealLight: 8 - idealHeat: 298 growthStages: 4 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 298 chemicals: Nutriment: Min: 5 @@ -2029,12 +2067,13 @@ production: 4 yield: 2 potency: 10 - idealHeat: 288 growthStages: 3 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 288 chemicals: PumpkinFlesh: Min: 1 @@ -2059,12 +2098,13 @@ production: 4 yield: 2 potency: 10 - idealHeat: 288 growthStages: 3 growthComponents: - type: BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 + - type: AtmosphericGrowthComponent + IdealHeat: 288 chemicals: Ammonia: Min: 1 From 2785cc6a6295c723761729b730a24c9ac821047a Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Thu, 31 Jul 2025 21:21:42 +0300 Subject: [PATCH 08/53] Update PlantGrowthComponent.cs --- Content.Server/Botany/Components/PlantGrowthComponent.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Content.Server/Botany/Components/PlantGrowthComponent.cs b/Content.Server/Botany/Components/PlantGrowthComponent.cs index 3db30624f30..5511f0eecad 100644 --- a/Content.Server/Botany/Components/PlantGrowthComponent.cs +++ b/Content.Server/Botany/Components/PlantGrowthComponent.cs @@ -3,7 +3,8 @@ using System.Security.Policy; namespace Content.Server.Botany.Components; [RegisterComponent] -public abstract partial class PlantGrowthComponent : Component { +[Virtual] +public partial class PlantGrowthComponent : Component { /// /// Creates a copy of this component. /// From a4ab02ec8a906597c54e4a05a5b96a017eb0f42e Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Thu, 31 Jul 2025 22:53:53 +0300 Subject: [PATCH 09/53] fix 1 --- .../Components/AtmosphericGrowthComponent.cs | 1 + .../Components/AutoHarvestGrowthComponent.cs | 1 + .../Botany/Components/BasicGrowthComponent.cs | 1 + .../ConsumeExudeGasGrowthComponent.cs | 1 + .../Botany/Components/PlantComponent.cs | 1 + .../Botany/Components/PlantGrowthComponent.cs | 2 +- .../Components/UnviableGrowthComponent.cs | 1 + .../Components/WeedPestGrowthComponent.cs | 1 + .../Botany/Systems/PlantHolderSystem.cs | 13 +- Resources/Maps/Salvage/vegan-meatball.yml | 8 +- Resources/Prototypes/Hydroponics/seeds.yml | 196 +++++++++--------- 11 files changed, 120 insertions(+), 106 deletions(-) diff --git a/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs b/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs index dbf6c5728ec..330b3210c08 100644 --- a/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs +++ b/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs @@ -1,6 +1,7 @@ namespace Content.Server.Botany.Components; [RegisterComponent] +[DataDefinition] public sealed partial class AtmosphericGrowthComponent : PlantGrowthComponent { [DataField] diff --git a/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs b/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs index 9d468cc789c..a56c9e0d23a 100644 --- a/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs +++ b/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs @@ -1,5 +1,6 @@ namespace Content.Server.Botany.Components; [RegisterComponent] +[DataDefinition] public sealed partial class AutoHarvestGrowthComponent : PlantGrowthComponent {} diff --git a/Content.Server/Botany/Components/BasicGrowthComponent.cs b/Content.Server/Botany/Components/BasicGrowthComponent.cs index 58663c363dd..bc71608afd4 100644 --- a/Content.Server/Botany/Components/BasicGrowthComponent.cs +++ b/Content.Server/Botany/Components/BasicGrowthComponent.cs @@ -1,6 +1,7 @@ namespace Content.Server.Botany.Components; [RegisterComponent] +[DataDefinition] public sealed partial class BasicGrowthComponent : PlantGrowthComponent { /// diff --git a/Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs b/Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs index 1da31d376b1..41b9e4a0682 100644 --- a/Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs +++ b/Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs @@ -3,6 +3,7 @@ using Content.Shared.Atmos; namespace Content.Server.Botany.Components; [RegisterComponent] +[DataDefinition] public sealed partial class ConsumeExudeGasGrowthComponent : PlantGrowthComponent { [DataField] public Dictionary ConsumeGasses = new(); diff --git a/Content.Server/Botany/Components/PlantComponent.cs b/Content.Server/Botany/Components/PlantComponent.cs index b77edc964fe..2da1f89a19f 100644 --- a/Content.Server/Botany/Components/PlantComponent.cs +++ b/Content.Server/Botany/Components/PlantComponent.cs @@ -1,5 +1,6 @@ namespace Content.Server.Botany.Components; [RegisterComponent] +[DataDefinition] public sealed partial class PlantComponent : PlantGrowthComponent {} diff --git a/Content.Server/Botany/Components/PlantGrowthComponent.cs b/Content.Server/Botany/Components/PlantGrowthComponent.cs index 5511f0eecad..c685755bcdc 100644 --- a/Content.Server/Botany/Components/PlantGrowthComponent.cs +++ b/Content.Server/Botany/Components/PlantGrowthComponent.cs @@ -3,7 +3,7 @@ using System.Security.Policy; namespace Content.Server.Botany.Components; [RegisterComponent] -[Virtual] +[DataDefinition] public partial class PlantGrowthComponent : Component { /// /// Creates a copy of this component. diff --git a/Content.Server/Botany/Components/UnviableGrowthComponent.cs b/Content.Server/Botany/Components/UnviableGrowthComponent.cs index c7fb0b45f86..e002139cdaf 100644 --- a/Content.Server/Botany/Components/UnviableGrowthComponent.cs +++ b/Content.Server/Botany/Components/UnviableGrowthComponent.cs @@ -1,5 +1,6 @@ namespace Content.Server.Botany.Components; [RegisterComponent] +[DataDefinition] public sealed partial class UnviableGrowthComponent : PlantGrowthComponent {} diff --git a/Content.Server/Botany/Components/WeedPestGrowthComponent.cs b/Content.Server/Botany/Components/WeedPestGrowthComponent.cs index b3b63ce8902..0e8cd82ff3c 100644 --- a/Content.Server/Botany/Components/WeedPestGrowthComponent.cs +++ b/Content.Server/Botany/Components/WeedPestGrowthComponent.cs @@ -1,6 +1,7 @@ namespace Content.Server.Botany.Components; [RegisterComponent] +[DataDefinition] public sealed partial class WeedPestGrowthComponent : PlantGrowthComponent { [DataField] diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index b919e4607f9..768dd31db7d 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -27,6 +27,7 @@ using Content.Shared.Administration.Logs; using Content.Shared.Containers.ItemSlots; using LogType = Content.Shared.Database.LogType; using Content.Shared.Labels.Components; +using System.Linq; namespace Content.Server.Botany.Systems; @@ -200,8 +201,13 @@ public sealed class PlantHolderSystem : EntitySystem } component.LastCycle = _gameTiming.CurTime; + // Ensure no existing growth components before adding new ones + var existingGrowthComponents = EntityManager.GetComponents(uid).ToList(); + foreach(var g in existingGrowthComponents) + EntityManager.RemoveComponent(uid, g); + foreach(var g in seed.GrowthComponents) - EntityManager.AddComponent(uid, _copier.CreateCopy(g, notNullableOverride: true)); + EntityManager.AddComponent(uid, _copier.CreateCopy(g, notNullableOverride: true), overwrite: true); if (TryComp(args.Used, out var paperLabel)) { @@ -595,8 +601,9 @@ public sealed class PlantHolderSystem : EntitySystem if (!Resolve(uid, ref component) || component.Seed == null) return; - //OK, how do i check the entity instead of the seed? - foreach(var g in EntityManager.GetComponents(uid)) + // Remove all growth components before planting new seed + var growthComponents = EntityManager.GetComponents(uid).ToList(); + foreach(var g in growthComponents) EntityManager.RemoveComponent(uid, g); component.YieldMod = 1; diff --git a/Resources/Maps/Salvage/vegan-meatball.yml b/Resources/Maps/Salvage/vegan-meatball.yml index 0fc58eaed87..e4afd9564aa 100644 --- a/Resources/Maps/Salvage/vegan-meatball.yml +++ b/Resources/Maps/Salvage/vegan-meatball.yml @@ -492,10 +492,10 @@ entities: pestTolerance: 5 toxinsTolerance: 4 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.4 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 HeatTolerance: 10 LowPressureTolerance: 81 @@ -563,10 +563,10 @@ entities: pestTolerance: 5 toxinsTolerance: 4 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 293 HeatTolerance: 10 LowPressureTolerance: 81 diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index 9ccdcf69f5b..244198f0ac7 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -16,7 +16,7 @@ potency: 5 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.4 chemicals: @@ -45,7 +45,7 @@ potency: 5 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.4 chemicals: @@ -74,7 +74,7 @@ potency: 5 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.4 chemicals: @@ -105,10 +105,10 @@ yield: 2 idealLight: 9 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Vitamin: @@ -136,10 +136,10 @@ yield: 2 idealLight: 9 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: MuteToxin: @@ -167,7 +167,7 @@ potency: 10 growthStages: 3 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 chemicals: @@ -204,7 +204,7 @@ idealLight: 8 harvestRepeat: Repeat growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.6 chemicals: @@ -242,7 +242,7 @@ potency: 10 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -272,7 +272,7 @@ potency: 1 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -305,7 +305,7 @@ potency: 10 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -339,7 +339,7 @@ potency: 10 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -369,7 +369,7 @@ potency: 10 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -404,7 +404,7 @@ idealLight: 8 growthStages: 3 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -437,7 +437,7 @@ potency: 10 growthStages: 4 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 chemicals: @@ -469,10 +469,10 @@ potency: 10 growthStages: 3 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Sugar: @@ -498,10 +498,10 @@ growthStages: 5 idealLight: 9 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Vitamin: @@ -526,10 +526,10 @@ potency: 10 growthStages: 3 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 - type: seed @@ -552,10 +552,10 @@ growthStages: 3 lightTolerance: 6 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 288 - type: seed @@ -576,10 +576,10 @@ growthStages: 3 lightTolerance: 6 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 288 - type: seed @@ -603,10 +603,10 @@ idealLight: 8 splatPrototype: PuddleSplatter growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.4 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Nutriment: @@ -640,10 +640,10 @@ idealLight: 8 splatPrototype: PuddleSplatter growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.7 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Nutriment: @@ -679,10 +679,10 @@ idealLight: 8 splatPrototype: PuddleSplatter growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.7 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Blood: @@ -715,10 +715,10 @@ growthStages: 2 splatPrototype: PuddleSplatter growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.7 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Blood: @@ -750,10 +750,10 @@ growthStages: 3 idealLight: 9 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Nutriment: @@ -781,7 +781,7 @@ potency: 10 growthStages: 1 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -810,7 +810,7 @@ potency: 25 growthStages: 3 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -846,7 +846,7 @@ potency: 10 idealLight: 6 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -876,7 +876,7 @@ potency: 10 idealLight: 6 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.75 NutrientConsumption: 0.75 chemicals: @@ -910,10 +910,10 @@ growthStages: 3 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Nutriment: @@ -944,10 +944,10 @@ growthStages: 3 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Nutriment: @@ -980,10 +980,10 @@ growthStages: 3 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Nutriment: @@ -1016,10 +1016,10 @@ growthStages: 3 lightTolerance: 6 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.5 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 288 chemicals: Nutriment: @@ -1044,10 +1044,10 @@ potency: 20 idealLight: 9 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.5 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Egg: @@ -1075,10 +1075,10 @@ growthStages: 3 idealLight: 9 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.4 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: THC: @@ -1104,10 +1104,10 @@ growthStages: 3 idealLight: 9 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.4 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: SpaceDrugs: @@ -1153,10 +1153,10 @@ growthStages: 3 idealLight: 9 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.4 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Nicotine: @@ -1183,10 +1183,10 @@ growthStages: 5 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Histamine: @@ -1213,10 +1213,10 @@ growthStages: 5 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.7 NutrientConsumption: 0.8 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: SulfuricAcid: @@ -1247,10 +1247,10 @@ potency: 20 idealLight: 9 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: CapsaicinOil: @@ -1283,10 +1283,10 @@ potency: 20 idealLight: 9 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: FrostOil: @@ -1320,7 +1320,7 @@ potency: 10 growthStages: 3 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 chemicals: @@ -1349,7 +1349,7 @@ potency: 10 growthStages: 5 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 chemicals: @@ -1380,7 +1380,7 @@ potency: 10 growthStages: 3 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 chemicals: @@ -1409,7 +1409,7 @@ potency: 10 growthStages: 3 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 chemicals: @@ -1440,7 +1440,7 @@ potency: 10 growthStages: 6 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 chemicals: @@ -1481,7 +1481,7 @@ potency: 10 growthStages: 6 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 chemicals: @@ -1520,7 +1520,7 @@ potency: 10 growthStages: 3 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 chemicals: @@ -1545,7 +1545,7 @@ potency: 10 growthStages: 3 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -1570,7 +1570,7 @@ potency: 10 growthStages: 2 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.5 chemicals: @@ -1603,7 +1603,7 @@ growthStages: 2 idealLight: 6 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -1633,7 +1633,7 @@ growthStages: 2 idealLight: 6 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -1663,7 +1663,7 @@ growthStages: 2 idealLight: 6 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -1693,7 +1693,7 @@ growthStages: 4 idealLight: 5 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.4 chemicals: @@ -1725,7 +1725,7 @@ potency: 5 idealLight: 7 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.4 chemicals: @@ -1750,7 +1750,7 @@ yield: 2 potency: 10 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 chemicals: @@ -1780,7 +1780,7 @@ potency: 5 idealLight: 7 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.4 chemicals: @@ -1809,7 +1809,7 @@ potency: 10 growthStages: 2 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -1840,7 +1840,7 @@ potency: 1 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -1873,7 +1873,7 @@ potency: 1 idealLight: 8 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -1906,10 +1906,10 @@ yield: 6 idealLight: 7 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 1.0 NutrientConsumption: 0.8 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Vitamin: @@ -1937,7 +1937,7 @@ yield: 4 idealLight: 7 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.6 chemicals: @@ -1968,10 +1968,10 @@ idealLight: 8 growthStages: 4 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 298 chemicals: Nutriment: @@ -2003,7 +2003,7 @@ idealLight: 8 harvestRepeat: Repeat growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.5 chemicals: @@ -2034,7 +2034,7 @@ idealLight: 8 harvestRepeat: Repeat growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.5 chemicals: @@ -2069,10 +2069,10 @@ potency: 10 growthStages: 3 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 288 chemicals: PumpkinFlesh: @@ -2100,10 +2100,10 @@ potency: 10 growthStages: 3 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 - - type: AtmosphericGrowthComponent + - !type:AtmosphericGrowthComponent IdealHeat: 288 chemicals: Ammonia: @@ -2138,7 +2138,7 @@ idealLight: 8 growthStages: 3 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.75 chemicals: @@ -2164,7 +2164,7 @@ idealLight: 8 growthStages: 3 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.8 NutrientConsumption: 0.75 chemicals: @@ -2194,7 +2194,7 @@ potency: 10 idealLight: 6 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.5 NutrientConsumption: 0.75 chemicals: @@ -2223,7 +2223,7 @@ potency: 10 growthStages: 2 growthComponents: - - type: BasicGrowthComponent + - !type:BasicGrowthComponent WaterConsumption: 0.6 NutrientConsumption: 0.5 chemicals: From d9ff9d55699562c32b9780506aad5f7d1ecfd6af Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Fri, 1 Aug 2025 01:45:46 +0300 Subject: [PATCH 10/53] growth correction --- .../Components/AtmosphericGrowthComponent.cs | 8 +- .../Components/AutoHarvestGrowthComponent.cs | 5 +- .../Botany/Components/BasicGrowthComponent.cs | 4 +- .../Components/UnviableGrowthComponent.cs | 8 +- .../Components/WeedPestGrowthComponent.cs | 16 +- Content.Server/Botany/SeedPrototype.cs | 21 +- .../Botany/Systems/AutoHarvestGrowthSystem.cs | 19 +- .../Botany/Systems/BotanySystem.Seed.cs | 4 +- .../Systems/ConsumeExudeGasGrowthSystem.cs | 30 +- .../Botany/Systems/PlantGrowthSystem.cs | 32 +- .../Botany/Systems/PlantHolderSystem.cs | 2 +- .../Botany/Systems/UnviableGrowthSystem.cs | 9 +- .../Botany/Systems/WeedPestGrowthSystem.cs | 20 +- Resources/Maps/Salvage/vegan-meatball.yml | 24 +- Resources/Prototypes/Hydroponics/seeds.yml | 336 +++++++++--------- 15 files changed, 297 insertions(+), 241 deletions(-) diff --git a/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs b/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs index 330b3210c08..6b85f262f9d 100644 --- a/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs +++ b/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs @@ -4,15 +4,15 @@ namespace Content.Server.Botany.Components; [DataDefinition] public sealed partial class AtmosphericGrowthComponent : PlantGrowthComponent { - [DataField] + [DataField("idealHeat")] public float IdealHeat = 293f; - [DataField] + [DataField("heatTolerance")] public float HeatTolerance = 10f; - [DataField] + [DataField("lowPressureTolerance")] public float LowPressureTolerance = 81f; - [DataField] + [DataField("lighPressureTolerance")] public float HighPressureTolerance = 121f; } diff --git a/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs b/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs index a56c9e0d23a..fdd9e3a704c 100644 --- a/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs +++ b/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs @@ -3,4 +3,7 @@ namespace Content.Server.Botany.Components; [RegisterComponent] [DataDefinition] public sealed partial class AutoHarvestGrowthComponent : PlantGrowthComponent -{} +{ + [DataField("harvestChance")] + public float HarvestChance = 0.1f; +} diff --git a/Content.Server/Botany/Components/BasicGrowthComponent.cs b/Content.Server/Botany/Components/BasicGrowthComponent.cs index bc71608afd4..0b447746aa0 100644 --- a/Content.Server/Botany/Components/BasicGrowthComponent.cs +++ b/Content.Server/Botany/Components/BasicGrowthComponent.cs @@ -7,12 +7,12 @@ public sealed partial class BasicGrowthComponent : PlantGrowthComponent /// /// Amount of water consumed per growth tick. /// - [DataField] + [DataField("waterConsumption")] public float WaterConsumption = 0.5f; /// /// Amount of nutrients consumed per growth tick. /// - [DataField] + [DataField("nutrientConsumption")] public float NutrientConsumption = 0.75f; } diff --git a/Content.Server/Botany/Components/UnviableGrowthComponent.cs b/Content.Server/Botany/Components/UnviableGrowthComponent.cs index e002139cdaf..2e85931adf4 100644 --- a/Content.Server/Botany/Components/UnviableGrowthComponent.cs +++ b/Content.Server/Botany/Components/UnviableGrowthComponent.cs @@ -3,4 +3,10 @@ namespace Content.Server.Botany.Components; [RegisterComponent] [DataDefinition] public sealed partial class UnviableGrowthComponent : PlantGrowthComponent -{} +{ + [DataField("deathChance")] + public float DeathChance = 0.1f; + + [DataField("deathDamage")] + public float DeathDamage = 6f; +} diff --git a/Content.Server/Botany/Components/WeedPestGrowthComponent.cs b/Content.Server/Botany/Components/WeedPestGrowthComponent.cs index 0e8cd82ff3c..5d5d90f5617 100644 --- a/Content.Server/Botany/Components/WeedPestGrowthComponent.cs +++ b/Content.Server/Botany/Components/WeedPestGrowthComponent.cs @@ -4,9 +4,21 @@ namespace Content.Server.Botany.Components; [DataDefinition] public sealed partial class WeedPestGrowthComponent : PlantGrowthComponent { - [DataField] + [DataField("weedTolerance")] public float WeedTolerance = 5f; - [DataField] + [DataField("pestTolerance")] public float PestTolerance = 5f; + + [DataField("weedGrowthChance")] + public float WeedGrowthChance = 0.01f; + + [DataField("weedGrowthAmount")] + public float WeedGrowthAmount = 0.5f; + + [DataField("pestDamageChance")] + public float PestDamageChance = 0.05f; + + [DataField("pestDamageAmount")] + public float PestDamageAmount = 1f; } diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index c019d13f377..a1ba7fc1f3e 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -9,6 +9,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; using Robust.Shared.Utility; +using Robust.Shared.Serialization.Manager; namespace Content.Server.Botany; @@ -99,7 +100,7 @@ public partial class SeedData public string PacketPrototype = "SeedBase"; /// - /// The entity prototype this seed spawns when it gets harvested. + /// The entity prototypes that are spawned when this type of seed is harvested. /// [DataField(customTypeSerializer: typeof(PrototypeIdListSerializer))] public List ProductPrototypes = new(); @@ -196,14 +197,8 @@ public partial class SeedData /// [DataField] public LogImpact? PlantLogImpact; - //{ - // new PlantComponent(), - // new BasicGrowthComponent(), - // new AtmosphericGrowthComponent(), - // new WeedPestGrowthComponent(), - // }; - //TODO: the mutation system should add the missing components when they mutate. - //This would be done with EnsureComp<> + //TODO: the mutation system should add the missing components when they mutate. + //This would be done with EnsureComp<> public SeedData Clone() { @@ -253,7 +248,9 @@ public partial class SeedData // Deep copy growth components foreach (var component in GrowthComponents) { - newSeed.GrowthComponents.Add(component.DupeComponent()); + // Use serialization manager for proper deep copying + var newComponent = IoCManager.Resolve().CreateCopy(component, notNullableOverride: true); + newSeed.GrowthComponents.Add(newComponent); } newSeed.Mutations.AddRange(Mutations); @@ -327,7 +324,9 @@ public partial class SeedData // Deep copy growth components from the new species foreach (var component in other.GrowthComponents) { - newSeed.GrowthComponents.Add(component.DupeComponent()); + // Use serialization manager for proper deep copying + var newComponent = IoCManager.Resolve().CreateCopy(component, notNullableOverride: true); + newSeed.GrowthComponents.Add(newComponent); } return newSeed; diff --git a/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs b/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs index f589e3f69a7..0aa75de4e84 100644 --- a/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs +++ b/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs @@ -1,9 +1,10 @@ using Content.Server.Botany.Components; +using Robust.Shared.Random; namespace Content.Server.Botany.Systems; + public sealed class AutoHarvestGrowthSystem : PlantGrowthSystem { - [Dependency] private readonly PlantHolderSystem _plantHolderSystem = default!; public override void Initialize() { base.Initialize(); @@ -15,9 +16,21 @@ public sealed class AutoHarvestGrowthSystem : PlantGrowthSystem PlantHolderComponent? holder = null; Resolve(uid, ref holder); - if (holder == null || holder.Seed == null || holder.Dead || !holder.Harvest) + if (holder == null || holder.Seed == null || holder.Dead) return; - _plantHolderSystem.AutoHarvest(uid, holder); + if (holder.Harvest && _random.Prob(component.HarvestChance)) + { + // Auto-harvest the plant + holder.Harvest = false; + holder.LastProduce = holder.Age; + + // Spawn the harvested items + if (holder.Seed.ProductPrototypes.Count > 0) + { + var product = _random.Pick(holder.Seed.ProductPrototypes); + var entity = Spawn(product, Transform(uid).Coordinates); + } + } } } diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index 7e15673ca5e..a2f8bfeb692 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -104,7 +104,7 @@ public sealed partial class BotanySystem : EntitySystem { var seed = Spawn(proto.PacketPrototype, coords); var seedComp = EnsureComp(seed); - seedComp.Seed = proto; + seedComp.Seed = proto.Clone(); seedComp.HealthOverride = healthOverride; var name = Loc.GetString(proto.Name); @@ -176,7 +176,7 @@ public sealed partial class BotanySystem : EntitySystem var produce = EnsureComp(entity); - produce.Seed = proto; + produce.Seed = proto.Clone(); ProduceGrown(entity, produce); _appearance.SetData(entity, ProduceVisuals.Potency, proto.Potency); diff --git a/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs index 9649888d94c..34562abcd0c 100644 --- a/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs +++ b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs @@ -6,6 +6,7 @@ namespace Content.Server.Botany.Systems; public sealed class ConsumeExudeGasGrowthSystem : PlantGrowthSystem { [Dependency] private readonly AtmosphereSystem _atmosphere = default!; + public override void Initialize() { base.Initialize(); @@ -22,26 +23,25 @@ public sealed class ConsumeExudeGasGrowthSystem : PlantGrowthSystem var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; - holder.MissingGas = 0; - if (component.ConsumeGasses.Count > 0) + // Consume Gasses + foreach (var (gas, amount) in component.ConsumeGasses) { - foreach (var (gas, amount) in component.ConsumeGasses) + if (environment.GetMoles(gas) >= amount) { - if (environment.GetMoles(gas) < amount) - { - holder.MissingGas++; - continue; - } - environment.AdjustMoles(gas, -amount); } + } - if (holder.MissingGas > 0) - { - holder.Health -= holder.MissingGas * HydroponicsSpeedMultiplier; - if (holder.DrawWarnings) - holder.UpdateSpriteAfterUpdate = true; - } + // Exude Gasses + foreach (var (gas, amount) in component.ExudeGasses) + { + environment.AdjustMoles(gas, amount); + } + + var containingMixture = _atmosphere.GetContainingMixture(uid, true, true); + if (containingMixture != null) + { + _atmosphere.Merge(containingMixture, environment); } } } diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs index 24cd20f8830..7039f5c3c29 100644 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -8,20 +8,16 @@ namespace Content.Server.Botany.Systems; public readonly record struct OnPlantGrowEvent; /// -/// Base system for plant growth mechanics. Handles the core growth cycle and provides -/// common functionality for all plant growth systems. +/// Handles the main growth cycle for all plants. /// -public abstract class PlantGrowthSystem : EntitySystem +public sealed class PlantGrowthCycleSystem : EntitySystem { - [Dependency] protected readonly IRobustRandom _random = default!; - [Dependency] protected readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; public TimeSpan nextUpdate = TimeSpan.Zero; public TimeSpan updateDelay = TimeSpan.FromSeconds(15); // PlantHolder has a 15 second delay on cycles - public const float HydroponicsSpeedMultiplier = 1f; - public const float HydroponicsConsumptionMultiplier = 2f; - public override void Initialize() { base.Initialize(); @@ -46,10 +42,30 @@ public abstract class PlantGrowthSystem : EntitySystem var plantGrow = new OnPlantGrowEvent(); RaiseLocalEvent(uid, ref plantGrow); + + // Update the last cycle time after processing growth + plantHolder.LastCycle = _gameTiming.CurTime; } nextUpdate = _gameTiming.CurTime + updateDelay; } +} + +/// +/// Base system for plant growth mechanics. Provides common functionality for all plant growth systems. +/// +public abstract class PlantGrowthSystem : EntitySystem +{ + [Dependency] protected readonly IRobustRandom _random = default!; + [Dependency] protected readonly IGameTiming _gameTiming = default!; + + public const float HydroponicsSpeedMultiplier = 1f; + public const float HydroponicsConsumptionMultiplier = 2f; + + public override void Initialize() + { + base.Initialize(); + } /// /// Affects the growth of a plant by modifying its age or production timing. diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 768dd31db7d..d0e28d65e54 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -188,7 +188,7 @@ public sealed class PlantHolderSystem : EntitySystem ("seedName", name), ("seedNoun", noun)), args.User, PopupType.Medium); - component.Seed = seed; + component.Seed = seed.Clone(); component.Dead = false; component.Age = 1; if (seeds.HealthOverride != null) diff --git a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs index cd2b8d3d762..08901008ebd 100644 --- a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs +++ b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Botany.Components; +using Robust.Shared.Random; namespace Content.Server.Botany.Systems; public sealed class UnviableGrowthSystem : PlantGrowthSystem @@ -17,6 +18,12 @@ public sealed class UnviableGrowthSystem : PlantGrowthSystem if (holder == null || holder.Seed == null || holder.Dead) return; - holder.Health -= 6 * _random.Next(1, 3) * HydroponicsSpeedMultiplier; + // Unviable plants have a chance to die each growth cycle + if (_random.Prob(component.DeathChance)) + { + holder.Health -= component.DeathDamage; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } } } diff --git a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs index 1980f18eb0c..a091fa372f9 100644 --- a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs +++ b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs @@ -18,20 +18,20 @@ public sealed class WeedPestGrowthSystem : PlantGrowthSystem if (holder == null || holder.Seed == null || holder.Dead) return; - // There's a small chance the pest population increases. Only happens with plants present. - if (_random.Prob(0.01f)) + // Weed growth logic + if (_random.Prob(component.WeedGrowthChance)) { - holder.PestLevel += 0.5f * HydroponicsSpeedMultiplier; + holder.WeedLevel += component.WeedGrowthAmount; if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; } - // Pest levels. - if (holder.PestLevel > component.PestTolerance) - holder.Health -= HydroponicsSpeedMultiplier; - - // Weed levels. - if (holder.WeedLevel >= component.WeedTolerance) - holder.Health -= HydroponicsSpeedMultiplier; + // Pest damage logic + if (_random.Prob(component.PestDamageChance)) + { + holder.Health -= component.PestDamageAmount; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } } } diff --git a/Resources/Maps/Salvage/vegan-meatball.yml b/Resources/Maps/Salvage/vegan-meatball.yml index e4afd9564aa..c6e85014e48 100644 --- a/Resources/Maps/Salvage/vegan-meatball.yml +++ b/Resources/Maps/Salvage/vegan-meatball.yml @@ -493,13 +493,13 @@ entities: toxinsTolerance: 4 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.4 - NutrientConsumption: 0.75 + waterConsumption: 0.4 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 - HeatTolerance: 10 - LowPressureTolerance: 81 - HighPressureTolerance: 121 + idealHeat: 298 + heatTolerance: 10 + lowPressureTolerance: 81 + highPressureTolerance: 121 chemicals: THC: Inherent: True @@ -564,13 +564,13 @@ entities: toxinsTolerance: 4 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 293 - HeatTolerance: 10 - LowPressureTolerance: 81 - HighPressureTolerance: 121 + idealHeat: 293 + heatTolerance: 10 + lowPressureTolerance: 81 + highPressureTolerance: 121 chemicals: Stellibinin: Inherent: True diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index 244198f0ac7..6ac19402be5 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -17,8 +17,8 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.4 + waterConsumption: 0.5 + nutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -46,8 +46,8 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.4 + waterConsumption: 0.5 + nutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -75,8 +75,8 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.4 + waterConsumption: 0.5 + nutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -106,10 +106,10 @@ idealLight: 9 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Vitamin: Min: 1 @@ -137,10 +137,10 @@ idealLight: 9 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: MuteToxin: Min: 1 @@ -168,8 +168,8 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 chemicals: JuiceCarrot: Min: 1 @@ -205,8 +205,8 @@ harvestRepeat: Repeat growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.6 + waterConsumption: 0.6 + nutrientConsumption: 0.6 chemicals: Nutriment: Min: 1 @@ -243,8 +243,8 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -273,8 +273,8 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Vitamin: Min: 1 @@ -306,8 +306,8 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -340,8 +340,8 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -370,8 +370,8 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Haloperidol: Min: 1 @@ -405,8 +405,8 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -438,8 +438,8 @@ growthStages: 4 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -470,10 +470,10 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Sugar: Min: 4 @@ -499,10 +499,10 @@ idealLight: 9 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Vitamin: Min: 1 @@ -527,10 +527,10 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 - type: seed id: towercap @@ -553,10 +553,10 @@ lightTolerance: 6 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 288 + idealHeat: 288 - type: seed id: steelcap @@ -577,10 +577,10 @@ lightTolerance: 6 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 288 + idealHeat: 288 - type: seed id: tomato @@ -604,10 +604,10 @@ splatPrototype: PuddleSplatter growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.4 + waterConsumption: 0.6 + nutrientConsumption: 0.4 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Nutriment: Min: 1 @@ -641,10 +641,10 @@ splatPrototype: PuddleSplatter growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.7 + waterConsumption: 0.6 + nutrientConsumption: 0.7 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Nutriment: Min: 1 @@ -680,10 +680,10 @@ splatPrototype: PuddleSplatter growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.7 + waterConsumption: 0.6 + nutrientConsumption: 0.7 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Blood: Min: 1 @@ -716,10 +716,10 @@ splatPrototype: PuddleSplatter growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.7 + waterConsumption: 0.6 + nutrientConsumption: 0.7 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Blood: Min: 1 @@ -751,10 +751,10 @@ idealLight: 9 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Nutriment: Min: 1 @@ -782,8 +782,8 @@ growthStages: 1 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -811,8 +811,8 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -847,8 +847,8 @@ idealLight: 6 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -877,8 +877,8 @@ idealLight: 6 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.75 - NutrientConsumption: 0.75 + waterConsumption: 0.75 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -911,10 +911,10 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Nutriment: Min: 1 @@ -945,10 +945,10 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Nutriment: Min: 1 @@ -981,10 +981,10 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Nutriment: Min: 1 @@ -1017,10 +1017,10 @@ lightTolerance: 6 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.5 + waterConsumption: 0.6 + nutrientConsumption: 0.5 - !type:AtmosphericGrowthComponent - IdealHeat: 288 + idealHeat: 288 chemicals: Nutriment: Min: 1 @@ -1045,10 +1045,10 @@ idealLight: 9 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.5 + waterConsumption: 0.5 + nutrientConsumption: 0.5 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Egg: Min: 4 @@ -1076,10 +1076,10 @@ idealLight: 9 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.4 - NutrientConsumption: 0.75 + waterConsumption: 0.4 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: THC: Min: 1 @@ -1105,10 +1105,10 @@ idealLight: 9 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.4 - NutrientConsumption: 0.75 + waterConsumption: 0.4 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: SpaceDrugs: Min: 1 @@ -1154,10 +1154,10 @@ idealLight: 9 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.4 - NutrientConsumption: 0.75 + waterConsumption: 0.4 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Nicotine: Min: 1 @@ -1184,10 +1184,10 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Histamine: Min: 1 @@ -1214,10 +1214,10 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.7 - NutrientConsumption: 0.8 + waterConsumption: 0.7 + nutrientConsumption: 0.8 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: SulfuricAcid: Min: 1 @@ -1248,10 +1248,10 @@ idealLight: 9 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: CapsaicinOil: Min: 1 @@ -1284,10 +1284,10 @@ idealLight: 9 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: FrostOil: Min: 1 @@ -1321,8 +1321,8 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1350,8 +1350,8 @@ growthStages: 5 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 chemicals: Aloe: Min: 1 @@ -1381,8 +1381,8 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1410,8 +1410,8 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 chemicals: Ultravasculine: Min: 1 @@ -1441,8 +1441,8 @@ growthStages: 6 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1482,8 +1482,8 @@ growthStages: 6 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1521,8 +1521,8 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 chemicals: Stellibinin: Min: 1 @@ -1546,8 +1546,8 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Razorium: Min: 1 @@ -1571,8 +1571,8 @@ growthStages: 2 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.5 + waterConsumption: 0.6 + nutrientConsumption: 0.5 chemicals: Amatoxin: Min: 1 @@ -1604,8 +1604,8 @@ idealLight: 6 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1634,8 +1634,8 @@ idealLight: 6 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1664,8 +1664,8 @@ idealLight: 6 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1694,8 +1694,8 @@ idealLight: 5 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.4 + waterConsumption: 0.6 + nutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -1726,8 +1726,8 @@ idealLight: 7 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.4 + waterConsumption: 0.5 + nutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -1751,8 +1751,8 @@ potency: 10 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1781,8 +1781,8 @@ idealLight: 7 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.4 + waterConsumption: 0.5 + nutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -1810,8 +1810,8 @@ growthStages: 2 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1841,8 +1841,8 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1874,8 +1874,8 @@ idealLight: 8 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -1907,10 +1907,10 @@ idealLight: 7 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 1.0 - NutrientConsumption: 0.8 + waterConsumption: 1.0 + nutrientConsumption: 0.8 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Vitamin: Min: 1 @@ -1938,8 +1938,8 @@ idealLight: 7 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.6 + waterConsumption: 0.5 + nutrientConsumption: 0.6 chemicals: Nutriment: Min: 2 @@ -1969,10 +1969,10 @@ growthStages: 4 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 298 + idealHeat: 298 chemicals: Nutriment: Min: 5 @@ -2004,8 +2004,8 @@ harvestRepeat: Repeat growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.5 + waterConsumption: 0.5 + nutrientConsumption: 0.5 chemicals: Nutriment: Min: 1 @@ -2035,8 +2035,8 @@ harvestRepeat: Repeat growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.5 + waterConsumption: 0.5 + nutrientConsumption: 0.5 chemicals: Happiness: Min: 1 @@ -2070,10 +2070,10 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 288 + idealHeat: 288 chemicals: PumpkinFlesh: Min: 1 @@ -2101,10 +2101,10 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent - IdealHeat: 288 + idealHeat: 288 chemicals: Ammonia: Min: 1 @@ -2139,8 +2139,8 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.75 + waterConsumption: 0.6 + nutrientConsumption: 0.75 chemicals: Fiber: Min: 5 @@ -2165,8 +2165,8 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.8 - NutrientConsumption: 0.75 + waterConsumption: 0.8 + nutrientConsumption: 0.75 chemicals: Fiber: Min: 5 @@ -2195,8 +2195,8 @@ idealLight: 6 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.5 - NutrientConsumption: 0.75 + waterConsumption: 0.5 + nutrientConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -2224,8 +2224,8 @@ growthStages: 2 growthComponents: - !type:BasicGrowthComponent - WaterConsumption: 0.6 - NutrientConsumption: 0.5 + waterConsumption: 0.6 + nutrientConsumption: 0.5 chemicals: Artifexium: Min: 1 From e5d53f06b5d197d289dcc94aee2d0b9bb7b90be7 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Fri, 1 Aug 2025 03:38:56 +0300 Subject: [PATCH 11/53] PlantGrowthComponent --- Content.Server/Botany/Components/PlantGrowthComponent.cs | 3 ++- Content.Server/Botany/SeedPrototype.cs | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Content.Server/Botany/Components/PlantGrowthComponent.cs b/Content.Server/Botany/Components/PlantGrowthComponent.cs index c685755bcdc..fd806cba7b2 100644 --- a/Content.Server/Botany/Components/PlantGrowthComponent.cs +++ b/Content.Server/Botany/Components/PlantGrowthComponent.cs @@ -1,4 +1,5 @@ using System.Security.Policy; +using Robust.Shared.Serialization.Manager; namespace Content.Server.Botany.Components; @@ -10,7 +11,7 @@ public partial class PlantGrowthComponent : Component { /// public PlantGrowthComponent DupeComponent() { - return (PlantGrowthComponent)this.MemberwiseClone(); + return IoCManager.Resolve().CreateCopy(this, notNullableOverride: true); } } diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index a1ba7fc1f3e..48c0766d139 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -248,8 +248,7 @@ public partial class SeedData // Deep copy growth components foreach (var component in GrowthComponents) { - // Use serialization manager for proper deep copying - var newComponent = IoCManager.Resolve().CreateCopy(component, notNullableOverride: true); + var newComponent = component.DupeComponent(); newSeed.GrowthComponents.Add(newComponent); } @@ -324,8 +323,7 @@ public partial class SeedData // Deep copy growth components from the new species foreach (var component in other.GrowthComponents) { - // Use serialization manager for proper deep copying - var newComponent = IoCManager.Resolve().CreateCopy(component, notNullableOverride: true); + var newComponent = component.DupeComponent(); newSeed.GrowthComponents.Add(newComponent); } From 8cfc2d82e24a62e4c940b336ea1e7e53342e64d2 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Fri, 1 Aug 2025 06:31:29 +0300 Subject: [PATCH 12/53] update 99 --- .../Components/AtmosphericGrowthComponent.cs | 12 ++ .../Components/AutoHarvestGrowthComponent.cs | 3 + .../Botany/Components/BotanySwabComponent.cs | 7 +- .../ConsumeExudeGasGrowthComponent.cs | 7 + .../Botany/Components/LogComponent.cs | 6 + .../Botany/Components/PlantHolderComponent.cs | 57 ++++++ .../Botany/Components/ProduceComponent.cs | 7 +- .../Components/UnviableGrowthComponent.cs | 6 + .../Components/WeedPestGrowthComponent.cs | 18 ++ Content.Server/Botany/SeedPrototype.cs | 26 +-- .../Botany/Systems/BasicGrowthSystem.cs | 12 +- .../Botany/Systems/PlantGrowthSystem.cs | 8 +- .../Botany/Systems/PlantHolderSystem.cs | 9 +- .../SharedSolutionContainerSystem.cs | 2 - Resources/Prototypes/Hydroponics/seeds.yml | 177 +++++------------- 15 files changed, 191 insertions(+), 166 deletions(-) diff --git a/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs b/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs index 6b85f262f9d..21abdc70fdb 100644 --- a/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs +++ b/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs @@ -4,15 +4,27 @@ namespace Content.Server.Botany.Components; [DataDefinition] public sealed partial class AtmosphericGrowthComponent : PlantGrowthComponent { + /// + /// Ideal temperature for plant growth in Kelvin. + /// [DataField("idealHeat")] public float IdealHeat = 293f; + /// + /// Temperature tolerance range around ideal heat. + /// [DataField("heatTolerance")] public float HeatTolerance = 10f; + /// + /// Minimum pressure tolerance for plant growth. + /// [DataField("lowPressureTolerance")] public float LowPressureTolerance = 81f; + /// + /// Maximum pressure tolerance for plant growth. + /// [DataField("lighPressureTolerance")] public float HighPressureTolerance = 121f; } diff --git a/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs b/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs index fdd9e3a704c..9b5c9690750 100644 --- a/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs +++ b/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs @@ -4,6 +4,9 @@ namespace Content.Server.Botany.Components; [DataDefinition] public sealed partial class AutoHarvestGrowthComponent : PlantGrowthComponent { + /// + /// Chance per tick for the plant to automatically harvest itself. + /// [DataField("harvestChance")] public float HarvestChance = 0.1f; } diff --git a/Content.Server/Botany/Components/BotanySwabComponent.cs b/Content.Server/Botany/Components/BotanySwabComponent.cs index 91508996c2d..1f99554cb31 100644 --- a/Content.Server/Botany/Components/BotanySwabComponent.cs +++ b/Content.Server/Botany/Components/BotanySwabComponent.cs @@ -8,6 +8,9 @@ namespace Content.Server.Botany; [RegisterComponent] public sealed partial class BotanySwabComponent : Component { + /// + /// Delay in seconds between swab uses. + /// [DataField("swabDelay")] public float SwabDelay = 2f; @@ -16,6 +19,8 @@ public sealed partial class BotanySwabComponent : Component /// public SeedData? SeedData; - //data from first plant. + /// + /// Growth components from the first plant that got swabbed. + /// public List components; } diff --git a/Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs b/Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs index 41b9e4a0682..6f815dfdf4e 100644 --- a/Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs +++ b/Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs @@ -6,6 +6,13 @@ namespace Content.Server.Botany.Components; [DataDefinition] public sealed partial class ConsumeExudeGasGrowthComponent : PlantGrowthComponent { + /// + /// Dictionary of gases and their consumption rates per growth tick. + /// [DataField] public Dictionary ConsumeGasses = new(); + + /// + /// Dictionary of gases and their exude rates per growth tick. + /// [DataField] public Dictionary ExudeGasses = new(); } diff --git a/Content.Server/Botany/Components/LogComponent.cs b/Content.Server/Botany/Components/LogComponent.cs index da3f82416e3..987ce2b1598 100644 --- a/Content.Server/Botany/Components/LogComponent.cs +++ b/Content.Server/Botany/Components/LogComponent.cs @@ -10,8 +10,14 @@ namespace Content.Server.Botany.Components; [Access(typeof(LogSystem))] public sealed partial class LogComponent : Component { + /// + /// Prototype ID of the entity spawned when this log is chopped. + /// [DataField("spawnedPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] public string SpawnedPrototype = "MaterialWoodPlank1"; + /// + /// Number of entities spawned when this log is chopped. + /// [DataField("spawnCount")] public int SpawnCount = 2; } diff --git a/Content.Server/Botany/Components/PlantHolderComponent.cs b/Content.Server/Botany/Components/PlantHolderComponent.cs index d65e1b702be..ad33fa9ea2f 100644 --- a/Content.Server/Botany/Components/PlantHolderComponent.cs +++ b/Content.Server/Botany/Components/PlantHolderComponent.cs @@ -19,9 +19,15 @@ public sealed partial class PlantHolderComponent : Component [DataField] public TimeSpan UpdateDelay = TimeSpan.FromSeconds(3); + /// + /// Age when the plant last produced harvestable items. + /// [DataField] public int LastProduce; + /// + /// Number of missing gases required for plant growth. + /// [DataField] public int MissingGas; @@ -43,6 +49,9 @@ public sealed partial class PlantHolderComponent : Component [DataField] public SoundSpecifier? WateringSound; + /// + /// Whether to update the sprite after the next update cycle. + /// [DataField] public bool UpdateSpriteAfterUpdate; @@ -53,30 +62,57 @@ public sealed partial class PlantHolderComponent : Component [DataField] public bool DrawWarnings = false; + /// + /// Current water level in the plant holder (0-100). + /// [DataField] public float WaterLevel = 100f; + /// + /// Current nutrient level in the plant holder (0-100). + /// [DataField] public float NutritionLevel = 100f; + /// + /// Current pest level in the plant holder (0-10). + /// [DataField] public float PestLevel; + /// + /// Current weed level in the plant holder (0-10). + /// [DataField] public float WeedLevel; + /// + /// Current toxin level in the plant holder (0-100). + /// [DataField] public float Toxins; + /// + /// Current age of the plant in growth cycles. + /// [DataField] public int Age; + /// + /// Number of growth cycles to skip due to poor conditions. + /// [DataField] public int SkipAging; + /// + /// Whether the plant is dead. + /// [DataField] public bool Dead; + /// + /// Whether the plant is ready for harvest. + /// [DataField] public bool Harvest; @@ -93,18 +129,33 @@ public sealed partial class PlantHolderComponent : Component [DataField] public int YieldMod = 1; + /// + /// Multiplier for mutation chance and severity. + /// [DataField] public float MutationMod = 1f; + /// + /// Current mutation level (0-100). + /// [DataField] public float MutationLevel; + /// + /// Current health of the plant (0 to seed endurance). + /// [DataField] public float Health; + /// + /// Multiplier for weed growth rate. + /// [DataField] public float WeedCoefficient = 1f; + /// + /// Seed data for the currently planted seed. + /// [DataField] public SeedData? Seed; @@ -133,9 +184,15 @@ public sealed partial class PlantHolderComponent : Component [DataField] public bool ForceUpdate; + /// + /// Name of the solution container that holds the soil/nutrient solution. + /// [DataField] public string SoilSolutionName = "soil"; + /// + /// Reference to the soil solution container. + /// [ViewVariables] public Entity? SoilSolution = null; } diff --git a/Content.Server/Botany/Components/ProduceComponent.cs b/Content.Server/Botany/Components/ProduceComponent.cs index db4ed62dd38..6d7ed46a377 100644 --- a/Content.Server/Botany/Components/ProduceComponent.cs +++ b/Content.Server/Botany/Components/ProduceComponent.cs @@ -8,16 +8,19 @@ namespace Content.Server.Botany.Components; [Access(typeof(BotanySystem))] public sealed partial class ProduceComponent : SharedProduceComponent { + /// + /// Name of the solution container that holds the produce's contents. + /// [DataField("targetSolution")] public string SolutionName { get; set; } = "food"; /// - /// Seed data used to create a when this produce has its seeds extracted. + /// Seed data used to create a when this produce has its seeds extracted. /// [DataField] public SeedData? Seed; /// - /// Seed data used to create a when this produce has its seeds extracted. + /// Prototype ID for the seed that can be extracted from this produce. /// [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string? SeedId; diff --git a/Content.Server/Botany/Components/UnviableGrowthComponent.cs b/Content.Server/Botany/Components/UnviableGrowthComponent.cs index 2e85931adf4..fb8f823b234 100644 --- a/Content.Server/Botany/Components/UnviableGrowthComponent.cs +++ b/Content.Server/Botany/Components/UnviableGrowthComponent.cs @@ -4,9 +4,15 @@ namespace Content.Server.Botany.Components; [DataDefinition] public sealed partial class UnviableGrowthComponent : PlantGrowthComponent { + /// + /// Chance per tick for the plant to take damage due to being unviable. + /// [DataField("deathChance")] public float DeathChance = 0.1f; + /// + /// Amount of damage dealt to the plant per successful death tick. + /// [DataField("deathDamage")] public float DeathDamage = 6f; } diff --git a/Content.Server/Botany/Components/WeedPestGrowthComponent.cs b/Content.Server/Botany/Components/WeedPestGrowthComponent.cs index 5d5d90f5617..4b9a4d5f597 100644 --- a/Content.Server/Botany/Components/WeedPestGrowthComponent.cs +++ b/Content.Server/Botany/Components/WeedPestGrowthComponent.cs @@ -4,21 +4,39 @@ namespace Content.Server.Botany.Components; [DataDefinition] public sealed partial class WeedPestGrowthComponent : PlantGrowthComponent { + /// + /// Maximum weed level the plant can tolerate before taking damage. + /// [DataField("weedTolerance")] public float WeedTolerance = 5f; + /// + /// Maximum pest level the plant can tolerate before taking damage. + /// [DataField("pestTolerance")] public float PestTolerance = 5f; + /// + /// Chance per tick for weeds to grow around this plant. + /// [DataField("weedGrowthChance")] public float WeedGrowthChance = 0.01f; + /// + /// Amount of weed growth per successful weed growth tick. + /// [DataField("weedGrowthAmount")] public float WeedGrowthAmount = 0.5f; + /// + /// Chance per tick for pests to damage this plant. + /// [DataField("pestDamageChance")] public float PestDamageChance = 0.05f; + /// + /// Amount of damage dealt to the plant per successful pest damage tick. + /// [DataField("pestDamageAmount")] public float PestDamageAmount = 1f; } diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index 48c0766d139..8543a224be9 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -59,20 +59,20 @@ public partial class SeedData #region Tracking /// - /// The name of this seed. Determines the name of seed packets. + /// The name of this seed. Determines the name of seed packets. /// [DataField] public string Name { get; private set; } = ""; /// - /// The noun for this type of seeds. E.g. for fungi this should probably be "spores" instead of "seeds". Also - /// used to determine the name of seed packets. + /// The noun for this type of seeds. E.g. for fungi this should probably be "spores" instead of "seeds". Also + /// used to determine the name of seed packets. /// [DataField] public string Noun { get; private set; } = ""; /// - /// Name displayed when examining the hydroponics tray. Describes the actual plant, not the seed itself. + /// Name displayed when examining the hydroponics tray. Describes the actual plant, not the seed itself. /// [DataField] public string DisplayName { get; private set; } = ""; @@ -80,13 +80,13 @@ public partial class SeedData [DataField] public bool Mysterious; /// - /// If true, the properties of this seed cannot be modified. + /// If true, the properties of this seed cannot be modified. /// [DataField] public bool Immutable; /// - /// If true, there is only a single reference to this seed and it's properties can be directly modified without - /// needing to clone the seed. + /// If true, there is only a single reference to this seed and it's properties can be directly modified without + /// needing to clone the seed. /// [ViewVariables] public bool Unique = false; // seed-prototypes or yaml-defined seeds for entity prototypes will not generally be unique. @@ -94,13 +94,13 @@ public partial class SeedData #region Output /// - /// The entity prototype that is spawned when this type of seed is extracted from produce using a seed extractor. + /// The entity prototype that is spawned when this type of seed is extracted from produce using a seed extractor. /// [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string PacketPrototype = "SeedBase"; /// - /// The entity prototypes that are spawned when this type of seed is harvested. + /// The entity prototypes that are spawned when this type of seed is harvested. /// [DataField(customTypeSerializer: typeof(PrototypeIdListSerializer))] public List ProductPrototypes = new(); @@ -129,13 +129,13 @@ public partial class SeedData [DataField] public float Potency = 1f; /// - /// If true, cannot be harvested for seeds. Balances hybrids and - /// mutations. + /// If true, cannot be harvested for seeds. Balances hybrids and + /// mutations. /// [DataField] public bool Seedless = false; /// - /// If true, a sharp tool is required to harvest this plant. + /// If true, a sharp tool is required to harvest this plant. /// [DataField] public bool Ligneous; @@ -169,7 +169,7 @@ public partial class SeedData [DataField] public List Mutations { get; set; } = new(); /// - /// The seed prototypes this seed may mutate into when prompted to. + /// The seed prototypes this seed may mutate into when prompted to. /// [DataField(customTypeSerializer: typeof(PrototypeIdListSerializer))] public List MutationPrototypes = new(); diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index fed24d6d198..e20f85efa28 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -52,7 +52,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem // Check if the plant is viable if (holder.Seed.Viable == false) { - holder.Health -= _random.Next(5, 10) * HydroponicsSpeedMultiplier; + holder.Health -= _random.Next(5, 10) * PlantGrowthSystem.HydroponicsSpeedMultiplier; if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; return; @@ -65,14 +65,14 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem { if (_random.Prob(0.8f)) { - holder.Age += (int)(1 * HydroponicsSpeedMultiplier); + holder.Age += (int)(1 * PlantGrowthSystem.HydroponicsSpeedMultiplier); holder.UpdateSpriteAfterUpdate = true; } } if (holder.Age > holder.Seed.Lifespan) { - holder.Health -= _random.Next(3, 5) * HydroponicsSpeedMultiplier; + holder.Health -= _random.Next(3, 5) * PlantGrowthSystem.HydroponicsSpeedMultiplier; if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; } @@ -110,7 +110,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem if (component.WaterConsumption > 0 && holder.WaterLevel > 0 && _random.Prob(0.75f)) { holder.WaterLevel -= MathF.Max(0f, - component.WaterConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); + component.WaterConsumption * PlantGrowthSystem.HydroponicsConsumptionMultiplier * PlantGrowthSystem.HydroponicsSpeedMultiplier); if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; } @@ -118,12 +118,12 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem if (component.NutrientConsumption > 0 && holder.NutritionLevel > 0 && _random.Prob(0.75f)) { holder.NutritionLevel -= MathF.Max(0f, - component.NutrientConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); + component.NutrientConsumption * PlantGrowthSystem.HydroponicsConsumptionMultiplier * PlantGrowthSystem.HydroponicsSpeedMultiplier); if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; } - var healthMod = _random.Next(1, 3) * HydroponicsSpeedMultiplier; + var healthMod = _random.Next(1, 3) * PlantGrowthSystem.HydroponicsSpeedMultiplier; if (holder.SkipAging < 10) { // Make sure the plant is not thirsty. diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs index 7039f5c3c29..e4b13086793 100644 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -28,7 +28,6 @@ public sealed class PlantGrowthCycleSystem : EntitySystem if (nextUpdate > _gameTiming.CurTime) return; - // Query for plant holders that have seeds and are not dead var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var plantHolder)) { @@ -59,7 +58,14 @@ public abstract class PlantGrowthSystem : EntitySystem [Dependency] protected readonly IRobustRandom _random = default!; [Dependency] protected readonly IGameTiming _gameTiming = default!; + /// + /// Multiplier for plant growth speed in hydroponics. + /// public const float HydroponicsSpeedMultiplier = 1f; + + /// + /// Multiplier for resource consumption (water, nutrients) in hydroponics. + /// public const float HydroponicsConsumptionMultiplier = 2f; public override void Initialize() diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index d0e28d65e54..0b3ca4c57b6 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -47,13 +47,10 @@ public sealed class PlantHolderSystem : EntitySystem [Dependency] private readonly RandomHelperSystem _randomHelper = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ISerializationManager _copier = default!; - - public const float WeedHighLevelThreshold = 10f; [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - public const float HydroponicsSpeedMultiplier = 1f; - public const float HydroponicsConsumptionMultiplier = 2f; + public const float WeedHighLevelThreshold = 10f; private static readonly ProtoId HoeTag = "Hoe"; private static readonly ProtoId PlantSampleTakerTag = "PlantSampleTaker"; @@ -418,7 +415,7 @@ public sealed class PlantHolderSystem : EntitySystem chance = 0.01f; if (_random.Prob(chance)) - component.WeedLevel += 1 + component.WeedCoefficient; // * HydroponicsSpeedMultiplier + component.WeedLevel += 1 + component.WeedCoefficient; if (component.DrawWarnings) component.UpdateSpriteAfterUpdate = true; @@ -591,7 +588,7 @@ public sealed class PlantHolderSystem : EntitySystem component.YieldMod = 1; component.MutationMod = 1; component.ImproperPressure = false; - component.WeedLevel += 1; // * HydroponicsSpeedMultiplier; + component.WeedLevel += 1; component.PestLevel = 0; UpdateSprite(uid, component); } diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs index dd5865e461b..c1653f9ef93 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs @@ -572,7 +572,6 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem if (quantity == 0) return false; - // Use the more efficient direct transfer method return TryDirectTransferReagents(soln, source, quantity); } @@ -1259,7 +1258,6 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem if (quantity == 0) return false; - // Directly transfer reagents without creating intermediate solutions foreach (var (reagentId, amount) in source.Contents) { var transferAmount = amount * (quantity / source.Volume); diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index 6ac19402be5..c44c932027e 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -14,11 +14,10 @@ production: 3 yield: 3 potency: 5 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 nutrientConsumption: 0.4 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -43,11 +42,10 @@ production: 3 yield: 3 potency: 5 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 nutrientConsumption: 0.4 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -72,11 +70,10 @@ production: 3 yield: 3 potency: 5 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 nutrientConsumption: 0.4 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -103,11 +100,9 @@ maturation: 6 production: 6 yield: 2 - idealLight: 9 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -134,11 +129,9 @@ maturation: 6 production: 6 yield: 2 - idealLight: 9 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -169,7 +162,7 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: JuiceCarrot: Min: 1 @@ -201,12 +194,12 @@ production: 5 yield: 3 potency: 20 - idealLight: 8 harvestRepeat: Repeat growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.6 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -240,11 +233,9 @@ production: 6 yield: 3 potency: 10 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -270,11 +261,9 @@ production: 6 yield: 4 potency: 1 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Vitamin: Min: 1 @@ -303,11 +292,9 @@ production: 6 yield: 3 potency: 10 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -337,11 +324,9 @@ production: 6 yield: 3 potency: 10 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -367,11 +352,9 @@ production: 6 yield: 3 potency: 10 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Haloperidol: Min: 1 @@ -401,12 +384,10 @@ production: 6 yield: 3 potency: 10 - idealLight: 8 growthStages: 3 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -439,7 +420,7 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -470,8 +451,6 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -496,11 +475,9 @@ yield: 2 potency: 20 growthStages: 5 - idealLight: 9 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -527,8 +504,6 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 @@ -550,11 +525,8 @@ yield: 5 potency: 1 growthStages: 3 - lightTolerance: 6 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 288 @@ -574,11 +546,8 @@ yield: 3 potency: 1 growthStages: 3 - lightTolerance: 6 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 288 @@ -600,7 +569,6 @@ production: 6 yield: 2 potency: 10 - idealLight: 8 splatPrototype: PuddleSplatter growthComponents: - !type:BasicGrowthComponent @@ -637,7 +605,6 @@ production: 6 yield: 2 potency: 10 - idealLight: 8 splatPrototype: PuddleSplatter growthComponents: - !type:BasicGrowthComponent @@ -676,7 +643,6 @@ production: 6 yield: 2 potency: 10 - idealLight: 8 splatPrototype: PuddleSplatter growthComponents: - !type:BasicGrowthComponent @@ -711,7 +677,6 @@ production: 6 yield: 2 potency: 10 - idealLight: 8 growthStages: 2 splatPrototype: PuddleSplatter growthComponents: @@ -748,11 +713,8 @@ yield: 2 potency: 20 growthStages: 3 - idealLight: 9 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -782,8 +744,7 @@ growthStages: 1 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -811,8 +772,7 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -844,11 +804,9 @@ production: 6 yield: 3 potency: 10 - idealLight: 6 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -874,11 +832,10 @@ production: 6 yield: 3 potency: 10 - idealLight: 6 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.75 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -908,11 +865,9 @@ yield: 2 potency: 20 growthStages: 3 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -942,11 +897,9 @@ yield: 2 potency: 20 growthStages: 3 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -978,11 +931,9 @@ yield: 2 potency: 20 growthStages: 3 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -1014,7 +965,6 @@ yield: 5 potency: 1 growthStages: 3 - lightTolerance: 6 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 @@ -1042,10 +992,8 @@ production: 12 yield: 2 potency: 20 - idealLight: 9 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 nutrientConsumption: 0.5 - !type:AtmosphericGrowthComponent idealHeat: 298 @@ -1073,11 +1021,9 @@ yield: 2 potency: 20 growthStages: 3 - idealLight: 9 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.4 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -1102,11 +1048,9 @@ yield: 2 potency: 20 growthStages: 3 - idealLight: 9 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.4 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -1151,11 +1095,9 @@ yield: 2 potency: 20 growthStages: 3 - idealLight: 9 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.4 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -1181,11 +1123,9 @@ yield: 2 potency: 20 growthStages: 5 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -1211,7 +1151,6 @@ yield: 2 potency: 20 growthStages: 5 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.7 @@ -1245,11 +1184,8 @@ production: 6 yield: 2 potency: 20 - idealLight: 9 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -1281,11 +1217,8 @@ production: 6 yield: 2 potency: 20 - idealLight: 9 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -1322,7 +1255,7 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1351,7 +1284,7 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Aloe: Min: 1 @@ -1382,7 +1315,7 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1411,7 +1344,7 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Ultravasculine: Min: 1 @@ -1442,7 +1375,7 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1483,7 +1416,7 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1522,7 +1455,7 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Stellibinin: Min: 1 @@ -1546,8 +1479,7 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Razorium: Min: 1 @@ -1573,6 +1505,7 @@ - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.5 + - !type:AtmosphericGrowthComponent chemicals: Amatoxin: Min: 1 @@ -1601,11 +1534,9 @@ yield: 1 potency: 10 growthStages: 2 - idealLight: 6 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1631,11 +1562,9 @@ yield: 1 potency: 10 growthStages: 2 - idealLight: 6 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1661,11 +1590,9 @@ yield: 1 potency: 10 growthStages: 2 - idealLight: 6 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1691,11 +1618,11 @@ yield: 3 potency: 5 growthStages: 4 - idealLight: 5 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.4 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1723,11 +1650,10 @@ production: 6 yield: 3 potency: 5 - idealLight: 7 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 nutrientConsumption: 0.4 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1752,7 +1678,7 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1778,11 +1704,10 @@ production: 6 yield: 3 potency: 5 - idealLight: 7 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 nutrientConsumption: 0.4 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1810,8 +1735,7 @@ growthStages: 2 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1838,11 +1762,9 @@ production: 3 yield: 1 potency: 1 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1871,11 +1793,9 @@ production: 3 yield: 1 potency: 1 - idealLight: 8 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1904,7 +1824,6 @@ maturation: 6 production: 6 yield: 6 - idealLight: 7 growthComponents: - !type:BasicGrowthComponent waterConsumption: 1.0 @@ -1935,11 +1854,10 @@ maturation: 6 production: 6 yield: 4 - idealLight: 7 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 nutrientConsumption: 0.6 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 2 @@ -1965,12 +1883,10 @@ production: 6 potency: 10 yield: 3 - idealLight: 8 growthStages: 4 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -2000,12 +1916,11 @@ production: 6 yield: 3 potency: 25 - idealLight: 8 harvestRepeat: Repeat growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 nutrientConsumption: 0.5 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -2031,12 +1946,11 @@ production: 6 yield: 3 potency: 25 - idealLight: 8 harvestRepeat: Repeat growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 nutrientConsumption: 0.5 + - !type:AtmosphericGrowthComponent chemicals: Happiness: Min: 1 @@ -2070,8 +1984,6 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 288 chemicals: @@ -2101,8 +2013,6 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 - !type:AtmosphericGrowthComponent idealHeat: 288 chemicals: @@ -2135,12 +2045,11 @@ production: 3 yield: 3 potency: 5 - idealLight: 8 growthStages: 3 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Fiber: Min: 5 @@ -2161,12 +2070,11 @@ production: 3 yield: 2 potency: 5 - idealLight: 8 growthStages: 3 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.8 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Fiber: Min: 5 @@ -2192,11 +2100,9 @@ production: 6 yield: 5 potency: 10 - idealLight: 6 growthComponents: - !type:BasicGrowthComponent - waterConsumption: 0.5 - nutrientConsumption: 0.75 + - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -2226,6 +2132,7 @@ - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.5 + - !type:AtmosphericGrowthComponent chemicals: Artifexium: Min: 1 From 731e86124cf8d68e134b99b3ffaf7cabec7a8770 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Fri, 1 Aug 2025 06:42:36 +0300 Subject: [PATCH 13/53] last update --- Content.Server/Botany/Systems/PlantHolderSystem.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 0b3ca4c57b6..57605c1b3a6 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Botany.Components; +using Content.Server.Botany.Systems; using Content.Server.Hands.Systems; using Content.Server.Kitchen.Components; using Content.Server.Popups; @@ -395,6 +396,12 @@ public sealed class PlantHolderSystem : EntitySystem component.LastCycle = curTime; + if (component.Seed != null && !component.Dead) + { + var plantGrow = new OnPlantGrowEvent(); + RaiseLocalEvent(uid, ref plantGrow); + } + // Process mutations. All plants can mutate, so this stays here. if (component.MutationLevel > 0) { From 4c3e99d193d61024daaae843ead1f909183a4a29 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Fri, 1 Aug 2025 09:50:52 +0300 Subject: [PATCH 14/53] oops --- .../Components/AtmosphericGrowthComponent.cs | 8 +-- .../Components/AutoHarvestGrowthComponent.cs | 2 +- .../Botany/Components/BasicGrowthComponent.cs | 4 +- .../Botany/Components/BotanySwabComponent.cs | 2 +- .../Components/UnviableGrowthComponent.cs | 4 +- .../Components/WeedPestGrowthComponent.cs | 12 ++-- .../Botany/Systems/PlantGrowthSystem.cs | 3 - .../EntityEffects/EntityEffectSystem.cs | 12 +--- .../SharedSolutionContainerSystem.cs | 55 +++---------------- .../EffectConditions/SolutionTemperature.cs | 18 +----- .../Tools/Components/WelderComponent.cs | 3 + 11 files changed, 32 insertions(+), 91 deletions(-) diff --git a/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs b/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs index 21abdc70fdb..61ed42cc42c 100644 --- a/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs +++ b/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs @@ -7,24 +7,24 @@ public sealed partial class AtmosphericGrowthComponent : PlantGrowthComponent /// /// Ideal temperature for plant growth in Kelvin. /// - [DataField("idealHeat")] + [DataField] public float IdealHeat = 293f; /// /// Temperature tolerance range around ideal heat. /// - [DataField("heatTolerance")] + [DataField] public float HeatTolerance = 10f; /// /// Minimum pressure tolerance for plant growth. /// - [DataField("lowPressureTolerance")] + [DataField] public float LowPressureTolerance = 81f; /// /// Maximum pressure tolerance for plant growth. /// - [DataField("lighPressureTolerance")] + [DataField] public float HighPressureTolerance = 121f; } diff --git a/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs b/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs index 9b5c9690750..5a28055c303 100644 --- a/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs +++ b/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs @@ -7,6 +7,6 @@ public sealed partial class AutoHarvestGrowthComponent : PlantGrowthComponent /// /// Chance per tick for the plant to automatically harvest itself. /// - [DataField("harvestChance")] + [DataField] public float HarvestChance = 0.1f; } diff --git a/Content.Server/Botany/Components/BasicGrowthComponent.cs b/Content.Server/Botany/Components/BasicGrowthComponent.cs index 0b447746aa0..bc71608afd4 100644 --- a/Content.Server/Botany/Components/BasicGrowthComponent.cs +++ b/Content.Server/Botany/Components/BasicGrowthComponent.cs @@ -7,12 +7,12 @@ public sealed partial class BasicGrowthComponent : PlantGrowthComponent /// /// Amount of water consumed per growth tick. /// - [DataField("waterConsumption")] + [DataField] public float WaterConsumption = 0.5f; /// /// Amount of nutrients consumed per growth tick. /// - [DataField("nutrientConsumption")] + [DataField] public float NutrientConsumption = 0.75f; } diff --git a/Content.Server/Botany/Components/BotanySwabComponent.cs b/Content.Server/Botany/Components/BotanySwabComponent.cs index 1f99554cb31..fcedaa036f4 100644 --- a/Content.Server/Botany/Components/BotanySwabComponent.cs +++ b/Content.Server/Botany/Components/BotanySwabComponent.cs @@ -11,7 +11,7 @@ public sealed partial class BotanySwabComponent : Component /// /// Delay in seconds between swab uses. /// - [DataField("swabDelay")] + [DataField] public float SwabDelay = 2f; /// diff --git a/Content.Server/Botany/Components/UnviableGrowthComponent.cs b/Content.Server/Botany/Components/UnviableGrowthComponent.cs index fb8f823b234..e60b331e03a 100644 --- a/Content.Server/Botany/Components/UnviableGrowthComponent.cs +++ b/Content.Server/Botany/Components/UnviableGrowthComponent.cs @@ -7,12 +7,12 @@ public sealed partial class UnviableGrowthComponent : PlantGrowthComponent /// /// Chance per tick for the plant to take damage due to being unviable. /// - [DataField("deathChance")] + [DataField] public float DeathChance = 0.1f; /// /// Amount of damage dealt to the plant per successful death tick. /// - [DataField("deathDamage")] + [DataField] public float DeathDamage = 6f; } diff --git a/Content.Server/Botany/Components/WeedPestGrowthComponent.cs b/Content.Server/Botany/Components/WeedPestGrowthComponent.cs index 4b9a4d5f597..592fad6ecbf 100644 --- a/Content.Server/Botany/Components/WeedPestGrowthComponent.cs +++ b/Content.Server/Botany/Components/WeedPestGrowthComponent.cs @@ -7,36 +7,36 @@ public sealed partial class WeedPestGrowthComponent : PlantGrowthComponent /// /// Maximum weed level the plant can tolerate before taking damage. /// - [DataField("weedTolerance")] + [DataField] public float WeedTolerance = 5f; /// /// Maximum pest level the plant can tolerate before taking damage. /// - [DataField("pestTolerance")] + [DataField] public float PestTolerance = 5f; /// /// Chance per tick for weeds to grow around this plant. /// - [DataField("weedGrowthChance")] + [DataField] public float WeedGrowthChance = 0.01f; /// /// Amount of weed growth per successful weed growth tick. /// - [DataField("weedGrowthAmount")] + [DataField] public float WeedGrowthAmount = 0.5f; /// /// Chance per tick for pests to damage this plant. /// - [DataField("pestDamageChance")] + [DataField] public float PestDamageChance = 0.05f; /// /// Amount of damage dealt to the plant per successful pest damage tick. /// - [DataField("pestDamageAmount")] + [DataField] public float PestDamageAmount = 1f; } diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs index e4b13086793..05084911bc8 100644 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -31,18 +31,15 @@ public sealed class PlantGrowthCycleSystem : EntitySystem var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var plantHolder)) { - // Only process plants that have seeds and are alive if (plantHolder.Seed == null || plantHolder.Dead) continue; - // Check if it's time for this plant to grow if (_gameTiming.CurTime < plantHolder.LastCycle + plantHolder.CycleDelay) continue; var plantGrow = new OnPlantGrowEvent(); RaiseLocalEvent(uid, ref plantGrow); - // Update the last cycle time after processing growth plantHolder.LastCycle = _gameTiming.CurTime; } diff --git a/Content.Server/EntityEffects/EntityEffectSystem.cs b/Content.Server/EntityEffects/EntityEffectSystem.cs index 87ca8543490..b31a39a1f1f 100644 --- a/Content.Server/EntityEffects/EntityEffectSystem.cs +++ b/Content.Server/EntityEffects/EntityEffectSystem.cs @@ -164,16 +164,8 @@ public sealed class EntityEffectSystem : EntitySystem return; } - // For non-reagent effects, we need to find the organ entity differently - var metabolizer = EntityManager.GetComponentOrNull(args.Args.TargetEntity); - if (metabolizer != null) - { - args.Result = OrganCondition(args.Condition, (args.Args.TargetEntity, metabolizer)); - return; - } - - // If no metabolizer is found, the condition fails - args.Result = false; + // TODO: Someone needs to figure out how to do this for non-reagent effects. + throw new NotImplementedException(); } public bool OrganCondition(OrganType condition, Entity metabolizer) diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs index c1653f9ef93..5cc12a9b695 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs @@ -298,7 +298,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem /// /// Dirties a solution entity that has been modified and prompts updates to chemical reactions and overflow state. - /// Should be invoked whenever a solution entity is changed. + /// Should be invoked whenever a solution entity is modified. /// /// /// 90% of this system is ensuring that this proc is invoked whenever a solution entity is changed. The other 10% is this proc. @@ -572,7 +572,12 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem if (quantity == 0) return false; - return TryDirectTransferReagents(soln, source, quantity); + // TODO This should be made into a function that directly transfers reagents. + // Currently this is quite inefficient. + solution.AddSolution(source.SplitSolution(quantity), PrototypeManager); + + UpdateChemicals(soln); + return true; } /// @@ -810,22 +815,8 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem return; } - var colorHex = primary.SubstanceColor - .ToHexNoAlpha(); - - // Ensure readability for dark colors by checking contrast - // For very dark colors, use a lighter variant - if (primary.SubstanceColor.R + primary.SubstanceColor.G + primary.SubstanceColor.B < 0.3f * 3) - { - // Create a lighter version of the color - var lighterColor = new Color( - Math.Min(1f, primary.SubstanceColor.R + 0.3f), - Math.Min(1f, primary.SubstanceColor.G + 0.3f), - Math.Min(1f, primary.SubstanceColor.B + 0.3f), - primary.SubstanceColor.A - ); - colorHex = lighterColor.ToHexNoAlpha(); - } + var colorHex = solution.GetColor(PrototypeManager) + .ToHexNoAlpha(); //TODO: If the chem has a dark color, the examine text becomes black on a black background, which is unreadable. var messageString = "shared-solution-container-component-on-examine-main-text"; using (args.PushGroup(nameof(ExaminableSolutionComponent))) @@ -1242,32 +1233,4 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem dissolvedReagentAmount += overflow; return dissolvedReagentAmount; } - - /// - /// Directly transfers reagents from one solution to another without creating intermediate solutions. - /// - public bool TryDirectTransferReagents(Entity targetSoln, Solution source, FixedPoint2 quantity) - { - var (uid, comp) = targetSoln; - var solution = comp.Solution; - - if (quantity <= FixedPoint2.Zero || source.Volume <= FixedPoint2.Zero) - return false; - - quantity = FixedPoint2.Min(quantity, solution.AvailableVolume, source.Volume); - if (quantity == 0) - return false; - - foreach (var (reagentId, amount) in source.Contents) - { - var transferAmount = amount * (quantity / source.Volume); - if (transferAmount > FixedPoint2.Zero) - { - solution.AddReagent(reagentId, transferAmount); - } - } - - UpdateChemicals(targetSoln); - return true; - } } diff --git a/Content.Shared/EntityEffects/EffectConditions/SolutionTemperature.cs b/Content.Shared/EntityEffects/EffectConditions/SolutionTemperature.cs index dccd6b8ed07..e2febd8f483 100644 --- a/Content.Shared/EntityEffects/EffectConditions/SolutionTemperature.cs +++ b/Content.Shared/EntityEffects/EffectConditions/SolutionTemperature.cs @@ -1,6 +1,4 @@ -using Content.Shared.EntityEffects; using Robust.Shared.Prototypes; -using Content.Shared.Chemistry.Components.SolutionManager; namespace Content.Shared.EntityEffects.EffectConditions; @@ -25,20 +23,8 @@ public sealed partial class SolutionTemperature : EntityEffectCondition reagentArgs.Source.Temperature <= Max; } - // For non-reagent effects, we need to find the solution differently - if (args.EntityManager.TryGetComponent(args.TargetEntity, out SolutionContainerManagerComponent? container) && - container != null && container.Solutions != null) - { - // Check the first available solution for temperature - foreach (var (name, solution) in container.Solutions) - { - if (solution.Temperature >= Min && solution.Temperature <= Max) - return true; - } - } - - // If no suitable solution is found, the condition fails - return false; + // TODO: Someone needs to figure out how to do this for non-reagent effects. + throw new NotImplementedException(); } public override string GuidebookExplanation(IPrototypeManager prototype) diff --git a/Content.Shared/Tools/Components/WelderComponent.cs b/Content.Shared/Tools/Components/WelderComponent.cs index bff55b8e589..a9111c7f534 100644 --- a/Content.Shared/Tools/Components/WelderComponent.cs +++ b/Content.Shared/Tools/Components/WelderComponent.cs @@ -11,6 +11,9 @@ namespace Content.Shared.Tools.Components; /// /// Handles fuel consumption for the tool and allows it to explode welding fuel tanks. /// +/// +/// TODO: De-hardcode welder bombing. +/// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), AutoGenerateComponentPause] [Access(typeof(SharedToolSystem))] public sealed partial class WelderComponent : Component From a67b68dbc07e4998cb49179ee74fd711f36407f5 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Fri, 1 Aug 2025 10:44:25 +0300 Subject: [PATCH 15/53] Update vegan-meatball.yml --- Resources/Maps/Salvage/vegan-meatball.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Resources/Maps/Salvage/vegan-meatball.yml b/Resources/Maps/Salvage/vegan-meatball.yml index c6e85014e48..d5860da20a5 100644 --- a/Resources/Maps/Salvage/vegan-meatball.yml +++ b/Resources/Maps/Salvage/vegan-meatball.yml @@ -488,8 +488,6 @@ entities: splatPrototype: null lifespan: 75 endurance: 100 - weedTolerance: 5 - pestTolerance: 5 toxinsTolerance: 4 growthComponents: - !type:BasicGrowthComponent @@ -500,6 +498,9 @@ entities: heatTolerance: 10 lowPressureTolerance: 81 highPressureTolerance: 121 + - !type:WeedPestGrowthComponent + weedTolerance: 5 + pestTolerance: 5 chemicals: THC: Inherent: True @@ -559,8 +560,6 @@ entities: splatPrototype: null lifespan: 25 endurance: 100 - weedTolerance: 5 - pestTolerance: 5 toxinsTolerance: 4 growthComponents: - !type:BasicGrowthComponent @@ -571,6 +570,9 @@ entities: heatTolerance: 10 lowPressureTolerance: 81 highPressureTolerance: 121 + - !type:WeedPestGrowthComponent + weedTolerance: 5 + pestTolerance: 5 chemicals: Stellibinin: Inherent: True From 5416c4312627b06426779cd9a8900bdec9d5c4b7 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Fri, 1 Aug 2025 16:52:13 +0300 Subject: [PATCH 16/53] Data loss --- Resources/Prototypes/Hydroponics/seeds.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index c44c932027e..554e8063ab5 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -527,6 +527,8 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent + waterConsumption: 0.6 + nutrientConsumption: 0.5 - !type:AtmosphericGrowthComponent idealHeat: 288 @@ -548,6 +550,8 @@ growthStages: 3 growthComponents: - !type:BasicGrowthComponent + waterConsumption: 0.6 + nutrientConsumption: 0.8 - !type:AtmosphericGrowthComponent idealHeat: 288 From 2e3db2c4c011ed1cc3380f70336348d45aa46c1a Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Fri, 1 Aug 2025 17:58:29 +0300 Subject: [PATCH 17/53] ToxinsComponent --- .../Botany/Components/ToxinsComponent.cs | 18 +++++++++ Content.Server/Botany/SeedPrototype.cs | 9 ----- .../Botany/Systems/MutationSystem.cs | 2 - .../Botany/Systems/PlantHolderSystem.cs | 15 -------- Content.Server/Botany/Systems/ToxinsSystem.cs | 38 +++++++++++++++++++ Resources/Maps/Salvage/vegan-meatball.yml | 6 ++- 6 files changed, 60 insertions(+), 28 deletions(-) create mode 100644 Content.Server/Botany/Components/ToxinsComponent.cs create mode 100644 Content.Server/Botany/Systems/ToxinsSystem.cs diff --git a/Content.Server/Botany/Components/ToxinsComponent.cs b/Content.Server/Botany/Components/ToxinsComponent.cs new file mode 100644 index 00000000000..df45ee2d719 --- /dev/null +++ b/Content.Server/Botany/Components/ToxinsComponent.cs @@ -0,0 +1,18 @@ +namespace Content.Server.Botany.Components; + +[RegisterComponent] +[DataDefinition] +public sealed partial class ToxinsComponent : PlantGrowthComponent +{ + /// + /// Maximum toxin level the plant can tolerate before taking damage. + /// + [DataField] + public float ToxinsTolerance = 4f; + + /// + /// Divisor for calculating toxin uptake rate. Higher values mean slower toxin processing. + /// + [DataField] + public float ToxinUptakeDivisor = 10f; +} diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index 8543a224be9..988e74cc379 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -109,11 +109,6 @@ public partial class SeedData #endregion - #region Tolerances - [DataField] public float ToxinsTolerance = 4f; - - #endregion - #region General traits [DataField] public float Endurance = 100f; @@ -220,8 +215,6 @@ public partial class SeedData MutationPrototypes = new List(MutationPrototypes), Chemicals = new Dictionary(Chemicals), - ToxinsTolerance = ToxinsTolerance, - Endurance = Endurance, Yield = Yield, Lifespan = Lifespan, @@ -279,8 +272,6 @@ public partial class SeedData Chemicals = new Dictionary(Chemicals), - ToxinsTolerance = ToxinsTolerance, - Endurance = Endurance, Yield = Yield, Lifespan = Lifespan, diff --git a/Content.Server/Botany/Systems/MutationSystem.cs b/Content.Server/Botany/Systems/MutationSystem.cs index d6c9f90d4ca..fe52648e3d4 100644 --- a/Content.Server/Botany/Systems/MutationSystem.cs +++ b/Content.Server/Botany/Systems/MutationSystem.cs @@ -79,8 +79,6 @@ public sealed class MutationSystem : EntitySystem CrossChemicals(ref result.Chemicals, a.Chemicals); - CrossFloat(ref result.ToxinsTolerance, a.ToxinsTolerance); - CrossFloat(ref result.Endurance, a.Endurance); CrossInt(ref result.Yield, a.Yield); CrossFloat(ref result.Lifespan, a.Lifespan); diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 57605c1b3a6..199c2f1b4d7 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -446,21 +446,6 @@ public sealed class PlantHolderSystem : EntitySystem return; } - // 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; - } - CheckHealth(uid, component); CheckLevelSanity(uid, component); diff --git a/Content.Server/Botany/Systems/ToxinsSystem.cs b/Content.Server/Botany/Systems/ToxinsSystem.cs new file mode 100644 index 00000000000..e07ab64c267 --- /dev/null +++ b/Content.Server/Botany/Systems/ToxinsSystem.cs @@ -0,0 +1,38 @@ +using System; +using Content.Server.Botany.Components; + +namespace Content.Server.Botany.Systems; + +/// +/// Handles toxin tolerance and damage for plants. +/// +public sealed class ToxinsSystem : PlantGrowthSystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); + } + + private void OnPlantGrow(EntityUid uid, ToxinsComponent component, OnPlantGrowEvent args) + { + PlantHolderComponent? holder = null; + Resolve(uid, ref holder); + + if (holder == null || holder.Seed == null || holder.Dead) + return; + + if (holder.Toxins > 0) + { + var toxinUptake = MathF.Max(1, MathF.Round(holder.Toxins / component.ToxinUptakeDivisor)); + if (holder.Toxins > component.ToxinsTolerance) + { + holder.Health -= toxinUptake; + } + + holder.Toxins -= toxinUptake; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + } +} diff --git a/Resources/Maps/Salvage/vegan-meatball.yml b/Resources/Maps/Salvage/vegan-meatball.yml index d5860da20a5..c89966623b9 100644 --- a/Resources/Maps/Salvage/vegan-meatball.yml +++ b/Resources/Maps/Salvage/vegan-meatball.yml @@ -488,7 +488,6 @@ entities: splatPrototype: null lifespan: 75 endurance: 100 - toxinsTolerance: 4 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.4 @@ -501,6 +500,8 @@ entities: - !type:WeedPestGrowthComponent weedTolerance: 5 pestTolerance: 5 + - !type:ToxinsComponent + toxinsTolerance: 4 chemicals: THC: Inherent: True @@ -560,7 +561,6 @@ entities: splatPrototype: null lifespan: 25 endurance: 100 - toxinsTolerance: 4 growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 @@ -573,6 +573,8 @@ entities: - !type:WeedPestGrowthComponent weedTolerance: 5 pestTolerance: 5 + - !type:ToxinsComponent + toxinsTolerance: 4 chemicals: Stellibinin: Inherent: True From e55688084258b1497105f178ecfb53e109f6441c Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Fri, 1 Aug 2025 18:47:48 +0300 Subject: [PATCH 18/53] Default components --- .../Botany/Systems/PlantHolderSystem.cs | 13 +++ Resources/Prototypes/Hydroponics/seeds.yml | 83 ------------------- 2 files changed, 13 insertions(+), 83 deletions(-) diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 199c2f1b4d7..0f3da00a8fa 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -207,6 +207,8 @@ public sealed class PlantHolderSystem : EntitySystem foreach(var g in seed.GrowthComponents) EntityManager.AddComponent(uid, _copier.CreateCopy(g, notNullableOverride: true), overwrite: true); + EnsureDefaultGrowthComponents(uid); + if (TryComp(args.Used, out var paperLabel)) { _itemSlots.TryEjectToHands(args.Used, paperLabel.LabelSlot, args.User); @@ -769,4 +771,15 @@ public sealed class PlantHolderSystem : EntitySystem component.ForceUpdate = true; Update(uid, component); } + + /// + /// Ensures that all default growth components are added to the plant if they're not already present. + /// + private void EnsureDefaultGrowthComponents(EntityUid uid) + { + EnsureComp(uid); + EnsureComp(uid); + EnsureComp(uid); + EnsureComp(uid); + } } diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index 554e8063ab5..6ddf7598f63 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -17,7 +17,6 @@ growthComponents: - !type:BasicGrowthComponent nutrientConsumption: 0.4 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -45,7 +44,6 @@ growthComponents: - !type:BasicGrowthComponent nutrientConsumption: 0.4 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -73,7 +71,6 @@ growthComponents: - !type:BasicGrowthComponent nutrientConsumption: 0.4 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -162,7 +159,6 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent chemicals: JuiceCarrot: Min: 1 @@ -199,7 +195,6 @@ - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.6 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -233,9 +228,6 @@ production: 6 yield: 3 potency: 10 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -261,9 +253,6 @@ production: 6 yield: 4 potency: 1 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Vitamin: Min: 1 @@ -292,9 +281,6 @@ production: 6 yield: 3 potency: 10 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -324,9 +310,6 @@ production: 6 yield: 3 potency: 10 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -352,9 +335,6 @@ production: 6 yield: 3 potency: 10 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Haloperidol: Min: 1 @@ -385,9 +365,6 @@ yield: 3 potency: 10 growthStages: 3 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -420,7 +397,6 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -450,7 +426,6 @@ potency: 10 growthStages: 3 growthComponents: - - !type:BasicGrowthComponent - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -503,7 +478,6 @@ potency: 10 growthStages: 3 growthComponents: - - !type:BasicGrowthComponent - !type:AtmosphericGrowthComponent idealHeat: 298 @@ -718,7 +692,6 @@ potency: 20 growthStages: 3 growthComponents: - - !type:BasicGrowthComponent - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -746,9 +719,6 @@ yield: 3 potency: 10 growthStages: 1 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -774,9 +744,6 @@ yield: 3 potency: 25 growthStages: 3 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -808,9 +775,6 @@ production: 6 yield: 3 potency: 10 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -839,7 +803,6 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.75 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1189,7 +1152,6 @@ yield: 2 potency: 20 growthComponents: - - !type:BasicGrowthComponent - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -1222,7 +1184,6 @@ yield: 2 potency: 20 growthComponents: - - !type:BasicGrowthComponent - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -1259,7 +1220,6 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1288,7 +1248,6 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent chemicals: Aloe: Min: 1 @@ -1319,7 +1278,6 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1348,7 +1306,6 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent chemicals: Ultravasculine: Min: 1 @@ -1379,7 +1336,6 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1420,7 +1376,6 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1459,7 +1414,6 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent chemicals: Stellibinin: Min: 1 @@ -1481,9 +1435,6 @@ yield: 3 potency: 10 growthStages: 3 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Razorium: Min: 1 @@ -1509,7 +1460,6 @@ - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.5 - - !type:AtmosphericGrowthComponent chemicals: Amatoxin: Min: 1 @@ -1538,9 +1488,6 @@ yield: 1 potency: 10 growthStages: 2 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1566,9 +1513,6 @@ yield: 1 potency: 10 growthStages: 2 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1594,9 +1538,6 @@ yield: 1 potency: 10 growthStages: 2 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1626,7 +1567,6 @@ - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.4 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1657,7 +1597,6 @@ growthComponents: - !type:BasicGrowthComponent nutrientConsumption: 0.4 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1682,7 +1621,6 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1711,7 +1649,6 @@ growthComponents: - !type:BasicGrowthComponent nutrientConsumption: 0.4 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1737,9 +1674,6 @@ yield: 3 potency: 10 growthStages: 2 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1766,9 +1700,6 @@ production: 3 yield: 1 potency: 1 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1797,9 +1728,6 @@ production: 3 yield: 1 potency: 1 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1861,7 +1789,6 @@ growthComponents: - !type:BasicGrowthComponent nutrientConsumption: 0.6 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 2 @@ -1924,7 +1851,6 @@ growthComponents: - !type:BasicGrowthComponent nutrientConsumption: 0.5 - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -1954,7 +1880,6 @@ growthComponents: - !type:BasicGrowthComponent nutrientConsumption: 0.5 - - !type:AtmosphericGrowthComponent chemicals: Happiness: Min: 1 @@ -1987,7 +1912,6 @@ potency: 10 growthStages: 3 growthComponents: - - !type:BasicGrowthComponent - !type:AtmosphericGrowthComponent idealHeat: 288 chemicals: @@ -2016,7 +1940,6 @@ potency: 10 growthStages: 3 growthComponents: - - !type:BasicGrowthComponent - !type:AtmosphericGrowthComponent idealHeat: 288 chemicals: @@ -2053,7 +1976,6 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent chemicals: Fiber: Min: 5 @@ -2078,7 +2000,6 @@ growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.8 - - !type:AtmosphericGrowthComponent chemicals: Fiber: Min: 5 @@ -2104,9 +2025,6 @@ production: 6 yield: 5 potency: 10 - growthComponents: - - !type:BasicGrowthComponent - - !type:AtmosphericGrowthComponent chemicals: Nutriment: Min: 1 @@ -2136,7 +2054,6 @@ - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.5 - - !type:AtmosphericGrowthComponent chemicals: Artifexium: Min: 1 From b44239d9aee5f38e9527f9dac499f0b445baa4d3 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Fri, 1 Aug 2025 23:31:24 +0300 Subject: [PATCH 19/53] minor fix --- Content.Server/Botany/SeedPrototype.cs | 48 +++++++++++++++---- .../Botany/Systems/MutationSystem.cs | 1 + .../Botany/Systems/PlantHolderSystem.cs | 1 + Resources/Maps/Salvage/vegan-meatball.yml | 2 - Resources/Prototypes/Hydroponics/seeds.yml | 4 -- 5 files changed, 42 insertions(+), 14 deletions(-) diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index 988e74cc379..e4878cd5603 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -111,21 +111,49 @@ public partial class SeedData #region General traits + /// + /// The plant's max health. + /// [DataField] public float Endurance = 100f; + /// + /// How many produce are created on harvest. + /// [DataField] public int Yield; + + /// + /// The number of growth ticks this plant can be alive for. Plants take high damage levels when Age > Lifespan. + /// [DataField] public float Lifespan; + + /// + /// The number of growth ticks it takes for a plant to reach its final growth stage. + /// [DataField] public float Maturation; + + /// + /// The number of growth ticks it takes for a plant to be (re-)harvestable. Shouldn't be lower than Maturation. + /// [DataField] public float Production; + + /// + /// How many different sprites appear before the plant is fully grown. + /// [DataField] public int GrowthStages = 6; + /// + /// Harvest options are NoRepeat(plant is removed on harvest), Repeat(Plant makes produce every Production ticks), + /// and SelfHarvest (Repeat, plus produce is dropped on the ground near the plant automatically) + /// [DataField] public HarvestType HarvestRepeat = HarvestType.NoRepeat; + /// + /// A scalar for sprite size and chemical quantity on the produce. Caps at 100. + /// [DataField] public float Potency = 1f; /// - /// If true, cannot be harvested for seeds. Balances hybrids and - /// mutations. + /// If true, produce can't be put into the seed maker. /// [DataField] public bool Seedless = false; @@ -149,12 +177,20 @@ public partial class SeedData [DataField] public SoundSpecifier ScreamSound = new SoundCollectionSpecifier("PlantScreams", AudioParams.Default.WithVolume(-10)); + /// + /// If true, AAAAAAAAAAAHHHHHHHHHHH! + /// [DataField("screaming")] public bool CanScream; + /// + /// Which kind of kudzu this plant will turn into if it kuzuifies. + /// [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string KudzuPrototype = "WeakKudzu"; + /// + /// If true, this plant turns into it's KudzuPrototype when the PlantHolder's WeedLevel hits this plant's WeedHighLevelThreshold. + /// [DataField] public bool TurnIntoKudzu; - [DataField] public string? SplatPrototype { get; set; } #endregion @@ -176,7 +212,7 @@ public partial class SeedData public List GrowthComponents = new(); /// - /// Whether this seed is viable for growth. + /// If false, rapidly decrease health while growing. Adds a bit of challenge to keep mutated plants alive via Unviable's frequency. /// [DataField] public bool Viable = true; @@ -192,8 +228,6 @@ public partial class SeedData /// [DataField] public LogImpact? PlantLogImpact; - //TODO: the mutation system should add the missing components when they mutate. - //This would be done with EnsureComp<> public SeedData Clone() { @@ -231,7 +265,6 @@ public partial class SeedData PlantIconState = PlantIconState, CanScream = CanScream, TurnIntoKudzu = TurnIntoKudzu, - SplatPrototype = SplatPrototype, Mutations = new List(), // Newly cloned seed is unique. No need to unnecessarily clone if repeatedly modified. @@ -290,7 +323,6 @@ public partial class SeedData PlantIconState = other.PlantIconState, CanScream = CanScream, TurnIntoKudzu = TurnIntoKudzu, - SplatPrototype = other.SplatPrototype, // Newly cloned seed is unique. No need to unnecessarily clone if repeatedly modified. Unique = true, diff --git a/Content.Server/Botany/Systems/MutationSystem.cs b/Content.Server/Botany/Systems/MutationSystem.cs index fe52648e3d4..4975787ec6b 100644 --- a/Content.Server/Botany/Systems/MutationSystem.cs +++ b/Content.Server/Botany/Systems/MutationSystem.cs @@ -89,6 +89,7 @@ public sealed class MutationSystem : EntitySystem CrossBool(ref result.Seedless, a.Seedless); CrossBool(ref result.Ligneous, a.Ligneous); CrossBool(ref result.TurnIntoKudzu, a.TurnIntoKudzu); + CrossBool(ref result.CanScream, a.CanScream); // LINQ Explanation // For the list of mutation effects on both plants, use a 50% chance to pick each one. diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 0f3da00a8fa..0ef689a10e4 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -455,6 +455,7 @@ public sealed class PlantHolderSystem : EntitySystem UpdateSprite(uid, component); } + //TODO: kill this bullshit /// /// Ensures all plant holder levels are within valid ranges. /// diff --git a/Resources/Maps/Salvage/vegan-meatball.yml b/Resources/Maps/Salvage/vegan-meatball.yml index c89966623b9..1d59c91e8a5 100644 --- a/Resources/Maps/Salvage/vegan-meatball.yml +++ b/Resources/Maps/Salvage/vegan-meatball.yml @@ -485,7 +485,6 @@ entities: age: 1 health: 100 seed: - splatPrototype: null lifespan: 75 endurance: 100 growthComponents: @@ -558,7 +557,6 @@ entities: age: 1 health: 100 seed: - splatPrototype: null lifespan: 25 endurance: 100 growthComponents: diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index 6ddf7598f63..2a63340d5fb 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -547,7 +547,6 @@ production: 6 yield: 2 potency: 10 - splatPrototype: PuddleSplatter growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 @@ -583,7 +582,6 @@ production: 6 yield: 2 potency: 10 - splatPrototype: PuddleSplatter growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 @@ -621,7 +619,6 @@ production: 6 yield: 2 potency: 10 - splatPrototype: PuddleSplatter growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 @@ -656,7 +653,6 @@ yield: 2 potency: 10 growthStages: 2 - splatPrototype: PuddleSplatter growthComponents: - !type:BasicGrowthComponent waterConsumption: 0.6 From 59e033f2dc410092937152f913a9eaea976fa600 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Sat, 2 Aug 2025 06:01:37 +0300 Subject: [PATCH 20/53] PlantTraitsComponent --- .../Botany/Components/HarvestComponent.cs | 27 + .../Botany/Components/PlantTraitsComponent.cs | 74 ++ Content.Server/Botany/SeedPrototype.cs | 94 -- .../Botany/Systems/AutoHarvestGrowthSystem.cs | 7 +- .../Botany/Systems/BasicGrowthSystem.cs | 33 +- .../Botany/Systems/BotanySystem.Produce.cs | 11 +- .../Botany/Systems/BotanySystem.Seed.cs | 56 +- .../Botany/Systems/HarvestSystem.cs | 176 ++++ .../Botany/Systems/MutationSystem.cs | 27 +- .../Botany/Systems/PlantGrowthSystem.cs | 24 +- .../Botany/Systems/PlantHolderSystem.cs | 131 +-- .../Botany/Systems/PlantTraitsSystem.cs | 57 ++ .../Botany/Systems/SeedExtractorSystem.cs | 6 +- .../EntityEffects/EntityEffectSystem.cs | 121 ++- Resources/Maps/Salvage/vegan-meatball.yml | 10 +- Resources/Prototypes/Hydroponics/seeds.yml | 966 ++++++++++-------- 16 files changed, 1132 insertions(+), 688 deletions(-) create mode 100644 Content.Server/Botany/Components/HarvestComponent.cs create mode 100644 Content.Server/Botany/Components/PlantTraitsComponent.cs create mode 100644 Content.Server/Botany/Systems/HarvestSystem.cs create mode 100644 Content.Server/Botany/Systems/PlantTraitsSystem.cs diff --git a/Content.Server/Botany/Components/HarvestComponent.cs b/Content.Server/Botany/Components/HarvestComponent.cs new file mode 100644 index 00000000000..73f35f3765a --- /dev/null +++ b/Content.Server/Botany/Components/HarvestComponent.cs @@ -0,0 +1,27 @@ +using Content.Server.Botany.Systems; + +namespace Content.Server.Botany.Components; + +[RegisterComponent] +[DataDefinition] +public sealed partial class HarvestComponent : PlantGrowthComponent +{ + /// + /// Harvest options are NoRepeat(plant is removed on harvest), Repeat(Plant makes produce every Production ticks), + /// and SelfHarvest (Repeat, plus produce is dropped on the ground near the plant automatically) + /// + [DataField] + public HarvestType HarvestRepeat = HarvestType.NoRepeat; + + /// + /// Whether the plant is currently ready for harvest. + /// + [ViewVariables] + public bool ReadyForHarvest = false; + + /// + /// The last time this plant was harvested. + /// + [ViewVariables] + public float LastHarvestTime = 0f; +} diff --git a/Content.Server/Botany/Components/PlantTraitsComponent.cs b/Content.Server/Botany/Components/PlantTraitsComponent.cs new file mode 100644 index 00000000000..68d1b35bfb0 --- /dev/null +++ b/Content.Server/Botany/Components/PlantTraitsComponent.cs @@ -0,0 +1,74 @@ +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.Botany.Components; + +[RegisterComponent] +[DataDefinition] +public sealed partial class PlantTraitsComponent : PlantGrowthComponent +{ + /// + /// The plant's max health. + /// + [DataField] + public float Endurance = 100f; + + /// + /// How many produce are created on harvest. + /// + [DataField] + public int Yield; + + /// + /// The number of growth ticks this plant can be alive for. Plants take high damage levels when Age > Lifespan. + /// + [DataField] + public float Lifespan; + + /// + /// The number of growth ticks it takes for a plant to reach its final growth stage. + /// + [DataField] + public float Maturation; + + /// + /// The number of growth ticks it takes for a plant to be (re-)harvestable. Shouldn't be lower than Maturation. + /// + [DataField] + public float Production; + + /// + /// How many different sprites appear before the plant is fully grown. + /// + [DataField] + public int GrowthStages = 6; + + /// + /// A scalar for sprite size and chemical quantity on the produce. Caps at 100. + /// + [DataField] + public float Potency = 1f; + + /// + /// If true, produce can't be put into the seed maker. + /// + [DataField] + public bool Seedless = false; + + /// + /// If true, a sharp tool is required to harvest this plant. + /// + [DataField] + public bool Ligneous = false; + + /// + /// If true, the plant can scream when harvested. + /// + [DataField] + public bool CanScream = false; + + /// + /// If true, the plant can turn into kudzu. + /// + [DataField] + public bool TurnIntoKudzu = false; +} diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index e4878cd5603..29cc4a46c0b 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -19,13 +19,6 @@ public sealed partial class SeedPrototype : SeedData, IPrototype [IdDataField] public string ID { get; private set; } = default!; } -public enum HarvestType : byte -{ - NoRepeat, - Repeat, - SelfHarvest -} - [DataDefinition] public partial struct SeedChemQuantity { @@ -111,56 +104,7 @@ public partial class SeedData #region General traits - /// - /// The plant's max health. - /// - [DataField] public float Endurance = 100f; - /// - /// How many produce are created on harvest. - /// - [DataField] public int Yield; - - /// - /// The number of growth ticks this plant can be alive for. Plants take high damage levels when Age > Lifespan. - /// - [DataField] public float Lifespan; - - /// - /// The number of growth ticks it takes for a plant to reach its final growth stage. - /// - [DataField] public float Maturation; - - /// - /// The number of growth ticks it takes for a plant to be (re-)harvestable. Shouldn't be lower than Maturation. - /// - [DataField] public float Production; - - /// - /// How many different sprites appear before the plant is fully grown. - /// - [DataField] public int GrowthStages = 6; - - /// - /// Harvest options are NoRepeat(plant is removed on harvest), Repeat(Plant makes produce every Production ticks), - /// and SelfHarvest (Repeat, plus produce is dropped on the ground near the plant automatically) - /// - [DataField] public HarvestType HarvestRepeat = HarvestType.NoRepeat; - - /// - /// A scalar for sprite size and chemical quantity on the produce. Caps at 100. - /// - [DataField] public float Potency = 1f; - - /// - /// If true, produce can't be put into the seed maker. - /// - [DataField] public bool Seedless = false; - - /// - /// If true, a sharp tool is required to harvest this plant. - /// - [DataField] public bool Ligneous; #endregion @@ -177,21 +121,11 @@ public partial class SeedData [DataField] public SoundSpecifier ScreamSound = new SoundCollectionSpecifier("PlantScreams", AudioParams.Default.WithVolume(-10)); - /// - /// If true, AAAAAAAAAAAHHHHHHHHHHH! - /// - [DataField("screaming")] public bool CanScream; - /// /// Which kind of kudzu this plant will turn into if it kuzuifies. /// [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string KudzuPrototype = "WeakKudzu"; - /// - /// If true, this plant turns into it's KudzuPrototype when the PlantHolder's WeedLevel hits this plant's WeedHighLevelThreshold. - /// - [DataField] public bool TurnIntoKudzu; - #endregion /// @@ -249,22 +183,8 @@ public partial class SeedData MutationPrototypes = new List(MutationPrototypes), Chemicals = new Dictionary(Chemicals), - Endurance = Endurance, - Yield = Yield, - Lifespan = Lifespan, - Maturation = Maturation, - Production = Production, - GrowthStages = GrowthStages, - HarvestRepeat = HarvestRepeat, - Potency = Potency, - - Seedless = Seedless, - Ligneous = Ligneous, - PlantRsi = PlantRsi, PlantIconState = PlantIconState, - CanScream = CanScream, - TurnIntoKudzu = TurnIntoKudzu, Mutations = new List(), // Newly cloned seed is unique. No need to unnecessarily clone if repeatedly modified. @@ -305,24 +225,10 @@ public partial class SeedData Chemicals = new Dictionary(Chemicals), - Endurance = Endurance, - Yield = Yield, - Lifespan = Lifespan, - Maturation = Maturation, - Production = Production, - GrowthStages = other.GrowthStages, - HarvestRepeat = HarvestRepeat, - Potency = Potency, - Mutations = Mutations, - Seedless = Seedless, - Ligneous = Ligneous, - PlantRsi = other.PlantRsi, PlantIconState = other.PlantIconState, - CanScream = CanScream, - TurnIntoKudzu = TurnIntoKudzu, // Newly cloned seed is unique. No need to unnecessarily clone if repeatedly modified. Unique = true, diff --git a/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs b/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs index 0aa75de4e84..bc82f101536 100644 --- a/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs +++ b/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs @@ -19,11 +19,12 @@ public sealed class AutoHarvestGrowthSystem : PlantGrowthSystem if (holder == null || holder.Seed == null || holder.Dead) return; - if (holder.Harvest && _random.Prob(component.HarvestChance)) + // Check if ready for harvest using HarvestComponent + if (TryComp(uid, out var harvestComp) && harvestComp.ReadyForHarvest && _random.Prob(component.HarvestChance)) { // Auto-harvest the plant - holder.Harvest = false; - holder.LastProduce = holder.Age; + harvestComp.ReadyForHarvest = false; + harvestComp.LastHarvestTime = holder.Age; // Spawn the harvested items if (holder.Seed.ProductPrototypes.Count > 0) diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index e20f85efa28..4e11c6b0f07 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -70,13 +70,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem } } - if (holder.Age > holder.Seed.Lifespan) - { - holder.Health -= _random.Next(3, 5) * PlantGrowthSystem.HydroponicsSpeedMultiplier; - if (holder.DrawWarnings) - holder.UpdateSpriteAfterUpdate = true; - } - else if (holder.Age < 0) // Revert back to seed packet! + if (holder.Age < 0) // Revert back to seed packet! { var packetSeed = holder.Seed; // will put it in the trays hands if it has any, please do not try doing this @@ -86,27 +80,6 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem _plantHolder.Update(uid, holder); } - // If enough time has passed since the plant was harvested, we're ready to harvest again! - if (holder.Seed.ProductPrototypes.Count > 0) - { - if (holder.Age > holder.Seed.Production) - { - if (holder.Age - holder.LastProduce > holder.Seed.Production && !holder.Harvest) - { - holder.Harvest = true; - holder.LastProduce = holder.Age; - } - } - else - { - if (holder.Harvest) - { - holder.Harvest = false; - holder.LastProduce = holder.Age; - } - } - } - if (component.WaterConsumption > 0 && holder.WaterLevel > 0 && _random.Prob(0.75f)) { holder.WaterLevel -= MathF.Max(0f, @@ -133,7 +106,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem } else { - AffectGrowth(-1, holder); + AffectGrowth(uid, -1, holder); holder.Health -= healthMod; } @@ -143,7 +116,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem } else { - AffectGrowth(-1, holder); + AffectGrowth(uid, -1, holder); holder.Health -= healthMod; } } diff --git a/Content.Server/Botany/Systems/BotanySystem.Produce.cs b/Content.Server/Botany/Systems/BotanySystem.Produce.cs index f6f3f99c09e..ee8b8ae96cb 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Produce.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Produce.cs @@ -28,11 +28,18 @@ public sealed partial class BotanySystem return; solutionContainer.RemoveAllSolution(); + + var traits = GetPlantTraits(seed); + if (traits == null) + return; + foreach (var (chem, quantity) in seed.Chemicals) { var amount = FixedPoint2.New(quantity.Min); - if (quantity.PotencyDivisor > 0 && seed.Potency > 0) - amount += FixedPoint2.New(seed.Potency / quantity.PotencyDivisor); + + if (quantity.PotencyDivisor > 0 && traits.Potency > 0) + amount += FixedPoint2.New(traits.Potency / quantity.PotencyDivisor); + amount = FixedPoint2.New(MathHelper.Clamp(amount.Float(), quantity.Min, quantity.Max)); solutionContainer.MaxVolume += amount; solutionContainer.AddReagent(chem, amount); diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index a2f8bfeb692..79f30fc27a2 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -78,6 +78,30 @@ public sealed partial class BotanySystem : EntitySystem return false; } + public PlantTraitsComponent? GetPlantTraits(SeedData seed) + { + foreach (var growthComponent in seed.GrowthComponents) + { + if (growthComponent is PlantTraitsComponent plantTraits) + { + return plantTraits; + } + } + return null; + } + + public HarvestComponent? GetHarvestComponent(SeedData seed) + { + foreach (var growthComponent in seed.GrowthComponents) + { + if (growthComponent is HarvestComponent harvestComp) + { + return harvestComp; + } + } + return null; + } + private void OnExamined(EntityUid uid, SeedComponent component, ExaminedEvent args) { if (!args.IsInDetailsRange) @@ -86,12 +110,16 @@ public sealed partial class BotanySystem : EntitySystem if (!TryGetSeed(component, out var seed)) return; + var traits = GetPlantTraits(seed); + if (traits == null) + return; + using (args.PushGroup(nameof(SeedComponent), 1)) { var name = Loc.GetString(seed.DisplayName); args.PushMarkup(Loc.GetString($"seed-component-description", ("seedName", name))); - args.PushMarkup(Loc.GetString($"seed-component-plant-yield-text", ("seedYield", seed.Yield))); - args.PushMarkup(Loc.GetString($"seed-component-plant-potency-text", ("seedPotency", seed.Potency))); + args.PushMarkup(Loc.GetString($"seed-component-plant-yield-text", ("seedYield", traits.Yield))); + args.PushMarkup(Loc.GetString($"seed-component-plant-potency-text", ("seedPotency", traits.Potency))); } } @@ -133,7 +161,8 @@ public sealed partial class BotanySystem : EntitySystem public IEnumerable Harvest(SeedData proto, EntityUid user, int yieldMod = 1) { - if (proto.ProductPrototypes.Count == 0 || proto.Yield <= 0) + var traits = GetPlantTraits(proto); + if (traits == null || proto.ProductPrototypes.Count == 0 || traits.Yield <= 0) { _popupSystem.PopupCursor(Loc.GetString("botany-harvest-fail-message"), user, PopupType.Medium); return Enumerable.Empty(); @@ -150,20 +179,25 @@ public sealed partial class BotanySystem : EntitySystem public IEnumerable GenerateProduct(SeedData proto, EntityCoordinates position, int yieldMod = 1) { + var traits = GetPlantTraits(proto); + if (traits == null) + return Enumerable.Empty(); + var totalYield = 0; - if (proto.Yield > -1) + + if (traits.Yield > -1) { if (yieldMod < 0) - totalYield = proto.Yield; + totalYield = traits.Yield; else - totalYield = proto.Yield * yieldMod; + totalYield = traits.Yield * yieldMod; totalYield = Math.Max(1, totalYield); } var products = new List(); - if (totalYield > 1 || proto.HarvestRepeat != HarvestType.NoRepeat) + if (totalYield > 1) proto.Unique = false; for (var i = 0; i < totalYield; i++) @@ -179,7 +213,7 @@ public sealed partial class BotanySystem : EntitySystem produce.Seed = proto.Clone(); ProduceGrown(entity, produce); - _appearance.SetData(entity, ProduceVisuals.Potency, proto.Potency); + _appearance.SetData(entity, ProduceVisuals.Potency, traits.Potency); if (proto.Mysterious) { @@ -195,7 +229,11 @@ public sealed partial class BotanySystem : EntitySystem public bool CanHarvest(SeedData proto, EntityUid? held = null) { - return !proto.Ligneous || proto.Ligneous && held != null && HasComp(held); + var traits = GetPlantTraits(proto); + if (traits == null) + return true; + + return !traits.Ligneous || traits.Ligneous && held != null && HasComp(held); } #endregion diff --git a/Content.Server/Botany/Systems/HarvestSystem.cs b/Content.Server/Botany/Systems/HarvestSystem.cs new file mode 100644 index 00000000000..ac407c8ed38 --- /dev/null +++ b/Content.Server/Botany/Systems/HarvestSystem.cs @@ -0,0 +1,176 @@ +using Content.Server.Botany.Components; +using Content.Server.Hands.Systems; +using Content.Server.Popups; +using Content.Shared.Botany; +using Content.Shared.Hands.Components; +using Content.Shared.Interaction; +using Content.Shared.Popups; +using Robust.Server.GameObjects; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server.Botany.Systems; + +/// +/// Harvest options for plants. +/// +public enum HarvestType +{ + /// + /// Plant is removed on harvest. + /// + NoRepeat, + + /// + /// Plant makes produce every Production ticks. + /// + Repeat, + + /// + /// Repeat, plus produce is dropped on the ground near the plant automatically. + /// + SelfHarvest +} + +public sealed class HarvestSystem : EntitySystem +{ + [Dependency] private readonly BotanySystem _botany = default!; + [Dependency] private readonly HandsSystem _hands = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly PlantHolderSystem _plantHolder = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); + SubscribeLocalEvent(OnInteractHand); + } + + private void OnPlantGrow(EntityUid uid, HarvestComponent component, OnPlantGrowEvent args) + { + if (!TryComp(uid, out var plantHolder) || + !TryComp(uid, out var traits)) + return; + + if (plantHolder.Dead || plantHolder.Seed == null) + return; + + // Check if plant is ready for harvest + if (plantHolder.Age >= traits.Production) + { + var timeSinceLastHarvest = plantHolder.Age - component.LastHarvestTime; + if (timeSinceLastHarvest >= traits.Production && !component.ReadyForHarvest) + { + component.ReadyForHarvest = true; + plantHolder.UpdateSpriteAfterUpdate = true; + } + } + } + + private void OnInteractHand(EntityUid uid, HarvestComponent component, InteractHandEvent args) + { + if (!TryComp(uid, out var plantHolder) || + !TryComp(uid, out var traits)) + return; + + if (!component.ReadyForHarvest || plantHolder.Dead) + return; + + // Check if sharp tool is required + if (traits.Ligneous) + { + if (!_hands.TryGetActiveItem(args.User, out var activeItem) || + plantHolder.Seed == null || + !_botany.CanHarvest(plantHolder.Seed, activeItem)) + { + _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), args.User); + return; + } + } + + // Perform harvest + DoHarvest(uid, args.User, component, plantHolder, traits); + } + + public void DoHarvest(EntityUid plantUid, EntityUid user, HarvestComponent? harvestComp = null, + PlantHolderComponent? plantHolder = null, PlantTraitsComponent? traits = null) + { + if (!Resolve(plantUid, ref harvestComp, ref plantHolder, ref traits)) + return; + + if (plantHolder.Dead) + { + // Remove dead plant + _plantHolder.RemovePlant(plantUid, plantHolder); + AfterHarvest(plantUid, harvestComp, plantHolder); + return; + } + + if (!harvestComp.ReadyForHarvest) + return; + + // Spawn products + var yield = traits.Yield; + if (plantHolder.Seed?.ProductPrototypes != null) + { + for (int i = 0; i < yield; i++) + { + foreach (var productPrototype in plantHolder.Seed.ProductPrototypes) + { + var product = Spawn(productPrototype, Transform(plantUid).Coordinates); + + // Apply mutations to product + if (TryComp(product, out var produce)) + { + _botany.ProduceGrown(product, produce); + } + } + } + } + + // Handle harvest type + switch (harvestComp.HarvestRepeat) + { + case HarvestType.NoRepeat: + _plantHolder.RemovePlant(plantUid, plantHolder); + break; + case HarvestType.Repeat: + case HarvestType.SelfHarvest: + harvestComp.ReadyForHarvest = false; + harvestComp.LastHarvestTime = plantHolder.Age; + break; + } + + AfterHarvest(plantUid, harvestComp, plantHolder); + } + + private void AfterHarvest(EntityUid uid, HarvestComponent component, PlantHolderComponent plantHolder) + { + // Play scream sound if applicable + if (TryComp(uid, out var traits) && traits.CanScream && plantHolder.Seed != null) + { + _audio.PlayPvs(plantHolder.Seed.ScreamSound, uid); + } + + // Update sprite + _plantHolder.UpdateSprite(uid, plantHolder); + } + + public void AutoHarvest(EntityUid uid, HarvestComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + if (!component.ReadyForHarvest) + return; + + DoHarvest(uid, uid, component); + } +} diff --git a/Content.Server/Botany/Systems/MutationSystem.cs b/Content.Server/Botany/Systems/MutationSystem.cs index 4975787ec6b..d7210380469 100644 --- a/Content.Server/Botany/Systems/MutationSystem.cs +++ b/Content.Server/Botany/Systems/MutationSystem.cs @@ -1,3 +1,4 @@ +using Content.Server.Botany.Systems; using Content.Shared.Atmos; using Content.Shared.EntityEffects; using Content.Shared.Random; @@ -13,6 +14,7 @@ public sealed class MutationSystem : EntitySystem [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly BotanySystem _botanySystem = default!; private RandomPlantMutationListPrototype _randomMutations = default!; public override void Initialize() @@ -79,17 +81,16 @@ public sealed class MutationSystem : EntitySystem CrossChemicals(ref result.Chemicals, a.Chemicals); - CrossFloat(ref result.Endurance, a.Endurance); - CrossInt(ref result.Yield, a.Yield); - CrossFloat(ref result.Lifespan, a.Lifespan); - CrossFloat(ref result.Maturation, a.Maturation); - CrossFloat(ref result.Production, a.Production); - CrossFloat(ref result.Potency, a.Potency); + var aTraits = _botanySystem.GetPlantTraits(a); + var resultTraits = _botanySystem.GetPlantTraits(result); - CrossBool(ref result.Seedless, a.Seedless); - CrossBool(ref result.Ligneous, a.Ligneous); - CrossBool(ref result.TurnIntoKudzu, a.TurnIntoKudzu); - CrossBool(ref result.CanScream, a.CanScream); + if (aTraits != null && resultTraits != null) + { + CrossBool(ref resultTraits.Seedless, aTraits.Seedless); + CrossBool(ref resultTraits.Ligneous, aTraits.Ligneous); + CrossBool(ref resultTraits.CanScream, aTraits.CanScream); + CrossBool(ref resultTraits.TurnIntoKudzu, aTraits.TurnIntoKudzu); + } // LINQ Explanation // For the list of mutation effects on both plants, use a 50% chance to pick each one. @@ -100,7 +101,11 @@ public sealed class MutationSystem : EntitySystem // effective hybrid crossings. if (a.Name != result.Name && Random(0.7f)) { - result.Seedless = true; + var traits = _botanySystem.GetPlantTraits(result); + if (traits != null) + { + traits.Seedless = true; + } } return result; diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs index 05084911bc8..8ea753c1046 100644 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -73,23 +73,35 @@ public abstract class PlantGrowthSystem : EntitySystem /// /// Affects the growth of a plant by modifying its age or production timing. /// - public void AffectGrowth(int amount, PlantHolderComponent? component = null) + public void AffectGrowth(EntityUid uid, int amount, PlantHolderComponent? component = null) { - if (component == null || component.Seed == null) + if (!Resolve(uid, ref component) || component.Seed == null) return; + PlantTraitsComponent? traits = null; + Resolve(uid, ref traits); + + if (traits == null) + return; + + // Synchronize harvest status with HarvestComponent if present + if (TryComp(uid, out var harvestComp)) + { + component.Harvest = harvestComp.ReadyForHarvest; + } + if (amount > 0) { - if (component.Age < component.Seed.Maturation) + if (component.Age < traits.Maturation) component.Age += amount; - else if (!component.Harvest && component.Seed.Yield > 0f) + else if (!component.Harvest && traits.Yield > 0f) component.LastProduce -= amount; } else { - if (component.Age < component.Seed.Maturation) + if (component.Age < traits.Maturation) component.SkipAging++; - else if (!component.Harvest && component.Seed.Yield > 0f) + else if (!component.Harvest && traits.Yield > 0f) component.LastProduce += amount; } } diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 0ef689a10e4..0ae4941c8a4 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -87,7 +87,10 @@ public sealed class PlantHolderSystem : EntitySystem if (component.Seed == null) return 0; - var result = Math.Max(1, (int)(component.Age * component.Seed.GrowthStages / component.Seed.Maturation)); + if (!TryComp(uid, out var traits)) + return 1; + + var result = Math.Max(1, (int)(component.Age * traits.GrowthStages / traits.Maturation)); return result; } @@ -111,25 +114,28 @@ public sealed class PlantHolderSystem : EntitySystem ("seedName", displayName), ("toBeForm", displayName.EndsWith('s') ? "are" : "is"))); - if (component.Health <= component.Seed.Endurance / 2) + if (!TryComp(entity, out var traits)) + return; + + if (component.Health <= traits.Endurance / 2) { args.PushMarkup(Loc.GetString( "plant-holder-component-something-already-growing-low-health-message", ("healthState", - Loc.GetString(component.Age > component.Seed.Lifespan + Loc.GetString(component.Age > traits.Lifespan ? "plant-holder-component-plant-old-adjective" : "plant-holder-component-plant-unhealthy-adjective")))); } // For future reference, mutations should only appear on examine if they apply to a plant, not to produce. - if (component.Seed.Ligneous) + if (traits.Ligneous) args.PushMarkup(Loc.GetString("mutation-plant-ligneous")); - if (component.Seed.TurnIntoKudzu) + if (traits.TurnIntoKudzu) args.PushMarkup(Loc.GetString("mutation-plant-kudzu")); - if (component.Seed.CanScream) + if (traits.CanScream) args.PushMarkup(Loc.GetString("mutation-plant-scream")); if (component.Seed.Viable == false) @@ -189,22 +195,26 @@ public sealed class PlantHolderSystem : EntitySystem component.Seed = seed.Clone(); component.Dead = false; component.Age = 1; + component.DrawWarnings = true; + + // Get endurance from seed's PlantTraitsComponent + var seedTraits = _botany.GetPlantTraits(seed); if (seeds.HealthOverride != null) { component.Health = seeds.HealthOverride.Value; } - else + else if (seedTraits != null) { - component.Health = component.Seed.Endurance; + component.Health = seedTraits.Endurance; } component.LastCycle = _gameTiming.CurTime; // Ensure no existing growth components before adding new ones var existingGrowthComponents = EntityManager.GetComponents(uid).ToList(); - foreach(var g in existingGrowthComponents) + foreach (var g in existingGrowthComponents) EntityManager.RemoveComponent(uid, g); - foreach(var g in seed.GrowthComponents) + foreach (var g in seed.GrowthComponents) EntityManager.AddComponent(uid, _copier.CreateCopy(g, notNullableOverride: true), overwrite: true); EnsureDefaultGrowthComponents(uid); @@ -361,8 +371,12 @@ public sealed class PlantHolderSystem : EntitySystem var seed = produce.Seed; if (seed != null) { - var nutrientBonus = seed.Potency / 2.5f; - AdjustNutrient(uid, nutrientBonus, component); + var seedTraits = _botany.GetPlantTraits(seed); + if (seedTraits != null) + { + var nutrientBonus = seedTraits.Potency / 2.5f; + AdjustNutrient(uid, nutrientBonus, component); + } } QueueDel(args.Used); } @@ -418,7 +432,7 @@ public sealed class PlantHolderSystem : EntitySystem var chance = 0f; if (component.Seed == null) chance = 0.05f; - else if (component.Seed.TurnIntoKudzu) + else if (TryComp(uid, out var traits) && traits.TurnIntoKudzu) chance = 1f; else chance = 0.01f; @@ -430,11 +444,11 @@ public sealed class PlantHolderSystem : EntitySystem component.UpdateSpriteAfterUpdate = true; } - if (component.Seed != null && component.Seed.TurnIntoKudzu + if (component.Seed != null && TryComp(uid, out var kudzuTraits) && kudzuTraits.TurnIntoKudzu && component.WeedLevel >= WeedHighLevelThreshold) { Spawn(component.Seed.KudzuPrototype, Transform(uid).Coordinates.SnapToGrid(EntityManager)); - component.Seed.TurnIntoKudzu = false; + kudzuTraits.TurnIntoKudzu = false; component.Health = 0; } @@ -451,6 +465,12 @@ public sealed class PlantHolderSystem : EntitySystem CheckHealth(uid, component); CheckLevelSanity(uid, component); + // Synchronize harvest status between PlantHolderComponent and HarvestComponent + if (TryComp(uid, out var harvestComp)) + { + component.Harvest = harvestComp.ReadyForHarvest; + } + if (component.UpdateSpriteAfterUpdate) UpdateSprite(uid, component); } @@ -464,8 +484,8 @@ public sealed class PlantHolderSystem : EntitySystem if (!Resolve(uid, ref component)) return; - if (component.Seed != null) - component.Health = MathHelper.Clamp(component.Health, 0, component.Seed.Endurance); + if (component.Seed != null && TryComp(uid, out var traits)) + component.Health = MathHelper.Clamp(component.Health, 0, traits.Endurance); else { component.Health = 0f; @@ -490,8 +510,8 @@ public sealed class PlantHolderSystem : EntitySystem if (component.Seed == null || Deleted(user)) return false; - - if (component.Harvest && !component.Dead) + // Try to get HarvestComponent for harvest system + if (TryComp(plantholder, out var harvestComp) && harvestComp.ReadyForHarvest && !component.Dead) { if (_hands.TryGetActiveItem(user, out var activeItem)) { @@ -525,7 +545,10 @@ public sealed class PlantHolderSystem : EntitySystem /// public bool DoScream(EntityUid plantholder, SeedData? seed = null) { - if (seed == null || seed.CanScream == false) + if (seed == null) + return false; + + if (!TryComp(plantholder, out var traits) || !traits.CanScream) return false; _audio.PlayPvs(seed.ScreamSound, plantholder); @@ -554,8 +577,15 @@ public sealed class PlantHolderSystem : EntitySystem DoScream(uid, component.Seed); - if (component.Seed?.HarvestRepeat == HarvestType.NoRepeat) - RemovePlant(uid, component); + HarvestComponent? harvest = null; + if (TryComp(uid, out harvest)) + { + harvest.ReadyForHarvest = false; + harvest.LastHarvestTime = component.Age; + + if (harvest.HarvestRepeat == HarvestType.NoRepeat) + RemovePlant(uid, component); + } CheckLevelSanity(uid, component); UpdateSprite(uid, component); @@ -595,7 +625,7 @@ public sealed class PlantHolderSystem : EntitySystem // Remove all growth components before planting new seed var growthComponents = EntityManager.GetComponents(uid).ToList(); - foreach(var g in growthComponents) + foreach (var g in growthComponents) EntityManager.RemoveComponent(uid, g); component.YieldMod = 1; @@ -613,30 +643,6 @@ public sealed class PlantHolderSystem : EntitySystem 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)) @@ -702,11 +708,13 @@ public sealed class PlantHolderSystem : EntitySystem if (!TryComp(uid, out var app)) return; + TryComp(uid, out var spriteTraits); + if (component.Seed != null) { if (component.DrawWarnings) { - _appearance.SetData(uid, PlantHolderVisuals.HealthLight, component.Health <= component.Seed.Endurance / 2f); + _appearance.SetData(uid, PlantHolderVisuals.HealthLight, component.Health <= (spriteTraits?.Endurance ?? 100f) / 2f); } if (component.Dead) @@ -719,18 +727,24 @@ public sealed class PlantHolderSystem : EntitySystem _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 = GetCurrentGrowthStage((uid, component)); - - _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); + if (spriteTraits == null) + return; + + if (component.Age < spriteTraits.Maturation) + { + var growthStage = GetCurrentGrowthStage((uid, component)); + + _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-{spriteTraits.GrowthStages}", app); + } } } else @@ -780,6 +794,7 @@ public sealed class PlantHolderSystem : EntitySystem { EnsureComp(uid); EnsureComp(uid); + EnsureComp(uid); EnsureComp(uid); EnsureComp(uid); } diff --git a/Content.Server/Botany/Systems/PlantTraitsSystem.cs b/Content.Server/Botany/Systems/PlantTraitsSystem.cs new file mode 100644 index 00000000000..65bbba3cd09 --- /dev/null +++ b/Content.Server/Botany/Systems/PlantTraitsSystem.cs @@ -0,0 +1,57 @@ +using Content.Server.Botany.Components; +using Robust.Shared.Random; + +namespace Content.Server.Botany.Systems; + +/// +/// System that handles plant traits like lifespan, maturation, production, yield, potency, and growth stages. +/// +public sealed class PlantTraitsSystem : PlantGrowthSystem +{ + [Dependency] private readonly BotanySystem _botany = default!; + [Dependency] private readonly PlantHolderSystem _plantHolder = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); + } + + private void OnPlantGrow(EntityUid uid, PlantTraitsComponent component, OnPlantGrowEvent args) + { + PlantHolderComponent? holder = null; + Resolve(uid, ref holder); + + if (holder == null || holder.Seed == null || holder.Dead) + return; + + // Check if plant is too old + if (holder.Age > component.Lifespan) + { + holder.Health -= _random.Next(3, 5) * HydroponicsSpeedMultiplier; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + + // Check if plant is ready for harvest + if (holder.Seed.ProductPrototypes.Count > 0 && TryComp(uid, out var harvestComp)) + { + if (holder.Age > component.Production) + { + if (holder.Age - harvestComp.LastHarvestTime > component.Production && !harvestComp.ReadyForHarvest) + { + harvestComp.ReadyForHarvest = true; + harvestComp.LastHarvestTime = holder.Age; + } + } + else + { + if (harvestComp.ReadyForHarvest) + { + harvestComp.ReadyForHarvest = false; + harvestComp.LastHarvestTime = holder.Age; + } + } + } + } +} diff --git a/Content.Server/Botany/Systems/SeedExtractorSystem.cs b/Content.Server/Botany/Systems/SeedExtractorSystem.cs index c7e20983a7a..b56dbe10442 100644 --- a/Content.Server/Botany/Systems/SeedExtractorSystem.cs +++ b/Content.Server/Botany/Systems/SeedExtractorSystem.cs @@ -27,7 +27,11 @@ public sealed class SeedExtractorSystem : EntitySystem if (!TryComp(args.Used, out ProduceComponent? produce)) return; - if (!_botanySystem.TryGetSeed(produce, out var seed) || seed.Seedless) + if (!_botanySystem.TryGetSeed(produce, out var seed)) + return; + + var traits = _botanySystem.GetPlantTraits(seed); + if (traits?.Seedless == true) { _popupSystem.PopupCursor(Loc.GetString("seed-extractor-component-no-seeds", ("name", args.Used)), args.User, PopupType.MediumCaution); diff --git a/Content.Server/EntityEffects/EntityEffectSystem.cs b/Content.Server/EntityEffects/EntityEffectSystem.cs index b31a39a1f1f..7cf13ff094d 100644 --- a/Content.Server/EntityEffects/EntityEffectSystem.cs +++ b/Content.Server/EntityEffects/EntityEffectSystem.cs @@ -64,6 +64,7 @@ public sealed class EntityEffectSystem : EntitySystem [Dependency] private readonly MutationSystem _mutation = default!; [Dependency] private readonly NarcolepsySystem _narcolepsy = default!; [Dependency] private readonly PlantHolderSystem _plantHolder = default!; + [Dependency] private readonly BasicGrowthSystem _plantGrowth = default!; [Dependency] private readonly PolymorphSystem _polymorph = default!; [Dependency] private readonly RespiratorSystem _respirator = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; @@ -252,8 +253,13 @@ public sealed class EntityEffectSystem : EntitySystem if (plantHolderComp.Seed == null) return; - _plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp); - plantHolderComp.Seed.Potency = Math.Max(plantHolderComp.Seed.Potency + args.Effect.Amount, 1); + PlantTraitsComponent? traits = null; + Resolve(args.Args.TargetEntity, ref traits); + + if (traits == null) + return; + + traits.Potency = Math.Max(traits.Potency + args.Effect.Amount, 1); } private void OnExecutePlantAdjustToxins(ref ExecuteEntityEffectEvent args) @@ -285,7 +291,7 @@ public sealed class EntityEffectSystem : EntitySystem if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp)) return; - _plantHolder.AffectGrowth(args.Args.TargetEntity, (int) args.Effect.Amount, plantHolderComp); + _plantGrowth.AffectGrowth(args.Args.TargetEntity, (int) args.Effect.Amount, plantHolderComp); } // Mutate reference 'val' between 'min' and 'max' by pretending the value @@ -364,35 +370,39 @@ public sealed class EntityEffectSystem : EntitySystem if (plantHolderComp.Seed == null) return; - var member = plantHolderComp.Seed.GetType().GetField(args.Effect.TargetValue); + // Try to find the field in PlantTraitsComponent first + PlantTraitsComponent? traits = null; + Resolve(args.Args.TargetEntity, ref traits); - if (member == null) + // Fallback to SeedData for fields that haven't been moved to components yet + var seedMember = plantHolderComp.Seed.GetType().GetField(args.Effect.TargetValue); + if (seedMember == 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) + var currentSeedValObj = seedMember.GetValue(plantHolderComp.Seed); + if (currentSeedValObj == null) return; - if (member.FieldType == typeof(float)) + if (seedMember.FieldType == typeof(float)) { - var floatVal = (float)currentValObj; + var floatVal = (float)currentSeedValObj; MutateFloat(ref floatVal, args.Effect.MinValue, args.Effect.MaxValue, args.Effect.Steps); - member.SetValue(plantHolderComp.Seed, floatVal); + seedMember.SetValue(plantHolderComp.Seed, floatVal); } - else if (member.FieldType == typeof(int)) + else if (seedMember.FieldType == typeof(int)) { - var intVal = (int)currentValObj; + var intVal = (int)currentSeedValObj; MutateInt(ref intVal, (int)args.Effect.MinValue, (int)args.Effect.MaxValue, args.Effect.Steps); - member.SetValue(plantHolderComp.Seed, intVal); + seedMember.SetValue(plantHolderComp.Seed, intVal); } - else if (member.FieldType == typeof(bool)) + else if (seedMember.FieldType == typeof(bool)) { - var boolVal = (bool)currentValObj; + var boolVal = (bool)currentSeedValObj; boolVal = !boolVal; - member.SetValue(plantHolderComp.Seed, boolVal); + seedMember.SetValue(plantHolderComp.Seed, boolVal); } } @@ -401,14 +411,21 @@ public sealed class EntityEffectSystem : EntitySystem 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)); + + PlantTraitsComponent? traits = null; + Resolve(args.Args.TargetEntity, ref traits); + + if (traits == null) + return; + + var deviation = 0; + if (plantHolderComp.Age > traits.Maturation) + deviation = (int) Math.Max(traits.Maturation - 1, plantHolderComp.Age - _random.Next(7, 10)); else - deviation = (int) (seed.Maturation / seed.GrowthStages); + deviation = (int) (traits.Maturation / traits.GrowthStages); plantHolderComp.Age -= deviation; plantHolderComp.LastProduce = plantHolderComp.Age; plantHolderComp.SkipAging++; @@ -420,7 +437,10 @@ public sealed class EntityEffectSystem : EntitySystem if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveMutableSeed: true)) return; - if (plantHolderComp.Seed!.Seedless == false) + PlantTraitsComponent? traits = null; + Resolve(args.Args.TargetEntity, ref traits); + + if (traits != null && !traits.Seedless) { _plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp); _popup.PopupEntity( @@ -428,7 +448,7 @@ public sealed class EntityEffectSystem : EntitySystem args.Args.TargetEntity, PopupType.SmallCaution ); - plantHolderComp.Seed.Seedless = true; + traits.Seedless = true; } } @@ -437,16 +457,20 @@ public sealed class EntityEffectSystem : EntitySystem if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveMutableSeed: true)) return; + PlantTraitsComponent? traits = null; + Resolve(args.Args.TargetEntity, ref traits); + + if (traits == null) + return; + if (_random.Prob(0.1f)) { - _plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp); - plantHolderComp.Seed!.Lifespan++; + traits.Lifespan++; } if (_random.Prob(0.1f)) { - _plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp); - plantHolderComp.Seed!.Endurance++; + traits.Endurance++; } } @@ -463,11 +487,14 @@ public sealed class EntityEffectSystem : EntitySystem if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveMutableSeed: true)) return; - if (plantHolderComp.Seed!.Seedless) + PlantTraitsComponent? traits = null; + Resolve(args.Args.TargetEntity, ref traits); + + if (traits != null && traits.Seedless) { _plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp); _popup.PopupEntity(Loc.GetString("botany-plant-seedsrestored"), args.Args.TargetEntity); - plantHolderComp.Seed.Seedless = false; + traits.Seedless = false; } } @@ -479,21 +506,25 @@ public sealed class EntityEffectSystem : EntitySystem 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); + PlantTraitsComponent? traits = null; + Resolve(args.Args.TargetEntity, ref traits); - if (plantHolderComp.Seed.Potency > args.Effect.PotencySeedlessThreshold) + if (traits == null) + return; + + if (traits.Potency < args.Effect.PotencyLimit) + { + traits.Potency = Math.Min(traits.Potency + args.Effect.PotencyIncrease, args.Effect.PotencyLimit); + + if (traits.Potency > args.Effect.PotencySeedlessThreshold) { - plantHolderComp.Seed.Seedless = true; + traits.Seedless = true; } } - else if (plantHolderComp.Seed.Yield > 1 && _random.Prob(0.1f)) + else if (traits.Yield > 1 && _random.Prob(0.1f)) { // Too much of a good thing reduces yield - _plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp); - plantHolderComp.Seed.Yield--; + traits.Yield--; } } @@ -932,16 +963,14 @@ public sealed class EntityEffectSystem : EntitySystem plantholder.Seed == null) return; - // Clone the seed to make it mutable - var clonedSeed = plantholder.Seed.Clone(); + // Get or create the harvest component + var harvestComponent = EnsureComp(args.Args.TargetEntity); - if (clonedSeed.HarvestRepeat == HarvestType.NoRepeat) - clonedSeed.HarvestRepeat = HarvestType.Repeat; - else if (clonedSeed.HarvestRepeat == HarvestType.Repeat) - clonedSeed.HarvestRepeat = HarvestType.SelfHarvest; - - // Update the plant holder with the cloned seed - plantholder.Seed = clonedSeed; + // Mutate the harvest type + if (harvestComponent.HarvestRepeat == HarvestType.NoRepeat) + harvestComponent.HarvestRepeat = HarvestType.Repeat; + else if (harvestComponent.HarvestRepeat == HarvestType.Repeat) + harvestComponent.HarvestRepeat = HarvestType.SelfHarvest; } private void OnExecutePlantSpeciesChange(ref ExecuteEntityEffectEvent args) diff --git a/Resources/Maps/Salvage/vegan-meatball.yml b/Resources/Maps/Salvage/vegan-meatball.yml index 1d59c91e8a5..1d6e29ac3b3 100644 --- a/Resources/Maps/Salvage/vegan-meatball.yml +++ b/Resources/Maps/Salvage/vegan-meatball.yml @@ -485,9 +485,10 @@ entities: age: 1 health: 100 seed: - lifespan: 75 - endurance: 100 growthComponents: + - !type:PlantTraitsComponent + lifespan: 75 + endurance: 100 - !type:BasicGrowthComponent waterConsumption: 0.4 nutrientConsumption: 0.75 @@ -557,9 +558,10 @@ entities: age: 1 health: 100 seed: - lifespan: 25 - endurance: 100 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + endurance: 100 - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.75 diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index 2a63340d5fb..73e7fe14373 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -9,12 +9,13 @@ - WheatBushel mutationPrototypes: - meatwheat - lifespan: 25 - maturation: 6 - production: 3 - yield: 3 - potency: 5 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 6 + production: 3 + yield: 3 + potency: 5 - !type:BasicGrowthComponent nutrientConsumption: 0.4 chemicals: @@ -36,12 +37,13 @@ packetPrototype: MeatwheatSeeds productPrototypes: - MeatwheatBushel - lifespan: 25 - maturation: 6 - production: 3 - yield: 3 - potency: 5 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 6 + production: 3 + yield: 3 + potency: 5 - !type:BasicGrowthComponent nutrientConsumption: 0.4 chemicals: @@ -63,12 +65,13 @@ packetPrototype: OatSeeds productPrototypes: - OatBushel - lifespan: 25 - maturation: 6 - production: 3 - yield: 3 - potency: 5 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 6 + production: 3 + yield: 3 + potency: 5 - !type:BasicGrowthComponent nutrientConsumption: 0.4 chemicals: @@ -92,12 +95,14 @@ - FoodBanana mutationPrototypes: - mimana - harvestRepeat: Repeat - lifespan: 50 - maturation: 6 - production: 6 - yield: 2 growthComponents: + - !type:PlantTraitsComponent + lifespan: 50 + maturation: 6 + production: 6 + yield: 2 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent waterConsumption: 0.6 - !type:AtmosphericGrowthComponent @@ -121,12 +126,14 @@ packetPrototype: MimanaSeeds productPrototypes: - FoodMimana - harvestRepeat: Repeat - lifespan: 50 - maturation: 6 - production: 6 - yield: 2 growthComponents: + - !type:PlantTraitsComponent + lifespan: 50 + maturation: 6 + production: 6 + yield: 2 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent waterConsumption: 0.6 - !type:AtmosphericGrowthComponent @@ -150,13 +157,14 @@ packetPrototype: CarrotSeeds productPrototypes: - FoodCarrot - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 3 - !type:BasicGrowthComponent waterConsumption: 0.6 chemicals: @@ -184,14 +192,16 @@ - FoodLaughinPeaPod mutationPrototypes: - worldPea - lifespan: 25 - growthStages: 3 - maturation: 7 - production: 5 - yield: 3 - potency: 20 - harvestRepeat: Repeat growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + growthStages: 3 + maturation: 7 + production: 5 + yield: 3 + potency: 20 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.6 @@ -222,12 +232,15 @@ - lemoon - lime - orange - harvestRepeat: Repeat - lifespan: 55 - maturation: 6 - production: 6 - yield: 3 - potency: 10 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + - !type:HarvestComponent + harvestRepeat: Repeat chemicals: Nutriment: Min: 1 @@ -245,14 +258,17 @@ displayName: seeds-lemoon-display-name plantRsi: Objects/Specific/Hydroponics/lemoon.rsi packetPrototype: LemoonSeeds + growthComponents: + - !type:PlantTraitsComponent + lifespan: 90 + maturation: 8 + production: 6 + yield: 4 + potency: 1 + - !type:HarvestComponent + harvestRepeat: Repeat productPrototypes: - FoodLemoon - harvestRepeat: Repeat - lifespan: 90 - maturation: 8 - production: 6 - yield: 4 - potency: 1 chemicals: Vitamin: Min: 1 @@ -275,12 +291,15 @@ mutationPrototypes: - orange - lemon - harvestRepeat: Repeat - lifespan: 55 - maturation: 6 - production: 6 - yield: 3 - potency: 10 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + - !type:HarvestComponent + harvestRepeat: Repeat chemicals: Nutriment: Min: 1 @@ -304,12 +323,15 @@ - extradimensionalOrange - lemon - lime - harvestRepeat: Repeat - lifespan: 55 - maturation: 6 - production: 6 - yield: 3 - potency: 10 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + - !type:HarvestComponent + harvestRepeat: Repeat chemicals: Nutriment: Min: 1 @@ -329,12 +351,15 @@ packetPrototype: ExtradimensionalOrangeSeeds productPrototypes: - FoodExtradimensionalOrange - harvestRepeat: Repeat - lifespan: 55 - maturation: 6 - production: 6 - yield: 3 - potency: 10 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + - !type:HarvestComponent + harvestRepeat: Repeat chemicals: Haloperidol: Min: 1 @@ -358,13 +383,16 @@ packetPrototype: PineappleSeeds productPrototypes: - FoodPineapple - harvestRepeat: Repeat - lifespan: 55 - maturation: 6 - production: 6 - yield: 3 - potency: 10 - growthStages: 3 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + growthStages: 3 + - !type:HarvestComponent + harvestRepeat: Repeat chemicals: Nutriment: Min: 1 @@ -388,13 +416,14 @@ packetPrototype: PotatoSeeds productPrototypes: - FoodPotato - lifespan: 30 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 4 growthComponents: + - !type:PlantTraitsComponent + lifespan: 30 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 4 - !type:BasicGrowthComponent waterConsumption: 0.6 chemicals: @@ -418,14 +447,16 @@ - Sugarcane mutationPrototypes: - papercane - harvestRepeat: Repeat - lifespan: 60 - maturation: 6 - production: 6 - yield: 3 - potency: 10 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 60 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + growthStages: 3 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -443,14 +474,16 @@ packetPrototype: TeaPlantSeeds productPrototypes: - LeavesTea - harvestRepeat: Repeat - lifespan: 75 - maturation: 5 - production: 3 - yield: 2 - potency: 20 - growthStages: 5 growthComponents: + - !type:PlantTraitsComponent + lifespan: 75 + maturation: 5 + production: 3 + yield: 2 + potency: 20 + growthStages: 5 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent waterConsumption: 0.6 - !type:AtmosphericGrowthComponent @@ -470,14 +503,16 @@ packetPrototype: PapercaneSeeds productPrototypes: - Papercane - harvestRepeat: Repeat - lifespan: 60 - maturation: 6 - production: 6 - yield: 3 - potency: 10 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 60 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + growthStages: 3 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:AtmosphericGrowthComponent idealHeat: 298 @@ -492,14 +527,15 @@ - Log mutationPrototypes: - steelcap - lifespan: 80 - maturation: 15 ligneous: true - production: 3 - yield: 5 - potency: 1 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 80 + maturation: 15 + production: 3 + yield: 5 + potency: 1 + growthStages: 3 - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.5 @@ -515,14 +551,15 @@ packetPrototype: SteelcapSeeds productPrototypes: - SteelLog - lifespan: 80 - maturation: 15 ligneous: true - production: 3 - yield: 3 - potency: 1 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 80 + maturation: 15 + production: 3 + yield: 3 + potency: 1 + growthStages: 3 - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.8 @@ -541,13 +578,15 @@ mutationPrototypes: - blueTomato - bloodTomato - harvestRepeat: Repeat - lifespan: 25 - maturation: 8 - production: 6 - yield: 2 - potency: 10 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 8 + production: 6 + yield: 2 + potency: 10 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.4 @@ -576,13 +615,15 @@ packetPrototype: BlueTomatoSeeds productPrototypes: - FoodBlueTomato - harvestRepeat: Repeat - lifespan: 25 - maturation: 8 - production: 6 - yield: 2 - potency: 10 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 8 + production: 6 + yield: 2 + potency: 10 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.7 @@ -613,13 +654,15 @@ - FoodBloodTomato mutationPrototypes: - killerTomato - harvestRepeat: Repeat - lifespan: 60 - maturation: 8 - production: 6 - yield: 2 - potency: 10 growthComponents: + - !type:PlantTraitsComponent + lifespan: 60 + maturation: 8 + production: 6 + yield: 2 + potency: 10 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.7 @@ -646,14 +689,16 @@ packetPrototype: KillerTomatoSeeds productPrototypes: - MobTomatoKiller - harvestRepeat: Repeat - lifespan: 25 - maturation: 15 - production: 6 - yield: 2 - potency: 10 - growthStages: 2 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 15 + production: 6 + yield: 2 + potency: 10 + growthStages: 2 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.7 @@ -680,14 +725,16 @@ - FoodEggplant mutationPrototypes: - eggy - harvestRepeat: Repeat - lifespan: 25 - maturation: 6 - production: 6 - yield: 2 - potency: 20 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 6 + production: 6 + yield: 2 + potency: 20 + growthStages: 3 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -709,12 +756,14 @@ packetPrototype: CabbageSeeds productPrototypes: - FoodCabbage - lifespan: 50 - maturation: 7 - production: 5 - yield: 3 - potency: 10 - growthStages: 1 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 50 + maturation: 7 + production: 5 + yield: 3 + potency: 10 + growthStages: 1 chemicals: Nutriment: Min: 1 @@ -734,12 +783,14 @@ packetPrototype: GarlicSeeds productPrototypes: - FoodGarlic - lifespan: 25 - maturation: 8 - production: 5 - yield: 3 - potency: 25 - growthStages: 3 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 8 + production: 5 + yield: 3 + potency: 25 + growthStages: 3 chemicals: Nutriment: Min: 1 @@ -765,12 +816,15 @@ - FoodApple mutationPrototypes: - goldenApple - harvestRepeat: Repeat - lifespan: 55 - maturation: 6 - production: 6 - yield: 3 - potency: 10 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + - !type:HarvestComponent + harvestRepeat: Repeat chemicals: Nutriment: Min: 1 @@ -790,13 +844,15 @@ packetPrototype: GoldenAppleSeeds productPrototypes: - FoodGoldenApple - harvestRepeat: Repeat - lifespan: 55 - maturation: 6 - production: 6 - yield: 3 - potency: 10 growthComponents: + - !type:PlantTraitsComponent + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent waterConsumption: 0.75 chemicals: @@ -822,13 +878,14 @@ packetPrototype: CornSeeds productPrototypes: - FoodCorn - lifespan: 25 - maturation: 8 - production: 6 - yield: 2 - potency: 20 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 8 + production: 6 + yield: 2 + potency: 20 + growthStages: 3 - !type:BasicGrowthComponent waterConsumption: 0.6 - !type:AtmosphericGrowthComponent @@ -854,13 +911,14 @@ - FoodOnion mutationPrototypes: - onionred - lifespan: 25 - maturation: 8 - production: 6 - yield: 2 - potency: 20 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 8 + production: 6 + yield: 2 + potency: 20 + growthStages: 3 - !type:BasicGrowthComponent waterConsumption: 0.6 - !type:AtmosphericGrowthComponent @@ -888,13 +946,14 @@ packetPrototype: OnionRedSeeds productPrototypes: - FoodOnionRed - lifespan: 25 - maturation: 8 - production: 6 - yield: 2 - potency: 20 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 8 + production: 6 + yield: 2 + potency: 20 + growthStages: 3 - !type:BasicGrowthComponent waterConsumption: 0.6 - !type:AtmosphericGrowthComponent @@ -922,13 +981,14 @@ packetPrototype: ChanterelleSeeds productPrototypes: - FoodMushroom - lifespan: 35 - maturation: 10 - production: 7 - yield: 5 - potency: 1 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 35 + maturation: 10 + production: 7 + yield: 5 + potency: 1 + growthStages: 3 - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.5 @@ -949,13 +1009,15 @@ packetPrototype: EggySeeds productPrototypes: - FoodEgg - harvestRepeat: Repeat - lifespan: 75 - maturation: 6 - production: 12 - yield: 2 - potency: 20 growthComponents: + - !type:PlantTraitsComponent + lifespan: 75 + maturation: 6 + production: 12 + yield: 2 + potency: 20 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent nutrientConsumption: 0.5 - !type:AtmosphericGrowthComponent @@ -977,14 +1039,16 @@ - LeavesCannabis mutationPrototypes: - rainbowCannabis - harvestRepeat: Repeat - lifespan: 75 - maturation: 8 - production: 12 - yield: 2 - potency: 20 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 75 + maturation: 8 + production: 12 + yield: 2 + potency: 20 + growthStages: 3 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent waterConsumption: 0.4 - !type:AtmosphericGrowthComponent @@ -1004,14 +1068,16 @@ packetPrototype: RainbowCannabisSeeds productPrototypes: - LeavesCannabisRainbow - harvestRepeat: Repeat - lifespan: 75 - maturation: 8 - production: 12 - yield: 2 - potency: 20 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 75 + maturation: 8 + production: 12 + yield: 2 + potency: 20 + growthStages: 3 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent waterConsumption: 0.4 - !type:AtmosphericGrowthComponent @@ -1051,14 +1117,16 @@ packetPrototype: TobaccoSeeds productPrototypes: - LeavesTobacco - harvestRepeat: Repeat - lifespan: 75 - maturation: 5 - production: 5 - yield: 2 - potency: 20 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 75 + maturation: 5 + production: 5 + yield: 2 + potency: 20 + growthStages: 3 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent waterConsumption: 0.4 - !type:AtmosphericGrowthComponent @@ -1080,13 +1148,14 @@ - Nettle mutationPrototypes: - deathNettle - lifespan: 25 - maturation: 8 - production: 6 - yield: 2 - potency: 20 - growthStages: 5 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 8 + production: 6 + yield: 2 + potency: 20 + growthStages: 5 - !type:BasicGrowthComponent waterConsumption: 0.6 - !type:AtmosphericGrowthComponent @@ -1108,13 +1177,14 @@ harvestLogImpact: High productPrototypes: - DeathNettle - lifespan: 25 - maturation: 8 - production: 6 - yield: 2 - potency: 20 - growthStages: 5 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 8 + production: 6 + yield: 2 + potency: 20 + growthStages: 5 - !type:BasicGrowthComponent waterConsumption: 0.7 nutrientConsumption: 0.8 @@ -1141,13 +1211,15 @@ - FoodChiliPepper mutationPrototypes: - chilly - harvestRepeat: Repeat - lifespan: 25 - maturation: 6 - production: 6 - yield: 2 - potency: 20 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 6 + production: 6 + yield: 2 + potency: 20 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -1173,13 +1245,15 @@ packetPrototype: ChillySeeds productPrototypes: - FoodChillyPepper - harvestRepeat: Repeat - lifespan: 25 - maturation: 6 - production: 6 - yield: 2 - potency: 20 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 6 + production: 6 + yield: 2 + potency: 20 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:AtmosphericGrowthComponent idealHeat: 298 chemicals: @@ -1207,13 +1281,14 @@ - FoodPoppy mutationPrototypes: - lily - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 3 - !type:BasicGrowthComponent waterConsumption: 0.6 chemicals: @@ -1235,13 +1310,14 @@ packetPrototype: AloeSeeds productPrototypes: - FoodAloe - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 5 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 5 - !type:BasicGrowthComponent waterConsumption: 0.6 chemicals: @@ -1265,13 +1341,14 @@ - FoodLily mutationPrototypes: - spacemansTrumpet - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 3 - !type:BasicGrowthComponent waterConsumption: 0.6 chemicals: @@ -1293,13 +1370,14 @@ packetPrototype: LingzhiSeeds productPrototypes: - FoodLingzhi - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 3 - !type:BasicGrowthComponent waterConsumption: 0.6 chemicals: @@ -1323,13 +1401,14 @@ - FoodAmbrosiaVulgaris mutationPrototypes: - ambrosiaDeus - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 6 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 6 - !type:BasicGrowthComponent waterConsumption: 0.6 chemicals: @@ -1363,13 +1442,14 @@ packetPrototype: AmbrosiaDeusSeeds productPrototypes: - FoodAmbrosiaDeus - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 6 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 6 - !type:BasicGrowthComponent waterConsumption: 0.6 chemicals: @@ -1401,13 +1481,14 @@ - FoodGalaxythistle mutationPrototypes: - glasstle - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 3 - !type:BasicGrowthComponent waterConsumption: 0.6 chemicals: @@ -1425,12 +1506,14 @@ packetPrototype: GlasstleSeeds productPrototypes: - FoodGlasstle - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 3 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 3 chemicals: Razorium: Min: 1 @@ -1446,13 +1529,14 @@ packetPrototype: FlyAmanitaSeeds productPrototypes: - FoodFlyAmanita - lifespan: 25 - maturation: 12 - production: 3 - yield: 3 - potency: 10 - growthStages: 2 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 12 + production: 3 + yield: 3 + potency: 10 + growthStages: 2 - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.5 @@ -1478,12 +1562,14 @@ mutationPrototypes: - fakeCapfruit - realCapfruit - lifespan: 65 - maturation: 25 - production: 25 - yield: 1 - potency: 10 - growthStages: 2 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 65 + maturation: 25 + production: 25 + yield: 1 + potency: 10 + growthStages: 2 chemicals: Nutriment: Min: 1 @@ -1503,12 +1589,14 @@ packetPrototype: FakeCapfruitSeeds productPrototypes: - FoodFakeCapfruit - lifespan: 65 - maturation: 25 - production: 25 - yield: 1 - potency: 10 - growthStages: 2 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 65 + maturation: 25 + production: 25 + yield: 1 + potency: 10 + growthStages: 2 chemicals: Nutriment: Min: 1 @@ -1528,12 +1616,14 @@ packetPrototype: RealCapfruitSeeds productPrototypes: - FoodRealCapfruit - lifespan: 65 - maturation: 25 - production: 25 - yield: 1 - potency: 10 - growthStages: 2 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 65 + maturation: 25 + production: 25 + yield: 1 + potency: 10 + growthStages: 2 chemicals: Nutriment: Min: 1 @@ -1553,13 +1643,14 @@ packetPrototype: RiceSeeds productPrototypes: - RiceBushel - lifespan: 25 - maturation: 6 - production: 3 - yield: 3 - potency: 5 - growthStages: 4 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 6 + production: 3 + yield: 3 + potency: 5 + growthStages: 4 - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.4 @@ -1584,13 +1675,14 @@ - FoodSoybeans mutationPrototypes: - koibean - growthStages: 4 - lifespan: 25 - maturation: 6 - production: 6 - yield: 3 - potency: 5 growthComponents: + - !type:PlantTraitsComponent + growthStages: 4 + lifespan: 25 + maturation: 6 + production: 6 + yield: 3 + potency: 5 - !type:BasicGrowthComponent nutrientConsumption: 0.4 chemicals: @@ -1608,13 +1700,14 @@ packetPrototype: SpacemansTrumpetSeeds productPrototypes: - FoodSpacemansTrumpet - growthStages: 4 - lifespan: 20 - maturation: 14 - production: 3 - yield: 2 - potency: 10 growthComponents: + - !type:PlantTraitsComponent + growthStages: 4 + lifespan: 20 + maturation: 14 + production: 3 + yield: 2 + potency: 10 - !type:BasicGrowthComponent waterConsumption: 0.6 chemicals: @@ -1636,13 +1729,14 @@ packetPrototype: KoibeanSeeds productPrototypes: - FoodKoibean - growthStages: 4 - lifespan: 25 - maturation: 6 - production: 6 - yield: 3 - potency: 5 growthComponents: + - !type:PlantTraitsComponent + growthStages: 4 + lifespan: 25 + maturation: 6 + production: 6 + yield: 3 + potency: 5 - !type:BasicGrowthComponent nutrientConsumption: 0.4 chemicals: @@ -1664,12 +1758,14 @@ packetPrototype: GrapeSeeds productPrototypes: - FoodGrape - lifespan: 50 - maturation: 6 - production: 5 - yield: 3 - potency: 10 - growthStages: 2 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 50 + maturation: 6 + production: 5 + yield: 3 + potency: 10 + growthStages: 2 chemicals: Nutriment: Min: 1 @@ -1691,11 +1787,13 @@ - FoodWatermelon mutationPrototypes: - holymelon - lifespan: 55 - maturation: 12 - production: 3 - yield: 1 - potency: 1 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 55 + maturation: 12 + production: 3 + yield: 1 + potency: 1 chemicals: Nutriment: Min: 1 @@ -1719,11 +1817,13 @@ packetPrototype: HolymelonSeeds productPrototypes: - FoodHolymelon - lifespan: 55 - maturation: 12 - production: 3 - yield: 1 - potency: 1 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 55 + maturation: 12 + production: 3 + yield: 1 + potency: 1 chemicals: Nutriment: Min: 1 @@ -1747,12 +1847,14 @@ packetPrototype: CocoaSeeds productPrototypes: - FoodCocoaPod - harvestRepeat: Repeat - lifespan: 50 - maturation: 6 - production: 6 - yield: 6 growthComponents: + - !type:PlantTraitsComponent + lifespan: 50 + maturation: 6 + production: 6 + yield: 6 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent waterConsumption: 1.0 nutrientConsumption: 0.8 @@ -1777,12 +1879,14 @@ packetPrototype: BerrySeeds productPrototypes: - FoodBerries - harvestRepeat: Repeat - lifespan: 50 - maturation: 6 - production: 6 - yield: 4 growthComponents: + - !type:PlantTraitsComponent + lifespan: 50 + maturation: 6 + production: 6 + yield: 4 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent nutrientConsumption: 0.6 chemicals: @@ -1804,14 +1908,16 @@ packetPrototype: BungoSeeds productPrototypes: - FoodBungo - harvestRepeat: Repeat - lifespan: 50 - maturation: 8 - production: 6 - potency: 10 - yield: 3 - growthStages: 4 growthComponents: + - !type:PlantTraitsComponent + lifespan: 50 + maturation: 8 + production: 6 + potency: 10 + yield: 3 + growthStages: 4 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent waterConsumption: 0.6 - !type:AtmosphericGrowthComponent @@ -1837,14 +1943,16 @@ - FoodPeaPod mutationPrototypes: - laughinPea - lifespan: 25 - growthStages: 3 - maturation: 8 - production: 6 - yield: 3 - potency: 25 - harvestRepeat: Repeat growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + growthStages: 3 + maturation: 8 + production: 6 + yield: 3 + potency: 25 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent nutrientConsumption: 0.5 chemicals: @@ -1866,14 +1974,16 @@ packetPrototype: PeaSeeds productPrototypes: - FoodWorldPeas - lifespan: 25 - growthStages: 3 - maturation: 20 - production: 6 - yield: 3 - potency: 25 - harvestRepeat: Repeat growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + growthStages: 3 + maturation: 20 + production: 6 + yield: 3 + potency: 25 + - !type:HarvestComponent + harvestRepeat: Repeat - !type:BasicGrowthComponent nutrientConsumption: 0.5 chemicals: @@ -1901,13 +2011,14 @@ - FoodPumpkin mutationPrototypes: - bluePumpkin - lifespan: 55 - maturation: 10 - production: 4 - yield: 2 - potency: 10 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 55 + maturation: 10 + production: 4 + yield: 2 + potency: 10 + growthStages: 3 - !type:AtmosphericGrowthComponent idealHeat: 288 chemicals: @@ -1929,13 +2040,14 @@ packetPrototype: BluePumpkinSeeds productPrototypes: - FoodBluePumpkin - lifespan: 55 - maturation: 10 - production: 4 - yield: 2 - potency: 10 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 55 + maturation: 10 + production: 4 + yield: 2 + potency: 10 + growthStages: 3 - !type:AtmosphericGrowthComponent idealHeat: 288 chemicals: @@ -1963,13 +2075,14 @@ - CottonBol mutationPrototypes: - pyrotton - lifespan: 25 - maturation: 8 - production: 3 - yield: 3 - potency: 5 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 8 + production: 3 + yield: 3 + potency: 5 + growthStages: 3 - !type:BasicGrowthComponent waterConsumption: 0.6 chemicals: @@ -1987,13 +2100,14 @@ packetPrototype: PyrottonSeeds productPrototypes: - PyrottonBol - lifespan: 25 - maturation: 8 - production: 3 - yield: 2 - potency: 5 - growthStages: 3 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 8 + production: 3 + yield: 2 + potency: 5 + growthStages: 3 - !type:BasicGrowthComponent waterConsumption: 0.8 chemicals: @@ -2015,12 +2129,15 @@ packetPrototype: CherrySeeds productPrototypes: - FoodCherry - harvestRepeat: Repeat - lifespan: 55 - maturation: 6 - production: 6 - yield: 5 - potency: 10 + growthComponents: + - !type:PlantTraitsComponent + lifespan: 55 + maturation: 6 + production: 6 + yield: 5 + potency: 10 + - !type:HarvestComponent + harvestRepeat: Repeat chemicals: Nutriment: Min: 1 @@ -2040,13 +2157,14 @@ packetPrototype: AnomalyBerrySeeds productPrototypes: - FoodAnomalyBerry - lifespan: 25 - maturation: 12 - production: 3 - yield: 3 - potency: 10 - growthStages: 2 growthComponents: + - !type:PlantTraitsComponent + lifespan: 25 + maturation: 12 + production: 3 + yield: 3 + potency: 10 + growthStages: 2 - !type:BasicGrowthComponent waterConsumption: 0.6 nutrientConsumption: 0.5 From 0f8c33584113e9b347a0cdfebd7d0b1188b0ff14 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Sat, 2 Aug 2025 20:38:28 +0300 Subject: [PATCH 21/53] fix error --- .../EntityEffects/Effects/PlantMutateGases.cs | 81 ------------------- .../EntityEffects/Effects/PlantMutateGases.cs | 23 +++++- Resources/Prototypes/Hydroponics/seeds.yml | 32 ++++---- 3 files changed, 35 insertions(+), 101 deletions(-) delete mode 100644 Content.Server/EntityEffects/Effects/PlantMutateGases.cs diff --git a/Content.Server/EntityEffects/Effects/PlantMutateGases.cs b/Content.Server/EntityEffects/Effects/PlantMutateGases.cs deleted file mode 100644 index c47105b1e17..00000000000 --- a/Content.Server/EntityEffects/Effects/PlantMutateGases.cs +++ /dev/null @@ -1,81 +0,0 @@ -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; - -/// -/// changes the gases that a plant or produce create. -/// -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 gasses = args.EntityManager.EnsureComponent(args.TargetEntity); - var random = IoCManager.Resolve(); - - // 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().ToList()); - if (gasses.ExudeGasses.ContainsKey(gas)) - { - gasses.ExudeGasses[gas] += amount; - } - else - { - gasses.ExudeGasses.Add(gas, amount); - } - } - - protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) - { - return Loc.GetString("reagent-effect-guidebook-plant-mutate-exude-gasses", - ("minValue", MinValue), - ("maxValue", MaxValue)); - } -} - -/// -/// changes the gases that a plant or produce consumes. -/// -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 gasses = args.EntityManager.GetComponent(args.TargetEntity); - var random = IoCManager.Resolve(); - - // 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().ToList()); - if (gasses.ConsumeGasses.ContainsKey(gas)) - { - gasses.ConsumeGasses[gas] += amount; - } - else - { - gasses.ConsumeGasses.Add(gas, amount); - } - } - - protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) - { - return Loc.GetString("reagent-effect-guidebook-plant-mutate-consume-gasses", - ("minValue", MinValue), - ("maxValue", MaxValue)); - } -} diff --git a/Content.Shared/EntityEffects/Effects/PlantMutateGases.cs b/Content.Shared/EntityEffects/Effects/PlantMutateGases.cs index 5eb5b1d07cf..7f646b608c0 100644 --- a/Content.Shared/EntityEffects/Effects/PlantMutateGases.cs +++ b/Content.Shared/EntityEffects/Effects/PlantMutateGases.cs @@ -1,3 +1,4 @@ +using Robust.Shared.Localization; using Robust.Shared.Prototypes; using Robust.Shared.Random; using System.Linq; @@ -7,7 +8,7 @@ namespace Content.Shared.EntityEffects.Effects; /// /// changes the gases that a plant or produce create. /// -public sealed partial class PlantMutateExudeGasses : EventEntityEffect +public sealed partial class PlantMutateExudeGasses : EntityEffect { [DataField] public float MinValue = 0.01f; @@ -15,16 +16,23 @@ public sealed partial class PlantMutateExudeGasses : EventEntityEffect /// changes the gases that a plant or produce consumes. /// -public sealed partial class PlantMutateConsumeGasses : EventEntityEffect +public sealed partial class PlantMutateConsumeGasses : EntityEffect { [DataField] public float MinValue = 0.01f; @@ -32,8 +40,15 @@ public sealed partial class PlantMutateConsumeGasses : EventEntityEffect Date: Sat, 2 Aug 2025 21:32:13 +0300 Subject: [PATCH 22/53] fix error 2 --- Content.Server/Botany/Systems/BotanySystem.Seed.cs | 8 ++++++-- Content.Server/Botany/Systems/PlantHolderSystem.cs | 4 ++-- Resources/Prototypes/Hydroponics/seeds.yml | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index 79f30fc27a2..33d9a239ee9 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -145,7 +145,7 @@ public sealed partial class BotanySystem : EntitySystem return seed; } - public IEnumerable AutoHarvest(SeedData proto, EntityCoordinates position, int yieldMod = 1) + public IEnumerable AutoHarvest(SeedData proto, EntityCoordinates position, EntityUid? plantEntity = null) { if (position.IsValid(EntityManager) && proto.ProductPrototypes.Count > 0) @@ -153,13 +153,15 @@ public sealed partial class BotanySystem : EntitySystem if (proto.HarvestLogImpact != null) _adminLogger.Add(LogType.Botany, proto.HarvestLogImpact.Value, $"Auto-harvested {Loc.GetString(proto.Name):seed} at Pos:{position}."); + var yieldMod = Comp(plantEntity!.Value).YieldMod; + return GenerateProduct(proto, position, yieldMod); } return Enumerable.Empty(); } - public IEnumerable Harvest(SeedData proto, EntityUid user, int yieldMod = 1) + public IEnumerable Harvest(SeedData proto, EntityUid user, EntityUid? plantEntity = null) { var traits = GetPlantTraits(proto); if (traits == null || proto.ProductPrototypes.Count == 0 || traits.Yield <= 0) @@ -174,6 +176,8 @@ public sealed partial class BotanySystem : EntitySystem if (proto.HarvestLogImpact != null) _adminLogger.Add(LogType.Botany, proto.HarvestLogImpact.Value, $"{ToPrettyString(user):player} harvested {Loc.GetString(proto.Name):seed} at Pos:{Transform(user).Coordinates}."); + var yieldMod = Comp(plantEntity!.Value).YieldMod; + return GenerateProduct(proto, Transform(user).Coordinates, yieldMod); } diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 0ae4941c8a4..aea2a2bd659 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -526,7 +526,7 @@ public sealed class PlantHolderSystem : EntitySystem return false; } - _botany.Harvest(component.Seed, user, component.YieldMod); + _botany.Harvest(component.Seed, user, plantholder); AfterHarvest(plantholder, component); return true; } @@ -563,7 +563,7 @@ public sealed class PlantHolderSystem : EntitySystem if (component.Seed == null || !component.Harvest) return; - _botany.AutoHarvest(component.Seed, Transform(uid).Coordinates); + _botany.AutoHarvest(component.Seed, Transform(uid).Coordinates, uid); AfterHarvest(uid, component); } diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index b294a5110b0..8a2ce37c491 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -527,9 +527,9 @@ - Log mutationPrototypes: - steelcap - ligneous: true growthComponents: - !type:PlantTraitsComponent + ligneous: true lifespan: 80 maturation: 15 production: 3 @@ -551,9 +551,9 @@ packetPrototype: SteelcapSeeds productPrototypes: - SteelLog - ligneous: true growthComponents: - !type:PlantTraitsComponent + ligneous: true lifespan: 80 maturation: 15 production: 3 From 17bfd0f5fb968d75344e130b00050d49d68735d5 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Sat, 2 Aug 2025 22:44:36 +0300 Subject: [PATCH 23/53] Copy growth components --- .../Botany/Systems/BotanySystem.Seed.cs | 12 +++--- .../Botany/Systems/PlantHolderSystem.cs | 38 +++++++++++++------ 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index 33d9a239ee9..b01b1fcd2f2 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -153,9 +153,7 @@ public sealed partial class BotanySystem : EntitySystem if (proto.HarvestLogImpact != null) _adminLogger.Add(LogType.Botany, proto.HarvestLogImpact.Value, $"Auto-harvested {Loc.GetString(proto.Name):seed} at Pos:{position}."); - var yieldMod = Comp(plantEntity!.Value).YieldMod; - - return GenerateProduct(proto, position, yieldMod); + return GenerateProduct(proto, position, plantEntity); } return Enumerable.Empty(); @@ -176,17 +174,16 @@ public sealed partial class BotanySystem : EntitySystem if (proto.HarvestLogImpact != null) _adminLogger.Add(LogType.Botany, proto.HarvestLogImpact.Value, $"{ToPrettyString(user):player} harvested {Loc.GetString(proto.Name):seed} at Pos:{Transform(user).Coordinates}."); - var yieldMod = Comp(plantEntity!.Value).YieldMod; - - return GenerateProduct(proto, Transform(user).Coordinates, yieldMod); + return GenerateProduct(proto, Transform(user).Coordinates, plantEntity); } - public IEnumerable GenerateProduct(SeedData proto, EntityCoordinates position, int yieldMod = 1) + public IEnumerable GenerateProduct(SeedData proto, EntityCoordinates position, EntityUid? plantEntity = null) { var traits = GetPlantTraits(proto); if (traits == null) return Enumerable.Empty(); + var yieldMod = Comp(plantEntity!.Value).YieldMod; var totalYield = 0; if (traits.Yield > -1) @@ -215,6 +212,7 @@ public sealed partial class BotanySystem : EntitySystem var produce = EnsureComp(entity); produce.Seed = proto.Clone(); + ProduceGrown(entity, produce); _appearance.SetData(entity, ProduceVisuals.Potency, traits.Potency); diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index aea2a2bd659..1ef3cb99fd2 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -307,7 +307,7 @@ public sealed class PlantHolderSystem : EntitySystem return; } - component.Health -= (_random.Next(3, 5) * 10); + component.Health -= _random.Next(3, 5) * 10; float? healthOverride; if (component.Harvest) @@ -319,20 +319,33 @@ public sealed class PlantHolderSystem : EntitySystem healthOverride = component.Health; } var packetSeed = component.Seed; - var seed = _botany.SpawnSeedPacket(packetSeed, Transform(args.User).Coordinates, args.User, healthOverride); - _randomHelper.RandomOffset(seed, 0.25f); - var displayName = Loc.GetString(component.Seed.DisplayName); - _popup.PopupCursor(Loc.GetString("plant-holder-component-take-sample-message", - ("seedName", displayName)), args.User); - DoScream(entity.Owner, component.Seed); + if (packetSeed != null) + { + // Copy growth components from the plant to the seed before creating seed packet + var plantGrowthComponents = EntityManager.GetComponents(uid).ToList(); + packetSeed.GrowthComponents?.Clear(); + foreach (var growthComponent in plantGrowthComponents) + { + var newComponent = growthComponent.DupeComponent(); + packetSeed.GrowthComponents?.Add(newComponent); + } - if (_random.Prob(0.3f)) - component.Sampled = true; + var seed = _botany.SpawnSeedPacket(packetSeed, Transform(args.User).Coordinates, args.User, healthOverride); + _randomHelper.RandomOffset(seed, 0.25f); + var displayName = Loc.GetString(component.Seed.DisplayName); + _popup.PopupCursor(Loc.GetString("plant-holder-component-take-sample-message", + ("seedName", displayName)), args.User); - // Just in case. - CheckLevelSanity(uid, component); - ForceUpdateByExternalCause(uid, component); + DoScream(entity.Owner, component.Seed); + + if (_random.Prob(0.3f)) + component.Sampled = true; + + // Just in case. + CheckLevelSanity(uid, component); + ForceUpdateByExternalCause(uid, component); + } return; } @@ -527,6 +540,7 @@ public sealed class PlantHolderSystem : EntitySystem } _botany.Harvest(component.Seed, user, plantholder); + AfterHarvest(plantholder, component); return true; } From a8140897f2d6a58129c27eda1299bc333304dc57 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Sun, 3 Aug 2025 08:06:25 +0300 Subject: [PATCH 24/53] last update within this PR --- .../Components/WeedPestGrowthComponent.cs | 6 + Content.Server/Botany/SeedPrototype.cs | 3 +- .../Botany/Systems/AutoHarvestGrowthSystem.cs | 13 +-- .../Botany/Systems/PlantGrowthSystem.cs | 3 - .../Botany/Systems/PlantHolderSystem.cs | 109 ++---------------- .../Botany/Systems/WeedPestGrowthSystem.cs | 38 ++++++ 6 files changed, 55 insertions(+), 117 deletions(-) diff --git a/Content.Server/Botany/Components/WeedPestGrowthComponent.cs b/Content.Server/Botany/Components/WeedPestGrowthComponent.cs index 592fad6ecbf..a0cd83afb04 100644 --- a/Content.Server/Botany/Components/WeedPestGrowthComponent.cs +++ b/Content.Server/Botany/Components/WeedPestGrowthComponent.cs @@ -28,6 +28,12 @@ public sealed partial class WeedPestGrowthComponent : PlantGrowthComponent [DataField] public float WeedGrowthAmount = 0.5f; + /// + /// Weed level threshold at which the plant is considered overgrown and will transform into kudzu. + /// + [DataField] + public float WeedHighLevelThreshold = 10f; + /// /// Chance per tick for pests to damage this plant. /// diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index 29cc4a46c0b..fdab80e1380 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -44,9 +44,8 @@ public partial struct SeedChemQuantity [DataField("Inherent")] public bool Inherent = true; } -// 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(EntityEffectSystem), typeof(MutationSystem))] +[Access(typeof(BotanySystem), typeof(PlantHolderSystem), typeof(SeedExtractorSystem), typeof(EntityEffectSystem), typeof(MutationSystem), typeof(AutoHarvestGrowthSystem), typeof(HarvestSystem), typeof(PlantTraitsSystem))] public partial class SeedData { #region Tracking diff --git a/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs b/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs index bc82f101536..d7f01ba5b30 100644 --- a/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs +++ b/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs @@ -5,6 +5,8 @@ namespace Content.Server.Botany.Systems; public sealed class AutoHarvestGrowthSystem : PlantGrowthSystem { + [Dependency] private readonly HarvestSystem _harvestSystem = default!; + public override void Initialize() { base.Initialize(); @@ -22,16 +24,7 @@ public sealed class AutoHarvestGrowthSystem : PlantGrowthSystem // Check if ready for harvest using HarvestComponent if (TryComp(uid, out var harvestComp) && harvestComp.ReadyForHarvest && _random.Prob(component.HarvestChance)) { - // Auto-harvest the plant - harvestComp.ReadyForHarvest = false; - harvestComp.LastHarvestTime = holder.Age; - - // Spawn the harvested items - if (holder.Seed.ProductPrototypes.Count > 0) - { - var product = _random.Pick(holder.Seed.ProductPrototypes); - var entity = Spawn(product, Transform(uid).Coordinates); - } + _harvestSystem.DoHarvest(uid, uid, harvestComp, holder); } } } diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs index 8ea753c1046..7341fb264b3 100644 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -31,9 +31,6 @@ public sealed class PlantGrowthCycleSystem : EntitySystem var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var plantHolder)) { - if (plantHolder.Seed == null || plantHolder.Dead) - continue; - if (_gameTiming.CurTime < plantHolder.LastCycle + plantHolder.CycleDelay) continue; diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 1ef3cb99fd2..78757555dbd 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -51,7 +51,6 @@ public sealed class PlantHolderSystem : EntitySystem [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - public const float WeedHighLevelThreshold = 10f; private static readonly ProtoId HoeTag = "Hoe"; private static readonly ProtoId PlantSampleTakerTag = "PlantSampleTaker"; @@ -61,7 +60,6 @@ public sealed class PlantHolderSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnExamine); SubscribeLocalEvent(OnInteractUsing); - SubscribeLocalEvent(OnInteractHand); SubscribeLocalEvent(OnSolutionTransferred); } @@ -337,12 +335,9 @@ public sealed class PlantHolderSystem : EntitySystem _popup.PopupCursor(Loc.GetString("plant-holder-component-take-sample-message", ("seedName", displayName)), args.User); - DoScream(entity.Owner, component.Seed); - if (_random.Prob(0.3f)) component.Sampled = true; - // Just in case. CheckLevelSanity(uid, component); ForceUpdateByExternalCause(uid, component); } @@ -350,12 +345,6 @@ public sealed class PlantHolderSystem : EntitySystem return; } - if (HasComp(args.Used)) - { - args.Handled = true; - DoHarvest(uid, args.User, component); - return; - } if (TryComp(args.Used, out var produce)) { @@ -399,11 +388,6 @@ public sealed class PlantHolderSystem : EntitySystem { _audio.PlayPvs(ent.Comp.WateringSound, ent.Owner); } - private void OnInteractHand(Entity entity, ref InteractHandEvent args) - { - DoHarvest(entity, args.User, entity.Comp); - } - public void Update(EntityUid uid, PlantHolderComponent? component = null) { @@ -414,6 +398,7 @@ public sealed class PlantHolderSystem : EntitySystem var curTime = _gameTiming.CurTime; + // ForceUpdate is used for external triggers like swabbing if (component.ForceUpdate) component.ForceUpdate = false; else if (curTime < (component.LastCycle + component.CycleDelay)) @@ -439,33 +424,6 @@ public sealed class PlantHolderSystem : EntitySystem component.MutationLevel = 0; } - // Weeds like water and nutrients! They may appear even if there's not a seed planted. Isnt connected to the plant, stays here in PlantHolder. - if (component.WaterLevel > 10 && component.NutritionLevel > 5) - { - var chance = 0f; - if (component.Seed == null) - chance = 0.05f; - else if (TryComp(uid, out var traits) && traits.TurnIntoKudzu) - chance = 1f; - else - chance = 0.01f; - - if (_random.Prob(chance)) - component.WeedLevel += 1 + component.WeedCoefficient; - - if (component.DrawWarnings) - component.UpdateSpriteAfterUpdate = true; - } - - if (component.Seed != null && TryComp(uid, out var kudzuTraits) && kudzuTraits.TurnIntoKudzu - && component.WeedLevel >= WeedHighLevelThreshold) - { - Spawn(component.Seed.KudzuPrototype, Transform(uid).Coordinates.SnapToGrid(EntityManager)); - kudzuTraits.TurnIntoKudzu = false; - component.Health = 0; - } - - // If we have no seed planted, or the plant is dead, stop processing here. if (component.Seed == null || component.Dead) { @@ -488,9 +446,9 @@ public sealed class PlantHolderSystem : EntitySystem UpdateSprite(uid, component); } - //TODO: kill this bullshit /// /// Ensures all plant holder levels are within valid ranges. + /// TODO: Move this validation logic to individual growth components /// public void CheckLevelSanity(EntityUid uid, PlantHolderComponent? component = null) { @@ -515,60 +473,6 @@ public sealed class PlantHolderSystem : EntitySystem 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; - - // Try to get HarvestComponent for harvest system - if (TryComp(plantholder, out var harvestComp) && harvestComp.ReadyForHarvest && !component.Dead) - { - if (_hands.TryGetActiveItem(user, out var activeItem)) - { - if (!_botany.CanHarvest(component.Seed, activeItem)) - { - _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), user); - return false; - } - } - else if (!_botany.CanHarvest(component.Seed)) - { - return false; - } - - _botany.Harvest(component.Seed, user, plantholder); - - AfterHarvest(plantholder, component); - return true; - } - - if (!component.Dead) - return false; - - RemovePlant(plantholder, component); - AfterHarvest(plantholder, component); - return true; - } - - /// - /// Force do scream on PlantHolder (like plant is screaming) using seed's ScreamSound specifier (collection or soundPath) - /// - /// - public bool DoScream(EntityUid plantholder, SeedData? seed = null) - { - if (seed == null) - return false; - - if (!TryComp(plantholder, out var traits) || !traits.CanScream) - return false; - - _audio.PlayPvs(seed.ScreamSound, plantholder); - return true; - } - public void AutoHarvest(EntityUid uid, PlantHolderComponent? component = null) { if (!Resolve(uid, ref component)) @@ -577,8 +481,11 @@ public sealed class PlantHolderSystem : EntitySystem if (component.Seed == null || !component.Harvest) return; - _botany.AutoHarvest(component.Seed, Transform(uid).Coordinates, uid); - AfterHarvest(uid, component); + if (TryComp(uid, out var harvestComp)) + { + var harvestSystem = EntitySystem.Get(); + harvestSystem.DoHarvest(uid, uid, harvestComp, component); + } } private void AfterHarvest(EntityUid uid, PlantHolderComponent? component = null) @@ -589,8 +496,6 @@ public sealed class PlantHolderSystem : EntitySystem component.Harvest = false; component.LastProduce = component.Age; - DoScream(uid, component.Seed); - HarvestComponent? harvest = null; if (TryComp(uid, out harvest)) { diff --git a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs index a091fa372f9..030f497c97c 100644 --- a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs +++ b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs @@ -1,4 +1,6 @@ using Content.Server.Botany.Components; +using Content.Shared.Coordinates.Helpers; +using Robust.Server.GameObjects; using Robust.Shared.Random; namespace Content.Server.Botany.Systems; @@ -8,6 +10,7 @@ public sealed class WeedPestGrowthSystem : PlantGrowthSystem { base.Initialize(); SubscribeLocalEvent(OnPlantGrow); + SubscribeLocalEvent(OnTrayUpdate); } private void OnPlantGrow(EntityUid uid, WeedPestGrowthComponent component, OnPlantGrowEvent args) @@ -34,4 +37,39 @@ public sealed class WeedPestGrowthSystem : PlantGrowthSystem holder.UpdateSpriteAfterUpdate = true; } } + + /// + /// Handles weed growth and kudzu transformation for plant holder trays. + /// + private void OnTrayUpdate(EntityUid uid, PlantHolderComponent component, OnPlantGrowEvent args) + { + // Weeds like water and nutrients! They may appear even if there's not a seed planted + if (component.WaterLevel > 10 && component.NutritionLevel > 5) + { + float chance; + if (component.Seed == null) + chance = 0.05f; + else if (TryComp(uid, out var traits) && traits.TurnIntoKudzu) + chance = 1f; + else + chance = 0.01f; + + if (_random.Prob(chance)) + component.WeedLevel += 1 + component.WeedCoefficient; + if (component.DrawWarnings) + component.UpdateSpriteAfterUpdate = true; + } + + // Handle kudzu transformation + if (component.Seed != null && !component.Dead && + TryComp(uid, out var weed) && + TryComp(uid, out var kudzuTraits) && + kudzuTraits.TurnIntoKudzu && + component.WeedLevel >= weed.WeedHighLevelThreshold) + { + Spawn(component.Seed.KudzuPrototype, Transform(uid).Coordinates.SnapToGrid(EntityManager)); + kudzuTraits.TurnIntoKudzu = false; + component.Health = 0; + } + } } From 256e233cfca8452dd8fd6ad3a361d37ca63c5729 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Sun, 3 Aug 2025 08:17:32 +0300 Subject: [PATCH 25/53] AutoHarvestGrowth remote --- .../Components/AutoHarvestGrowthComponent.cs | 12 -------- Content.Server/Botany/SeedPrototype.cs | 2 +- .../Botany/Systems/AutoHarvestGrowthSystem.cs | 30 ------------------- 3 files changed, 1 insertion(+), 43 deletions(-) delete mode 100644 Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs delete mode 100644 Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs diff --git a/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs b/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs deleted file mode 100644 index 5a28055c303..00000000000 --- a/Content.Server/Botany/Components/AutoHarvestGrowthComponent.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Content.Server.Botany.Components; - -[RegisterComponent] -[DataDefinition] -public sealed partial class AutoHarvestGrowthComponent : PlantGrowthComponent -{ - /// - /// Chance per tick for the plant to automatically harvest itself. - /// - [DataField] - public float HarvestChance = 0.1f; -} diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index fdab80e1380..9f20df58770 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -45,7 +45,7 @@ public partial struct SeedChemQuantity } [Virtual, DataDefinition] -[Access(typeof(BotanySystem), typeof(PlantHolderSystem), typeof(SeedExtractorSystem), typeof(EntityEffectSystem), typeof(MutationSystem), typeof(AutoHarvestGrowthSystem), typeof(HarvestSystem), typeof(PlantTraitsSystem))] +[Access(typeof(BotanySystem), typeof(PlantHolderSystem), typeof(SeedExtractorSystem), typeof(EntityEffectSystem), typeof(MutationSystem), typeof(HarvestSystem), typeof(PlantTraitsSystem))] public partial class SeedData { #region Tracking diff --git a/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs b/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs deleted file mode 100644 index d7f01ba5b30..00000000000 --- a/Content.Server/Botany/Systems/AutoHarvestGrowthSystem.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Content.Server.Botany.Components; -using Robust.Shared.Random; - -namespace Content.Server.Botany.Systems; - -public sealed class AutoHarvestGrowthSystem : PlantGrowthSystem -{ - [Dependency] private readonly HarvestSystem _harvestSystem = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); - } - - private void OnPlantGrow(EntityUid uid, AutoHarvestGrowthComponent component, OnPlantGrowEvent args) - { - PlantHolderComponent? holder = null; - Resolve(uid, ref holder); - - if (holder == null || holder.Seed == null || holder.Dead) - return; - - // Check if ready for harvest using HarvestComponent - if (TryComp(uid, out var harvestComp) && harvestComp.ReadyForHarvest && _random.Prob(component.HarvestChance)) - { - _harvestSystem.DoHarvest(uid, uid, harvestComp, holder); - } - } -} From bc8a436029301ac56a01094b1d9b1652f1e8155e Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Mon, 4 Aug 2025 17:28:25 +0300 Subject: [PATCH 26/53] Viable --- .../Botany/Components/PlantTraitsComponent.cs | 6 ++ Content.Server/Botany/SeedPrototype.cs | 8 -- .../Botany/Systems/BasicGrowthSystem.cs | 2 +- .../Botany/Systems/PlantHolderSystem.cs | 2 +- .../EntityEffects/EntityEffectSystem.cs | 82 ++++++++++++------- 5 files changed, 59 insertions(+), 41 deletions(-) diff --git a/Content.Server/Botany/Components/PlantTraitsComponent.cs b/Content.Server/Botany/Components/PlantTraitsComponent.cs index 68d1b35bfb0..fc7ee2f7470 100644 --- a/Content.Server/Botany/Components/PlantTraitsComponent.cs +++ b/Content.Server/Botany/Components/PlantTraitsComponent.cs @@ -71,4 +71,10 @@ public sealed partial class PlantTraitsComponent : PlantGrowthComponent /// [DataField] public bool TurnIntoKudzu = false; + + /// + /// If false, rapidly decrease health while growing. Adds a bit of challenge to keep mutated plants alive via Unviable's frequency. + /// + [DataField] + public bool Viable = true; } diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index 9f20df58770..0c9250b0ae6 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -144,12 +144,6 @@ public partial class SeedData [DataField] public List GrowthComponents = new(); - /// - /// If false, rapidly decrease health while growing. Adds a bit of challenge to keep mutated plants alive via Unviable's frequency. - /// - [DataField] - public bool Viable = true; - /// /// Log impact for harvest operations. /// @@ -169,7 +163,6 @@ public partial class SeedData var newSeed = new SeedData { GrowthComponents = new List(), - Viable = Viable, HarvestLogImpact = HarvestLogImpact, PlantLogImpact = PlantLogImpact, Name = Name, @@ -210,7 +203,6 @@ public partial class SeedData var newSeed = new SeedData { GrowthComponents = new List(), - Viable = other.Viable, HarvestLogImpact = other.HarvestLogImpact, PlantLogImpact = other.PlantLogImpact, Name = other.Name, diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index 4e11c6b0f07..1052105dd63 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -50,7 +50,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem return; // Check if the plant is viable - if (holder.Seed.Viable == false) + if (TryComp(uid, out var traits) && !traits.Viable) { holder.Health -= _random.Next(5, 10) * PlantGrowthSystem.HydroponicsSpeedMultiplier; if (holder.DrawWarnings) diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 78757555dbd..5c290e768ad 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -136,7 +136,7 @@ public sealed class PlantHolderSystem : EntitySystem if (traits.CanScream) args.PushMarkup(Loc.GetString("mutation-plant-scream")); - if (component.Seed.Viable == false) + if (!traits.Viable) args.PushMarkup(Loc.GetString("mutation-plant-unviable")); } else diff --git a/Content.Server/EntityEffects/EntityEffectSystem.cs b/Content.Server/EntityEffects/EntityEffectSystem.cs index 7cf13ff094d..568d54cf811 100644 --- a/Content.Server/EntityEffects/EntityEffectSystem.cs +++ b/Content.Server/EntityEffects/EntityEffectSystem.cs @@ -370,40 +370,59 @@ public sealed class EntityEffectSystem : EntitySystem if (plantHolderComp.Seed == null) return; - // Try to find the field in PlantTraitsComponent first - PlantTraitsComponent? traits = null; - Resolve(args.Args.TargetEntity, ref traits); + var targetValue = args.Effect.TargetValue; + var entity = args.Args.TargetEntity; - // Fallback to SeedData for fields that haven't been moved to components yet - var seedMember = plantHolderComp.Seed.GetType().GetField(args.Effect.TargetValue); - if (seedMember == null) + // List of component types to check + var componentTypes = new[] { - _mutation.Log.Error(args.Effect.GetType().Name + " Error: Member " + args.Effect.TargetValue + " not found on " + plantHolderComp.Seed.GetType().Name + ". Did you misspell it?"); - return; + typeof(PlantTraitsComponent), + typeof(BasicGrowthComponent), + typeof(AtmosphericGrowthComponent), + typeof(ToxinsComponent), + typeof(WeedPestGrowthComponent) + }; + + // Try to find and mutate the field in any component + foreach (var componentType in componentTypes) + { + if (!EntityManager.HasComponent(entity, componentType)) + continue; + + var component = EntityManager.GetComponent(entity, componentType); + var field = componentType.GetField(targetValue); + + if (field == null) + continue; + + var currentValue = field.GetValue(component); + if (currentValue == null) + continue; + + if (field.FieldType == typeof(float)) + { + var floatVal = (float)currentValue; + MutateFloat(ref floatVal, args.Effect.MinValue, args.Effect.MaxValue, args.Effect.Steps); + field.SetValue(component, floatVal); + return; + } + else if (field.FieldType == typeof(int)) + { + var intVal = (int)currentValue; + MutateInt(ref intVal, (int)args.Effect.MinValue, (int)args.Effect.MaxValue, args.Effect.Steps); + field.SetValue(component, intVal); + return; + } + else if (field.FieldType == typeof(bool)) + { + var boolVal = (bool)currentValue; + field.SetValue(component, !boolVal); + return; + } } - var currentSeedValObj = seedMember.GetValue(plantHolderComp.Seed); - if (currentSeedValObj == null) - return; - - if (seedMember.FieldType == typeof(float)) - { - var floatVal = (float)currentSeedValObj; - MutateFloat(ref floatVal, args.Effect.MinValue, args.Effect.MaxValue, args.Effect.Steps); - seedMember.SetValue(plantHolderComp.Seed, floatVal); - } - else if (seedMember.FieldType == typeof(int)) - { - var intVal = (int)currentSeedValObj; - MutateInt(ref intVal, (int)args.Effect.MinValue, (int)args.Effect.MaxValue, args.Effect.Steps); - seedMember.SetValue(plantHolderComp.Seed, intVal); - } - else if (seedMember.FieldType == typeof(bool)) - { - var boolVal = (bool)currentSeedValObj; - boolVal = !boolVal; - seedMember.SetValue(plantHolderComp.Seed, boolVal); - } + // Field not found in any component + _mutation.Log.Error($"{args.Effect.GetType().Name} Error: Field '{targetValue}' not found in any plant component. Did you misspell it?"); } private void OnExecutePlantCryoxadone(ref ExecuteEntityEffectEvent args) @@ -479,7 +498,8 @@ public sealed class EntityEffectSystem : EntitySystem if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveMutableSeed: true)) return; - plantHolderComp.Seed!.Viable = true; + if (TryComp(args.Args.TargetEntity, out var traits)) + traits.Viable = true; } private void OnExecutePlantRestoreSeeds(ref ExecuteEntityEffectEvent args) From 2173d26c1e616adcc21c34f0a34c54703ed9d509 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Mon, 4 Aug 2025 20:40:48 +0300 Subject: [PATCH 27/53] fix --- .../Botany/Systems/HarvestSystem.cs | 12 +++++- .../Botany/Systems/PlantGrowthSystem.cs | 5 --- .../Botany/Systems/PlantHolderSystem.cs | 37 ------------------- 3 files changed, 11 insertions(+), 43 deletions(-) diff --git a/Content.Server/Botany/Systems/HarvestSystem.cs b/Content.Server/Botany/Systems/HarvestSystem.cs index ac407c8ed38..7d521906945 100644 --- a/Content.Server/Botany/Systems/HarvestSystem.cs +++ b/Content.Server/Botany/Systems/HarvestSystem.cs @@ -63,8 +63,9 @@ public sealed class HarvestSystem : EntitySystem return; // Check if plant is ready for harvest - if (plantHolder.Age >= traits.Production) + if (component.HarvestRepeat == HarvestType.Repeat || component.HarvestRepeat == HarvestType.SelfHarvest) { + // Repeat harvest var timeSinceLastHarvest = plantHolder.Age - component.LastHarvestTime; if (timeSinceLastHarvest >= traits.Production && !component.ReadyForHarvest) { @@ -72,6 +73,15 @@ public sealed class HarvestSystem : EntitySystem plantHolder.UpdateSpriteAfterUpdate = true; } } + else + { + // Non-repeat harvest + if (plantHolder.Age >= traits.Production && !component.ReadyForHarvest) + { + component.ReadyForHarvest = true; + plantHolder.UpdateSpriteAfterUpdate = true; + } + } } private void OnInteractHand(EntityUid uid, HarvestComponent component, InteractHandEvent args) diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs index 7341fb264b3..ea0ddbe5f7c 100644 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -31,13 +31,8 @@ public sealed class PlantGrowthCycleSystem : EntitySystem var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var plantHolder)) { - if (_gameTiming.CurTime < plantHolder.LastCycle + plantHolder.CycleDelay) - continue; - var plantGrow = new OnPlantGrowEvent(); RaiseLocalEvent(uid, ref plantGrow); - - plantHolder.LastCycle = _gameTiming.CurTime; } nextUpdate = _gameTiming.CurTime + updateDelay; diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 5c290e768ad..86bbf71ea76 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -473,43 +473,6 @@ public sealed class PlantHolderSystem : EntitySystem component.MutationMod = MathHelper.Clamp(component.MutationMod, 0f, 3f); } - public void AutoHarvest(EntityUid uid, PlantHolderComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - if (component.Seed == null || !component.Harvest) - return; - - if (TryComp(uid, out var harvestComp)) - { - var harvestSystem = EntitySystem.Get(); - harvestSystem.DoHarvest(uid, uid, harvestComp, component); - } - } - - private void AfterHarvest(EntityUid uid, PlantHolderComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - component.Harvest = false; - component.LastProduce = component.Age; - - HarvestComponent? harvest = null; - if (TryComp(uid, out harvest)) - { - harvest.ReadyForHarvest = false; - harvest.LastHarvestTime = component.Age; - - if (harvest.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)) From 4a8126e42425c6c22e2e08c330e903ff66c37067 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Tue, 5 Aug 2025 01:25:22 +0300 Subject: [PATCH 28/53] Fix growth rate --- .../Botany/Components/PlantHolderComponent.cs | 12 +++---- .../Botany/Systems/HarvestSystem.cs | 5 +-- .../Botany/Systems/PlantGrowthSystem.cs | 36 ++----------------- .../Botany/Systems/PlantHolderSystem.cs | 19 +++++----- .../Botany/Systems/PlantTraitsSystem.cs | 21 ----------- 5 files changed, 20 insertions(+), 73 deletions(-) diff --git a/Content.Server/Botany/Components/PlantHolderComponent.cs b/Content.Server/Botany/Components/PlantHolderComponent.cs index ad33fa9ea2f..7e033fb96bb 100644 --- a/Content.Server/Botany/Components/PlantHolderComponent.cs +++ b/Content.Server/Botany/Components/PlantHolderComponent.cs @@ -13,12 +13,6 @@ public sealed partial class PlantHolderComponent : Component [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] public TimeSpan NextUpdate = TimeSpan.Zero; - /// - /// Time between plant reagent consumption updates. - /// - [DataField] - public TimeSpan UpdateDelay = TimeSpan.FromSeconds(3); - /// /// Age when the plant last produced harvestable items. /// @@ -37,6 +31,12 @@ public sealed partial class PlantHolderComponent : Component [DataField] public TimeSpan CycleDelay = TimeSpan.FromSeconds(15f); + /// + /// Time between plant reagent consumption updates. + /// + [DataField] + public TimeSpan UpdateDelay = TimeSpan.FromSeconds(3); + /// /// Game time when the plant last did a growth update. /// diff --git a/Content.Server/Botany/Systems/HarvestSystem.cs b/Content.Server/Botany/Systems/HarvestSystem.cs index 7d521906945..271efe6a204 100644 --- a/Content.Server/Botany/Systems/HarvestSystem.cs +++ b/Content.Server/Botany/Systems/HarvestSystem.cs @@ -67,7 +67,7 @@ public sealed class HarvestSystem : EntitySystem { // Repeat harvest var timeSinceLastHarvest = plantHolder.Age - component.LastHarvestTime; - if (timeSinceLastHarvest >= traits.Production && !component.ReadyForHarvest) + if (timeSinceLastHarvest > traits.Production && !component.ReadyForHarvest) { component.ReadyForHarvest = true; plantHolder.UpdateSpriteAfterUpdate = true; @@ -76,7 +76,7 @@ public sealed class HarvestSystem : EntitySystem else { // Non-repeat harvest - if (plantHolder.Age >= traits.Production && !component.ReadyForHarvest) + if (plantHolder.Age > traits.Production && !component.ReadyForHarvest) { component.ReadyForHarvest = true; plantHolder.UpdateSpriteAfterUpdate = true; @@ -155,6 +155,7 @@ public sealed class HarvestSystem : EntitySystem case HarvestType.SelfHarvest: harvestComp.ReadyForHarvest = false; harvestComp.LastHarvestTime = plantHolder.Age; + plantHolder.Harvest = false; break; } diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs index ea0ddbe5f7c..03608a9a0c3 100644 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -7,38 +7,6 @@ namespace Content.Server.Botany.Systems; [ByRefEvent] public readonly record struct OnPlantGrowEvent; -/// -/// Handles the main growth cycle for all plants. -/// -public sealed class PlantGrowthCycleSystem : EntitySystem -{ - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - - public TimeSpan nextUpdate = TimeSpan.Zero; - public TimeSpan updateDelay = TimeSpan.FromSeconds(15); // PlantHolder has a 15 second delay on cycles - - public override void Initialize() - { - base.Initialize(); - } - - public override void Update(float frameTime) - { - if (nextUpdate > _gameTiming.CurTime) - return; - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var plantHolder)) - { - var plantGrow = new OnPlantGrowEvent(); - RaiseLocalEvent(uid, ref plantGrow); - } - - nextUpdate = _gameTiming.CurTime + updateDelay; - } -} - /// /// Base system for plant growth mechanics. Provides common functionality for all plant growth systems. /// @@ -86,14 +54,14 @@ public abstract class PlantGrowthSystem : EntitySystem { if (component.Age < traits.Maturation) component.Age += amount; - else if (!component.Harvest && traits.Yield > 0f) + else if (!component.Harvest && traits.Yield <= 0f) component.LastProduce -= amount; } else { if (component.Age < traits.Maturation) component.SkipAging++; - else if (!component.Harvest && traits.Yield > 0f) + else if (!component.Harvest && traits.Yield <= 0f) component.LastProduce += amount; } } diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 86bbf71ea76..0f1cf3e7df2 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -345,7 +345,6 @@ public sealed class PlantHolderSystem : EntitySystem return; } - if (TryComp(args.Used, out var produce)) { args.Handled = true; @@ -590,13 +589,16 @@ public sealed class PlantHolderSystem : EntitySystem if (!TryComp(uid, out var app)) return; - TryComp(uid, out var spriteTraits); + TryComp(uid, out var traits); + + if (traits == null) + return; if (component.Seed != null) { if (component.DrawWarnings) { - _appearance.SetData(uid, PlantHolderVisuals.HealthLight, component.Health <= (spriteTraits?.Endurance ?? 100f) / 2f); + _appearance.SetData(uid, PlantHolderVisuals.HealthLight, component.Health <= traits.Endurance / 2f); } if (component.Dead) @@ -611,10 +613,7 @@ public sealed class PlantHolderSystem : EntitySystem } else { - if (spriteTraits == null) - return; - - if (component.Age < spriteTraits.Maturation) + if (component.Age < traits.Maturation) { var growthStage = GetCurrentGrowthStage((uid, component)); @@ -625,7 +624,7 @@ public sealed class PlantHolderSystem : EntitySystem else { _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app); - _appearance.SetData(uid, PlantHolderVisuals.PlantState, $"stage-{spriteTraits.GrowthStages}", app); + _appearance.SetData(uid, PlantHolderVisuals.PlantState, $"stage-{traits.GrowthStages}", app); } } } @@ -647,8 +646,8 @@ public sealed class PlantHolderSystem : EntitySystem } /// - /// 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. + /// 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) { diff --git a/Content.Server/Botany/Systems/PlantTraitsSystem.cs b/Content.Server/Botany/Systems/PlantTraitsSystem.cs index 65bbba3cd09..171cab39db9 100644 --- a/Content.Server/Botany/Systems/PlantTraitsSystem.cs +++ b/Content.Server/Botany/Systems/PlantTraitsSystem.cs @@ -32,26 +32,5 @@ public sealed class PlantTraitsSystem : PlantGrowthSystem if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; } - - // Check if plant is ready for harvest - if (holder.Seed.ProductPrototypes.Count > 0 && TryComp(uid, out var harvestComp)) - { - if (holder.Age > component.Production) - { - if (holder.Age - harvestComp.LastHarvestTime > component.Production && !harvestComp.ReadyForHarvest) - { - harvestComp.ReadyForHarvest = true; - harvestComp.LastHarvestTime = holder.Age; - } - } - else - { - if (harvestComp.ReadyForHarvest) - { - harvestComp.ReadyForHarvest = false; - harvestComp.LastHarvestTime = holder.Age; - } - } - } } } From 8a5eaa670c2e1d9842159ea6c43f12e8d73205c6 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Mon, 1 Sep 2025 22:03:06 +0300 Subject: [PATCH 29/53] Improvements to the YAML --- .../Botany/Components/BotanySwabComponent.cs | 5 - .../Components/GrowthComponentsHolder.cs | 49 + Content.Server/Botany/SeedPrototype.cs | 24 +- .../Botany/Systems/BasicGrowthSystem.cs | 16 +- .../Botany/Systems/BotanySwabSystem.cs | 24 +- .../Botany/Systems/BotanySystem.Seed.cs | 18 +- .../Botany/Systems/MutationSystem.cs | 8 +- .../Botany/Systems/PlantHolderSystem.cs | 112 +- Resources/Maps/Salvage/vegan-meatball.yml | 64 +- Resources/Prototypes/Hydroponics/seeds.yml | 1396 ++++++++--------- 10 files changed, 864 insertions(+), 852 deletions(-) create mode 100644 Content.Server/Botany/Components/GrowthComponentsHolder.cs diff --git a/Content.Server/Botany/Components/BotanySwabComponent.cs b/Content.Server/Botany/Components/BotanySwabComponent.cs index fcedaa036f4..987a804c960 100644 --- a/Content.Server/Botany/Components/BotanySwabComponent.cs +++ b/Content.Server/Botany/Components/BotanySwabComponent.cs @@ -18,9 +18,4 @@ public sealed partial class BotanySwabComponent : Component /// SeedData from the first plant that got swabbed. /// public SeedData? SeedData; - - /// - /// Growth components from the first plant that got swabbed. - /// - public List components; } diff --git a/Content.Server/Botany/Components/GrowthComponentsHolder.cs b/Content.Server/Botany/Components/GrowthComponentsHolder.cs new file mode 100644 index 00000000000..3bbe7ee15e6 --- /dev/null +++ b/Content.Server/Botany/Components/GrowthComponentsHolder.cs @@ -0,0 +1,49 @@ +namespace Content.Server.Botany.Components; + +/// +/// Holder for plant growth settings serialized from YAML as a map under "growthComponents". +/// Each property corresponds to a concrete growth component type. +/// +[DataDefinition] +public sealed partial class GrowthComponentsHolder +{ + [DataField("plantTraits")] + public PlantTraitsComponent? PlantTraits { get; set; } + + [DataField("basicGrowth")] + public BasicGrowthComponent? BasicGrowth { get; set; } + + [DataField("toxins")] + public ToxinsComponent? Toxins { get; set; } + + [DataField("harvest")] + public HarvestComponent? Harvest { get; set; } + + [DataField("atmosphericGrowth")] + public AtmosphericGrowthComponent? AtmosphericGrowth { get; set; } + + [DataField("consumeExudeGasGrowth")] + public ConsumeExudeGasGrowthComponent? ConsumeExudeGasGrowth { get; set; } + + [DataField("weedPestGrowth")] + public WeedPestGrowthComponent? WeedPestGrowth { get; set; } + + [DataField("unviableGrowth")] + public UnviableGrowthComponent? UnviableGrowth { get; set; } + + /// + /// Populates any null properties with default component instances so that + /// systems can always apply a full set. Existing (YAML-provided) values are kept. + /// + public void EnsureGrowthComponents() + { + foreach (var prop in typeof(GrowthComponentsHolder).GetProperties()) + { + if (prop.GetValue(this) == null) + { + var instance = System.Activator.CreateInstance(prop.PropertyType); + prop.SetValue(this, instance); + } + } + } +} diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index 0c9250b0ae6..e8ab7a368cb 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -45,7 +45,7 @@ public partial struct SeedChemQuantity } [Virtual, DataDefinition] -[Access(typeof(BotanySystem), typeof(PlantHolderSystem), typeof(SeedExtractorSystem), typeof(EntityEffectSystem), typeof(MutationSystem), typeof(HarvestSystem), typeof(PlantTraitsSystem))] +[Access(typeof(BotanySystem), typeof(PlantHolderSystem), typeof(SeedExtractorSystem), typeof(EntityEffectSystem), typeof(MutationSystem), typeof(HarvestSystem), typeof(PlantTraitsSystem), typeof(BotanySwabSystem), typeof(BasicGrowthSystem))] public partial class SeedData { #region Tracking @@ -142,7 +142,7 @@ public partial class SeedData /// The growth components used by this seed. /// [DataField] - public List GrowthComponents = new(); + public GrowthComponentsHolder GrowthComponents = new(); /// /// Log impact for harvest operations. @@ -159,10 +159,11 @@ public partial class SeedData public SeedData Clone() { DebugTools.Assert(!Immutable, "There should be no need to clone an immutable seed."); + var serializationManager = IoCManager.Resolve(); var newSeed = new SeedData { - GrowthComponents = new List(), + GrowthComponents = serializationManager.CreateCopy(GrowthComponents, notNullableOverride: true), HarvestLogImpact = HarvestLogImpact, PlantLogImpact = PlantLogImpact, Name = Name, @@ -183,13 +184,6 @@ public partial class SeedData Unique = true, }; - // Deep copy growth components - foreach (var component in GrowthComponents) - { - var newComponent = component.DupeComponent(); - newSeed.GrowthComponents.Add(newComponent); - } - newSeed.Mutations.AddRange(Mutations); return newSeed; } @@ -200,9 +194,10 @@ public partial class SeedData /// public SeedData SpeciesChange(SeedData other) { + var serializationManager = IoCManager.Resolve(); var newSeed = new SeedData { - GrowthComponents = new List(), + GrowthComponents = serializationManager.CreateCopy(other.GrowthComponents, notNullableOverride: true), HarvestLogImpact = other.HarvestLogImpact, PlantLogImpact = other.PlantLogImpact, Name = other.Name, @@ -240,13 +235,6 @@ public partial class SeedData } } - // Deep copy growth components from the new species - foreach (var component in other.GrowthComponents) - { - var newComponent = component.DupeComponent(); - newSeed.GrowthComponents.Add(newComponent); - } - return newSeed; } } diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index 1052105dd63..1863c28c3a9 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -22,22 +22,24 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem private void OnSwab(EntityUid uid, BasicGrowthComponent component, BotanySwabDoAfterEvent args) { if (args.Cancelled || args.Handled || !TryComp(args.Args.Target, out var plant) || - args.Used == null || !TryComp(args.Used.Value, out var swab)) + args.Used == null || !TryComp(args.Used.Value, out var swab) || swab.SeedData == null) return; - var swabComp = swab.components.Find(c => c.GetType() == typeof(BasicGrowthComponent)); + var swabComp = swab.SeedData.GrowthComponents.BasicGrowth; if (swabComp == null) { - swab.components.Add(new BasicGrowthComponent() { + swab.SeedData.GrowthComponents.BasicGrowth = new BasicGrowthComponent() + { WaterConsumption = component.WaterConsumption, NutrientConsumption = component.NutrientConsumption - }); + }; } else { - BasicGrowthComponent typedComp = (BasicGrowthComponent)swabComp; - if (_random.Prob(0.5f)) typedComp.WaterConsumption = component.WaterConsumption; - if (_random.Prob(0.5f)) typedComp.NutrientConsumption = component.NutrientConsumption; + if (_random.Prob(0.5f)) + swabComp.WaterConsumption = component.WaterConsumption; + if (_random.Prob(0.5f)) + swabComp.NutrientConsumption = component.NutrientConsumption; } } diff --git a/Content.Server/Botany/Systems/BotanySwabSystem.cs b/Content.Server/Botany/Systems/BotanySwabSystem.cs index 9231f0c2cd5..95436dab1a5 100644 --- a/Content.Server/Botany/Systems/BotanySwabSystem.cs +++ b/Content.Server/Botany/Systems/BotanySwabSystem.cs @@ -68,17 +68,8 @@ public sealed class BotanySwabSystem : EntitySystem if (swab.SeedData == null) { // Pick up pollen - swab.SeedData = plant.Seed; if (plant.Seed != null) - { - // Deep copy components to prevent changes to the original plant from affecting the swab - swab.components = new List(); - foreach (var component in plant.Seed.GrowthComponents) - { - var copiedComponent = component.DupeComponent(); - swab.components.Add(copiedComponent); - } - } + swab.SeedData = plant.Seed.Clone(); _popupSystem.PopupEntity(Loc.GetString("botany-swab-from"), args.Args.Target.Value, args.Args.User); } @@ -92,18 +83,7 @@ public sealed class BotanySwabSystem : EntitySystem plant.Seed = _mutationSystem.Cross(swab.SeedData, old); // Transfer old plant pollen to swab - swab.SeedData = old; - - // Copy components from the old plant to the swab - if (old.GrowthComponents != null) - { - swab.components = new List(); - foreach (var component in old.GrowthComponents) - { - var copiedComponent = component.DupeComponent(); - swab.components.Add(copiedComponent); - } - } + swab.SeedData = old.Clone(); _popupSystem.PopupEntity(Loc.GetString("botany-swab-to"), args.Args.Target.Value, args.Args.User); } diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index b56a2bb2a4c..7c1b4244f0a 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -79,26 +79,12 @@ public sealed partial class BotanySystem : EntitySystem public PlantTraitsComponent? GetPlantTraits(SeedData seed) { - foreach (var growthComponent in seed.GrowthComponents) - { - if (growthComponent is PlantTraitsComponent plantTraits) - { - return plantTraits; - } - } - return null; + return seed.GrowthComponents.PlantTraits; } public HarvestComponent? GetHarvestComponent(SeedData seed) { - foreach (var growthComponent in seed.GrowthComponents) - { - if (growthComponent is HarvestComponent harvestComp) - { - return harvestComp; - } - } - return null; + return seed.GrowthComponents.Harvest; } private void OnExamined(EntityUid uid, SeedComponent component, ExaminedEvent args) diff --git a/Content.Server/Botany/Systems/MutationSystem.cs b/Content.Server/Botany/Systems/MutationSystem.cs index d7210380469..c28ce393531 100644 --- a/Content.Server/Botany/Systems/MutationSystem.cs +++ b/Content.Server/Botany/Systems/MutationSystem.cs @@ -1,3 +1,4 @@ +using Content.Server.Botany.Components; using Content.Server.Botany.Systems; using Content.Shared.Atmos; using Content.Shared.EntityEffects; @@ -65,9 +66,12 @@ public sealed class MutationSystem : EntitySystem /// private void EnsureGrowthComponents(EntityUid plantHolder, SeedData seed) { - foreach (var component in seed.GrowthComponents) + // Fill missing components in the seed with defaults. + seed.GrowthComponents.EnsureGrowthComponents(); + + foreach (var prop in typeof(GrowthComponentsHolder).GetProperties()) { - if (!EntityManager.HasComponent(plantHolder, component.GetType())) + if (prop.GetValue(seed.GrowthComponents) is PlantGrowthComponent component && !EntityManager.HasComponent(plantHolder, component.GetType())) { var newComponent = component.DupeComponent(); EntityManager.AddComponent(plantHolder, newComponent); diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 87b1740f929..f1f7f1d5b0a 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -174,11 +174,11 @@ public sealed class PlantHolderSystem : EntitySystem private void OnInteractUsing(Entity entity, ref InteractUsingEvent args) { - var (uid, component) = entity; + var (uid, plantHolder) = entity; if (TryComp(args.Used, out SeedComponent? seeds)) { - if (component.Seed == null) + if (plantHolder.Seed == null) { if (!_botany.TryGetSeed(seeds, out var seed)) return; @@ -190,32 +190,38 @@ public sealed class PlantHolderSystem : EntitySystem ("seedName", name), ("seedNoun", noun)), args.User, PopupType.Medium); - component.Seed = seed.Clone(); - component.Dead = false; - component.Age = 1; - component.DrawWarnings = true; + plantHolder.Seed = seed.Clone(); + plantHolder.Dead = false; + plantHolder.Age = 1; + plantHolder.DrawWarnings = true; // Get endurance from seed's PlantTraitsComponent var seedTraits = _botany.GetPlantTraits(seed); if (seeds.HealthOverride != null) { - component.Health = seeds.HealthOverride.Value; + plantHolder.Health = seeds.HealthOverride.Value; } else if (seedTraits != null) { - component.Health = seedTraits.Endurance; + plantHolder.Health = seedTraits.Endurance; } - component.LastCycle = _gameTiming.CurTime; + plantHolder.LastCycle = _gameTiming.CurTime; // Ensure no existing growth components before adding new ones - var existingGrowthComponents = EntityManager.GetComponents(uid).ToList(); - foreach (var g in existingGrowthComponents) - EntityManager.RemoveComponent(uid, g); + RemoveAllGrowthComponents(uid); - foreach (var g in seed.GrowthComponents) - EntityManager.AddComponent(uid, _copier.CreateCopy(g, notNullableOverride: true), overwrite: true); + // Fill missing components with defaults + seed.GrowthComponents.EnsureGrowthComponents(); - EnsureDefaultGrowthComponents(uid); + foreach (var prop in typeof(GrowthComponentsHolder).GetProperties()) + { + if (prop.GetValue(seed.GrowthComponents) is PlantGrowthComponent growthComp) + { + EntityManager.AddComponent(uid, _copier.CreateCopy(growthComp, notNullableOverride: true), overwrite: true); + } + } + + EnsureComp(uid); if (TryComp(args.Used, out var paperLabel)) { @@ -223,8 +229,8 @@ public sealed class PlantHolderSystem : EntitySystem } QueueDel(args.Used); - CheckLevelSanity(uid, component); - UpdateSprite(uid, component); + CheckLevelSanity(uid, plantHolder); + UpdateSprite(uid, plantHolder); if (seed.PlantLogImpact != null) _adminLogger.Add(LogType.Botany, seed.PlantLogImpact.Value, $"{ToPrettyString(args.User):player} planted {Loc.GetString(seed.Name):seed} at Pos:{Transform(uid).Coordinates}."); @@ -241,14 +247,14 @@ public sealed class PlantHolderSystem : EntitySystem if (_tagSystem.HasTag(args.Used, HoeTag)) { args.Handled = true; - if (component.WeedLevel > 0) + if (plantHolder.WeedLevel > 0) { _popup.PopupCursor(Loc.GetString("plant-holder-component-remove-weeds-message", ("name", Comp(uid).EntityName)), args.User, PopupType.Medium); _popup.PopupEntity(Loc.GetString("plant-holder-component-remove-weeds-others-message", ("otherName", Comp(args.User).EntityName)), uid, Filter.PvsExcept(args.User), true); - component.WeedLevel = 0; - UpdateSprite(uid, component); + plantHolder.WeedLevel = 0; + UpdateSprite(uid, plantHolder); } else { @@ -261,13 +267,13 @@ public sealed class PlantHolderSystem : EntitySystem if (HasComp(args.Used)) { args.Handled = true; - if (component.Seed != null) + if (plantHolder.Seed != null) { _popup.PopupCursor(Loc.GetString("plant-holder-component-remove-plant-message", ("name", Comp(uid).EntityName)), args.User, PopupType.Medium); _popup.PopupEntity(Loc.GetString("plant-holder-component-remove-plant-others-message", ("name", Comp(args.User).EntityName)), uid, Filter.PvsExcept(args.User), true); - RemovePlant(uid, component); + RemovePlant(uid, plantHolder); } else { @@ -281,19 +287,19 @@ public sealed class PlantHolderSystem : EntitySystem if (_tagSystem.HasTag(args.Used, PlantSampleTakerTag)) { args.Handled = true; - if (component.Seed == null) + if (plantHolder.Seed == null) { _popup.PopupCursor(Loc.GetString("plant-holder-component-nothing-to-sample-message"), args.User); return; } - if (component.Sampled) + if (plantHolder.Sampled) { _popup.PopupCursor(Loc.GetString("plant-holder-component-already-sampled-message"), args.User); return; } - if (component.Dead) + if (plantHolder.Dead) { _popup.PopupCursor(Loc.GetString("plant-holder-component-dead-plant-message"), args.User); return; @@ -305,41 +311,46 @@ public sealed class PlantHolderSystem : EntitySystem return; } - component.Health -= _random.Next(3, 5) * 10; + plantHolder.Health -= _random.Next(3, 5) * 10; float? healthOverride; - if (component.Harvest) + if (plantHolder.Harvest) { healthOverride = null; } else { - healthOverride = component.Health; + healthOverride = plantHolder.Health; } - var packetSeed = component.Seed; + var packetSeed = plantHolder.Seed; if (packetSeed != null) { // Copy growth components from the plant to the seed before creating seed packet - var plantGrowthComponents = EntityManager.GetComponents(uid).ToList(); - packetSeed.GrowthComponents?.Clear(); - foreach (var growthComponent in plantGrowthComponents) + var holder = new GrowthComponentsHolder(); + + foreach (var prop in typeof(GrowthComponentsHolder).GetProperties()) { - var newComponent = growthComponent.DupeComponent(); - packetSeed.GrowthComponents?.Add(newComponent); + if (EntityManager.TryGetComponent(uid, prop.PropertyType, out var growthComponent)) + { + var copiedComponent = _copier.CreateCopy((Component)growthComponent, notNullableOverride: true); + prop.SetValue(holder, copiedComponent); + } } + packetSeed.GrowthComponents = holder; + var seed = _botany.SpawnSeedPacket(packetSeed, Transform(args.User).Coordinates, args.User, healthOverride); _randomHelper.RandomOffset(seed, 0.25f); - var displayName = Loc.GetString(component.Seed.DisplayName); + var displayName = Loc.GetString(plantHolder.Seed.DisplayName); _popup.PopupCursor(Loc.GetString("plant-holder-component-take-sample-message", ("seedName", displayName)), args.User); if (_random.Prob(0.3f)) - component.Sampled = true; + plantHolder.Sampled = true; - CheckLevelSanity(uid, component); - ForceUpdateByExternalCause(uid, component); + CheckLevelSanity(uid, plantHolder); + ForceUpdateByExternalCause(uid, plantHolder); } return; @@ -358,15 +369,15 @@ public sealed class PlantHolderSystem : EntitySystem if (_solutionContainerSystem.TryGetSolution(args.Used, produce.SolutionName, out var soln2, out var solution2)) { - if (_solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution, out var solution1)) + if (_solutionContainerSystem.ResolveSolution(uid, plantHolder.SoilSolutionName, ref plantHolder.SoilSolution, out var solution1)) { // We try to fit as much of the composted plant's contained solution into the hydroponics tray as we can, // since the plant will be consumed anyway. var fillAmount = FixedPoint2.Min(solution2.Volume, solution1.AvailableVolume); - _solutionContainerSystem.TryAddSolution(component.SoilSolution.Value, _solutionContainerSystem.SplitSolution(soln2.Value, fillAmount)); + _solutionContainerSystem.TryAddSolution(plantHolder.SoilSolution.Value, _solutionContainerSystem.SplitSolution(soln2.Value, fillAmount)); - ForceUpdateByExternalCause(uid, component); + ForceUpdateByExternalCause(uid, plantHolder); } } var seed = produce.Seed; @@ -376,7 +387,7 @@ public sealed class PlantHolderSystem : EntitySystem if (seedTraits != null) { var nutrientBonus = seedTraits.Potency / 2.5f; - AdjustNutrient(uid, nutrientBonus, component); + AdjustNutrient(uid, nutrientBonus, plantHolder); } } QueueDel(args.Used); @@ -505,9 +516,7 @@ public sealed class PlantHolderSystem : EntitySystem return; // Remove all growth components before planting new seed - var growthComponents = EntityManager.GetComponents(uid).ToList(); - foreach (var g in growthComponents) - EntityManager.RemoveComponent(uid, g); + RemoveAllGrowthComponents(uid); component.YieldMod = 1; component.MutationMod = 1; @@ -669,14 +678,13 @@ public sealed class PlantHolderSystem : EntitySystem } /// - /// Ensures that all default growth components are added to the plant if they're not already present. + /// Removes all growth-related components from a plant. /// - private void EnsureDefaultGrowthComponents(EntityUid uid) + private void RemoveAllGrowthComponents(EntityUid uid) { - EnsureComp(uid); - EnsureComp(uid); - EnsureComp(uid); - EnsureComp(uid); - EnsureComp(uid); + foreach (var comp in EntityManager.GetComponents(uid)) + { + RemComp(uid, comp); + } } } diff --git a/Resources/Maps/Salvage/vegan-meatball.yml b/Resources/Maps/Salvage/vegan-meatball.yml index 1d6e29ac3b3..48944cf21a7 100644 --- a/Resources/Maps/Salvage/vegan-meatball.yml +++ b/Resources/Maps/Salvage/vegan-meatball.yml @@ -486,22 +486,22 @@ entities: health: 100 seed: growthComponents: - - !type:PlantTraitsComponent - lifespan: 75 - endurance: 100 - - !type:BasicGrowthComponent - waterConsumption: 0.4 - nutrientConsumption: 0.75 - - !type:AtmosphericGrowthComponent - idealHeat: 298 - heatTolerance: 10 - lowPressureTolerance: 81 - highPressureTolerance: 121 - - !type:WeedPestGrowthComponent - weedTolerance: 5 - pestTolerance: 5 - - !type:ToxinsComponent - toxinsTolerance: 4 + plantTraits: + lifespan: 75 + endurance: 100 + basicGrowth: + waterConsumption: 0.4 + nutrientConsumption: 0.75 + atmosphericGrowth: + idealHeat: 298 + heatTolerance: 10 + lowPressureTolerance: 81 + highPressureTolerance: 121 + weedPestGrowth: + weedTolerance: 5 + pestTolerance: 5 + toxins: + toxinsTolerance: 4 chemicals: THC: Inherent: True @@ -559,22 +559,22 @@ entities: health: 100 seed: growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - endurance: 100 - - !type:BasicGrowthComponent - waterConsumption: 0.6 - nutrientConsumption: 0.75 - - !type:AtmosphericGrowthComponent - idealHeat: 293 - heatTolerance: 10 - lowPressureTolerance: 81 - highPressureTolerance: 121 - - !type:WeedPestGrowthComponent - weedTolerance: 5 - pestTolerance: 5 - - !type:ToxinsComponent - toxinsTolerance: 4 + plantTraits: + lifespan: 25 + endurance: 100 + basicGrowth: + waterConsumption: 0.6 + nutrientConsumption: 0.75 + atmosphericGrowth: + idealHeat: 293 + heatTolerance: 10 + lowPressureTolerance: 81 + highPressureTolerance: 121 + weedPestGrowth: + weedTolerance: 5 + pestTolerance: 5 + toxins: + toxinsTolerance: 4 chemicals: Stellibinin: Inherent: True diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index 840b8c8c178..e663a79fc09 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -10,14 +10,14 @@ mutationPrototypes: - meatwheat growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 6 - production: 3 - yield: 3 - potency: 5 - - !type:BasicGrowthComponent - nutrientConsumption: 0.4 + plantTraits: + lifespan: 25 + maturation: 6 + production: 3 + yield: 3 + potency: 5 + basicGrowth: + nutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -38,14 +38,14 @@ productPrototypes: - MeatwheatBushel growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 6 - production: 3 - yield: 3 - potency: 5 - - !type:BasicGrowthComponent - nutrientConsumption: 0.4 + plantTraits: + lifespan: 25 + maturation: 6 + production: 3 + yield: 3 + potency: 5 + basicGrowth: + nutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -66,14 +66,14 @@ productPrototypes: - OatBushel growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 6 - production: 3 - yield: 3 - potency: 5 - - !type:BasicGrowthComponent - nutrientConsumption: 0.4 + plantTraits: + lifespan: 25 + maturation: 6 + production: 3 + yield: 3 + potency: 5 + basicGrowth: + nutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -96,17 +96,17 @@ mutationPrototypes: - mimana growthComponents: - - !type:PlantTraitsComponent - lifespan: 50 - maturation: 6 - production: 6 - yield: 2 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 50 + maturation: 6 + production: 6 + yield: 2 + harvest: + harvestRepeat: Repeat + basicGrowth: + waterConsumption: 0.6 + atmosphericGrowth: + idealHeat: 298 chemicals: Vitamin: Min: 1 @@ -127,17 +127,17 @@ productPrototypes: - FoodMimana growthComponents: - - !type:PlantTraitsComponent - lifespan: 50 - maturation: 6 - production: 6 - yield: 2 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 50 + maturation: 6 + production: 6 + yield: 2 + harvest: + harvestRepeat: Repeat + basicGrowth: + waterConsumption: 0.6 + atmosphericGrowth: + idealHeat: 298 chemicals: MuteToxin: Min: 1 @@ -158,15 +158,15 @@ productPrototypes: - FoodCarrot growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 3 - - !type:BasicGrowthComponent - waterConsumption: 0.6 + plantTraits: + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 3 + basicGrowth: + waterConsumption: 0.6 chemicals: JuiceCarrot: Min: 1 @@ -193,18 +193,18 @@ mutationPrototypes: - worldPea growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - growthStages: 3 - maturation: 7 - production: 5 - yield: 3 - potency: 20 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - waterConsumption: 0.6 - nutrientConsumption: 0.6 + plantTraits: + lifespan: 25 + growthStages: 3 + maturation: 7 + production: 5 + yield: 3 + potency: 20 + harvest: + harvestRepeat: Repeat + basicGrowth: + waterConsumption: 0.6 + nutrientConsumption: 0.6 chemicals: Nutriment: Min: 1 @@ -233,14 +233,14 @@ - lime - orange growthComponents: - - !type:PlantTraitsComponent - lifespan: 55 - maturation: 6 - production: 6 - yield: 3 - potency: 10 - - !type:HarvestComponent - harvestRepeat: Repeat + plantTraits: + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + harvest: + harvestRepeat: Repeat chemicals: Nutriment: Min: 1 @@ -259,14 +259,14 @@ plantRsi: Objects/Specific/Hydroponics/lemoon.rsi packetPrototype: LemoonSeeds growthComponents: - - !type:PlantTraitsComponent - lifespan: 90 - maturation: 8 - production: 6 - yield: 4 - potency: 1 - - !type:HarvestComponent - harvestRepeat: Repeat + plantTraits: + lifespan: 90 + maturation: 8 + production: 6 + yield: 4 + potency: 1 + harvest: + harvestRepeat: Repeat productPrototypes: - FoodLemoon chemicals: @@ -292,14 +292,14 @@ - orange - lemon growthComponents: - - !type:PlantTraitsComponent - lifespan: 55 - maturation: 6 - production: 6 - yield: 3 - potency: 10 - - !type:HarvestComponent - harvestRepeat: Repeat + plantTraits: + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + harvest: + harvestRepeat: Repeat chemicals: Nutriment: Min: 1 @@ -324,14 +324,14 @@ - lemon - lime growthComponents: - - !type:PlantTraitsComponent - lifespan: 55 - maturation: 6 - production: 6 - yield: 3 - potency: 10 - - !type:HarvestComponent - harvestRepeat: Repeat + plantTraits: + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + harvest: + harvestRepeat: Repeat chemicals: Nutriment: Min: 1 @@ -352,14 +352,14 @@ productPrototypes: - FoodExtradimensionalOrange growthComponents: - - !type:PlantTraitsComponent - lifespan: 55 - maturation: 6 - production: 6 - yield: 3 - potency: 10 - - !type:HarvestComponent - harvestRepeat: Repeat + plantTraits: + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + harvest: + harvestRepeat: Repeat chemicals: Haloperidol: Min: 1 @@ -384,15 +384,15 @@ productPrototypes: - FoodPineapple growthComponents: - - !type:PlantTraitsComponent - lifespan: 55 - maturation: 6 - production: 6 - yield: 3 - potency: 10 - growthStages: 3 - - !type:HarvestComponent - harvestRepeat: Repeat + plantTraits: + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + growthStages: 3 + harvest: + harvestRepeat: Repeat chemicals: Nutriment: Min: 1 @@ -417,15 +417,15 @@ productPrototypes: - FoodPotato growthComponents: - - !type:PlantTraitsComponent - lifespan: 30 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 4 - - !type:BasicGrowthComponent - waterConsumption: 0.6 + plantTraits: + lifespan: 30 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 4 + basicGrowth: + waterConsumption: 0.6 chemicals: Nutriment: Min: 1 @@ -448,17 +448,17 @@ mutationPrototypes: - papercane growthComponents: - - !type:PlantTraitsComponent - lifespan: 60 - maturation: 6 - production: 6 - yield: 3 - potency: 10 - growthStages: 3 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 60 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + growthStages: 3 + harvest: + harvestRepeat: Repeat + atmosphericGrowth: + idealHeat: 298 chemicals: Sugar: Min: 4 @@ -475,19 +475,19 @@ productPrototypes: - LeavesTea growthComponents: - - !type:PlantTraitsComponent - lifespan: 75 - maturation: 5 - production: 3 - yield: 2 - potency: 20 - growthStages: 5 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 75 + maturation: 5 + production: 3 + yield: 2 + potency: 20 + growthStages: 5 + harvest: + harvestRepeat: Repeat + basicGrowth: + waterConsumption: 0.6 + atmosphericGrowth: + idealHeat: 298 chemicals: Vitamin: Min: 1 @@ -504,17 +504,17 @@ productPrototypes: - Papercane growthComponents: - - !type:PlantTraitsComponent - lifespan: 60 - maturation: 6 - production: 6 - yield: 3 - potency: 10 - growthStages: 3 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 60 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + growthStages: 3 + harvest: + harvestRepeat: Repeat + atmosphericGrowth: + idealHeat: 298 - type: seed id: towercap @@ -528,19 +528,19 @@ mutationPrototypes: - steelcap growthComponents: - - !type:PlantTraitsComponent - ligneous: true - lifespan: 80 - maturation: 15 - production: 3 - yield: 5 - potency: 1 - growthStages: 3 - - !type:BasicGrowthComponent - waterConsumption: 0.6 - nutrientConsumption: 0.5 - - !type:AtmosphericGrowthComponent - idealHeat: 288 + plantTraits: + ligneous: true + lifespan: 80 + maturation: 15 + production: 3 + yield: 5 + potency: 1 + growthStages: 3 + basicGrowth: + waterConsumption: 0.6 + nutrientConsumption: 0.5 + atmosphericGrowth: + idealHeat: 288 - type: seed id: steelcap @@ -552,19 +552,19 @@ productPrototypes: - SteelLog growthComponents: - - !type:PlantTraitsComponent - ligneous: true - lifespan: 80 - maturation: 15 - production: 3 - yield: 3 - potency: 1 - growthStages: 3 - - !type:BasicGrowthComponent - waterConsumption: 0.6 - nutrientConsumption: 0.8 - - !type:AtmosphericGrowthComponent - idealHeat: 288 + plantTraits: + ligneous: true + lifespan: 80 + maturation: 15 + production: 3 + yield: 3 + potency: 1 + growthStages: 3 + basicGrowth: + waterConsumption: 0.6 + nutrientConsumption: 0.8 + atmosphericGrowth: + idealHeat: 288 - type: seed id: tomato @@ -579,19 +579,19 @@ - blueTomato - bloodTomato growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 8 - production: 6 - yield: 2 - potency: 10 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - waterConsumption: 0.6 - nutrientConsumption: 0.4 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 25 + maturation: 8 + production: 6 + yield: 2 + potency: 10 + harvest: + harvestRepeat: Repeat + basicGrowth: + waterConsumption: 0.6 + nutrientConsumption: 0.4 + atmosphericGrowth: + idealHeat: 298 chemicals: Nutriment: Min: 1 @@ -616,19 +616,19 @@ productPrototypes: - FoodBlueTomato growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 8 - production: 6 - yield: 2 - potency: 10 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - waterConsumption: 0.6 - nutrientConsumption: 0.7 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 25 + maturation: 8 + production: 6 + yield: 2 + potency: 10 + harvest: + harvestRepeat: Repeat + basicGrowth: + waterConsumption: 0.6 + nutrientConsumption: 0.7 + atmosphericGrowth: + idealHeat: 298 chemicals: Nutriment: Min: 1 @@ -655,19 +655,19 @@ mutationPrototypes: - killerTomato growthComponents: - - !type:PlantTraitsComponent - lifespan: 60 - maturation: 8 - production: 6 - yield: 2 - potency: 10 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - waterConsumption: 0.6 - nutrientConsumption: 0.7 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 60 + maturation: 8 + production: 6 + yield: 2 + potency: 10 + harvest: + harvestRepeat: Repeat + basicGrowth: + waterConsumption: 0.6 + nutrientConsumption: 0.7 + atmosphericGrowth: + idealHeat: 298 chemicals: Blood: Min: 1 @@ -690,20 +690,20 @@ productPrototypes: - MobTomatoKiller growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 15 - production: 6 - yield: 2 - potency: 10 - growthStages: 2 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - waterConsumption: 0.6 - nutrientConsumption: 0.7 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 25 + maturation: 15 + production: 6 + yield: 2 + potency: 10 + growthStages: 2 + harvest: + harvestRepeat: Repeat + basicGrowth: + waterConsumption: 0.6 + nutrientConsumption: 0.7 + atmosphericGrowth: + idealHeat: 298 chemicals: Blood: Min: 1 @@ -726,17 +726,17 @@ mutationPrototypes: - eggy growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 6 - production: 6 - yield: 2 - potency: 20 - growthStages: 3 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 25 + maturation: 6 + production: 6 + yield: 2 + potency: 20 + growthStages: 3 + harvest: + harvestRepeat: Repeat + atmosphericGrowth: + idealHeat: 298 chemicals: Nutriment: Min: 1 @@ -757,13 +757,13 @@ productPrototypes: - FoodCabbage growthComponents: - - !type:PlantTraitsComponent - lifespan: 50 - maturation: 7 - production: 5 - yield: 3 - potency: 10 - growthStages: 1 + plantTraits: + lifespan: 50 + maturation: 7 + production: 5 + yield: 3 + potency: 10 + growthStages: 1 chemicals: Nutriment: Min: 1 @@ -784,13 +784,13 @@ productPrototypes: - FoodGarlic growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 8 - production: 5 - yield: 3 - potency: 25 - growthStages: 3 + plantTraits: + lifespan: 25 + maturation: 8 + production: 5 + yield: 3 + potency: 25 + growthStages: 3 chemicals: Nutriment: Min: 1 @@ -817,14 +817,14 @@ mutationPrototypes: - goldenApple growthComponents: - - !type:PlantTraitsComponent - lifespan: 55 - maturation: 6 - production: 6 - yield: 3 - potency: 10 - - !type:HarvestComponent - harvestRepeat: Repeat + plantTraits: + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + harvest: + harvestRepeat: Repeat chemicals: Nutriment: Min: 1 @@ -845,16 +845,16 @@ productPrototypes: - FoodGoldenApple growthComponents: - - !type:PlantTraitsComponent - lifespan: 55 - maturation: 6 - production: 6 - yield: 3 - potency: 10 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - waterConsumption: 0.75 + plantTraits: + lifespan: 55 + maturation: 6 + production: 6 + yield: 3 + potency: 10 + harvest: + harvestRepeat: Repeat + basicGrowth: + waterConsumption: 0.75 chemicals: Nutriment: Min: 1 @@ -879,17 +879,17 @@ productPrototypes: - FoodCorn growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 8 - production: 6 - yield: 2 - potency: 20 - growthStages: 3 - - !type:BasicGrowthComponent - waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 25 + maturation: 8 + production: 6 + yield: 2 + potency: 20 + growthStages: 3 + basicGrowth: + waterConsumption: 0.6 + atmosphericGrowth: + idealHeat: 298 chemicals: Nutriment: Min: 1 @@ -913,17 +913,17 @@ - onionred - bloonion growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 8 - production: 6 - yield: 2 - potency: 20 - growthStages: 3 - - !type:BasicGrowthComponent - waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 25 + maturation: 8 + production: 6 + yield: 2 + potency: 20 + growthStages: 3 + basicGrowth: + waterConsumption: 0.6 + atmosphericGrowth: + idealHeat: 298 chemicals: Nutriment: Min: 1 @@ -948,17 +948,17 @@ productPrototypes: - FoodOnionRed growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 8 - production: 6 - yield: 2 - potency: 20 - growthStages: 3 - - !type:BasicGrowthComponent - waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 25 + maturation: 8 + production: 6 + yield: 2 + potency: 20 + growthStages: 3 + basicGrowth: + waterConsumption: 0.6 + atmosphericGrowth: + idealHeat: 298 chemicals: Nutriment: Min: 1 @@ -983,18 +983,18 @@ productPrototypes: - FoodMushroom growthComponents: - - !type:PlantTraitsComponent - lifespan: 35 - maturation: 10 - production: 7 - yield: 5 - potency: 1 - growthStages: 3 - - !type:BasicGrowthComponent - waterConsumption: 0.6 - nutrientConsumption: 0.5 - - !type:AtmosphericGrowthComponent - idealHeat: 288 + plantTraits: + lifespan: 35 + maturation: 10 + production: 7 + yield: 5 + potency: 1 + growthStages: 3 + basicGrowth: + waterConsumption: 0.6 + nutrientConsumption: 0.5 + atmosphericGrowth: + idealHeat: 288 chemicals: Nutriment: Min: 1 @@ -1011,18 +1011,18 @@ productPrototypes: - FoodEgg growthComponents: - - !type:PlantTraitsComponent - lifespan: 75 - maturation: 6 - production: 12 - yield: 2 - potency: 20 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - nutrientConsumption: 0.5 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 75 + maturation: 6 + production: 12 + yield: 2 + potency: 20 + harvest: + harvestRepeat: Repeat + basicGrowth: + nutrientConsumption: 0.5 + atmosphericGrowth: + idealHeat: 298 chemicals: Egg: Min: 4 @@ -1041,19 +1041,19 @@ mutationPrototypes: - rainbowCannabis growthComponents: - - !type:PlantTraitsComponent - lifespan: 75 - maturation: 8 - production: 12 - yield: 2 - potency: 20 - growthStages: 3 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - waterConsumption: 0.4 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 75 + maturation: 8 + production: 12 + yield: 2 + potency: 20 + growthStages: 3 + harvest: + harvestRepeat: Repeat + basicGrowth: + waterConsumption: 0.4 + atmosphericGrowth: + idealHeat: 298 chemicals: THC: Min: 1 @@ -1070,19 +1070,19 @@ productPrototypes: - LeavesCannabisRainbow growthComponents: - - !type:PlantTraitsComponent - lifespan: 75 - maturation: 8 - production: 12 - yield: 2 - potency: 20 - growthStages: 3 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - waterConsumption: 0.4 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 75 + maturation: 8 + production: 12 + yield: 2 + potency: 20 + growthStages: 3 + harvest: + harvestRepeat: Repeat + basicGrowth: + waterConsumption: 0.4 + atmosphericGrowth: + idealHeat: 298 chemicals: SpaceDrugs: Min: 1 @@ -1119,19 +1119,19 @@ productPrototypes: - LeavesTobacco growthComponents: - - !type:PlantTraitsComponent - lifespan: 75 - maturation: 5 - production: 5 - yield: 2 - potency: 20 - growthStages: 3 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - waterConsumption: 0.4 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 75 + maturation: 5 + production: 5 + yield: 2 + potency: 20 + growthStages: 3 + harvest: + harvestRepeat: Repeat + basicGrowth: + waterConsumption: 0.4 + atmosphericGrowth: + idealHeat: 298 chemicals: Nicotine: Min: 1 @@ -1150,17 +1150,17 @@ mutationPrototypes: - deathNettle growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 8 - production: 6 - yield: 2 - potency: 20 - growthStages: 5 - - !type:BasicGrowthComponent - waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 25 + maturation: 8 + production: 6 + yield: 2 + potency: 20 + growthStages: 5 + basicGrowth: + waterConsumption: 0.6 + atmosphericGrowth: + idealHeat: 298 chemicals: Histamine: Min: 1 @@ -1179,18 +1179,18 @@ productPrototypes: - DeathNettle growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 8 - production: 6 - yield: 2 - potency: 20 - growthStages: 5 - - !type:BasicGrowthComponent - waterConsumption: 0.7 - nutrientConsumption: 0.8 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 25 + maturation: 8 + production: 6 + yield: 2 + potency: 20 + growthStages: 5 + basicGrowth: + waterConsumption: 0.7 + nutrientConsumption: 0.8 + atmosphericGrowth: + idealHeat: 298 chemicals: SulfuricAcid: Min: 1 @@ -1213,16 +1213,16 @@ mutationPrototypes: - chilly growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 6 - production: 6 - yield: 2 - potency: 20 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 25 + maturation: 6 + production: 6 + yield: 2 + potency: 20 + harvest: + harvestRepeat: Repeat + atmosphericGrowth: + idealHeat: 298 chemicals: CapsaicinOil: Min: 1 @@ -1247,16 +1247,16 @@ productPrototypes: - FoodChillyPepper growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 6 - production: 6 - yield: 2 - potency: 20 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 25 + maturation: 6 + production: 6 + yield: 2 + potency: 20 + harvest: + harvestRepeat: Repeat + atmosphericGrowth: + idealHeat: 298 chemicals: FrostOil: Min: 1 @@ -1283,15 +1283,15 @@ mutationPrototypes: - lily growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 3 - - !type:BasicGrowthComponent - waterConsumption: 0.6 + plantTraits: + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 3 + basicGrowth: + waterConsumption: 0.6 chemicals: Nutriment: Min: 1 @@ -1312,15 +1312,15 @@ productPrototypes: - FoodAloe growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 5 - - !type:BasicGrowthComponent - waterConsumption: 0.6 + plantTraits: + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 5 + basicGrowth: + waterConsumption: 0.6 chemicals: Aloe: Min: 1 @@ -1343,15 +1343,15 @@ mutationPrototypes: - spacemansTrumpet growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 3 - - !type:BasicGrowthComponent - waterConsumption: 0.6 + plantTraits: + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 3 + basicGrowth: + waterConsumption: 0.6 chemicals: Nutriment: Min: 1 @@ -1372,15 +1372,15 @@ productPrototypes: - FoodLingzhi growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 3 - - !type:BasicGrowthComponent - waterConsumption: 0.6 + plantTraits: + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 3 + basicGrowth: + waterConsumption: 0.6 chemicals: Ultravasculine: Min: 1 @@ -1403,15 +1403,15 @@ mutationPrototypes: - ambrosiaDeus growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 6 - - !type:BasicGrowthComponent - waterConsumption: 0.6 + plantTraits: + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 6 + basicGrowth: + waterConsumption: 0.6 chemicals: Nutriment: Min: 1 @@ -1444,15 +1444,15 @@ productPrototypes: - FoodAmbrosiaDeus growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 6 - - !type:BasicGrowthComponent - waterConsumption: 0.6 + plantTraits: + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 6 + basicGrowth: + waterConsumption: 0.6 chemicals: Nutriment: Min: 1 @@ -1483,15 +1483,15 @@ mutationPrototypes: - glasstle growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 3 - - !type:BasicGrowthComponent - waterConsumption: 0.6 + plantTraits: + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 3 + basicGrowth: + waterConsumption: 0.6 chemicals: Stellibinin: Min: 1 @@ -1508,13 +1508,13 @@ productPrototypes: - FoodGlasstle growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 10 - production: 3 - yield: 3 - potency: 10 - growthStages: 3 + plantTraits: + lifespan: 25 + maturation: 10 + production: 3 + yield: 3 + potency: 10 + growthStages: 3 chemicals: Razorium: Min: 1 @@ -1531,16 +1531,16 @@ productPrototypes: - FoodFlyAmanita growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 12 - production: 3 - yield: 3 - potency: 10 - growthStages: 2 - - !type:BasicGrowthComponent - waterConsumption: 0.6 - nutrientConsumption: 0.5 + plantTraits: + lifespan: 25 + maturation: 12 + production: 3 + yield: 3 + potency: 10 + growthStages: 2 + basicGrowth: + waterConsumption: 0.6 + nutrientConsumption: 0.5 chemicals: Amatoxin: Min: 1 @@ -1564,13 +1564,13 @@ - fakeCapfruit - realCapfruit growthComponents: - - !type:PlantTraitsComponent - lifespan: 65 - maturation: 25 - production: 25 - yield: 1 - potency: 10 - growthStages: 2 + plantTraits: + lifespan: 65 + maturation: 25 + production: 25 + yield: 1 + potency: 10 + growthStages: 2 chemicals: Nutriment: Min: 1 @@ -1591,13 +1591,13 @@ productPrototypes: - FoodFakeCapfruit growthComponents: - - !type:PlantTraitsComponent - lifespan: 65 - maturation: 25 - production: 25 - yield: 1 - potency: 10 - growthStages: 2 + plantTraits: + lifespan: 65 + maturation: 25 + production: 25 + yield: 1 + potency: 10 + growthStages: 2 chemicals: Nutriment: Min: 1 @@ -1618,13 +1618,13 @@ productPrototypes: - FoodRealCapfruit growthComponents: - - !type:PlantTraitsComponent - lifespan: 65 - maturation: 25 - production: 25 - yield: 1 - potency: 10 - growthStages: 2 + plantTraits: + lifespan: 65 + maturation: 25 + production: 25 + yield: 1 + potency: 10 + growthStages: 2 chemicals: Nutriment: Min: 1 @@ -1645,16 +1645,16 @@ productPrototypes: - RiceBushel growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 6 - production: 3 - yield: 3 - potency: 5 - growthStages: 4 - - !type:BasicGrowthComponent - waterConsumption: 0.6 - nutrientConsumption: 0.4 + plantTraits: + lifespan: 25 + maturation: 6 + production: 3 + yield: 3 + potency: 5 + growthStages: 4 + basicGrowth: + waterConsumption: 0.6 + nutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -1677,15 +1677,15 @@ mutationPrototypes: - koibean growthComponents: - - !type:PlantTraitsComponent - growthStages: 4 - lifespan: 25 - maturation: 6 - production: 6 - yield: 3 - potency: 5 - - !type:BasicGrowthComponent - nutrientConsumption: 0.4 + plantTraits: + growthStages: 4 + lifespan: 25 + maturation: 6 + production: 6 + yield: 3 + potency: 5 + basicGrowth: + nutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -1702,15 +1702,15 @@ productPrototypes: - FoodSpacemansTrumpet growthComponents: - - !type:PlantTraitsComponent - growthStages: 4 - lifespan: 20 - maturation: 14 - production: 3 - yield: 2 - potency: 10 - - !type:BasicGrowthComponent - waterConsumption: 0.6 + plantTraits: + growthStages: 4 + lifespan: 20 + maturation: 14 + production: 3 + yield: 2 + potency: 10 + basicGrowth: + waterConsumption: 0.6 chemicals: Nutriment: Min: 1 @@ -1731,15 +1731,15 @@ productPrototypes: - FoodKoibean growthComponents: - - !type:PlantTraitsComponent - growthStages: 4 - lifespan: 25 - maturation: 6 - production: 6 - yield: 3 - potency: 5 - - !type:BasicGrowthComponent - nutrientConsumption: 0.4 + plantTraits: + growthStages: 4 + lifespan: 25 + maturation: 6 + production: 6 + yield: 3 + potency: 5 + basicGrowth: + nutrientConsumption: 0.4 chemicals: Nutriment: Min: 1 @@ -1760,13 +1760,13 @@ productPrototypes: - FoodGrape growthComponents: - - !type:PlantTraitsComponent - lifespan: 50 - maturation: 6 - production: 5 - yield: 3 - potency: 10 - growthStages: 2 + plantTraits: + lifespan: 50 + maturation: 6 + production: 5 + yield: 3 + potency: 10 + growthStages: 2 chemicals: Nutriment: Min: 1 @@ -1789,12 +1789,12 @@ mutationPrototypes: - holymelon growthComponents: - - !type:PlantTraitsComponent - lifespan: 55 - maturation: 12 - production: 3 - yield: 1 - potency: 1 + plantTraits: + lifespan: 55 + maturation: 12 + production: 3 + yield: 1 + potency: 1 chemicals: Nutriment: Min: 1 @@ -1819,12 +1819,12 @@ productPrototypes: - FoodHolymelon growthComponents: - - !type:PlantTraitsComponent - lifespan: 55 - maturation: 12 - production: 3 - yield: 1 - potency: 1 + plantTraits: + lifespan: 55 + maturation: 12 + production: 3 + yield: 1 + potency: 1 chemicals: Nutriment: Min: 1 @@ -1849,18 +1849,18 @@ productPrototypes: - FoodCocoaPod growthComponents: - - !type:PlantTraitsComponent - lifespan: 50 - maturation: 6 - production: 6 - yield: 6 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - waterConsumption: 1.0 - nutrientConsumption: 0.8 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 50 + maturation: 6 + production: 6 + yield: 6 + harvest: + harvestRepeat: Repeat + basicGrowth: + waterConsumption: 1.0 + nutrientConsumption: 0.8 + atmosphericGrowth: + idealHeat: 298 chemicals: Vitamin: Min: 1 @@ -1881,15 +1881,15 @@ productPrototypes: - FoodBerries growthComponents: - - !type:PlantTraitsComponent - lifespan: 50 - maturation: 6 - production: 6 - yield: 4 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - nutrientConsumption: 0.6 + plantTraits: + lifespan: 50 + maturation: 6 + production: 6 + yield: 4 + harvest: + harvestRepeat: Repeat + basicGrowth: + nutrientConsumption: 0.6 chemicals: Nutriment: Min: 2 @@ -1910,19 +1910,19 @@ productPrototypes: - FoodBungo growthComponents: - - !type:PlantTraitsComponent - lifespan: 50 - maturation: 8 - production: 6 - potency: 10 - yield: 3 - growthStages: 4 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - waterConsumption: 0.6 - - !type:AtmosphericGrowthComponent - idealHeat: 298 + plantTraits: + lifespan: 50 + maturation: 8 + production: 6 + potency: 10 + yield: 3 + growthStages: 4 + harvest: + harvestRepeat: Repeat + basicGrowth: + waterConsumption: 0.6 + atmosphericGrowth: + idealHeat: 298 chemicals: Nutriment: Min: 5 @@ -1945,17 +1945,17 @@ mutationPrototypes: - laughinPea growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - growthStages: 3 - maturation: 8 - production: 6 - yield: 3 - potency: 25 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - nutrientConsumption: 0.5 + plantTraits: + lifespan: 25 + growthStages: 3 + maturation: 8 + production: 6 + yield: 3 + potency: 25 + harvest: + harvestRepeat: Repeat + basicGrowth: + nutrientConsumption: 0.5 chemicals: Nutriment: Min: 1 @@ -1976,17 +1976,17 @@ productPrototypes: - FoodWorldPeas growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - growthStages: 3 - maturation: 20 - production: 6 - yield: 3 - potency: 25 - - !type:HarvestComponent - harvestRepeat: Repeat - - !type:BasicGrowthComponent - nutrientConsumption: 0.5 + plantTraits: + lifespan: 25 + growthStages: 3 + maturation: 20 + production: 6 + yield: 3 + potency: 25 + harvest: + harvestRepeat: Repeat + basicGrowth: + nutrientConsumption: 0.5 chemicals: Happiness: Min: 1 @@ -2013,15 +2013,15 @@ mutationPrototypes: - bluePumpkin growthComponents: - - !type:PlantTraitsComponent - lifespan: 55 - maturation: 10 - production: 4 - yield: 2 - potency: 10 - growthStages: 3 - - !type:AtmosphericGrowthComponent - idealHeat: 288 + plantTraits: + lifespan: 55 + maturation: 10 + production: 4 + yield: 2 + potency: 10 + growthStages: 3 + atmosphericGrowth: + idealHeat: 288 chemicals: PumpkinFlesh: Min: 1 @@ -2042,15 +2042,15 @@ productPrototypes: - FoodBluePumpkin growthComponents: - - !type:PlantTraitsComponent - lifespan: 55 - maturation: 10 - production: 4 - yield: 2 - potency: 10 - growthStages: 3 - - !type:AtmosphericGrowthComponent - idealHeat: 288 + plantTraits: + lifespan: 55 + maturation: 10 + production: 4 + yield: 2 + potency: 10 + growthStages: 3 + atmosphericGrowth: + idealHeat: 288 chemicals: Ammonia: Min: 1 @@ -2077,15 +2077,15 @@ mutationPrototypes: - pyrotton growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 8 - production: 3 - yield: 3 - potency: 5 - growthStages: 3 - - !type:BasicGrowthComponent - waterConsumption: 0.6 + plantTraits: + lifespan: 25 + maturation: 8 + production: 3 + yield: 3 + potency: 5 + growthStages: 3 + basicGrowth: + waterConsumption: 0.6 chemicals: Fiber: Min: 5 @@ -2102,15 +2102,15 @@ productPrototypes: - PyrottonBol growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 8 - production: 3 - yield: 2 - potency: 5 - growthStages: 3 - - !type:BasicGrowthComponent - waterConsumption: 0.8 + plantTraits: + lifespan: 25 + maturation: 8 + production: 3 + yield: 2 + potency: 5 + growthStages: 3 + basicGrowth: + waterConsumption: 0.8 chemicals: Fiber: Min: 5 @@ -2131,14 +2131,14 @@ productPrototypes: - FoodCherry growthComponents: - - !type:PlantTraitsComponent - lifespan: 55 - maturation: 6 - production: 6 - yield: 5 - potency: 10 - - !type:HarvestComponent - harvestRepeat: Repeat + plantTraits: + lifespan: 55 + maturation: 6 + production: 6 + yield: 5 + potency: 10 + harvest: + harvestRepeat: Repeat chemicals: Nutriment: Min: 1 @@ -2159,16 +2159,16 @@ productPrototypes: - FoodAnomalyBerry growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 12 - production: 3 - yield: 3 - potency: 10 - growthStages: 2 - - !type:BasicGrowthComponent - waterConsumption: 0.6 - nutrientConsumption: 0.5 + plantTraits: + lifespan: 25 + maturation: 12 + production: 3 + yield: 3 + potency: 10 + growthStages: 2 + basicGrowth: + waterConsumption: 0.6 + nutrientConsumption: 0.5 chemicals: Artifexium: Min: 1 @@ -2193,16 +2193,16 @@ productPrototypes: - FoodBloonion growthComponents: - - !type:PlantTraitsComponent - lifespan: 25 - maturation: 15 - production: 3 - yield: 3 - potency: 10 - growthStages: 4 - - !type:BasicGrowthComponent - waterConsumption: 0.6 - nutrientConsumption: 0.5 + plantTraits: + lifespan: 25 + maturation: 15 + production: 3 + yield: 3 + potency: 10 + growthStages: 4 + basicGrowth: + waterConsumption: 0.6 + nutrientConsumption: 0.5 chemicals: Potassium: Min: 1 From 5748b1fe010ae55ff28b9ac352ac4979ca831209 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Mon, 13 Oct 2025 15:23:07 +0300 Subject: [PATCH 30/53] merge update --- .../Components/GrowthComponentsHolder.cs | 16 ++-- .../Botany/Components/SeedComponent.cs | 4 +- .../Botany/Systems/HarvestSystem.cs | 10 +-- .../Botany/Systems/PlantHolderSystem.cs | 9 +- .../PlantAdjustPotencyEntityEffectSystem.cs | 6 +- .../PlantAffectGrowthEntityEffectSystem.cs | 4 +- .../PlantChangeStatEntityEffectSystem.cs | 82 +++++++++++-------- .../PlantCryoxadoneEntityEffectSystem.cs | 10 +-- .../PlantDestroySeedsEntityEffectSystem.cs | 7 +- .../PlantDiethylamineEntityEffectSystem.cs | 14 +--- .../PlantPhalanximineEntityEffectSystem.cs | 3 +- .../PlantRestoreSeedsEntityEffectSystem.cs | 4 +- .../RobustHarvestEntityEffectSystem.cs | 14 ++-- .../PlantMutateGasesEntityEffectSystem.cs | 14 ++-- .../PlantMutateHarvestEntityEffectSystem.cs | 9 +- .../PlantMetabolism/PlantPhalanximine.cs | 23 ------ .../Botany/PlantMutateGasesEntityEffect.cs | 18 ++++ .../guidebook/entity-effects/effects.ftl | 1 - 18 files changed, 123 insertions(+), 125 deletions(-) delete mode 100644 Content.Server/EntityEffects/Effects/PlantMetabolism/PlantPhalanximine.cs diff --git a/Content.Server/Botany/Components/GrowthComponentsHolder.cs b/Content.Server/Botany/Components/GrowthComponentsHolder.cs index 3bbe7ee15e6..af3d4684823 100644 --- a/Content.Server/Botany/Components/GrowthComponentsHolder.cs +++ b/Content.Server/Botany/Components/GrowthComponentsHolder.cs @@ -7,28 +7,28 @@ namespace Content.Server.Botany.Components; [DataDefinition] public sealed partial class GrowthComponentsHolder { - [DataField("plantTraits")] + [DataField] public PlantTraitsComponent? PlantTraits { get; set; } - [DataField("basicGrowth")] + [DataField] public BasicGrowthComponent? BasicGrowth { get; set; } - [DataField("toxins")] + [DataField] public ToxinsComponent? Toxins { get; set; } - [DataField("harvest")] + [DataField] public HarvestComponent? Harvest { get; set; } - [DataField("atmosphericGrowth")] + [DataField] public AtmosphericGrowthComponent? AtmosphericGrowth { get; set; } - [DataField("consumeExudeGasGrowth")] + [DataField] public ConsumeExudeGasGrowthComponent? ConsumeExudeGasGrowth { get; set; } - [DataField("weedPestGrowth")] + [DataField] public WeedPestGrowthComponent? WeedPestGrowth { get; set; } - [DataField("unviableGrowth")] + [DataField] public UnviableGrowthComponent? UnviableGrowth { get; set; } /// diff --git a/Content.Server/Botany/Components/SeedComponent.cs b/Content.Server/Botany/Components/SeedComponent.cs index b56361146bb..a979741da3d 100644 --- a/Content.Server/Botany/Components/SeedComponent.cs +++ b/Content.Server/Botany/Components/SeedComponent.cs @@ -12,7 +12,7 @@ public sealed partial class SeedComponent : SharedSeedComponent /// null, will instead attempt to get data from a seed prototype, if one is defined. See . /// - [DataField("seed")] + [DataField] public SeedData? Seed; /// @@ -24,6 +24,6 @@ public sealed partial class SeedComponent : SharedSeedComponent /// /// Name of a base seed prototype that is used if is null. /// - [DataField("seedId", customTypeSerializer: typeof(PrototypeIdSerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string? SeedId; } diff --git a/Content.Server/Botany/Systems/HarvestSystem.cs b/Content.Server/Botany/Systems/HarvestSystem.cs index 271efe6a204..9296defe694 100644 --- a/Content.Server/Botany/Systems/HarvestSystem.cs +++ b/Content.Server/Botany/Systems/HarvestSystem.cs @@ -1,13 +1,8 @@ using Content.Server.Botany.Components; using Content.Server.Hands.Systems; using Content.Server.Popups; -using Content.Shared.Botany; -using Content.Shared.Hands.Components; using Content.Shared.Interaction; -using Content.Shared.Popups; -using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -41,9 +36,6 @@ public sealed class HarvestSystem : EntitySystem [Dependency] private readonly HandsSystem _hands = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly IPrototypeManager _prototype = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly PlantHolderSystem _plantHolder = default!; public override void Initialize() @@ -130,7 +122,7 @@ public sealed class HarvestSystem : EntitySystem var yield = traits.Yield; if (plantHolder.Seed?.ProductPrototypes != null) { - for (int i = 0; i < yield; i++) + for (var i = 0; i < yield; i++) { foreach (var productPrototype in plantHolder.Seed.ProductPrototypes) { diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index a3414f41c37..26debc8f50a 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -1,6 +1,5 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Botany.Components; -using Content.Server.Botany.Systems; using Content.Server.Hands.Systems; using Content.Server.Popups; using Content.Shared.Administration.Logs; @@ -8,10 +7,8 @@ using Content.Shared.Botany; using Content.Shared.Burial.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Coordinates.Helpers; using Content.Shared.Examine; using Content.Shared.FixedPoint; -using Content.Shared.Hands.Components; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Popups; @@ -24,14 +21,10 @@ using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization.Manager; using Robust.Shared.Timing; -using Content.Shared.Administration.Logs; -using Content.Shared.Chemistry.Reaction; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; using Content.Shared.EntityEffects; -using Content.Shared.Kitchen.Components; using Content.Shared.Labels.Components; -using System.Linq; namespace Content.Server.Botany.Systems; @@ -415,7 +408,7 @@ public sealed class PlantHolderSystem : EntitySystem // ForceUpdate is used for external triggers like swabbing if (component.ForceUpdate) component.ForceUpdate = false; - else if (curTime < (component.LastCycle + component.CycleDelay)) + else if (curTime < component.LastCycle + component.CycleDelay) { if (component.UpdateSpriteAfterUpdate) UpdateSprite(uid, component); diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs index ebe5c83181c..c30d028d926 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs @@ -13,7 +13,9 @@ public sealed partial class PlantAdjustPotencyEntityEffectSystem : EntityEffectS if (entity.Comp.Seed == null || entity.Comp.Dead) return; - _plantHolder.EnsureUniqueSeed(entity, entity.Comp); - entity.Comp.Seed.Potency = Math.Max(entity.Comp.Seed.Potency + args.Effect.Amount, 1); + if (!TryComp(entity, out var traits)) + return; + + traits.Potency = Math.Max(traits.Potency + args.Effect.Amount, 1); } } diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAffectGrowthEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAffectGrowthEntityEffectSystem.cs index b0faa6255e1..74849db6c78 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAffectGrowthEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAffectGrowthEntityEffectSystem.cs @@ -7,13 +7,13 @@ namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes; public sealed partial class PlantAffectGrowthEntityEffectSystem : EntityEffectSystem { - [Dependency] private readonly PlantHolderSystem _plantHolder = default!; + [Dependency] private readonly BasicGrowthSystem _plantGrowth = default!; protected override void Effect(Entity entity, ref EntityEffectEvent args) { if (entity.Comp.Seed == null || entity.Comp.Dead) return; - _plantHolder.AffectGrowth(entity, (int)args.Effect.Amount, entity); + _plantGrowth.AffectGrowth(entity, (int)args.Effect.Amount, entity.Comp); } } diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs index 3d82f74b116..07ba8a11181 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs @@ -19,37 +19,55 @@ public sealed partial class PlantChangeStatEntityEffectSystem : EntityEffectSyst if (entity.Comp.Seed == null || entity.Comp.Dead) return; - var effect = args.Effect; - var member = entity.Comp.Seed.GetType().GetField(args.Effect.TargetValue); + var targetValue = args.Effect.TargetValue; - if (member == null) + // Build the list of the seed's growth components. + var growthHolder = entity.Comp.Seed.GrowthComponents; + growthHolder.EnsureGrowthComponents(); + + foreach (var prop in typeof(GrowthComponentsHolder).GetProperties()) { - Log.Error($"{ effect.GetType().Name } Error: Member { args.Effect.TargetValue} not found on { entity.Comp.Seed.GetType().Name }. Did you misspell it?"); - return; + if (prop.GetValue(growthHolder) is not PlantGrowthComponent) + continue; + + var componentType = prop.PropertyType; + if (!EntityManager.HasComponent(entity, componentType)) + continue; + + var component = EntityManager.GetComponent(entity, componentType); + var field = componentType.GetField(targetValue); + + if (field == null) + continue; + + var currentValue = field.GetValue(component); + if (currentValue == null) + continue; + + if (field.FieldType == typeof(float)) + { + var floatVal = (float)currentValue; + MutateFloat(ref floatVal, args.Effect.MinValue, args.Effect.MaxValue, args.Effect.Steps); + field.SetValue(component, floatVal); + return; + } + else if (field.FieldType == typeof(int)) + { + var intVal = (int)currentValue; + MutateInt(ref intVal, (int)args.Effect.MinValue, (int)args.Effect.MaxValue, args.Effect.Steps); + field.SetValue(component, intVal); + return; + } + else if (field.FieldType == typeof(bool)) + { + var boolVal = (bool)currentValue; + field.SetValue(component, !boolVal); + return; + } } - var currentValObj = member.GetValue(entity.Comp.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(entity.Comp.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(entity.Comp.Seed, intVal); - } - else if (member.FieldType == typeof(bool)) - { - var boolVal = (bool)currentValObj; - boolVal = !boolVal; - member.SetValue(entity.Comp.Seed, boolVal); - } + // Field not found in any component. + Log.Error($"{nameof(PlantChangeStat)} Error: Field '{targetValue}' not found in any plant component. Did you misspell it?"); } // Mutate reference 'val' between 'min' and 'max' by pretending the value @@ -65,14 +83,14 @@ public sealed partial class PlantChangeStatEntityEffectSystem : EntityEffectSyst // 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); + var 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; + var probIncrease = 1 - (float)valInt / bits; int valIntMutated; if (_random.Prob(probIncrease)) { @@ -84,7 +102,7 @@ public sealed partial class PlantChangeStatEntityEffectSystem : EntityEffectSyst } // Set value based on mutated thermometer code. - float valMutated = Math.Clamp((float)valIntMutated / bits * (max - min) + min, min, max); + var valMutated = Math.Clamp((float)valIntMutated / bits * (max - min) + min, min, max); val = valMutated; } @@ -98,14 +116,14 @@ public sealed partial class PlantChangeStatEntityEffectSystem : EntityEffectSyst // 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); + var 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; + var probIncrease = 1 - (float)valInt / bits; int valMutated; if (_random.Prob(probIncrease)) { diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs index 710bce24dde..b403e47cd21 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs @@ -15,13 +15,13 @@ public sealed partial class PlantCryoxadoneEntityEffectSystem : EntityEffectSyst return; var deviation = 0; - var seed = entity.Comp.Seed; - if (seed == null) + if (!TryComp(entity, out var traits)) return; - if (entity.Comp.Age > seed.Maturation) - deviation = (int) Math.Max(seed.Maturation - 1, entity.Comp.Age - _random.Next(7, 10)); + + if (entity.Comp.Age > traits.Maturation) + deviation = (int)Math.Max(traits.Maturation - 1, entity.Comp.Age - _random.Next(7, 10)); else - deviation = (int) (seed.Maturation / seed.GrowthStages); + deviation = (int)(traits.Maturation / traits.GrowthStages); entity.Comp.Age -= deviation; entity.Comp.LastProduce = entity.Comp.Age; entity.Comp.SkipAging++; diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDestroySeedsEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDestroySeedsEntityEffectSystem.cs index 1661c501be1..8c53c739814 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDestroySeedsEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDestroySeedsEntityEffectSystem.cs @@ -17,7 +17,10 @@ public sealed partial class PlantDestroySeedsEntityEffectSystem : EntityEffectSy if (entity.Comp.Seed == null || entity.Comp.Dead || entity.Comp.Seed.Immutable) return; - if (entity.Comp.Seed.Seedless) + if (!TryComp(entity, out var traits)) + return; + + if (traits.Seedless) return; _plantHolder.EnsureUniqueSeed(entity, entity.Comp); @@ -26,6 +29,6 @@ public sealed partial class PlantDestroySeedsEntityEffectSystem : EntityEffectSy entity, PopupType.SmallCaution ); - entity.Comp.Seed.Seedless = true; + traits.Seedless = true; } } diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDiethylamineEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDiethylamineEntityEffectSystem.cs index f6aebde465d..e5c0b954c3e 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDiethylamineEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDiethylamineEntityEffectSystem.cs @@ -16,16 +16,10 @@ public sealed partial class PlantDiethylamineEntityEffectSystem : EntityEffectSy if (entity.Comp.Seed == null || entity.Comp.Dead || entity.Comp.Seed.Immutable) return; - if (_random.Prob(0.1f)) - { - _plantHolder.EnsureUniqueSeed(entity, entity); - entity.Comp.Seed!.Lifespan++; - } + if (_random.Prob(0.1f) && TryComp(entity, out var traits)) + traits.Lifespan++; - if (_random.Prob(0.1f)) - { - _plantHolder.EnsureUniqueSeed(entity, entity); - entity.Comp.Seed!.Endurance++; - } + if (_random.Prob(0.1f) && TryComp(entity, out traits)) + traits.Endurance++; } } diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantPhalanximineEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantPhalanximineEntityEffectSystem.cs index 8a073392e1c..7971d6ea2da 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantPhalanximineEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantPhalanximineEntityEffectSystem.cs @@ -11,6 +11,7 @@ public sealed partial class PlantPhalanximineEntityEffectSystem : EntityEffectSy if (entity.Comp.Seed == null || entity.Comp.Dead || entity.Comp.Seed.Immutable) return; - entity.Comp.Seed.Viable = true; + if (TryComp(entity, out var traits)) + traits.Viable = true; } } diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRestoreSeedsEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRestoreSeedsEntityEffectSystem.cs index 4d724be2443..53402018405 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRestoreSeedsEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRestoreSeedsEntityEffectSystem.cs @@ -16,11 +16,11 @@ public sealed partial class PlantRestoreSeedsEntityEffectSystem : EntityEffectSy if (entity.Comp.Seed == null || entity.Comp.Dead || entity.Comp.Seed.Immutable) return; - if (!entity.Comp.Seed.Seedless) + if (!TryComp(entity, out var traits) || !traits.Seedless) return; _plantHolder.EnsureUniqueSeed(entity, entity.Comp); _popup.PopupEntity(Loc.GetString("botany-plant-seedsrestored"), entity); - entity.Comp.Seed.Seedless = false; + traits.Seedless = false; } } diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/RobustHarvestEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/RobustHarvestEntityEffectSystem.cs index 68ea3319ef4..42c3b549478 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/RobustHarvestEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/RobustHarvestEntityEffectSystem.cs @@ -21,21 +21,19 @@ public sealed partial class RobustHarvestEntityEffectSystem : EntityEffectSystem if (entity.Comp.Seed == null || entity.Comp.Dead) return; - if (entity.Comp.Seed.Potency < args.Effect.PotencyLimit) + if (TryComp(entity, out var traits) && traits.Potency < args.Effect.PotencyLimit) { - _plantHolder.EnsureUniqueSeed(entity, entity.Comp); - entity.Comp.Seed.Potency = Math.Min(entity.Comp.Seed.Potency + args.Effect.PotencyIncrease, args.Effect.PotencyLimit); + traits.Potency = Math.Min(traits.Potency + args.Effect.PotencyIncrease, args.Effect.PotencyLimit); - if (entity.Comp.Seed.Potency > args.Effect.PotencySeedlessThreshold) + if (traits.Potency > args.Effect.PotencySeedlessThreshold) { - entity.Comp.Seed.Seedless = true; + traits.Seedless = true; } } - else if (entity.Comp.Seed.Yield > 1 && _random.Prob(0.1f)) + else if (TryComp(entity, out traits) && traits.Yield > 1 && _random.Prob(0.1f)) { // Too much of a good thing reduces yield - _plantHolder.EnsureUniqueSeed(entity, entity.Comp); - entity.Comp.Seed.Yield--; + traits.Yield--; } } } diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffectSystem.cs index e2376ba186c..e5d00ce6162 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffectSystem.cs @@ -16,11 +16,12 @@ public sealed partial class PlantMutateExudeGasesEntityEffectSystem : EntityEffe if (entity.Comp.Seed == null) return; - var gasses = entity.Comp.Seed.ExudeGasses; + var gasComponent = EnsureComp(entity); + var gasses = gasComponent.ExudeGasses; - // Add a random amount of a random gas to this gas dictionary + // Add a random amount of a random gas to this gas dictionary. float amount = _random.NextFloat(args.Effect.MinValue, args.Effect.MaxValue); - var gas = _random.Pick(Enum.GetValues(typeof(Gas)).Cast().ToList()); + var gas = _random.Pick(Enum.GetValues()); if (!gasses.TryAdd(gas, amount)) { @@ -38,11 +39,12 @@ public sealed partial class PlantMutateConsumeGasesEntityEffectSystem : EntityEf if (entity.Comp.Seed == null) return; - var gasses = entity.Comp.Seed.ConsumeGasses; + var gasComponent = EnsureComp(entity); + var gasses = gasComponent.ConsumeGasses; - // Add a random amount of a random gas to this gas dictionary + // Add a random amount of a random gas to this gas dictionary. var amount = _random.NextFloat(args.Effect.MinValue, args.Effect.MaxValue); - var gas = _random.Pick(Enum.GetValues(typeof(Gas)).Cast().ToList()); + var gas = _random.Pick(Enum.GetValues()); if (!gasses.TryAdd(gas, amount)) { diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantMutateHarvestEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantMutateHarvestEntityEffectSystem.cs index 95d7f97bbe3..8f385f423c2 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantMutateHarvestEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantMutateHarvestEntityEffectSystem.cs @@ -1,5 +1,5 @@ -using Content.Server.Botany; using Content.Server.Botany.Components; +using Content.Server.Botany.Systems; using Content.Shared.EntityEffects; using Content.Shared.EntityEffects.Effects.Botany; @@ -12,13 +12,14 @@ public sealed partial class PlantMutateHarvestEntityEffectSystem : EntityEffectS if (entity.Comp.Seed == null) return; - switch (entity.Comp.Seed.HarvestRepeat) + var harvest = EnsureComp(entity); + switch (harvest.HarvestRepeat) { case HarvestType.NoRepeat: - entity.Comp.Seed.HarvestRepeat = HarvestType.Repeat; + harvest.HarvestRepeat = HarvestType.Repeat; break; case HarvestType.Repeat: - entity.Comp.Seed.HarvestRepeat = HarvestType.SelfHarvest; + harvest.HarvestRepeat = HarvestType.SelfHarvest; break; } } diff --git a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantPhalanximine.cs b/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantPhalanximine.cs deleted file mode 100644 index 8a2fa559ca9..00000000000 --- a/Content.Server/EntityEffects/Effects/PlantMetabolism/PlantPhalanximine.cs +++ /dev/null @@ -1,23 +0,0 @@ -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; - - args.EntityManager.RemoveComponent(args.TargetEntity); - } - - protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-plant-phalanximine", ("chance", Probability)); -} diff --git a/Content.Shared/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffect.cs b/Content.Shared/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffect.cs index c617c05b334..2e19b234401 100644 --- a/Content.Shared/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffect.cs +++ b/Content.Shared/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffect.cs @@ -1,3 +1,5 @@ +using Robust.Shared.Prototypes; + namespace Content.Shared.EntityEffects.Effects.Botany; /// @@ -10,6 +12,14 @@ public sealed partial class PlantMutateConsumeGases : EntityEffectBase + public override string? EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return Loc.GetString("reagent-effect-guidebook-plant-mutate-consume-gasses", + ("minValue", MinValue), + ("maxValue", MaxValue)); + } } public sealed partial class PlantMutateExudeGases : EntityEffectBase @@ -19,4 +29,12 @@ public sealed partial class PlantMutateExudeGases : EntityEffectBase + public override string? EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return Loc.GetString("reagent-effect-guidebook-plant-mutate-exude-gasses", + ("minValue", MinValue), + ("maxValue", MaxValue)); + } } diff --git a/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl b/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl index 44f82c8f281..9b4dd5994c7 100644 --- a/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl +++ b/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl @@ -512,4 +512,3 @@ reagent-effect-guidebook-plant-mutate-consume-gasses = [1] Mutates *[other] mutate } the plant to consume gases between {$minValue} and {$maxValue} moles - From ab2f933d7a83eab67fc3c1fc50b6ca09f2182133 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Mon, 13 Oct 2025 15:45:35 +0300 Subject: [PATCH 31/53] fix --- .../Botany/Systems/PlantHolderSystem.cs | 26 +- Resources/Prototypes/Hydroponics/seeds.yml | 912 +++++++++--------- 2 files changed, 471 insertions(+), 467 deletions(-) diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 26debc8f50a..35d59dc1be1 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -504,6 +504,7 @@ public sealed class PlantHolderSystem : EntitySystem component.ImproperPressure = false; component.WeedLevel += 1; component.PestLevel = 0; + UpdateSprite(uid, component); } @@ -595,13 +596,21 @@ public sealed class PlantHolderSystem : EntitySystem if (!TryComp(uid, out var app)) return; - TryComp(uid, out var traits); - - if (traits == null) - return; - - if (component.Seed != null) + // If no seed, clear visuals regardless of traits. + if (component.Seed == null) { + _appearance.SetData(uid, PlantHolderVisuals.PlantState, "", app); + _appearance.SetData(uid, PlantHolderVisuals.HealthLight, false, app); + _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, false, app); + } + else + { + // Have a seed, require traits for detailed visuals. + TryComp(uid, out var traits); + + if (traits == null) + return; + if (component.DrawWarnings) { _appearance.SetData(uid, PlantHolderVisuals.HealthLight, component.Health <= traits.Endurance / 2f); @@ -634,11 +643,6 @@ public sealed class PlantHolderSystem : EntitySystem } } } - else - { - _appearance.SetData(uid, PlantHolderVisuals.PlantState, "", app); - _appearance.SetData(uid, PlantHolderVisuals.HealthLight, false, app); - } if (!component.DrawWarnings) return; diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index aba33f30aed..d6b5445e00b 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -20,13 +20,13 @@ nutrientConsumption: 0.4 chemicals: Nutriment: - Min: 1 - Max: 20 - PotencyDivisor: 20 + min: 1 + max: 20 + potencyDivisor: 20 Flour: - Min: 5 - Max: 20 - PotencyDivisor: 20 + min: 5 + max: 20 + potencyDivisor: 20 - type: seed id: meatwheat @@ -48,13 +48,13 @@ nutrientConsumption: 0.4 chemicals: Nutriment: - Min: 1 - Max: 20 - PotencyDivisor: 20 + min: 1 + max: 20 + potencyDivisor: 20 UncookedAnimalProteins: - Min: 5 - Max: 20 - PotencyDivisor: 20 + min: 5 + max: 20 + potencyDivisor: 20 - type: seed id: oat @@ -76,13 +76,13 @@ nutrientConsumption: 0.4 chemicals: Nutriment: - Min: 1 - Max: 20 - PotencyDivisor: 20 + min: 1 + max: 20 + potencyDivisor: 20 Oats: - Min: 5 - Max: 20 - PotencyDivisor: 20 + min: 5 + max: 20 + potencyDivisor: 20 - type: seed id: banana @@ -109,13 +109,13 @@ idealHeat: 298 chemicals: Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 Nutriment: - Min: 1 - Max: 2 - PotencyDivisor: 50 + min: 1 + max: 2 + potencyDivisor: 50 - type: seed id: mimana @@ -140,13 +140,13 @@ idealHeat: 298 chemicals: MuteToxin: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Nutriment: - Min: 1 - Max: 2 - PotencyDivisor: 50 + min: 1 + max: 2 + potencyDivisor: 50 - type: seed id: carrots @@ -169,17 +169,17 @@ waterConsumption: 0.6 chemicals: JuiceCarrot: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Oculine: - Min: 2 - Max: 6 - PotencyDivisor: 20 + min: 2 + max: 6 + potencyDivisor: 20 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: laughinPea @@ -207,17 +207,17 @@ nutrientConsumption: 0.6 chemicals: Nutriment: - Min: 1 - Max: 3 - PotencyDivisor: 7 + min: 1 + max: 3 + potencyDivisor: 7 Sugar: - Min: 1 - Max: 10 - PotencyDivisor: 5 + min: 1 + max: 10 + potencyDivisor: 5 Laughter: - Min: 1 - Max: 10 - PotencyDivisor: 5 + min: 1 + max: 10 + potencyDivisor: 5 - type: seed id: lemon @@ -243,13 +243,13 @@ harvestRepeat: Repeat chemicals: Nutriment: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: lemoon @@ -271,13 +271,13 @@ - FoodLemoon chemicals: Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 Milk: - Min: 8 - Max: 20 - PotencyDivisor: 5 + min: 8 + max: 20 + potencyDivisor: 5 - type: seed id: lime @@ -302,13 +302,13 @@ harvestRepeat: Repeat chemicals: Nutriment: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: orange @@ -334,13 +334,13 @@ harvestRepeat: Repeat chemicals: Nutriment: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: extradimensionalOrange @@ -362,17 +362,17 @@ harvestRepeat: Repeat chemicals: Haloperidol: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Nutriment: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: pineapple @@ -395,17 +395,17 @@ harvestRepeat: Repeat chemicals: Nutriment: - Min: 1 - Max: 20 - PotencyDivisor: 20 + min: 1 + max: 20 + potencyDivisor: 20 Water: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 Vitamin: - Min: 1 - Max: 2 - PotencyDivisor: 50 + min: 1 + max: 2 + potencyDivisor: 50 - type: seed id: potato @@ -428,13 +428,13 @@ waterConsumption: 0.6 chemicals: Nutriment: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: sugarcane @@ -461,9 +461,9 @@ idealHeat: 298 chemicals: Sugar: - Min: 4 - Max: 5 - PotencyDivisor: 5 + min: 4 + max: 5 + potencyDivisor: 5 - type: seed id: teaPlant @@ -490,9 +490,9 @@ idealHeat: 298 chemicals: Vitamin: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 - type: seed id: papercane @@ -594,17 +594,17 @@ idealHeat: 298 chemicals: Nutriment: - Min: 1 - Max: 7 - PotencyDivisor: 14 + min: 1 + max: 7 + potencyDivisor: 14 Vitamin: - Min: 1 - Max: 3 - PotencyDivisor: 33 + min: 1 + max: 3 + potencyDivisor: 33 Water: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: blueTomato @@ -631,17 +631,17 @@ idealHeat: 298 chemicals: Nutriment: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 SpaceLube: - Min: 5 - Max: 15 - PotencyDivisor: 10 + min: 5 + max: 15 + potencyDivisor: 10 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: bloodTomato @@ -670,13 +670,13 @@ idealHeat: 298 chemicals: Blood: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: killerTomato @@ -706,13 +706,13 @@ idealHeat: 298 chemicals: Blood: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 JuiceTomato: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: eggplant @@ -739,13 +739,13 @@ idealHeat: 298 chemicals: Nutriment: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: cabbage @@ -766,13 +766,13 @@ growthStages: 1 chemicals: Nutriment: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: garlic @@ -793,17 +793,17 @@ growthStages: 3 chemicals: Nutriment: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 Allicin: - Min: 1 - Max: 8 - PotencyDivisor: 25 + min: 1 + max: 8 + potencyDivisor: 25 - type: seed id: apple @@ -827,13 +827,13 @@ harvestRepeat: Repeat chemicals: Nutriment: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: goldenApple @@ -857,17 +857,17 @@ waterConsumption: 0.75 chemicals: Nutriment: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 DoctorsDelight: - Min: 3 - Max: 13 - PotencyDivisor: 10 + min: 3 + max: 13 + potencyDivisor: 10 - type: seed id: corn @@ -892,13 +892,13 @@ idealHeat: 298 chemicals: Nutriment: - Min: 1 - Max: 10 - PotencyDivisor: 20 + min: 1 + max: 10 + potencyDivisor: 20 Cornmeal: - Min: 5 - Max: 15 - PotencyDivisor: 10 + min: 5 + max: 15 + potencyDivisor: 10 - type: seed id: onion @@ -926,17 +926,17 @@ idealHeat: 298 chemicals: Nutriment: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 Allicin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: onionred @@ -961,17 +961,17 @@ idealHeat: 298 chemicals: Nutriment: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 Allicin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: chanterelle @@ -997,9 +997,9 @@ idealHeat: 288 chemicals: Nutriment: - Min: 1 - Max: 25 - PotencyDivisor: 25 + min: 1 + max: 25 + potencyDivisor: 25 - type: seed id: eggy @@ -1025,9 +1025,9 @@ idealHeat: 298 chemicals: Egg: - Min: 4 - Max: 12 - PotencyDivisor: 10 + min: 4 + max: 12 + potencyDivisor: 10 - type: seed id: cannabis @@ -1056,9 +1056,9 @@ idealHeat: 298 chemicals: THC: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 - type: seed id: rainbowCannabis @@ -1085,29 +1085,29 @@ idealHeat: 298 chemicals: SpaceDrugs: - Min: 1 - Max: 15 - PotencyDivisor: 10 + min: 1 + max: 15 + potencyDivisor: 10 Lipolicide: - Min: 1 - Max: 15 - PotencyDivisor: 10 + min: 1 + max: 15 + potencyDivisor: 10 MindbreakerToxin: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Happiness: - Min: 1 - Max: 5 -# PotencyDivisor: 20 + min: 1 + max: 5 +# potencyDivisor: 20 # ColorfulReagent: -# Min: 0 -# Max: 5 -# PotencyDivisor: 20 +# min: 0 +# max: 5 +# potencyDivisor: 20 Psicodine: - Min: 0 - Max: 5 - PotencyDivisor: 33 + min: 0 + max: 5 + potencyDivisor: 33 - type: seed id: tobacco @@ -1134,9 +1134,9 @@ idealHeat: 298 chemicals: Nicotine: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 - type: seed id: nettle @@ -1163,9 +1163,9 @@ idealHeat: 298 chemicals: Histamine: - Min: 1 - Max: 25 - PotencyDivisor: 4 + min: 1 + max: 25 + potencyDivisor: 4 - type: seed id: deathNettle @@ -1193,13 +1193,13 @@ idealHeat: 298 chemicals: SulfuricAcid: - Min: 1 - Max: 15 - PotencyDivisor: 6 + min: 1 + max: 15 + potencyDivisor: 6 FluorosulfuricAcid: - Min: 1 - Max: 15 - PotencyDivisor: 6 + min: 1 + max: 15 + potencyDivisor: 6 - type: seed id: chili @@ -1225,17 +1225,17 @@ idealHeat: 298 chemicals: CapsaicinOil: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Nutriment: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: chilly @@ -1259,17 +1259,17 @@ idealHeat: 298 chemicals: FrostOil: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Nutriment: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: poppy @@ -1294,13 +1294,13 @@ waterConsumption: 0.6 chemicals: Nutriment: - Min: 1 - Max: 2 - PotencyDivisor: 50 + min: 1 + max: 2 + potencyDivisor: 50 Bicaridine: - Min: 1 - Max: 20 - PotencyDivisor: 5 + min: 1 + max: 20 + potencyDivisor: 5 - type: seed id: aloe @@ -1323,13 +1323,13 @@ waterConsumption: 0.6 chemicals: Aloe: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Dermaline: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 - type: seed id: lily @@ -1354,13 +1354,13 @@ waterConsumption: 0.6 chemicals: Nutriment: - Min: 1 - Max: 2 - PotencyDivisor: 50 + min: 1 + max: 2 + potencyDivisor: 50 Bicaridine: - Min: 1 - Max: 20 - PotencyDivisor: 5 + min: 1 + max: 20 + potencyDivisor: 5 - type: seed id: lingzhi @@ -1383,13 +1383,13 @@ waterConsumption: 0.6 chemicals: Ultravasculine: - Min: 1 - Max: 20 - PotencyDivisor: 5 + min: 1 + max: 20 + potencyDivisor: 5 Epinephrine: - Min: 1 - Max: 20 - PotencyDivisor: 5 + min: 1 + max: 20 + potencyDivisor: 5 - type: seed id: ambrosiaVulgaris @@ -1414,21 +1414,21 @@ waterConsumption: 0.6 chemicals: Nutriment: - Min: 1 - Max: 2 - PotencyDivisor: 10 + min: 1 + max: 2 + potencyDivisor: 10 Bicaridine: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Kelotane: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Vitamin: - Min: 1 - Max: 2 - PotencyDivisor: 50 + min: 1 + max: 2 + potencyDivisor: 50 - type: seed id: ambrosiaDeus @@ -1451,17 +1451,17 @@ waterConsumption: 0.6 chemicals: Nutriment: - Min: 1 - Max: 2 - PotencyDivisor: 10 + min: 1 + max: 2 + potencyDivisor: 10 Omnizine: # Don't kill me - Min: 1 - Max: 3 - PotencyDivisor: 35 + min: 1 + max: 3 + potencyDivisor: 35 SpaceDrugs: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 - type: seed id: galaxythistle @@ -1486,9 +1486,9 @@ waterConsumption: 0.6 chemicals: Stellibinin: - Min: 1 - Max: 25 - PotencyDivisor: 4 + min: 1 + max: 25 + potencyDivisor: 4 - type: seed id: glasstle @@ -1509,13 +1509,13 @@ growthStages: 3 chemicals: Razorium: - Min: 1 - Max: 25 - PotencyDivisor: 4 + min: 1 + max: 25 + potencyDivisor: 4 Desoxyephedrine: # meff!! - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 - type: seed id: flyAmanita @@ -1539,13 +1539,13 @@ nutrientConsumption: 0.5 chemicals: Amatoxin: - Min: 1 - Max: 10 - PotencyDivisor: 12 + min: 1 + max: 10 + potencyDivisor: 12 Nutriment: ## yumby :) - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 - type: seed id: gatfruit @@ -1569,13 +1569,13 @@ growthStages: 2 chemicals: Nutriment: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Sulfur: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 - type: seed id: fakeCapfruit @@ -1596,13 +1596,13 @@ growthStages: 2 chemicals: Nutriment: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Sulfur: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 - type: seed id: realCapfruit @@ -1623,13 +1623,13 @@ growthStages: 2 chemicals: Nutriment: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Sulfur: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 - type: seed id: rice @@ -1653,13 +1653,13 @@ nutrientConsumption: 0.4 chemicals: Nutriment: - Min: 1 - Max: 20 - PotencyDivisor: 20 + min: 1 + max: 20 + potencyDivisor: 20 Rice: - Min: 5 - Max: 20 - PotencyDivisor: 20 + min: 5 + max: 20 + potencyDivisor: 20 - type: seed id: soybeans @@ -1684,9 +1684,9 @@ nutrientConsumption: 0.4 chemicals: Nutriment: - Min: 1 - Max: 2 - PotencyDivisor: 50 + min: 1 + max: 2 + potencyDivisor: 50 - type: seed id: spacemansTrumpet @@ -1709,13 +1709,13 @@ waterConsumption: 0.6 chemicals: Nutriment: - Min: 1 - Max: 5 - PotencyDivisor: 50 + min: 1 + max: 5 + potencyDivisor: 50 PolypyryliumOligomers: - Min: 1 - Max: 15 - PotencyDivisor: 5 + min: 1 + max: 15 + potencyDivisor: 5 - type: seed id: koibean @@ -1738,13 +1738,13 @@ nutrientConsumption: 0.4 chemicals: Nutriment: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 CarpoToxin: - Min: 1 - Max: 4 - PotencyDivisor: 30 + min: 1 + max: 4 + potencyDivisor: 30 - type: seed id: grape @@ -1765,13 +1765,13 @@ growthStages: 2 chemicals: Nutriment: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 - type: seed id: watermelon @@ -1793,17 +1793,17 @@ potency: 1 chemicals: Nutriment: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Water: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Vitamin: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 - type: seed id: holymelon @@ -1823,17 +1823,17 @@ potency: 1 chemicals: Nutriment: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Holywater: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 Vitamin: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 - type: seed id: cocoa @@ -1859,13 +1859,13 @@ idealHeat: 298 chemicals: Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 25 + min: 1 + max: 4 + potencyDivisor: 25 Nutriment: - Min: 1 - Max: 2 - PotencyDivisor: 50 + min: 1 + max: 2 + potencyDivisor: 50 - type: seed id: berries @@ -1888,13 +1888,13 @@ nutrientConsumption: 0.6 chemicals: Nutriment: - Min: 2 - Max: 5 - PotencyDivisor: 30 + min: 2 + max: 5 + potencyDivisor: 30 Vitamin: - Min: 1 - Max: 4 - PotencyDivisor: 40 + min: 1 + max: 4 + potencyDivisor: 40 - type: seed id: bungo @@ -1921,13 +1921,13 @@ idealHeat: 298 chemicals: Nutriment: - Min: 5 - Max: 10 - PotencyDivisor: 20 + min: 5 + max: 10 + potencyDivisor: 20 Enzyme: - Min: 5 - Max: 10 - PotencyDivisor: 20 + min: 5 + max: 10 + potencyDivisor: 20 - type: seed id: pea @@ -1954,13 +1954,13 @@ nutrientConsumption: 0.5 chemicals: Nutriment: - Min: 1 - Max: 3 - PotencyDivisor: 33 + min: 1 + max: 3 + potencyDivisor: 33 Vitamin: - Min: 1 - Max: 2 - PotencyDivisor: 50 + min: 1 + max: 2 + potencyDivisor: 50 - type: seed id: worldPea @@ -1985,17 +1985,17 @@ nutrientConsumption: 0.5 chemicals: Happiness: - Min: 1 - Max: 3 - PotencyDivisor: 25 + min: 1 + max: 3 + potencyDivisor: 25 Nutriment: - Min: 1 - Max: 3 - PotencyDivisor: 20 + min: 1 + max: 3 + potencyDivisor: 20 Pax: - Min: 1 - Max: 2 - PotencyDivisor: 50 + min: 1 + max: 2 + potencyDivisor: 50 - type: seed id: pumpkin @@ -2020,13 +2020,13 @@ idealHeat: 288 chemicals: PumpkinFlesh: - Min: 1 - Max: 20 - PotencyDivisor: 5 + min: 1 + max: 20 + potencyDivisor: 5 Vitamin: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 - type: seed id: bluePumpkin @@ -2049,17 +2049,17 @@ idealHeat: 288 chemicals: Ammonia: - Min: 1 - Max: 15 - PotencyDivisor: 3 + min: 1 + max: 15 + potencyDivisor: 3 Chlorine: - Min: 1 - Max: 5 - PotencyDivisor: 5 + min: 1 + max: 5 + potencyDivisor: 5 Vitamin: - Min: 1 - Max: 10 - PotencyDivisor: 3 + min: 1 + max: 10 + potencyDivisor: 3 - type: seed id: cotton @@ -2084,9 +2084,9 @@ waterConsumption: 0.6 chemicals: Fiber: - Min: 5 - Max: 10 - PotencyDivisor: 20 + min: 5 + max: 10 + potencyDivisor: 20 - type: seed id: pyrotton @@ -2109,13 +2109,13 @@ waterConsumption: 0.8 chemicals: Fiber: - Min: 5 - Max: 10 - PotencyDivisor: 20 + min: 5 + max: 10 + potencyDivisor: 20 Phlogiston: - Min: 4 - Max: 8 - PotencyDivisor: 30 + min: 4 + max: 8 + potencyDivisor: 30 - type: seed id: cherry @@ -2137,13 +2137,13 @@ harvestRepeat: Repeat chemicals: Nutriment: - Min: 1 - Max: 3 - PotencyDivisor: 30 + min: 1 + max: 3 + potencyDivisor: 30 Vitamin: - Min: 1 - Max: 3 - PotencyDivisor: 40 + min: 1 + max: 3 + potencyDivisor: 40 - type: seed id: anomalyBerry @@ -2167,17 +2167,17 @@ nutrientConsumption: 0.5 chemicals: Artifexium: - Min: 1 - Max: 1 - PotencyDivisor: 4 + min: 1 + max: 1 + potencyDivisor: 4 Nutriment: - Min: 1 - Max: 2 - PotencyDivisor: 30 + min: 1 + max: 2 + potencyDivisor: 30 Vitamin: - Min: 1 - Max: 2 - PotencyDivisor: 40 + min: 1 + max: 2 + potencyDivisor: 40 - type: seed id: bloonion @@ -2201,18 +2201,18 @@ nutrientConsumption: 0.5 chemicals: Potassium: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Phosphorus: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Sugar: - Min: 1 - Max: 5 - PotencyDivisor: 20 + min: 1 + max: 5 + potencyDivisor: 20 Allicin: - Min: 1 - Max: 10 - PotencyDivisor: 10 + min: 1 + max: 10 + potencyDivisor: 10 From c0a95d34e53aadf2ecd8e1baa4632141ff0d04be Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Thu, 16 Oct 2025 01:45:55 +0300 Subject: [PATCH 32/53] code cleanup --- .../Botany/Components/BotanySwabComponent.cs | 5 +- .../Botany/Systems/AtmosphericGrowthSystem.cs | 17 +++- .../Botany/Systems/BasicGrowthSystem.cs | 33 +++++--- .../Systems/ConsumeExudeGasGrowthSystem.cs | 13 ++- .../Botany/Systems/HarvestSystem.cs | 81 +++++++++++-------- .../Botany/Systems/PlantGrowthSystem.cs | 13 +-- .../Botany/Systems/PlantTraitsSystem.cs | 23 ++++-- Content.Server/Botany/Systems/ToxinsSystem.cs | 12 ++- .../Botany/Systems/UnviableGrowthSystem.cs | 13 ++- .../Botany/Systems/WeedPestGrowthSystem.cs | 21 ++++- .../PlantAdjustPotencyEntityEffectSystem.cs | 5 +- 11 files changed, 159 insertions(+), 77 deletions(-) diff --git a/Content.Server/Botany/Components/BotanySwabComponent.cs b/Content.Server/Botany/Components/BotanySwabComponent.cs index 987a804c960..66ad1cbb368 100644 --- a/Content.Server/Botany/Components/BotanySwabComponent.cs +++ b/Content.Server/Botany/Components/BotanySwabComponent.cs @@ -1,3 +1,4 @@ +using System; using Content.Server.Botany.Components; namespace Content.Server.Botany; @@ -9,10 +10,10 @@ namespace Content.Server.Botany; public sealed partial class BotanySwabComponent : Component { /// - /// Delay in seconds between swab uses. + /// Delay between swab uses. /// [DataField] - public float SwabDelay = 2f; + public TimeSpan SwabDelay = TimeSpan.FromSeconds(2); /// /// SeedData from the first plant that got swabbed. diff --git a/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs b/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs index c34fbd14c1f..c86cf0c688f 100644 --- a/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs +++ b/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs @@ -1,24 +1,33 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Botany.Components; using Content.Shared.Atmos; +using Robust.Shared.Random; namespace Content.Server.Botany.Systems; + +/// +/// Applies atmospheric temperature and pressure effects to plants during growth ticks. +/// Uses current tile gas mixture to penalize or clear warnings based on tolerances. +/// public sealed class AtmosphericGrowthSystem : PlantGrowthSystem { - [Dependency] private readonly BotanySystem _botany = default!; - [Dependency] private readonly PlantHolderSystem _plantHolderSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphere = default!; + [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); } - private void OnPlantGrow(EntityUid uid, AtmosphericGrowthComponent component, OnPlantGrowEvent args) + private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { + var uid = ent.Owner; + var component = ent.Comp; + PlantHolderComponent? holder = null; - Resolve(uid, ref holder); + Resolve(uid, ref holder); if (holder == null || holder.Seed == null || holder.Dead) return; diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index 1863c28c3a9..7ef26baea8c 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -7,22 +7,32 @@ namespace Content.Server.Botany.Systems; // TODO: make CO2Boost (add potency if the plant can eat an increasing amount of CO2). separate PR post-merge // TODO: make GrowLight (run bonus ticks if theres a grow light nearby). separate PR post-merge. +/// +/// Handles baseline plant progression each growth tick: aging, resource consumption, +/// simple viability checks, and basic swab cross-pollination behavior. +/// public sealed class BasicGrowthSystem : PlantGrowthSystem { [Dependency] private readonly BotanySystem _botany = default!; [Dependency] private readonly PlantHolderSystem _plantHolder = default!; + [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); SubscribeLocalEvent(OnSwab); } - private void OnSwab(EntityUid uid, BasicGrowthComponent component, BotanySwabDoAfterEvent args) + private void OnSwab(Entity ent, ref BotanySwabDoAfterEvent args) { - if (args.Cancelled || args.Handled || !TryComp(args.Args.Target, out var plant) || - args.Used == null || !TryComp(args.Used.Value, out var swab) || swab.SeedData == null) + var component = ent.Comp; + + if (args.Cancelled || args.Handled || args.Used == null) + return; + + if (!TryComp(args.Used.Value, out var swab) || swab.SeedData == null) return; var swabComp = swab.SeedData.GrowthComponents.BasicGrowth; @@ -43,10 +53,13 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem } } - private void OnPlantGrow(EntityUid uid, BasicGrowthComponent component, OnPlantGrowEvent args) + private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { + var uid = ent.Owner; + var component = ent.Comp; + PlantHolderComponent? holder = null; - Resolve(uid, ref holder); + Resolve(uid, ref holder); if (holder == null || holder.Seed == null || holder.Dead) return; @@ -54,7 +67,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem // Check if the plant is viable if (TryComp(uid, out var traits) && !traits.Viable) { - holder.Health -= _random.Next(5, 10) * PlantGrowthSystem.HydroponicsSpeedMultiplier; + holder.Health -= _random.Next(5, 10) * HydroponicsSpeedMultiplier; if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; return; @@ -67,7 +80,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem { if (_random.Prob(0.8f)) { - holder.Age += (int)(1 * PlantGrowthSystem.HydroponicsSpeedMultiplier); + holder.Age += (int)(1 * HydroponicsSpeedMultiplier); holder.UpdateSpriteAfterUpdate = true; } } @@ -85,7 +98,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem if (component.WaterConsumption > 0 && holder.WaterLevel > 0 && _random.Prob(0.75f)) { holder.WaterLevel -= MathF.Max(0f, - component.WaterConsumption * PlantGrowthSystem.HydroponicsConsumptionMultiplier * PlantGrowthSystem.HydroponicsSpeedMultiplier); + component.WaterConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; } @@ -93,12 +106,12 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem if (component.NutrientConsumption > 0 && holder.NutritionLevel > 0 && _random.Prob(0.75f)) { holder.NutritionLevel -= MathF.Max(0f, - component.NutrientConsumption * PlantGrowthSystem.HydroponicsConsumptionMultiplier * PlantGrowthSystem.HydroponicsSpeedMultiplier); + component.NutrientConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; } - var healthMod = _random.Next(1, 3) * PlantGrowthSystem.HydroponicsSpeedMultiplier; + var healthMod = _random.Next(1, 3) * HydroponicsSpeedMultiplier; if (holder.SkipAging < 10) { // Make sure the plant is not thirsty. diff --git a/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs index 34562abcd0c..bf02a5de509 100644 --- a/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs +++ b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs @@ -3,6 +3,11 @@ using Content.Server.Botany.Components; using Content.Shared.Atmos; namespace Content.Server.Botany.Systems; + +/// +/// Consumes and emits configured gases around plants each growth tick, then merges +/// the adjusted gas mixture back into the environment. +/// public sealed class ConsumeExudeGasGrowthSystem : PlantGrowthSystem { [Dependency] private readonly AtmosphereSystem _atmosphere = default!; @@ -10,13 +15,17 @@ public sealed class ConsumeExudeGasGrowthSystem : PlantGrowthSystem public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); } - private void OnPlantGrow(EntityUid uid, ConsumeExudeGasGrowthComponent component, OnPlantGrowEvent args) + private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { + var uid = ent.Owner; + var component = ent.Comp; + PlantHolderComponent? holder = null; - Resolve(uid, ref holder); + Resolve(uid, ref holder); if (holder == null || holder.Seed == null || holder.Dead) return; diff --git a/Content.Server/Botany/Systems/HarvestSystem.cs b/Content.Server/Botany/Systems/HarvestSystem.cs index 9296defe694..da0850ffdcc 100644 --- a/Content.Server/Botany/Systems/HarvestSystem.cs +++ b/Content.Server/Botany/Systems/HarvestSystem.cs @@ -3,9 +3,6 @@ using Content.Server.Hands.Systems; using Content.Server.Popups; using Content.Shared.Interaction; using Robust.Shared.Audio.Systems; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Timing; namespace Content.Server.Botany.Systems; @@ -30,6 +27,10 @@ public enum HarvestType SelfHarvest } +/// +/// Manages harvest readiness and execution for plants, including repeat/self-harvest +/// logic and produce spawning, responding to growth and interaction events. +/// public sealed class HarvestSystem : EntitySystem { [Dependency] private readonly BotanySystem _botany = default!; @@ -41,14 +42,19 @@ public sealed class HarvestSystem : EntitySystem public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); SubscribeLocalEvent(OnInteractHand); } - private void OnPlantGrow(EntityUid uid, HarvestComponent component, OnPlantGrowEvent args) + private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { - if (!TryComp(uid, out var plantHolder) || - !TryComp(uid, out var traits)) + var uid = ent.Owner; + var component = ent.Comp; + + PlantHolderComponent? plantHolder = null; + PlantTraitsComponent? traits = null; + if (!Resolve(uid, ref plantHolder, ref traits)) return; if (plantHolder.Dead || plantHolder.Seed == null) @@ -76,10 +82,14 @@ public sealed class HarvestSystem : EntitySystem } } - private void OnInteractHand(EntityUid uid, HarvestComponent component, InteractHandEvent args) + private void OnInteractHand(Entity ent, ref InteractHandEvent args) { - if (!TryComp(uid, out var plantHolder) || - !TryComp(uid, out var traits)) + var uid = ent.Owner; + var component = ent.Comp; + + PlantHolderComponent? plantHolder = null; + PlantTraitsComponent? traits = null; + if (!Resolve(uid, ref plantHolder, ref traits)) return; if (!component.ReadyForHarvest || plantHolder.Dead) @@ -98,24 +108,28 @@ public sealed class HarvestSystem : EntitySystem } // Perform harvest - DoHarvest(uid, args.User, component, plantHolder, traits); + DoHarvest(ent); } - public void DoHarvest(EntityUid plantUid, EntityUid user, HarvestComponent? harvestComp = null, - PlantHolderComponent? plantHolder = null, PlantTraitsComponent? traits = null) + public void DoHarvest(Entity ent) { - if (!Resolve(plantUid, ref harvestComp, ref plantHolder, ref traits)) + var uid = ent.Owner; + var component = ent.Comp; + + PlantHolderComponent? plantHolder = null; + PlantTraitsComponent? traits = null; + if (!Resolve(uid, ref plantHolder, ref traits)) return; if (plantHolder.Dead) { // Remove dead plant - _plantHolder.RemovePlant(plantUid, plantHolder); - AfterHarvest(plantUid, harvestComp, plantHolder); + _plantHolder.RemovePlant(uid, plantHolder); + AfterHarvest((uid, plantHolder)); return; } - if (!harvestComp.ReadyForHarvest) + if (!component.ReadyForHarvest) return; // Spawn products @@ -126,7 +140,7 @@ public sealed class HarvestSystem : EntitySystem { foreach (var productPrototype in plantHolder.Seed.ProductPrototypes) { - var product = Spawn(productPrototype, Transform(plantUid).Coordinates); + var product = Spawn(productPrototype, Transform(uid).Coordinates); // Apply mutations to product if (TryComp(product, out var produce)) @@ -138,42 +152,45 @@ public sealed class HarvestSystem : EntitySystem } // Handle harvest type - switch (harvestComp.HarvestRepeat) + switch (component.HarvestRepeat) { case HarvestType.NoRepeat: - _plantHolder.RemovePlant(plantUid, plantHolder); + _plantHolder.RemovePlant(uid, plantHolder); break; case HarvestType.Repeat: case HarvestType.SelfHarvest: - harvestComp.ReadyForHarvest = false; - harvestComp.LastHarvestTime = plantHolder.Age; + component.ReadyForHarvest = false; + component.LastHarvestTime = plantHolder.Age; plantHolder.Harvest = false; break; } - AfterHarvest(plantUid, harvestComp, plantHolder); + AfterHarvest((uid, plantHolder)); } - private void AfterHarvest(EntityUid uid, HarvestComponent component, PlantHolderComponent plantHolder) + private void AfterHarvest(Entity ent) { + var uid = ent.Owner; + var component = ent.Comp; + // Play scream sound if applicable - if (TryComp(uid, out var traits) && traits.CanScream && plantHolder.Seed != null) + if (TryComp(uid, out var traits) && traits.CanScream && component.Seed != null) { - _audio.PlayPvs(plantHolder.Seed.ScreamSound, uid); + _audio.PlayPvs(component.Seed.ScreamSound, uid); } // Update sprite - _plantHolder.UpdateSprite(uid, plantHolder); + _plantHolder.UpdateSprite(uid, component); } - public void AutoHarvest(EntityUid uid, HarvestComponent? component = null) + /// + /// Auto-harvests a plant. + /// + public void AutoHarvest(Entity ent) { - if (!Resolve(uid, ref component)) + if (!ent.Comp.ReadyForHarvest) return; - if (!component.ReadyForHarvest) - return; - - DoHarvest(uid, uid, component); + DoHarvest(ent); } } diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs index 03608a9a0c3..ef1319fe026 100644 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -1,6 +1,4 @@ using Content.Server.Botany.Components; -using Robust.Shared.Random; -using Robust.Shared.Timing; namespace Content.Server.Botany.Systems; @@ -8,13 +6,11 @@ namespace Content.Server.Botany.Systems; public readonly record struct OnPlantGrowEvent; /// -/// Base system for plant growth mechanics. Provides common functionality for all plant growth systems. +/// Base for botany growth systems, providing shared helpers and constants used by +/// per-trait/per-environment growth handlers. /// public abstract class PlantGrowthSystem : EntitySystem { - [Dependency] protected readonly IRobustRandom _random = default!; - [Dependency] protected readonly IGameTiming _gameTiming = default!; - /// /// Multiplier for plant growth speed in hydroponics. /// @@ -38,10 +34,7 @@ public abstract class PlantGrowthSystem : EntitySystem if (!Resolve(uid, ref component) || component.Seed == null) return; - PlantTraitsComponent? traits = null; - Resolve(uid, ref traits); - - if (traits == null) + if (!TryComp(uid, out var traits)) return; // Synchronize harvest status with HarvestComponent if present diff --git a/Content.Server/Botany/Systems/PlantTraitsSystem.cs b/Content.Server/Botany/Systems/PlantTraitsSystem.cs index 171cab39db9..311a26ba65f 100644 --- a/Content.Server/Botany/Systems/PlantTraitsSystem.cs +++ b/Content.Server/Botany/Systems/PlantTraitsSystem.cs @@ -4,23 +4,26 @@ using Robust.Shared.Random; namespace Content.Server.Botany.Systems; /// -/// System that handles plant traits like lifespan, maturation, production, yield, potency, and growth stages. +/// Applies plant trait effects on growth ticks. /// public sealed class PlantTraitsSystem : PlantGrowthSystem { - [Dependency] private readonly BotanySystem _botany = default!; - [Dependency] private readonly PlantHolderSystem _plantHolder = default!; + [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); } - private void OnPlantGrow(EntityUid uid, PlantTraitsComponent component, OnPlantGrowEvent args) + private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { + var uid = ent.Owner; + var component = ent.Comp; + PlantHolderComponent? holder = null; - Resolve(uid, ref holder); + Resolve(uid, ref holder); if (holder == null || holder.Seed == null || holder.Dead) return; @@ -33,4 +36,14 @@ public sealed class PlantTraitsSystem : PlantGrowthSystem holder.UpdateSpriteAfterUpdate = true; } } + + /// + /// Adjusts the potency of a plant traits component. + /// + public void AdjustPotency(Entity ent, float delta) + { + ref var traits = ref ent.Comp; + traits.Potency = Math.Max(traits.Potency + delta, 1); + Dirty(ent); + } } diff --git a/Content.Server/Botany/Systems/ToxinsSystem.cs b/Content.Server/Botany/Systems/ToxinsSystem.cs index e07ab64c267..e077992e76f 100644 --- a/Content.Server/Botany/Systems/ToxinsSystem.cs +++ b/Content.Server/Botany/Systems/ToxinsSystem.cs @@ -1,23 +1,27 @@ -using System; using Content.Server.Botany.Components; namespace Content.Server.Botany.Systems; /// -/// Handles toxin tolerance and damage for plants. +/// Handles toxin accumulation and tolerance for plants, applying health damage +/// and decrementing toxins based on per-tick uptake. /// public sealed class ToxinsSystem : PlantGrowthSystem { public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); } - private void OnPlantGrow(EntityUid uid, ToxinsComponent component, OnPlantGrowEvent args) + private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { + var uid = ent.Owner; + var component = ent.Comp; + PlantHolderComponent? holder = null; - Resolve(uid, ref holder); + Resolve(uid, ref holder); if (holder == null || holder.Seed == null || holder.Dead) return; diff --git a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs index 08901008ebd..08005ce8d85 100644 --- a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs +++ b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs @@ -2,18 +2,27 @@ using Content.Server.Botany.Components; using Robust.Shared.Random; namespace Content.Server.Botany.Systems; + +/// +/// Applies a death chance and damage to unviable plants each growth tick, updating visuals when necessary. +/// public sealed class UnviableGrowthSystem : PlantGrowthSystem { + [Dependency] private readonly IRobustRandom _random = default!; + public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnPlantGrow); } - private void OnPlantGrow(EntityUid uid, UnviableGrowthComponent component, OnPlantGrowEvent args) + private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { + var uid = ent.Owner; + var component = ent.Comp; + PlantHolderComponent? holder = null; - Resolve(uid, ref holder); + Resolve(uid, ref holder); if (holder == null || holder.Seed == null || holder.Dead) return; diff --git a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs index 030f497c97c..5240917cac8 100644 --- a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs +++ b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs @@ -1,22 +1,32 @@ using Content.Server.Botany.Components; using Content.Shared.Coordinates.Helpers; -using Robust.Server.GameObjects; using Robust.Shared.Random; namespace Content.Server.Botany.Systems; + +/// +/// Manages weed growth and pest damage per growth tick, and handles tray-level +/// weed spawning and kudzu transformation based on conditions. +/// public sealed class WeedPestGrowthSystem : PlantGrowthSystem { + [Dependency] private readonly IRobustRandom _random = default!; + public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); SubscribeLocalEvent(OnTrayUpdate); } - private void OnPlantGrow(EntityUid uid, WeedPestGrowthComponent component, OnPlantGrowEvent args) + private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { + var uid = ent.Owner; + var component = ent.Comp; + PlantHolderComponent? holder = null; - Resolve(uid, ref holder); + Resolve(uid, ref holder); if (holder == null || holder.Seed == null || holder.Dead) return; @@ -41,8 +51,11 @@ public sealed class WeedPestGrowthSystem : PlantGrowthSystem /// /// Handles weed growth and kudzu transformation for plant holder trays. /// - private void OnTrayUpdate(EntityUid uid, PlantHolderComponent component, OnPlantGrowEvent args) + private void OnTrayUpdate(Entity ent, ref OnPlantGrowEvent args) { + var uid = ent.Owner; + var component = ent.Comp; + // Weeds like water and nutrients! They may appear even if there's not a seed planted if (component.WaterLevel > 10 && component.NutritionLevel > 5) { diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs index c30d028d926..f5a5bcbd9dd 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs @@ -7,7 +7,8 @@ namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes; public sealed partial class PlantAdjustPotencyEntityEffectSystem : EntityEffectSystem { - [Dependency] private readonly PlantHolderSystem _plantHolder = default!; + [Dependency] private readonly PlantTraitsSystem _plantTraits = default!; + protected override void Effect(Entity entity, ref EntityEffectEvent args) { if (entity.Comp.Seed == null || entity.Comp.Dead) @@ -16,6 +17,6 @@ public sealed partial class PlantAdjustPotencyEntityEffectSystem : EntityEffectS if (!TryComp(entity, out var traits)) return; - traits.Potency = Math.Max(traits.Potency + args.Effect.Amount, 1); + _plantTraits.AdjustPotency((entity.Owner, traits), args.Effect.Amount); } } From 789f3a02d071170a37d949e0cb0ffa4699472d4d Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Thu, 16 Oct 2025 15:15:23 +0300 Subject: [PATCH 33/53] TODO --- .../Components/GrowthComponentsHolder.cs | 3 +-- Content.Server/Botany/SeedPrototype.cs | 1 + .../Botany/Systems/PlantHolderSystem.cs | 1 + .../PlantChangeStatEntityEffectSystem.cs | 24 ++++++------------- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/Content.Server/Botany/Components/GrowthComponentsHolder.cs b/Content.Server/Botany/Components/GrowthComponentsHolder.cs index af3d4684823..e02f3d066ff 100644 --- a/Content.Server/Botany/Components/GrowthComponentsHolder.cs +++ b/Content.Server/Botany/Components/GrowthComponentsHolder.cs @@ -1,8 +1,7 @@ namespace Content.Server.Botany.Components; /// -/// Holder for plant growth settings serialized from YAML as a map under "growthComponents". -/// Each property corresponds to a concrete growth component type. +/// TODO: Delete after plants transition to entities /// [DataDefinition] public sealed partial class GrowthComponentsHolder diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index d8ff2bbd9ab..e0f2197bb5e 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -151,6 +151,7 @@ public partial class SeedData /// /// The growth components used by this seed. + /// TODO: Delete after plants transition to entities /// [DataField] public GrowthComponentsHolder GrowthComponents = new(); diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 35d59dc1be1..25cc24467d2 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -680,6 +680,7 @@ public sealed class PlantHolderSystem : EntitySystem /// /// Removes all growth-related components from a plant. + /// TODO: Delete after plants transition to entities /// private void RemoveAllGrowthComponents(EntityUid uid) { diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs index 07ba8a11181..08e9b554d87 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs @@ -21,26 +21,16 @@ public sealed partial class PlantChangeStatEntityEffectSystem : EntityEffectSyst var targetValue = args.Effect.TargetValue; - // Build the list of the seed's growth components. - var growthHolder = entity.Comp.Seed.GrowthComponents; - growthHolder.EnsureGrowthComponents(); - - foreach (var prop in typeof(GrowthComponentsHolder).GetProperties()) + // Scan live plant growth components and mutate the first matching field. + foreach (var growthComp in EntityManager.GetComponents(entity.Owner)) { - if (prop.GetValue(growthHolder) is not PlantGrowthComponent) - continue; - - var componentType = prop.PropertyType; - if (!EntityManager.HasComponent(entity, componentType)) - continue; - - var component = EntityManager.GetComponent(entity, componentType); + var componentType = growthComp.GetType(); var field = componentType.GetField(targetValue); if (field == null) continue; - var currentValue = field.GetValue(component); + var currentValue = field.GetValue(growthComp); if (currentValue == null) continue; @@ -48,20 +38,20 @@ public sealed partial class PlantChangeStatEntityEffectSystem : EntityEffectSyst { var floatVal = (float)currentValue; MutateFloat(ref floatVal, args.Effect.MinValue, args.Effect.MaxValue, args.Effect.Steps); - field.SetValue(component, floatVal); + field.SetValue(growthComp, floatVal); return; } else if (field.FieldType == typeof(int)) { var intVal = (int)currentValue; MutateInt(ref intVal, (int)args.Effect.MinValue, (int)args.Effect.MaxValue, args.Effect.Steps); - field.SetValue(component, intVal); + field.SetValue(growthComp, intVal); return; } else if (field.FieldType == typeof(bool)) { var boolVal = (bool)currentValue; - field.SetValue(component, !boolVal); + field.SetValue(growthComp, !boolVal); return; } } From b6a2ee6b1726426fcceeab1bc54c0c828b875d36 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:21:47 +0300 Subject: [PATCH 34/53] fix --- Content.Server/Botany/Components/PlantGrowthComponent.cs | 6 ++---- Content.Server/Botany/Systems/BasicGrowthSystem.cs | 4 ++-- Content.Server/Botany/Systems/PlantGrowthSystem.cs | 7 +++++-- .../PlantAttributes/PlantAffectGrowthEntityEffectSystem.cs | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Content.Server/Botany/Components/PlantGrowthComponent.cs b/Content.Server/Botany/Components/PlantGrowthComponent.cs index fd806cba7b2..6856b2fe05e 100644 --- a/Content.Server/Botany/Components/PlantGrowthComponent.cs +++ b/Content.Server/Botany/Components/PlantGrowthComponent.cs @@ -1,11 +1,11 @@ -using System.Security.Policy; using Robust.Shared.Serialization.Manager; namespace Content.Server.Botany.Components; [RegisterComponent] [DataDefinition] -public partial class PlantGrowthComponent : Component { +public sealed partial class PlantGrowthComponent : Component +{ /// /// Creates a copy of this component. /// @@ -14,5 +14,3 @@ public partial class PlantGrowthComponent : Component { return IoCManager.Resolve().CreateCopy(this, notNullableOverride: true); } } - - diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index 7ef26baea8c..3ba8b10ec93 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -121,7 +121,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem } else { - AffectGrowth(uid, -1, holder); + AffectGrowth((uid, holder), -1); holder.Health -= healthMod; } @@ -131,7 +131,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem } else { - AffectGrowth(uid, -1, holder); + AffectGrowth((uid, holder), -1); holder.Health -= healthMod; } } diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs index ef1319fe026..28380b1cc9f 100644 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -29,9 +29,12 @@ public abstract class PlantGrowthSystem : EntitySystem /// /// Affects the growth of a plant by modifying its age or production timing. /// - public void AffectGrowth(EntityUid uid, int amount, PlantHolderComponent? component = null) + public void AffectGrowth(Entity ent, int amount) { - if (!Resolve(uid, ref component) || component.Seed == null) + var uid = ent.Owner; + var component = ent.Comp; + + if (component.Seed == null) return; if (!TryComp(uid, out var traits)) diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAffectGrowthEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAffectGrowthEntityEffectSystem.cs index 74849db6c78..ed699612e79 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAffectGrowthEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAffectGrowthEntityEffectSystem.cs @@ -14,6 +14,6 @@ public sealed partial class PlantAffectGrowthEntityEffectSystem : EntityEffectSy if (entity.Comp.Seed == null || entity.Comp.Dead) return; - _plantGrowth.AffectGrowth(entity, (int)args.Effect.Amount, entity.Comp); + _plantGrowth.AffectGrowth(entity, (int)args.Effect.Amount); } } From 4f3a8a9ff0aba36c096d199504dcea1fe9436c91 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Thu, 16 Oct 2025 21:16:16 +0300 Subject: [PATCH 35/53] oops --- Content.Server/Botany/Components/PlantGrowthComponent.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Content.Server/Botany/Components/PlantGrowthComponent.cs b/Content.Server/Botany/Components/PlantGrowthComponent.cs index 6856b2fe05e..354fb5caf9d 100644 --- a/Content.Server/Botany/Components/PlantGrowthComponent.cs +++ b/Content.Server/Botany/Components/PlantGrowthComponent.cs @@ -3,8 +3,7 @@ using Robust.Shared.Serialization.Manager; namespace Content.Server.Botany.Components; [RegisterComponent] -[DataDefinition] -public sealed partial class PlantGrowthComponent : Component +public abstract partial class PlantGrowthComponent : Component { /// /// Creates a copy of this component. From 929d2b62358d2bafe3d96a7e8aacfac8c8b5fed4 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Thu, 16 Oct 2025 21:31:16 +0300 Subject: [PATCH 36/53] oops 2 --- Content.Server/Botany/Components/PlantGrowthComponent.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Content.Server/Botany/Components/PlantGrowthComponent.cs b/Content.Server/Botany/Components/PlantGrowthComponent.cs index 354fb5caf9d..7a978ab8cf3 100644 --- a/Content.Server/Botany/Components/PlantGrowthComponent.cs +++ b/Content.Server/Botany/Components/PlantGrowthComponent.cs @@ -2,7 +2,6 @@ using Robust.Shared.Serialization.Manager; namespace Content.Server.Botany.Components; -[RegisterComponent] public abstract partial class PlantGrowthComponent : Component { /// From ac2e23d5c85197a07312b9aaf729ee54760bb8b5 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Fri, 17 Oct 2025 11:58:50 +0300 Subject: [PATCH 37/53] more TODO --- Content.Server/Botany/Components/GrowthComponentsHolder.cs | 7 +++++-- Content.Server/Botany/Systems/BasicGrowthSystem.cs | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Content.Server/Botany/Components/GrowthComponentsHolder.cs b/Content.Server/Botany/Components/GrowthComponentsHolder.cs index e02f3d066ff..1465f7d2fe2 100644 --- a/Content.Server/Botany/Components/GrowthComponentsHolder.cs +++ b/Content.Server/Botany/Components/GrowthComponentsHolder.cs @@ -1,7 +1,10 @@ namespace Content.Server.Botany.Components; /// -/// TODO: Delete after plants transition to entities +/// TODO: Delete after plants transition to entities. +/// This is an intentionally evil approach kept only to simplify the +/// upcoming refactor: plants will become standalone entities that own these components. +/// Once that happens, this holder is no longer needed. /// [DataDefinition] public sealed partial class GrowthComponentsHolder @@ -40,7 +43,7 @@ public sealed partial class GrowthComponentsHolder { if (prop.GetValue(this) == null) { - var instance = System.Activator.CreateInstance(prop.PropertyType); + var instance = Activator.CreateInstance(prop.PropertyType); prop.SetValue(this, instance); } } diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index 3ba8b10ec93..2b710faf062 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -3,7 +3,6 @@ using Content.Shared.Swab; using Robust.Shared.Random; namespace Content.Server.Botany.Systems; -// For all the very common stuff all plants are expected to do. // TODO: make CO2Boost (add potency if the plant can eat an increasing amount of CO2). separate PR post-merge // TODO: make GrowLight (run bonus ticks if theres a grow light nearby). separate PR post-merge. From b51a75204739586c123329d51605b33ba71d5038 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Fri, 17 Oct 2025 12:14:06 +0300 Subject: [PATCH 38/53] fix names --- .../Components/GrowthComponentsHolder.cs | 4 +- .../Botany/Components/HarvestComponent.cs | 27 --- .../Botany/Components/ToxinsComponent.cs | 18 -- .../Botany/Systems/BotanySystem.Seed.cs | 7 +- .../Botany/Systems/HarvestSystem.cs | 196 ------------------ .../Botany/Systems/MutationSystem.cs | 6 +- .../Botany/Systems/PlantGrowthSystem.cs | 4 +- .../Botany/Systems/PlantHolderSystem.cs | 8 +- .../Botany/Systems/SeedExtractorSystem.cs | 2 +- Content.Server/Botany/Systems/ToxinsSystem.cs | 42 ---- .../PlantMutateHarvestEntityEffectSystem.cs | 2 +- 11 files changed, 14 insertions(+), 302 deletions(-) delete mode 100644 Content.Server/Botany/Components/HarvestComponent.cs delete mode 100644 Content.Server/Botany/Components/ToxinsComponent.cs delete mode 100644 Content.Server/Botany/Systems/HarvestSystem.cs delete mode 100644 Content.Server/Botany/Systems/ToxinsSystem.cs diff --git a/Content.Server/Botany/Components/GrowthComponentsHolder.cs b/Content.Server/Botany/Components/GrowthComponentsHolder.cs index 1465f7d2fe2..1f5196427ee 100644 --- a/Content.Server/Botany/Components/GrowthComponentsHolder.cs +++ b/Content.Server/Botany/Components/GrowthComponentsHolder.cs @@ -16,10 +16,10 @@ public sealed partial class GrowthComponentsHolder public BasicGrowthComponent? BasicGrowth { get; set; } [DataField] - public ToxinsComponent? Toxins { get; set; } + public PlantToxinsComponent? Toxins { get; set; } [DataField] - public HarvestComponent? Harvest { get; set; } + public PlantHarvestComponent? Harvest { get; set; } [DataField] public AtmosphericGrowthComponent? AtmosphericGrowth { get; set; } diff --git a/Content.Server/Botany/Components/HarvestComponent.cs b/Content.Server/Botany/Components/HarvestComponent.cs deleted file mode 100644 index 73f35f3765a..00000000000 --- a/Content.Server/Botany/Components/HarvestComponent.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Content.Server.Botany.Systems; - -namespace Content.Server.Botany.Components; - -[RegisterComponent] -[DataDefinition] -public sealed partial class HarvestComponent : PlantGrowthComponent -{ - /// - /// Harvest options are NoRepeat(plant is removed on harvest), Repeat(Plant makes produce every Production ticks), - /// and SelfHarvest (Repeat, plus produce is dropped on the ground near the plant automatically) - /// - [DataField] - public HarvestType HarvestRepeat = HarvestType.NoRepeat; - - /// - /// Whether the plant is currently ready for harvest. - /// - [ViewVariables] - public bool ReadyForHarvest = false; - - /// - /// The last time this plant was harvested. - /// - [ViewVariables] - public float LastHarvestTime = 0f; -} diff --git a/Content.Server/Botany/Components/ToxinsComponent.cs b/Content.Server/Botany/Components/ToxinsComponent.cs deleted file mode 100644 index df45ee2d719..00000000000 --- a/Content.Server/Botany/Components/ToxinsComponent.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Content.Server.Botany.Components; - -[RegisterComponent] -[DataDefinition] -public sealed partial class ToxinsComponent : PlantGrowthComponent -{ - /// - /// Maximum toxin level the plant can tolerate before taking damage. - /// - [DataField] - public float ToxinsTolerance = 4f; - - /// - /// Divisor for calculating toxin uptake rate. Higher values mean slower toxin processing. - /// - [DataField] - public float ToxinUptakeDivisor = 10f; -} diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index 7c1b4244f0a..a58d3ceed88 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -77,16 +77,11 @@ public sealed partial class BotanySystem : EntitySystem return false; } - public PlantTraitsComponent? GetPlantTraits(SeedData seed) + public static PlantTraitsComponent? GetPlantTraits(SeedData seed) { return seed.GrowthComponents.PlantTraits; } - public HarvestComponent? GetHarvestComponent(SeedData seed) - { - return seed.GrowthComponents.Harvest; - } - private void OnExamined(EntityUid uid, SeedComponent component, ExaminedEvent args) { if (!args.IsInDetailsRange) diff --git a/Content.Server/Botany/Systems/HarvestSystem.cs b/Content.Server/Botany/Systems/HarvestSystem.cs deleted file mode 100644 index da0850ffdcc..00000000000 --- a/Content.Server/Botany/Systems/HarvestSystem.cs +++ /dev/null @@ -1,196 +0,0 @@ -using Content.Server.Botany.Components; -using Content.Server.Hands.Systems; -using Content.Server.Popups; -using Content.Shared.Interaction; -using Robust.Shared.Audio.Systems; - -namespace Content.Server.Botany.Systems; - -/// -/// Harvest options for plants. -/// -public enum HarvestType -{ - /// - /// Plant is removed on harvest. - /// - NoRepeat, - - /// - /// Plant makes produce every Production ticks. - /// - Repeat, - - /// - /// Repeat, plus produce is dropped on the ground near the plant automatically. - /// - SelfHarvest -} - -/// -/// Manages harvest readiness and execution for plants, including repeat/self-harvest -/// logic and produce spawning, responding to growth and interaction events. -/// -public sealed class HarvestSystem : EntitySystem -{ - [Dependency] private readonly BotanySystem _botany = default!; - [Dependency] private readonly HandsSystem _hands = default!; - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly PlantHolderSystem _plantHolder = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnPlantGrow); - SubscribeLocalEvent(OnInteractHand); - } - - private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) - { - var uid = ent.Owner; - var component = ent.Comp; - - PlantHolderComponent? plantHolder = null; - PlantTraitsComponent? traits = null; - if (!Resolve(uid, ref plantHolder, ref traits)) - return; - - if (plantHolder.Dead || plantHolder.Seed == null) - return; - - // Check if plant is ready for harvest - if (component.HarvestRepeat == HarvestType.Repeat || component.HarvestRepeat == HarvestType.SelfHarvest) - { - // Repeat harvest - var timeSinceLastHarvest = plantHolder.Age - component.LastHarvestTime; - if (timeSinceLastHarvest > traits.Production && !component.ReadyForHarvest) - { - component.ReadyForHarvest = true; - plantHolder.UpdateSpriteAfterUpdate = true; - } - } - else - { - // Non-repeat harvest - if (plantHolder.Age > traits.Production && !component.ReadyForHarvest) - { - component.ReadyForHarvest = true; - plantHolder.UpdateSpriteAfterUpdate = true; - } - } - } - - private void OnInteractHand(Entity ent, ref InteractHandEvent args) - { - var uid = ent.Owner; - var component = ent.Comp; - - PlantHolderComponent? plantHolder = null; - PlantTraitsComponent? traits = null; - if (!Resolve(uid, ref plantHolder, ref traits)) - return; - - if (!component.ReadyForHarvest || plantHolder.Dead) - return; - - // Check if sharp tool is required - if (traits.Ligneous) - { - if (!_hands.TryGetActiveItem(args.User, out var activeItem) || - plantHolder.Seed == null || - !_botany.CanHarvest(plantHolder.Seed, activeItem)) - { - _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), args.User); - return; - } - } - - // Perform harvest - DoHarvest(ent); - } - - public void DoHarvest(Entity ent) - { - var uid = ent.Owner; - var component = ent.Comp; - - PlantHolderComponent? plantHolder = null; - PlantTraitsComponent? traits = null; - if (!Resolve(uid, ref plantHolder, ref traits)) - return; - - if (plantHolder.Dead) - { - // Remove dead plant - _plantHolder.RemovePlant(uid, plantHolder); - AfterHarvest((uid, plantHolder)); - return; - } - - if (!component.ReadyForHarvest) - return; - - // Spawn products - var yield = traits.Yield; - if (plantHolder.Seed?.ProductPrototypes != null) - { - for (var i = 0; i < yield; i++) - { - foreach (var productPrototype in plantHolder.Seed.ProductPrototypes) - { - var product = Spawn(productPrototype, Transform(uid).Coordinates); - - // Apply mutations to product - if (TryComp(product, out var produce)) - { - _botany.ProduceGrown(product, produce); - } - } - } - } - - // Handle harvest type - switch (component.HarvestRepeat) - { - case HarvestType.NoRepeat: - _plantHolder.RemovePlant(uid, plantHolder); - break; - case HarvestType.Repeat: - case HarvestType.SelfHarvest: - component.ReadyForHarvest = false; - component.LastHarvestTime = plantHolder.Age; - plantHolder.Harvest = false; - break; - } - - AfterHarvest((uid, plantHolder)); - } - - private void AfterHarvest(Entity ent) - { - var uid = ent.Owner; - var component = ent.Comp; - - // Play scream sound if applicable - if (TryComp(uid, out var traits) && traits.CanScream && component.Seed != null) - { - _audio.PlayPvs(component.Seed.ScreamSound, uid); - } - - // Update sprite - _plantHolder.UpdateSprite(uid, component); - } - - /// - /// Auto-harvests a plant. - /// - public void AutoHarvest(Entity ent) - { - if (!ent.Comp.ReadyForHarvest) - return; - - DoHarvest(ent); - } -} diff --git a/Content.Server/Botany/Systems/MutationSystem.cs b/Content.Server/Botany/Systems/MutationSystem.cs index 2401d7ea695..2b23c68a95b 100644 --- a/Content.Server/Botany/Systems/MutationSystem.cs +++ b/Content.Server/Botany/Systems/MutationSystem.cs @@ -84,8 +84,8 @@ public sealed class MutationSystem : EntitySystem CrossChemicals(ref result.Chemicals, a.Chemicals); - var aTraits = _botanySystem.GetPlantTraits(a); - var resultTraits = _botanySystem.GetPlantTraits(result); + var aTraits = BotanySystem.GetPlantTraits(a); + var resultTraits = BotanySystem.GetPlantTraits(result); if (aTraits != null && resultTraits != null) { @@ -104,7 +104,7 @@ public sealed class MutationSystem : EntitySystem // effective hybrid crossings. if (a.Name != result.Name && Random(0.7f)) { - var traits = _botanySystem.GetPlantTraits(result); + var traits = BotanySystem.GetPlantTraits(result); if (traits != null) { traits.Seedless = true; diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs index 28380b1cc9f..d3d82c3568e 100644 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -40,8 +40,8 @@ public abstract class PlantGrowthSystem : EntitySystem if (!TryComp(uid, out var traits)) return; - // Synchronize harvest status with HarvestComponent if present - if (TryComp(uid, out var harvestComp)) + // Synchronize harvest status with PlantHarvestComponent if present + if (TryComp(uid, out var harvestComp)) { component.Harvest = harvestComp.ReadyForHarvest; } diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 25cc24467d2..aa55b56d1f7 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -193,7 +193,7 @@ public sealed class PlantHolderSystem : EntitySystem plantHolder.DrawWarnings = true; // Get endurance from seed's PlantTraitsComponent - var seedTraits = _botany.GetPlantTraits(seed); + var seedTraits = BotanySystem.GetPlantTraits(seed); if (seeds.HealthOverride != null) { plantHolder.Health = seeds.HealthOverride.Value; @@ -380,7 +380,7 @@ public sealed class PlantHolderSystem : EntitySystem var seed = produce.Seed; if (seed != null) { - var seedTraits = _botany.GetPlantTraits(seed); + var seedTraits = BotanySystem.GetPlantTraits(seed); if (seedTraits != null) { var nutrientBonus = seedTraits.Potency / 2.5f; @@ -443,8 +443,8 @@ public sealed class PlantHolderSystem : EntitySystem CheckHealth(uid, component); CheckLevelSanity(uid, component); - // Synchronize harvest status between PlantHolderComponent and HarvestComponent - if (TryComp(uid, out var harvestComp)) + // Synchronize harvest status between PlantHolderComponent and PlantHarvestComponent + if (TryComp(uid, out var harvestComp)) { component.Harvest = harvestComp.ReadyForHarvest; } diff --git a/Content.Server/Botany/Systems/SeedExtractorSystem.cs b/Content.Server/Botany/Systems/SeedExtractorSystem.cs index b56dbe10442..4c6bf508f54 100644 --- a/Content.Server/Botany/Systems/SeedExtractorSystem.cs +++ b/Content.Server/Botany/Systems/SeedExtractorSystem.cs @@ -30,7 +30,7 @@ public sealed class SeedExtractorSystem : EntitySystem if (!_botanySystem.TryGetSeed(produce, out var seed)) return; - var traits = _botanySystem.GetPlantTraits(seed); + var traits = BotanySystem.GetPlantTraits(seed); if (traits?.Seedless == true) { _popupSystem.PopupCursor(Loc.GetString("seed-extractor-component-no-seeds", ("name", args.Used)), diff --git a/Content.Server/Botany/Systems/ToxinsSystem.cs b/Content.Server/Botany/Systems/ToxinsSystem.cs deleted file mode 100644 index e077992e76f..00000000000 --- a/Content.Server/Botany/Systems/ToxinsSystem.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Content.Server.Botany.Components; - -namespace Content.Server.Botany.Systems; - -/// -/// Handles toxin accumulation and tolerance for plants, applying health damage -/// and decrementing toxins based on per-tick uptake. -/// -public sealed class ToxinsSystem : PlantGrowthSystem -{ - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnPlantGrow); - } - - private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) - { - var uid = ent.Owner; - var component = ent.Comp; - - PlantHolderComponent? holder = null; - Resolve(uid, ref holder); - - if (holder == null || holder.Seed == null || holder.Dead) - return; - - if (holder.Toxins > 0) - { - var toxinUptake = MathF.Max(1, MathF.Round(holder.Toxins / component.ToxinUptakeDivisor)); - if (holder.Toxins > component.ToxinsTolerance) - { - holder.Health -= toxinUptake; - } - - holder.Toxins -= toxinUptake; - if (holder.DrawWarnings) - holder.UpdateSpriteAfterUpdate = true; - } - } -} diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantMutateHarvestEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantMutateHarvestEntityEffectSystem.cs index 8f385f423c2..5377eaa29e7 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantMutateHarvestEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantMutateHarvestEntityEffectSystem.cs @@ -12,7 +12,7 @@ public sealed partial class PlantMutateHarvestEntityEffectSystem : EntityEffectS if (entity.Comp.Seed == null) return; - var harvest = EnsureComp(entity); + var harvest = EnsureComp(entity); switch (harvest.HarvestRepeat) { case HarvestType.NoRepeat: From 78e7d43301c18ccdcef1cfed9c09ebf89993c0ca Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Fri, 17 Oct 2025 12:16:59 +0300 Subject: [PATCH 39/53] fix names oops --- .../Components/PlantHarvestComponent.cs | 27 +++ .../Botany/Components/PlantToxinsComponent.cs | 18 ++ .../Botany/Systems/PlantHarvestSystem.cs | 196 ++++++++++++++++++ .../Botany/Systems/PlantToxinsSystem.cs | 42 ++++ 4 files changed, 283 insertions(+) create mode 100644 Content.Server/Botany/Components/PlantHarvestComponent.cs create mode 100644 Content.Server/Botany/Components/PlantToxinsComponent.cs create mode 100644 Content.Server/Botany/Systems/PlantHarvestSystem.cs create mode 100644 Content.Server/Botany/Systems/PlantToxinsSystem.cs diff --git a/Content.Server/Botany/Components/PlantHarvestComponent.cs b/Content.Server/Botany/Components/PlantHarvestComponent.cs new file mode 100644 index 00000000000..78577b2118d --- /dev/null +++ b/Content.Server/Botany/Components/PlantHarvestComponent.cs @@ -0,0 +1,27 @@ +using Content.Server.Botany.Systems; + +namespace Content.Server.Botany.Components; + +[RegisterComponent] +[DataDefinition] +public sealed partial class PlantHarvestComponent : PlantGrowthComponent +{ + /// + /// Harvest options are NoRepeat(plant is removed on harvest), Repeat(Plant makes produce every Production ticks), + /// and SelfHarvest (Repeat, plus produce is dropped on the ground near the plant automatically) + /// + [DataField] + public HarvestType HarvestRepeat = HarvestType.NoRepeat; + + /// + /// Whether the plant is currently ready for harvest. + /// + [ViewVariables] + public bool ReadyForHarvest = false; + + /// + /// The last time this plant was harvested. + /// + [ViewVariables] + public float LastHarvestTime = 0f; +} diff --git a/Content.Server/Botany/Components/PlantToxinsComponent.cs b/Content.Server/Botany/Components/PlantToxinsComponent.cs new file mode 100644 index 00000000000..a0295a98d19 --- /dev/null +++ b/Content.Server/Botany/Components/PlantToxinsComponent.cs @@ -0,0 +1,18 @@ +namespace Content.Server.Botany.Components; + +[RegisterComponent] +[DataDefinition] +public sealed partial class PlantToxinsComponent : PlantGrowthComponent +{ + /// + /// Maximum toxin level the plant can tolerate before taking damage. + /// + [DataField] + public float ToxinsTolerance = 4f; + + /// + /// Divisor for calculating toxin uptake rate. Higher values mean slower toxin processing. + /// + [DataField] + public float ToxinUptakeDivisor = 10f; +} diff --git a/Content.Server/Botany/Systems/PlantHarvestSystem.cs b/Content.Server/Botany/Systems/PlantHarvestSystem.cs new file mode 100644 index 00000000000..88d3b6bfa6b --- /dev/null +++ b/Content.Server/Botany/Systems/PlantHarvestSystem.cs @@ -0,0 +1,196 @@ +using Content.Server.Botany.Components; +using Content.Server.Hands.Systems; +using Content.Server.Popups; +using Content.Shared.Interaction; +using Robust.Shared.Audio.Systems; + +namespace Content.Server.Botany.Systems; + +/// +/// Harvest options for plants. +/// +public enum HarvestType +{ + /// + /// Plant is removed on harvest. + /// + NoRepeat, + + /// + /// Plant makes produce every Production ticks. + /// + Repeat, + + /// + /// Repeat, plus produce is dropped on the ground near the plant automatically. + /// + SelfHarvest +} + +/// +/// Manages harvest readiness and execution for plants, including repeat/self-harvest +/// logic and produce spawning, responding to growth and interaction events. +/// +public sealed class HarvestSystem : EntitySystem +{ + [Dependency] private readonly BotanySystem _botany = default!; + [Dependency] private readonly HandsSystem _hands = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly PlantHolderSystem _plantHolder = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPlantGrow); + SubscribeLocalEvent(OnInteractHand); + } + + private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) + { + var uid = ent.Owner; + var component = ent.Comp; + + PlantHolderComponent? plantHolder = null; + PlantTraitsComponent? traits = null; + if (!Resolve(uid, ref plantHolder, ref traits)) + return; + + if (plantHolder.Dead || plantHolder.Seed == null) + return; + + // Check if plant is ready for harvest + if (component.HarvestRepeat == HarvestType.Repeat || component.HarvestRepeat == HarvestType.SelfHarvest) + { + // Repeat harvest + var timeSinceLastHarvest = plantHolder.Age - component.LastHarvestTime; + if (timeSinceLastHarvest > traits.Production && !component.ReadyForHarvest) + { + component.ReadyForHarvest = true; + plantHolder.UpdateSpriteAfterUpdate = true; + } + } + else + { + // Non-repeat harvest + if (plantHolder.Age > traits.Production && !component.ReadyForHarvest) + { + component.ReadyForHarvest = true; + plantHolder.UpdateSpriteAfterUpdate = true; + } + } + } + + private void OnInteractHand(Entity ent, ref InteractHandEvent args) + { + var uid = ent.Owner; + var component = ent.Comp; + + PlantHolderComponent? plantHolder = null; + PlantTraitsComponent? traits = null; + if (!Resolve(uid, ref plantHolder, ref traits)) + return; + + if (!component.ReadyForHarvest || plantHolder.Dead) + return; + + // Check if sharp tool is required + if (traits.Ligneous) + { + if (!_hands.TryGetActiveItem(args.User, out var activeItem) || + plantHolder.Seed == null || + !_botany.CanHarvest(plantHolder.Seed, activeItem)) + { + _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), args.User); + return; + } + } + + // Perform harvest + DoHarvest(ent); + } + + public void DoHarvest(Entity ent) + { + var uid = ent.Owner; + var component = ent.Comp; + + PlantHolderComponent? plantHolder = null; + PlantTraitsComponent? traits = null; + if (!Resolve(uid, ref plantHolder, ref traits)) + return; + + if (plantHolder.Dead) + { + // Remove dead plant + _plantHolder.RemovePlant(uid, plantHolder); + AfterHarvest((uid, plantHolder)); + return; + } + + if (!component.ReadyForHarvest) + return; + + // Spawn products + var yield = traits.Yield; + if (plantHolder.Seed?.ProductPrototypes != null) + { + for (var i = 0; i < yield; i++) + { + foreach (var productPrototype in plantHolder.Seed.ProductPrototypes) + { + var product = Spawn(productPrototype, Transform(uid).Coordinates); + + // Apply mutations to product + if (TryComp(product, out var produce)) + { + _botany.ProduceGrown(product, produce); + } + } + } + } + + // Handle harvest type + switch (component.HarvestRepeat) + { + case HarvestType.NoRepeat: + _plantHolder.RemovePlant(uid, plantHolder); + break; + case HarvestType.Repeat: + case HarvestType.SelfHarvest: + component.ReadyForHarvest = false; + component.LastHarvestTime = plantHolder.Age; + plantHolder.Harvest = false; + break; + } + + AfterHarvest((uid, plantHolder)); + } + + private void AfterHarvest(Entity ent) + { + var uid = ent.Owner; + var component = ent.Comp; + + // Play scream sound if applicable + if (TryComp(uid, out var traits) && traits.CanScream && component.Seed != null) + { + _audio.PlayPvs(component.Seed.ScreamSound, uid); + } + + // Update sprite + _plantHolder.UpdateSprite(uid, component); + } + + /// + /// Auto-harvests a plant. + /// + public void AutoHarvest(Entity ent) + { + if (!ent.Comp.ReadyForHarvest) + return; + + DoHarvest(ent); + } +} diff --git a/Content.Server/Botany/Systems/PlantToxinsSystem.cs b/Content.Server/Botany/Systems/PlantToxinsSystem.cs new file mode 100644 index 00000000000..9c681f1e568 --- /dev/null +++ b/Content.Server/Botany/Systems/PlantToxinsSystem.cs @@ -0,0 +1,42 @@ +using Content.Server.Botany.Components; + +namespace Content.Server.Botany.Systems; + +/// +/// Handles toxin accumulation and tolerance for plants, applying health damage +/// and decrementing toxins based on per-tick uptake. +/// +public sealed class ToxinsSystem : PlantGrowthSystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPlantGrow); + } + + private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) + { + var uid = ent.Owner; + var component = ent.Comp; + + PlantHolderComponent? holder = null; + Resolve(uid, ref holder); + + if (holder == null || holder.Seed == null || holder.Dead) + return; + + if (holder.Toxins > 0) + { + var toxinUptake = MathF.Max(1, MathF.Round(holder.Toxins / component.ToxinUptakeDivisor)); + if (holder.Toxins > component.ToxinsTolerance) + { + holder.Health -= toxinUptake; + } + + holder.Toxins -= toxinUptake; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + } +} From 8f446dfddbf669391163b0aa0462a20fcd1c9d6c Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Thu, 23 Oct 2025 16:39:46 +0300 Subject: [PATCH 40/53] Harvest bug fixes --- .../Botany/Components/PlantGrowthComponent.cs | 5 +- .../Components/PlantHarvestComponent.cs | 26 ++++++- .../Botany/Components/PlantHolderComponent.cs | 12 --- .../Botany/Components/PlantTraitsComponent.cs | 3 + .../Botany/Systems/PlantGrowthSystem.cs | 18 ++--- .../Botany/Systems/PlantHarvestSystem.cs | 73 ++++++------------- .../Botany/Systems/PlantHolderSystem.cs | 66 +++++++---------- .../PlantCryoxadoneEntityEffectSystem.cs | 8 +- .../Botany/PlantMutateGasesEntityEffect.cs | 6 +- .../guidebook/entity-effects/effects.ftl | 4 +- 10 files changed, 100 insertions(+), 121 deletions(-) diff --git a/Content.Server/Botany/Components/PlantGrowthComponent.cs b/Content.Server/Botany/Components/PlantGrowthComponent.cs index 7a978ab8cf3..29aecf71897 100644 --- a/Content.Server/Botany/Components/PlantGrowthComponent.cs +++ b/Content.Server/Botany/Components/PlantGrowthComponent.cs @@ -2,10 +2,13 @@ using Robust.Shared.Serialization.Manager; namespace Content.Server.Botany.Components; +/// +/// Base class for plant growth components. +/// public abstract partial class PlantGrowthComponent : Component { /// - /// Creates a copy of this component. + /// Creates a copy of this growth components. /// public PlantGrowthComponent DupeComponent() { diff --git a/Content.Server/Botany/Components/PlantHarvestComponent.cs b/Content.Server/Botany/Components/PlantHarvestComponent.cs index 78577b2118d..596965b4147 100644 --- a/Content.Server/Botany/Components/PlantHarvestComponent.cs +++ b/Content.Server/Botany/Components/PlantHarvestComponent.cs @@ -7,8 +7,7 @@ namespace Content.Server.Botany.Components; public sealed partial class PlantHarvestComponent : PlantGrowthComponent { /// - /// Harvest options are NoRepeat(plant is removed on harvest), Repeat(Plant makes produce every Production ticks), - /// and SelfHarvest (Repeat, plus produce is dropped on the ground near the plant automatically) + /// Harvest repeat type. /// [DataField] public HarvestType HarvestRepeat = HarvestType.NoRepeat; @@ -23,5 +22,26 @@ public sealed partial class PlantHarvestComponent : PlantGrowthComponent /// The last time this plant was harvested. /// [ViewVariables] - public float LastHarvestTime = 0f; + public int LastHarvest = 0; +} + +/// +/// Harvest options for plants. +/// +public enum HarvestType +{ + /// + /// Plant is removed on harvest. + /// + NoRepeat, + + /// + /// Plant makes produce every Production ticks. + /// + Repeat, + + /// + /// Repeat, plus produce is dropped on the ground near the plant automatically. + /// + SelfHarvest } diff --git a/Content.Server/Botany/Components/PlantHolderComponent.cs b/Content.Server/Botany/Components/PlantHolderComponent.cs index 7e033fb96bb..f12c8ad0a61 100644 --- a/Content.Server/Botany/Components/PlantHolderComponent.cs +++ b/Content.Server/Botany/Components/PlantHolderComponent.cs @@ -13,12 +13,6 @@ public sealed partial class PlantHolderComponent : Component [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] public TimeSpan NextUpdate = TimeSpan.Zero; - /// - /// Age when the plant last produced harvestable items. - /// - [DataField] - public int LastProduce; - /// /// Number of missing gases required for plant growth. /// @@ -110,12 +104,6 @@ public sealed partial class PlantHolderComponent : Component [DataField] public bool Dead; - /// - /// Whether the plant is ready for harvest. - /// - [DataField] - public bool Harvest; - /// /// Set to true if this plant has been clipped by seed clippers. Used to prevent a single plant /// from repeatedly being clipped. diff --git a/Content.Server/Botany/Components/PlantTraitsComponent.cs b/Content.Server/Botany/Components/PlantTraitsComponent.cs index fc7ee2f7470..bbee4366c4c 100644 --- a/Content.Server/Botany/Components/PlantTraitsComponent.cs +++ b/Content.Server/Botany/Components/PlantTraitsComponent.cs @@ -48,6 +48,9 @@ public sealed partial class PlantTraitsComponent : PlantGrowthComponent [DataField] public float Potency = 1f; + /// TODO: The logic for these fields is quite hardcoded. + /// They require a separate component and a system that will use events or APIs from other growth systems. + /// /// If true, produce can't be put into the seed maker. /// diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs index d3d82c3568e..a60d20f9ebd 100644 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -37,28 +37,24 @@ public abstract class PlantGrowthSystem : EntitySystem if (component.Seed == null) return; - if (!TryComp(uid, out var traits)) + PlantHarvestComponent? harvest = null; + PlantTraitsComponent? traits = null; + if (!Resolve(uid, ref harvest, ref traits)) return; - // Synchronize harvest status with PlantHarvestComponent if present - if (TryComp(uid, out var harvestComp)) - { - component.Harvest = harvestComp.ReadyForHarvest; - } - if (amount > 0) { if (component.Age < traits.Maturation) component.Age += amount; - else if (!component.Harvest && traits.Yield <= 0f) - component.LastProduce -= amount; + else if (!harvest.ReadyForHarvest && traits.Yield <= 0f) + harvest.LastHarvest -= amount; } else { if (component.Age < traits.Maturation) component.SkipAging++; - else if (!component.Harvest && traits.Yield <= 0f) - component.LastProduce += amount; + else if (!harvest.ReadyForHarvest && traits.Yield <= 0f) + harvest.LastHarvest += amount; } } } diff --git a/Content.Server/Botany/Systems/PlantHarvestSystem.cs b/Content.Server/Botany/Systems/PlantHarvestSystem.cs index 88d3b6bfa6b..b19355f3149 100644 --- a/Content.Server/Botany/Systems/PlantHarvestSystem.cs +++ b/Content.Server/Botany/Systems/PlantHarvestSystem.cs @@ -6,27 +6,6 @@ using Robust.Shared.Audio.Systems; namespace Content.Server.Botany.Systems; -/// -/// Harvest options for plants. -/// -public enum HarvestType -{ - /// - /// Plant is removed on harvest. - /// - NoRepeat, - - /// - /// Plant makes produce every Production ticks. - /// - Repeat, - - /// - /// Repeat, plus produce is dropped on the ground near the plant automatically. - /// - SelfHarvest -} - /// /// Manages harvest readiness and execution for plants, including repeat/self-harvest /// logic and produce spawning, responding to growth and interaction events. @@ -60,25 +39,16 @@ public sealed class HarvestSystem : EntitySystem if (plantHolder.Dead || plantHolder.Seed == null) return; + if (component.ReadyForHarvest && component.HarvestRepeat == HarvestType.SelfHarvest) + AutoHarvest(ent); + // Check if plant is ready for harvest - if (component.HarvestRepeat == HarvestType.Repeat || component.HarvestRepeat == HarvestType.SelfHarvest) + var timeLastHarvest = plantHolder.Age - component.LastHarvest; + if (timeLastHarvest > traits.Production && !component.ReadyForHarvest) { - // Repeat harvest - var timeSinceLastHarvest = plantHolder.Age - component.LastHarvestTime; - if (timeSinceLastHarvest > traits.Production && !component.ReadyForHarvest) - { - component.ReadyForHarvest = true; - plantHolder.UpdateSpriteAfterUpdate = true; - } - } - else - { - // Non-repeat harvest - if (plantHolder.Age > traits.Production && !component.ReadyForHarvest) - { - component.ReadyForHarvest = true; - plantHolder.UpdateSpriteAfterUpdate = true; - } + component.ReadyForHarvest = true; + component.LastHarvest = plantHolder.Age; + plantHolder.UpdateSpriteAfterUpdate = true; } } @@ -125,7 +95,7 @@ public sealed class HarvestSystem : EntitySystem { // Remove dead plant _plantHolder.RemovePlant(uid, plantHolder); - AfterHarvest((uid, plantHolder)); + AfterHarvest(ent); return; } @@ -160,27 +130,32 @@ public sealed class HarvestSystem : EntitySystem case HarvestType.Repeat: case HarvestType.SelfHarvest: component.ReadyForHarvest = false; - component.LastHarvestTime = plantHolder.Age; - plantHolder.Harvest = false; + component.LastHarvest = plantHolder.Age; break; } - AfterHarvest((uid, plantHolder)); + AfterHarvest(ent); } - private void AfterHarvest(Entity ent) + private void AfterHarvest(Entity ent) { var uid = ent.Owner; var component = ent.Comp; + PlantTraitsComponent? traits = null; + PlantHolderComponent? plantHolder = null; + if (!Resolve(uid, ref traits, ref plantHolder)) + return; + + component.ReadyForHarvest = false; + component.LastHarvest = plantHolder.Age; + // Play scream sound if applicable - if (TryComp(uid, out var traits) && traits.CanScream && component.Seed != null) - { - _audio.PlayPvs(component.Seed.ScreamSound, uid); - } + if (traits.CanScream && plantHolder.Seed != null) + _audio.PlayPvs(plantHolder.Seed.ScreamSound, uid); // Update sprite - _plantHolder.UpdateSprite(uid, component); + _plantHolder.UpdateSprite(uid, plantHolder); } /// @@ -191,6 +166,6 @@ public sealed class HarvestSystem : EntitySystem if (!ent.Comp.ReadyForHarvest) return; - DoHarvest(ent); + AfterHarvest(ent); } } diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index aa55b56d1f7..030eaad6377 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -83,7 +83,7 @@ public sealed class PlantHolderSystem : EntitySystem return 0; if (!TryComp(uid, out var traits)) - return 1; + return 0; var result = Math.Max(1, (int)(component.Age * traits.GrowthStages / traits.Maturation)); return result; @@ -311,7 +311,7 @@ public sealed class PlantHolderSystem : EntitySystem plantHolder.Health -= _random.Next(3, 5) * 10; float? healthOverride; - if (plantHolder.Harvest) + if (TryComp(uid, out var harvest) && harvest.ReadyForHarvest) { healthOverride = null; } @@ -443,12 +443,6 @@ public sealed class PlantHolderSystem : EntitySystem CheckHealth(uid, component); CheckLevelSanity(uid, component); - // Synchronize harvest status between PlantHolderComponent and PlantHarvestComponent - if (TryComp(uid, out var harvestComp)) - { - component.Harvest = harvestComp.ReadyForHarvest; - } - if (component.UpdateSpriteAfterUpdate) UpdateSprite(uid, component); } @@ -491,13 +485,14 @@ public sealed class PlantHolderSystem : EntitySystem } } - public void Die(EntityUid uid, PlantHolderComponent? component = null) + public void Die(EntityUid uid, PlantHolderComponent component) { - if (!Resolve(uid, ref component)) + PlantHarvestComponent? harvest = null; + if (!Resolve(uid, ref harvest)) return; component.Dead = true; - component.Harvest = false; + harvest.ReadyForHarvest = false; component.MutationLevel = 0; component.YieldMod = 1; component.MutationMod = 1; @@ -510,7 +505,8 @@ public sealed class PlantHolderSystem : EntitySystem public void RemovePlant(EntityUid uid, PlantHolderComponent? component = null) { - if (!Resolve(uid, ref component) || component.Seed == null) + PlantHarvestComponent? harvest = null; + if (!Resolve(uid, ref component, ref harvest) || component.Seed == null) return; // Remove all growth components before planting new seed @@ -522,9 +518,9 @@ public sealed class PlantHolderSystem : EntitySystem component.Seed = null; component.Dead = false; component.Age = 0; - component.LastProduce = 0; + harvest.LastHarvest = 0; component.Sampled = false; - component.Harvest = false; + harvest.ReadyForHarvest = false; component.ImproperPressure = false; component.ImproperHeat = false; @@ -586,10 +582,11 @@ public sealed class PlantHolderSystem : EntitySystem } } - public void UpdateSprite(EntityUid uid, PlantHolderComponent? component = null) + public void UpdateSprite(EntityUid uid, PlantHolderComponent component) { - if (!Resolve(uid, ref component)) - return; + PlantHarvestComponent? harvest = null; + PlantTraitsComponent? traits = null; + Resolve(uid, ref harvest, ref traits); component.UpdateSpriteAfterUpdate = false; @@ -603,14 +600,8 @@ public sealed class PlantHolderSystem : EntitySystem _appearance.SetData(uid, PlantHolderVisuals.HealthLight, false, app); _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, false, app); } - else + else if (harvest != null && traits != null) { - // Have a seed, require traits for detailed visuals. - TryComp(uid, out var traits); - - if (traits == null) - return; - if (component.DrawWarnings) { _appearance.SetData(uid, PlantHolderVisuals.HealthLight, component.Health <= traits.Endurance / 2f); @@ -621,26 +612,23 @@ public sealed class PlantHolderSystem : EntitySystem _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app); _appearance.SetData(uid, PlantHolderVisuals.PlantState, "dead", app); } - else if (component.Harvest) + else if (harvest.ReadyForHarvest) { _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app); _appearance.SetData(uid, PlantHolderVisuals.PlantState, "harvest", app); } + else if (component.Age < traits.Maturation) + { + var growthStage = GetCurrentGrowthStage((uid, component)); + + _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app); + _appearance.SetData(uid, PlantHolderVisuals.PlantState, $"stage-{growthStage}", app); + harvest.LastHarvest = component.Age; + } else { - if (component.Age < traits.Maturation) - { - var growthStage = GetCurrentGrowthStage((uid, component)); - - _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-{traits.GrowthStages}", app); - } + _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app); + _appearance.SetData(uid, PlantHolderVisuals.PlantState, $"stage-{traits.GrowthStages}", app); } } @@ -652,7 +640,7 @@ public sealed class PlantHolderSystem : EntitySystem _appearance.SetData(uid, PlantHolderVisuals.AlertLight, component.WeedLevel >= 5 || component.PestLevel >= 5 || component.Toxins >= 40 || component.ImproperHeat || component.ImproperPressure || component.MissingGas > 0, app); - _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, component.Harvest, app); + _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, harvest != null && harvest.ReadyForHarvest, app); } /// diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs index b403e47cd21..ba21d0f8240 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs @@ -14,17 +14,21 @@ public sealed partial class PlantCryoxadoneEntityEffectSystem : EntityEffectSyst if (entity.Comp.Seed == null || entity.Comp.Dead) return; - var deviation = 0; if (!TryComp(entity, out var traits)) return; + if (!TryComp(entity, out var harvest)) + return; + + int deviation; if (entity.Comp.Age > traits.Maturation) deviation = (int)Math.Max(traits.Maturation - 1, entity.Comp.Age - _random.Next(7, 10)); else deviation = (int)(traits.Maturation / traits.GrowthStages); + entity.Comp.Age -= deviation; - entity.Comp.LastProduce = entity.Comp.Age; entity.Comp.SkipAging++; entity.Comp.ForceUpdate = true; + harvest.LastHarvest = entity.Comp.Age; } } diff --git a/Content.Shared/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffect.cs b/Content.Shared/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffect.cs index 2e19b234401..692bd0c400f 100644 --- a/Content.Shared/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffect.cs +++ b/Content.Shared/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffect.cs @@ -16,7 +16,8 @@ public sealed partial class PlantMutateConsumeGases : EntityEffectBase public override string? EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) { - return Loc.GetString("reagent-effect-guidebook-plant-mutate-consume-gasses", + return Loc.GetString("entity-effect-guidebook-plant-mutate-consume-gasses", + ("chance", Probability), ("minValue", MinValue), ("maxValue", MaxValue)); } @@ -33,7 +34,8 @@ public sealed partial class PlantMutateExudeGases : EntityEffectBase public override string? EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) { - return Loc.GetString("reagent-effect-guidebook-plant-mutate-exude-gasses", + return Loc.GetString("entity-effect-guidebook-plant-mutate-exude-gasses", + ("chance", Probability), ("minValue", MinValue), ("maxValue", MaxValue)); } diff --git a/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl b/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl index 9b4dd5994c7..98ad45a0edc 100644 --- a/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl +++ b/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl @@ -501,13 +501,13 @@ entity-effect-guidebook-plant-seeds-remove = *[other] remove the } seeds of the plant -reagent-effect-guidebook-plant-mutate-exude-gasses = +entity-effect-guidebook-plant-mutate-exude-gasses = { $chance -> [1] Mutates *[other] mutate } the plant to exude gases between {$minValue} and {$maxValue} moles -reagent-effect-guidebook-plant-mutate-consume-gasses = +entity-effect-guidebook-plant-mutate-consume-gasses = { $chance -> [1] Mutates *[other] mutate From fac0feb8ed9c92c171a81475f1f915e48a82038c Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Thu, 23 Oct 2025 17:17:45 +0300 Subject: [PATCH 41/53] fix tests --- Content.Server/Botany/Systems/PlantHolderSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 030eaad6377..62201bba6e6 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -586,7 +586,7 @@ public sealed class PlantHolderSystem : EntitySystem { PlantHarvestComponent? harvest = null; PlantTraitsComponent? traits = null; - Resolve(uid, ref harvest, ref traits); + Resolve(uid, ref harvest, ref traits, false); component.UpdateSpriteAfterUpdate = false; From 860e4a83bad1616858c931eb76ce3e7a73cd8031 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sun, 7 Dec 2025 22:35:53 +0300 Subject: [PATCH 42/53] refactor: cleanup after mering master --- .../Botany/Components/AtmosphericGrowthComponent.cs | 6 ++++-- Content.Server/Botany/SeedPrototype.cs | 2 +- Content.Server/Botany/Systems/BotanySystem.Produce.cs | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs b/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs index 61ed42cc42c..8eeb72bd23a 100644 --- a/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs +++ b/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs @@ -1,3 +1,5 @@ +using Content.Shared.Atmos; + namespace Content.Server.Botany.Components; [RegisterComponent] @@ -8,13 +10,13 @@ public sealed partial class AtmosphericGrowthComponent : PlantGrowthComponent /// Ideal temperature for plant growth in Kelvin. /// [DataField] - public float IdealHeat = 293f; + public float IdealHeat = Atmospherics.T20C; /// /// Temperature tolerance range around ideal heat. /// [DataField] - public float HeatTolerance = 10f; + public float HeatTolerance = Atmospherics.T0C + 10f; /// /// Minimum pressure tolerance for plant growth. diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index bee6443f5a6..26901cb0c45 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -1,4 +1,4 @@ -using Content.Shared.Atmos; +using Content.Server.Botany.Components; using Content.Shared.Database; using Content.Shared.FixedPoint; using Content.Shared.Random; diff --git a/Content.Server/Botany/Systems/BotanySystem.Produce.cs b/Content.Server/Botany/Systems/BotanySystem.Produce.cs index 2e97786a95f..d0134c7de33 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Produce.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Produce.cs @@ -34,12 +34,12 @@ public sealed partial class BotanySystem foreach (var (chem, quantity) in seed.Chemicals) { - var amount = FixedPoint2.New(quantity.Min); + var amount = quantity.Min; if (quantity.PotencyDivisor > 0 && traits.Potency > 0) amount += FixedPoint2.New(traits.Potency / quantity.PotencyDivisor); - amount = FixedPoint2.New(MathHelper.Clamp(amount.Float(), quantity.Min, quantity.Max)); + amount = FixedPoint2.Clamp(amount, quantity.Min, quantity.Max); solutionContainer.MaxVolume += amount; solutionContainer.AddReagent(chem, amount); } From 488fab34090adf2ce7331484dabab415cc931369 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Mon, 8 Dec 2025 21:53:46 +0200 Subject: [PATCH 43/53] Update ConsumeExudeGasGrowthSystem.cs --- .../Systems/ConsumeExudeGasGrowthSystem.cs | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs index bf02a5de509..3ea3d58b196 100644 --- a/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs +++ b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs @@ -25,32 +25,47 @@ public sealed class ConsumeExudeGasGrowthSystem : PlantGrowthSystem var component = ent.Comp; PlantHolderComponent? holder = null; - Resolve(uid, ref holder); + PlantTraitsComponent? traits = null; + if (!Resolve(uid, ref holder, ref traits)) + return; - if (holder == null || holder.Seed == null || holder.Dead) + if (holder.Seed == null || holder.Dead) return; var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; - // Consume Gasses - foreach (var (gas, amount) in component.ConsumeGasses) + // Consume Gasses. + holder.MissingGas = 0; + if (component.ConsumeGasses.Count > 0) { - if (environment.GetMoles(gas) >= amount) + foreach (var (gas, amount) in component.ConsumeGasses) { + if (environment.GetMoles(gas) < amount) + { + holder.MissingGas++; + continue; + } + environment.AdjustMoles(gas, -amount); } + + if (holder.MissingGas > 0) + { + holder.Health -= holder.MissingGas * HydroponicsSpeedMultiplier; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } } - // Exude Gasses - foreach (var (gas, amount) in component.ExudeGasses) + // Exude Gasses. + var exudeCount = component.ExudeGasses.Count; + if (exudeCount > 0) { - environment.AdjustMoles(gas, amount); - } - - var containingMixture = _atmosphere.GetContainingMixture(uid, true, true); - if (containingMixture != null) - { - _atmosphere.Merge(containingMixture, environment); + foreach (var (gas, amount) in component.ExudeGasses) + { + environment.AdjustMoles(gas, + MathF.Max(1f, MathF.Round(amount * MathF.Round(traits.Potency) / exudeCount))); + } } } } From fe8c9c7e5c6c696659c982905fc25565491aef93 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Fri, 12 Dec 2025 23:05:58 +0300 Subject: [PATCH 44/53] refactor: make this piece of happiness and ponies work --- .../Components/AtmosphericGrowthComponent.cs | 9 +- .../Botany/Components/BasicGrowthComponent.cs | 5 +- .../Botany/Components/BotanySwabComponent.cs | 5 +- .../ConsumeExudeGasGrowthComponent.cs | 5 +- .../Components/GrowthComponentsHolder.cs | 34 +++++- .../Botany/Components/PlantComponent.cs | 3 +- .../Botany/Components/PlantGrowthComponent.cs | 17 --- .../Components/PlantHarvestComponent.cs | 5 +- .../Botany/Components/PlantHolderComponent.cs | 3 + .../Botany/Components/PlantToxinsComponent.cs | 5 +- .../Botany/Components/PlantTraitsComponent.cs | 5 +- .../Botany/Components/ProduceComponent.cs | 3 + .../Botany/Components/SeedComponent.cs | 13 ++- .../Components/UnviableGrowthComponent.cs | 3 +- .../Components/WeedPestGrowthComponent.cs | 6 +- Content.Server/Botany/SeedPrototype.cs | 15 ++- .../Botany/Systems/AtmosphericGrowthSystem.cs | 5 +- .../Botany/Systems/BasicGrowthSystem.cs | 6 +- .../Botany/Systems/BotanySwabSystem.cs | 5 - .../Botany/Systems/BotanySystem.Produce.cs | 2 +- .../Botany/Systems/BotanySystem.Seed.cs | 9 +- .../Systems/ConsumeExudeGasGrowthSystem.cs | 3 +- .../Botany/Systems/MutationSystem.cs | 23 ++-- .../Botany/Systems/PlantGrowthSystem.cs | 15 +-- .../Botany/Systems/PlantHarvestSystem.cs | 101 ++++++++---------- .../Botany/Systems/PlantHolderSystem.cs | 89 +++++++++------ .../Botany/Systems/PlantToxinsSystem.cs | 5 +- .../Botany/Systems/PlantTraitsSystem.cs | 7 +- .../Botany/Systems/UnviableGrowthSystem.cs | 6 +- .../Botany/Systems/WeedPestGrowthSystem.cs | 26 ++--- .../PlantAdjustPotencyEntityEffectSystem.cs | 3 + .../PlantAffectGrowthEntityEffectSystem.cs | 3 + .../PlantChangeStatEntityEffectSystem.cs | 31 ++++-- .../PlantCryoxadoneEntityEffectSystem.cs | 16 ++- .../PlantDestroySeedsEntityEffectSystem.cs | 8 +- .../PlantDiethylamineEntityEffectSystem.cs | 12 ++- .../PlantPhalanximineEntityEffectSystem.cs | 3 + .../PlantRestoreSeedsEntityEffectSystem.cs | 3 + .../RobustHarvestEntityEffectSystem.cs | 8 +- .../PlantMutateGasesEntityEffectSystem.cs | 7 +- .../PlantMutateHarvestEntityEffectSystem.cs | 4 +- 41 files changed, 303 insertions(+), 233 deletions(-) delete mode 100644 Content.Server/Botany/Components/PlantGrowthComponent.cs diff --git a/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs b/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs index 8eeb72bd23a..8894e3f6862 100644 --- a/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs +++ b/Content.Server/Botany/Components/AtmosphericGrowthComponent.cs @@ -2,9 +2,12 @@ using Content.Shared.Atmos; namespace Content.Server.Botany.Components; +/// +/// Atmospheric-related requirements for proper entity growth. Used in botany. +/// [RegisterComponent] [DataDefinition] -public sealed partial class AtmosphericGrowthComponent : PlantGrowthComponent +public sealed partial class AtmosphericGrowthComponent : Component { /// /// Ideal temperature for plant growth in Kelvin. @@ -13,10 +16,10 @@ public sealed partial class AtmosphericGrowthComponent : PlantGrowthComponent public float IdealHeat = Atmospherics.T20C; /// - /// Temperature tolerance range around ideal heat. + /// Temperature tolerance range around . /// [DataField] - public float HeatTolerance = Atmospherics.T0C + 10f; + public float HeatTolerance = 10f; /// /// Minimum pressure tolerance for plant growth. diff --git a/Content.Server/Botany/Components/BasicGrowthComponent.cs b/Content.Server/Botany/Components/BasicGrowthComponent.cs index bc71608afd4..aac178dfe7a 100644 --- a/Content.Server/Botany/Components/BasicGrowthComponent.cs +++ b/Content.Server/Botany/Components/BasicGrowthComponent.cs @@ -1,8 +1,11 @@ namespace Content.Server.Botany.Components; +/// +/// Basic parameters for plant growth. +/// [RegisterComponent] [DataDefinition] -public sealed partial class BasicGrowthComponent : PlantGrowthComponent +public sealed partial class BasicGrowthComponent : Component { /// /// Amount of water consumed per growth tick. diff --git a/Content.Server/Botany/Components/BotanySwabComponent.cs b/Content.Server/Botany/Components/BotanySwabComponent.cs index 66ad1cbb368..8652dbcfa9f 100644 --- a/Content.Server/Botany/Components/BotanySwabComponent.cs +++ b/Content.Server/Botany/Components/BotanySwabComponent.cs @@ -1,7 +1,4 @@ -using System; -using Content.Server.Botany.Components; - -namespace Content.Server.Botany; +namespace Content.Server.Botany.Components; /// /// Anything that can be used to cross-pollinate plants. diff --git a/Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs b/Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs index 6f815dfdf4e..96d92693f30 100644 --- a/Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs +++ b/Content.Server/Botany/Components/ConsumeExudeGasGrowthComponent.cs @@ -2,9 +2,12 @@ using Content.Shared.Atmos; namespace Content.Server.Botany.Components; +/// +/// Data for gas to consume/exude on plant growth. +/// [RegisterComponent] [DataDefinition] -public sealed partial class ConsumeExudeGasGrowthComponent : PlantGrowthComponent +public sealed partial class ConsumeExudeGasGrowthComponent : Component { /// /// Dictionary of gases and their consumption rates per growth tick. diff --git a/Content.Server/Botany/Components/GrowthComponentsHolder.cs b/Content.Server/Botany/Components/GrowthComponentsHolder.cs index 1f5196427ee..fd5969e13bc 100644 --- a/Content.Server/Botany/Components/GrowthComponentsHolder.cs +++ b/Content.Server/Botany/Components/GrowthComponentsHolder.cs @@ -1,3 +1,6 @@ +using System.Linq; +using System.Reflection; + namespace Content.Server.Botany.Components; /// @@ -9,27 +12,54 @@ namespace Content.Server.Botany.Components; [DataDefinition] public sealed partial class GrowthComponentsHolder { + public static readonly PropertyInfo[] ComponentGetters = typeof(GrowthComponentsHolder).GetProperties(); + public static readonly Type[] GrowthComponentTypes = ComponentGetters.Select(x => x.PropertyType).ToArray(); + + /// + /// Extra-traits. + /// [DataField] public PlantTraitsComponent? PlantTraits { get; set; } + /// + /// Basic properties for plant growth. + /// [DataField] public BasicGrowthComponent? BasicGrowth { get; set; } + /// + /// What defence plant have against toxins? + /// [DataField] public PlantToxinsComponent? Toxins { get; set; } + /// + /// Harvesting process-related data. + /// [DataField] public PlantHarvestComponent? Harvest { get; set; } + /// + /// Atmos-related environment requirements for plant growth. + /// [DataField] public AtmosphericGrowthComponent? AtmosphericGrowth { get; set; } + /// + /// What gases plant consume/exude upon growth. + /// [DataField] public ConsumeExudeGasGrowthComponent? ConsumeExudeGasGrowth { get; set; } + /// + /// Weeds and pests related data for plant. + /// [DataField] public WeedPestGrowthComponent? WeedPestGrowth { get; set; } + /// + /// Damage tolerance of plant. + /// [DataField] public UnviableGrowthComponent? UnviableGrowth { get; set; } @@ -39,11 +69,11 @@ public sealed partial class GrowthComponentsHolder /// public void EnsureGrowthComponents() { - foreach (var prop in typeof(GrowthComponentsHolder).GetProperties()) + foreach (var prop in ComponentGetters) { if (prop.GetValue(this) == null) { - var instance = Activator.CreateInstance(prop.PropertyType); + var instance = Activator.CreateInstance(prop.PropertyType); // this is really cursed and should not be used in master, also this should be blocked by sandboxing. prop.SetValue(this, instance); } } diff --git a/Content.Server/Botany/Components/PlantComponent.cs b/Content.Server/Botany/Components/PlantComponent.cs index 2da1f89a19f..c9f1677d88b 100644 --- a/Content.Server/Botany/Components/PlantComponent.cs +++ b/Content.Server/Botany/Components/PlantComponent.cs @@ -2,5 +2,4 @@ namespace Content.Server.Botany.Components; [RegisterComponent] [DataDefinition] -public sealed partial class PlantComponent : PlantGrowthComponent -{} +public sealed partial class PlantComponent : Component; diff --git a/Content.Server/Botany/Components/PlantGrowthComponent.cs b/Content.Server/Botany/Components/PlantGrowthComponent.cs deleted file mode 100644 index 29aecf71897..00000000000 --- a/Content.Server/Botany/Components/PlantGrowthComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Robust.Shared.Serialization.Manager; - -namespace Content.Server.Botany.Components; - -/// -/// Base class for plant growth components. -/// -public abstract partial class PlantGrowthComponent : Component -{ - /// - /// Creates a copy of this growth components. - /// - public PlantGrowthComponent DupeComponent() - { - return IoCManager.Resolve().CreateCopy(this, notNullableOverride: true); - } -} diff --git a/Content.Server/Botany/Components/PlantHarvestComponent.cs b/Content.Server/Botany/Components/PlantHarvestComponent.cs index 596965b4147..abcc1d74748 100644 --- a/Content.Server/Botany/Components/PlantHarvestComponent.cs +++ b/Content.Server/Botany/Components/PlantHarvestComponent.cs @@ -1,10 +1,9 @@ -using Content.Server.Botany.Systems; - namespace Content.Server.Botany.Components; +/// Data for plant harvesting process. [RegisterComponent] [DataDefinition] -public sealed partial class PlantHarvestComponent : PlantGrowthComponent +public sealed partial class PlantHarvestComponent : Component { /// /// Harvest repeat type. diff --git a/Content.Server/Botany/Components/PlantHolderComponent.cs b/Content.Server/Botany/Components/PlantHolderComponent.cs index f12c8ad0a61..be88d2ca929 100644 --- a/Content.Server/Botany/Components/PlantHolderComponent.cs +++ b/Content.Server/Botany/Components/PlantHolderComponent.cs @@ -4,6 +4,9 @@ using Robust.Shared.Audio; namespace Content.Server.Botany.Components; +/// +/// Container for plant-holder and plant combined data. +/// [RegisterComponent] public sealed partial class PlantHolderComponent : Component { diff --git a/Content.Server/Botany/Components/PlantToxinsComponent.cs b/Content.Server/Botany/Components/PlantToxinsComponent.cs index a0295a98d19..e02bdd196bc 100644 --- a/Content.Server/Botany/Components/PlantToxinsComponent.cs +++ b/Content.Server/Botany/Components/PlantToxinsComponent.cs @@ -1,8 +1,11 @@ namespace Content.Server.Botany.Components; +/// +/// Data for plant resistance to toxins. +/// [RegisterComponent] [DataDefinition] -public sealed partial class PlantToxinsComponent : PlantGrowthComponent +public sealed partial class PlantToxinsComponent : Component { /// /// Maximum toxin level the plant can tolerate before taking damage. diff --git a/Content.Server/Botany/Components/PlantTraitsComponent.cs b/Content.Server/Botany/Components/PlantTraitsComponent.cs index bbee4366c4c..c1b4aae1a17 100644 --- a/Content.Server/Botany/Components/PlantTraitsComponent.cs +++ b/Content.Server/Botany/Components/PlantTraitsComponent.cs @@ -1,10 +1,9 @@ -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - namespace Content.Server.Botany.Components; +/// Aggregate for general plant information and rare quirks. [RegisterComponent] [DataDefinition] -public sealed partial class PlantTraitsComponent : PlantGrowthComponent +public sealed partial class PlantTraitsComponent : Component { /// /// The plant's max health. diff --git a/Content.Server/Botany/Components/ProduceComponent.cs b/Content.Server/Botany/Components/ProduceComponent.cs index 6d7ed46a377..6af952ce363 100644 --- a/Content.Server/Botany/Components/ProduceComponent.cs +++ b/Content.Server/Botany/Components/ProduceComponent.cs @@ -4,6 +4,9 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy namespace Content.Server.Botany.Components; +/// +/// Produce-related data for plant and plant growth cycle. +/// [RegisterComponent] [Access(typeof(BotanySystem))] public sealed partial class ProduceComponent : SharedProduceComponent diff --git a/Content.Server/Botany/Components/SeedComponent.cs b/Content.Server/Botany/Components/SeedComponent.cs index a979741da3d..e3e046b389b 100644 --- a/Content.Server/Botany/Components/SeedComponent.cs +++ b/Content.Server/Botany/Components/SeedComponent.cs @@ -4,25 +4,28 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy namespace Content.Server.Botany.Components; +/// +/// Data container for plant seed. Contains all info (values for components) for new plant to grow from seed. +/// [RegisterComponent, Access(typeof(BotanySystem))] public sealed partial class SeedComponent : SharedSeedComponent { /// - /// Seed data containing information about the plant type & properties that this seed can grow seed. If - /// null, will instead attempt to get data from a seed prototype, if one is defined. See . + /// Seed data containing information about the plant type & properties that this seed can grow seed. If + /// null, will instead attempt to get data from a seed prototype, if one is defined. See . /// [DataField] public SeedData? Seed; /// - /// If not null, overrides the plant's initial health. Otherwise, the plant's initial health is set to the Endurance value. + /// If not null, overrides the plant's initial health. Otherwise, the plant's initial health is set to the Endurance value. /// [DataField] public float? HealthOverride = null; /// - /// Name of a base seed prototype that is used if is null. + /// Name of a base seed prototype that is used if is null. /// [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string? SeedId; diff --git a/Content.Server/Botany/Components/UnviableGrowthComponent.cs b/Content.Server/Botany/Components/UnviableGrowthComponent.cs index e60b331e03a..5e31bfdc414 100644 --- a/Content.Server/Botany/Components/UnviableGrowthComponent.cs +++ b/Content.Server/Botany/Components/UnviableGrowthComponent.cs @@ -1,8 +1,9 @@ namespace Content.Server.Botany.Components; +/// Damage tolerance of plant. [RegisterComponent] [DataDefinition] -public sealed partial class UnviableGrowthComponent : PlantGrowthComponent +public sealed partial class UnviableGrowthComponent : Component { /// /// Chance per tick for the plant to take damage due to being unviable. diff --git a/Content.Server/Botany/Components/WeedPestGrowthComponent.cs b/Content.Server/Botany/Components/WeedPestGrowthComponent.cs index a0cd83afb04..ae80a30c7ef 100644 --- a/Content.Server/Botany/Components/WeedPestGrowthComponent.cs +++ b/Content.Server/Botany/Components/WeedPestGrowthComponent.cs @@ -1,8 +1,12 @@ namespace Content.Server.Botany.Components; +/// +/// Data for weed and pest problems which can happen to plants - how well plant tolerates them, +/// chances to develop them, how big of a problem they will be. +/// [RegisterComponent] [DataDefinition] -public sealed partial class WeedPestGrowthComponent : PlantGrowthComponent +public sealed partial class WeedPestGrowthComponent : Component { /// /// Maximum weed level the plant can tolerate before taking damage. diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index 26901cb0c45..2f48d93fecb 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -23,33 +23,32 @@ public partial struct SeedChemQuantity /// /// Minimum amount of chemical that is added to produce, regardless of the potency /// - [DataField("Min")] public FixedPoint2 Min = FixedPoint2.Epsilon; + [DataField] public FixedPoint2 Min = FixedPoint2.Epsilon; /// /// Maximum amount of chemical that can be produced after taking plant potency into account. /// - [DataField("Max")] public FixedPoint2 Max; + [DataField] public FixedPoint2 Max; /// /// When chemicals are added to produce, the potency of the seed is divided with this value. Final chemical amount is the result plus the `Min` value. /// Example: PotencyDivisor of 20 with seed potency of 55 results in 2.75, 55/20 = 2.75. If minimum is 1 then final result will be 3.75 of that chemical, 55/20+1 = 3.75. /// - [DataField("PotencyDivisor")] public float PotencyDivisor; + [DataField] public float PotencyDivisor; /// /// Inherent chemical is one that is NOT result of mutation or crossbreeding. These chemicals are removed if species mutation is executed. /// - [DataField] - public bool Inherent = true; + [DataField] public bool Inherent = true; } -[Virtual, DataDefinition] // TODO Make Botany ECS and give it a proper API. I removed the limited access of this class because it's egregious how many systems needed access to it due to a lack of an actual API. /// /// SeedData is no longer restricted because the number of friends is absolutely unreasonable. /// This entire data definition is unreasonable. I felt genuine fear looking at this, this is horrific. Send help. /// // TODO: Hit Botany with hammers +[Virtual, DataDefinition] public partial class SeedData { #region Tracking @@ -110,7 +109,6 @@ public partial class SeedData #region General traits - #endregion #region Cosmetics @@ -118,8 +116,7 @@ public partial class SeedData [DataField(required: true)] public ResPath PlantRsi { get; set; } = default!; - [DataField] - public string PlantIconState { get; set; } = "produce"; + [DataField] public string PlantIconState { get; set; } = "produce"; /// /// Screams random sound from collection SoundCollectionSpecifier diff --git a/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs b/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs index c86cf0c688f..54c18e6d5c0 100644 --- a/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs +++ b/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs @@ -23,13 +23,12 @@ public sealed class AtmosphericGrowthSystem : PlantGrowthSystem private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { - var uid = ent.Owner; - var component = ent.Comp; + var (uid, component) = ent; PlantHolderComponent? holder = null; Resolve(uid, ref holder); - if (holder == null || holder.Seed == null || holder.Dead) + if (holder?.Seed == null || holder.Dead) return; var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index 2b710faf062..d4fe0f1a2fb 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -37,7 +37,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem var swabComp = swab.SeedData.GrowthComponents.BasicGrowth; if (swabComp == null) { - swab.SeedData.GrowthComponents.BasicGrowth = new BasicGrowthComponent() + swab.SeedData.GrowthComponents.BasicGrowth = new BasicGrowthComponent { WaterConsumption = component.WaterConsumption, NutrientConsumption = component.NutrientConsumption @@ -54,8 +54,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { - var uid = ent.Owner; - var component = ent.Comp; + var (uid, component) = ent; PlantHolderComponent? holder = null; Resolve(uid, ref holder); @@ -69,6 +68,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem holder.Health -= _random.Next(5, 10) * HydroponicsSpeedMultiplier; if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; + return; } diff --git a/Content.Server/Botany/Systems/BotanySwabSystem.cs b/Content.Server/Botany/Systems/BotanySwabSystem.cs index 95436dab1a5..84256f6b8da 100644 --- a/Content.Server/Botany/Systems/BotanySwabSystem.cs +++ b/Content.Server/Botany/Systems/BotanySwabSystem.cs @@ -4,9 +4,6 @@ using Content.Shared.DoAfter; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Swab; -using Robust.Shared.Random; -using Robust.Shared.Serialization.Manager; -using System.Collections.Generic; namespace Content.Server.Botany.Systems; @@ -15,8 +12,6 @@ public sealed class BotanySwabSystem : EntitySystem [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly MutationSystem _mutationSystem = default!; - [Dependency] private readonly ISerializationManager _serializationManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { diff --git a/Content.Server/Botany/Systems/BotanySystem.Produce.cs b/Content.Server/Botany/Systems/BotanySystem.Produce.cs index d0134c7de33..e98429af88f 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Produce.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Produce.cs @@ -37,7 +37,7 @@ public sealed partial class BotanySystem var amount = quantity.Min; if (quantity.PotencyDivisor > 0 && traits.Potency > 0) - amount += FixedPoint2.New(traits.Potency / quantity.PotencyDivisor); + amount += traits.Potency / quantity.PotencyDivisor; amount = FixedPoint2.Clamp(amount, quantity.Min, quantity.Max); solutionContainer.MaxVolume += amount; diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index a58d3ceed88..c13b5ed27ca 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -6,7 +6,6 @@ using Content.Shared.Examine; using Content.Shared.Hands.EntitySystems; using Content.Shared.Popups; using Content.Shared.Random; -using Content.Shared.Random.Helpers; using Robust.Server.GameObjects; using Robust.Shared.Map; using Robust.Shared.Prototypes; @@ -125,7 +124,7 @@ public sealed partial class BotanySystem : EntitySystem return seed; } - public IEnumerable AutoHarvest(SeedData proto, EntityCoordinates position, EntityUid? plantEntity = null) + public IEnumerable AutoHarvest(SeedData proto, EntityCoordinates position, EntityUid plantEntity) { if (position.IsValid(EntityManager) && proto.ProductPrototypes.Count > 0) @@ -139,7 +138,7 @@ public sealed partial class BotanySystem : EntitySystem return Enumerable.Empty(); } - public IEnumerable Harvest(SeedData proto, EntityUid user, EntityUid? plantEntity = null) + public IEnumerable Harvest(SeedData proto, EntityUid user, EntityUid plantEntity) { var traits = GetPlantTraits(proto); if (traits == null || proto.ProductPrototypes.Count == 0 || traits.Yield <= 0) @@ -157,13 +156,13 @@ public sealed partial class BotanySystem : EntitySystem return GenerateProduct(proto, Transform(user).Coordinates, plantEntity); } - public IEnumerable GenerateProduct(SeedData proto, EntityCoordinates position, EntityUid? plantEntity = null) + public IEnumerable GenerateProduct(SeedData proto, EntityCoordinates position, EntityUid plantEntity) { var traits = GetPlantTraits(proto); if (traits == null) return Enumerable.Empty(); - var yieldMod = Comp(plantEntity!.Value).YieldMod; + var yieldMod = Comp(plantEntity).YieldMod; var totalYield = 0; if (traits.Yield > -1) diff --git a/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs index 3ea3d58b196..d8ae0bee281 100644 --- a/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs +++ b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs @@ -21,8 +21,7 @@ public sealed class ConsumeExudeGasGrowthSystem : PlantGrowthSystem private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { - var uid = ent.Owner; - var component = ent.Comp; + var (uid, component) = ent; PlantHolderComponent? holder = null; PlantTraitsComponent? traits = null; diff --git a/Content.Server/Botany/Systems/MutationSystem.cs b/Content.Server/Botany/Systems/MutationSystem.cs index 2b23c68a95b..022b792cf99 100644 --- a/Content.Server/Botany/Systems/MutationSystem.cs +++ b/Content.Server/Botany/Systems/MutationSystem.cs @@ -6,6 +6,7 @@ using Content.Shared.Random; using Robust.Shared.Prototypes; using Robust.Shared.Random; using System.Linq; +using Robust.Shared.Serialization.Manager; namespace Content.Server.Botany; @@ -15,7 +16,7 @@ public sealed class MutationSystem : EntitySystem [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly BotanySystem _botanySystem = default!; + [Dependency] private readonly ISerializationManager _serializationManager = default!; [Dependency] private readonly SharedEntityEffectsSystem _entityEffects = default!; private RandomPlantMutationListPrototype _randomMutations = default!; @@ -27,8 +28,6 @@ public sealed class MutationSystem : EntitySystem /// /// For each random mutation, see if it occurs on this plant this check. /// - /// - /// public void CheckRandomMutations(EntityUid plantHolder, ref SeedData seed, float severity) { foreach (var mutation in _randomMutations.mutations) @@ -68,11 +67,11 @@ public sealed class MutationSystem : EntitySystem // Fill missing components in the seed with defaults. seed.GrowthComponents.EnsureGrowthComponents(); - foreach (var prop in typeof(GrowthComponentsHolder).GetProperties()) + foreach (var prop in GrowthComponentsHolder.ComponentGetters) { - if (prop.GetValue(seed.GrowthComponents) is PlantGrowthComponent component && !EntityManager.HasComponent(plantHolder, component.GetType())) + if (prop.GetValue(seed.GrowthComponents) is Component component && !EntityManager.HasComponent(plantHolder, component.GetType())) { - var newComponent = component.DupeComponent(); + var newComponent = _serializationManager.CreateCopy(component, notNullableOverride: true); EntityManager.AddComponent(plantHolder, newComponent); } } @@ -84,15 +83,15 @@ public sealed class MutationSystem : EntitySystem CrossChemicals(ref result.Chemicals, a.Chemicals); - var aTraits = BotanySystem.GetPlantTraits(a); + var sourceTraits = BotanySystem.GetPlantTraits(a); var resultTraits = BotanySystem.GetPlantTraits(result); - if (aTraits != null && resultTraits != null) + if (sourceTraits != null && resultTraits != null) { - CrossBool(ref resultTraits.Seedless, aTraits.Seedless); - CrossBool(ref resultTraits.Ligneous, aTraits.Ligneous); - CrossBool(ref resultTraits.CanScream, aTraits.CanScream); - CrossBool(ref resultTraits.TurnIntoKudzu, aTraits.TurnIntoKudzu); + CrossBool(ref resultTraits.Seedless, sourceTraits.Seedless); + CrossBool(ref resultTraits.Ligneous, sourceTraits.Ligneous); + CrossBool(ref resultTraits.CanScream, sourceTraits.CanScream); + CrossBool(ref resultTraits.TurnIntoKudzu, sourceTraits.TurnIntoKudzu); } // LINQ Explanation diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs index a60d20f9ebd..6fd14e00ba7 100644 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ b/Content.Server/Botany/Systems/PlantGrowthSystem.cs @@ -2,9 +2,6 @@ using Content.Server.Botany.Components; namespace Content.Server.Botany.Systems; -[ByRefEvent] -public readonly record struct OnPlantGrowEvent; - /// /// Base for botany growth systems, providing shared helpers and constants used by /// per-trait/per-environment growth handlers. @@ -21,18 +18,12 @@ public abstract class PlantGrowthSystem : EntitySystem /// public const float HydroponicsConsumptionMultiplier = 2f; - public override void Initialize() - { - base.Initialize(); - } - /// /// Affects the growth of a plant by modifying its age or production timing. /// public void AffectGrowth(Entity ent, int amount) { - var uid = ent.Owner; - var component = ent.Comp; + var (uid, component) = ent; if (component.Seed == null) return; @@ -58,3 +49,7 @@ public abstract class PlantGrowthSystem : EntitySystem } } } + +/// Event of plant growing ticking. +[ByRefEvent] +public readonly record struct OnPlantGrowEvent; diff --git a/Content.Server/Botany/Systems/PlantHarvestSystem.cs b/Content.Server/Botany/Systems/PlantHarvestSystem.cs index b19355f3149..ede0bbf8906 100644 --- a/Content.Server/Botany/Systems/PlantHarvestSystem.cs +++ b/Content.Server/Botany/Systems/PlantHarvestSystem.cs @@ -24,12 +24,12 @@ public sealed class HarvestSystem : EntitySystem SubscribeLocalEvent(OnPlantGrow); SubscribeLocalEvent(OnInteractHand); + SubscribeLocalEvent(OnInteractUsing); } private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { - var uid = ent.Owner; - var component = ent.Comp; + var (uid, component) = ent; PlantHolderComponent? plantHolder = null; PlantTraitsComponent? traits = null; @@ -39,8 +39,8 @@ public sealed class HarvestSystem : EntitySystem if (plantHolder.Dead || plantHolder.Seed == null) return; - if (component.ReadyForHarvest && component.HarvestRepeat == HarvestType.SelfHarvest) - AutoHarvest(ent); + if (component is { ReadyForHarvest: true, HarvestRepeat: HarvestType.SelfHarvest }) + AutoHarvest((ent, ent, plantHolder)); // Check if plant is ready for harvest var timeLastHarvest = plantHolder.Age - component.LastHarvest; @@ -52,39 +52,55 @@ public sealed class HarvestSystem : EntitySystem } } - private void OnInteractHand(Entity ent, ref InteractHandEvent args) + private void OnInteractUsing(Entity ent, ref InteractUsingEvent args) { - var uid = ent.Owner; - var component = ent.Comp; + var (uid, component) = ent; PlantHolderComponent? plantHolder = null; PlantTraitsComponent? traits = null; if (!Resolve(uid, ref plantHolder, ref traits)) return; - if (!component.ReadyForHarvest || plantHolder.Dead) + if (!component.ReadyForHarvest || plantHolder.Dead || plantHolder.Seed == null) + return; + + // Check if sharp tool is required + if (traits.Ligneous && !_botany.CanHarvest(plantHolder.Seed, args.Used)) + { + _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), args.User); + return; + } + + // Perform harvest + DoHarvest(ent, args.User); + } + + private void OnInteractHand(Entity ent, ref InteractHandEvent args) + { + var (uid, component) = ent; + + PlantHolderComponent? plantHolder = null; + PlantTraitsComponent? traits = null; + if (!Resolve(uid, ref plantHolder, ref traits)) + return; + + if (!component.ReadyForHarvest || plantHolder.Dead || plantHolder.Seed == null) return; // Check if sharp tool is required if (traits.Ligneous) { - if (!_hands.TryGetActiveItem(args.User, out var activeItem) || - plantHolder.Seed == null || - !_botany.CanHarvest(plantHolder.Seed, activeItem)) - { - _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), args.User); - return; - } + _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), args.User); + return; } // Perform harvest - DoHarvest(ent); + DoHarvest(ent, args.User); } - public void DoHarvest(Entity ent) + public void DoHarvest(Entity ent, EntityUid user) { - var uid = ent.Owner; - var component = ent.Comp; + var (uid, component) = ent; PlantHolderComponent? plantHolder = null; PlantTraitsComponent? traits = null; @@ -103,47 +119,19 @@ public sealed class HarvestSystem : EntitySystem return; // Spawn products - var yield = traits.Yield; - if (plantHolder.Seed?.ProductPrototypes != null) - { - for (var i = 0; i < yield; i++) - { - foreach (var productPrototype in plantHolder.Seed.ProductPrototypes) - { - var product = Spawn(productPrototype, Transform(uid).Coordinates); - - // Apply mutations to product - if (TryComp(product, out var produce)) - { - _botany.ProduceGrown(product, produce); - } - } - } - } + if(plantHolder.Seed != null) + _botany.Harvest(plantHolder.Seed, user, ent); // Handle harvest type - switch (component.HarvestRepeat) - { - case HarvestType.NoRepeat: - _plantHolder.RemovePlant(uid, plantHolder); - break; - case HarvestType.Repeat: - case HarvestType.SelfHarvest: - component.ReadyForHarvest = false; - component.LastHarvest = plantHolder.Age; - break; - } + if (component.HarvestRepeat == HarvestType.NoRepeat) + _plantHolder.RemovePlant(uid, plantHolder); - AfterHarvest(ent); + AfterHarvest(ent, plantHolder, traits); } - private void AfterHarvest(Entity ent) + private void AfterHarvest(Entity ent, PlantHolderComponent? plantHolder = null, PlantTraitsComponent? traits = null) { - var uid = ent.Owner; - var component = ent.Comp; - - PlantTraitsComponent? traits = null; - PlantHolderComponent? plantHolder = null; + var (uid, component) = ent; if (!Resolve(uid, ref traits, ref plantHolder)) return; @@ -161,11 +149,12 @@ public sealed class HarvestSystem : EntitySystem /// /// Auto-harvests a plant. /// - public void AutoHarvest(Entity ent) + public void AutoHarvest(Entity ent) { - if (!ent.Comp.ReadyForHarvest) + if (!ent.Comp1.ReadyForHarvest || ent.Comp2.Seed == null) return; + _botany.AutoHarvest(ent.Comp2.Seed, Transform(ent.Owner).Coordinates, ent); AfterHarvest(ent); } } diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 1643e9796ff..858926353d0 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -1,6 +1,5 @@ -using Content.Server.Atmos.EntitySystems; +using System.Linq; using Content.Server.Botany.Components; -using Content.Server.Hands.Systems; using Content.Server.Popups; using Content.Shared.Administration.Logs; using Content.Shared.Botany; @@ -30,13 +29,11 @@ namespace Content.Server.Botany.Systems; public sealed class PlantHolderSystem : EntitySystem { - [Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly BotanySystem _botany = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly MutationSystem _mutation = default!; [Dependency] private readonly AppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly HandsSystem _hands = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; @@ -69,6 +66,7 @@ public sealed class PlantHolderSystem : EntitySystem { if (plantHolder.NextUpdate > _gameTiming.CurTime) continue; + plantHolder.NextUpdate = _gameTiming.CurTime + plantHolder.UpdateDelay; Update(uid, plantHolder); @@ -94,7 +92,7 @@ public sealed class PlantHolderSystem : EntitySystem if (!args.IsInDetailsRange) return; - var (_, component) = entity; + var component = entity.Comp; using (args.PushGroup(nameof(PlantHolderComponent))) { @@ -185,7 +183,9 @@ public sealed class PlantHolderSystem : EntitySystem var noun = Loc.GetString(seed.Noun); _popup.PopupCursor(Loc.GetString("plant-holder-component-plant-success-message", ("seedName", name), - ("seedNoun", noun)), args.User, PopupType.Medium); + ("seedNoun", noun)), + args.User, + PopupType.Medium); plantHolder.Seed = seed.Clone(); plantHolder.Dead = false; @@ -210,9 +210,9 @@ public sealed class PlantHolderSystem : EntitySystem // Fill missing components with defaults seed.GrowthComponents.EnsureGrowthComponents(); - foreach (var prop in typeof(GrowthComponentsHolder).GetProperties()) + foreach (var prop in GrowthComponentsHolder.ComponentGetters) { - if (prop.GetValue(seed.GrowthComponents) is PlantGrowthComponent growthComp) + if (prop.GetValue(seed.GrowthComponents) is Component growthComp) { EntityManager.AddComponent(uid, _copier.CreateCopy(growthComp, notNullableOverride: true), overwrite: true); } @@ -236,8 +236,10 @@ public sealed class PlantHolderSystem : EntitySystem } args.Handled = true; - _popup.PopupCursor(Loc.GetString("plant-holder-component-already-seeded-message", - ("name", Comp(uid).EntityName)), args.User, PopupType.Medium); + _popup.PopupCursor( + Loc.GetString("plant-holder-component-already-seeded-message", ("name", MetaData(uid).EntityName)), + args.User, + PopupType.Medium); return; } @@ -246,10 +248,15 @@ public sealed class PlantHolderSystem : EntitySystem args.Handled = true; if (plantHolder.WeedLevel > 0) { - _popup.PopupCursor(Loc.GetString("plant-holder-component-remove-weeds-message", - ("name", Comp(uid).EntityName)), args.User, PopupType.Medium); - _popup.PopupEntity(Loc.GetString("plant-holder-component-remove-weeds-others-message", - ("otherName", Comp(args.User).EntityName)), uid, Filter.PvsExcept(args.User), true); + _popup.PopupCursor( + Loc.GetString("plant-holder-component-remove-weeds-message", ("name", MetaData(uid).EntityName)), + args.User, + PopupType.Medium); + _popup.PopupEntity( + Loc.GetString("plant-holder-component-remove-weeds-others-message", ("otherName", MetaData(args.User).EntityName)), + uid, + Filter.PvsExcept(args.User), + true); plantHolder.WeedLevel = 0; UpdateSprite(uid, plantHolder); } @@ -266,16 +273,22 @@ public sealed class PlantHolderSystem : EntitySystem args.Handled = true; if (plantHolder.Seed != null) { - _popup.PopupCursor(Loc.GetString("plant-holder-component-remove-plant-message", - ("name", Comp(uid).EntityName)), args.User, PopupType.Medium); - _popup.PopupEntity(Loc.GetString("plant-holder-component-remove-plant-others-message", - ("name", Comp(args.User).EntityName)), uid, Filter.PvsExcept(args.User), true); + _popup.PopupCursor( + Loc.GetString("plant-holder-component-remove-plant-message", ("name", MetaData(uid).EntityName)), + args.User, + PopupType.Medium); + _popup.PopupEntity( + Loc.GetString("plant-holder-component-remove-plant-others-message", ("name", MetaData(args.User).EntityName)), + uid, + Filter.PvsExcept(args.User), + true); RemovePlant(uid, plantHolder); } else { - _popup.PopupCursor(Loc.GetString("plant-holder-component-no-plant-message", - ("name", Comp(uid).EntityName)), args.User); + _popup.PopupCursor( + Loc.GetString("plant-holder-component-no-plant-message", ("name", MetaData(uid).EntityName)), + args.User); } return; @@ -319,8 +332,8 @@ public sealed class PlantHolderSystem : EntitySystem { healthOverride = plantHolder.Health; } - var packetSeed = plantHolder.Seed; + var packetSeed = plantHolder.Seed; if (packetSeed != null) { // Copy growth components from the plant to the seed before creating seed packet @@ -341,7 +354,8 @@ public sealed class PlantHolderSystem : EntitySystem _randomHelper.RandomOffset(seed, 0.25f); var displayName = Loc.GetString(plantHolder.Seed.DisplayName); _popup.PopupCursor(Loc.GetString("plant-holder-component-take-sample-message", - ("seedName", displayName)), args.User); + ("seedName", displayName)), + args.User); if (_random.Prob(0.3f)) plantHolder.Sampled = true; @@ -358,11 +372,16 @@ public sealed class PlantHolderSystem : EntitySystem args.Handled = true; _popup.PopupCursor(Loc.GetString("plant-holder-component-compost-message", ("owner", uid), - ("usingItem", args.Used)), args.User, PopupType.Medium); + ("usingItem", args.Used)), + args.User, + PopupType.Medium); _popup.PopupEntity(Loc.GetString("plant-holder-component-compost-others-message", ("user", Identity.Entity(args.User, EntityManager)), ("usingItem", args.Used), - ("owner", uid)), uid, Filter.PvsExcept(args.User), true); + ("owner", uid)), + uid, + Filter.PvsExcept(args.User), + true); if (_solutionContainerSystem.TryGetSolution(args.Used, produce.SolutionName, out var soln2, out var solution2)) { @@ -412,12 +431,13 @@ public sealed class PlantHolderSystem : EntitySystem { if (component.UpdateSpriteAfterUpdate) UpdateSprite(uid, component); + return; } component.LastCycle = curTime; - if (component.Seed != null && !component.Dead) + if (component is { Seed: not null, Dead: false }) { var plantGrow = new OnPlantGrowEvent(); RaiseLocalEvent(uid, ref plantGrow); @@ -597,7 +617,7 @@ public sealed class PlantHolderSystem : EntitySystem // If no seed, clear visuals regardless of traits. if (component.Seed == null) { - _appearance.SetData(uid, PlantHolderVisuals.PlantState, "", app); + _appearance.SetData(uid, PlantHolderVisuals.PlantState, string.Empty, app); _appearance.SetData(uid, PlantHolderVisuals.HealthLight, false, app); _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, false, app); } @@ -638,10 +658,12 @@ public sealed class PlantHolderSystem : EntitySystem _appearance.SetData(uid, PlantHolderVisuals.WaterLight, component.WaterLevel <= 15, app); _appearance.SetData(uid, PlantHolderVisuals.NutritionLight, component.NutritionLevel <= 8, app); - _appearance.SetData(uid, PlantHolderVisuals.AlertLight, - component.WeedLevel >= 5 || component.PestLevel >= 5 || component.Toxins >= 40 || component.ImproperHeat || - component.ImproperPressure || component.MissingGas > 0, app); - _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, harvest != null && harvest.ReadyForHarvest, app); + _appearance.SetData(uid, + PlantHolderVisuals.AlertLight, + component.WeedLevel >= 5 || component.PestLevel >= 5 || component.Toxins >= 40 || component.ImproperHeat + ||component.ImproperPressure || component.MissingGas > 0, + app); + _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, harvest is { ReadyForHarvest: true }, app); } /// @@ -673,9 +695,12 @@ public sealed class PlantHolderSystem : EntitySystem /// private void RemoveAllGrowthComponents(EntityUid uid) { - foreach (var comp in EntityManager.GetComponents(uid)) + foreach (var comp in EntityManager.GetComponents(uid)) { - RemComp(uid, comp); + if (GrowthComponentsHolder.GrowthComponentTypes.Contains(comp.GetType())) + { + RemComp(uid, comp); + } } } } diff --git a/Content.Server/Botany/Systems/PlantToxinsSystem.cs b/Content.Server/Botany/Systems/PlantToxinsSystem.cs index 9c681f1e568..920d4f4e82c 100644 --- a/Content.Server/Botany/Systems/PlantToxinsSystem.cs +++ b/Content.Server/Botany/Systems/PlantToxinsSystem.cs @@ -17,13 +17,12 @@ public sealed class ToxinsSystem : PlantGrowthSystem private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { - var uid = ent.Owner; - var component = ent.Comp; + var (uid, component) = ent; PlantHolderComponent? holder = null; Resolve(uid, ref holder); - if (holder == null || holder.Seed == null || holder.Dead) + if (holder?.Seed == null || holder.Dead) return; if (holder.Toxins > 0) diff --git a/Content.Server/Botany/Systems/PlantTraitsSystem.cs b/Content.Server/Botany/Systems/PlantTraitsSystem.cs index 311a26ba65f..46c5daa18eb 100644 --- a/Content.Server/Botany/Systems/PlantTraitsSystem.cs +++ b/Content.Server/Botany/Systems/PlantTraitsSystem.cs @@ -19,13 +19,12 @@ public sealed class PlantTraitsSystem : PlantGrowthSystem private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { - var uid = ent.Owner; - var component = ent.Comp; + var (uid, component) = ent; PlantHolderComponent? holder = null; Resolve(uid, ref holder); - if (holder == null || holder.Seed == null || holder.Dead) + if (holder?.Seed == null || holder.Dead) return; // Check if plant is too old @@ -42,7 +41,7 @@ public sealed class PlantTraitsSystem : PlantGrowthSystem /// public void AdjustPotency(Entity ent, float delta) { - ref var traits = ref ent.Comp; + var traits = ent.Comp; traits.Potency = Math.Max(traits.Potency + delta, 1); Dirty(ent); } diff --git a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs index 08005ce8d85..bda86eb25ba 100644 --- a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs +++ b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs @@ -13,18 +13,18 @@ public sealed class UnviableGrowthSystem : PlantGrowthSystem public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnPlantGrow); } private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { - var uid = ent.Owner; - var component = ent.Comp; + var (uid, component) = ent; PlantHolderComponent? holder = null; Resolve(uid, ref holder); - if (holder == null || holder.Seed == null || holder.Dead) + if (holder?.Seed == null || holder.Dead) return; // Unviable plants have a chance to die each growth cycle diff --git a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs index 5240917cac8..ce83a488229 100644 --- a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs +++ b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs @@ -22,13 +22,12 @@ public sealed class WeedPestGrowthSystem : PlantGrowthSystem private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { - var uid = ent.Owner; - var component = ent.Comp; + var (uid, component) = ent; PlantHolderComponent? holder = null; Resolve(uid, ref holder); - if (holder == null || holder.Seed == null || holder.Dead) + if (holder?.Seed == null || holder.Dead) return; // Weed growth logic @@ -53,32 +52,35 @@ public sealed class WeedPestGrowthSystem : PlantGrowthSystem /// private void OnTrayUpdate(Entity ent, ref OnPlantGrowEvent args) { - var uid = ent.Owner; - var component = ent.Comp; + var (uid, component) = ent; + + if (!TryComp(uid, out var traits)) + return; // Weeds like water and nutrients! They may appear even if there's not a seed planted - if (component.WaterLevel > 10 && component.NutritionLevel > 5) + if (component is { WaterLevel: > 10, NutritionLevel: > 5 }) { float chance; if (component.Seed == null) chance = 0.05f; - else if (TryComp(uid, out var traits) && traits.TurnIntoKudzu) + else if (traits.TurnIntoKudzu) chance = 1f; else chance = 0.01f; if (_random.Prob(chance)) component.WeedLevel += 1 + component.WeedCoefficient; + if (component.DrawWarnings) component.UpdateSpriteAfterUpdate = true; } // Handle kudzu transformation - if (component.Seed != null && !component.Dead && - TryComp(uid, out var weed) && - TryComp(uid, out var kudzuTraits) && - kudzuTraits.TurnIntoKudzu && - component.WeedLevel >= weed.WeedHighLevelThreshold) + if (component is { Seed: not null, Dead: false } + && TryComp(uid, out var weed) + && TryComp(uid, out var kudzuTraits) + && kudzuTraits.TurnIntoKudzu + && component.WeedLevel >= weed.WeedHighLevelThreshold) { Spawn(component.Seed.KudzuPrototype, Transform(uid).Coordinates.SnapToGrid(EntityManager)); kudzuTraits.TurnIntoKudzu = false; diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs index f5a5bcbd9dd..ae80f8ed0d6 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs @@ -5,6 +5,9 @@ using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes; namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes; +/// +/// Entity effect that sets plant potency. +/// public sealed partial class PlantAdjustPotencyEntityEffectSystem : EntityEffectSystem { [Dependency] private readonly PlantTraitsSystem _plantTraits = default!; diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAffectGrowthEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAffectGrowthEntityEffectSystem.cs index ed699612e79..571ff827b26 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAffectGrowthEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAffectGrowthEntityEffectSystem.cs @@ -5,6 +5,9 @@ using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes; namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes; +/// +/// Entity effect that increments plant age / growth cycle. +/// public sealed partial class PlantAffectGrowthEntityEffectSystem : EntityEffectSystem { [Dependency] private readonly BasicGrowthSystem _plantGrowth = default!; diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs index 08e9b554d87..5d2a66ac196 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs @@ -1,3 +1,5 @@ +using System.Linq; +using System.Reflection; using Content.Server.Botany.Components; using Content.Shared.EntityEffects; using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes; @@ -22,9 +24,13 @@ public sealed partial class PlantChangeStatEntityEffectSystem : EntityEffectSyst var targetValue = args.Effect.TargetValue; // Scan live plant growth components and mutate the first matching field. - foreach (var growthComp in EntityManager.GetComponents(entity.Owner)) + foreach (var growthComp in EntityManager.GetComponents(entity.Owner)) { + var componentType = growthComp.GetType(); + if(!GrowthComponentsHolder.GrowthComponentTypes.Contains(componentType)) + continue; + var field = componentType.GetField(targetValue); if (field == null) @@ -34,23 +40,22 @@ public sealed partial class PlantChangeStatEntityEffectSystem : EntityEffectSyst if (currentValue == null) continue; - if (field.FieldType == typeof(float)) + if (TryGetValue(currentValue, out var floatVal)) { - var floatVal = (float)currentValue; MutateFloat(ref floatVal, args.Effect.MinValue, args.Effect.MaxValue, args.Effect.Steps); field.SetValue(growthComp, floatVal); return; } - else if (field.FieldType == typeof(int)) + + if (TryGetValue(currentValue, out var intVal)) { - var intVal = (int)currentValue; MutateInt(ref intVal, (int)args.Effect.MinValue, (int)args.Effect.MaxValue, args.Effect.Steps); field.SetValue(growthComp, intVal); return; } - else if (field.FieldType == typeof(bool)) + + if (TryGetValue(currentValue, out var boolVal)) { - var boolVal = (bool)currentValue; field.SetValue(growthComp, !boolVal); return; } @@ -60,6 +65,18 @@ public sealed partial class PlantChangeStatEntityEffectSystem : EntityEffectSyst Log.Error($"{nameof(PlantChangeStat)} Error: Field '{targetValue}' not found in any plant component. Did you misspell it?"); } + private bool TryGetValue(object value, out T? result) + { + result = default; + if (value is T val) + { + result = val; + return true; + } + + return false; + } + // 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. diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs index ba21d0f8240..ebd3e0953d1 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs @@ -5,6 +5,9 @@ using Robust.Shared.Random; namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes; +/// +/// Entity effect that reverts aging of plant. +/// public sealed partial class PlantCryoxadoneEntityEffectSystem : EntityEffectSystem { [Dependency] private readonly IRobustRandom _random = default!; @@ -14,17 +17,12 @@ public sealed partial class PlantCryoxadoneEntityEffectSystem : EntityEffectSyst if (entity.Comp.Seed == null || entity.Comp.Dead) return; - if (!TryComp(entity, out var traits)) + if (!TryComp(entity, out var traits) || !TryComp(entity, out var harvest)) return; - if (!TryComp(entity, out var harvest)) - return; - - int deviation; - if (entity.Comp.Age > traits.Maturation) - deviation = (int)Math.Max(traits.Maturation - 1, entity.Comp.Age - _random.Next(7, 10)); - else - deviation = (int)(traits.Maturation / traits.GrowthStages); + var deviation = entity.Comp.Age > traits.Maturation + ? (int)Math.Max(traits.Maturation - 1, entity.Comp.Age - _random.Next(7, 10)) + : (int)(traits.Maturation / traits.GrowthStages); entity.Comp.Age -= deviation; entity.Comp.SkipAging++; diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDestroySeedsEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDestroySeedsEntityEffectSystem.cs index 8c53c739814..dd1522d8b62 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDestroySeedsEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDestroySeedsEntityEffectSystem.cs @@ -7,6 +7,9 @@ using Content.Shared.Popups; namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes; +/// +/// Entity effect that removes ability to get seeds from plant using seed maker. +/// public sealed partial class PlantDestroySeedsEntityEffectSystem : EntityEffectSystem { [Dependency] private readonly PlantHolderSystem _plantHolder = default!; @@ -17,10 +20,7 @@ public sealed partial class PlantDestroySeedsEntityEffectSystem : EntityEffectSy if (entity.Comp.Seed == null || entity.Comp.Dead || entity.Comp.Seed.Immutable) return; - if (!TryComp(entity, out var traits)) - return; - - if (traits.Seedless) + if (!TryComp(entity, out var traits) || traits.Seedless) return; _plantHolder.EnsureUniqueSeed(entity, entity.Comp); diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDiethylamineEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDiethylamineEntityEffectSystem.cs index e5c0b954c3e..3abfe2a1ffd 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDiethylamineEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDiethylamineEntityEffectSystem.cs @@ -1,25 +1,29 @@ using Content.Server.Botany.Components; -using Content.Server.Botany.Systems; using Content.Shared.EntityEffects; using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes; using Robust.Shared.Random; namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes; +/// +/// Entity effect that enhances plant longevity and endurance. +/// public sealed partial class PlantDiethylamineEntityEffectSystem : EntityEffectSystem { [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly PlantHolderSystem _plantHolder = default!; protected override void Effect(Entity entity, ref EntityEffectEvent args) { if (entity.Comp.Seed == null || entity.Comp.Dead || entity.Comp.Seed.Immutable) return; - if (_random.Prob(0.1f) && TryComp(entity, out var traits)) + if(!TryComp(entity, out var traits)) + return; + + if (_random.Prob(0.1f)) traits.Lifespan++; - if (_random.Prob(0.1f) && TryComp(entity, out traits)) + if (_random.Prob(0.1f)) traits.Endurance++; } } diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantPhalanximineEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantPhalanximineEntityEffectSystem.cs index 7971d6ea2da..fd28a1ea46f 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantPhalanximineEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantPhalanximineEntityEffectSystem.cs @@ -4,6 +4,9 @@ using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes; namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes; +/// +/// Entity effect that mutates plant to lose health with time. +/// public sealed partial class PlantPhalanximineEntityEffectSystem : EntityEffectSystem { protected override void Effect(Entity entity, ref EntityEffectEvent args) diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRestoreSeedsEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRestoreSeedsEntityEffectSystem.cs index 53402018405..bde6bdb111a 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRestoreSeedsEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRestoreSeedsEntityEffectSystem.cs @@ -6,6 +6,9 @@ using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes; namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes; +/// +/// Entity effect that restores ability to get seeds from plant seed maker. +/// public sealed partial class PlantRestoreSeedsEntityEffectSystem : EntityEffectSystem { [Dependency] private readonly PlantHolderSystem _plantHolder = default!; diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/RobustHarvestEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/RobustHarvestEntityEffectSystem.cs index 42c3b549478..f88951fedc6 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/RobustHarvestEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/RobustHarvestEntityEffectSystem.cs @@ -14,14 +14,16 @@ namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes; public sealed partial class RobustHarvestEntityEffectSystem : EntityEffectSystem { [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly PlantHolderSystem _plantHolder = default!; protected override void Effect(Entity entity, ref EntityEffectEvent args) { if (entity.Comp.Seed == null || entity.Comp.Dead) return; - if (TryComp(entity, out var traits) && traits.Potency < args.Effect.PotencyLimit) + if (!TryComp(entity, out var traits)) + return; + + if (traits.Potency < args.Effect.PotencyLimit) { traits.Potency = Math.Min(traits.Potency + args.Effect.PotencyIncrease, args.Effect.PotencyLimit); @@ -30,7 +32,7 @@ public sealed partial class RobustHarvestEntityEffectSystem : EntityEffectSystem traits.Seedless = true; } } - else if (TryComp(entity, out traits) && traits.Yield > 1 && _random.Prob(0.1f)) + else if (traits.Yield > 1 && _random.Prob(0.1f)) { // Too much of a good thing reduces yield traits.Yield--; diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffectSystem.cs index e5d00ce6162..70f1f52bf39 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantMutateGasesEntityEffectSystem.cs @@ -1,4 +1,3 @@ -using System.Linq; using Content.Server.Botany.Components; using Content.Shared.Atmos; using Content.Shared.EntityEffects; @@ -7,6 +6,9 @@ using Robust.Shared.Random; namespace Content.Server.EntityEffects.Effects.Botany; +/// +/// Plant mutation entity effect that forces plant to exude gas while living. +/// public sealed partial class PlantMutateExudeGasesEntityEffectSystem : EntityEffectSystem { [Dependency] private readonly IRobustRandom _random = default!; @@ -30,6 +32,9 @@ public sealed partial class PlantMutateExudeGasesEntityEffectSystem : EntityEffe } } +/// +/// Plant mutation entity effect that forces plant to consume gas while living. +/// public sealed partial class PlantMutateConsumeGasesEntityEffectSystem : EntityEffectSystem { [Dependency] private readonly IRobustRandom _random = default!; diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantMutateHarvestEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantMutateHarvestEntityEffectSystem.cs index 5377eaa29e7..c96b80588c4 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantMutateHarvestEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantMutateHarvestEntityEffectSystem.cs @@ -1,10 +1,12 @@ using Content.Server.Botany.Components; -using Content.Server.Botany.Systems; using Content.Shared.EntityEffects; using Content.Shared.EntityEffects.Effects.Botany; namespace Content.Server.EntityEffects.Effects.Botany; +/// +/// Plant mutation entity effect that changes repeatability of plant harvesting (without re-planting). +/// public sealed partial class PlantMutateHarvestEntityEffectSystem : EntityEffectSystem { protected override void Effect(Entity entity, ref EntityEffectEvent args) From 32fa986407f11b2ad0a8da61c7a12d8b62668881 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Sat, 13 Dec 2025 15:00:17 +0200 Subject: [PATCH 45/53] review --- .../Botany/Components/LogComponent.cs | 6 -- .../Botany/Components/PlantComponent.cs | 5 -- .../Botany/Components/PlantTraitsComponent.cs | 12 ++- .../Botany/Components/ProduceComponent.cs | 9 +- .../Botany/Components/SeedComponent.cs | 6 +- .../Components/UnviableGrowthComponent.cs | 14 ++- Content.Server/Botany/SeedPrototype.cs | 35 +++----- .../Botany/Systems/AtmosphericGrowthSystem.cs | 7 +- .../Botany/Systems/BasicGrowthSystem.cs | 78 ++++++++++++++--- .../Botany/Systems/BotanySwabSystem.cs | 21 ++--- .../Botany/Systems/BotanySystem.Seed.cs | 23 ++--- .../Systems/ConsumeExudeGasGrowthSystem.cs | 4 +- Content.Server/Botany/Systems/LogSystem.cs | 2 +- .../Botany/Systems/MutationSystem.cs | 5 +- .../Botany/Systems/PlantGrowthSystem.cs | 55 ------------ .../Botany/Systems/PlantHarvestSystem.cs | 26 +++--- .../Botany/Systems/PlantHolderSystem.cs | 86 ++++++------------- .../Botany/Systems/PlantToxinsSystem.cs | 7 +- .../Botany/Systems/PlantTraitsSystem.cs | 13 +-- .../Botany/Systems/UnviableGrowthSystem.cs | 22 ++--- .../Botany/Systems/WeedPestGrowthSystem.cs | 33 ++++--- 21 files changed, 205 insertions(+), 264 deletions(-) delete mode 100644 Content.Server/Botany/Components/PlantComponent.cs delete mode 100644 Content.Server/Botany/Systems/PlantGrowthSystem.cs diff --git a/Content.Server/Botany/Components/LogComponent.cs b/Content.Server/Botany/Components/LogComponent.cs index 987ce2b1598..da3f82416e3 100644 --- a/Content.Server/Botany/Components/LogComponent.cs +++ b/Content.Server/Botany/Components/LogComponent.cs @@ -10,14 +10,8 @@ namespace Content.Server.Botany.Components; [Access(typeof(LogSystem))] public sealed partial class LogComponent : Component { - /// - /// Prototype ID of the entity spawned when this log is chopped. - /// [DataField("spawnedPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] public string SpawnedPrototype = "MaterialWoodPlank1"; - /// - /// Number of entities spawned when this log is chopped. - /// [DataField("spawnCount")] public int SpawnCount = 2; } diff --git a/Content.Server/Botany/Components/PlantComponent.cs b/Content.Server/Botany/Components/PlantComponent.cs deleted file mode 100644 index c9f1677d88b..00000000000 --- a/Content.Server/Botany/Components/PlantComponent.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Content.Server.Botany.Components; - -[RegisterComponent] -[DataDefinition] -public sealed partial class PlantComponent : Component; diff --git a/Content.Server/Botany/Components/PlantTraitsComponent.cs b/Content.Server/Botany/Components/PlantTraitsComponent.cs index c1b4aae1a17..81024940422 100644 --- a/Content.Server/Botany/Components/PlantTraitsComponent.cs +++ b/Content.Server/Botany/Components/PlantTraitsComponent.cs @@ -1,6 +1,10 @@ +using Robust.Shared.Prototypes; + namespace Content.Server.Botany.Components; -/// Aggregate for general plant information and rare quirks. +/// +/// Aggregate for general plant information and rare quirks. +/// [RegisterComponent] [DataDefinition] public sealed partial class PlantTraitsComponent : Component @@ -74,6 +78,12 @@ public sealed partial class PlantTraitsComponent : Component [DataField] public bool TurnIntoKudzu = false; + /// + /// Which kind of kudzu this plant will turn into if it kuzuifies. + /// + [DataField] + public EntProtoId KudzuPrototype = "WeakKudzu"; + /// /// If false, rapidly decrease health while growing. Adds a bit of challenge to keep mutated plants alive via Unviable's frequency. /// diff --git a/Content.Server/Botany/Components/ProduceComponent.cs b/Content.Server/Botany/Components/ProduceComponent.cs index 6af952ce363..344fc7a89f6 100644 --- a/Content.Server/Botany/Components/ProduceComponent.cs +++ b/Content.Server/Botany/Components/ProduceComponent.cs @@ -1,6 +1,6 @@ using Content.Server.Botany.Systems; using Content.Shared.Botany.Components; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Prototypes; namespace Content.Server.Botany.Components; @@ -14,7 +14,8 @@ public sealed partial class ProduceComponent : SharedProduceComponent /// /// Name of the solution container that holds the produce's contents. /// - [DataField("targetSolution")] public string SolutionName { get; set; } = "food"; + [DataField("targetSolution")] + public string SolutionName { get; set; } = "food"; /// /// Seed data used to create a when this produce has its seeds extracted. @@ -25,6 +26,6 @@ public sealed partial class ProduceComponent : SharedProduceComponent /// /// Prototype ID for the seed that can be extracted from this produce. /// - [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? SeedId; + [DataField] + public ProtoId? SeedId; } diff --git a/Content.Server/Botany/Components/SeedComponent.cs b/Content.Server/Botany/Components/SeedComponent.cs index e3e046b389b..0f82d88720a 100644 --- a/Content.Server/Botany/Components/SeedComponent.cs +++ b/Content.Server/Botany/Components/SeedComponent.cs @@ -1,6 +1,6 @@ using Content.Server.Botany.Systems; using Content.Shared.Botany.Components; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Prototypes; namespace Content.Server.Botany.Components; @@ -27,6 +27,6 @@ public sealed partial class SeedComponent : SharedSeedComponent /// /// Name of a base seed prototype that is used if is null. /// - [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? SeedId; + [DataField] + public ProtoId? SeedId; } diff --git a/Content.Server/Botany/Components/UnviableGrowthComponent.cs b/Content.Server/Botany/Components/UnviableGrowthComponent.cs index 5e31bfdc414..4cf116d1e3b 100644 --- a/Content.Server/Botany/Components/UnviableGrowthComponent.cs +++ b/Content.Server/Botany/Components/UnviableGrowthComponent.cs @@ -1,19 +1,15 @@ namespace Content.Server.Botany.Components; -/// Damage tolerance of plant. +/// +/// Damage tolerance of plant. +/// [RegisterComponent] [DataDefinition] public sealed partial class UnviableGrowthComponent : Component { /// - /// Chance per tick for the plant to take damage due to being unviable. + /// Amount of damage dealt to the plant per successful tick with unviable. /// [DataField] - public float DeathChance = 0.1f; - - /// - /// Amount of damage dealt to the plant per successful death tick. - /// - [DataField] - public float DeathDamage = 6f; + public float UnviableDamage = 6f; } diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index 2f48d93fecb..c2591db93b6 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -4,10 +4,8 @@ using Content.Shared.FixedPoint; using Content.Shared.Random; using Robust.Shared.Audio; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; -using Robust.Shared.Utility; using Robust.Shared.Serialization.Manager; +using Robust.Shared.Utility; namespace Content.Server.Botany; @@ -92,23 +90,18 @@ public partial class SeedData /// /// The entity prototype that is spawned when this type of seed is extracted from produce using a seed extractor. /// - [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] - public string PacketPrototype = "SeedBase"; + [DataField] + public EntProtoId PacketPrototype = "SeedBase"; /// /// The entity prototypes that are spawned when this type of seed is harvested. /// - [DataField(customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List ProductPrototypes = []; + [DataField] + public List ProductPrototypes = []; [DataField] public Dictionary Chemicals = []; - #endregion - - #region General traits - - #endregion #region Cosmetics @@ -124,12 +117,6 @@ public partial class SeedData [DataField] public SoundSpecifier ScreamSound = new SoundCollectionSpecifier("PlantScreams", AudioParams.Default.WithVolume(-10)); - /// - /// Which kind of kudzu this plant will turn into if it kuzuifies. - /// - [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] - public string KudzuPrototype = "WeakKudzu"; - #endregion /// @@ -141,8 +128,8 @@ public partial class SeedData /// /// The seed prototypes this seed may mutate into when prompted to. /// - [DataField(customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List MutationPrototypes = []; + [DataField] + public List> MutationPrototypes = []; /// /// The growth components used by this seed. @@ -179,8 +166,8 @@ public partial class SeedData Mysterious = Mysterious, PacketPrototype = PacketPrototype, - ProductPrototypes = new List(ProductPrototypes), - MutationPrototypes = new List(MutationPrototypes), + ProductPrototypes = new List(ProductPrototypes), + MutationPrototypes = new List>(MutationPrototypes), Chemicals = new Dictionary(Chemicals), PlantRsi = PlantRsi, @@ -213,8 +200,8 @@ public partial class SeedData Mysterious = other.Mysterious, PacketPrototype = other.PacketPrototype, - ProductPrototypes = new List(other.ProductPrototypes), - MutationPrototypes = new List(other.MutationPrototypes), + ProductPrototypes = new List(other.ProductPrototypes), + MutationPrototypes = new List>(other.MutationPrototypes), Chemicals = new Dictionary(Chemicals), diff --git a/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs b/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs index 54c18e6d5c0..e2b172ca870 100644 --- a/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs +++ b/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs @@ -9,7 +9,7 @@ namespace Content.Server.Botany.Systems; /// Applies atmospheric temperature and pressure effects to plants during growth ticks. /// Uses current tile gas mixture to penalize or clear warnings based on tolerances. /// -public sealed class AtmosphericGrowthSystem : PlantGrowthSystem +public sealed class AtmosphericGrowthSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly IRobustRandom _random = default!; @@ -26,9 +26,10 @@ public sealed class AtmosphericGrowthSystem : PlantGrowthSystem var (uid, component) = ent; PlantHolderComponent? holder = null; - Resolve(uid, ref holder); + if (!Resolve(uid, ref holder)) + return; - if (holder?.Seed == null || holder.Dead) + if (holder.Seed == null || holder.Dead) return; var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index d4fe0f1a2fb..8c621003236 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -4,17 +4,26 @@ using Robust.Shared.Random; namespace Content.Server.Botany.Systems; -// TODO: make CO2Boost (add potency if the plant can eat an increasing amount of CO2). separate PR post-merge -// TODO: make GrowLight (run bonus ticks if theres a grow light nearby). separate PR post-merge. /// /// Handles baseline plant progression each growth tick: aging, resource consumption, /// simple viability checks, and basic swab cross-pollination behavior. /// -public sealed class BasicGrowthSystem : PlantGrowthSystem +public sealed class BasicGrowthSystem : EntitySystem { [Dependency] private readonly BotanySystem _botany = default!; - [Dependency] private readonly PlantHolderSystem _plantHolder = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly PlantHolderSystem _plantHolder = default!; + + // TODO: Multipliers should be taken from the hydroponics component. + /// + /// Multiplier for plant growth speed in hydroponics. + /// + public const float HydroponicsSpeedMultiplier = 1f; + + /// + /// Multiplier for resource consumption (water, nutrients) in hydroponics. + /// + public const float HydroponicsConsumptionMultiplier = 2f; public override void Initialize() { @@ -57,12 +66,13 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem var (uid, component) = ent; PlantHolderComponent? holder = null; - Resolve(uid, ref holder); - - if (holder == null || holder.Seed == null || holder.Dead) + if (!Resolve(uid, ref holder)) return; - // Check if the plant is viable + if (holder.Seed == null || holder.Dead) + return; + + // Check if the plant is viable. if (TryComp(uid, out var traits) && !traits.Viable) { holder.Health -= _random.Next(5, 10) * HydroponicsSpeedMultiplier; @@ -74,30 +84,33 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem // Advance plant age here. if (holder.SkipAging > 0) + { holder.SkipAging--; + } else { if (_random.Prob(0.8f)) - { holder.Age += (int)(1 * HydroponicsSpeedMultiplier); - holder.UpdateSpriteAfterUpdate = true; - } + + holder.UpdateSpriteAfterUpdate = true; } if (holder.Age < 0) // Revert back to seed packet! { var packetSeed = holder.Seed; - // will put it in the trays hands if it has any, please do not try doing this + // will put it in the trays hands if it has any, please do not try doing this. _botany.SpawnSeedPacket(packetSeed, Transform(uid).Coordinates, uid); _plantHolder.RemovePlant(uid, holder); holder.ForceUpdate = true; _plantHolder.Update(uid, holder); + return; } if (component.WaterConsumption > 0 && holder.WaterLevel > 0 && _random.Prob(0.75f)) { holder.WaterLevel -= MathF.Max(0f, component.WaterConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); + if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; } @@ -106,6 +119,7 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem { holder.NutritionLevel -= MathF.Max(0f, component.NutrientConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier); + if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; } @@ -133,6 +147,46 @@ public sealed class BasicGrowthSystem : PlantGrowthSystem AffectGrowth((uid, holder), -1); holder.Health -= healthMod; } + + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; + } + } + + /// + /// Affects the growth of a plant by modifying its age or production timing. + /// + public void AffectGrowth(Entity ent, int amount) + { + var (uid, component) = ent; + + if (component.Seed == null) + return; + + PlantHarvestComponent? harvest = null; + PlantTraitsComponent? traits = null; + if (!Resolve(uid, ref harvest, ref traits)) + return; + + if (amount > 0) + { + if (component.Age < traits.Maturation) + component.Age += amount; + else if (!harvest.ReadyForHarvest && traits.Yield <= 0f) + harvest.LastHarvest -= amount; + } + else + { + if (component.Age < traits.Maturation) + component.SkipAging++; + else if (!harvest.ReadyForHarvest && traits.Yield <= 0f) + harvest.LastHarvest += amount; } } } + +/// +/// Event of plant growing ticking. +/// +[ByRefEvent] +public readonly record struct OnPlantGrowEvent; diff --git a/Content.Server/Botany/Systems/BotanySwabSystem.cs b/Content.Server/Botany/Systems/BotanySwabSystem.cs index 84256f6b8da..fb698cad13b 100644 --- a/Content.Server/Botany/Systems/BotanySwabSystem.cs +++ b/Content.Server/Botany/Systems/BotanySwabSystem.cs @@ -9,13 +9,14 @@ namespace Content.Server.Botany.Systems; public sealed class BotanySwabSystem : EntitySystem { - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly MutationSystem _mutationSystem = default!; + [Dependency] private readonly MutationSystem _mutation = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnDoAfter); @@ -44,7 +45,7 @@ public sealed class BotanySwabSystem : EntitySystem if (args.Target == null || !args.CanReach || !HasComp(args.Target)) return; - _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, swab.SwabDelay, new BotanySwabDoAfterEvent(), uid, target: args.Target, used: uid) + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, swab.SwabDelay, new BotanySwabDoAfterEvent(), uid, target: args.Target, used: uid) { Broadcast = true, BreakOnMove = true, @@ -62,11 +63,11 @@ public sealed class BotanySwabSystem : EntitySystem if (swab.SeedData == null) { - // Pick up pollen + // Pick up pollen..= if (plant.Seed != null) swab.SeedData = plant.Seed.Clone(); - _popupSystem.PopupEntity(Loc.GetString("botany-swab-from"), args.Args.Target.Value, args.Args.User); + _popup.PopupEntity(Loc.GetString("botany-swab-from"), args.Args.Target.Value, args.Args.User); } else { @@ -74,13 +75,13 @@ public sealed class BotanySwabSystem : EntitySystem if (old == null) return; - // Cross-pollenate the plants - plant.Seed = _mutationSystem.Cross(swab.SeedData, old); + // Cross-pollenate the plants. + plant.Seed = _mutation.Cross(swab.SeedData, old); - // Transfer old plant pollen to swab + // Transfer old plant pollen to swab. swab.SeedData = old.Clone(); - _popupSystem.PopupEntity(Loc.GetString("botany-swab-to"), args.Args.Target.Value, args.Args.User); + _popup.PopupEntity(Loc.GetString("botany-swab-to"), args.Args.Target.Value, args.Args.User); } args.Handled = true; diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index c13b5ed27ca..c5a2e178e52 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -1,9 +1,12 @@ using Content.Server.Botany.Components; using Content.Server.Popups; -using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Administration.Logs; using Content.Shared.Botany; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Database; using Content.Shared.Examine; using Content.Shared.Hands.EntitySystems; +using Content.Shared.Kitchen.Components; using Content.Shared.Popups; using Content.Shared.Random; using Robust.Server.GameObjects; @@ -11,10 +14,6 @@ using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Random; using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Content.Shared.Administration.Logs; -using Content.Shared.Database; -using Content.Shared.Kitchen.Components; namespace Content.Server.Botany.Systems; @@ -47,7 +46,7 @@ public sealed partial class BotanySystem : EntitySystem } if (comp.SeedId != null - && _prototypeManager.TryIndex(comp.SeedId, out SeedPrototype? protoSeed)) + && _prototypeManager.TryIndex(comp.SeedId, out var protoSeed)) { seed = protoSeed.Clone(); return true; @@ -66,7 +65,7 @@ public sealed partial class BotanySystem : EntitySystem } if (comp.SeedId != null - && _prototypeManager.TryIndex(comp.SeedId, out SeedPrototype? protoSeed)) + && _prototypeManager.TryIndex(comp.SeedId, out var protoSeed)) { seed = protoSeed; return true; @@ -135,7 +134,7 @@ public sealed partial class BotanySystem : EntitySystem return GenerateProduct(proto, position, plantEntity); } - return Enumerable.Empty(); + return []; } public IEnumerable Harvest(SeedData proto, EntityUid user, EntityUid plantEntity) @@ -144,7 +143,7 @@ public sealed partial class BotanySystem : EntitySystem if (traits == null || proto.ProductPrototypes.Count == 0 || traits.Yield <= 0) { _popupSystem.PopupCursor(Loc.GetString("botany-harvest-fail-message"), user, PopupType.Medium); - return Enumerable.Empty(); + return []; } var name = Loc.GetString(proto.DisplayName); @@ -160,9 +159,11 @@ public sealed partial class BotanySystem : EntitySystem { var traits = GetPlantTraits(proto); if (traits == null) - return Enumerable.Empty(); + return []; var yieldMod = Comp(plantEntity).YieldMod; + var harvest = Comp(plantEntity); + var totalYield = 0; if (traits.Yield > -1) @@ -177,7 +178,7 @@ public sealed partial class BotanySystem : EntitySystem var products = new List(); - if (totalYield > 1) + if (totalYield > 1 || harvest.HarvestRepeat != HarvestType.NoRepeat) proto.Unique = false; for (var i = 0; i < totalYield; i++) diff --git a/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs index d8ae0bee281..c8a46c04669 100644 --- a/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs +++ b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs @@ -8,7 +8,7 @@ namespace Content.Server.Botany.Systems; /// Consumes and emits configured gases around plants each growth tick, then merges /// the adjusted gas mixture back into the environment. /// -public sealed class ConsumeExudeGasGrowthSystem : PlantGrowthSystem +public sealed class ConsumeExudeGasGrowthSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphere = default!; @@ -50,7 +50,7 @@ public sealed class ConsumeExudeGasGrowthSystem : PlantGrowthSystem if (holder.MissingGas > 0) { - holder.Health -= holder.MissingGas * HydroponicsSpeedMultiplier; + holder.Health -= holder.MissingGas * BasicGrowthSystem.HydroponicsSpeedMultiplier; if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; } diff --git a/Content.Server/Botany/Systems/LogSystem.cs b/Content.Server/Botany/Systems/LogSystem.cs index 08190af7083..7ca77b78d96 100644 --- a/Content.Server/Botany/Systems/LogSystem.cs +++ b/Content.Server/Botany/Systems/LogSystem.cs @@ -25,7 +25,7 @@ public sealed class LogSystem : EntitySystem if (!HasComp(args.Used)) return; - // if in some container, try pick up, else just drop to world + // if in some container, try pick up, else just drop to world. var inContainer = _containerSystem.IsEntityInContainer(uid); var pos = Transform(uid).Coordinates; diff --git a/Content.Server/Botany/Systems/MutationSystem.cs b/Content.Server/Botany/Systems/MutationSystem.cs index 022b792cf99..d11effe1eb0 100644 --- a/Content.Server/Botany/Systems/MutationSystem.cs +++ b/Content.Server/Botany/Systems/MutationSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Botany.Components; -using Content.Server.Botany.Systems; using Content.Shared.Atmos; using Content.Shared.EntityEffects; using Content.Shared.Random; @@ -8,7 +7,7 @@ using Robust.Shared.Random; using System.Linq; using Robust.Shared.Serialization.Manager; -namespace Content.Server.Botany; +namespace Content.Server.Botany.Systems; public sealed class MutationSystem : EntitySystem { @@ -105,9 +104,7 @@ public sealed class MutationSystem : EntitySystem { var traits = BotanySystem.GetPlantTraits(result); if (traits != null) - { traits.Seedless = true; - } } return result; diff --git a/Content.Server/Botany/Systems/PlantGrowthSystem.cs b/Content.Server/Botany/Systems/PlantGrowthSystem.cs deleted file mode 100644 index 6fd14e00ba7..00000000000 --- a/Content.Server/Botany/Systems/PlantGrowthSystem.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Content.Server.Botany.Components; - -namespace Content.Server.Botany.Systems; - -/// -/// Base for botany growth systems, providing shared helpers and constants used by -/// per-trait/per-environment growth handlers. -/// -public abstract class PlantGrowthSystem : EntitySystem -{ - /// - /// Multiplier for plant growth speed in hydroponics. - /// - public const float HydroponicsSpeedMultiplier = 1f; - - /// - /// Multiplier for resource consumption (water, nutrients) in hydroponics. - /// - public const float HydroponicsConsumptionMultiplier = 2f; - - /// - /// Affects the growth of a plant by modifying its age or production timing. - /// - public void AffectGrowth(Entity ent, int amount) - { - var (uid, component) = ent; - - if (component.Seed == null) - return; - - PlantHarvestComponent? harvest = null; - PlantTraitsComponent? traits = null; - if (!Resolve(uid, ref harvest, ref traits)) - return; - - if (amount > 0) - { - if (component.Age < traits.Maturation) - component.Age += amount; - else if (!harvest.ReadyForHarvest && traits.Yield <= 0f) - harvest.LastHarvest -= amount; - } - else - { - if (component.Age < traits.Maturation) - component.SkipAging++; - else if (!harvest.ReadyForHarvest && traits.Yield <= 0f) - harvest.LastHarvest += amount; - } - } -} - -/// Event of plant growing ticking. -[ByRefEvent] -public readonly record struct OnPlantGrowEvent; diff --git a/Content.Server/Botany/Systems/PlantHarvestSystem.cs b/Content.Server/Botany/Systems/PlantHarvestSystem.cs index 67d146a8285..f3bc753fec1 100644 --- a/Content.Server/Botany/Systems/PlantHarvestSystem.cs +++ b/Content.Server/Botany/Systems/PlantHarvestSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Botany.Components; -using Content.Server.Hands.Systems; using Content.Server.Popups; using Content.Shared.Interaction; using Robust.Shared.Audio.Systems; @@ -13,10 +12,9 @@ namespace Content.Server.Botany.Systems; public sealed class HarvestSystem : EntitySystem { [Dependency] private readonly BotanySystem _botany = default!; - [Dependency] private readonly HandsSystem _hands = default!; + [Dependency] private readonly PlantHolderSystem _plantHolder = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly PlantHolderSystem _plantHolder = default!; public override void Initialize() { @@ -42,7 +40,7 @@ public sealed class HarvestSystem : EntitySystem if (component is { ReadyForHarvest: true, HarvestRepeat: HarvestType.SelfHarvest }) AutoHarvest((ent, ent, plantHolder)); - // Check if plant is ready for harvest + // Check if plant is ready for harvest. var timeLastHarvest = plantHolder.Age - component.LastHarvest; if (timeLastHarvest > traits.Production && !component.ReadyForHarvest) { @@ -64,14 +62,14 @@ public sealed class HarvestSystem : EntitySystem if (!component.ReadyForHarvest || plantHolder.Dead || plantHolder.Seed == null || !traits.Ligneous) return; - // Check if sharp tool is required + // Check if sharp tool is required. if (!_botany.CanHarvest(plantHolder.Seed, args.Used)) { _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), args.User); return; } - // Perform harvest + // Perform harvest. DoHarvest(ent, args.User); } @@ -87,14 +85,14 @@ public sealed class HarvestSystem : EntitySystem if (!component.ReadyForHarvest || plantHolder.Dead || plantHolder.Seed == null) return; - // Check if sharp tool is required + // Check if sharp tool is required. if (traits.Ligneous) { _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), args.User); return; } - // Perform harvest + // Perform harvest. DoHarvest(ent, args.User); } @@ -109,7 +107,7 @@ public sealed class HarvestSystem : EntitySystem if (plantHolder.Dead) { - // Remove dead plant + // Remove dead plant. _plantHolder.RemovePlant(uid, plantHolder); AfterHarvest(ent); return; @@ -118,11 +116,11 @@ public sealed class HarvestSystem : EntitySystem if (!component.ReadyForHarvest) return; - // Spawn products - if(plantHolder.Seed != null) + // Spawn products. + if (plantHolder.Seed != null) _botany.Harvest(plantHolder.Seed, user, ent); - // Handle harvest type + // Handle harvest type. if (component.HarvestRepeat == HarvestType.NoRepeat) _plantHolder.RemovePlant(uid, plantHolder); @@ -138,11 +136,11 @@ public sealed class HarvestSystem : EntitySystem component.ReadyForHarvest = false; component.LastHarvest = plantHolder.Age; - // Play scream sound if applicable + // Play scream sound if applicable. if (traits.CanScream && plantHolder.Seed != null) _audio.PlayPvs(plantHolder.Seed.ScreamSound, uid); - // Update sprite + // Update sprite. _plantHolder.UpdateSprite(uid, plantHolder); } diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 858926353d0..4e7661f2677 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -6,10 +6,14 @@ using Content.Shared.Botany; using Content.Shared.Burial.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Database; +using Content.Shared.EntityEffects; using Content.Shared.Examine; using Content.Shared.FixedPoint; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; +using Content.Shared.Labels.Components; using Content.Shared.Popups; using Content.Shared.Random; using Content.Shared.Tag; @@ -20,31 +24,26 @@ using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization.Manager; using Robust.Shared.Timing; -using Content.Shared.Containers.ItemSlots; -using Content.Shared.Database; -using Content.Shared.EntityEffects; -using Content.Shared.Labels.Components; namespace Content.Server.Botany.Systems; public sealed class PlantHolderSystem : EntitySystem { - [Dependency] private readonly BotanySystem _botany = default!; - [Dependency] private readonly IPrototypeManager _prototype = default!; - [Dependency] private readonly MutationSystem _mutation = default!; [Dependency] private readonly AppearanceSystem _appearance = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly BotanySystem _botany = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; - [Dependency] private readonly TagSystem _tagSystem = default!; - [Dependency] private readonly RandomHelperSystem _randomHelper = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ISerializationManager _copier = default!; - [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; + [Dependency] private readonly MutationSystem _mutation = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly RandomHelperSystem _randomHelper = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedEntityEffectsSystem _entityEffects = default!; - + [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; + [Dependency] private readonly TagSystem _tag = default!; private static readonly ProtoId HoeTag = "Hoe"; private static readonly ProtoId PlantSampleTakerTag = "PlantSampleTaker"; @@ -190,7 +189,6 @@ public sealed class PlantHolderSystem : EntitySystem plantHolder.Seed = seed.Clone(); plantHolder.Dead = false; plantHolder.Age = 1; - plantHolder.DrawWarnings = true; // Get endurance from seed's PlantTraitsComponent var seedTraits = BotanySystem.GetPlantTraits(seed); @@ -218,15 +216,12 @@ public sealed class PlantHolderSystem : EntitySystem } } - EnsureComp(uid); - if (TryComp(args.Used, out var paperLabel)) { _itemSlots.TryEjectToHands(args.Used, paperLabel.LabelSlot, args.User); } QueueDel(args.Used); - CheckLevelSanity(uid, plantHolder); UpdateSprite(uid, plantHolder); if (seed.PlantLogImpact != null) @@ -243,7 +238,7 @@ public sealed class PlantHolderSystem : EntitySystem return; } - if (_tagSystem.HasTag(args.Used, HoeTag)) + if (_tag.HasTag(args.Used, HoeTag)) { args.Handled = true; if (plantHolder.WeedLevel > 0) @@ -294,7 +289,7 @@ public sealed class PlantHolderSystem : EntitySystem return; } - if (_tagSystem.HasTag(args.Used, PlantSampleTakerTag)) + if (_tag.HasTag(args.Used, PlantSampleTakerTag)) { args.Handled = true; if (plantHolder.Seed == null) @@ -360,7 +355,6 @@ public sealed class PlantHolderSystem : EntitySystem if (_random.Prob(0.3f)) plantHolder.Sampled = true; - CheckLevelSanity(uid, plantHolder); ForceUpdateByExternalCause(uid, plantHolder); } @@ -383,15 +377,15 @@ public sealed class PlantHolderSystem : EntitySystem Filter.PvsExcept(args.User), true); - if (_solutionContainerSystem.TryGetSolution(args.Used, produce.SolutionName, out var soln2, out var solution2)) + if (_solutionContainer.TryGetSolution(args.Used, produce.SolutionName, out var soln2, out var solution2)) { - if (_solutionContainerSystem.ResolveSolution(uid, plantHolder.SoilSolutionName, ref plantHolder.SoilSolution, out var solution1)) + if (_solutionContainer.ResolveSolution(uid, plantHolder.SoilSolutionName, ref plantHolder.SoilSolution, out var solution1)) { // We try to fit as much of the composted plant's contained solution into the hydroponics tray as we can, // since the plant will be consumed anyway. var fillAmount = FixedPoint2.Min(solution2.Volume, solution1.AvailableVolume); - _solutionContainerSystem.TryAddSolution(plantHolder.SoilSolution.Value, _solutionContainerSystem.SplitSolution(soln2.Value, fillAmount)); + _solutionContainer.TryAddSolution(plantHolder.SoilSolution.Value, _solutionContainer.SplitSolution(soln2.Value, fillAmount)); ForceUpdateByExternalCause(uid, plantHolder); } @@ -461,39 +455,12 @@ public sealed class PlantHolderSystem : EntitySystem } CheckHealth(uid, component); - CheckLevelSanity(uid, component); + if (component.UpdateSpriteAfterUpdate) UpdateSprite(uid, component); } - /// - /// Ensures all plant holder levels are within valid ranges. - /// TODO: Move this validation logic to individual growth components - /// - public void CheckLevelSanity(EntityUid uid, PlantHolderComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - if (component.Seed != null && TryComp(uid, out var traits)) - component.Health = MathHelper.Clamp(component.Health, 0, traits.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 void CheckHealth(EntityUid uid, PlantHolderComponent? component = null) { if (!Resolve(uid, ref component)) @@ -517,7 +484,7 @@ public sealed class PlantHolderSystem : EntitySystem component.YieldMod = 1; component.MutationMod = 1; component.ImproperPressure = false; - component.WeedLevel += 1; + component.WeedLevel += 1 * BasicGrowthSystem.HydroponicsSpeedMultiplier; component.PestLevel = 0; UpdateSprite(uid, component); @@ -526,7 +493,10 @@ public sealed class PlantHolderSystem : EntitySystem public void RemovePlant(EntityUid uid, PlantHolderComponent? component = null) { PlantHarvestComponent? harvest = null; - if (!Resolve(uid, ref component, ref harvest) || component.Seed == null) + if (!Resolve(uid, ref component, ref harvest)) + return; + + if (component.Seed == null) return; // Remove all growth components before planting new seed @@ -574,7 +544,7 @@ public sealed class PlantHolderSystem : EntitySystem if (!Resolve(uid, ref component)) return; - if (!_solutionContainerSystem.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution, out var solution)) + if (!_solutionContainer.ResolveSolution(uid, component.SoilSolutionName, ref component.SoilSolution, out var solution)) return; if (solution.Volume > 0 && component.MutationLevel < 25) @@ -585,10 +555,8 @@ public sealed class PlantHolderSystem : EntitySystem _entityEffects.ApplyEffects(uid, reagentProto.PlantMetabolisms.ToArray(), entry.Quantity.Float()); } - _solutionContainerSystem.RemoveEachReagent(component.SoilSolution.Value, FixedPoint2.New(1)); + _solutionContainer.RemoveEachReagent(component.SoilSolution.Value, FixedPoint2.New(1)); } - - CheckLevelSanity(uid, component); } private void Mutate(EntityUid uid, float severity, PlantHolderComponent? component = null) @@ -661,7 +629,7 @@ public sealed class PlantHolderSystem : EntitySystem _appearance.SetData(uid, PlantHolderVisuals.AlertLight, component.WeedLevel >= 5 || component.PestLevel >= 5 || component.Toxins >= 40 || component.ImproperHeat - ||component.ImproperPressure || component.MissingGas > 0, + || component.ImproperPressure || component.MissingGas > 0, app); _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, harvest is { ReadyForHarvest: true }, app); } diff --git a/Content.Server/Botany/Systems/PlantToxinsSystem.cs b/Content.Server/Botany/Systems/PlantToxinsSystem.cs index 920d4f4e82c..8038341c02e 100644 --- a/Content.Server/Botany/Systems/PlantToxinsSystem.cs +++ b/Content.Server/Botany/Systems/PlantToxinsSystem.cs @@ -6,7 +6,7 @@ namespace Content.Server.Botany.Systems; /// Handles toxin accumulation and tolerance for plants, applying health damage /// and decrementing toxins based on per-tick uptake. /// -public sealed class ToxinsSystem : PlantGrowthSystem +public sealed class ToxinsSystem : EntitySystem { public override void Initialize() { @@ -20,9 +20,10 @@ public sealed class ToxinsSystem : PlantGrowthSystem var (uid, component) = ent; PlantHolderComponent? holder = null; - Resolve(uid, ref holder); + if (!Resolve(uid, ref holder)) + return; - if (holder?.Seed == null || holder.Dead) + if (holder.Seed == null || holder.Dead) return; if (holder.Toxins > 0) diff --git a/Content.Server/Botany/Systems/PlantTraitsSystem.cs b/Content.Server/Botany/Systems/PlantTraitsSystem.cs index 46c5daa18eb..34ed7f0fa40 100644 --- a/Content.Server/Botany/Systems/PlantTraitsSystem.cs +++ b/Content.Server/Botany/Systems/PlantTraitsSystem.cs @@ -6,7 +6,7 @@ namespace Content.Server.Botany.Systems; /// /// Applies plant trait effects on growth ticks. /// -public sealed class PlantTraitsSystem : PlantGrowthSystem +public sealed class PlantTraitsSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; @@ -22,15 +22,16 @@ public sealed class PlantTraitsSystem : PlantGrowthSystem var (uid, component) = ent; PlantHolderComponent? holder = null; - Resolve(uid, ref holder); - - if (holder?.Seed == null || holder.Dead) + if (!Resolve(uid, ref holder)) return; - // Check if plant is too old + if (holder.Seed == null || holder.Dead) + return; + + // Check if plant is too old. if (holder.Age > component.Lifespan) { - holder.Health -= _random.Next(3, 5) * HydroponicsSpeedMultiplier; + holder.Health -= _random.Next(3, 5) * BasicGrowthSystem.HydroponicsSpeedMultiplier; if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; } diff --git a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs index bda86eb25ba..01faf62e84c 100644 --- a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs +++ b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs @@ -1,15 +1,12 @@ using Content.Server.Botany.Components; -using Robust.Shared.Random; namespace Content.Server.Botany.Systems; /// /// Applies a death chance and damage to unviable plants each growth tick, updating visuals when necessary. /// -public sealed class UnviableGrowthSystem : PlantGrowthSystem +public sealed class UnviableGrowthSystem : EntitySystem { - [Dependency] private readonly IRobustRandom _random = default!; - public override void Initialize() { base.Initialize(); @@ -22,17 +19,14 @@ public sealed class UnviableGrowthSystem : PlantGrowthSystem var (uid, component) = ent; PlantHolderComponent? holder = null; - Resolve(uid, ref holder); - - if (holder?.Seed == null || holder.Dead) + if (!Resolve(uid, ref holder)) return; - // Unviable plants have a chance to die each growth cycle - if (_random.Prob(component.DeathChance)) - { - holder.Health -= component.DeathDamage; - if (holder.DrawWarnings) - holder.UpdateSpriteAfterUpdate = true; - } + if (holder.Seed == null || holder.Dead) + return; + + holder.Health -= component.UnviableDamage; + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; } } diff --git a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs index ce83a488229..ddb7d0406ef 100644 --- a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs +++ b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs @@ -8,7 +8,7 @@ namespace Content.Server.Botany.Systems; /// Manages weed growth and pest damage per growth tick, and handles tray-level /// weed spawning and kudzu transformation based on conditions. /// -public sealed class WeedPestGrowthSystem : PlantGrowthSystem +public sealed class WeedPestGrowthSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; @@ -25,12 +25,13 @@ public sealed class WeedPestGrowthSystem : PlantGrowthSystem var (uid, component) = ent; PlantHolderComponent? holder = null; - Resolve(uid, ref holder); - - if (holder?.Seed == null || holder.Dead) + if (!Resolve(uid, ref holder)) return; - // Weed growth logic + if (holder.Seed == null || holder.Dead) + return; + + // Weed growth logic. if (_random.Prob(component.WeedGrowthChance)) { holder.WeedLevel += component.WeedGrowthAmount; @@ -38,13 +39,9 @@ public sealed class WeedPestGrowthSystem : PlantGrowthSystem holder.UpdateSpriteAfterUpdate = true; } - // Pest damage logic + // Pest damage logic. if (_random.Prob(component.PestDamageChance)) - { holder.Health -= component.PestDamageAmount; - if (holder.DrawWarnings) - holder.UpdateSpriteAfterUpdate = true; - } } /// @@ -54,7 +51,9 @@ public sealed class WeedPestGrowthSystem : PlantGrowthSystem { var (uid, component) = ent; - if (!TryComp(uid, out var traits)) + PlantTraitsComponent? traits = null; + WeedPestGrowthComponent? weed = null; + if (!Resolve(uid, ref traits, ref weed)) return; // Weeds like water and nutrients! They may appear even if there's not a seed planted @@ -69,21 +68,19 @@ public sealed class WeedPestGrowthSystem : PlantGrowthSystem chance = 0.01f; if (_random.Prob(chance)) - component.WeedLevel += 1 + component.WeedCoefficient; + component.WeedLevel += 1 + component.WeedCoefficient * BasicGrowthSystem.HydroponicsSpeedMultiplier; if (component.DrawWarnings) component.UpdateSpriteAfterUpdate = true; } - // Handle kudzu transformation + // Handle kudzu transformation. if (component is { Seed: not null, Dead: false } - && TryComp(uid, out var weed) - && TryComp(uid, out var kudzuTraits) - && kudzuTraits.TurnIntoKudzu + && traits.TurnIntoKudzu && component.WeedLevel >= weed.WeedHighLevelThreshold) { - Spawn(component.Seed.KudzuPrototype, Transform(uid).Coordinates.SnapToGrid(EntityManager)); - kudzuTraits.TurnIntoKudzu = false; + Spawn(traits.KudzuPrototype, Transform(uid).Coordinates.SnapToGrid(EntityManager)); + traits.TurnIntoKudzu = false; component.Health = 0; } } From d258fa4ff6f98a192afd76241e3242a188e6a917 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Sat, 13 Dec 2025 15:19:44 +0200 Subject: [PATCH 46/53] oops --- Content.Server/Botany/Systems/BotanySwabSystem.cs | 2 +- Content.Server/Botany/Systems/PlantHolderSystem.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Content.Server/Botany/Systems/BotanySwabSystem.cs b/Content.Server/Botany/Systems/BotanySwabSystem.cs index fb698cad13b..3fa55ea574c 100644 --- a/Content.Server/Botany/Systems/BotanySwabSystem.cs +++ b/Content.Server/Botany/Systems/BotanySwabSystem.cs @@ -63,7 +63,7 @@ public sealed class BotanySwabSystem : EntitySystem if (swab.SeedData == null) { - // Pick up pollen..= + // Pick up pollen. if (plant.Seed != null) swab.SeedData = plant.Seed.Clone(); diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 4e7661f2677..62cc3969cf1 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -456,7 +456,6 @@ public sealed class PlantHolderSystem : EntitySystem CheckHealth(uid, component); - if (component.UpdateSpriteAfterUpdate) UpdateSprite(uid, component); } From d4895c182a0c87b1ede1f02147d286bd23ea7584 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Sat, 13 Dec 2025 16:04:48 +0200 Subject: [PATCH 47/53] fix --- Content.Server/Botany/Components/PlantHarvestComponent.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Content.Server/Botany/Components/PlantHarvestComponent.cs b/Content.Server/Botany/Components/PlantHarvestComponent.cs index abcc1d74748..8bec13891eb 100644 --- a/Content.Server/Botany/Components/PlantHarvestComponent.cs +++ b/Content.Server/Botany/Components/PlantHarvestComponent.cs @@ -1,6 +1,8 @@ namespace Content.Server.Botany.Components; -/// Data for plant harvesting process. +/// +/// Data for plant harvesting process. +/// [RegisterComponent] [DataDefinition] public sealed partial class PlantHarvestComponent : Component From 3e55cd29dc5746d7faeba8ad511ff7be21d04815 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Sat, 13 Dec 2025 18:01:50 +0200 Subject: [PATCH 48/53] Separation of plant components into PlantComponent and PlantTraitsComponent and cleaning --- .../Components/GrowthComponentsHolder.cs | 6 + .../Botany/Components/PlantComponent.cs | 51 ++++++ .../Components/PlantHarvestComponent.cs | 4 +- .../Botany/Components/PlantTraitsComponent.cs | 49 +----- .../Botany/Systems/AtmosphericGrowthSystem.cs | 3 - .../Botany/Systems/BasicGrowthSystem.cs | 12 +- .../Botany/Systems/BotanySystem.Produce.cs | 8 +- .../Botany/Systems/BotanySystem.Seed.cs | 35 +++-- .../Systems/ConsumeExudeGasGrowthSystem.cs | 9 +- .../Botany/Systems/MutationSystem.cs | 6 +- .../Botany/Systems/PlantHarvestSystem.cs | 63 ++++---- .../Botany/Systems/PlantHolderSystem.cs | 53 +++---- .../{PlantTraitsSystem.cs => PlantSystem.cs} | 17 +- .../Botany/Systems/PlantToxinsSystem.cs | 20 +-- .../Botany/Systems/SeedExtractorSystem.cs | 3 +- .../Botany/Systems/UnviableGrowthSystem.cs | 3 - .../Botany/Systems/WeedPestGrowthSystem.cs | 5 +- .../PlantAdjustPotencyEntityEffectSystem.cs | 6 +- .../PlantCryoxadoneEntityEffectSystem.cs | 8 +- .../PlantDiethylamineEntityEffectSystem.cs | 6 +- .../RobustHarvestEntityEffectSystem.cs | 16 +- Resources/Maps/Salvage/vegan-meatball.yml | 4 +- Resources/Prototypes/Hydroponics/seeds.yml | 148 +++++++++--------- 23 files changed, 266 insertions(+), 269 deletions(-) create mode 100644 Content.Server/Botany/Components/PlantComponent.cs rename Content.Server/Botany/Systems/{PlantTraitsSystem.cs => PlantSystem.cs} (62%) diff --git a/Content.Server/Botany/Components/GrowthComponentsHolder.cs b/Content.Server/Botany/Components/GrowthComponentsHolder.cs index fd5969e13bc..d2c8ef65fd8 100644 --- a/Content.Server/Botany/Components/GrowthComponentsHolder.cs +++ b/Content.Server/Botany/Components/GrowthComponentsHolder.cs @@ -21,6 +21,12 @@ public sealed partial class GrowthComponentsHolder [DataField] public PlantTraitsComponent? PlantTraits { get; set; } + /// + /// Plant characteristics. + /// + [DataField] + public PlantComponent? Plant { get; set; } + /// /// Basic properties for plant growth. /// diff --git a/Content.Server/Botany/Components/PlantComponent.cs b/Content.Server/Botany/Components/PlantComponent.cs new file mode 100644 index 00000000000..350356dca37 --- /dev/null +++ b/Content.Server/Botany/Components/PlantComponent.cs @@ -0,0 +1,51 @@ +namespace Content.Server.Botany.Components; + +/// +/// Component for storing core plant data. +/// +[RegisterComponent] +[DataDefinition] +public sealed partial class PlantComponent : Component +{ + /// + /// The plant's max health. + /// + [DataField] + public float Endurance = 100f; + + /// + /// How many produce are created on harvest. + /// + [DataField] + public int Yield; + + /// + /// The number of growth ticks this plant can be alive for. Plants take high damage levels when Age > Lifespan. + /// + [DataField] + public float Lifespan; + + /// + /// The number of growth ticks it takes for a plant to reach its final growth stage. + /// + [DataField] + public float Maturation; + + /// + /// The number of growth ticks it takes for a plant to be (re-)harvestable. Shouldn't be lower than Maturation. + /// + [DataField] + public float Production; + + /// + /// How many different sprites appear before the plant is fully grown. + /// + [DataField] + public int GrowthStages = 6; + + /// + /// A scalar for sprite size and chemical quantity on the produce. Caps at 100. + /// + [DataField] + public float Potency = 1f; +} diff --git a/Content.Server/Botany/Components/PlantHarvestComponent.cs b/Content.Server/Botany/Components/PlantHarvestComponent.cs index 8bec13891eb..510f3619693 100644 --- a/Content.Server/Botany/Components/PlantHarvestComponent.cs +++ b/Content.Server/Botany/Components/PlantHarvestComponent.cs @@ -1,7 +1,7 @@ namespace Content.Server.Botany.Components; -/// -/// Data for plant harvesting process. +/// +/// Data for plant harvesting process. /// [RegisterComponent] [DataDefinition] diff --git a/Content.Server/Botany/Components/PlantTraitsComponent.cs b/Content.Server/Botany/Components/PlantTraitsComponent.cs index 81024940422..b2c1253060e 100644 --- a/Content.Server/Botany/Components/PlantTraitsComponent.cs +++ b/Content.Server/Botany/Components/PlantTraitsComponent.cs @@ -3,57 +3,14 @@ using Robust.Shared.Prototypes; namespace Content.Server.Botany.Components; /// -/// Aggregate for general plant information and rare quirks. +/// Component for managing special plant traits and mutations. /// +/// TODO: The logic for these component is quite hardcoded. +/// They require a separate a system that will use events or APIs from other growth systems. [RegisterComponent] [DataDefinition] public sealed partial class PlantTraitsComponent : Component { - /// - /// The plant's max health. - /// - [DataField] - public float Endurance = 100f; - - /// - /// How many produce are created on harvest. - /// - [DataField] - public int Yield; - - /// - /// The number of growth ticks this plant can be alive for. Plants take high damage levels when Age > Lifespan. - /// - [DataField] - public float Lifespan; - - /// - /// The number of growth ticks it takes for a plant to reach its final growth stage. - /// - [DataField] - public float Maturation; - - /// - /// The number of growth ticks it takes for a plant to be (re-)harvestable. Shouldn't be lower than Maturation. - /// - [DataField] - public float Production; - - /// - /// How many different sprites appear before the plant is fully grown. - /// - [DataField] - public int GrowthStages = 6; - - /// - /// A scalar for sprite size and chemical quantity on the produce. Caps at 100. - /// - [DataField] - public float Potency = 1f; - - /// TODO: The logic for these fields is quite hardcoded. - /// They require a separate component and a system that will use events or APIs from other growth systems. - /// /// If true, produce can't be put into the seed maker. /// diff --git a/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs b/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs index e2b172ca870..fbecc58e2b9 100644 --- a/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs +++ b/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs @@ -29,9 +29,6 @@ public sealed class AtmosphericGrowthSystem : EntitySystem if (!Resolve(uid, ref holder)) return; - if (holder.Seed == null || holder.Dead) - return; - var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; if (MathF.Abs(environment.Temperature - component.IdealHeat) > component.HeatTolerance) { diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index 8c621003236..a9aae6fb7ab 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -164,22 +164,22 @@ public sealed class BasicGrowthSystem : EntitySystem return; PlantHarvestComponent? harvest = null; - PlantTraitsComponent? traits = null; - if (!Resolve(uid, ref harvest, ref traits)) + PlantComponent? plant = null; + if (!Resolve(uid, ref harvest, ref plant)) return; if (amount > 0) { - if (component.Age < traits.Maturation) + if (component.Age < plant.Maturation) component.Age += amount; - else if (!harvest.ReadyForHarvest && traits.Yield <= 0f) + else if (!harvest.ReadyForHarvest && plant.Yield <= 0f) harvest.LastHarvest -= amount; } else { - if (component.Age < traits.Maturation) + if (component.Age < plant.Maturation) component.SkipAging++; - else if (!harvest.ReadyForHarvest && traits.Yield <= 0f) + else if (!harvest.ReadyForHarvest && plant.Yield <= 0f) harvest.LastHarvest += amount; } } diff --git a/Content.Server/Botany/Systems/BotanySystem.Produce.cs b/Content.Server/Botany/Systems/BotanySystem.Produce.cs index a4ad8a7e01c..022ce8c55e5 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Produce.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Produce.cs @@ -28,15 +28,15 @@ public sealed partial class BotanySystem solutionContainer.RemoveAllSolution(); - var traits = GetPlantTraits(seed); - if (traits == null) + var plant = GetPlantComponent(seed); + if (plant == null) return; foreach (var (chem, quantity) in seed.Chemicals) { var amount = quantity.Min; - if (quantity.PotencyDivisor > 0 && traits.Potency > 0) - amount += traits.Potency / quantity.PotencyDivisor; + if (quantity.PotencyDivisor > 0 && plant.Potency > 0) + amount += plant.Potency / quantity.PotencyDivisor; amount = FixedPoint2.Clamp(amount, quantity.Min, quantity.Max); solutionContainer.MaxVolume += amount; solutionContainer.AddReagent(chem, amount); diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index c5a2e178e52..3544c0d5839 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -75,7 +75,14 @@ public sealed partial class BotanySystem : EntitySystem return false; } - public static PlantTraitsComponent? GetPlantTraits(SeedData seed) + /// TODO: Delete after plants transition to entities + public static PlantComponent? GetPlantComponent(SeedData seed) + { + return seed.GrowthComponents.Plant; + } + + /// TODO: Delete after plants transition to entities + public static PlantTraitsComponent? GetPlantTraitsComponent(SeedData seed) { return seed.GrowthComponents.PlantTraits; } @@ -88,16 +95,16 @@ public sealed partial class BotanySystem : EntitySystem if (!TryGetSeed(component, out var seed)) return; - var traits = GetPlantTraits(seed); - if (traits == null) + var plant = GetPlantComponent(seed); + if (plant == null) return; using (args.PushGroup(nameof(SeedComponent), 1)) { var name = Loc.GetString(seed.DisplayName); args.PushMarkup(Loc.GetString($"seed-component-description", ("seedName", name))); - args.PushMarkup(Loc.GetString($"seed-component-plant-yield-text", ("seedYield", traits.Yield))); - args.PushMarkup(Loc.GetString($"seed-component-plant-potency-text", ("seedPotency", traits.Potency))); + args.PushMarkup(Loc.GetString($"seed-component-plant-yield-text", ("seedYield", plant.Yield))); + args.PushMarkup(Loc.GetString($"seed-component-plant-potency-text", ("seedPotency", plant.Potency))); } } @@ -139,8 +146,8 @@ public sealed partial class BotanySystem : EntitySystem public IEnumerable Harvest(SeedData proto, EntityUid user, EntityUid plantEntity) { - var traits = GetPlantTraits(proto); - if (traits == null || proto.ProductPrototypes.Count == 0 || traits.Yield <= 0) + var plant = GetPlantComponent(proto); + if (plant == null || proto.ProductPrototypes.Count == 0 || plant.Yield <= 0) { _popupSystem.PopupCursor(Loc.GetString("botany-harvest-fail-message"), user, PopupType.Medium); return []; @@ -157,8 +164,8 @@ public sealed partial class BotanySystem : EntitySystem public IEnumerable GenerateProduct(SeedData proto, EntityCoordinates position, EntityUid plantEntity) { - var traits = GetPlantTraits(proto); - if (traits == null) + var plant = GetPlantComponent(proto); + if (plant == null) return []; var yieldMod = Comp(plantEntity).YieldMod; @@ -166,12 +173,12 @@ public sealed partial class BotanySystem : EntitySystem var totalYield = 0; - if (traits.Yield > -1) + if (plant.Yield > -1) { if (yieldMod < 0) - totalYield = traits.Yield; + totalYield = plant.Yield; else - totalYield = traits.Yield * yieldMod; + totalYield = plant.Yield * yieldMod; totalYield = Math.Max(1, totalYield); } @@ -195,7 +202,7 @@ public sealed partial class BotanySystem : EntitySystem ProduceGrown(entity, produce); - _appearance.SetData(entity, ProduceVisuals.Potency, traits.Potency); + _appearance.SetData(entity, ProduceVisuals.Potency, plant.Potency); if (proto.Mysterious) { @@ -211,7 +218,7 @@ public sealed partial class BotanySystem : EntitySystem public bool CanHarvest(SeedData proto, EntityUid? held = null) { - var traits = GetPlantTraits(proto); + var traits = GetPlantTraitsComponent(proto); if (traits == null) return true; diff --git a/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs index c8a46c04669..f975f52e9bb 100644 --- a/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs +++ b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs @@ -24,11 +24,8 @@ public sealed class ConsumeExudeGasGrowthSystem : EntitySystem var (uid, component) = ent; PlantHolderComponent? holder = null; - PlantTraitsComponent? traits = null; - if (!Resolve(uid, ref holder, ref traits)) - return; - - if (holder.Seed == null || holder.Dead) + PlantComponent? plant = null; + if (!Resolve(uid, ref holder, ref plant)) return; var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; @@ -63,7 +60,7 @@ public sealed class ConsumeExudeGasGrowthSystem : EntitySystem foreach (var (gas, amount) in component.ExudeGasses) { environment.AdjustMoles(gas, - MathF.Max(1f, MathF.Round(amount * MathF.Round(traits.Potency) / exudeCount))); + MathF.Max(1f, MathF.Round(amount * MathF.Round(plant.Potency) / exudeCount))); } } } diff --git a/Content.Server/Botany/Systems/MutationSystem.cs b/Content.Server/Botany/Systems/MutationSystem.cs index d11effe1eb0..eafe1d4ab97 100644 --- a/Content.Server/Botany/Systems/MutationSystem.cs +++ b/Content.Server/Botany/Systems/MutationSystem.cs @@ -82,8 +82,8 @@ public sealed class MutationSystem : EntitySystem CrossChemicals(ref result.Chemicals, a.Chemicals); - var sourceTraits = BotanySystem.GetPlantTraits(a); - var resultTraits = BotanySystem.GetPlantTraits(result); + var sourceTraits = BotanySystem.GetPlantTraitsComponent(a); + var resultTraits = BotanySystem.GetPlantTraitsComponent(result); if (sourceTraits != null && resultTraits != null) { @@ -102,7 +102,7 @@ public sealed class MutationSystem : EntitySystem // effective hybrid crossings. if (a.Name != result.Name && Random(0.7f)) { - var traits = BotanySystem.GetPlantTraits(result); + var traits = BotanySystem.GetPlantTraitsComponent(result); if (traits != null) traits.Seedless = true; } diff --git a/Content.Server/Botany/Systems/PlantHarvestSystem.cs b/Content.Server/Botany/Systems/PlantHarvestSystem.cs index f3bc753fec1..217c03f754a 100644 --- a/Content.Server/Botany/Systems/PlantHarvestSystem.cs +++ b/Content.Server/Botany/Systems/PlantHarvestSystem.cs @@ -12,7 +12,7 @@ namespace Content.Server.Botany.Systems; public sealed class HarvestSystem : EntitySystem { [Dependency] private readonly BotanySystem _botany = default!; - [Dependency] private readonly PlantHolderSystem _plantHolder = default!; + [Dependency] private readonly PlantHolderSystem _holder = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; @@ -29,24 +29,21 @@ public sealed class HarvestSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? plantHolder = null; - PlantTraitsComponent? traits = null; - if (!Resolve(uid, ref plantHolder, ref traits)) - return; - - if (plantHolder.Dead || plantHolder.Seed == null) + PlantHolderComponent? holder = null; + PlantComponent? plant = null; + if (!Resolve(uid, ref holder, ref plant)) return; if (component is { ReadyForHarvest: true, HarvestRepeat: HarvestType.SelfHarvest }) - AutoHarvest((ent, ent, plantHolder)); + AutoHarvest((ent, ent, holder)); // Check if plant is ready for harvest. - var timeLastHarvest = plantHolder.Age - component.LastHarvest; - if (timeLastHarvest > traits.Production && !component.ReadyForHarvest) + var timeLastHarvest = holder.Age - component.LastHarvest; + if (timeLastHarvest > plant.Production && !component.ReadyForHarvest) { component.ReadyForHarvest = true; - component.LastHarvest = plantHolder.Age; - plantHolder.UpdateSpriteAfterUpdate = true; + component.LastHarvest = holder.Age; + holder.UpdateSpriteAfterUpdate = true; } } @@ -54,16 +51,16 @@ public sealed class HarvestSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? plantHolder = null; + PlantHolderComponent? holder = null; PlantTraitsComponent? traits = null; - if (!Resolve(uid, ref plantHolder, ref traits)) + if (!Resolve(uid, ref holder, ref traits)) return; - if (!component.ReadyForHarvest || plantHolder.Dead || plantHolder.Seed == null || !traits.Ligneous) + if (!component.ReadyForHarvest || holder.Dead || holder.Seed == null || !traits.Ligneous) return; // Check if sharp tool is required. - if (!_botany.CanHarvest(plantHolder.Seed, args.Used)) + if (!_botany.CanHarvest(holder.Seed, args.Used)) { _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), args.User); return; @@ -77,12 +74,12 @@ public sealed class HarvestSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? plantHolder = null; + PlantHolderComponent? holder = null; PlantTraitsComponent? traits = null; - if (!Resolve(uid, ref plantHolder, ref traits)) + if (!Resolve(uid, ref holder, ref traits)) return; - if (!component.ReadyForHarvest || plantHolder.Dead || plantHolder.Seed == null) + if (!component.ReadyForHarvest || holder.Dead || holder.Seed == null) return; // Check if sharp tool is required. @@ -100,15 +97,15 @@ public sealed class HarvestSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? plantHolder = null; + PlantHolderComponent? holder = null; PlantTraitsComponent? traits = null; - if (!Resolve(uid, ref plantHolder, ref traits)) + if (!Resolve(uid, ref holder, ref traits)) return; - if (plantHolder.Dead) + if (holder.Dead) { // Remove dead plant. - _plantHolder.RemovePlant(uid, plantHolder); + _holder.RemovePlant(uid, holder); AfterHarvest(ent); return; } @@ -117,31 +114,31 @@ public sealed class HarvestSystem : EntitySystem return; // Spawn products. - if (plantHolder.Seed != null) - _botany.Harvest(plantHolder.Seed, user, ent); + if (holder.Seed != null) + _botany.Harvest(holder.Seed, user, ent); // Handle harvest type. if (component.HarvestRepeat == HarvestType.NoRepeat) - _plantHolder.RemovePlant(uid, plantHolder); + _holder.RemovePlant(uid, holder); - AfterHarvest(ent, plantHolder, traits); + AfterHarvest(ent, holder, traits); } - private void AfterHarvest(Entity ent, PlantHolderComponent? plantHolder = null, PlantTraitsComponent? traits = null) + private void AfterHarvest(Entity ent, PlantHolderComponent? holder = null, PlantTraitsComponent? traits = null) { var (uid, component) = ent; - if (!Resolve(uid, ref traits, ref plantHolder)) + if (!Resolve(uid, ref traits, ref holder)) return; component.ReadyForHarvest = false; - component.LastHarvest = plantHolder.Age; + component.LastHarvest = holder.Age; // Play scream sound if applicable. - if (traits.CanScream && plantHolder.Seed != null) - _audio.PlayPvs(plantHolder.Seed.ScreamSound, uid); + if (traits.CanScream && holder.Seed != null) + _audio.PlayPvs(holder.Seed.ScreamSound, uid); // Update sprite. - _plantHolder.UpdateSprite(uid, plantHolder); + _holder.UpdateSprite(uid, holder); } /// diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 62cc3969cf1..e43105c9fa7 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -79,19 +79,24 @@ public sealed class PlantHolderSystem : EntitySystem if (component.Seed == null) return 0; - if (!TryComp(uid, out var traits)) + if (!TryComp(uid, out var plant)) return 0; - var result = Math.Max(1, (int)(component.Age * traits.GrowthStages / traits.Maturation)); + var result = Math.Max(1, (int)(component.Age * plant.GrowthStages / plant.Maturation)); return result; } private void OnExamine(Entity entity, ref ExaminedEvent args) { - if (!args.IsInDetailsRange) + var (uid, component) = entity; + + PlantComponent? plant = null; + PlantTraitsComponent? traits = null; + if (!Resolve(uid, ref plant, ref traits)) return; - var component = entity.Comp; + if (!args.IsInDetailsRange) + return; using (args.PushGroup(nameof(PlantHolderComponent))) { @@ -106,21 +111,17 @@ public sealed class PlantHolderSystem : EntitySystem ("seedName", displayName), ("toBeForm", displayName.EndsWith('s') ? "are" : "is"))); - if (!TryComp(entity, out var traits)) - return; - - if (component.Health <= traits.Endurance / 2) + if (component.Health <= plant.Endurance / 2) { args.PushMarkup(Loc.GetString( "plant-holder-component-something-already-growing-low-health-message", ("healthState", - Loc.GetString(component.Age > traits.Lifespan + Loc.GetString(component.Age > plant.Lifespan ? "plant-holder-component-plant-old-adjective" : "plant-holder-component-plant-unhealthy-adjective")))); } // For future reference, mutations should only appear on examine if they apply to a plant, not to produce. - if (traits.Ligneous) args.PushMarkup(Loc.GetString("mutation-plant-ligneous")); @@ -190,16 +191,12 @@ public sealed class PlantHolderSystem : EntitySystem plantHolder.Dead = false; plantHolder.Age = 1; - // Get endurance from seed's PlantTraitsComponent - var seedTraits = BotanySystem.GetPlantTraits(seed); + var seedPlant = BotanySystem.GetPlantComponent(seed); if (seeds.HealthOverride != null) - { plantHolder.Health = seeds.HealthOverride.Value; - } - else if (seedTraits != null) - { - plantHolder.Health = seedTraits.Endurance; - } + else if (seedPlant != null) + plantHolder.Health = seedPlant.Endurance; + plantHolder.LastCycle = _gameTiming.CurTime; // Ensure no existing growth components before adding new ones @@ -393,10 +390,10 @@ public sealed class PlantHolderSystem : EntitySystem var seed = produce.Seed; if (seed != null) { - var seedTraits = BotanySystem.GetPlantTraits(seed); - if (seedTraits != null) + var seedPlant = BotanySystem.GetPlantComponent(seed); + if (seedPlant != null) { - var nutrientBonus = seedTraits.Potency / 2.5f; + var nutrientBonus = seedPlant.Potency / 2.5f; AdjustNutrient(uid, nutrientBonus, plantHolder); } } @@ -551,7 +548,7 @@ public sealed class PlantHolderSystem : EntitySystem foreach (var entry in component.SoilSolution.Value.Comp.Solution.Contents) { var reagentProto = _prototype.Index(entry.Reagent.Prototype); - _entityEffects.ApplyEffects(uid, reagentProto.PlantMetabolisms.ToArray(), entry.Quantity.Float()); + _entityEffects.ApplyEffects(uid, [.. reagentProto.PlantMetabolisms], entry.Quantity.Float()); } _solutionContainer.RemoveEachReagent(component.SoilSolution.Value, FixedPoint2.New(1)); @@ -573,8 +570,8 @@ public sealed class PlantHolderSystem : EntitySystem public void UpdateSprite(EntityUid uid, PlantHolderComponent component) { PlantHarvestComponent? harvest = null; - PlantTraitsComponent? traits = null; - Resolve(uid, ref harvest, ref traits, false); + PlantComponent? plant = null; + Resolve(uid, ref harvest, ref plant, false); component.UpdateSpriteAfterUpdate = false; @@ -588,11 +585,11 @@ public sealed class PlantHolderSystem : EntitySystem _appearance.SetData(uid, PlantHolderVisuals.HealthLight, false, app); _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, false, app); } - else if (harvest != null && traits != null) + else if (harvest != null && plant != null) { if (component.DrawWarnings) { - _appearance.SetData(uid, PlantHolderVisuals.HealthLight, component.Health <= traits.Endurance / 2f); + _appearance.SetData(uid, PlantHolderVisuals.HealthLight, component.Health <= plant.Endurance / 2f); } if (component.Dead) @@ -605,7 +602,7 @@ public sealed class PlantHolderSystem : EntitySystem _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app); _appearance.SetData(uid, PlantHolderVisuals.PlantState, "harvest", app); } - else if (component.Age < traits.Maturation) + else if (component.Age < plant.Maturation) { var growthStage = GetCurrentGrowthStage((uid, component)); @@ -616,7 +613,7 @@ public sealed class PlantHolderSystem : EntitySystem else { _appearance.SetData(uid, PlantHolderVisuals.PlantRsi, component.Seed.PlantRsi.ToString(), app); - _appearance.SetData(uid, PlantHolderVisuals.PlantState, $"stage-{traits.GrowthStages}", app); + _appearance.SetData(uid, PlantHolderVisuals.PlantState, $"stage-{plant.GrowthStages}", app); } } diff --git a/Content.Server/Botany/Systems/PlantTraitsSystem.cs b/Content.Server/Botany/Systems/PlantSystem.cs similarity index 62% rename from Content.Server/Botany/Systems/PlantTraitsSystem.cs rename to Content.Server/Botany/Systems/PlantSystem.cs index 34ed7f0fa40..d7cbee77ae9 100644 --- a/Content.Server/Botany/Systems/PlantTraitsSystem.cs +++ b/Content.Server/Botany/Systems/PlantSystem.cs @@ -6,7 +6,7 @@ namespace Content.Server.Botany.Systems; /// /// Applies plant trait effects on growth ticks. /// -public sealed class PlantTraitsSystem : EntitySystem +public sealed class PlantSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; @@ -14,10 +14,10 @@ public sealed class PlantTraitsSystem : EntitySystem { base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); + SubscribeLocalEvent(OnPlantGrow); } - private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) + private void OnPlantGrow(Entity ent, ref OnPlantGrowEvent args) { var (uid, component) = ent; @@ -25,9 +25,6 @@ public sealed class PlantTraitsSystem : EntitySystem if (!Resolve(uid, ref holder)) return; - if (holder.Seed == null || holder.Dead) - return; - // Check if plant is too old. if (holder.Age > component.Lifespan) { @@ -38,12 +35,12 @@ public sealed class PlantTraitsSystem : EntitySystem } /// - /// Adjusts the potency of a plant traits component. + /// Adjusts the potency of a plant component. /// - public void AdjustPotency(Entity ent, float delta) + public void AdjustPotency(Entity ent, float delta) { - var traits = ent.Comp; - traits.Potency = Math.Max(traits.Potency + delta, 1); + var plant = ent.Comp; + plant.Potency = Math.Max(plant.Potency + delta, 1); Dirty(ent); } } diff --git a/Content.Server/Botany/Systems/PlantToxinsSystem.cs b/Content.Server/Botany/Systems/PlantToxinsSystem.cs index 8038341c02e..b3505922995 100644 --- a/Content.Server/Botany/Systems/PlantToxinsSystem.cs +++ b/Content.Server/Botany/Systems/PlantToxinsSystem.cs @@ -23,20 +23,16 @@ public sealed class ToxinsSystem : EntitySystem if (!Resolve(uid, ref holder)) return; - if (holder.Seed == null || holder.Dead) + if (holder.Toxins < 0) return; - if (holder.Toxins > 0) - { - var toxinUptake = MathF.Max(1, MathF.Round(holder.Toxins / component.ToxinUptakeDivisor)); - if (holder.Toxins > component.ToxinsTolerance) - { - holder.Health -= toxinUptake; - } + var toxinUptake = MathF.Max(1, MathF.Round(holder.Toxins / component.ToxinUptakeDivisor)); + if (holder.Toxins > component.ToxinsTolerance) + holder.Health -= toxinUptake; - holder.Toxins -= toxinUptake; - if (holder.DrawWarnings) - holder.UpdateSpriteAfterUpdate = true; - } + holder.Toxins -= toxinUptake; + + if (holder.DrawWarnings) + holder.UpdateSpriteAfterUpdate = true; } } diff --git a/Content.Server/Botany/Systems/SeedExtractorSystem.cs b/Content.Server/Botany/Systems/SeedExtractorSystem.cs index 4c6bf508f54..be3e2174285 100644 --- a/Content.Server/Botany/Systems/SeedExtractorSystem.cs +++ b/Content.Server/Botany/Systems/SeedExtractorSystem.cs @@ -27,10 +27,11 @@ public sealed class SeedExtractorSystem : EntitySystem if (!TryComp(args.Used, out ProduceComponent? produce)) return; + if (!_botanySystem.TryGetSeed(produce, out var seed)) return; - var traits = BotanySystem.GetPlantTraits(seed); + var traits = BotanySystem.GetPlantTraitsComponent(seed); if (traits?.Seedless == true) { _popupSystem.PopupCursor(Loc.GetString("seed-extractor-component-no-seeds", ("name", args.Used)), diff --git a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs index 01faf62e84c..8f0b9d46207 100644 --- a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs +++ b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs @@ -22,9 +22,6 @@ public sealed class UnviableGrowthSystem : EntitySystem if (!Resolve(uid, ref holder)) return; - if (holder.Seed == null || holder.Dead) - return; - holder.Health -= component.UnviableDamage; if (holder.DrawWarnings) holder.UpdateSpriteAfterUpdate = true; diff --git a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs index ddb7d0406ef..07ef9350569 100644 --- a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs +++ b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs @@ -28,9 +28,6 @@ public sealed class WeedPestGrowthSystem : EntitySystem if (!Resolve(uid, ref holder)) return; - if (holder.Seed == null || holder.Dead) - return; - // Weed growth logic. if (_random.Prob(component.WeedGrowthChance)) { @@ -56,7 +53,7 @@ public sealed class WeedPestGrowthSystem : EntitySystem if (!Resolve(uid, ref traits, ref weed)) return; - // Weeds like water and nutrients! They may appear even if there's not a seed planted + // Weeds like water and nutrients! They may appear even if there's not a seed planted. if (component is { WaterLevel: > 10, NutritionLevel: > 5 }) { float chance; diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs index ae80f8ed0d6..2ac191ad413 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantAdjustPotencyEntityEffectSystem.cs @@ -10,16 +10,16 @@ namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes; /// public sealed partial class PlantAdjustPotencyEntityEffectSystem : EntityEffectSystem { - [Dependency] private readonly PlantTraitsSystem _plantTraits = default!; + [Dependency] private readonly PlantSystem _plant = default!; protected override void Effect(Entity entity, ref EntityEffectEvent args) { if (entity.Comp.Seed == null || entity.Comp.Dead) return; - if (!TryComp(entity, out var traits)) + if (!TryComp(entity, out var plant)) return; - _plantTraits.AdjustPotency((entity.Owner, traits), args.Effect.Amount); + _plant.AdjustPotency((entity.Owner, plant), args.Effect.Amount); } } diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs index ebd3e0953d1..c9d3bf7e221 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantCryoxadoneEntityEffectSystem.cs @@ -17,12 +17,12 @@ public sealed partial class PlantCryoxadoneEntityEffectSystem : EntityEffectSyst if (entity.Comp.Seed == null || entity.Comp.Dead) return; - if (!TryComp(entity, out var traits) || !TryComp(entity, out var harvest)) + if (!TryComp(entity, out var plant) || !TryComp(entity, out var harvest)) return; - var deviation = entity.Comp.Age > traits.Maturation - ? (int)Math.Max(traits.Maturation - 1, entity.Comp.Age - _random.Next(7, 10)) - : (int)(traits.Maturation / traits.GrowthStages); + var deviation = entity.Comp.Age > plant.Maturation + ? (int)Math.Max(plant.Maturation - 1, entity.Comp.Age - _random.Next(7, 10)) + : (int)(plant.Maturation / plant.GrowthStages); entity.Comp.Age -= deviation; entity.Comp.SkipAging++; diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDiethylamineEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDiethylamineEntityEffectSystem.cs index 3abfe2a1ffd..34575b2124c 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDiethylamineEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDiethylamineEntityEffectSystem.cs @@ -17,13 +17,13 @@ public sealed partial class PlantDiethylamineEntityEffectSystem : EntityEffectSy if (entity.Comp.Seed == null || entity.Comp.Dead || entity.Comp.Seed.Immutable) return; - if(!TryComp(entity, out var traits)) + if (!TryComp(entity, out var plant)) return; if (_random.Prob(0.1f)) - traits.Lifespan++; + plant.Lifespan++; if (_random.Prob(0.1f)) - traits.Endurance++; + plant.Endurance++; } } diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/RobustHarvestEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/RobustHarvestEntityEffectSystem.cs index f88951fedc6..79ed860343b 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/RobustHarvestEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/RobustHarvestEntityEffectSystem.cs @@ -20,22 +20,20 @@ public sealed partial class RobustHarvestEntityEffectSystem : EntityEffectSystem if (entity.Comp.Seed == null || entity.Comp.Dead) return; - if (!TryComp(entity, out var traits)) + if (!TryComp(entity, out var plant) || !TryComp(entity, out var traits)) return; - if (traits.Potency < args.Effect.PotencyLimit) + if (plant.Potency < args.Effect.PotencyLimit) { - traits.Potency = Math.Min(traits.Potency + args.Effect.PotencyIncrease, args.Effect.PotencyLimit); + plant.Potency = Math.Min(plant.Potency + args.Effect.PotencyIncrease, args.Effect.PotencyLimit); - if (traits.Potency > args.Effect.PotencySeedlessThreshold) - { + if (plant.Potency > args.Effect.PotencySeedlessThreshold) traits.Seedless = true; - } } - else if (traits.Yield > 1 && _random.Prob(0.1f)) + else if (plant.Yield > 1 && _random.Prob(0.1f)) { - // Too much of a good thing reduces yield - traits.Yield--; + // Too much of a good thing reduces yield. + plant.Yield--; } } } diff --git a/Resources/Maps/Salvage/vegan-meatball.yml b/Resources/Maps/Salvage/vegan-meatball.yml index 48944cf21a7..e128151ca58 100644 --- a/Resources/Maps/Salvage/vegan-meatball.yml +++ b/Resources/Maps/Salvage/vegan-meatball.yml @@ -486,7 +486,7 @@ entities: health: 100 seed: growthComponents: - plantTraits: + plant: lifespan: 75 endurance: 100 basicGrowth: @@ -559,7 +559,7 @@ entities: health: 100 seed: growthComponents: - plantTraits: + plant: lifespan: 25 endurance: 100 basicGrowth: diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index d6b5445e00b..d3d444b9816 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -10,7 +10,7 @@ mutationPrototypes: - meatwheat growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 6 production: 3 @@ -38,7 +38,7 @@ productPrototypes: - MeatwheatBushel growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 6 production: 3 @@ -66,7 +66,7 @@ productPrototypes: - OatBushel growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 6 production: 3 @@ -96,7 +96,7 @@ mutationPrototypes: - mimana growthComponents: - plantTraits: + plant: lifespan: 50 maturation: 6 production: 6 @@ -127,7 +127,7 @@ productPrototypes: - FoodMimana growthComponents: - plantTraits: + plant: lifespan: 50 maturation: 6 production: 6 @@ -158,7 +158,7 @@ productPrototypes: - FoodCarrot growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 10 production: 3 @@ -193,7 +193,7 @@ mutationPrototypes: - worldPea growthComponents: - plantTraits: + plant: lifespan: 25 growthStages: 3 maturation: 7 @@ -233,7 +233,7 @@ - lime - orange growthComponents: - plantTraits: + plant: lifespan: 55 maturation: 6 production: 6 @@ -259,7 +259,7 @@ plantRsi: Objects/Specific/Hydroponics/lemoon.rsi packetPrototype: LemoonSeeds growthComponents: - plantTraits: + plant: lifespan: 90 maturation: 8 production: 6 @@ -292,7 +292,7 @@ - orange - lemon growthComponents: - plantTraits: + plant: lifespan: 55 maturation: 6 production: 6 @@ -324,7 +324,7 @@ - lemon - lime growthComponents: - plantTraits: + plant: lifespan: 55 maturation: 6 production: 6 @@ -352,7 +352,7 @@ productPrototypes: - FoodExtradimensionalOrange growthComponents: - plantTraits: + plant: lifespan: 55 maturation: 6 production: 6 @@ -384,7 +384,7 @@ productPrototypes: - FoodPineapple growthComponents: - plantTraits: + plant: lifespan: 55 maturation: 6 production: 6 @@ -417,7 +417,7 @@ productPrototypes: - FoodPotato growthComponents: - plantTraits: + plant: lifespan: 30 maturation: 10 production: 3 @@ -448,7 +448,7 @@ mutationPrototypes: - papercane growthComponents: - plantTraits: + plant: lifespan: 60 maturation: 6 production: 6 @@ -475,7 +475,7 @@ productPrototypes: - LeavesTea growthComponents: - plantTraits: + plant: lifespan: 75 maturation: 5 production: 3 @@ -504,7 +504,7 @@ productPrototypes: - Papercane growthComponents: - plantTraits: + plant: lifespan: 60 maturation: 6 production: 6 @@ -528,14 +528,15 @@ mutationPrototypes: - steelcap growthComponents: - plantTraits: - ligneous: true + plant: lifespan: 80 maturation: 15 production: 3 yield: 5 potency: 1 growthStages: 3 + plantTraits: + ligneous: true basicGrowth: waterConsumption: 0.6 nutrientConsumption: 0.5 @@ -552,14 +553,15 @@ productPrototypes: - SteelLog growthComponents: - plantTraits: - ligneous: true + plant: lifespan: 80 maturation: 15 production: 3 yield: 3 potency: 1 growthStages: 3 + plantTraits: + ligneous: true basicGrowth: waterConsumption: 0.6 nutrientConsumption: 0.8 @@ -579,7 +581,7 @@ - blueTomato - bloodTomato growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 8 production: 6 @@ -616,7 +618,7 @@ productPrototypes: - FoodBlueTomato growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 8 production: 6 @@ -655,7 +657,7 @@ mutationPrototypes: - killerTomato growthComponents: - plantTraits: + plant: lifespan: 60 maturation: 8 production: 6 @@ -690,7 +692,7 @@ productPrototypes: - MobTomatoKiller growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 15 production: 6 @@ -726,7 +728,7 @@ mutationPrototypes: - eggy growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 6 production: 6 @@ -757,7 +759,7 @@ productPrototypes: - FoodCabbage growthComponents: - plantTraits: + plant: lifespan: 50 maturation: 7 production: 5 @@ -784,7 +786,7 @@ productPrototypes: - FoodGarlic growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 8 production: 5 @@ -817,7 +819,7 @@ mutationPrototypes: - goldenApple growthComponents: - plantTraits: + plant: lifespan: 55 maturation: 6 production: 6 @@ -845,7 +847,7 @@ productPrototypes: - FoodGoldenApple growthComponents: - plantTraits: + plant: lifespan: 55 maturation: 6 production: 6 @@ -879,7 +881,7 @@ productPrototypes: - FoodCorn growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 8 production: 6 @@ -913,7 +915,7 @@ - onionred - bloonion growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 8 production: 6 @@ -948,7 +950,7 @@ productPrototypes: - FoodOnionRed growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 8 production: 6 @@ -983,7 +985,7 @@ productPrototypes: - FoodMushroom growthComponents: - plantTraits: + plant: lifespan: 35 maturation: 10 production: 7 @@ -1011,7 +1013,7 @@ productPrototypes: - FoodEgg growthComponents: - plantTraits: + plant: lifespan: 75 maturation: 6 production: 12 @@ -1041,7 +1043,7 @@ mutationPrototypes: - rainbowCannabis growthComponents: - plantTraits: + plant: lifespan: 75 maturation: 8 production: 12 @@ -1070,7 +1072,7 @@ productPrototypes: - LeavesCannabisRainbow growthComponents: - plantTraits: + plant: lifespan: 75 maturation: 8 production: 12 @@ -1119,7 +1121,7 @@ productPrototypes: - LeavesTobacco growthComponents: - plantTraits: + plant: lifespan: 75 maturation: 5 production: 5 @@ -1150,7 +1152,7 @@ mutationPrototypes: - deathNettle growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 8 production: 6 @@ -1179,7 +1181,7 @@ productPrototypes: - DeathNettle growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 8 production: 6 @@ -1213,7 +1215,7 @@ mutationPrototypes: - chilly growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 6 production: 6 @@ -1247,7 +1249,7 @@ productPrototypes: - FoodChillyPepper growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 6 production: 6 @@ -1283,7 +1285,7 @@ mutationPrototypes: - lily growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 10 production: 3 @@ -1312,7 +1314,7 @@ productPrototypes: - FoodAloe growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 10 production: 3 @@ -1343,7 +1345,7 @@ mutationPrototypes: - spacemansTrumpet growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 10 production: 3 @@ -1372,7 +1374,7 @@ productPrototypes: - FoodLingzhi growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 10 production: 3 @@ -1403,7 +1405,7 @@ mutationPrototypes: - ambrosiaDeus growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 10 production: 3 @@ -1440,7 +1442,7 @@ productPrototypes: - FoodAmbrosiaDeus growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 10 production: 3 @@ -1475,7 +1477,7 @@ mutationPrototypes: - glasstle growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 10 production: 3 @@ -1500,7 +1502,7 @@ productPrototypes: - FoodGlasstle growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 10 production: 3 @@ -1527,7 +1529,7 @@ productPrototypes: - FoodFlyAmanita growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 12 production: 3 @@ -1560,7 +1562,7 @@ - fakeCapfruit - realCapfruit growthComponents: - plantTraits: + plant: lifespan: 65 maturation: 25 production: 25 @@ -1587,7 +1589,7 @@ productPrototypes: - FoodFakeCapfruit growthComponents: - plantTraits: + plant: lifespan: 65 maturation: 25 production: 25 @@ -1614,7 +1616,7 @@ productPrototypes: - FoodRealCapfruit growthComponents: - plantTraits: + plant: lifespan: 65 maturation: 25 production: 25 @@ -1641,7 +1643,7 @@ productPrototypes: - RiceBushel growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 6 production: 3 @@ -1673,7 +1675,7 @@ mutationPrototypes: - koibean growthComponents: - plantTraits: + plant: growthStages: 4 lifespan: 25 maturation: 6 @@ -1698,7 +1700,7 @@ productPrototypes: - FoodSpacemansTrumpet growthComponents: - plantTraits: + plant: growthStages: 4 lifespan: 20 maturation: 14 @@ -1727,7 +1729,7 @@ productPrototypes: - FoodKoibean growthComponents: - plantTraits: + plant: growthStages: 4 lifespan: 25 maturation: 6 @@ -1756,7 +1758,7 @@ productPrototypes: - FoodGrape growthComponents: - plantTraits: + plant: lifespan: 50 maturation: 6 production: 5 @@ -1785,7 +1787,7 @@ mutationPrototypes: - holymelon growthComponents: - plantTraits: + plant: lifespan: 55 maturation: 12 production: 3 @@ -1815,7 +1817,7 @@ productPrototypes: - FoodHolymelon growthComponents: - plantTraits: + plant: lifespan: 55 maturation: 12 production: 3 @@ -1845,7 +1847,7 @@ productPrototypes: - FoodCocoaPod growthComponents: - plantTraits: + plant: lifespan: 50 maturation: 6 production: 6 @@ -1877,7 +1879,7 @@ productPrototypes: - FoodBerries growthComponents: - plantTraits: + plant: lifespan: 50 maturation: 6 production: 6 @@ -1906,7 +1908,7 @@ productPrototypes: - FoodBungo growthComponents: - plantTraits: + plant: lifespan: 50 maturation: 8 production: 6 @@ -1941,7 +1943,7 @@ mutationPrototypes: - laughinPea growthComponents: - plantTraits: + plant: lifespan: 25 growthStages: 3 maturation: 8 @@ -1972,7 +1974,7 @@ productPrototypes: - FoodWorldPeas growthComponents: - plantTraits: + plant: lifespan: 25 growthStages: 3 maturation: 20 @@ -2009,7 +2011,7 @@ mutationPrototypes: - bluePumpkin growthComponents: - plantTraits: + plant: lifespan: 55 maturation: 10 production: 4 @@ -2038,7 +2040,7 @@ productPrototypes: - FoodBluePumpkin growthComponents: - plantTraits: + plant: lifespan: 55 maturation: 10 production: 4 @@ -2073,7 +2075,7 @@ mutationPrototypes: - pyrotton growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 8 production: 3 @@ -2098,7 +2100,7 @@ productPrototypes: - PyrottonBol growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 8 production: 3 @@ -2127,7 +2129,7 @@ productPrototypes: - FoodCherry growthComponents: - plantTraits: + plant: lifespan: 55 maturation: 6 production: 6 @@ -2155,7 +2157,7 @@ productPrototypes: - FoodAnomalyBerry growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 12 production: 3 @@ -2189,7 +2191,7 @@ productPrototypes: - FoodBloonion growthComponents: - plantTraits: + plant: lifespan: 25 maturation: 15 production: 3 From a10823518664b8c5b930ef2fc3e0da91c2c8b233 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Sat, 13 Dec 2025 20:14:48 +0200 Subject: [PATCH 49/53] EnsureUniqueSeed --- .../Botany/Systems/PlantHolderSystem.cs | 16 ---------------- Content.Server/Botany/Systems/PlantSystem.cs | 2 +- .../PlantDestroySeedsEntityEffectSystem.cs | 1 - .../PlantRestoreSeedsEntityEffectSystem.cs | 1 - 4 files changed, 1 insertion(+), 19 deletions(-) diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index e43105c9fa7..e55c493c60c 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -561,10 +561,7 @@ public sealed class PlantHolderSystem : EntitySystem return; if (component.Seed != null) - { - EnsureUniqueSeed(uid, component); _mutation.MutateSeed(uid, ref component.Seed, severity); - } } public void UpdateSprite(EntityUid uid, PlantHolderComponent component) @@ -630,19 +627,6 @@ public sealed class PlantHolderSystem : EntitySystem _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, harvest is { ReadyForHarvest: true }, 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)) diff --git a/Content.Server/Botany/Systems/PlantSystem.cs b/Content.Server/Botany/Systems/PlantSystem.cs index d7cbee77ae9..7b6f5044f7a 100644 --- a/Content.Server/Botany/Systems/PlantSystem.cs +++ b/Content.Server/Botany/Systems/PlantSystem.cs @@ -4,7 +4,7 @@ using Robust.Shared.Random; namespace Content.Server.Botany.Systems; /// -/// Applies plant trait effects on growth ticks. +/// Handles plant behavior and growth processing. /// public sealed class PlantSystem : EntitySystem { diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDestroySeedsEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDestroySeedsEntityEffectSystem.cs index dd1522d8b62..667e31cc684 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDestroySeedsEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantDestroySeedsEntityEffectSystem.cs @@ -23,7 +23,6 @@ public sealed partial class PlantDestroySeedsEntityEffectSystem : EntityEffectSy if (!TryComp(entity, out var traits) || traits.Seedless) return; - _plantHolder.EnsureUniqueSeed(entity, entity.Comp); _popup.PopupEntity( Loc.GetString("botany-plant-seedsdestroyed"), entity, diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRestoreSeedsEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRestoreSeedsEntityEffectSystem.cs index bde6bdb111a..dc1081ca516 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRestoreSeedsEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRestoreSeedsEntityEffectSystem.cs @@ -22,7 +22,6 @@ public sealed partial class PlantRestoreSeedsEntityEffectSystem : EntityEffectSy if (!TryComp(entity, out var traits) || !traits.Seedless) return; - _plantHolder.EnsureUniqueSeed(entity, entity.Comp); _popup.PopupEntity(Loc.GetString("botany-plant-seedsrestored"), entity); traits.Seedless = false; } From 07a66e6eb01bde7bdd543d1dbcf9f452502a5675 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sun, 14 Dec 2025 00:34:20 +0300 Subject: [PATCH 50/53] refactor: change Resolve to TryComp where appropriate --- .../Botany/Systems/AtmosphericGrowthSystem.cs | 5 +---- .../Botany/Systems/BasicGrowthSystem.cs | 13 +++++------ .../Systems/ConsumeExudeGasGrowthSystem.cs | 7 ++---- Content.Server/Botany/Systems/LogSystem.cs | 2 +- .../Botany/Systems/PlantHarvestSystem.cs | 22 +++++++------------ .../Botany/Systems/PlantHolderSystem.cs | 1 - Content.Server/Botany/Systems/PlantSystem.cs | 5 +---- .../Botany/Systems/PlantToxinsSystem.cs | 5 +---- .../Botany/Systems/UnviableGrowthSystem.cs | 5 +---- .../Botany/Systems/WeedPestGrowthSystem.cs | 10 +++------ .../PlantChangeStatEntityEffectSystem.cs | 1 - 11 files changed, 23 insertions(+), 53 deletions(-) diff --git a/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs b/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs index fbecc58e2b9..232f9fbe582 100644 --- a/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs +++ b/Content.Server/Botany/Systems/AtmosphericGrowthSystem.cs @@ -16,8 +16,6 @@ public sealed class AtmosphericGrowthSystem : EntitySystem public override void Initialize() { - base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); } @@ -25,8 +23,7 @@ public sealed class AtmosphericGrowthSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? holder = null; - if (!Resolve(uid, ref holder)) + if (!TryComp(uid, out PlantHolderComponent? holder)) return; var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index a9aae6fb7ab..156e9cfe0f3 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -27,8 +27,6 @@ public sealed class BasicGrowthSystem : EntitySystem public override void Initialize() { - base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); SubscribeLocalEvent(OnSwab); } @@ -65,15 +63,15 @@ public sealed class BasicGrowthSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? holder = null; - if (!Resolve(uid, ref holder)) + if (!TryComp(uid, out PlantHolderComponent? holder) + || !TryComp(uid, out var traits)) return; if (holder.Seed == null || holder.Dead) return; // Check if the plant is viable. - if (TryComp(uid, out var traits) && !traits.Viable) + if (!traits.Viable) { holder.Health -= _random.Next(5, 10) * HydroponicsSpeedMultiplier; if (holder.DrawWarnings) @@ -163,9 +161,8 @@ public sealed class BasicGrowthSystem : EntitySystem if (component.Seed == null) return; - PlantHarvestComponent? harvest = null; - PlantComponent? plant = null; - if (!Resolve(uid, ref harvest, ref plant)) + if (!TryComp(uid, out PlantHarvestComponent? harvest) + || !TryComp(uid, out PlantComponent? plant)) return; if (amount > 0) diff --git a/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs index f975f52e9bb..a98cf831504 100644 --- a/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs +++ b/Content.Server/Botany/Systems/ConsumeExudeGasGrowthSystem.cs @@ -14,8 +14,6 @@ public sealed class ConsumeExudeGasGrowthSystem : EntitySystem public override void Initialize() { - base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); } @@ -23,9 +21,8 @@ public sealed class ConsumeExudeGasGrowthSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? holder = null; - PlantComponent? plant = null; - if (!Resolve(uid, ref holder, ref plant)) + if (!TryComp(uid, out PlantHolderComponent? holder) + || !TryComp(uid, out PlantComponent? plant)) return; var environment = _atmosphere.GetContainingMixture(uid, true, true) ?? GasMixture.SpaceGas; diff --git a/Content.Server/Botany/Systems/LogSystem.cs b/Content.Server/Botany/Systems/LogSystem.cs index 7ca77b78d96..08190af7083 100644 --- a/Content.Server/Botany/Systems/LogSystem.cs +++ b/Content.Server/Botany/Systems/LogSystem.cs @@ -25,7 +25,7 @@ public sealed class LogSystem : EntitySystem if (!HasComp(args.Used)) return; - // if in some container, try pick up, else just drop to world. + // if in some container, try pick up, else just drop to world var inContainer = _containerSystem.IsEntityInContainer(uid); var pos = Transform(uid).Coordinates; diff --git a/Content.Server/Botany/Systems/PlantHarvestSystem.cs b/Content.Server/Botany/Systems/PlantHarvestSystem.cs index 217c03f754a..021af9d9167 100644 --- a/Content.Server/Botany/Systems/PlantHarvestSystem.cs +++ b/Content.Server/Botany/Systems/PlantHarvestSystem.cs @@ -18,8 +18,6 @@ public sealed class HarvestSystem : EntitySystem public override void Initialize() { - base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); SubscribeLocalEvent(OnInteractHand); SubscribeLocalEvent(OnInteractUsing); @@ -29,9 +27,8 @@ public sealed class HarvestSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? holder = null; - PlantComponent? plant = null; - if (!Resolve(uid, ref holder, ref plant)) + if (!TryComp(uid, out PlantHolderComponent? holder) + || !TryComp(uid, out PlantComponent? plant)) return; if (component is { ReadyForHarvest: true, HarvestRepeat: HarvestType.SelfHarvest }) @@ -51,9 +48,8 @@ public sealed class HarvestSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? holder = null; - PlantTraitsComponent? traits = null; - if (!Resolve(uid, ref holder, ref traits)) + if (!TryComp(uid, out PlantHolderComponent? holder) + || !TryComp(uid, out PlantTraitsComponent? traits)) return; if (!component.ReadyForHarvest || holder.Dead || holder.Seed == null || !traits.Ligneous) @@ -74,9 +70,8 @@ public sealed class HarvestSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? holder = null; - PlantTraitsComponent? traits = null; - if (!Resolve(uid, ref holder, ref traits)) + if (!TryComp(uid, out PlantHolderComponent? holder) + || !TryComp(uid, out PlantTraitsComponent? traits)) return; if (!component.ReadyForHarvest || holder.Dead || holder.Seed == null) @@ -97,9 +92,8 @@ public sealed class HarvestSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? holder = null; - PlantTraitsComponent? traits = null; - if (!Resolve(uid, ref holder, ref traits)) + if (!TryComp(uid, out PlantHolderComponent? holder) + || !TryComp(uid, out PlantTraitsComponent? traits)) return; if (holder.Dead) diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index e55c493c60c..7f37611db0b 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -50,7 +50,6 @@ public sealed class PlantHolderSystem : EntitySystem public override void Initialize() { - base.Initialize(); SubscribeLocalEvent(OnExamine); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnSolutionTransferred); diff --git a/Content.Server/Botany/Systems/PlantSystem.cs b/Content.Server/Botany/Systems/PlantSystem.cs index 7b6f5044f7a..23120c4a130 100644 --- a/Content.Server/Botany/Systems/PlantSystem.cs +++ b/Content.Server/Botany/Systems/PlantSystem.cs @@ -12,8 +12,6 @@ public sealed class PlantSystem : EntitySystem public override void Initialize() { - base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); } @@ -21,8 +19,7 @@ public sealed class PlantSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? holder = null; - if (!Resolve(uid, ref holder)) + if (!TryComp(uid, out PlantHolderComponent? holder)) return; // Check if plant is too old. diff --git a/Content.Server/Botany/Systems/PlantToxinsSystem.cs b/Content.Server/Botany/Systems/PlantToxinsSystem.cs index b3505922995..bc49e31d07f 100644 --- a/Content.Server/Botany/Systems/PlantToxinsSystem.cs +++ b/Content.Server/Botany/Systems/PlantToxinsSystem.cs @@ -10,8 +10,6 @@ public sealed class ToxinsSystem : EntitySystem { public override void Initialize() { - base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); } @@ -19,8 +17,7 @@ public sealed class ToxinsSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? holder = null; - if (!Resolve(uid, ref holder)) + if (!TryComp(uid, out PlantHolderComponent? holder )) return; if (holder.Toxins < 0) diff --git a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs index 8f0b9d46207..5b1947b647f 100644 --- a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs +++ b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs @@ -9,8 +9,6 @@ public sealed class UnviableGrowthSystem : EntitySystem { public override void Initialize() { - base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); } @@ -18,8 +16,7 @@ public sealed class UnviableGrowthSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? holder = null; - if (!Resolve(uid, ref holder)) + if (!TryComp(uid, out PlantHolderComponent? holder)) return; holder.Health -= component.UnviableDamage; diff --git a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs index 07ef9350569..015851880b2 100644 --- a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs +++ b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs @@ -14,8 +14,6 @@ public sealed class WeedPestGrowthSystem : EntitySystem public override void Initialize() { - base.Initialize(); - SubscribeLocalEvent(OnPlantGrow); SubscribeLocalEvent(OnTrayUpdate); } @@ -24,8 +22,7 @@ public sealed class WeedPestGrowthSystem : EntitySystem { var (uid, component) = ent; - PlantHolderComponent? holder = null; - if (!Resolve(uid, ref holder)) + if (!TryComp(uid, out PlantHolderComponent? holder)) return; // Weed growth logic. @@ -48,9 +45,8 @@ public sealed class WeedPestGrowthSystem : EntitySystem { var (uid, component) = ent; - PlantTraitsComponent? traits = null; - WeedPestGrowthComponent? weed = null; - if (!Resolve(uid, ref traits, ref weed)) + if (!TryComp(uid, out PlantTraitsComponent? traits) + || !TryComp(uid, out WeedPestGrowthComponent? weed)) return; // Weeds like water and nutrients! They may appear even if there's not a seed planted. diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs index 5d2a66ac196..f98a378be2c 100644 --- a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantChangeStatEntityEffectSystem.cs @@ -1,5 +1,4 @@ using System.Linq; -using System.Reflection; using Content.Server.Botany.Components; using Content.Shared.EntityEffects; using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes; From 7d3e385a86b96492bac9a26bc00a37335c853e46 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sun, 14 Dec 2025 17:39:32 +0300 Subject: [PATCH 51/53] refactor: review comments - try get for components, clenaups --- .../Botany/Components/PlantComponent.cs | 2 +- .../Components/PlantHarvestComponent.cs | 2 +- .../Botany/Components/PlantHolderComponent.cs | 6 -- .../Components/UnviableGrowthComponent.cs | 2 +- Content.Server/Botany/SeedPrototype.cs | 8 ++- .../Botany/Systems/BasicGrowthSystem.cs | 3 + .../Botany/Systems/BotanySwabSystem.cs | 2 +- .../Botany/Systems/BotanySystem.Produce.cs | 6 +- .../Botany/Systems/BotanySystem.Seed.cs | 25 ++++---- .../Botany/Systems/MutationSystem.cs | 13 +++-- .../Botany/Systems/PlantHarvestSystem.cs | 58 ++++++++++--------- .../Botany/Systems/PlantHolderSystem.cs | 35 +++++------ .../Botany/Systems/PlantToxinsSystem.cs | 2 + .../Botany/Systems/SeedExtractorSystem.cs | 3 +- .../Botany/Systems/UnviableGrowthSystem.cs | 4 +- .../Botany/Systems/WeedPestGrowthSystem.cs | 2 +- Resources/Maps/Salvage/vegan-meatball.yml | 20 ------- 17 files changed, 84 insertions(+), 109 deletions(-) diff --git a/Content.Server/Botany/Components/PlantComponent.cs b/Content.Server/Botany/Components/PlantComponent.cs index 350356dca37..066ec206da7 100644 --- a/Content.Server/Botany/Components/PlantComponent.cs +++ b/Content.Server/Botany/Components/PlantComponent.cs @@ -44,7 +44,7 @@ public sealed partial class PlantComponent : Component public int GrowthStages = 6; /// - /// A scalar for sprite size and chemical quantity on the produce. Caps at 100. + /// A scalar for sprite size and chemical solution volume in the produce. Caps at 100. /// [DataField] public float Potency = 1f; diff --git a/Content.Server/Botany/Components/PlantHarvestComponent.cs b/Content.Server/Botany/Components/PlantHarvestComponent.cs index 510f3619693..2a3c918218c 100644 --- a/Content.Server/Botany/Components/PlantHarvestComponent.cs +++ b/Content.Server/Botany/Components/PlantHarvestComponent.cs @@ -20,7 +20,7 @@ public sealed partial class PlantHarvestComponent : Component public bool ReadyForHarvest = false; /// - /// The last time this plant was harvested. + /// The age of the plant when last harvested. /// [ViewVariables] public int LastHarvest = 0; diff --git a/Content.Server/Botany/Components/PlantHolderComponent.cs b/Content.Server/Botany/Components/PlantHolderComponent.cs index be88d2ca929..e72ec57d699 100644 --- a/Content.Server/Botany/Components/PlantHolderComponent.cs +++ b/Content.Server/Botany/Components/PlantHolderComponent.cs @@ -162,12 +162,6 @@ public sealed partial class PlantHolderComponent : Component [DataField] public bool ImproperPressure; - /// - /// Not currently used. - /// - [DataField] - public bool ImproperLight; - /// /// Set to true to force a plant update (visuals, component, etc.) regardless of the current /// update cycle time. Typically used when some interaction affects this plant. diff --git a/Content.Server/Botany/Components/UnviableGrowthComponent.cs b/Content.Server/Botany/Components/UnviableGrowthComponent.cs index 4cf116d1e3b..745d8e1ab8e 100644 --- a/Content.Server/Botany/Components/UnviableGrowthComponent.cs +++ b/Content.Server/Botany/Components/UnviableGrowthComponent.cs @@ -8,7 +8,7 @@ namespace Content.Server.Botany.Components; public sealed partial class UnviableGrowthComponent : Component { /// - /// Amount of damage dealt to the plant per successful tick with unviable. + /// Amount of damage dealt to the plant per growth tick with unviable. /// [DataField] public float UnviableDamage = 6f; diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index c2591db93b6..b1cefe60a11 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -79,7 +79,7 @@ public partial class SeedData public bool Immutable; /// - /// If true, there is only a single reference to this seed and it's properties can be directly modified without + /// If true, there is only a single reference to this seed and its properties can be directly modified without /// needing to clone the seed. /// [ViewVariables] @@ -152,7 +152,11 @@ public partial class SeedData public SeedData Clone() { - DebugTools.Assert(!Immutable, "There should be no need to clone an immutable seed."); + if (Immutable) + { + return this; + } + var serializationManager = IoCManager.Resolve(); var newSeed = new SeedData diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index 156e9cfe0f3..1f6a29d6442 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -156,6 +156,9 @@ public sealed class BasicGrowthSystem : EntitySystem /// public void AffectGrowth(Entity ent, int amount) { + if(amount == 0) + return; + var (uid, component) = ent; if (component.Seed == null) diff --git a/Content.Server/Botany/Systems/BotanySwabSystem.cs b/Content.Server/Botany/Systems/BotanySwabSystem.cs index 3fa55ea574c..d557d111de4 100644 --- a/Content.Server/Botany/Systems/BotanySwabSystem.cs +++ b/Content.Server/Botany/Systems/BotanySwabSystem.cs @@ -75,7 +75,7 @@ public sealed class BotanySwabSystem : EntitySystem if (old == null) return; - // Cross-pollenate the plants. + // Cross-pollinate the plants. plant.Seed = _mutation.Cross(swab.SeedData, old); // Transfer old plant pollen to swab. diff --git a/Content.Server/Botany/Systems/BotanySystem.Produce.cs b/Content.Server/Botany/Systems/BotanySystem.Produce.cs index 022ce8c55e5..c63ebdad378 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Produce.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Produce.cs @@ -11,7 +11,7 @@ public sealed partial class BotanySystem public void ProduceGrown(EntityUid uid, ProduceComponent produce) { - if (!TryGetSeed(produce, out var seed)) + if (!TryGetSeed(produce, out var seed) || !TryGetPlant(seed, out var plant)) return; foreach (var mutation in seed.Mutations) @@ -28,10 +28,6 @@ public sealed partial class BotanySystem solutionContainer.RemoveAllSolution(); - var plant = GetPlantComponent(seed); - if (plant == null) - return; - foreach (var (chem, quantity) in seed.Chemicals) { var amount = quantity.Min; diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index 3544c0d5839..999966b4ac9 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -76,15 +76,17 @@ public sealed partial class BotanySystem : EntitySystem } /// TODO: Delete after plants transition to entities - public static PlantComponent? GetPlantComponent(SeedData seed) + public static bool TryGetPlant(SeedData? seed, [NotNullWhen(true)] out PlantComponent? plantComponent) { - return seed.GrowthComponents.Plant; + plantComponent = seed?.GrowthComponents.Plant; + return plantComponent != null; } /// TODO: Delete after plants transition to entities - public static PlantTraitsComponent? GetPlantTraitsComponent(SeedData seed) + public static bool TryGetPlantTraits(SeedData? seed, [NotNullWhen(true)] out PlantTraitsComponent? traitsComponent) { - return seed.GrowthComponents.PlantTraits; + traitsComponent = seed?.GrowthComponents.PlantTraits; + return traitsComponent != null; } private void OnExamined(EntityUid uid, SeedComponent component, ExaminedEvent args) @@ -92,11 +94,7 @@ public sealed partial class BotanySystem : EntitySystem if (!args.IsInDetailsRange) return; - if (!TryGetSeed(component, out var seed)) - return; - - var plant = GetPlantComponent(seed); - if (plant == null) + if (!TryGetSeed(component, out var seed) || !TryGetPlant(seed, out var plant)) return; using (args.PushGroup(nameof(SeedComponent), 1)) @@ -146,8 +144,7 @@ public sealed partial class BotanySystem : EntitySystem public IEnumerable Harvest(SeedData proto, EntityUid user, EntityUid plantEntity) { - var plant = GetPlantComponent(proto); - if (plant == null || proto.ProductPrototypes.Count == 0 || plant.Yield <= 0) + if (!TryGetPlant(proto, out var plant) || proto.ProductPrototypes.Count == 0 || plant.Yield <= 0) { _popupSystem.PopupCursor(Loc.GetString("botany-harvest-fail-message"), user, PopupType.Medium); return []; @@ -164,8 +161,7 @@ public sealed partial class BotanySystem : EntitySystem public IEnumerable GenerateProduct(SeedData proto, EntityCoordinates position, EntityUid plantEntity) { - var plant = GetPlantComponent(proto); - if (plant == null) + if (!TryGetPlant(proto, out var plant)) return []; var yieldMod = Comp(plantEntity).YieldMod; @@ -218,8 +214,7 @@ public sealed partial class BotanySystem : EntitySystem public bool CanHarvest(SeedData proto, EntityUid? held = null) { - var traits = GetPlantTraitsComponent(proto); - if (traits == null) + if (!TryGetPlantTraits(proto, out var traits)) return true; return !traits.Ligneous || traits.Ligneous && held != null && HasComp(held); diff --git a/Content.Server/Botany/Systems/MutationSystem.cs b/Content.Server/Botany/Systems/MutationSystem.cs index eafe1d4ab97..ac8269b0f9a 100644 --- a/Content.Server/Botany/Systems/MutationSystem.cs +++ b/Content.Server/Botany/Systems/MutationSystem.cs @@ -78,14 +78,16 @@ public sealed class MutationSystem : EntitySystem public SeedData Cross(SeedData a, SeedData b) { + if (b.Immutable) + { + return b; + } + var result = b.Clone(); CrossChemicals(ref result.Chemicals, a.Chemicals); - var sourceTraits = BotanySystem.GetPlantTraitsComponent(a); - var resultTraits = BotanySystem.GetPlantTraitsComponent(result); - - if (sourceTraits != null && resultTraits != null) + if (BotanySystem.TryGetPlantTraits(a, out var sourceTraits) && BotanySystem.TryGetPlantTraits(result, out var resultTraits)) { CrossBool(ref resultTraits.Seedless, sourceTraits.Seedless); CrossBool(ref resultTraits.Ligneous, sourceTraits.Ligneous); @@ -102,8 +104,7 @@ public sealed class MutationSystem : EntitySystem // effective hybrid crossings. if (a.Name != result.Name && Random(0.7f)) { - var traits = BotanySystem.GetPlantTraitsComponent(result); - if (traits != null) + if (BotanySystem.TryGetPlantTraits(result, out var traits)) traits.Seedless = true; } diff --git a/Content.Server/Botany/Systems/PlantHarvestSystem.cs b/Content.Server/Botany/Systems/PlantHarvestSystem.cs index 021af9d9167..d56914e869c 100644 --- a/Content.Server/Botany/Systems/PlantHarvestSystem.cs +++ b/Content.Server/Botany/Systems/PlantHarvestSystem.cs @@ -48,44 +48,46 @@ public sealed class HarvestSystem : EntitySystem { var (uid, component) = ent; - if (!TryComp(uid, out PlantHolderComponent? holder) - || !TryComp(uid, out PlantTraitsComponent? traits)) - return; - - if (!component.ReadyForHarvest || holder.Dead || holder.Seed == null || !traits.Ligneous) - return; - - // Check if sharp tool is required. - if (!_botany.CanHarvest(holder.Seed, args.Used)) - { - _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), args.User); - return; - } - - // Perform harvest. - DoHarvest(ent, args.User); - } - - private void OnInteractHand(Entity ent, ref InteractHandEvent args) - { - var (uid, component) = ent; - - if (!TryComp(uid, out PlantHolderComponent? holder) - || !TryComp(uid, out PlantTraitsComponent? traits)) + if (!TryComp(uid, out PlantTraitsComponent? traits) + || !traits.Ligneous + || !TryComp(uid, out PlantHolderComponent? holder) + || holder.Seed == null) return; if (!component.ReadyForHarvest || holder.Dead || holder.Seed == null) return; - // Check if sharp tool is required. - if (traits.Ligneous) + var canHarvestUsing = _botany.CanHarvest(holder.Seed, args.Used); + HandleInteraction((ent, ent, holder), args.User, !canHarvestUsing); + } + + private void OnInteractHand(Entity ent, ref InteractHandEvent args) + { + if (!TryComp(ent, out PlantHolderComponent? holder) + || !TryComp(ent, out PlantTraitsComponent? traits)) + return; + + HandleInteraction((ent, ent, holder), args.User, traits.Ligneous); + } + + private void HandleInteraction( + Entity ent, + EntityUid user, + bool missingRequiredTool + ) + { + if (missingRequiredTool) { - _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), args.User); + _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), user); return; } + var (_, harvest, holder) = ent; + if (!harvest.ReadyForHarvest || holder.Dead || holder.Seed == null) + return; + // Perform harvest. - DoHarvest(ent, args.User); + DoHarvest(ent, user); } public void DoHarvest(Entity ent, EntityUid user) diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 7f37611db0b..ea00617b6f2 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -174,7 +174,7 @@ public sealed class PlantHolderSystem : EntitySystem { if (plantHolder.Seed == null) { - if (!_botany.TryGetSeed(seeds, out var seed)) + if (!_botany.TryGetSeed(seeds, out var seed) || !BotanySystem.TryGetPlant(seed, out var seedPlant)) return; args.Handled = true; @@ -190,11 +190,7 @@ public sealed class PlantHolderSystem : EntitySystem plantHolder.Dead = false; plantHolder.Age = 1; - var seedPlant = BotanySystem.GetPlantComponent(seed); - if (seeds.HealthOverride != null) - plantHolder.Health = seeds.HealthOverride.Value; - else if (seedPlant != null) - plantHolder.Health = seedPlant.Endurance; + plantHolder.Health = seeds.HealthOverride ?? seedPlant.Endurance; plantHolder.LastCycle = _gameTiming.CurTime; @@ -387,14 +383,10 @@ public sealed class PlantHolderSystem : EntitySystem } } var seed = produce.Seed; - if (seed != null) + if (seed != null && BotanySystem.TryGetPlant(seed, out var seedPlant)) { - var seedPlant = BotanySystem.GetPlantComponent(seed); - if (seedPlant != null) - { - var nutrientBonus = seedPlant.Potency / 2.5f; - AdjustNutrient(uid, nutrientBonus, plantHolder); - } + var nutrientBonus = seedPlant.Potency / 2.5f; + AdjustNutrient(uid, nutrientBonus, plantHolder); } QueueDel(args.Used); } @@ -563,17 +555,19 @@ public sealed class PlantHolderSystem : EntitySystem _mutation.MutateSeed(uid, ref component.Seed, severity); } - public void UpdateSprite(EntityUid uid, PlantHolderComponent component) + public void UpdateSprite(EntityUid uid, PlantHolderComponent? component = null) { PlantHarvestComponent? harvest = null; PlantComponent? plant = null; - Resolve(uid, ref harvest, ref plant, false); + Resolve(uid, ref harvest, ref plant, ref component, false); + + if (!TryComp(uid, out var app) + || component == null) + return; + component.UpdateSpriteAfterUpdate = false; - if (!TryComp(uid, out var app)) - return; - // If no seed, clear visuals regardless of traits. if (component.Seed == null) { @@ -581,7 +575,7 @@ public sealed class PlantHolderSystem : EntitySystem _appearance.SetData(uid, PlantHolderVisuals.HealthLight, false, app); _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, false, app); } - else if (harvest != null && plant != null) + else if(harvest != null && plant != null) { if (component.DrawWarnings) { @@ -616,6 +610,9 @@ public sealed class PlantHolderSystem : EntitySystem if (!component.DrawWarnings) return; + // todo: dehardcode those alert levels. + // Not obvious where they go, as plant holder have alerts, sure, but some plants could have + // very different consumption rates so it would make sense to have different thresholds _appearance.SetData(uid, PlantHolderVisuals.WaterLight, component.WaterLevel <= 15, app); _appearance.SetData(uid, PlantHolderVisuals.NutritionLight, component.NutritionLevel <= 8, app); _appearance.SetData(uid, diff --git a/Content.Server/Botany/Systems/PlantToxinsSystem.cs b/Content.Server/Botany/Systems/PlantToxinsSystem.cs index bc49e31d07f..eaca60077d4 100644 --- a/Content.Server/Botany/Systems/PlantToxinsSystem.cs +++ b/Content.Server/Botany/Systems/PlantToxinsSystem.cs @@ -27,6 +27,8 @@ public sealed class ToxinsSystem : EntitySystem if (holder.Toxins > component.ToxinsTolerance) holder.Health -= toxinUptake; + // there is a possibility that it will remove more toxin than amount of damage it took on plant health (and killed it). + // todo: get min out of health left and toxin uptake - would work better, probably. holder.Toxins -= toxinUptake; if (holder.DrawWarnings) diff --git a/Content.Server/Botany/Systems/SeedExtractorSystem.cs b/Content.Server/Botany/Systems/SeedExtractorSystem.cs index be3e2174285..fa030f50e30 100644 --- a/Content.Server/Botany/Systems/SeedExtractorSystem.cs +++ b/Content.Server/Botany/Systems/SeedExtractorSystem.cs @@ -31,8 +31,7 @@ public sealed class SeedExtractorSystem : EntitySystem if (!_botanySystem.TryGetSeed(produce, out var seed)) return; - var traits = BotanySystem.GetPlantTraitsComponent(seed); - if (traits?.Seedless == true) + if (!BotanySystem.TryGetPlantTraits(seed, out var traits) || traits.Seedless) { _popupSystem.PopupCursor(Loc.GetString("seed-extractor-component-no-seeds", ("name", args.Used)), args.User, PopupType.MediumCaution); diff --git a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs index 5b1947b647f..64ed3f32ad2 100644 --- a/Content.Server/Botany/Systems/UnviableGrowthSystem.cs +++ b/Content.Server/Botany/Systems/UnviableGrowthSystem.cs @@ -16,7 +16,9 @@ public sealed class UnviableGrowthSystem : EntitySystem { var (uid, component) = ent; - if (!TryComp(uid, out PlantHolderComponent? holder)) + if (!TryComp(uid, out PlantHolderComponent? holder) + || !BotanySystem.TryGetPlantTraits(holder.Seed, out var traits) + || traits.Viable) return; holder.Health -= component.UnviableDamage; diff --git a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs index 015851880b2..0f0d8f00be4 100644 --- a/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs +++ b/Content.Server/Botany/Systems/WeedPestGrowthSystem.cs @@ -68,7 +68,7 @@ public sealed class WeedPestGrowthSystem : EntitySystem } // Handle kudzu transformation. - if (component is { Seed: not null, Dead: false } + if (component is { Seed: not null } && traits.TurnIntoKudzu && component.WeedLevel >= weed.WeedHighLevelThreshold) { diff --git a/Resources/Maps/Salvage/vegan-meatball.yml b/Resources/Maps/Salvage/vegan-meatball.yml index e128151ca58..f6b045d26a4 100644 --- a/Resources/Maps/Salvage/vegan-meatball.yml +++ b/Resources/Maps/Salvage/vegan-meatball.yml @@ -488,20 +488,10 @@ entities: growthComponents: plant: lifespan: 75 - endurance: 100 basicGrowth: waterConsumption: 0.4 - nutrientConsumption: 0.75 atmosphericGrowth: idealHeat: 298 - heatTolerance: 10 - lowPressureTolerance: 81 - highPressureTolerance: 121 - weedPestGrowth: - weedTolerance: 5 - pestTolerance: 5 - toxins: - toxinsTolerance: 4 chemicals: THC: Inherent: True @@ -561,20 +551,10 @@ entities: growthComponents: plant: lifespan: 25 - endurance: 100 basicGrowth: waterConsumption: 0.6 - nutrientConsumption: 0.75 atmosphericGrowth: idealHeat: 293 - heatTolerance: 10 - lowPressureTolerance: 81 - highPressureTolerance: 121 - weedPestGrowth: - weedTolerance: 5 - pestTolerance: 5 - toxins: - toxinsTolerance: 4 chemicals: Stellibinin: Inherent: True From 7115917ab7899430dc9fa17a361dd81cb1f3a884 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sun, 14 Dec 2025 21:11:37 +0300 Subject: [PATCH 52/53] refactor: minor yaml cleanups --- Resources/Prototypes/Hydroponics/seeds.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index d3d444b9816..d90ff84e6d4 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -258,6 +258,8 @@ displayName: seeds-lemoon-display-name plantRsi: Objects/Specific/Hydroponics/lemoon.rsi packetPrototype: LemoonSeeds + productPrototypes: + - FoodLemoon growthComponents: plant: lifespan: 90 @@ -267,8 +269,6 @@ potency: 1 harvest: harvestRepeat: Repeat - productPrototypes: - - FoodLemoon chemicals: Vitamin: min: 1 @@ -1651,8 +1651,8 @@ potency: 5 growthStages: 4 basicGrowth: - waterConsumption: 0.6 nutrientConsumption: 0.4 + waterConsumption: 0.6 chemicals: Nutriment: min: 1 From fa285fc72b12d75c33de9b21d50b094cf0ffc1a9 Mon Sep 17 00:00:00 2001 From: Pok245 <113675512+Pok27@users.noreply.github.com> Date: Sun, 14 Dec 2025 20:30:06 +0200 Subject: [PATCH 53/53] fixing trifles --- Content.Server/Botany/SeedPrototype.cs | 2 -- Content.Server/Botany/Systems/BasicGrowthSystem.cs | 2 +- Content.Server/Botany/Systems/MutationSystem.cs | 2 -- Content.Server/Botany/Systems/PlantHolderSystem.cs | 4 ++-- Content.Server/Botany/Systems/PlantToxinsSystem.cs | 2 +- 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Content.Server/Botany/SeedPrototype.cs b/Content.Server/Botany/SeedPrototype.cs index b1cefe60a11..8b58fdec0eb 100644 --- a/Content.Server/Botany/SeedPrototype.cs +++ b/Content.Server/Botany/SeedPrototype.cs @@ -153,9 +153,7 @@ public partial class SeedData public SeedData Clone() { if (Immutable) - { return this; - } var serializationManager = IoCManager.Resolve(); diff --git a/Content.Server/Botany/Systems/BasicGrowthSystem.cs b/Content.Server/Botany/Systems/BasicGrowthSystem.cs index 1f6a29d6442..3ef0db6ce16 100644 --- a/Content.Server/Botany/Systems/BasicGrowthSystem.cs +++ b/Content.Server/Botany/Systems/BasicGrowthSystem.cs @@ -156,7 +156,7 @@ public sealed class BasicGrowthSystem : EntitySystem /// public void AffectGrowth(Entity ent, int amount) { - if(amount == 0) + if (amount == 0) return; var (uid, component) = ent; diff --git a/Content.Server/Botany/Systems/MutationSystem.cs b/Content.Server/Botany/Systems/MutationSystem.cs index ac8269b0f9a..6bda05b0ad7 100644 --- a/Content.Server/Botany/Systems/MutationSystem.cs +++ b/Content.Server/Botany/Systems/MutationSystem.cs @@ -79,9 +79,7 @@ public sealed class MutationSystem : EntitySystem public SeedData Cross(SeedData a, SeedData b) { if (b.Immutable) - { return b; - } var result = b.Clone(); diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index ea00617b6f2..ba9782eef90 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -575,7 +575,7 @@ public sealed class PlantHolderSystem : EntitySystem _appearance.SetData(uid, PlantHolderVisuals.HealthLight, false, app); _appearance.SetData(uid, PlantHolderVisuals.HarvestLight, false, app); } - else if(harvest != null && plant != null) + else if (harvest != null && plant != null) { if (component.DrawWarnings) { @@ -610,7 +610,7 @@ public sealed class PlantHolderSystem : EntitySystem if (!component.DrawWarnings) return; - // todo: dehardcode those alert levels. + // TODO: dehardcode those alert levels. // Not obvious where they go, as plant holder have alerts, sure, but some plants could have // very different consumption rates so it would make sense to have different thresholds _appearance.SetData(uid, PlantHolderVisuals.WaterLight, component.WaterLevel <= 15, app); diff --git a/Content.Server/Botany/Systems/PlantToxinsSystem.cs b/Content.Server/Botany/Systems/PlantToxinsSystem.cs index eaca60077d4..7c03da028fd 100644 --- a/Content.Server/Botany/Systems/PlantToxinsSystem.cs +++ b/Content.Server/Botany/Systems/PlantToxinsSystem.cs @@ -28,7 +28,7 @@ public sealed class ToxinsSystem : EntitySystem holder.Health -= toxinUptake; // there is a possibility that it will remove more toxin than amount of damage it took on plant health (and killed it). - // todo: get min out of health left and toxin uptake - would work better, probably. + // TODO: get min out of health left and toxin uptake - would work better, probably. holder.Toxins -= toxinUptake; if (holder.DrawWarnings)