1
0

DungeonJob.OreDunGen.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. using System.Threading.Tasks;
  2. using Content.Shared.Procedural;
  3. using Content.Shared.Procedural.Components;
  4. using Content.Shared.Procedural.DungeonLayers;
  5. using Robust.Shared.Collections;
  6. using Robust.Shared.Prototypes;
  7. using Robust.Shared.Random;
  8. namespace Content.Server.Procedural.DungeonJob;
  9. public sealed partial class DungeonJob
  10. {
  11. /// <summary>
  12. /// <see cref="OreDunGen"/>
  13. /// </summary>
  14. private async Task PostGen(
  15. OreDunGen gen,
  16. Dungeon dungeon,
  17. Random random)
  18. {
  19. // Doesn't use dungeon data because layers and we don't need top-down support at the moment.
  20. var emptyTiles = false;
  21. var replaceEntities = new Dictionary<Vector2i, EntityUid>();
  22. var availableTiles = new List<Vector2i>();
  23. foreach (var node in dungeon.AllTiles)
  24. {
  25. // Empty tile, skip if relevant.
  26. if (!emptyTiles && (!_maps.TryGetTile(_grid, node, out var tile) || tile.IsEmpty))
  27. continue;
  28. // Check if it's a valid spawn, if so then use it.
  29. var enumerator = _maps.GetAnchoredEntitiesEnumerator(_gridUid, _grid, node);
  30. var found = false;
  31. // We use existing entities as a mark to spawn in place
  32. // OR
  33. // We check for any existing entities to see if we can spawn there.
  34. while (enumerator.MoveNext(out var uid))
  35. {
  36. // We can't replace so just stop here.
  37. if (gen.Replacement == null)
  38. break;
  39. var prototype = _entManager.GetComponent<MetaDataComponent>(uid.Value).EntityPrototype;
  40. if (prototype?.ID == gen.Replacement)
  41. {
  42. replaceEntities[node] = uid.Value;
  43. found = true;
  44. break;
  45. }
  46. }
  47. if (!found)
  48. continue;
  49. // Add it to valid nodes.
  50. availableTiles.Add(node);
  51. await SuspendDungeon();
  52. if (!ValidateResume())
  53. return;
  54. }
  55. var remapping = new Dictionary<EntProtoId, EntProtoId>();
  56. // TODO: Move this to engine
  57. if (_prototype.TryIndex(gen.Entity, out var proto) &&
  58. proto.Components.TryGetComponent("EntityRemap", out var comps))
  59. {
  60. var remappingComp = (EntityRemapComponent) comps;
  61. remapping = remappingComp.Mask;
  62. }
  63. var frontier = new ValueList<Vector2i>(32);
  64. // Iterate the group counts and pathfind out each group.
  65. for (var i = 0; i < gen.Count; i++)
  66. {
  67. await SuspendDungeon();
  68. if (!ValidateResume())
  69. return;
  70. var groupSize = random.Next(gen.MinGroupSize, gen.MaxGroupSize + 1);
  71. // While we have remaining tiles keep iterating
  72. while (groupSize > 0 && availableTiles.Count > 0)
  73. {
  74. var startNode = random.PickAndTake(availableTiles);
  75. frontier.Clear();
  76. frontier.Add(startNode);
  77. // This essentially may lead to a vein being split in multiple areas but the count matters more than position.
  78. while (frontier.Count > 0 && groupSize > 0)
  79. {
  80. // Need to pick a random index so we don't just get straight lines of ores.
  81. var frontierIndex = random.Next(frontier.Count);
  82. var node = frontier[frontierIndex];
  83. frontier.RemoveSwap(frontierIndex);
  84. availableTiles.Remove(node);
  85. // Add neighbors if they're valid, worst case we add no more and pick another random seed tile.
  86. for (var x = -1; x <= 1; x++)
  87. {
  88. for (var y = -1; y <= 1; y++)
  89. {
  90. var neighbor = new Vector2i(node.X + x, node.Y + y);
  91. if (frontier.Contains(neighbor) || !availableTiles.Contains(neighbor))
  92. continue;
  93. frontier.Add(neighbor);
  94. }
  95. }
  96. var prototype = gen.Entity;
  97. if (replaceEntities.TryGetValue(node, out var existingEnt))
  98. {
  99. var existingProto = _entManager.GetComponent<MetaDataComponent>(existingEnt).EntityPrototype;
  100. _entManager.DeleteEntity(existingEnt);
  101. if (existingProto != null && remapping.TryGetValue(existingProto.ID, out var remapped))
  102. {
  103. prototype = remapped;
  104. }
  105. }
  106. // Tile valid salad so add it.
  107. _entManager.SpawnAtPosition(prototype, _maps.GridTileToLocal(_gridUid, _grid, node));
  108. groupSize--;
  109. }
  110. }
  111. if (groupSize > 0)
  112. {
  113. _sawmill.Warning($"Found remaining group size for ore veins of {gen.Replacement ?? "null"}!");
  114. }
  115. }
  116. }
  117. }