Biome marker layer tweaks (#23663)

* Biome marker layer tweaks

- Ensure veins get spawned at great perf cost (it's time-sliced anyway).
- Bump asteroids from 6 nodes to 10 nodes.

* Fixes

* magnet

* Magnet dictates wreck spawn

* Update Content.Shared/Procedural/PostGeneration/BiomeMarkerLayerPostGen.cs

* mraow
This commit is contained in:
metalgearsloth
2024-01-09 22:44:38 +11:00
committed by GitHub
parent 122611cda9
commit d7eb7b700c
6 changed files with 111 additions and 76 deletions

View File

@@ -1,6 +1,5 @@
<controls:FancyWindow xmlns="https://spacestation14.io" <controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'salvage-expedition-window-title'}"
MinSize="800 360"> MinSize="800 360">
<BoxContainer Orientation="Vertical"> <BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal" Name="ProgressionBox" Visible="False"> <BoxContainer Orientation="Horizontal" Name="ProgressionBox" Visible="False">

View File

@@ -31,6 +31,7 @@ public sealed class SalvageExpeditionConsoleBoundUserInterface : BoundUserInterf
{ {
base.Open(); base.Open();
_window = new OfferingWindow(); _window = new OfferingWindow();
_window.Title = Loc.GetString("salvage-expedition-window-title");
_window.OnClose += Close; _window.OnClose += Close;
_window?.OpenCenteredLeft(); _window?.OpenCenteredLeft();
} }

View File

@@ -21,6 +21,7 @@ public sealed class SalvageMagnetBoundUserInterface : BoundUserInterface
{ {
base.Open(); base.Open();
_window = new OfferingWindow(); _window = new OfferingWindow();
_window.Title = Loc.GetString("salvage-magnet-window-title");
_window.OnClose += Close; _window.OnClose += Close;
_window.OpenCenteredLeft(); _window.OpenCenteredLeft();
} }

View File

@@ -1,3 +1,4 @@
using System.Linq;
using System.Numerics; using System.Numerics;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.Atmos; using Content.Server.Atmos;
@@ -553,54 +554,19 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
bool emptyTiles = true) bool emptyTiles = true)
{ {
DebugTools.Assert(count > 0); DebugTools.Assert(count > 0);
var remainingTiles = _tilePool.Get();
var nodeEntities = new Dictionary<Vector2i, EntityUid?>();
var nodeMask = new Dictionary<Vector2i, string?>();
var frontier = new ValueList<Vector2i>(32); // Okay so originally we picked a random tile and BFS outwards
// TODO: Need poisson but crashes whenever I use moony's due to inputs or smth idk // the problem is if you somehow get a cooked frontier then it might drop entire veins
// Get the total amount of groups to spawn across the entire chunk. // hence we'll grab all valid tiles up front and use that as possible seeds.
// We treat a null entity mask as requiring nothing else on the tile // It's hella more expensive but stops issues.
for (var x = bounds.Left; x < bounds.Right; x++)
spawnSet = new Dictionary<Vector2i, string?>();
var visited = _tilePool.Get();
existingEnts = new HashSet<EntityUid>();
// Pick a random tile then BFS outwards from it
// It will bias edge tiles significantly more but will make the CPU cry less.
for (var i = 0; i < count; i++)
{ {
var groupSize = rand.Next(layerProto.MinGroupSize, layerProto.MaxGroupSize + 1); for (var y = bounds.Bottom; y < bounds.Top; y++)
var startNodeX = rand.Next(bounds.Left, bounds.Right);
var startNodeY = rand.Next(bounds.Bottom, bounds.Top);
var startNode = new Vector2i(startNodeX, startNodeY);
frontier.Clear();
frontier.Add(startNode);
visited.Add(startNode);
while (groupSize >= 0 && frontier.Count > 0)
{ {
var frontierIndex = rand.Next(frontier.Count); var node = new Vector2i(x, y);
var node = frontier[frontierIndex];
frontier.RemoveSwap(frontierIndex);
// Add neighbors regardless.
for (var x = -1; x <= 1; x++)
{
for (var y = -1; y <= 1; y++)
{
if (x != 0 && y != 0)
continue;
var neighbor = new Vector2i(node.X + x, node.Y + y);
// Check if it's inbounds.
if (!bounds.Contains(neighbor))
continue;
if (!visited.Add(neighbor))
continue;
frontier.Add(neighbor);
}
}
// Empty tile, skip if relevant. // Empty tile, skip if relevant.
if (!emptyTiles && (!_mapSystem.TryGetTile(grid, node, out var tile) || tile.IsEmpty)) if (!emptyTiles && (!_mapSystem.TryGetTile(grid, node, out var tile) || tile.IsEmpty))
@@ -631,21 +597,77 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
} }
DebugTools.Assert(layerProto.EntityMask.Count == 0 || !string.IsNullOrEmpty(proto)); DebugTools.Assert(layerProto.EntityMask.Count == 0 || !string.IsNullOrEmpty(proto));
remainingTiles.Add(node);
// Don't fight other layers. nodeEntities.Add(node, existing);
if (!spawnSet.TryAdd(node, proto)) nodeMask.Add(node, proto);
continue;
groupSize--;
if (existing != null)
{
existingEnts.Add(existing.Value);
}
} }
} }
_tilePool.Return(visited); var frontier = new ValueList<Vector2i>(32);
// TODO: Need poisson but crashes whenever I use moony's due to inputs or smth idk
// Get the total amount of groups to spawn across the entire chunk.
// We treat a null entity mask as requiring nothing else on the tile
spawnSet = new Dictionary<Vector2i, string?>();
existingEnts = new HashSet<EntityUid>();
// Iterate the group counts and pathfind out each group.
for (var i = 0; i < count; i++)
{
var groupSize = rand.Next(layerProto.MinGroupSize, layerProto.MaxGroupSize + 1);
// While we have remaining tiles keep iterating
while (groupSize >= 0 && remainingTiles.Count > 0)
{
var startNode = rand.PickAndTake(remainingTiles);
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 = rand.Next(frontier.Count);
var node = frontier[frontierIndex];
frontier.RemoveSwap(frontierIndex);
remainingTiles.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++)
{
if (x != 0 && y != 0)
continue;
var neighbor = new Vector2i(node.X + x, node.Y + y);
if (frontier.Contains(neighbor) || !remainingTiles.Contains(neighbor))
continue;
frontier.Add(neighbor);
}
}
// Tile valid salad so add it.
var mask = nodeMask[node];
spawnSet.Add(node, mask);
groupSize--;
if (nodeEntities.TryGetValue(node, out var existing))
{
Del(existing);
}
}
}
if (groupSize > 0)
{
Log.Warning($"Found remaining group size for ore veins!");
}
}
_tilePool.Return(remainingTiles);
} }
/// <summary> /// <summary>

