1
0

EntitySpawnCollectionCache.cs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. using System.Linq;
  2. using Content.Shared.Storage;
  3. using Robust.Shared.Random;
  4. namespace Content.Server.Worldgen.Tools;
  5. /// <summary>
  6. /// A faster version of EntitySpawnCollection that requires caching to work.
  7. /// </summary>
  8. public sealed class EntitySpawnCollectionCache
  9. {
  10. [ViewVariables] private readonly Dictionary<string, OrGroup> _orGroups = new();
  11. public EntitySpawnCollectionCache(IEnumerable<EntitySpawnEntry> entries)
  12. {
  13. // collect groups together, create singular items that pass probability
  14. foreach (var entry in entries)
  15. {
  16. if (!_orGroups.TryGetValue(entry.GroupId ?? string.Empty, out var orGroup))
  17. {
  18. orGroup = new OrGroup();
  19. _orGroups.Add(entry.GroupId ?? string.Empty, orGroup);
  20. }
  21. orGroup.Entries.Add(entry);
  22. orGroup.CumulativeProbability += entry.SpawnProbability;
  23. }
  24. }
  25. /// <summary>
  26. /// Using a collection of entity spawn entries, picks a random list of entity prototypes to spawn from that collection.
  27. /// </summary>
  28. /// <remarks>
  29. /// This does not spawn the entities. The caller is responsible for doing so, since it may want to do something
  30. /// special to those entities (offset them, insert them into storage, etc)
  31. /// </remarks>
  32. /// <param name="random">Resolve param.</param>
  33. /// <param name="spawned">List that spawned entities are inserted into.</param>
  34. /// <returns>A list of entity prototypes that should be spawned.</returns>
  35. /// <remarks>This is primarily useful if you're calling it many times over, as it lets you reuse the list repeatedly.</remarks>
  36. public void GetSpawns(IRobustRandom random, ref List<string?> spawned)
  37. {
  38. // handle orgroup spawns
  39. foreach (var spawnValue in _orGroups.Values)
  40. {
  41. //HACK: This doesn't seem to work without this if there's only a single orgroup entry. Not sure how to fix the original math properly, but it works in every other case.
  42. if (spawnValue.Entries.Count == 1)
  43. {
  44. var entry = spawnValue.Entries.First();
  45. var amount = entry.Amount;
  46. if (entry.MaxAmount > amount)
  47. amount = random.Next(amount, entry.MaxAmount);
  48. for (var index = 0; index < amount; index++)
  49. {
  50. spawned.Add(entry.PrototypeId);
  51. }
  52. continue;
  53. }
  54. // For each group use the added cumulative probability to roll a double in that range
  55. var diceRoll = random.NextDouble() * spawnValue.CumulativeProbability;
  56. // Add the entry's spawn probability to this value, if equals or lower, spawn item, otherwise continue to next item.
  57. var cumulative = 0.0;
  58. foreach (var entry in spawnValue.Entries)
  59. {
  60. cumulative += entry.SpawnProbability;
  61. if (diceRoll > cumulative)
  62. continue;
  63. // Dice roll succeeded, add item and break loop
  64. var amount = entry.Amount;
  65. if (entry.MaxAmount > amount)
  66. amount = random.Next(amount, entry.MaxAmount);
  67. for (var index = 0; index < amount; index++)
  68. {
  69. spawned.Add(entry.PrototypeId);
  70. }
  71. break;
  72. }
  73. }
  74. }
  75. private sealed class OrGroup
  76. {
  77. [ViewVariables] public List<EntitySpawnEntry> Entries { get; } = new();
  78. [ViewVariables] public float CumulativeProbability { get; set; }
  79. }
  80. }