Fix dungeon spawn + ftl overlap (#31413)

* Fix dungeon spawn + ftl overlap

* Better fixes
This commit is contained in:
metalgearsloth
2024-08-25 14:48:29 +10:00
committed by GitHub
parent d7bbb94857
commit 60b34b57f3
4 changed files with 110 additions and 45 deletions

View File

@@ -4,6 +4,7 @@ using Content.Server.Decals;
using Content.Server.NPC.Components; using Content.Server.NPC.Components;
using Content.Server.NPC.HTN; using Content.Server.NPC.HTN;
using Content.Server.NPC.Systems; using Content.Server.NPC.Systems;
using Content.Server.Shuttles.Systems;
using Content.Shared.Construction.EntitySystems; using Content.Shared.Construction.EntitySystems;
using Content.Shared.Maps; using Content.Shared.Maps;
using Content.Shared.Procedural; using Content.Shared.Procedural;
@@ -51,6 +52,8 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
private readonly EntityUid _gridUid; private readonly EntityUid _gridUid;
private readonly MapGridComponent _grid; private readonly MapGridComponent _grid;
private readonly EntityCoordinates? _targetCoordinates;
private readonly ISawmill _sawmill; private readonly ISawmill _sawmill;
public DungeonJob( public DungeonJob(
@@ -70,6 +73,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
EntityUid gridUid, EntityUid gridUid,
int seed, int seed,
Vector2i position, Vector2i position,
EntityCoordinates? targetCoordinates = null,
CancellationToken cancellation = default) : base(maxTime, cancellation) CancellationToken cancellation = default) : base(maxTime, cancellation)
{ {
_sawmill = sawmill; _sawmill = sawmill;
@@ -94,6 +98,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
_gridUid = gridUid; _gridUid = gridUid;
_seed = seed; _seed = seed;
_position = position; _position = position;
_targetCoordinates = targetCoordinates;
} }
/// <summary> /// <summary>
@@ -151,6 +156,12 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
// To make it slightly more deterministic treat this RNG as separate ig. // To make it slightly more deterministic treat this RNG as separate ig.
// Post-processing after finishing loading. // Post-processing after finishing loading.
if (_targetCoordinates != null)
{
var oldMap = _xformQuery.Comp(_gridUid).MapUid;
_entManager.System<ShuttleSystem>().TryFTLProximity(_gridUid, _targetCoordinates.Value);
_entManager.DeleteEntity(oldMap);
}
// Defer splitting so they don't get spammed and so we don't have to worry about tracking the grid along the way. // 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; _grid.CanSplit = true;

View File

@@ -183,11 +183,16 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
return mapId; return mapId;
} }
/// <summary>
/// Generates a dungeon in the background with the specified config.
/// </summary>
/// <param name="coordinates">Coordinates to move the dungeon to afterwards. Will delete the original map</param>
public void GenerateDungeon(DungeonConfig gen, public void GenerateDungeon(DungeonConfig gen,
EntityUid gridUid, EntityUid gridUid,
MapGridComponent grid, MapGridComponent grid,
Vector2i position, Vector2i position,
int seed) int seed,
EntityCoordinates? coordinates = null)
{ {
var cancelToken = new CancellationTokenSource(); var cancelToken = new CancellationTokenSource();
var job = new DungeonJob.DungeonJob( var job = new DungeonJob.DungeonJob(
@@ -207,6 +212,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
gridUid, gridUid,
seed, seed,
position, position,
coordinates,
cancelToken.Token); cancelToken.Token);
_dungeonJobs.Add(job, cancelToken); _dungeonJobs.Add(job, cancelToken);
@@ -238,6 +244,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
gridUid, gridUid,
seed, seed,
position, position,
null,
cancelToken.Token); cancelToken.Token);
_dungeonJobs.Add(job, cancelToken); _dungeonJobs.Add(job, cancelToken);

View File

