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:
metalgearsloth
2024-01-04 14:25:32 +11:00
committed by GitHub
parent 98f5f47355
commit bf79acd127
66 changed files with 2257 additions and 1252 deletions

View 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;
}
}

View File

@@ -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);
}
}

View 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();
}
}
}
}

View File

@@ -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;

View File

@@ -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)