IonStormSystem.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. using Content.Server.StationEvents.Components;
  2. using Content.Shared.Administration.Logs;
  3. using Content.Shared.Database;
  4. using Content.Shared.Dataset;
  5. using Content.Shared.FixedPoint;
  6. using Content.Shared.GameTicking.Components;
  7. using Content.Shared.Random;
  8. using Content.Shared.Random.Helpers;
  9. using Content.Shared.Silicons.Laws;
  10. using Content.Shared.Silicons.Laws.Components;
  11. using Robust.Shared.Prototypes;
  12. using Robust.Shared.Random;
  13. using System.Linq;
  14. namespace Content.Server.Silicons.Laws;
  15. public sealed class IonStormSystem : EntitySystem
  16. {
  17. [Dependency] private readonly IPrototypeManager _proto = default!;
  18. [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
  19. [Dependency] private readonly SiliconLawSystem _siliconLaw = default!;
  20. [Dependency] private readonly IRobustRandom _robustRandom = default!;
  21. // funny
  22. [ValidatePrototypeId<DatasetPrototype>]
  23. private const string Threats = "IonStormThreats";
  24. [ValidatePrototypeId<DatasetPrototype>]
  25. private const string Objects = "IonStormObjects";
  26. [ValidatePrototypeId<DatasetPrototype>]
  27. private const string Crew = "IonStormCrew";
  28. [ValidatePrototypeId<DatasetPrototype>]
  29. private const string Adjectives = "IonStormAdjectives";
  30. [ValidatePrototypeId<DatasetPrototype>]
  31. private const string Verbs = "IonStormVerbs";
  32. [ValidatePrototypeId<DatasetPrototype>]
  33. private const string NumberBase = "IonStormNumberBase";
  34. [ValidatePrototypeId<DatasetPrototype>]
  35. private const string NumberMod = "IonStormNumberMod";
  36. [ValidatePrototypeId<DatasetPrototype>]
  37. private const string Areas = "IonStormAreas";
  38. [ValidatePrototypeId<DatasetPrototype>]
  39. private const string Feelings = "IonStormFeelings";
  40. [ValidatePrototypeId<DatasetPrototype>]
  41. private const string FeelingsPlural = "IonStormFeelingsPlural";
  42. [ValidatePrototypeId<DatasetPrototype>]
  43. private const string Musts = "IonStormMusts";
  44. [ValidatePrototypeId<DatasetPrototype>]
  45. private const string Requires = "IonStormRequires";
  46. [ValidatePrototypeId<DatasetPrototype>]
  47. private const string Actions = "IonStormActions";
  48. [ValidatePrototypeId<DatasetPrototype>]
  49. private const string Allergies = "IonStormAllergies";
  50. [ValidatePrototypeId<DatasetPrototype>]
  51. private const string AllergySeverities = "IonStormAllergySeverities";
  52. [ValidatePrototypeId<DatasetPrototype>]
  53. private const string Concepts = "IonStormConcepts";
  54. [ValidatePrototypeId<DatasetPrototype>]
  55. private const string Drinks = "IonStormDrinks";
  56. [ValidatePrototypeId<DatasetPrototype>]
  57. private const string Foods = "IonStormFoods";
  58. /// <summary>
  59. /// Randomly alters the laws of an individual silicon.
  60. /// </summary>
  61. public void IonStormTarget(Entity<SiliconLawBoundComponent, IonStormTargetComponent> ent, bool adminlog = true)
  62. {
  63. var lawBound = ent.Comp1;
  64. var target = ent.Comp2;
  65. if (!_robustRandom.Prob(target.Chance))
  66. return;
  67. var laws = _siliconLaw.GetLaws(ent, lawBound);
  68. if (laws.Laws.Count == 0)
  69. return;
  70. // try to swap it out with a random lawset
  71. if (_robustRandom.Prob(target.RandomLawsetChance))
  72. {
  73. var lawsets = _proto.Index<WeightedRandomPrototype>(target.RandomLawsets);
  74. var lawset = lawsets.Pick(_robustRandom);
  75. laws = _siliconLaw.GetLawset(lawset);
  76. }
  77. // clone it so not modifying stations lawset
  78. laws = laws.Clone();
  79. // shuffle them all
  80. if (_robustRandom.Prob(target.ShuffleChance))
  81. {
  82. // hopefully work with existing glitched laws if there are multiple ion storms
  83. var baseOrder = FixedPoint2.New(1);
  84. foreach (var law in laws.Laws)
  85. {
  86. if (law.Order < baseOrder)
  87. baseOrder = law.Order;
  88. }
  89. _robustRandom.Shuffle(laws.Laws);
  90. // change order based on shuffled position
  91. for (int i = 0; i < laws.Laws.Count; i++)
  92. {
  93. laws.Laws[i].Order = baseOrder + i;
  94. }
  95. }
  96. // see if we can remove a random law
  97. if (laws.Laws.Count > 0 && _robustRandom.Prob(target.RemoveChance))
  98. {
  99. var i = _robustRandom.Next(laws.Laws.Count);
  100. laws.Laws.RemoveAt(i);
  101. }
  102. // generate a new law...
  103. var newLaw = GenerateLaw();
  104. // see if the law we add will replace a random existing law or be a new glitched order one
  105. if (laws.Laws.Count > 0 && _robustRandom.Prob(target.ReplaceChance))
  106. {
  107. var i = _robustRandom.Next(laws.Laws.Count);
  108. laws.Laws[i] = new SiliconLaw()
  109. {
  110. LawString = newLaw,
  111. Order = laws.Laws[i].Order
  112. };
  113. }
  114. else
  115. {
  116. laws.Laws.Insert(0, new SiliconLaw
  117. {
  118. LawString = newLaw,
  119. Order = -1,
  120. LawIdentifierOverride = Loc.GetString("ion-storm-law-scrambled-number", ("length", _robustRandom.Next(5, 10)))
  121. });
  122. }
  123. // sets all unobfuscated laws' indentifier in order from highest to lowest priority
  124. // This could technically override the Obfuscation from the code above, but it seems unlikely enough to basically never happen
  125. int orderDeduction = -1;
  126. for (int i = 0; i < laws.Laws.Count; i++)
  127. {
  128. var notNullIdentifier = laws.Laws[i].LawIdentifierOverride ?? (i - orderDeduction).ToString();
  129. if (notNullIdentifier.Any(char.IsSymbol))
  130. {
  131. orderDeduction += 1;
  132. }
  133. else
  134. {
  135. laws.Laws[i].LawIdentifierOverride = (i - orderDeduction).ToString();
  136. }
  137. }
  138. // adminlog is used to prevent adminlog spam.
  139. if (adminlog)
  140. _adminLogger.Add(LogType.Mind, LogImpact.High, $"{ToPrettyString(ent):silicon} had its laws changed by an ion storm to {laws.LoggingString()}");
  141. // laws unique to this silicon, dont use station laws anymore
  142. EnsureComp<SiliconLawProviderComponent>(ent);
  143. var ev = new IonStormLawsEvent(laws);
  144. RaiseLocalEvent(ent, ref ev);
  145. }
  146. // for your own sake direct your eyes elsewhere
  147. private string GenerateLaw()
  148. {
  149. // pick all values ahead of time to make the logic cleaner
  150. var threats = Pick(Threats);
  151. var objects = Pick(Objects);
  152. var crew1 = Pick(Crew);
  153. var crew2 = Pick(Crew);
  154. var adjective = Pick(Adjectives);
  155. var verb = Pick(Verbs);
  156. var number = Pick(NumberBase) + " " + Pick(NumberMod);
  157. var area = Pick(Areas);
  158. var feeling = Pick(Feelings);
  159. var feelingPlural = Pick(FeelingsPlural);
  160. var must = Pick(Musts);
  161. var require = Pick(Requires);
  162. var action = Pick(Actions);
  163. var allergy = Pick(Allergies);
  164. var allergySeverity = Pick(AllergySeverities);
  165. var concept = Pick(Concepts);
  166. var drink = Pick(Drinks);
  167. var food = Pick(Foods);
  168. var joined = $"{number} {adjective}";
  169. // a lot of things have subjects of a threat/crew/object
  170. var triple = _robustRandom.Next(0, 3) switch
  171. {
  172. 0 => threats,
  173. 1 => crew1,
  174. 2 => objects,
  175. _ => throw new IndexOutOfRangeException(),
  176. };
  177. var crewAll = _robustRandom.Prob(0.5f) ? crew2 : Loc.GetString("ion-storm-crew");
  178. var objectsThreats = _robustRandom.Prob(0.5f) ? objects : threats;
  179. var objectsConcept = _robustRandom.Prob(0.5f) ? objects : concept;
  180. // s goes ahead of require, is/are
  181. // i dont think theres a way to do this in fluent
  182. var (who, plural) = _robustRandom.Next(0, 5) switch
  183. {
  184. 0 => (Loc.GetString("ion-storm-you"), true),
  185. 1 => (Loc.GetString("ion-storm-the-station"), false),
  186. 2 => (Loc.GetString("ion-storm-the-crew"), false),
  187. 3 => (Loc.GetString("ion-storm-the-job", ("job", crew2)), true),
  188. _ => (area, false) // THE SINGULARITY REQUIRES THE HAPPY CLOWNS
  189. };
  190. var jobChange = _robustRandom.Next(0, 3) switch
  191. {
  192. 0 => crew1,
  193. 1 => Loc.GetString("ion-storm-clowns"),
  194. _ => Loc.GetString("ion-storm-heads")
  195. };
  196. var part = Loc.GetString("ion-storm-part", ("part", _robustRandom.Prob(0.5f)));
  197. var harm = _robustRandom.Next(0, 6) switch
  198. {
  199. 0 => concept,
  200. 1 => $"{adjective} {threats}",
  201. 2 => $"{adjective} {objects}",
  202. 3 => Loc.GetString("ion-storm-adjective-things", ("adjective", adjective)),
  203. 4 => crew1,
  204. _ => Loc.GetString("ion-storm-x-and-y", ("x", crew1), ("y", crew2))
  205. };
  206. if (plural) feeling = feelingPlural;
  207. var subjects = _robustRandom.Prob(0.5f) ? objectsThreats : Loc.GetString("ion-storm-people");
  208. // message logic!!!
  209. return _robustRandom.Next(0, 35) switch
  210. {
  211. 0 => Loc.GetString("ion-storm-law-on-station", ("joined", joined), ("subjects", triple)),
  212. 1 => Loc.GetString("ion-storm-law-call-shuttle", ("joined", joined), ("subjects", triple)),
  213. 2 => Loc.GetString("ion-storm-law-crew-are", ("who", crewAll), ("joined", joined), ("subjects", objectsThreats)),
  214. 3 => Loc.GetString("ion-storm-law-subjects-harmful", ("adjective", adjective), ("subjects", triple)),
  215. 4 => Loc.GetString("ion-storm-law-must-harmful", ("must", must)),
  216. 5 => Loc.GetString("ion-storm-law-thing-harmful", ("thing", _robustRandom.Prob(0.5f) ? concept : action)),
  217. 6 => Loc.GetString("ion-storm-law-job-harmful", ("adjective", adjective), ("job", crew1)),
  218. 7 => Loc.GetString("ion-storm-law-having-harmful", ("adjective", adjective), ("thing", objectsConcept)),
  219. 8 => Loc.GetString("ion-storm-law-not-having-harmful", ("adjective", adjective), ("thing", objectsConcept)),
  220. 9 => Loc.GetString("ion-storm-law-requires", ("who", who), ("plural", plural), ("thing", _robustRandom.Prob(0.5f) ? concept : require)),
  221. 10 => Loc.GetString("ion-storm-law-requires-subjects", ("who", who), ("plural", plural), ("joined", joined), ("subjects", triple)),
  222. 11 => Loc.GetString("ion-storm-law-allergic", ("who", who), ("plural", plural), ("severity", allergySeverity), ("allergy", _robustRandom.Prob(0.5f) ? concept : allergy)),
  223. 12 => Loc.GetString("ion-storm-law-allergic-subjects", ("who", who), ("plural", plural), ("severity", allergySeverity), ("adjective", adjective), ("subjects", _robustRandom.Prob(0.5f) ? objects : crew1)),
  224. 13 => Loc.GetString("ion-storm-law-feeling", ("who", who), ("feeling", feeling), ("concept", concept)),
  225. 14 => Loc.GetString("ion-storm-law-feeling-subjects", ("who", who), ("feeling", feeling), ("joined", joined), ("subjects", triple)),
  226. 15 => Loc.GetString("ion-storm-law-you-are", ("concept", concept)),
  227. 16 => Loc.GetString("ion-storm-law-you-are-subjects", ("joined", joined), ("subjects", triple)),
  228. 17 => Loc.GetString("ion-storm-law-you-must-always", ("must", must)),
  229. 18 => Loc.GetString("ion-storm-law-you-must-never", ("must", must)),
  230. 19 => Loc.GetString("ion-storm-law-eat", ("who", crewAll), ("adjective", adjective), ("food", _robustRandom.Prob(0.5f) ? food : triple)),
  231. 20 => Loc.GetString("ion-storm-law-drink", ("who", crewAll), ("adjective", adjective), ("drink", drink)),
  232. 21 => Loc.GetString("ion-storm-law-change-job", ("who", crewAll), ("adjective", adjective), ("change", jobChange)),
  233. 22 => Loc.GetString("ion-storm-law-highest-rank", ("who", crew1)),
  234. 23 => Loc.GetString("ion-storm-law-lowest-rank", ("who", crew1)),
  235. 24 => Loc.GetString("ion-storm-law-crew-must", ("who", crewAll), ("must", must)),
  236. 25 => Loc.GetString("ion-storm-law-crew-must-go", ("who", crewAll), ("area", area)),
  237. 26 => Loc.GetString("ion-storm-law-crew-only-1", ("who", crew1), ("part", part)),
  238. 27 => Loc.GetString("ion-storm-law-crew-only-2", ("who", crew1), ("other", crew2), ("part", part)),
  239. 28 => Loc.GetString("ion-storm-law-crew-only-subjects", ("adjective", adjective), ("subjects", subjects), ("part", part)),
  240. 29 => Loc.GetString("ion-storm-law-crew-must-do", ("must", must), ("part", part)),
  241. 30 => Loc.GetString("ion-storm-law-crew-must-have", ("adjective", adjective), ("objects", objects), ("part", part)),
  242. 31 => Loc.GetString("ion-storm-law-crew-must-eat", ("who", who), ("adjective", adjective), ("food", food), ("part", part)),
  243. 32 => Loc.GetString("ion-storm-law-harm", ("who", harm)),
  244. 33 => Loc.GetString("ion-storm-law-protect", ("who", harm)),
  245. _ => Loc.GetString("ion-storm-law-concept-verb", ("concept", concept), ("verb", verb), ("subjects", triple))
  246. };
  247. }
  248. /// <summary>
  249. /// Picks a random value from an ion storm dataset.
  250. /// All ion storm datasets start with IonStorm.
  251. /// </summary>
  252. private string Pick(string name)
  253. {
  254. var dataset = _proto.Index<DatasetPrototype>(name);
  255. return _robustRandom.Pick(dataset.Values);
  256. }
  257. }