@@ -66,7 +66,7 @@ public sealed partial class ShuttleSystem
/// <summary> /// <summary>
/// How many times we try to proximity warp close to something before falling back to map-wideAABB. /// How many times we try to proximity warp close to something before falling back to map-wideAABB.
/// </summary> /// </summary>
private const int FTLProximityIterations = 3; private const int FTLProximityIterations = 5;
private readonly HashSet<EntityUid> _lookupEnts = new(); private readonly HashSet<EntityUid> _lookupEnts = new();
private readonly HashSet<EntityUid> _immuneEnts = new(); private readonly HashSet<EntityUid> _immuneEnts = new();
@@ -321,7 +321,7 @@ public sealed partial class ShuttleSystem
hyperspace.TargetCoordinates = config.Coordinates; hyperspace.TargetCoordinates = config.Coordinates;
hyperspace.TargetAngle = config.Angle; hyperspace.TargetAngle = config.Angle;
} }
else if (TryGetFTLProximity(shuttleUid, target, out var coords, out var targAngle)) else if (TryGetFTLProximity(shuttleUid, new EntityCoordinates(target, Vector2.Zero), out var coords, out var targAngle))
{ {
hyperspace.TargetCoordinates = coords; hyperspace.TargetCoordinates = coords;
hyperspace.TargetAngle = targAngle; hyperspace.TargetAngle = targAngle;
@@ -377,10 +377,11 @@ public sealed partial class ShuttleSystem
var fromMatrix = _transform.GetWorldMatrix(xform); var fromMatrix = _transform.GetWorldMatrix(xform);
var fromRotation = _transform.GetWorldRotation(xform); var fromRotation = _transform.GetWorldRotation(xform);
var width = Comp<MapGridComponent>(uid).LocalAABB.Width; var grid = Comp<MapGridComponent>(uid);
var width = grid.LocalAABB.Width;
var ftlMap = EnsureFTLMap(); var ftlMap = EnsureFTLMap();
var body = _physicsQuery.GetComponent(entity); var body = _physicsQuery.GetComponent(entity);
var shuttleCenter = body.LocalCenter; var shuttleCenter = grid.LocalAABB.Center;
// Leave audio at the old spot // Leave audio at the old spot
// Just so we don't clip // Just so we don't clip
@@ -500,6 +501,7 @@ public sealed partial class ShuttleSystem
// Position ftl // Position ftl
else else
{ {
// TODO: This should now use tryftlproximity
mapId = target.GetMapId(EntityManager); mapId = target.GetMapId(EntityManager);
_transform.SetCoordinates(uid, xform, target, rotation: entity.Comp1.TargetAngle); _transform.SetCoordinates(uid, xform, target, rotation: entity.Comp1.TargetAngle);
} }
@@ -698,16 +700,23 @@ public sealed partial class ShuttleSystem
} }
/// <summary> /// <summary>
/// Tries to get the target position to FTL near to another grid. /// Tries to get the target position to FTL near the target coordinates.
/// If the target coordinates have a mapgrid then will try to offset the AABB.
/// </summary> /// </summary>
private bool TryGetFTLProximity(EntityUid shuttleUid, EntityUid targetUid, /// <param name="minOffset">Min offset for the final FTL.</param>
/// <param name="maxOffset">Max offset for the final FTL from the box we spawn.</param>
private bool TryGetFTLProximity(
EntityUid shuttleUid,
EntityCoordinates targetCoordinates,
out EntityCoordinates coordinates, out Angle angle, out EntityCoordinates coordinates, out Angle angle,
float minOffset = 0f, float maxOffset = 64f,
TransformComponent? xform = null, TransformComponent? targetXform = null) TransformComponent? xform = null, TransformComponent? targetXform = null)
{ {
DebugTools.Assert(minOffset < maxOffset);
coordinates = EntityCoordinates.Invalid; coordinates = EntityCoordinates.Invalid;
angle = Angle.Zero; angle = Angle.Zero;
if (!Resolve(targetUid, ref targetXform) || if (!Resolve(targetCoordinates.EntityId, ref targetXform) ||
targetXform.MapUid == null || targetXform.MapUid == null ||
!targetXform.MapUid.Value.IsValid() || !targetXform.MapUid.Value.IsValid() ||
!Resolve(shuttleUid, ref xform)) !Resolve(shuttleUid, ref xform))
@@ -715,26 +724,24 @@ public sealed partial class ShuttleSystem
return false; return false;
} }
var xformQuery = GetEntityQuery<TransformComponent>();
var shuttleAABB = Comp<MapGridComponent>(shuttleUid).LocalAABB;
Box2 targetLocalAABB;
// Spawn nearby.
// We essentially expand the Box2 of the target area until nothing else is added then we know it's valid. // We essentially expand the Box2 of the target area until nothing else is added then we know it's valid.
// Can't just get an AABB of every grid as we may spawn very far away. // Can't just get an AABB of every grid as we may spawn very far away.
if (TryComp<MapGridComponent>(targetXform.GridUid, out var targetGrid))
{
targetLocalAABB = targetGrid.LocalAABB;
}
else
{
targetLocalAABB = new Box2();
}
var targetAABB = _transform.GetWorldMatrix(targetXform, xformQuery)
.TransformBox(targetLocalAABB).Enlarged(shuttleAABB.Size.Length());
var nearbyGrids = new HashSet<EntityUid>(); var nearbyGrids = new HashSet<EntityUid>();
var shuttleAABB = Comp<MapGridComponent>(shuttleUid).LocalAABB;
// Start with small point.
// If our target pos is offset we mot even intersect our target's AABB so we don't include it.
var targetLocalAABB = Box2.CenteredAround(targetCoordinates.Position, Vector2.One);
// How much we expand the target AABB be.
// We half it because we only need the width / height in each direction if it's placed at a particular spot.
var expansionAmount = MathF.Max(shuttleAABB.Width / 2f, shuttleAABB.Height / 2f);
// Expand the starter AABB so we have something to query to start with.
var targetAABB = _transform.GetWorldMatrix(targetXform)
.TransformBox(targetLocalAABB)
.Enlarged(expansionAmount);
var iteration = 0; var iteration = 0;
var lastCount = nearbyGrids.Count; var lastCount = nearbyGrids.Count;
var mapId = targetXform.MapID; var mapId = targetXform.MapID;
@@ -743,15 +750,21 @@ public sealed partial class ShuttleSystem
while (iteration < FTLProximityIterations) while (iteration < FTLProximityIterations)
{ {
grids.Clear(); grids.Clear();
_mapManager.FindGridsIntersecting(mapId, targetAABB, ref grids); // We pass in an expanded offset here so we can safely do a random offset later.
// We don't include this in the actual targetAABB because then we would be double-expanding it.
// Once in this loop, then again when placing the shuttle later.
// Note that targetAABB already has expansionAmount factored in already.
_mapManager.FindGridsIntersecting(mapId, targetAABB.Enlarged(maxOffset), ref grids);
foreach (var grid in grids) foreach (var grid in grids)
{ {
if (!nearbyGrids.Add(grid)) if (!nearbyGrids.Add(grid))
continue; continue;
targetAABB = targetAABB.Union(_transform.GetWorldMatrix(grid, xformQuery) // Include the other grid's AABB (expanded by ours) as well.
.TransformBox(Comp<MapGridComponent>(grid).LocalAABB)); targetAABB = targetAABB.Union(
_transform.GetWorldMatrix(grid)
.TransformBox(Comp<MapGridComponent>(grid).LocalAABB.Enlarged(expansionAmount)));
} }
// Can do proximity // Can do proximity
@@ -760,7 +773,6 @@ public sealed partial class ShuttleSystem
break; break;
} }
targetAABB = targetAABB.Enlarged(shuttleAABB.Size.Length() / 2f);
iteration++; iteration++;
lastCount = nearbyGrids.Count; lastCount = nearbyGrids.Count;
@@ -775,13 +787,15 @@ public sealed partial class ShuttleSystem
if (nearbyGrids.Contains(uid)) if (nearbyGrids.Contains(uid))
continue; continue;
targetAABB = targetAABB.Union(_transform.GetWorldMatrix(uid, xformQuery) targetAABB = targetAABB.Union(
.TransformBox(Comp<MapGridComponent>(uid).LocalAABB)); _transform.GetWorldMatrix(uid)
.TransformBox(Comp<MapGridComponent>(uid).LocalAABB.Enlarged(expansionAmount)));
} }
break; break;
} }
// Now we have a targetAABB. This has already been expanded to account for our fat ass.
Vector2 spawnPos; Vector2 spawnPos;
if (TryComp<PhysicsComponent>(shuttleUid, out var shuttleBody)) if (TryComp<PhysicsComponent>(shuttleUid, out var shuttleBody))
@@ -790,21 +804,32 @@ public sealed partial class ShuttleSystem
_physics.SetAngularVelocity(shuttleUid, 0f, body: shuttleBody); _physics.SetAngularVelocity(shuttleUid, 0f, body: shuttleBody);
} }
// TODO: This should prefer the position's angle instead.
// TODO: This is pretty crude for multiple landings. // TODO: This is pretty crude for multiple landings.
if (nearbyGrids.Count > 1 || !HasComp<MapComponent>(targetXform.GridUid)) if (nearbyGrids.Count > 1 || !HasComp<MapComponent>(targetXform.GridUid))
{ {
var minRadius = (MathF.Max(targetAABB.Width, targetAABB.Height) + MathF.Max(shuttleAABB.Width, shuttleAABB.Height)) / 2f; // Pick a random angle
spawnPos = targetAABB.Center + _random.NextVector2(minRadius, minRadius + 64f); var offsetAngle = _random.NextAngle();
// Our valid spawn positions are <targetAABB width / height + offset> away.
var minRadius = MathF.Max(targetAABB.Width / 2f, targetAABB.Height / 2f);
spawnPos = targetAABB.Center + offsetAngle.RotateVec(new Vector2(_random.NextFloat(minRadius + minOffset, minRadius + maxOffset), 0f));
} }
else if (shuttleBody != null) else if (shuttleBody != null)
{ {
var (targetPos, targetRot) = _transform.GetWorldPositionRotation(targetXform, xformQuery); (spawnPos, angle) = _transform.GetWorldPositionRotation(targetXform);
var transform = new Transform(targetPos, targetRot);
spawnPos = Robust.Shared.Physics.Transform.Mul(transform, -shuttleBody.LocalCenter);
} }
else else
{ {
spawnPos = _transform.GetWorldPosition(targetXform, xformQuery); spawnPos = _transform.GetWorldPosition(targetXform);
}
var offset = Vector2.Zero;
// Offset it because transform does not correspond to AABB position.
if (TryComp(shuttleUid, out MapGridComponent? shuttleGrid))
{
offset = -shuttleGrid.LocalAABB.Center;
} }
if (!HasComp<MapComponent>(targetXform.GridUid)) if (!HasComp<MapComponent>(targetXform.GridUid))
@@ -816,7 +841,11 @@ public sealed partial class ShuttleSystem
angle = Angle.Zero; angle = Angle.Zero;
} }
coordinates = new EntityCoordinates(targetXform.MapUid.Value, spawnPos); // Rotate our localcenter around so we spawn exactly where we "think" we should (center of grid on the dot).
var transform = new Transform(spawnPos, angle);
spawnPos = Robust.Shared.Physics.Transform.Mul(transform, offset);
coordinates = new EntityCoordinates(targetXform.MapUid.Value, spawnPos - offset);
return true; return true;
} }
@@ -833,13 +862,31 @@ public sealed partial class ShuttleSystem
return false; return false;
} }
if (!TryGetFTLProximity(shuttleUid, targetUid, out var coords, out var angle, xform, targetXform)) if (!TryGetFTLProximity(shuttleUid, new EntityCoordinates(targetUid, Vector2.Zero), out var coords, out var angle, xform: xform, targetXform: targetXform))
return false; return false;
_transform.SetCoordinates(shuttleUid, xform, coords, rotation: angle); _transform.SetCoordinates(shuttleUid, xform, coords, rotation: angle);
return true; return true;
} }
/// <summary>
/// Tries to FTL to the target coordinates; will move nearby if not possible.
/// </summary>
public bool TryFTLProximity(Entity<TransformComponent?> shuttle, EntityCoordinates targetCoordinates)
{
if (!Resolve(shuttle.Owner, ref shuttle.Comp) ||
_transform.GetMap(targetCoordinates)?.IsValid() != true)
{
return false;
}
if (!TryGetFTLProximity(shuttle, targetCoordinates, out var coords, out var angle))
return false;
_transform.SetCoordinates(shuttle, shuttle.Comp, coords, rotation: angle);
return true;
}
/// <summary> /// <summary>
/// Flattens / deletes everything under the grid upon FTL. /// Flattens / deletes everything under the grid upon FTL.
/// </summary> /// </summary>
@@ -861,7 +908,6 @@ public sealed partial class ShuttleSystem
var aabb = fixture.Shape.ComputeAABB(transform, 0); var aabb = fixture.Shape.ComputeAABB(transform, 0);
// Shift it slightly // Shift it slightly
aabb = aabb.Translated(-grid.TileSizeHalfVector);
// Create a small border around it. // Create a small border around it.
aabb = aabb.Enlarged(0.2f); aabb = aabb.Enlarged(0.2f);
aabbs.Add(aabb); aabbs.Add(aabb);

