Ore + entitytable fixes (#37675)
* Ore + entitytable fixes Iterate every dungeon not just last. * Big shot * Fixes
This commit is contained in:
@@ -14,22 +14,31 @@ public sealed partial class DungeonJob
|
|||||||
{
|
{
|
||||||
private async Task PostGen(
|
private async Task PostGen(
|
||||||
EntityTableDunGen gen,
|
EntityTableDunGen gen,
|
||||||
Dungeon dungeon,
|
List<Dungeon> dungeons,
|
||||||
|
HashSet<Vector2i> reservedTiles,
|
||||||
Random random)
|
Random random)
|
||||||
{
|
{
|
||||||
var availableRooms = new ValueList<DungeonRoom>();
|
|
||||||
availableRooms.AddRange(dungeon.Rooms);
|
|
||||||
var availableTiles = new ValueList<Vector2i>(dungeon.AllTiles);
|
|
||||||
|
|
||||||
var count = random.Next(gen.MinCount, gen.MaxCount + 1);
|
var count = random.Next(gen.MinCount, gen.MaxCount + 1);
|
||||||
var npcs = _entManager.System<NPCSystem>();
|
var npcs = _entManager.System<NPCSystem>();
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
foreach (var dungeon in dungeons)
|
||||||
{
|
{
|
||||||
while (availableTiles.Count > 0)
|
var availableRooms = new ValueList<DungeonRoom>();
|
||||||
|
availableRooms.AddRange(dungeon.Rooms);
|
||||||
|
var availableTiles = new ValueList<Vector2i>(dungeon.AllTiles);
|
||||||
|
|
||||||
|
while (availableTiles.Count > 0 && count > 0)
|
||||||
{
|
{
|
||||||
var tile = availableTiles.RemoveSwap(random.Next(availableTiles.Count));
|
var tile = availableTiles.RemoveSwap(random.Next(availableTiles.Count));
|
||||||
|
|
||||||
|
await SuspendDungeon();
|
||||||
|
|
||||||
|
if (!ValidateResume())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (reservedTiles.Contains(tile))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (!_anchorable.TileFree(_grid,
|
if (!_anchorable.TileFree(_grid,
|
||||||
tile,
|
tile,
|
||||||
(int) CollisionGroup.MachineLayer,
|
(int) CollisionGroup.MachineLayer,
|
||||||
@@ -47,13 +56,18 @@ public sealed partial class DungeonJob
|
|||||||
npcs.SleepNPC(uid);
|
npcs.SleepNPC(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
await SuspendDungeon();
|
if (gen.PerDungeon)
|
||||||
|
{
|
||||||
if (!ValidateResume())
|
count = random.Next(gen.MinCount, gen.MaxCount + 1);
|
||||||
|
}
|
||||||
|
// Stop if count is 0, otherwise go to next dungeon.
|
||||||
|
else if (count == 0)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,131 +15,136 @@ public sealed partial class DungeonJob
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task PostGen(
|
private async Task PostGen(
|
||||||
OreDunGen gen,
|
OreDunGen gen,
|
||||||
Dungeon dungeon,
|
List<Dungeon> dungeons,
|
||||||
|
HashSet<Vector2i> reservedTiles,
|
||||||
Random random)
|
Random random)
|
||||||
{
|
{
|
||||||
// Doesn't use dungeon data because layers and we don't need top-down support at the moment.
|
foreach (var dungeon in dungeons)
|
||||||
|
|
||||||
var emptyTiles = false;
|
|
||||||
var replaceEntities = new Dictionary<Vector2i, EntityUid>();
|
|
||||||
var availableTiles = new List<Vector2i>();
|
|
||||||
|
|
||||||
foreach (var node in dungeon.AllTiles)
|
|
||||||
{
|
{
|
||||||
// Empty tile, skip if relevant.
|
var emptyTiles = false;
|
||||||
if (!emptyTiles && (!_maps.TryGetTile(_grid, node, out var tile) || tile.IsEmpty))
|
var replaceEntities = new Dictionary<Vector2i, EntityUid>();
|
||||||
continue;
|
var availableTiles = new List<Vector2i>();
|
||||||
|
|
||||||
// Check if it's a valid spawn, if so then use it.
|
foreach (var node in dungeon.AllTiles)
|
||||||
var enumerator = _maps.GetAnchoredEntitiesEnumerator(_gridUid, _grid, node);
|
|
||||||
var found = false;
|
|
||||||
|
|
||||||
// We use existing entities as a mark to spawn in place
|
|
||||||
// OR
|
|
||||||
// We check for any existing entities to see if we can spawn there.
|
|
||||||
while (enumerator.MoveNext(out var uid))
|
|
||||||
{
|
{
|
||||||
// We can't replace so just stop here.
|
if (reservedTiles.Contains(node))
|
||||||
if (gen.Replacement == null)
|
continue;
|
||||||
break;
|
|
||||||
|
|
||||||
var prototype = _entManager.GetComponent<MetaDataComponent>(uid.Value).EntityPrototype;
|
// Empty tile, skip if relevant.
|
||||||
|
if (!emptyTiles && (!_maps.TryGetTile(_grid, node, out var tile) || tile.IsEmpty))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (prototype?.ID == gen.Replacement)
|
// Check if it's a valid spawn, if so then use it.
|
||||||
|
var enumerator = _maps.GetAnchoredEntitiesEnumerator(_gridUid, _grid, node);
|
||||||
|
var found = false;
|
||||||
|
|
||||||
|
// We use existing entities as a mark to spawn in place
|
||||||
|
// OR
|
||||||
|
// We check for any existing entities to see if we can spawn there.
|
||||||
|
while (enumerator.MoveNext(out var uid))
|
||||||
{
|
{
|
||||||
replaceEntities[node] = uid.Value;
|
// We can't replace so just stop here.
|
||||||
found = true;
|
if (gen.Replacement == null)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
var prototype = _entManager.GetComponent<MetaDataComponent>(uid.Value).EntityPrototype;
|
||||||
|
|
||||||
|
if (prototype?.ID == gen.Replacement)
|
||||||
|
{
|
||||||
|
replaceEntities[node] = uid.Value;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Add it to valid nodes.
|
||||||
|
availableTiles.Add(node);
|
||||||
|
|
||||||
|
await SuspendDungeon();
|
||||||
|
|
||||||
|
if (!ValidateResume())
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found)
|
var remapping = new Dictionary<EntProtoId, EntProtoId>();
|
||||||
continue;
|
|
||||||
|
|
||||||
// Add it to valid nodes.
|
// TODO: Move this to engine
|
||||||
availableTiles.Add(node);
|
if (_prototype.TryIndex(gen.Entity, out var proto) &&
|
||||||
|
proto.Components.TryGetComponent("EntityRemap", out var comps))
|
||||||
await SuspendDungeon();
|
|
||||||
|
|
||||||
if (!ValidateResume())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var remapping = new Dictionary<EntProtoId, EntProtoId>();
|
|
||||||
|
|
||||||
// TODO: Move this to engine
|
|
||||||
if (_prototype.TryIndex(gen.Entity, out var proto) &&
|
|
||||||
proto.Components.TryGetComponent("EntityRemap", out var comps))
|
|
||||||
{
|
|
||||||
var remappingComp = (EntityRemapComponent) comps;
|
|
||||||
remapping = remappingComp.Mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
var frontier = new ValueList<Vector2i>(32);
|
|
||||||
|
|
||||||
// Iterate the group counts and pathfind out each group.
|
|
||||||
for (var i = 0; i < gen.Count; i++)
|
|
||||||
{
|
|
||||||
await SuspendDungeon();
|
|
||||||
|
|
||||||
if (!ValidateResume())
|
|
||||||
return;
|
|
||||||
|
|
||||||
var groupSize = random.Next(gen.MinGroupSize, gen.MaxGroupSize + 1);
|
|
||||||
|
|
||||||
// While we have remaining tiles keep iterating
|
|
||||||
while (groupSize > 0 && availableTiles.Count > 0)
|
|
||||||
{
|
{
|
||||||
var startNode = random.PickAndTake(availableTiles);
|
var remappingComp = (EntityRemapComponent) comps;
|
||||||
frontier.Clear();
|
remapping = remappingComp.Mask;
|
||||||
frontier.Add(startNode);
|
|
||||||
|
|
||||||
// This essentially may lead to a vein being split in multiple areas but the count matters more than position.
|
|
||||||
while (frontier.Count > 0 && groupSize > 0)
|
|
||||||
{
|
|
||||||
// Need to pick a random index so we don't just get straight lines of ores.
|
|
||||||
var frontierIndex = random.Next(frontier.Count);
|
|
||||||
var node = frontier[frontierIndex];
|
|
||||||
frontier.RemoveSwap(frontierIndex);
|
|
||||||
availableTiles.Remove(node);
|
|
||||||
|
|
||||||
// Add neighbors if they're valid, worst case we add no more and pick another random seed tile.
|
|
||||||
for (var x = -1; x <= 1; x++)
|
|
||||||
{
|
|
||||||
for (var y = -1; y <= 1; y++)
|
|
||||||
{
|
|
||||||
var neighbor = new Vector2i(node.X + x, node.Y + y);
|
|
||||||
|
|
||||||
if (frontier.Contains(neighbor) || !availableTiles.Contains(neighbor))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
frontier.Add(neighbor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var prototype = gen.Entity;
|
|
||||||
|
|
||||||
if (replaceEntities.TryGetValue(node, out var existingEnt))
|
|
||||||
{
|
|
||||||
var existingProto = _entManager.GetComponent<MetaDataComponent>(existingEnt).EntityPrototype;
|
|
||||||
_entManager.DeleteEntity(existingEnt);
|
|
||||||
|
|
||||||
if (existingProto != null && remapping.TryGetValue(existingProto.ID, out var remapped))
|
|
||||||
{
|
|
||||||
prototype = remapped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tile valid salad so add it.
|
|
||||||
_entManager.SpawnAtPosition(prototype, _maps.GridTileToLocal(_gridUid, _grid, node));
|
|
||||||
|
|
||||||
groupSize--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupSize > 0)
|
var frontier = new ValueList<Vector2i>(32);
|
||||||
|
|
||||||
|
// Iterate the group counts and pathfind out each group.
|
||||||
|
for (var i = 0; i < gen.Count; i++)
|
||||||
{
|
{
|
||||||
_sawmill.Warning($"Found remaining group size for ore veins of {gen.Replacement ?? "null"}!");
|
await SuspendDungeon();
|
||||||
|
|
||||||
|
if (!ValidateResume())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var groupSize = random.Next(gen.MinGroupSize, gen.MaxGroupSize + 1);
|
||||||
|
|
||||||
|
// While we have remaining tiles keep iterating
|
||||||
|
while (groupSize > 0 && availableTiles.Count > 0)
|
||||||
|
{
|
||||||
|
var startNode = random.PickAndTake(availableTiles);
|
||||||
|
frontier.Clear();
|
||||||
|
frontier.Add(startNode);
|
||||||
|
|
||||||
|
// This essentially may lead to a vein being split in multiple areas but the count matters more than position.
|
||||||
|
while (frontier.Count > 0 && groupSize > 0)
|
||||||
|
{
|
||||||
|
// Need to pick a random index so we don't just get straight lines of ores.
|
||||||
|
var frontierIndex = random.Next(frontier.Count);
|
||||||
|
var node = frontier[frontierIndex];
|
||||||
|
frontier.RemoveSwap(frontierIndex);
|
||||||
|
availableTiles.Remove(node);
|
||||||
|
|
||||||
|
// Add neighbors if they're valid, worst case we add no more and pick another random seed tile.
|
||||||
|
for (var x = -1; x <= 1; x++)
|
||||||
|
{
|
||||||
|
for (var y = -1; y <= 1; y++)
|
||||||
|
{
|
||||||
|
var neighbor = new Vector2i(node.X + x, node.Y + y);
|
||||||
|
|
||||||
|
if (frontier.Contains(neighbor) || !availableTiles.Contains(neighbor))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
frontier.Add(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var prototype = gen.Entity;
|
||||||
|
|
||||||
|
if (replaceEntities.TryGetValue(node, out var existingEnt))
|
||||||
|
{
|
||||||
|
var existingProto = _entManager.GetComponent<MetaDataComponent>(existingEnt).EntityPrototype;
|
||||||
|
_entManager.DeleteEntity(existingEnt);
|
||||||
|
|
||||||
|
if (existingProto != null && remapping.TryGetValue(existingProto.ID, out var remapped))
|
||||||
|
{
|
||||||
|
prototype = remapped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tile valid salad so add it.
|
||||||
|
_entManager.SpawnAtPosition(prototype, _maps.GridTileToLocal(_gridUid, _grid, node));
|
||||||
|
|
||||||
|
groupSize--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupSize > 0)
|
||||||
|
{
|
||||||
|
_sawmill.Warning($"Found remaining group size for ore veins of {gen.Replacement ?? "null"}!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,27 +134,15 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
|
|||||||
|
|
||||||
foreach (var layer in layers)
|
foreach (var layer in layers)
|
||||||
{
|
{
|
||||||
|
var dungCount = dungeons.Count;
|
||||||
await RunLayer(dungeons, position, layer, reservedTiles, seed, random);
|
await RunLayer(dungeons, position, layer, reservedTiles, seed, random);
|
||||||
|
|
||||||
if (config.ReserveTiles)
|
if (config.ReserveTiles)
|
||||||
{
|
{
|
||||||
// Remove any dungeons passed in so we don't interfere with them
|
// Reserve tiles on any new dungeons.
|
||||||
// This is kinda goofy but okay for now.
|
for (var d = dungCount; d < dungeons.Count; d++)
|
||||||
if (existing != null)
|
|
||||||
{
|
|
||||||
for (var j = 0; j < dungeons.Count; j++)
|
|
||||||
{
|
|
||||||
var dung = dungeons[j];
|
|
||||||
|
|
||||||
if (existing.Contains(dung))
|
|
||||||
{
|
|
||||||
dungeons.RemoveSwap(j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var dungeon in dungeons)
|
|
||||||
{
|
{
|
||||||
|
var dungeon = dungeons[d];
|
||||||
reservedTiles.UnionWith(dungeon.AllTiles);
|
reservedTiles.UnionWith(dungeon.AllTiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,6 +190,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
|
|||||||
npcSystem.WakeNPC(npc.Owner, npc.Comp);
|
npcSystem.WakeNPC(npc.Owner, npc.Comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_sawmill.Info($"Finished generating dungeon {_gen} with seed {_seed}");
|
||||||
return dungeons;
|
return dungeons;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +265,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
|
|||||||
await PostGen(mob, dungeons[^1], random);
|
await PostGen(mob, dungeons[^1], random);
|
||||||
break;
|
break;
|
||||||
case EntityTableDunGen entityTable:
|
case EntityTableDunGen entityTable:
|
||||||
await PostGen(entityTable, dungeons[^1], random);
|
await PostGen(entityTable, dungeons, reservedTiles, random);
|
||||||
break;
|
break;
|
||||||
case NoiseDistanceDunGen distance:
|
case NoiseDistanceDunGen distance:
|
||||||
dungeons.Add(await GenerateNoiseDistanceDunGen(position, distance, reservedTiles, seed, random));
|
dungeons.Add(await GenerateNoiseDistanceDunGen(position, distance, reservedTiles, seed, random));
|
||||||
@@ -285,7 +274,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
|
|||||||
dungeons.Add(await GenerateNoiseDunGen(position, noise, reservedTiles, seed, random));
|
dungeons.Add(await GenerateNoiseDunGen(position, noise, reservedTiles, seed, random));
|
||||||
break;
|
break;
|
||||||
case OreDunGen ore:
|
case OreDunGen ore:
|
||||||
await PostGen(ore, dungeons[^1], random);
|
await PostGen(ore, dungeons, reservedTiles, random);
|
||||||
break;
|
break;
|
||||||
case PrefabDunGen prefab:
|
case PrefabDunGen prefab:
|
||||||
dungeons.Add(await GeneratePrefabDunGen(position, prefab, reservedTiles, random));
|
dungeons.Add(await GeneratePrefabDunGen(position, prefab, reservedTiles, random));
|
||||||
|
|||||||
@@ -18,4 +18,10 @@ public sealed partial class EntityTableDunGen : IDunGenLayer
|
|||||||
|
|
||||||
[DataField(required: true)]
|
[DataField(required: true)]
|
||||||
public EntityTableSelector Table;
|
public EntityTableSelector Table;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should the count be per dungeon or across all dungeons.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool PerDungeon;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user