SeedPrototype.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. using Content.Server.Botany.Components;
  2. using Content.Server.Botany.Systems;
  3. using Content.Shared.Atmos;
  4. using Content.Shared.EntityEffects;
  5. using Content.Shared.Random;
  6. using Robust.Shared.Audio;
  7. using Robust.Shared.Prototypes;
  8. using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
  9. using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
  10. using Robust.Shared.Utility;
  11. namespace Content.Server.Botany;
  12. [Prototype]
  13. public sealed partial class SeedPrototype : SeedData, IPrototype
  14. {
  15. [IdDataField] public string ID { get; private set; } = default!;
  16. }
  17. public enum HarvestType : byte
  18. {
  19. NoRepeat,
  20. Repeat,
  21. SelfHarvest
  22. }
  23. /*
  24. public enum PlantSpread : byte
  25. {
  26. NoSpread,
  27. Creepers,
  28. Vines,
  29. }
  30. public enum PlantMutation : byte
  31. {
  32. NoMutation,
  33. Mutable,
  34. HighlyMutable,
  35. }
  36. public enum PlantCarnivorous : byte
  37. {
  38. NotCarnivorous,
  39. EatPests,
  40. EatLivingBeings,
  41. }
  42. public enum PlantJuicy : byte
  43. {
  44. NotJuicy,
  45. Juicy,
  46. Slippery,
  47. }
  48. */
  49. [DataDefinition]
  50. public partial struct SeedChemQuantity
  51. {
  52. /// <summary>
  53. /// Minimum amount of chemical that is added to produce, regardless of the potency
  54. /// </summary>
  55. [DataField("Min")] public int Min;
  56. /// <summary>
  57. /// Maximum amount of chemical that can be produced after taking plant potency into account.
  58. /// </summary>
  59. [DataField("Max")] public int Max;
  60. /// <summary>
  61. /// 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.
  62. /// 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.
  63. /// </summary>
  64. [DataField("PotencyDivisor")] public int PotencyDivisor;
  65. /// <summary>
  66. /// Inherent chemical is one that is NOT result of mutation or crossbreeding. These chemicals are removed if species mutation is executed.
  67. /// </summary>
  68. [DataField("Inherent")] public bool Inherent = true;
  69. }
  70. // TODO reduce the number of friends to a reasonable level. Requires ECS-ing things like plant holder component.
  71. [Virtual, DataDefinition]
  72. [Access(typeof(BotanySystem), typeof(PlantHolderSystem), typeof(SeedExtractorSystem), typeof(PlantHolderComponent), typeof(EntityEffect), typeof(MutationSystem))]
  73. public partial class SeedData
  74. {
  75. #region Tracking
  76. /// <summary>
  77. /// The name of this seed. Determines the name of seed packets.
  78. /// </summary>
  79. [DataField("name")]
  80. public string Name { get; private set; } = "";
  81. /// <summary>
  82. /// The noun for this type of seeds. E.g. for fungi this should probably be "spores" instead of "seeds". Also
  83. /// used to determine the name of seed packets.
  84. /// </summary>
  85. [DataField("noun")]
  86. public string Noun { get; private set; } = "";
  87. /// <summary>
  88. /// Name displayed when examining the hydroponics tray. Describes the actual plant, not the seed itself.
  89. /// </summary>
  90. [DataField("displayName")]
  91. public string DisplayName { get; private set; } = "";
  92. [DataField("mysterious")] public bool Mysterious;
  93. /// <summary>
  94. /// If true, the properties of this seed cannot be modified.
  95. /// </summary>
  96. [DataField("immutable")] public bool Immutable;
  97. /// <summary>
  98. /// If true, there is only a single reference to this seed and it's properties can be directly modified without
  99. /// needing to clone the seed.
  100. /// </summary>
  101. [ViewVariables]
  102. public bool Unique = false; // seed-prototypes or yaml-defined seeds for entity prototypes will not generally be unique.
  103. #endregion
  104. #region Output
  105. /// <summary>
  106. /// The entity prototype that is spawned when this type of seed is extracted from produce using a seed extractor.
  107. /// </summary>
  108. [DataField("packetPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
  109. public string PacketPrototype = "SeedBase";
  110. /// <summary>
  111. /// The entity prototype this seed spawns when it gets harvested.
  112. /// </summary>
  113. [DataField("productPrototypes", customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>))]
  114. public List<string> ProductPrototypes = new();
  115. [DataField] public Dictionary<string, SeedChemQuantity> Chemicals = new();
  116. [DataField] public Dictionary<Gas, float> ConsumeGasses = new();
  117. [DataField] public Dictionary<Gas, float> ExudeGasses = new();
  118. #endregion
  119. #region Tolerances
  120. [DataField] public float NutrientConsumption = 0.75f;
  121. [DataField] public float WaterConsumption = 0.5f;
  122. [DataField] public float IdealHeat = 293f;
  123. [DataField] public float HeatTolerance = 10f;
  124. [DataField] public float IdealLight = 7f;
  125. [DataField] public float LightTolerance = 3f;
  126. [DataField] public float ToxinsTolerance = 4f;
  127. [DataField] public float LowPressureTolerance = 81f;
  128. [DataField] public float HighPressureTolerance = 121f;
  129. [DataField] public float PestTolerance = 5f;
  130. [DataField] public float WeedTolerance = 5f;
  131. [DataField] public float WeedHighLevelThreshold = 10f;
  132. #endregion
  133. #region General traits
  134. [DataField] public float Endurance = 100f;
  135. [DataField] public int Yield;
  136. [DataField] public float Lifespan;
  137. [DataField] public float Maturation;
  138. [DataField] public float Production;
  139. [DataField] public int GrowthStages = 6;
  140. [DataField] public HarvestType HarvestRepeat = HarvestType.NoRepeat;
  141. [DataField] public float Potency = 1f;
  142. /// <summary>
  143. /// If true, cannot be harvested for seeds. Balances hybrids and
  144. /// mutations.
  145. /// </summary>
  146. [DataField] public bool Seedless = false;
  147. /// <summary>
  148. /// If false, rapidly decrease health while growing. Used to kill off
  149. /// plants with "bad" mutations.
  150. /// </summary>
  151. [DataField] public bool Viable = true;
  152. /// <summary>
  153. /// If true, a sharp tool is required to harvest this plant.
  154. /// </summary>
  155. [DataField] public bool Ligneous;
  156. // No, I'm not removing these.
  157. // if you re-add these, make sure that they get cloned.
  158. //public PlantSpread Spread { get; set; }
  159. //public PlantMutation Mutation { get; set; }
  160. //public float AlterTemperature { get; set; }
  161. //public PlantCarnivorous Carnivorous { get; set; }
  162. //public bool Parasite { get; set; }
  163. //public bool Hematophage { get; set; }
  164. //public bool Thorny { get; set; }
  165. //public bool Stinging { get; set; }
  166. // public bool Teleporting { get; set; }
  167. // public PlantJuicy Juicy { get; set; }
  168. #endregion
  169. #region Cosmetics
  170. [DataField(required: true)]
  171. public ResPath PlantRsi { get; set; } = default!;
  172. [DataField] public string PlantIconState { get; set; } = "produce";
  173. /// <summary>
  174. /// Screams random sound from collection SoundCollectionSpecifier
  175. /// </summary>
  176. [DataField]
  177. public SoundSpecifier ScreamSound = new SoundCollectionSpecifier("PlantScreams", AudioParams.Default.WithVolume(-10));
  178. [DataField("screaming")] public bool CanScream;
  179. [DataField(customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))] public string KudzuPrototype = "WeakKudzu";
  180. [DataField] public bool TurnIntoKudzu;
  181. [DataField] public string? SplatPrototype { get; set; }
  182. #endregion
  183. /// <summary>
  184. /// The mutation effects that have been applied to this plant.
  185. /// </summary>
  186. [DataField] public List<RandomPlantMutation> Mutations { get; set; } = new();
  187. /// <summary>
  188. /// The seed prototypes this seed may mutate into when prompted to.
  189. /// </summary>
  190. [DataField(customTypeSerializer: typeof(PrototypeIdListSerializer<SeedPrototype>))]
  191. public List<string> MutationPrototypes = new();
  192. public SeedData Clone()
  193. {
  194. DebugTools.Assert(!Immutable, "There should be no need to clone an immutable seed.");
  195. var newSeed = new SeedData
  196. {
  197. Name = Name,
  198. Noun = Noun,
  199. DisplayName = DisplayName,
  200. Mysterious = Mysterious,
  201. PacketPrototype = PacketPrototype,
  202. ProductPrototypes = new List<string>(ProductPrototypes),
  203. MutationPrototypes = new List<string>(MutationPrototypes),
  204. Chemicals = new Dictionary<string, SeedChemQuantity>(Chemicals),
  205. ConsumeGasses = new Dictionary<Gas, float>(ConsumeGasses),
  206. ExudeGasses = new Dictionary<Gas, float>(ExudeGasses),
  207. NutrientConsumption = NutrientConsumption,
  208. WaterConsumption = WaterConsumption,
  209. IdealHeat = IdealHeat,
  210. HeatTolerance = HeatTolerance,
  211. IdealLight = IdealLight,
  212. LightTolerance = LightTolerance,
  213. ToxinsTolerance = ToxinsTolerance,
  214. LowPressureTolerance = LowPressureTolerance,
  215. HighPressureTolerance = HighPressureTolerance,
  216. PestTolerance = PestTolerance,
  217. WeedTolerance = WeedTolerance,
  218. Endurance = Endurance,
  219. Yield = Yield,
  220. Lifespan = Lifespan,
  221. Maturation = Maturation,
  222. Production = Production,
  223. GrowthStages = GrowthStages,
  224. HarvestRepeat = HarvestRepeat,
  225. Potency = Potency,
  226. Seedless = Seedless,
  227. Viable = Viable,
  228. Ligneous = Ligneous,
  229. PlantRsi = PlantRsi,
  230. PlantIconState = PlantIconState,
  231. CanScream = CanScream,
  232. TurnIntoKudzu = TurnIntoKudzu,
  233. SplatPrototype = SplatPrototype,
  234. Mutations = new List<RandomPlantMutation>(),
  235. // Newly cloned seed is unique. No need to unnecessarily clone if repeatedly modified.
  236. Unique = true,
  237. };
  238. newSeed.Mutations.AddRange(Mutations);
  239. return newSeed;
  240. }
  241. /// <summary>
  242. /// Handles copying most species defining data from 'other' to this seed while keeping the accumulated mutations intact.
  243. /// </summary>
  244. public SeedData SpeciesChange(SeedData other)
  245. {
  246. var newSeed = new SeedData
  247. {
  248. Name = other.Name,
  249. Noun = other.Noun,
  250. DisplayName = other.DisplayName,
  251. Mysterious = other.Mysterious,
  252. PacketPrototype = other.PacketPrototype,
  253. ProductPrototypes = new List<string>(other.ProductPrototypes),
  254. MutationPrototypes = new List<string>(other.MutationPrototypes),
  255. Chemicals = new Dictionary<string, SeedChemQuantity>(Chemicals),
  256. ConsumeGasses = new Dictionary<Gas, float>(ConsumeGasses),
  257. ExudeGasses = new Dictionary<Gas, float>(ExudeGasses),
  258. NutrientConsumption = NutrientConsumption,
  259. WaterConsumption = WaterConsumption,
  260. IdealHeat = IdealHeat,
  261. HeatTolerance = HeatTolerance,
  262. IdealLight = IdealLight,
  263. LightTolerance = LightTolerance,
  264. ToxinsTolerance = ToxinsTolerance,
  265. LowPressureTolerance = LowPressureTolerance,
  266. HighPressureTolerance = HighPressureTolerance,
  267. PestTolerance = PestTolerance,
  268. WeedTolerance = WeedTolerance,
  269. Endurance = Endurance,
  270. Yield = Yield,
  271. Lifespan = Lifespan,
  272. Maturation = Maturation,
  273. Production = Production,
  274. GrowthStages = other.GrowthStages,
  275. HarvestRepeat = HarvestRepeat,
  276. Potency = Potency,
  277. Mutations = Mutations,
  278. Seedless = Seedless,
  279. Viable = Viable,
  280. Ligneous = Ligneous,
  281. PlantRsi = other.PlantRsi,
  282. PlantIconState = other.PlantIconState,
  283. CanScream = CanScream,
  284. TurnIntoKudzu = TurnIntoKudzu,
  285. SplatPrototype = other.SplatPrototype,
  286. // Newly cloned seed is unique. No need to unnecessarily clone if repeatedly modified.
  287. Unique = true,
  288. };
  289. // Adding the new chemicals from the new species.
  290. foreach (var otherChem in other.Chemicals)
  291. {
  292. newSeed.Chemicals.TryAdd(otherChem.Key, otherChem.Value);
  293. }
  294. // Removing the inherent chemicals from the old species. Leaving mutated/crossbread ones intact.
  295. foreach (var originalChem in newSeed.Chemicals)
  296. {
  297. if (!other.Chemicals.ContainsKey(originalChem.Key) && originalChem.Value.Inherent)
  298. {
  299. newSeed.Chemicals.Remove(originalChem.Key);
  300. }
  301. }
  302. return newSeed;
  303. }
  304. }