mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-17 13:37:47 +03:00
# Description <!-- Explain this PR in as much detail as applicable Some example prompts to consider: How might this affect the game? The codebase? What might be some alternatives to this? How/Who does this benefit/hurt [the game/codebase]? --> Ports https://github.com/space-wizards/space-station-14/pull/32294 Ports https://github.com/ss14-harmony/ss14-harmony/pull/310 (and everything needed for it to function) Early-merges https://github.com/space-wizards/space-station-14/pull/34302 Adds the ability for multiple central command maps that get randomly selected. Tested and works. --- # Changelog <!-- You can add an author after the `🆑` to change the name that appears in the changelog (ex: `🆑 Death`) Leaving it blank will default to your GitHub display name This includes all available types for the changelog --> 🆑 Several contributors - add: Added a new central command map that is randomly picked alongside the old one (thank you to Spanky from Harmony) - add: Added Advanced SMES for mappers. - add: Added the atmospheric network monitor for seeing what the temperature, moles, and pressure is on every pipe everywhere through a computer. - add: Nukie med bundle now contains a compact defibrillator. - add: Ported a better mapping editor. - add: Added the throngler plushie. - remove: Removed the Throngler as a possible loot spawn for gamble crates. --------- Signed-off-by: sleepyyapril <123355664+sleepyyapril@users.noreply.github.com> (cherry picked from commit 9272f65b64392f66a7cd4fd7c84bb152dc93b65a)
180 lines
6.6 KiB
C#
180 lines
6.6 KiB
C#
using System.Collections;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Numerics;
|
|
using Robust.Shared.GameStates;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Serialization;
|
|
using static Content.Shared.Decals.DecalGridComponent;
|
|
using ChunkIndicesEnumerator = Robust.Shared.Map.Enumerators.ChunkIndicesEnumerator;
|
|
|
|
namespace Content.Shared.Decals
|
|
{
|
|
public abstract class SharedDecalSystem : EntitySystem
|
|
{
|
|
[Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
|
|
[Dependency] protected readonly IMapManager MapManager = default!;
|
|
|
|
protected bool PvsEnabled;
|
|
|
|
// Note that this constant is effectively baked into all map files, because of how they save the grid decal component.
|
|
// So if this ever needs changing, the maps need converting.
|
|
public const int ChunkSize = 32;
|
|
public static Vector2i GetChunkIndices(Vector2 coordinates) => new ((int) Math.Floor(coordinates.X / ChunkSize), (int) Math.Floor(coordinates.Y / ChunkSize));
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
SubscribeLocalEvent<GridInitializeEvent>(OnGridInitialize);
|
|
SubscribeLocalEvent<DecalGridComponent, ComponentStartup>(OnCompStartup);
|
|
SubscribeLocalEvent<DecalGridComponent, ComponentGetState>(OnGetState);
|
|
}
|
|
|
|
private void OnGetState(EntityUid uid, DecalGridComponent component, ref ComponentGetState args)
|
|
{
|
|
if (PvsEnabled && !args.ReplayState)
|
|
return;
|
|
|
|
// Should this be a full component state or a delta-state?
|
|
if (args.FromTick <= component.CreationTick || args.FromTick <= component.ForceTick)
|
|
{
|
|
args.State = new DecalGridState(component.ChunkCollection.ChunkCollection);
|
|
return;
|
|
}
|
|
|
|
var data = new Dictionary<Vector2i, DecalChunk>();
|
|
foreach (var (index, chunk) in component.ChunkCollection.ChunkCollection)
|
|
{
|
|
if (chunk.LastModified >= args.FromTick)
|
|
data[index] = chunk;
|
|
}
|
|
|
|
args.State = new DecalGridDeltaState(data, new(component.ChunkCollection.ChunkCollection.Keys));
|
|
}
|
|
|
|
private void OnGridInitialize(GridInitializeEvent msg)
|
|
{
|
|
EnsureComp<DecalGridComponent>(msg.EntityUid);
|
|
}
|
|
|
|
private void OnCompStartup(EntityUid uid, DecalGridComponent component, ComponentStartup args)
|
|
{
|
|
foreach (var (indices, decals) in component.ChunkCollection.ChunkCollection)
|
|
{
|
|
foreach (var decalUid in decals.Decals.Keys)
|
|
{
|
|
component.DecalIndex[decalUid] = indices;
|
|
}
|
|
}
|
|
|
|
// This **shouldn't** be required, but just in case we ever get entity prototypes that have decal grids, we
|
|
// need to ensure that we send an initial full state to players.
|
|
Dirty(uid, component);
|
|
}
|
|
|
|
protected Dictionary<Vector2i, DecalChunk>? ChunkCollection(EntityUid gridEuid, DecalGridComponent? comp = null)
|
|
{
|
|
if (!Resolve(gridEuid, ref comp))
|
|
return null;
|
|
|
|
return comp.ChunkCollection.ChunkCollection;
|
|
}
|
|
|
|
protected virtual void DirtyChunk(EntityUid id, Vector2i chunkIndices, DecalChunk chunk) {}
|
|
|
|
// internal, so that client/predicted code doesn't accidentally remove decals. There is a public server-side function.
|
|
protected bool RemoveDecalInternal(EntityUid gridId, uint decalId, [NotNullWhen(true)] out Decal? removed, DecalGridComponent? component = null)
|
|
{
|
|
removed = null;
|
|
if (!Resolve(gridId, ref component))
|
|
return false;
|
|
|
|
if (!component.DecalIndex.Remove(decalId, out var indices)
|
|
|| !component.ChunkCollection.ChunkCollection.TryGetValue(indices, out var chunk)
|
|
|| !chunk.Decals.Remove(decalId, out removed))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (chunk.Decals.Count == 0)
|
|
component.ChunkCollection.ChunkCollection.Remove(indices);
|
|
|
|
DirtyChunk(gridId, indices, chunk);
|
|
OnDecalRemoved(gridId, decalId, component, indices, chunk);
|
|
return true;
|
|
}
|
|
|
|
public HashSet<(uint Index, Decal Decal)> GetDecalsIntersecting(EntityUid gridUid, Box2 bounds, DecalGridComponent? component = null)
|
|
{
|
|
var decalIds = new HashSet<(uint, Decal)>();
|
|
var chunkCollection = ChunkCollection(gridUid, component);
|
|
|
|
if (chunkCollection == null)
|
|
return decalIds;
|
|
|
|
var chunks = new ChunkIndicesEnumerator(bounds, ChunkSize);
|
|
|
|
while (chunks.MoveNext(out var chunkOrigin))
|
|
{
|
|
if (!chunkCollection.TryGetValue(chunkOrigin.Value, out var chunk))
|
|
continue;
|
|
|
|
foreach (var (id, decal) in chunk.Decals)
|
|
{
|
|
if (!bounds.Contains(decal.Coordinates))
|
|
continue;
|
|
|
|
decalIds.Add((id, decal));
|
|
}
|
|
}
|
|
|
|
return decalIds;
|
|
}
|
|
|
|
protected virtual void OnDecalRemoved(EntityUid gridId, uint decalId, DecalGridComponent component, Vector2i indices, DecalChunk chunk)
|
|
{
|
|
// used by client-side overlay code
|
|
}
|
|
|
|
public virtual HashSet<(uint Index, Decal Decal)> GetDecalsInRange(EntityUid gridId, Vector2 position, float distance = 0.75f, Func<Decal, bool>? validDelegate = null)
|
|
{
|
|
// NOOP on client atm.
|
|
return new HashSet<(uint Index, Decal Decal)>();
|
|
}
|
|
|
|
public virtual bool RemoveDecal(EntityUid gridId, uint decalId, DecalGridComponent? component = null)
|
|
{
|
|
// NOOP on client atm.
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sent by clients to request that a decal is placed on the server.
|
|
/// </summary>
|
|
[Serializable, NetSerializable]
|
|
public sealed class RequestDecalPlacementEvent : EntityEventArgs
|
|
{
|
|
public Decal Decal;
|
|
public NetCoordinates Coordinates;
|
|
|
|
public RequestDecalPlacementEvent(Decal decal, NetCoordinates coordinates)
|
|
{
|
|
Decal = decal;
|
|
Coordinates = coordinates;
|
|
}
|
|
}
|
|
|
|
[Serializable, NetSerializable]
|
|
public sealed class RequestDecalRemovalEvent : EntityEventArgs
|
|
{
|
|
public NetCoordinates Coordinates;
|
|
|
|
public RequestDecalRemovalEvent(NetCoordinates coordinates)
|
|
{
|
|
Coordinates = coordinates;
|
|
}
|
|
}
|
|
}
|