1
0

MutationSystem.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. using Content.Shared.Atmos;
  2. using Content.Shared.EntityEffects;
  3. using Content.Shared.Random;
  4. using Robust.Shared.Prototypes;
  5. using Robust.Shared.Random;
  6. using System.Linq;
  7. namespace Content.Server.Botany;
  8. public sealed class MutationSystem : EntitySystem
  9. {
  10. [Dependency] private readonly IRobustRandom _robustRandom = default!;
  11. [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
  12. private RandomPlantMutationListPrototype _randomMutations = default!;
  13. public override void Initialize()
  14. {
  15. _randomMutations = _prototypeManager.Index<RandomPlantMutationListPrototype>("RandomPlantMutations");
  16. }
  17. /// <summary>
  18. /// For each random mutation, see if it occurs on this plant this check.
  19. /// </summary>
  20. /// <param name="seed"></param>
  21. /// <param name="severity"></param>
  22. public void CheckRandomMutations(EntityUid plantHolder, ref SeedData seed, float severity)
  23. {
  24. foreach (var mutation in _randomMutations.mutations)
  25. {
  26. if (Random(Math.Min(mutation.BaseOdds * severity, 1.0f)))
  27. {
  28. if (mutation.AppliesToPlant)
  29. {
  30. var args = new EntityEffectBaseArgs(plantHolder, EntityManager);
  31. mutation.Effect.Effect(args);
  32. }
  33. // Stat adjustments do not persist by being an attached effect, they just change the stat.
  34. if (mutation.Persists && !seed.Mutations.Any(m => m.Name == mutation.Name))
  35. seed.Mutations.Add(mutation);
  36. }
  37. }
  38. }
  39. /// <summary>
  40. /// Checks all defined mutations against a seed to see which of them are applied.
  41. /// </summary>
  42. public void MutateSeed(EntityUid plantHolder, ref SeedData seed, float severity)
  43. {
  44. if (!seed.Unique)
  45. {
  46. Log.Error($"Attempted to mutate a shared seed");
  47. return;
  48. }
  49. CheckRandomMutations(plantHolder, ref seed, severity);
  50. }
  51. public SeedData Cross(SeedData a, SeedData b)
  52. {
  53. SeedData result = b.Clone();
  54. CrossChemicals(ref result.Chemicals, a.Chemicals);
  55. CrossFloat(ref result.NutrientConsumption, a.NutrientConsumption);
  56. CrossFloat(ref result.WaterConsumption, a.WaterConsumption);
  57. CrossFloat(ref result.IdealHeat, a.IdealHeat);
  58. CrossFloat(ref result.HeatTolerance, a.HeatTolerance);
  59. CrossFloat(ref result.IdealLight, a.IdealLight);
  60. CrossFloat(ref result.LightTolerance, a.LightTolerance);
  61. CrossFloat(ref result.ToxinsTolerance, a.ToxinsTolerance);
  62. CrossFloat(ref result.LowPressureTolerance, a.LowPressureTolerance);
  63. CrossFloat(ref result.HighPressureTolerance, a.HighPressureTolerance);
  64. CrossFloat(ref result.PestTolerance, a.PestTolerance);
  65. CrossFloat(ref result.WeedTolerance, a.WeedTolerance);
  66. CrossFloat(ref result.Endurance, a.Endurance);
  67. CrossInt(ref result.Yield, a.Yield);
  68. CrossFloat(ref result.Lifespan, a.Lifespan);
  69. CrossFloat(ref result.Maturation, a.Maturation);
  70. CrossFloat(ref result.Production, a.Production);
  71. CrossFloat(ref result.Potency, a.Potency);
  72. CrossBool(ref result.Seedless, a.Seedless);
  73. CrossBool(ref result.Ligneous, a.Ligneous);
  74. CrossBool(ref result.TurnIntoKudzu, a.TurnIntoKudzu);
  75. CrossBool(ref result.CanScream, a.CanScream);
  76. CrossGasses(ref result.ExudeGasses, a.ExudeGasses);
  77. CrossGasses(ref result.ConsumeGasses, a.ConsumeGasses);
  78. // LINQ Explanation
  79. // For the list of mutation effects on both plants, use a 50% chance to pick each one.
  80. // Union all of the chosen mutations into one list, and pick ones with a Distinct (unique) name.
  81. result.Mutations = result.Mutations.Where(m => Random(0.5f)).Union(a.Mutations.Where(m => Random(0.5f))).DistinctBy(m => m.Name).ToList();
  82. // Hybrids have a high chance of being seedless. Balances very
  83. // effective hybrid crossings.
  84. if (a.Name != result.Name && Random(0.7f))
  85. {
  86. result.Seedless = true;
  87. }
  88. return result;
  89. }
  90. private void CrossChemicals(ref Dictionary<string, SeedChemQuantity> val, Dictionary<string, SeedChemQuantity> other)
  91. {
  92. // Go through chemicals from the pollen in swab
  93. foreach (var otherChem in other)
  94. {
  95. // if both have same chemical, randomly pick potency ratio from the two.
  96. if (val.ContainsKey(otherChem.Key))
  97. {
  98. val[otherChem.Key] = Random(0.5f) ? otherChem.Value : val[otherChem.Key];
  99. }
  100. // if target plant doesn't have this chemical, has 50% chance to add it.
  101. else
  102. {
  103. if (Random(0.5f))
  104. {
  105. var fixedChem = otherChem.Value;
  106. fixedChem.Inherent = false;
  107. val.Add(otherChem.Key, fixedChem);
  108. }
  109. }
  110. }
  111. // if the target plant has chemical that the pollen in swab does not, 50% chance to remove it.
  112. foreach (var thisChem in val)
  113. {
  114. if (!other.ContainsKey(thisChem.Key))
  115. {
  116. if (Random(0.5f))
  117. {
  118. if (val.Count > 1)
  119. {
  120. val.Remove(thisChem.Key);
  121. }
  122. }
  123. }
  124. }
  125. }
  126. private void CrossGasses(ref Dictionary<Gas, float> val, Dictionary<Gas, float> other)
  127. {
  128. // Go through gasses from the pollen in swab
  129. foreach (var otherGas in other)
  130. {
  131. // if both have same gas, randomly pick ammount from the two.
  132. if (val.ContainsKey(otherGas.Key))
  133. {
  134. val[otherGas.Key] = Random(0.5f) ? otherGas.Value : val[otherGas.Key];
  135. }
  136. // if target plant doesn't have this gas, has 50% chance to add it.
  137. else
  138. {
  139. if (Random(0.5f))
  140. {
  141. val.Add(otherGas.Key, otherGas.Value);
  142. }
  143. }
  144. }
  145. // if the target plant has gas that the pollen in swab does not, 50% chance to remove it.
  146. foreach (var thisGas in val)
  147. {
  148. if (!other.ContainsKey(thisGas.Key))
  149. {
  150. if (Random(0.5f))
  151. {
  152. val.Remove(thisGas.Key);
  153. }
  154. }
  155. }
  156. }
  157. private void CrossFloat(ref float val, float other)
  158. {
  159. val = Random(0.5f) ? val : other;
  160. }
  161. private void CrossInt(ref int val, int other)
  162. {
  163. val = Random(0.5f) ? val : other;
  164. }
  165. private void CrossBool(ref bool val, bool other)
  166. {
  167. val = Random(0.5f) ? val : other;
  168. }
  169. private bool Random(float p)
  170. {
  171. return _robustRandom.Prob(p);
  172. }
  173. }