Salvage expeditions (#12745)
This commit is contained in:
@@ -1,21 +1,31 @@
|
||||
using Content.Server.Decals;
|
||||
using Content.Server.Shuttles.Events;
|
||||
using Content.Shared.Decals;
|
||||
using Content.Shared.Parallax.Biomes;
|
||||
using Content.Shared.Parallax.Biomes.Layers;
|
||||
using Content.Shared.Parallax.Biomes.Markers;
|
||||
using Content.Shared.Parallax.Biomes.Points;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Noise;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Parallax;
|
||||
|
||||
public sealed class BiomeSystem : SharedBiomeSystem
|
||||
public sealed partial class BiomeSystem : SharedBiomeSystem
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Dependency] private readonly IConsoleHost _console = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly DecalSystem _decals = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
@@ -23,6 +33,10 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
private readonly HashSet<EntityUid> _handledEntities = new();
|
||||
private const float DefaultLoadRange = 16f;
|
||||
private float _loadRange = DefaultLoadRange;
|
||||
|
||||
/// <summary>
|
||||
/// Load area for chunks containing tiles, decals etc.
|
||||
/// </summary>
|
||||
private Box2 _loadArea = new(-DefaultLoadRange, -DefaultLoadRange, DefaultLoadRange, DefaultLoadRange);
|
||||
|
||||
/// <summary>
|
||||
@@ -30,18 +44,41 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
/// </summary>
|
||||
private readonly Dictionary<BiomeComponent, HashSet<Vector2i>> _activeChunks = new();
|
||||
|
||||
private readonly Dictionary<BiomeComponent,
|
||||
Dictionary<string, HashSet<Vector2i>>> _markerChunks = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<BiomeComponent, ComponentStartup>(OnBiomeStartup);
|
||||
SubscribeLocalEvent<BiomeComponent, MapInitEvent>(OnBiomeMapInit);
|
||||
SubscribeLocalEvent<FTLStartedEvent>(OnFTLStarted);
|
||||
_configManager.OnValueChanged(CVars.NetMaxUpdateRange, SetLoadRange, true);
|
||||
InitializeCommands();
|
||||
_proto.PrototypesReloaded += ProtoReload;
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_configManager.UnsubValueChanged(CVars.NetMaxUpdateRange, SetLoadRange);
|
||||
_proto.PrototypesReloaded -= ProtoReload;
|
||||
}
|
||||
|
||||
private void ProtoReload(PrototypesReloadedEventArgs obj)
|
||||
{
|
||||
if (!obj.ByType.TryGetValue(typeof(BiomeTemplatePrototype), out var reloads))
|
||||
return;
|
||||
|
||||
var query = AllEntityQuery<BiomeComponent>();
|
||||
|
||||
while (query.MoveNext(out var biome))
|
||||
{
|
||||
if (biome.Template == null || !reloads.Modified.TryGetValue(biome.Template, out var proto))
|
||||
continue;
|
||||
|
||||
SetTemplate(biome, (BiomeTemplatePrototype) proto);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetLoadRange(float obj)
|
||||
@@ -61,15 +98,6 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
SetSeed(component, _random.Next());
|
||||
}
|
||||
|
||||
public void SetPrototype(BiomeComponent component, string proto)
|
||||
{
|
||||
if (component.BiomePrototype == proto)
|
||||
return;
|
||||
|
||||
component.BiomePrototype = proto;
|
||||
Dirty(component);
|
||||
}
|
||||
|
||||
public void SetSeed(BiomeComponent component, int seed)
|
||||
{
|
||||
component.Seed = seed;
|
||||
@@ -77,6 +105,120 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
Dirty(component);
|
||||
}
|
||||
|
||||
public void ClearTemplate(BiomeComponent component)
|
||||
{
|
||||
component.Layers.Clear();
|
||||
component.Template = null;
|
||||
Dirty(component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="BiomeComponent.Template"/> and refreshes layers.
|
||||
/// </summary>
|
||||
public void SetTemplate(BiomeComponent component, BiomeTemplatePrototype template)
|
||||
{
|
||||
component.Layers.Clear();
|
||||
component.Template = template.ID;
|
||||
|
||||
foreach (var layer in template.Layers)
|
||||
{
|
||||
component.Layers.Add(layer);
|
||||
}
|
||||
|
||||
Dirty(component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified layer at the specified marker if it exists.
|
||||
/// </summary>
|
||||
public void AddLayer(BiomeComponent component, string id, IBiomeLayer addedLayer, int seedOffset = 0)
|
||||
{
|
||||
for (var i = 0; i < component.Layers.Count; i++)
|
||||
{
|
||||
var layer = component.Layers[i];
|
||||
|
||||
if (layer is not BiomeDummyLayer dummy || dummy.ID != id)
|
||||
continue;
|
||||
|
||||
addedLayer.Noise.SetSeed(addedLayer.Noise.GetSeed() + seedOffset);
|
||||
component.Layers.Insert(i, addedLayer);
|
||||
break;
|
||||
}
|
||||
|
||||
Dirty(component);
|
||||
}
|
||||
|
||||
public void AddMarkerLayer(BiomeComponent component, string marker)
|
||||
{
|
||||
if (!_proto.HasIndex<BiomeMarkerLayerPrototype>(marker))
|
||||
{
|
||||
// TODO: Log when we get a sawmill
|
||||
return;
|
||||
}
|
||||
|
||||
component.MarkerLayers.Add(marker);
|
||||
Dirty(component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified template at the specified marker if it exists, withour overriding every layer.
|
||||
/// </summary>
|
||||
public void AddTemplate(BiomeComponent component, string id, BiomeTemplatePrototype template, int seedOffset = 0)
|
||||
{
|
||||
for (var i = 0; i < component.Layers.Count; i++)
|
||||
{
|
||||
var layer = component.Layers[i];
|
||||
|
||||
if (layer is not BiomeDummyLayer dummy || dummy.ID != id)
|
||||
continue;
|
||||
|
||||
for (var j = template.Layers.Count - 1; j >= 0; j--)
|
||||
{
|
||||
var addedLayer = template.Layers[j];
|
||||
addedLayer.Noise.SetSeed(addedLayer.Noise.GetSeed() + seedOffset);
|
||||
component.Layers.Insert(i, addedLayer);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Dirty(component);
|
||||
}
|
||||
|
||||
private void OnFTLStarted(ref FTLStartedEvent ev)
|
||||
{
|
||||
var targetMap = ev.TargetCoordinates.ToMap(EntityManager, _transform);
|
||||
var targetMapUid = _mapManager.GetMapEntityId(targetMap.MapId);
|
||||
|
||||
if (!TryComp<BiomeComponent>(targetMapUid, out var biome))
|
||||
return;
|
||||
|
||||
var targetArea = new Box2(targetMap.Position - 64f, targetMap.Position + 64f);
|
||||
Preload(targetMapUid, biome, targetArea);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Preloads biome for the specified area.
|
||||
/// </summary>
|
||||
public void Preload(EntityUid uid, BiomeComponent component, Box2 area)
|
||||
{
|
||||
var markers = component.MarkerLayers;
|
||||
var goobers = _markerChunks.GetOrNew(component);
|
||||
|
||||
foreach (var layer in markers)
|
||||
{
|
||||
var proto = _proto.Index<BiomeMarkerLayerPrototype>(layer);
|
||||
var enumerator = new ChunkIndicesEnumerator(area, proto.Size);
|
||||
|
||||
while (enumerator.MoveNext(out var chunk))
|
||||
{
|
||||
var chunkOrigin = chunk * proto.Size;
|
||||
var layerChunks = goobers.GetOrNew(proto.ID);
|
||||
layerChunks.Add(chunkOrigin.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -87,6 +229,7 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
while (biomes.MoveNext(out var biome))
|
||||
{
|
||||
_activeChunks.Add(biome, new HashSet<Vector2i>());
|
||||
_markerChunks.GetOrNew(biome);
|
||||
}
|
||||
|
||||
// Get chunks in range
|
||||
@@ -98,7 +241,14 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
_handledEntities.Add(pSession.AttachedEntity.Value) &&
|
||||
biomeQuery.TryGetComponent(xform.MapUid, out var biome))
|
||||
{
|
||||
AddChunksInRange(biome, _transform.GetWorldPosition(xform, xformQuery));
|
||||
var worldPos = _transform.GetWorldPosition(xform, xformQuery);
|
||||
AddChunksInRange(biome, worldPos);
|
||||
|
||||
foreach (var layer in biome.MarkerLayers)
|
||||
{
|
||||
var layerProto = _proto.Index<BiomeMarkerLayerPrototype>(layer);
|
||||
AddMarkerChunksInRange(biome, worldPos, layerProto);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var viewer in pSession.ViewSubscriptions)
|
||||
@@ -110,16 +260,22 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
continue;
|
||||
}
|
||||
|
||||
AddChunksInRange(biome, _transform.GetWorldPosition(xform, xformQuery));
|
||||
var worldPos = _transform.GetWorldPosition(xform, xformQuery);
|
||||
AddChunksInRange(biome, worldPos);
|
||||
|
||||
foreach (var layer in biome.MarkerLayers)
|
||||
{
|
||||
var layerProto = _proto.Index<BiomeMarkerLayerPrototype>(layer);
|
||||
AddMarkerChunksInRange(biome, worldPos, layerProto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var loadBiomes = AllEntityQuery<BiomeComponent, MapGridComponent>();
|
||||
|
||||
while (loadBiomes.MoveNext(out var biome, out var grid))
|
||||
while (loadBiomes.MoveNext(out var gridUid, out var biome, out var grid))
|
||||
{
|
||||
var noise = biome.Noise;
|
||||
var gridUid = grid.Owner;
|
||||
|
||||
// Load new chunks
|
||||
LoadChunks(biome, gridUid, grid, noise, xformQuery);
|
||||
@@ -129,6 +285,7 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
|
||||
_handledEntities.Clear();
|
||||
_activeChunks.Clear();
|
||||
_markerChunks.Clear();
|
||||
}
|
||||
|
||||
private void AddChunksInRange(BiomeComponent biome, Vector2 worldPos)
|
||||
@@ -141,6 +298,21 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void AddMarkerChunksInRange(BiomeComponent biome, Vector2 worldPos, IBiomeMarkerLayer layer)
|
||||
{
|
||||
// Offset the load area so it's centralised.
|
||||
var loadArea = new Box2(0, 0, layer.Size, layer.Size);
|
||||
var enumerator = new ChunkIndicesEnumerator(loadArea.Translated(worldPos - layer.Size / 2f), layer.Size);
|
||||
|
||||
while (enumerator.MoveNext(out var chunkOrigin))
|
||||
{
|
||||
var lay = _markerChunks[biome].GetOrNew(layer.ID);
|
||||
lay.Add(chunkOrigin.Value * layer.Size);
|
||||
}
|
||||
}
|
||||
|
||||
#region Load
|
||||
|
||||
private void LoadChunks(
|
||||
BiomeComponent component,
|
||||
EntityUid gridUid,
|
||||
@@ -148,8 +320,54 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
FastNoiseLite noise,
|
||||
EntityQuery<TransformComponent> xformQuery)
|
||||
{
|
||||
var markers = _markerChunks[component];
|
||||
var loadedMarkers = component.LoadedMarkers;
|
||||
|
||||
foreach (var (layer, chunks) in markers)
|
||||
{
|
||||
foreach (var chunk in chunks)
|
||||
{
|
||||
if (loadedMarkers.TryGetValue(layer, out var mobChunks) && mobChunks.Contains(chunk))
|
||||
continue;
|
||||
|
||||
var layerProto = _proto.Index<BiomeMarkerLayerPrototype>(layer);
|
||||
var buffer = layerProto.Radius / 2f;
|
||||
mobChunks ??= new HashSet<Vector2i>();
|
||||
mobChunks.Add(chunk);
|
||||
loadedMarkers[layer] = mobChunks;
|
||||
var rand = new Random(noise.GetSeed() + chunk.X * 8 + chunk.Y);
|
||||
|
||||
// Load NOW
|
||||
// TODO: Need poisson but crashes whenever I use moony's due to inputs or smth
|
||||
var count = (int) ((layerProto.Size - buffer) * (layerProto.Size - buffer) / (layerProto.Radius * layerProto.Radius));
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
for (var j = 0; j < 5; j++)
|
||||
{
|
||||
var point = new Vector2(
|
||||
chunk.X + buffer * rand.NextFloat() * (layerProto.Size - buffer),
|
||||
chunk.Y + buffer * rand.NextFloat() * (layerProto.Size - buffer));
|
||||
|
||||
var coords = new EntityCoordinates(gridUid, point);
|
||||
var tile = grid.LocalToTile(coords);
|
||||
|
||||
// Blocked spawn, try again.
|
||||
if (grid.GetAnchoredEntitiesEnumerator(tile).MoveNext(out _))
|
||||
continue;
|
||||
|
||||
for (var k = 0; k < layerProto.GroupCount; k++)
|
||||
{
|
||||
Spawn(layerProto.Prototype, new EntityCoordinates(gridUid, point));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var active = _activeChunks[component];
|
||||
var prototype = ProtoManager.Index<BiomePrototype>(component.BiomePrototype);
|
||||
List<(Vector2i, Tile)>? tiles = null;
|
||||
|
||||
foreach (var chunk in active)
|
||||
@@ -159,7 +377,7 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
|
||||
tiles ??= new List<(Vector2i, Tile)>(ChunkSize * ChunkSize);
|
||||
// Load NOW!
|
||||
LoadChunk(component, gridUid, grid, chunk, noise, prototype, tiles, xformQuery);
|
||||
LoadChunk(component, gridUid, grid, chunk, noise, tiles, xformQuery);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +387,6 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
MapGridComponent grid,
|
||||
Vector2i chunk,
|
||||
FastNoiseLite noise,
|
||||
BiomePrototype prototype,
|
||||
List<(Vector2i, Tile)> tiles,
|
||||
EntityQuery<TransformComponent> xformQuery)
|
||||
{
|
||||
@@ -191,7 +408,7 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
continue;
|
||||
|
||||
// Pass in null so we don't try to get the tileref.
|
||||
if (!TryGetBiomeTile(indices, prototype, noise, null, out var biomeTile) || biomeTile.Value == tileRef.Tile)
|
||||
if (!TryGetBiomeTile(indices, component.Layers, noise, null, out var biomeTile) || biomeTile.Value == tileRef.Tile)
|
||||
continue;
|
||||
|
||||
tiles.Add((indices, biomeTile.Value));
|
||||
@@ -217,7 +434,7 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
// Don't mess with anything that's potentially anchored.
|
||||
var anchored = grid.GetAnchoredEntitiesEnumerator(indices);
|
||||
|
||||
if (anchored.MoveNext(out _) || !TryGetEntity(indices, prototype, noise, grid, out var entPrototype))
|
||||
if (anchored.MoveNext(out _) || !TryGetEntity(indices, component.Layers, noise, grid, out var entPrototype))
|
||||
continue;
|
||||
|
||||
// TODO: Fix non-anchored ents spawning.
|
||||
@@ -227,7 +444,7 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
// At least for now unless we do lookups or smth, only work with anchoring.
|
||||
if (xformQuery.TryGetComponent(ent, out var xform) && !xform.Anchored)
|
||||
{
|
||||
_transform.AnchorEntity(ent, xform, grid, indices);
|
||||
_transform.AnchorEntity(ent, xform, gridUid, grid, indices);
|
||||
}
|
||||
|
||||
loadedEntities.Add(ent);
|
||||
@@ -250,7 +467,7 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
// Don't mess with anything that's potentially anchored.
|
||||
var anchored = grid.GetAnchoredEntitiesEnumerator(indices);
|
||||
|
||||
if (anchored.MoveNext(out _) || !TryGetDecals(indices, prototype, noise, grid, out var decals))
|
||||
if (anchored.MoveNext(out _) || !TryGetDecals(indices, component.Layers, noise, grid, out var decals))
|
||||
continue;
|
||||
|
||||
foreach (var decal in decals)
|
||||
@@ -273,6 +490,10 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unload
|
||||
|
||||
private void UnloadChunks(BiomeComponent component, EntityUid gridUid, MapGridComponent grid, FastNoiseLite noise)
|
||||
{
|
||||
var active = _activeChunks[component];
|
||||
@@ -292,7 +513,6 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
private void UnloadChunk(BiomeComponent component, EntityUid gridUid, MapGridComponent grid, Vector2i chunk, FastNoiseLite noise, List<(Vector2i, Tile)> tiles)
|
||||
{
|
||||
// Reverse order to loading
|
||||
var prototype = ProtoManager.Index<BiomePrototype>(component.BiomePrototype);
|
||||
component.ModifiedTiles.TryGetValue(chunk, out var modified);
|
||||
modified ??= new HashSet<Vector2i>();
|
||||
|
||||
@@ -338,7 +558,7 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
}
|
||||
|
||||
// If it's default data unload the tile.
|
||||
if (!TryGetBiomeTile(indices, prototype, noise, null, out var biomeTile) ||
|
||||
if (!TryGetBiomeTile(indices, component.Layers, noise, null, out var biomeTile) ||
|
||||
grid.TryGetTileRef(indices, out var tileRef) && tileRef.Tile != biomeTile.Value)
|
||||
{
|
||||
modified.Add(indices);
|
||||
@@ -362,4 +582,6 @@ public sealed class BiomeSystem : SharedBiomeSystem
|
||||
component.ModifiedTiles[chunk] = modified;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user