View File

@@ -317,20 +317,29 @@ public sealed partial class SalvageSystem
} }
} }
var magnetGridUid = _xformQuery.GetComponent(magnet.Owner).GridUid; var magnetXform = _xformQuery.GetComponent(magnet.Owner);
Box2 attachedBounds = Box2.Empty; var magnetGridUid = magnetXform.GridUid;
MapId mapId = MapId.Nullspace; var attachedBounds = new Box2Rotated();
var mapId = MapId.Nullspace;
Angle worldAngle;
if (magnetGridUid != null) if (magnetGridUid != null)
{ {
var magnetGridXform = _xformQuery.GetComponent(magnetGridUid.Value); var magnetGridXform = _xformQuery.GetComponent(magnetGridUid.Value);
attachedBounds = _transform.GetWorldMatrix(magnetGridXform) var (gridPos, gridRot) = _transform.GetWorldPositionRotation(magnetGridXform);
.TransformBox(_gridQuery.GetComponent(magnetGridUid.Value).LocalAABB); var gridAABB = _gridQuery.GetComponent(magnetGridUid.Value).LocalAABB;
attachedBounds = new Box2Rotated(gridAABB.Translated(gridPos), gridRot, gridPos);
worldAngle = (gridRot + magnetXform.LocalRotation) - MathF.PI / 2;
mapId = magnetGridXform.MapID; mapId = magnetGridXform.MapID;
} }
else
{
worldAngle = _random.NextAngle();
}
if (!TryGetSalvagePlacementLocation(mapId, attachedBounds, bounds!.Value, out var spawnLocation, out var spawnAngle)) if (!TryGetSalvagePlacementLocation(mapId, attachedBounds, bounds!.Value, worldAngle, out var spawnLocation, out var spawnAngle))
{ {
Report(magnet.Owner, MagnetChannel, "salvage-system-announcement-spawn-no-debris-available"); Report(magnet.Owner, MagnetChannel, "salvage-system-announcement-spawn-no-debris-available");
_mapManager.DeleteMap(salvMap); _mapManager.DeleteMap(salvMap);
@@ -376,26 +385,25 @@ public sealed partial class SalvageSystem
RaiseLocalEvent(ref active); RaiseLocalEvent(ref active);
} }
private bool TryGetSalvagePlacementLocation(MapId mapId, Box2 attachedBounds, Box2 bounds, out MapCoordinates coords, out Angle angle) private bool TryGetSalvagePlacementLocation(MapId mapId, Box2Rotated attachedBounds, Box2 bounds, Angle worldAngle, out MapCoordinates coords, out Angle angle)
{ {
const float OffsetRadiusMin = 4f; // Grid intersection only does AABB atm.
const float OffsetRadiusMax = 16f; var attachedAABB = attachedBounds.CalcBoundingBox();
var minDistance = (attachedBounds.Height < attachedBounds.Width ? attachedBounds.Width : attachedBounds.Height) / 2f; var minDistance = (attachedAABB.Height < attachedAABB.Width ? attachedAABB.Width : attachedAABB.Height) / 2f;
var minActualDistance = bounds.Height < bounds.Width ? minDistance + bounds.Width / 2f : minDistance + bounds.Height / 2f; var minActualDistance = bounds.Height < bounds.Width ? minDistance + bounds.Width / 2f : minDistance + bounds.Height / 2f;
var attachedCenter = attachedBounds.Center; var attachedCenter = attachedAABB.Center;
var fraction = 0.25f;
angle = _random.NextAngle();
// Thanks 20kdc // Thanks 20kdc
for (var i = 0; i < 20; i++) for (var i = 0; i < 20; i++)
{ {
var randomPos = attachedCenter + var randomPos = attachedCenter +
_random.NextAngle().ToVec() * (minActualDistance + worldAngle.ToVec() * (minActualDistance * fraction);
_random.NextFloat(OffsetRadiusMin, OffsetRadiusMax));
var finalCoords = new MapCoordinates(randomPos, mapId); var finalCoords = new MapCoordinates(randomPos, mapId);
angle = _random.NextAngle();
var box2 = Box2.CenteredAround(finalCoords.Position, bounds.Size); var box2 = Box2.CenteredAround(finalCoords.Position, bounds.Size);
var box2Rot = new Box2Rotated(box2, angle, finalCoords.Position); var box2Rot = new Box2Rotated(box2, angle, finalCoords.Position);
@@ -404,7 +412,7 @@ public sealed partial class SalvageSystem
if (_mapManager.FindGridsIntersecting(finalCoords.MapId, box2Rot).Any()) if (_mapManager.FindGridsIntersecting(finalCoords.MapId, box2Rot).Any())
{ {
// Bump it further and further just in case. // Bump it further and further just in case.
minActualDistance += 4f; fraction += 0.25f;
continue; continue;
} }
@@ -412,6 +420,7 @@ public sealed partial class SalvageSystem
return true; return true;
} }
angle = Angle.Zero;
coords = MapCoordinates.Nullspace; coords = MapCoordinates.Nullspace;
return false; return false;
} }

View File

@@ -4,6 +4,7 @@ salvage-system-announcement-spawn-no-debris-available = No debris could be recov
salvage-system-announcement-arrived = A piece of salvagable debris has been pulled in. Estimated hold time: {$timeLeft} seconds. salvage-system-announcement-arrived = A piece of salvagable debris has been pulled in. Estimated hold time: {$timeLeft} seconds.
salvage-asteroid-name = Asteroid salvage-asteroid-name = Asteroid
salvage-magnet-window-title = Salvage magnet
salvage-expedition-window-progression = Progression salvage-expedition-window-progression = Progression
salvage-magnet-resources = {$resource -> salvage-magnet-resources = {$resource ->
@@ -19,8 +20,10 @@ salvage-magnet-resources = {$resource ->
salvage-magnet-resources-count = {$count -> salvage-magnet-resources-count = {$count ->
[1] (Poor) [1] (Poor)
[2] (Rich) [2] (Moderate)
[3] (Rich) [3] (Moderate)
[4] (Rich)
[5] (Rich)
*[other] (Extraordinary) *[other] (Extraordinary)
} }