Fix dungeon spawn + ftl overlap (#31413)
* Fix dungeon spawn + ftl overlap * Better fixes
This commit is contained in:
@@ -4,6 +4,7 @@ using Content.Server.Decals;
|
||||
using Content.Server.NPC.Components;
|
||||
using Content.Server.NPC.HTN;
|
||||
using Content.Server.NPC.Systems;
|
||||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Shared.Construction.EntitySystems;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Procedural;
|
||||
@@ -51,6 +52,8 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
|
||||
private readonly EntityUid _gridUid;
|
||||
private readonly MapGridComponent _grid;
|
||||
|
||||
private readonly EntityCoordinates? _targetCoordinates;
|
||||
|
||||
private readonly ISawmill _sawmill;
|
||||
|
||||
public DungeonJob(
|
||||
@@ -70,6 +73,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
|
||||
EntityUid gridUid,
|
||||
int seed,
|
||||
Vector2i position,
|
||||
EntityCoordinates? targetCoordinates = null,
|
||||
CancellationToken cancellation = default) : base(maxTime, cancellation)
|
||||
{
|
||||
_sawmill = sawmill;
|
||||
@@ -94,6 +98,7 @@ public sealed partial class DungeonJob : Job<List<Dungeon>>
|
||||
_gridUid = gridUid;
|
||||
_seed = seed;
|
||||
_position = position;
|
||||
_targetCoordinates = targetCoordinates;
|
||||
}
|
||||
|
||||
/// <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.
|
||||
|
||||
// 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.
|
||||
_grid.CanSplit = true;
|
||||
|
||||
@@ -183,11 +183,16 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
|
||||
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,
|
||||
EntityUid gridUid,
|
||||
MapGridComponent grid,
|
||||
Vector2i position,
|
||||
int seed)
|
||||
int seed,
|
||||
EntityCoordinates? coordinates = null)
|
||||
{
|
||||
var cancelToken = new CancellationTokenSource();
|
||||
var job = new DungeonJob.DungeonJob(
|
||||
@@ -207,6 +212,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
|
||||
gridUid,
|
||||
seed,
|
||||
position,
|
||||
coordinates,
|
||||
cancelToken.Token);
|
||||
|
||||
_dungeonJobs.Add(job, cancelToken);
|
||||
@@ -238,6 +244,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem
|
||||
gridUid,
|
||||
seed,
|
||||
position,
|
||||
null,
|
||||
cancelToken.Token);
|
||||
|
||||
_dungeonJobs.Add(job, cancelToken);
|
||||
|
||||
@@ -66,7 +66,7 @@ public sealed partial class ShuttleSystem
|
||||
/// <summary>
|
||||
/// How many times we try to proximity warp close to something before falling back to map-wideAABB.
|
||||
/// </summary>
|
||||
private const int FTLProximityIterations = 3;
|
||||
private const int FTLProximityIterations = 5;
|
||||
|
||||
private readonly HashSet<EntityUid> _lookupEnts = new();
|
||||
private readonly HashSet<EntityUid> _immuneEnts = new();
|
||||
@@ -321,7 +321,7 @@ public sealed partial class ShuttleSystem
|
||||
hyperspace.TargetCoordinates = config.Coordinates;
|
||||
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.TargetAngle = targAngle;
|
||||
@@ -377,10 +377,11 @@ public sealed partial class ShuttleSystem
|
||||
var fromMatrix = _transform.GetWorldMatrix(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 body = _physicsQuery.GetComponent(entity);
|
||||
var shuttleCenter = body.LocalCenter;
|
||||
var shuttleCenter = grid.LocalAABB.Center;
|
||||
|
||||
// Leave audio at the old spot
|
||||
// Just so we don't clip
|
||||
@@ -500,6 +501,7 @@ public sealed partial class ShuttleSystem
|
||||
// Position ftl
|
||||
else
|
||||
{
|
||||
// TODO: This should now use tryftlproximity
|
||||
mapId = target.GetMapId(EntityManager);
|
||||
_transform.SetCoordinates(uid, xform, target, rotation: entity.Comp1.TargetAngle);
|
||||
}
|
||||
@@ -698,16 +700,23 @@ public sealed partial class ShuttleSystem
|
||||
}
|
||||
|
||||
/// <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>
|
||||
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,
|
||||
float minOffset = 0f, float maxOffset = 64f,
|
||||
TransformComponent? xform = null, TransformComponent? targetXform = null)
|
||||
{
|
||||
DebugTools.Assert(minOffset < maxOffset);
|
||||
coordinates = EntityCoordinates.Invalid;
|
||||
angle = Angle.Zero;
|
||||
|
||||
if (!Resolve(targetUid, ref targetXform) ||
|
||||
if (!Resolve(targetCoordinates.EntityId, ref targetXform) ||
|
||||
targetXform.MapUid == null ||
|
||||
!targetXform.MapUid.Value.IsValid() ||
|
||||
!Resolve(shuttleUid, ref xform))
|
||||
@@ -715,26 +724,24 @@ public sealed partial class ShuttleSystem
|
||||
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.
|
||||
// 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 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 lastCount = nearbyGrids.Count;
|
||||
var mapId = targetXform.MapID;
|
||||
@@ -743,15 +750,21 @@ public sealed partial class ShuttleSystem
|
||||
while (iteration < FTLProximityIterations)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!nearbyGrids.Add(grid))
|
||||
continue;
|
||||
|
||||
targetAABB = targetAABB.Union(_transform.GetWorldMatrix(grid, xformQuery)
|
||||
.TransformBox(Comp<MapGridComponent>(grid).LocalAABB));
|
||||
// Include the other grid's AABB (expanded by ours) as well.
|
||||
targetAABB = targetAABB.Union(
|
||||
_transform.GetWorldMatrix(grid)
|
||||
.TransformBox(Comp<MapGridComponent>(grid).LocalAABB.Enlarged(expansionAmount)));
|
||||
}
|
||||
|
||||
// Can do proximity
|
||||
@@ -760,7 +773,6 @@ public sealed partial class ShuttleSystem
|
||||
break;
|
||||
}
|
||||
|
||||
targetAABB = targetAABB.Enlarged(shuttleAABB.Size.Length() / 2f);
|
||||
iteration++;
|
||||
lastCount = nearbyGrids.Count;
|
||||
|
||||
@@ -775,13 +787,15 @@ public sealed partial class ShuttleSystem
|
||||
if (nearbyGrids.Contains(uid))
|
||||
continue;
|
||||
|
||||
targetAABB = targetAABB.Union(_transform.GetWorldMatrix(uid, xformQuery)
|
||||
.TransformBox(Comp<MapGridComponent>(uid).LocalAABB));
|
||||
targetAABB = targetAABB.Union(
|
||||
_transform.GetWorldMatrix(uid)
|
||||
.TransformBox(Comp<MapGridComponent>(uid).LocalAABB.Enlarged(expansionAmount)));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Now we have a targetAABB. This has already been expanded to account for our fat ass.
|
||||
Vector2 spawnPos;
|
||||
|
||||
if (TryComp<PhysicsComponent>(shuttleUid, out var shuttleBody))
|
||||
@@ -790,21 +804,32 @@ public sealed partial class ShuttleSystem
|
||||
_physics.SetAngularVelocity(shuttleUid, 0f, body: shuttleBody);
|
||||
}
|
||||
|
||||
// TODO: This should prefer the position's angle instead.
|
||||
// TODO: This is pretty crude for multiple landings.
|
||||
if (nearbyGrids.Count > 1 || !HasComp<MapComponent>(targetXform.GridUid))
|
||||
{
|
||||
var minRadius = (MathF.Max(targetAABB.Width, targetAABB.Height) + MathF.Max(shuttleAABB.Width, shuttleAABB.Height)) / 2f;
|
||||
spawnPos = targetAABB.Center + _random.NextVector2(minRadius, minRadius + 64f);
|
||||
// Pick a random angle
|
||||
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)
|
||||
{
|
||||
var (targetPos, targetRot) = _transform.GetWorldPositionRotation(targetXform, xformQuery);
|
||||
var transform = new Transform(targetPos, targetRot);
|
||||
spawnPos = Robust.Shared.Physics.Transform.Mul(transform, -shuttleBody.LocalCenter);
|
||||
(spawnPos, angle) = _transform.GetWorldPositionRotation(targetXform);
|
||||
}
|
||||
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))
|
||||
@@ -816,7 +841,11 @@ public sealed partial class ShuttleSystem
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -833,13 +862,31 @@ public sealed partial class ShuttleSystem
|
||||
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;
|
||||
|
||||
_transform.SetCoordinates(shuttleUid, xform, coords, rotation: angle);
|
||||
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>
|
||||
/// Flattens / deletes everything under the grid upon FTL.
|
||||
/// </summary>
|
||||
@@ -861,7 +908,6 @@ public sealed partial class ShuttleSystem
|
||||
var aabb = fixture.Shape.ComputeAABB(transform, 0);
|
||||
|
||||
// Shift it slightly
|
||||
aabb = aabb.Translated(-grid.TileSizeHalfVector);
|
||||
// Create a small border around it.
|
||||
aabb = aabb.Enlarged(0.2f);
|
||||
aabbs.Add(aabb);
|
||||
|
||||
@@ -85,7 +85,7 @@ public sealed partial class ShuttleSystem
|
||||
_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;
|
||||
|
||||
@@ -110,11 +110,12 @@ public sealed partial class ShuttleSystem
|
||||
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);
|
||||
|
||||
_transform.SetMapCoordinates(spawnedGrid, spawnMapCoords);
|
||||
_dungeon.GenerateDungeon(dungeonProto, spawnedGrid.Owner, spawnedGrid.Comp, Vector2i.Zero, _random.Next());
|
||||
_transform.SetMapCoordinates(spawnedGrid, new MapCoordinates(Vector2.Zero, mapId));
|
||||
_dungeon.GenerateDungeon(dungeonProto, spawnedGrid.Owner, spawnedGrid.Comp, Vector2i.Zero, _random.Next(), spawnCoords);
|
||||
|
||||
spawned = spawnedGrid.Owner;
|
||||
return true;
|
||||
@@ -192,7 +193,7 @@ public sealed partial class ShuttleSystem
|
||||
switch (group)
|
||||
{
|
||||
case DungeonSpawnGroup dungeon:
|
||||
if (!TryDungeonSpawn(targetGrid.Value, mapId, dungeon, out spawned))
|
||||
if (!TryDungeonSpawn(targetGrid.Value, dungeon, out spawned))
|
||||
continue;
|
||||
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user