Salvage magnet revamp (#23119)
* Generic offering window * More work * weh * Parity * Progression meter * magnet * rona * PG asteroid work * code red * Asteroid spawnings * clams * a * Marker fixes * More fixes * Workings of biome asteroids * A * Fix this loading code * a * Fix masking * weh * Fixes * Magnet claiming * toe * petogue * magnet * Bunch of fixes * Fix default * Fixes * asteroids * Fix offerings * Localisation and a bunch of fixes * a * Fixes * Preliminary draft * Announcement fixes * Fixes and bump spawn rate * Fix asteroid spawns and UI * More fixes * Expeditions fix * fix * Gravity * Fix announcement rounding * a * Offset tweak * sus * jankass * Fix merge
This commit is contained in:
146
Content.Server/Procedural/DungeonJob.NoiseDunGen.cs
Normal file
146
Content.Server/Procedural/DungeonJob.NoiseDunGen.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Procedural;
|
||||
using Content.Shared.Procedural.DungeonGenerators;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Procedural;
|
||||
|
||||
public sealed partial class DungeonJob
|
||||
{
|
||||
private async Task<Dungeon> GenerateNoiseDungeon(NoiseDunGen dungen, EntityUid gridUid, MapGridComponent grid,
|
||||
int seed)
|
||||
{
|
||||
var rand = new Random(seed);
|
||||
var tiles = new List<(Vector2i, Tile)>();
|
||||
|
||||
foreach (var layer in dungen.Layers)
|
||||
{
|
||||
layer.Noise.SetSeed(seed);
|
||||
}
|
||||
|
||||
// First we have to find a seed tile, then floodfill from there until we get to noise
|
||||
// at which point we floodfill the entire noise.
|
||||
var iterations = dungen.Iterations;
|
||||
var area = new Box2i();
|
||||
var frontier = new Queue<Vector2i>();
|
||||
var rooms = new List<DungeonRoom>();
|
||||
var tileCount = 0;
|
||||
var tileCap = rand.NextGaussian(dungen.TileCap, dungen.CapStd);
|
||||
var visited = new HashSet<Vector2i>();
|
||||
|
||||
while (iterations > 0 && tileCount < tileCap)
|
||||
{
|
||||
var roomTiles = new HashSet<Vector2i>();
|
||||
iterations--;
|
||||
|
||||
// Get a random exterior tile to start floodfilling from.
|
||||
var edge = rand.Next(4);
|
||||
Vector2i seedTile;
|
||||
|
||||
switch (edge)
|
||||
{
|
||||
case 0:
|
||||
seedTile = new Vector2i(rand.Next(area.Left - 2, area.Right + 1), area.Bottom - 2);
|
||||
break;
|
||||
case 1:
|
||||
seedTile = new Vector2i(area.Right + 1, rand.Next(area.Bottom - 2, area.Top + 1));
|
||||
break;
|
||||
case 2:
|
||||
seedTile = new Vector2i(rand.Next(area.Left - 2, area.Right + 1), area.Top + 1);
|
||||
break;
|
||||
case 3:
|
||||
seedTile = new Vector2i(area.Left - 2, rand.Next(area.Bottom - 2, area.Top + 1));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
DebugTools.Assert(!visited.Contains(seedTile));
|
||||
var noiseFill = false;
|
||||
frontier.Clear();
|
||||
visited.Add(seedTile);
|
||||
frontier.Enqueue(seedTile);
|
||||
area = area.UnionTile(seedTile);
|
||||
Box2i roomArea = new Box2i(seedTile, seedTile + Vector2i.One);
|
||||
|
||||
// Time to floodfill again
|
||||
while (frontier.TryDequeue(out var node) && tileCount < tileCap)
|
||||
{
|
||||
var foundNoise = false;
|
||||
|
||||
foreach (var layer in dungen.Layers)
|
||||
{
|
||||
var value = layer.Noise.GetNoise(node.X, node.Y);
|
||||
|
||||
if (value < layer.Threshold)
|
||||
continue;
|
||||
|
||||
roomArea = roomArea.UnionTile(node);
|
||||
foundNoise = true;
|
||||
noiseFill = true;
|
||||
var tileDef = _tileDefManager[layer.Tile];
|
||||
var variant = rand.NextByte(tileDef.Variants);
|
||||
|
||||
tiles.Add((node, new Tile(tileDef.TileId, variant: variant)));
|
||||
roomTiles.Add(node);
|
||||
tileCount++;
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't get neighbors if they don't have noise.
|
||||
// only if we've already found any noise.
|
||||
if (noiseFill && !foundNoise)
|
||||
continue;
|
||||
|
||||
for (var x = -1; x <= 1; x++)
|
||||
{
|
||||
for (var y = -1; y <= 1; y++)
|
||||
{
|
||||
// Cardinals only
|
||||
if (x != 0 && y != 0)
|
||||
continue;
|
||||
|
||||
var neighbor = new Vector2i(node.X + x, node.Y + y);
|
||||
|
||||
if (!visited.Add(neighbor))
|
||||
continue;
|
||||
|
||||
area = area.UnionTile(neighbor);
|
||||
frontier.Enqueue(neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
await SuspendIfOutOfTime();
|
||||
ValidateResume();
|
||||
}
|
||||
|
||||
var center = Vector2.Zero;
|
||||
|
||||
foreach (var tile in roomTiles)
|
||||
{
|
||||
center += tile + grid.TileSizeHalfVector;
|
||||
}
|
||||
|
||||
center /= roomTiles.Count;
|
||||
rooms.Add(new DungeonRoom(roomTiles, center, roomArea, new HashSet<Vector2i>()));
|
||||
await SuspendIfOutOfTime();
|
||||
ValidateResume();
|
||||
}
|
||||
|
||||
grid.SetTiles(tiles);
|
||||
|
||||
var dungeon = new Dungeon(rooms);
|
||||
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
dungeon.RoomTiles.Add(tile.Item1);
|
||||
}
|
||||
|
||||
return dungeon;
|
||||
}
|
||||
}
|
||||
@@ -790,7 +790,7 @@ public sealed partial class DungeonJob
|
||||
foreach (var entrance in room.Entrances)
|
||||
{
|
||||
// Just so we can still actually get in to the entrance we won't deter from a tile away from it.
|
||||
var normal = ((Vector2) entrance + grid.TileSizeHalfVector - room.Center).ToWorldAngle().GetCardinalDir().ToIntVec();
|
||||
var normal = (entrance + grid.TileSizeHalfVector - room.Center).ToWorldAngle().GetCardinalDir().ToIntVec();
|
||||
deterredTiles.Remove(entrance + normal);
|
||||
}
|
||||
}
|
||||
|
||||
138
Content.Server/Procedural/DungeonJob.PostGenBiome.cs
Normal file
138
Content.Server/Procedural/DungeonJob.PostGenBiome.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Parallax;
|
||||
using Content.Shared.Parallax.Biomes;
|
||||
using Content.Shared.Parallax.Biomes.Markers;
|
||||
using Content.Shared.Procedural;
|
||||
using Content.Shared.Procedural.PostGeneration;
|
||||
using Content.Shared.Random.Helpers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Procedural;
|
||||
|
||||
public sealed partial class DungeonJob
|
||||
{
|
||||
/*
|
||||
* Handles PostGen code for marker layers + biomes.
|
||||
*/
|
||||
|
||||
private async Task PostGen(BiomePostGen postGen, Dungeon dungeon, EntityUid gridUid, MapGridComponent grid, Random random)
|
||||
{
|
||||
if (_entManager.TryGetComponent(gridUid, out BiomeComponent? biomeComp))
|
||||
return;
|
||||
|
||||
biomeComp = _entManager.AddComponent<BiomeComponent>(gridUid);
|
||||
var biomeSystem = _entManager.System<BiomeSystem>();
|
||||
biomeSystem.SetTemplate(gridUid, biomeComp, _prototype.Index(postGen.BiomeTemplate));
|
||||
var seed = random.Next();
|
||||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var node in dungeon.RoomTiles)
|
||||
{
|
||||
// Need to set per-tile to override data.
|
||||
if (biomeSystem.TryGetTile(node, biomeComp.Layers, seed, grid, out var tile))
|
||||
{
|
||||
_maps.SetTile(gridUid, grid, node, tile.Value);
|
||||
}
|
||||
|
||||
if (biomeSystem.TryGetDecals(node, biomeComp.Layers, seed, grid, out var decals))
|
||||
{
|
||||
foreach (var decal in decals)
|
||||
{
|
||||
_decals.TryAddDecal(decal.ID, new EntityCoordinates(gridUid, decal.Position), out _);
|
||||
}
|
||||
}
|
||||
|
||||
if (biomeSystem.TryGetEntity(node, biomeComp, grid, out var entityProto))
|
||||
{
|
||||
var ent = _entManager.SpawnEntity(entityProto, new EntityCoordinates(gridUid, node + grid.TileSizeHalfVector));
|
||||
var xform = xformQuery.Get(ent);
|
||||
|
||||
if (!xform.Comp.Anchored)
|
||||
{
|
||||
_transform.AnchorEntity(ent, xform);
|
||||
}
|
||||
|
||||
// TODO: Engine bug with SpawnAtPosition
|
||||
DebugTools.Assert(xform.Comp.Anchored);
|
||||
}
|
||||
|
||||
await SuspendIfOutOfTime();
|
||||
ValidateResume();
|
||||
}
|
||||
|
||||
biomeComp.Enabled = false;
|
||||
}
|
||||
|
||||
private async Task PostGen(BiomeMarkerLayerPostGen postGen, Dungeon dungeon, EntityUid gridUid, MapGridComponent grid, Random random)
|
||||
{
|
||||
if (!_entManager.TryGetComponent(gridUid, out BiomeComponent? biomeComp))
|
||||
return;
|
||||
|
||||
var biomeSystem = _entManager.System<BiomeSystem>();
|
||||
var weightedRandom = _prototype.Index(postGen.MarkerTemplate);
|
||||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
||||
var templates = new Dictionary<string, int>();
|
||||
|
||||
for (var i = 0; i < postGen.Count; i++)
|
||||
{
|
||||
var template = weightedRandom.Pick(random);
|
||||
var count = templates.GetOrNew(template);
|
||||
count++;
|
||||
templates[template] = count;
|
||||
}
|
||||
|
||||
foreach (var (template, count) in templates)
|
||||
{
|
||||
var markerTemplate = _prototype.Index<BiomeMarkerLayerPrototype>(template);
|
||||
|
||||
var bounds = new Box2i();
|
||||
|
||||
foreach (var tile in dungeon.RoomTiles)
|
||||
{
|
||||
bounds = bounds.UnionTile(tile);
|
||||
}
|
||||
|
||||
await SuspendIfOutOfTime();
|
||||
ValidateResume();
|
||||
|
||||
biomeSystem.GetMarkerNodes(gridUid, biomeComp, grid, markerTemplate, true, bounds, count,
|
||||
random, out var spawnSet, out var existing, false);
|
||||
|
||||
await SuspendIfOutOfTime();
|
||||
ValidateResume();
|
||||
|
||||
foreach (var ent in existing)
|
||||
{
|
||||
_entManager.DeleteEntity(ent);
|
||||
}
|
||||
|
||||
await SuspendIfOutOfTime();
|
||||
ValidateResume();
|
||||
|
||||
foreach (var (node, mask) in spawnSet)
|
||||
{
|
||||
string? proto;
|
||||
|
||||
if (mask != null && markerTemplate.EntityMask.TryGetValue(mask, out var maskedProto))
|
||||
{
|
||||
proto = maskedProto;
|
||||
}
|
||||
else
|
||||
{
|
||||
proto = markerTemplate.Prototype;
|
||||
}
|
||||
|
||||
var ent = _entManager.SpawnAtPosition(proto, new EntityCoordinates(gridUid, node + grid.TileSizeHalfVector));
|
||||
var xform = xformQuery.Get(ent);
|
||||
|
||||
if (!xform.Comp.Anchored)
|
||||
_transform.AnchorEntity(ent, xform);
|
||||
|
||||
await SuspendIfOutOfTime();
|
||||
ValidateResume();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ public sealed partial class DungeonJob : Job<Dungeon>
|
||||
private readonly DecalSystem _decals;
|
||||
private readonly DungeonSystem _dungeon;
|
||||
private readonly EntityLookupSystem _lookup;
|
||||
private readonly SharedMapSystem _maps;
|
||||
private readonly SharedTransformSystem _transform;
|
||||
private EntityQuery<TagComponent> _tagQuery;
|
||||
|
||||
@@ -68,6 +69,7 @@ public sealed partial class DungeonJob : Job<Dungeon>
|
||||
_decals = decals;
|
||||
_dungeon = dungeon;
|
||||
_lookup = lookup;
|
||||
_maps = _entManager.System<SharedMapSystem>();
|
||||
_transform = transform;
|
||||
_tagQuery = _entManager.GetEntityQuery<TagComponent>();
|
||||
|
||||
@@ -86,15 +88,18 @@ public sealed partial class DungeonJob : Job<Dungeon>
|
||||
|
||||
switch (_gen.Generator)
|
||||
{
|
||||
case NoiseDunGen noise:
|
||||
dungeon = await GenerateNoiseDungeon(noise, _gridUid, _grid, _seed);
|
||||
break;
|
||||
case PrefabDunGen prefab:
|
||||
dungeon = await GeneratePrefabDungeon(prefab, _gridUid, _grid, _seed);
|
||||
DebugTools.Assert(dungeon.RoomExteriorTiles.Count > 0);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
DebugTools.Assert(dungeon.RoomTiles.Count > 0);
|
||||
DebugTools.Assert(dungeon.RoomExteriorTiles.Count > 0);
|
||||
|
||||
// To make it slightly more deterministic treat this RNG as separate ig.
|
||||
var random = new Random(_seed);
|
||||
@@ -108,6 +113,9 @@ public sealed partial class DungeonJob : Job<Dungeon>
|
||||
case AutoCablingPostGen cabling:
|
||||
await PostGen(cabling, dungeon, _gridUid, _grid, random);
|
||||
break;
|
||||
case BiomePostGen biome:
|
||||
await PostGen(biome, dungeon, _gridUid, _grid, random);
|
||||
break;
|
||||
case BoundaryWallPostGen boundary:
|
||||
await PostGen(boundary, dungeon, _gridUid, _grid, random);
|
||||
break;
|
||||
@@ -138,6 +146,9 @@ public sealed partial class DungeonJob : Job<Dungeon>
|
||||
case InternalWindowPostGen internalWindow:
|
||||
await PostGen(internalWindow, dungeon, _gridUid, _grid, random);
|
||||
break;
|
||||
case BiomeMarkerLayerPostGen markerPost:
|
||||
await PostGen(markerPost, dungeon, _gridUid, _grid, random);
|
||||
break;
|
||||
case RoomEntrancePostGen rEntrance:
|
||||
await PostGen(rEntrance, dungeon, _gridUid, _grid, random);
|
||||
break;
|
||||
@@ -154,6 +165,7 @@ public sealed partial class DungeonJob : Job<Dungeon>
|
||||
break;
|
||||
}
|
||||
|
||||
// Defer splitting so they don't get spammed and so we don't have to worry about tracking the grid along the way.
|
||||
_grid.CanSplit = true;
|
||||
_entManager.System<GridFixtureSystem>().CheckSplits(_gridUid);
|
||||
return dungeon;
|
||||
|
||||
@@ -227,7 +227,6 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
|
||||
|
||||
_dungeonJobs.Add(job, cancelToken);
|
||||
_dungeonJobQueue.EnqueueJob(job);
|
||||
job.Run();
|
||||
await job.AsTask;
|
||||
|
||||
if (job.Exception != null)
|
||||
|
||||
Reference in New Issue
Block a user