using Content.Server.Botany.Components; using Content.Server.Botany.Systems; using Content.Server.EntityEffects; using Content.Shared.Atmos; using Content.Shared.Database; using Content.Shared.Random; using Robust.Shared.Audio; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Utility; namespace Content.Server.Botany; [Prototype] public sealed partial class SeedPrototype : SeedData, IPrototype { [IdDataField] public string ID { get; private set; } = default!; } public enum HarvestType : byte { NoRepeat, Repeat, 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 { /// /// Minimum amount of chemical that is added to produce, regardless of the potency /// [DataField("Min")] public int Min; /// /// Maximum amount of chemical that can be produced after taking plant potency into account. /// [DataField("Max")] public int 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 int PotencyDivisor; /// /// Inherent chemical is one that is NOT result of mutation or crossbreeding. These chemicals are removed if species mutation is executed. /// [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))] public partial class SeedData { #region Tracking /// /// The name of this seed. Determines the name of seed packets. /// [DataField("name")] 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")] public string Noun { get; private set; } = ""; /// /// Name displayed when examining the hydroponics tray. Describes the actual plant, not the seed itself. /// [DataField("displayName")] public string DisplayName { get; private set; } = ""; [DataField("mysterious")] public bool Mysterious; /// /// If true, the properties of this seed cannot be modified. /// [DataField("immutable")] 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. /// [ViewVariables] public bool Unique = false; // seed-prototypes or yaml-defined seeds for entity prototypes will not generally be unique. #endregion #region Output /// /// The entity prototype that is spawned when this type of seed is extracted from produce using a seed extractor. /// [DataField("packetPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] public string PacketPrototype = "SeedBase"; /// /// The entity prototype this seed spawns when it gets harvested. /// [DataField] 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 [DataField] public float Endurance = 100f; [DataField] public int Yield; [DataField] public float Lifespan; [DataField] public float Maturation; [DataField] public float Production; [DataField] public int GrowthStages = 6; [DataField] public HarvestType HarvestRepeat = HarvestType.NoRepeat; [DataField] public float Potency = 1f; /// /// If true, cannot be harvested for seeds. Balances hybrids and /// mutations. /// [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 [DataField(required: true)] public ResPath PlantRsi { get; set; } = default!; [DataField] public string PlantIconState { get; set; } = "produce"; /// /// Screams random sound from collection SoundCollectionSpecifier /// [DataField] public SoundSpecifier ScreamSound = new SoundCollectionSpecifier("PlantScreams", AudioParams.Default.WithVolume(-10)); [DataField("screaming")] public bool CanScream; [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string KudzuPrototype = "WeakKudzu"; [DataField] public bool TurnIntoKudzu; [DataField] public string? SplatPrototype { get; set; } #endregion /// /// The mutation effects that have been applied to this plant. /// [DataField] public List Mutations { get; set; } = new(); /// /// The seed prototypes this seed may mutate into when prompted to. /// [DataField] public List> MutationPrototypes = new(); /// /// Log impact for when the seed is planted. /// [DataField] public LogImpact? PlantLogImpact = null; /// /// Log impact for when the seed is harvested. /// [DataField] public LogImpact? HarvestLogImpact = null; public SeedData Clone() { DebugTools.Assert(!Immutable, "There should be no need to clone an immutable seed."); var newSeed = new SeedData { Name = Name, Noun = Noun, DisplayName = DisplayName, Mysterious = Mysterious, PacketPrototype = PacketPrototype, 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, Lifespan = Lifespan, Maturation = Maturation, Production = Production, GrowthStages = GrowthStages, HarvestRepeat = HarvestRepeat, Potency = Potency, Seedless = Seedless, Viable = Viable, Ligneous = Ligneous, PlantRsi = PlantRsi, PlantIconState = PlantIconState, CanScream = CanScream, TurnIntoKudzu = TurnIntoKudzu, SplatPrototype = SplatPrototype, Mutations = new List(), // Newly cloned seed is unique. No need to unnecessarily clone if repeatedly modified. Unique = true, }; newSeed.Mutations.AddRange(Mutations); return newSeed; } /// /// Handles copying most species defining data from 'other' to this seed while keeping the accumulated mutations intact. /// public SeedData SpeciesChange(SeedData other) { var newSeed = new SeedData { 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), 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, Lifespan = Lifespan, Maturation = Maturation, Production = Production, GrowthStages = other.GrowthStages, HarvestRepeat = HarvestRepeat, Potency = Potency, Mutations = Mutations, Seedless = Seedless, Viable = Viable, Ligneous = Ligneous, PlantRsi = other.PlantRsi, 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, }; // Adding the new chemicals from the new species. foreach (var otherChem in other.Chemicals) { newSeed.Chemicals.TryAdd(otherChem.Key, otherChem.Value); } // Removing the inherent chemicals from the old species. Leaving mutated/crossbread ones intact. foreach (var originalChem in newSeed.Chemicals) { if (!other.Chemicals.ContainsKey(originalChem.Key) && originalChem.Value.Inherent) { newSeed.Chemicals.Remove(originalChem.Key); } } return newSeed; } }