Files
space-station-14/Content.Server/Botany/SeedPrototype.cs
2025-12-14 20:30:06 +02:00

237 lines
8.0 KiB
C#

using Content.Server.Botany.Components;
using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Random;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Utility;
namespace Content.Server.Botany;
[Prototype]
public sealed partial class SeedPrototype : SeedData, IPrototype
{
[IdDataField] public string ID { get; private set; } = default!;
}
[DataDefinition]
public partial struct SeedChemQuantity
{
/// <summary>
/// Minimum amount of chemical that is added to produce, regardless of the potency
/// </summary>
[DataField] public FixedPoint2 Min = FixedPoint2.Epsilon;
/// <summary>
/// Maximum amount of chemical that can be produced after taking plant potency into account.
/// </summary>
[DataField] public FixedPoint2 Max;
/// <summary>
/// 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.
/// </summary>
[DataField] public float PotencyDivisor;
/// <summary>
/// Inherent chemical is one that is NOT result of mutation or crossbreeding. These chemicals are removed if species mutation is executed.
/// </summary>
[DataField] public bool Inherent = true;
}
// 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.
/// <remarks>
/// 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.
/// </remarks>
// TODO: Hit Botany with hammers
[Virtual, DataDefinition]
public partial class SeedData
{
#region Tracking
/// <summary>
/// The name of this seed. Determines the name of seed packets.
/// </summary>
[DataField]
public string Name { get; private set; } = "";
/// <summary>
/// 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.
/// </summary>
[DataField]
public string Noun { get; private set; } = "";
/// <summary>
/// Name displayed when examining the hydroponics tray. Describes the actual plant, not the seed itself.
/// </summary>
[DataField]
public string DisplayName { get; private set; } = "";
[DataField] public bool Mysterious;
/// <summary>
/// If true, the properties of this seed cannot be modified.
/// </summary>
[DataField]
public bool Immutable;
/// <summary>
/// If true, there is only a single reference to this seed and its properties can be directly modified without
/// needing to clone the seed.
/// </summary>
[ViewVariables]
public bool Unique = false; // seed-prototypes or yaml-defined seeds for entity prototypes will not generally be unique.
#endregion
#region Output
/// <summary>
/// The entity prototype that is spawned when this type of seed is extracted from produce using a seed extractor.
/// </summary>
[DataField]
public EntProtoId PacketPrototype = "SeedBase";
/// <summary>
/// The entity prototypes that are spawned when this type of seed is harvested.
/// </summary>
[DataField]
public List<EntProtoId> ProductPrototypes = [];
[DataField]
public Dictionary<string, SeedChemQuantity> Chemicals = [];
#endregion
#region Cosmetics
[DataField(required: true)]
public ResPath PlantRsi { get; set; } = default!;
[DataField] public string PlantIconState { get; set; } = "produce";
/// <summary>
/// Screams random sound from collection SoundCollectionSpecifier
/// </summary>
[DataField]
public SoundSpecifier ScreamSound = new SoundCollectionSpecifier("PlantScreams", AudioParams.Default.WithVolume(-10));
#endregion
/// <summary>
/// The mutation effects that have been applied to this plant.
/// </summary>
[DataField]
public List<RandomPlantMutation> Mutations { get; set; } = [];
/// <summary>
/// The seed prototypes this seed may mutate into when prompted to.
/// </summary>
[DataField]
public List<ProtoId<SeedPrototype>> MutationPrototypes = [];
/// <summary>
/// The growth components used by this seed.
/// TODO: Delete after plants transition to entities
/// </summary>
[DataField]
public GrowthComponentsHolder GrowthComponents = new();
/// <summary>
/// Log impact for harvest operations.
/// </summary>
[DataField]
public LogImpact? HarvestLogImpact;
/// <summary>
/// Log impact for plant operations.
/// </summary>
[DataField]
public LogImpact? PlantLogImpact;
public SeedData Clone()
{
if (Immutable)
return this;
var serializationManager = IoCManager.Resolve<ISerializationManager>();
var newSeed = new SeedData
{
GrowthComponents = serializationManager.CreateCopy(GrowthComponents, notNullableOverride: true),
HarvestLogImpact = HarvestLogImpact,
PlantLogImpact = PlantLogImpact,
Name = Name,
Noun = Noun,
DisplayName = DisplayName,
Mysterious = Mysterious,
PacketPrototype = PacketPrototype,
ProductPrototypes = new List<EntProtoId>(ProductPrototypes),
MutationPrototypes = new List<ProtoId<SeedPrototype>>(MutationPrototypes),
Chemicals = new Dictionary<string, SeedChemQuantity>(Chemicals),
PlantRsi = PlantRsi,
PlantIconState = PlantIconState,
Mutations = new List<RandomPlantMutation>(Mutations),
// Newly cloned seed is unique. No need to unnecessarily clone if repeatedly modified.
Unique = true,
};
newSeed.Mutations.AddRange(Mutations);
return newSeed;
}
/// <summary>
/// Handles copying most species defining data from 'other' to this seed while keeping the accumulated mutations intact.
/// </summary>
public SeedData SpeciesChange(SeedData other)
{
var serializationManager = IoCManager.Resolve<ISerializationManager>();
var newSeed = new SeedData
{
GrowthComponents = serializationManager.CreateCopy(other.GrowthComponents, notNullableOverride: true),
HarvestLogImpact = other.HarvestLogImpact,
PlantLogImpact = other.PlantLogImpact,
Name = other.Name,
Noun = other.Noun,
DisplayName = other.DisplayName,
Mysterious = other.Mysterious,
PacketPrototype = other.PacketPrototype,
ProductPrototypes = new List<EntProtoId>(other.ProductPrototypes),
MutationPrototypes = new List<ProtoId<SeedPrototype>>(other.MutationPrototypes),
Chemicals = new Dictionary<string, SeedChemQuantity>(Chemicals),
Mutations = Mutations,
PlantRsi = other.PlantRsi,
PlantIconState = other.PlantIconState,
// 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;
}
}