View File

@@ -85,7 +85,7 @@ public sealed partial class ShuttleSystem
_mapManager.DeleteMap(mapId); _mapManager.DeleteMap(mapId);
} }
private bool TryDungeonSpawn(Entity<MapGridComponent?> targetGrid, MapId mapId, DungeonSpawnGroup group, out EntityUid spawned) private bool TryDungeonSpawn(Entity<MapGridComponent?> targetGrid, DungeonSpawnGroup group, out EntityUid spawned)
{ {
spawned = EntityUid.Invalid; spawned = EntityUid.Invalid;
@@ -110,11 +110,12 @@ public sealed partial class ShuttleSystem
spawnCoords = spawnCoords.Offset(_random.NextVector2(distancePadding + group.MinimumDistance, distancePadding + group.MaximumDistance)); spawnCoords = spawnCoords.Offset(_random.NextVector2(distancePadding + group.MinimumDistance, distancePadding + group.MaximumDistance));
} }
var spawnMapCoords = _transform.ToMapCoordinates(spawnCoords); _maps.CreateMap(out var mapId);
var spawnedGrid = _mapManager.CreateGridEntity(mapId); var spawnedGrid = _mapManager.CreateGridEntity(mapId);
_transform.SetMapCoordinates(spawnedGrid, spawnMapCoords); _transform.SetMapCoordinates(spawnedGrid, new MapCoordinates(Vector2.Zero, mapId));
_dungeon.GenerateDungeon(dungeonProto, spawnedGrid.Owner, spawnedGrid.Comp, Vector2i.Zero, _random.Next()); _dungeon.GenerateDungeon(dungeonProto, spawnedGrid.Owner, spawnedGrid.Comp, Vector2i.Zero, _random.Next(), spawnCoords);
spawned = spawnedGrid.Owner; spawned = spawnedGrid.Owner;
return true; return true;
@@ -192,7 +193,7 @@ public sealed partial class ShuttleSystem
switch (group) switch (group)
{ {
case DungeonSpawnGroup dungeon: case DungeonSpawnGroup dungeon:
if (!TryDungeonSpawn(targetGrid.Value, mapId, dungeon, out spawned)) if (!TryDungeonSpawn(targetGrid.Value, dungeon, out spawned))
continue; continue;
break; break;