Merge remote-tracking branch 'space-station-14/master' into 02-05-2024-upstream

This commit is contained in:
Ed
2024-05-02 20:46:45 +03:00
152 changed files with 1869 additions and 1526 deletions

View File

@@ -0,0 +1,17 @@
using System.Numerics;
namespace Content.Client.Animations;
/// <summary>
/// Entities with this component tracks the user's world position every frame.
/// </summary>
[RegisterComponent]
public sealed partial class TrackUserComponent : Component
{
public EntityUid? User;
/// <summary>
/// Offset in the direction of the entity's rotation.
/// </summary>
public Vector2 Offset = Vector2.Zero;
}

View File

@@ -52,7 +52,7 @@ public sealed class ExpendableLightSystem : VisualizerSystem<ExpendableLightComp
case ExpendableLightState.Lit:
_audioSystem.Stop(comp.PlayingStream);
comp.PlayingStream = _audioSystem.PlayPvs(
comp.LoopedSound, uid, SharedExpendableLightComponent.LoopedSoundParams)?.Entity;
comp.LoopedSound, uid)?.Entity;
if (args.Sprite.LayerMapTryGet(ExpendableLightVisualLayers.Overlay, out var layerIdx, true))
{

View File

@@ -49,7 +49,7 @@ public sealed class MouseRotatorSystem : SharedMouseRotatorSystem
if (angleDir == curRot.GetCardinalDir())
return;
RaisePredictiveEvent(new RequestMouseRotatorRotationSimpleEvent()
RaisePredictiveEvent(new RequestMouseRotatorRotationSimpleEvent()
{
Direction = angleDir,
});

View File

@@ -24,14 +24,7 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
if (!state.AllChunks!.Contains(index))
component.Chunks.Remove(index);
}
foreach (var beacon in component.Beacons)
{
if (!state.AllBeacons!.Contains(beacon))
component.Beacons.Remove(beacon);
}
}
else
{
foreach (var index in component.Chunks.Keys)
@@ -39,25 +32,19 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
if (!state.Chunks.ContainsKey(index))
component.Chunks.Remove(index);
}
foreach (var beacon in component.Beacons)
{
if (!state.Beacons.Contains(beacon))
component.Beacons.Remove(beacon);
}
}
foreach (var ((category, origin), chunk) in state.Chunks)
foreach (var (origin, chunk) in state.Chunks)
{
var newChunk = new NavMapChunk(origin);
foreach (var (atmosDirection, value) in chunk)
newChunk.TileData[atmosDirection] = value;
component.Chunks[(category, origin)] = newChunk;
Array.Copy(chunk, newChunk.TileData, chunk.Length);
component.Chunks[origin] = newChunk;
}
foreach (var beacon in state.Beacons)
component.Beacons.Add(beacon);
component.Beacons.Clear();
foreach (var (nuid, beacon) in state.Beacons)
{
component.Beacons[nuid] = beacon;
}
}
}

View File

@@ -18,6 +18,7 @@ using System.Numerics;
using JetBrains.Annotations;
using Content.Shared.Atmos;
using System.Linq;
using Robust.Shared.Utility;
namespace Content.Client.Pinpointer.UI;
@@ -71,10 +72,10 @@ public partial class NavMapControl : MapGridControl
protected float BackgroundOpacity = 0.9f;
private int _targetFontsize = 8;
protected Dictionary<(int, Vector2i), (int, Vector2i)> HorizLinesLookup = new();
protected Dictionary<(int, Vector2i), (int, Vector2i)> HorizLinesLookupReversed = new();
protected Dictionary<(int, Vector2i), (int, Vector2i)> VertLinesLookup = new();
protected Dictionary<(int, Vector2i), (int, Vector2i)> VertLinesLookupReversed = new();
private Dictionary<Vector2i, Vector2i> _horizLines = new();
private Dictionary<Vector2i, Vector2i> _horizLinesReversed = new();
private Dictionary<Vector2i, Vector2i> _vertLines = new();
private Dictionary<Vector2i, Vector2i> _vertLinesReversed = new();
// Components
private NavMapComponent? _navMap;
@@ -376,7 +377,7 @@ public partial class NavMapControl : MapGridControl
var fontSize = (int) Math.Round(1 / WorldRange * DefaultDisplayedRange * UIScale * _targetFontsize, 0);
var font = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Bold.ttf"), fontSize);
foreach (var beacon in _navMap.Beacons)
foreach (var beacon in _navMap.Beacons.Values)
{
var position = beacon.Position - offset;
position = ScalePosition(position with { Y = -position.Y });
@@ -485,113 +486,106 @@ public partial class NavMapControl : MapGridControl
return;
// We'll use the following dictionaries to combine collinear wall lines
HorizLinesLookup.Clear();
HorizLinesLookupReversed.Clear();
VertLinesLookup.Clear();
VertLinesLookupReversed.Clear();
_horizLines.Clear();
_horizLinesReversed.Clear();
_vertLines.Clear();
_vertLinesReversed.Clear();
foreach ((var (category, chunkOrigin), var chunk) in _navMap.Chunks)
const int southMask = (int) AtmosDirection.South << (int) NavMapChunkType.Wall;
const int eastMask = (int) AtmosDirection.East << (int) NavMapChunkType.Wall;
const int westMask = (int) AtmosDirection.West << (int) NavMapChunkType.Wall;
const int northMask = (int) AtmosDirection.North << (int) NavMapChunkType.Wall;
foreach (var (chunkOrigin, chunk) in _navMap.Chunks)
{
if (category != NavMapChunkType.Wall)
continue;
for (var i = 0; i < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; i++)
for (var i = 0; i < SharedNavMapSystem.ArraySize; i++)
{
var value = (ushort) Math.Pow(2, i);
var mask = _navMapSystem.GetCombinedEdgesForChunk(chunk.TileData) & value;
if (mask == 0x0)
var tileData = chunk.TileData[i] & SharedNavMapSystem.WallMask;
if (tileData == 0)
continue;
var relativeTile = SharedNavMapSystem.GetTile(mask);
tileData >>= (int) NavMapChunkType.Wall;
var relativeTile = SharedNavMapSystem.GetTileFromIndex(i);
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * _grid.TileSize;
if (!_navMapSystem.AllTileEdgesAreOccupied(chunk.TileData, relativeTile))
if (tileData != SharedNavMapSystem.AllDirMask)
{
AddRectForThinWall(chunk.TileData, tile);
AddRectForThinWall(tileData, tile);
continue;
}
tile = tile with { Y = -tile.Y };
NavMapChunk? neighborChunk;
bool neighbor;
// North edge
if (relativeTile.Y == SharedNavMapSystem.ChunkSize - 1)
{
neighbor = _navMap.Chunks.TryGetValue((NavMapChunkType.Wall, chunkOrigin + new Vector2i(0, 1)), out neighborChunk) &&
(neighborChunk.TileData[AtmosDirection.South] &
SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
}
else
{
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, 1));
neighbor = (chunk.TileData[AtmosDirection.South] & flag) != 0x0;
}
var neighborData = 0;
if (relativeTile.Y != SharedNavMapSystem.ChunkSize - 1)
neighborData = chunk.TileData[i+1];
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Up, out neighborChunk))
neighborData = neighborChunk.TileData[i + 1 - SharedNavMapSystem.ChunkSize];
if (!neighbor)
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile + new Vector2i(_grid.TileSize, -_grid.TileSize), HorizLinesLookup, HorizLinesLookupReversed);
if ((neighborData & southMask) == 0)
{
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize),
tile + new Vector2i(_grid.TileSize, -_grid.TileSize), _horizLines,
_horizLinesReversed);
}
// East edge
if (relativeTile.X == SharedNavMapSystem.ChunkSize - 1)
{
neighbor = _navMap.Chunks.TryGetValue((NavMapChunkType.Wall, chunkOrigin + new Vector2i(1, 0)), out neighborChunk) &&
(neighborChunk.TileData[AtmosDirection.West] &
SharedNavMapSystem.GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
}
else
{
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(1, 0));
neighbor = (chunk.TileData[AtmosDirection.West] & flag) != 0x0;
}
neighborData = 0;
if (relativeTile.X != SharedNavMapSystem.ChunkSize - 1)
neighborData = chunk.TileData[i+SharedNavMapSystem.ChunkSize];
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Right, out neighborChunk))
neighborData = neighborChunk.TileData[i + SharedNavMapSystem.ChunkSize - SharedNavMapSystem.ArraySize];
if (!neighbor)
AddOrUpdateNavMapLine(tile + new Vector2i(_grid.TileSize, -_grid.TileSize), tile + new Vector2i(_grid.TileSize, 0), VertLinesLookup, VertLinesLookupReversed);
if ((neighborData & westMask) == 0)
{
AddOrUpdateNavMapLine(tile + new Vector2i(_grid.TileSize, -_grid.TileSize),
tile + new Vector2i(_grid.TileSize, 0), _vertLines, _vertLinesReversed);
}
// South edge
if (relativeTile.Y == 0)
{
neighbor = _navMap.Chunks.TryGetValue((NavMapChunkType.Wall, chunkOrigin + new Vector2i(0, -1)), out neighborChunk) &&
(neighborChunk.TileData[AtmosDirection.North] &
SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, SharedNavMapSystem.ChunkSize - 1))) != 0x0;
}
else
{
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, -1));
neighbor = (chunk.TileData[AtmosDirection.North] & flag) != 0x0;
}
neighborData = 0;
if (relativeTile.Y != 0)
neighborData = chunk.TileData[i-1];
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Down, out neighborChunk))
neighborData = neighborChunk.TileData[i - 1 + SharedNavMapSystem.ChunkSize];
if (!neighbor)
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), HorizLinesLookup, HorizLinesLookupReversed);
if ((neighborData & northMask) == 0)
{
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), _horizLines,
_horizLinesReversed);
}
// West edge
if (relativeTile.X == 0)
{
neighbor = _navMap.Chunks.TryGetValue((NavMapChunkType.Wall, chunkOrigin + new Vector2i(-1, 0)), out neighborChunk) &&
(neighborChunk.TileData[AtmosDirection.East] &
SharedNavMapSystem.GetFlag(new Vector2i(SharedNavMapSystem.ChunkSize - 1, relativeTile.Y))) != 0x0;
}
else
{
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(-1, 0));
neighbor = (chunk.TileData[AtmosDirection.East] & flag) != 0x0;
}
neighborData = 0;
if (relativeTile.X != 0)
neighborData = chunk.TileData[i-SharedNavMapSystem.ChunkSize];
else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Left, out neighborChunk))
neighborData = neighborChunk.TileData[i - SharedNavMapSystem.ChunkSize + SharedNavMapSystem.ArraySize];
if (!neighbor)
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, VertLinesLookup, VertLinesLookupReversed);
if ((neighborData & eastMask) == 0)
{
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, _vertLines,
_vertLinesReversed);
}
// Add a diagonal line for interiors. Unless there are a lot of double walls, there is no point combining these
TileLines.Add((tile + new Vector2(0, -_grid.TileSize), tile + new Vector2(_grid.TileSize, 0)));
}
}
// Record the combined lines
foreach (var (origin, terminal) in HorizLinesLookup)
TileLines.Add((origin.Item2, terminal.Item2));
// Record the combined lines
foreach (var (origin, terminal) in _horizLines)
{
TileLines.Add((origin, terminal));
}
foreach (var (origin, terminal) in VertLinesLookup)
TileLines.Add((origin.Item2, terminal.Item2));
foreach (var (origin, terminal) in _vertLines)
{
TileLines.Add((origin, terminal));
}
}
private void UpdateNavMapAirlocks()
@@ -599,26 +593,23 @@ public partial class NavMapControl : MapGridControl
if (_navMap == null || _grid == null)
return;
foreach (var ((category, _), chunk) in _navMap.Chunks)
foreach (var chunk in _navMap.Chunks.Values)
{
if (category != NavMapChunkType.Airlock)
continue;
for (var i = 0; i < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; i++)
for (var i = 0; i < SharedNavMapSystem.ArraySize; i++)
{
var value = (int) Math.Pow(2, i);
var mask = _navMapSystem.GetCombinedEdgesForChunk(chunk.TileData) & value;
if (mask == 0x0)
var tileData = chunk.TileData[i] & SharedNavMapSystem.AirlockMask;
if (tileData == 0)
continue;
var relative = SharedNavMapSystem.GetTile(mask);
tileData >>= (int) NavMapChunkType.Airlock;
var relative = SharedNavMapSystem.GetTileFromIndex(i);
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relative) * _grid.TileSize;
// If the edges of an airlock tile are not all occupied, draw a thin airlock for each edge
if (!_navMapSystem.AllTileEdgesAreOccupied(chunk.TileData, relative))
if (tileData != SharedNavMapSystem.AllDirMask)
{
AddRectForThinAirlock(chunk.TileData, tile);
AddRectForThinAirlock(tileData, tile);
continue;
}
@@ -632,108 +623,90 @@ public partial class NavMapControl : MapGridControl
}
}
private void AddRectForThinWall(Dictionary<AtmosDirection, ushort> tileData, Vector2i tile)
private void AddRectForThinWall(int tileData, Vector2i tile)
{
if (_navMapSystem == null || _grid == null)
return;
var leftTop = new Vector2(-0.5f, 0.5f - ThinWallThickness);
var rightBottom = new Vector2(0.5f, 0.5f);
var leftTop = new Vector2(-0.5f, -0.5f + ThinWallThickness);
var rightBottom = new Vector2(0.5f, -0.5f);
foreach (var (direction, mask) in tileData)
for (var i = 0; i < SharedNavMapSystem.Directions; i++)
{
var relative = SharedMapSystem.GetChunkRelative(tile, SharedNavMapSystem.ChunkSize);
var flag = (ushort) SharedNavMapSystem.GetFlag(relative);
if ((mask & flag) == 0)
var dirMask = 1 << i;
if ((tileData & dirMask) == 0)
continue;
var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f);
var angle = new Angle(0);
switch (direction)
{
case AtmosDirection.East: angle = new Angle(MathF.PI * 0.5f); break;
case AtmosDirection.South: angle = new Angle(MathF.PI); break;
case AtmosDirection.West: angle = new Angle(MathF.PI * -0.5f); break;
}
// TODO NAVMAP
// Consider using faster rotation operations, given that these are always 90 degree increments
var angle = -((AtmosDirection) dirMask).ToAngle();
TileRects.Add((angle.RotateVec(leftTop) + tilePosition, angle.RotateVec(rightBottom) + tilePosition));
}
}
private void AddRectForThinAirlock(Dictionary<AtmosDirection, ushort> tileData, Vector2i tile)
private void AddRectForThinAirlock(int tileData, Vector2i tile)
{
if (_navMapSystem == null || _grid == null)
return;
var leftTop = new Vector2(-0.5f + FullWallInstep, 0.5f - FullWallInstep - ThinDoorThickness);
var rightBottom = new Vector2(0.5f - FullWallInstep, 0.5f - FullWallInstep);
var centreTop = new Vector2(0f, 0.5f - FullWallInstep - ThinDoorThickness);
var centreBottom = new Vector2(0f, 0.5f - FullWallInstep);
var leftTop = new Vector2(-0.5f + FullWallInstep, -0.5f + FullWallInstep + ThinDoorThickness);
var rightBottom = new Vector2(0.5f - FullWallInstep, -0.5f + FullWallInstep);
var centreTop = new Vector2(0f, -0.5f + FullWallInstep + ThinDoorThickness);
var centreBottom = new Vector2(0f, -0.5f + FullWallInstep);
foreach (var (direction, mask) in tileData)
for (var i = 0; i < SharedNavMapSystem.Directions; i++)
{
var relative = SharedMapSystem.GetChunkRelative(tile, SharedNavMapSystem.ChunkSize);
var flag = (ushort) SharedNavMapSystem.GetFlag(relative);
if ((mask & flag) == 0)
var dirMask = 1 << i;
if ((tileData & dirMask) == 0)
continue;
var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f);
var angle = new Angle(0);
switch (direction)
{
case AtmosDirection.East: angle = new Angle(MathF.PI * 0.5f);break;
case AtmosDirection.South: angle = new Angle(MathF.PI); break;
case AtmosDirection.West: angle = new Angle(MathF.PI * -0.5f); break;
}
var angle = -((AtmosDirection) dirMask).ToAngle();
TileRects.Add((angle.RotateVec(leftTop) + tilePosition, angle.RotateVec(rightBottom) + tilePosition));
TileLines.Add((angle.RotateVec(centreTop) + tilePosition, angle.RotateVec(centreBottom) + tilePosition));
}
}
protected void AddOrUpdateNavMapLine
(Vector2i origin,
protected void AddOrUpdateNavMapLine(
Vector2i origin,
Vector2i terminus,
Dictionary<(int, Vector2i), (int, Vector2i)> lookup,
Dictionary<(int, Vector2i), (int, Vector2i)> lookupReversed,
int index = 0)
Dictionary<Vector2i, Vector2i> lookup,
Dictionary<Vector2i, Vector2i> lookupReversed)
{
(int, Vector2i) foundTermiusTuple;
(int, Vector2i) foundOriginTuple;
Vector2i foundTermius;
Vector2i foundOrigin;
if (lookup.TryGetValue((index, terminus), out foundTermiusTuple) &&
lookupReversed.TryGetValue((index, origin), out foundOriginTuple))
// Does our new line end at the beginning of an existing line?
if (lookup.Remove(terminus, out foundTermius))
{
lookup[foundOriginTuple] = foundTermiusTuple;
lookupReversed[foundTermiusTuple] = foundOriginTuple;
DebugTools.Assert(lookupReversed[foundTermius] == terminus);
lookup.Remove((index, terminus));
lookupReversed.Remove((index, origin));
// Does our new line start at the end of an existing line?
if (lookupReversed.Remove(origin, out foundOrigin))
{
// Our new line just connects two existing lines
DebugTools.Assert(lookup[foundOrigin] == origin);
lookup[foundOrigin] = foundTermius;
lookupReversed[foundTermius] = foundOrigin;
}
else
{
// Our new line precedes an existing line, extending it further to the left
lookup[origin] = foundTermius;
lookupReversed[foundTermius] = origin;
}
return;
}
else if (lookup.TryGetValue((index, terminus), out foundTermiusTuple))
// Does our new line start at the end of an existing line?
if (lookupReversed.Remove(origin, out foundOrigin))
{
lookup[(index, origin)] = foundTermiusTuple;
lookup.Remove((index, terminus));
lookupReversed[foundTermiusTuple] = (index, origin);
// Our new line just extends an existing line further to the right
DebugTools.Assert(lookup[foundOrigin] == origin);
lookup[foundOrigin] = terminus;
lookupReversed[terminus] = foundOrigin;
return;
}
else if (lookupReversed.TryGetValue((index, origin), out foundOriginTuple))
{
lookupReversed[(index, terminus)] = foundOriginTuple;
lookupReversed.Remove(foundOriginTuple);
lookup[foundOriginTuple] = (index, terminus);
}
else
{
lookup.Add((index, origin), (index, terminus));
lookupReversed.Add((index, terminus), (index, origin));
}
// Completely disconnected line segment.
lookup.Add(origin, terminus);
lookupReversed.Add(terminus, origin);
}
protected Vector2 GetOffset()

View File

@@ -5,6 +5,7 @@ using Robust.Client.Graphics;
using Robust.Shared.Collections;
using Robust.Shared.Map.Components;
using System.Numerics;
using static Content.Shared.Power.SharedPowerMonitoringConsoleSystem;
namespace Content.Client.Power;
@@ -26,6 +27,11 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
public List<PowerMonitoringConsoleLine> PowerCableNetwork = new();
public List<PowerMonitoringConsoleLine> FocusCableNetwork = new();
private Dictionary<Vector2i, Vector2i>[] _horizLines = [new(), new(), new()];
private Dictionary<Vector2i, Vector2i>[] _horizLinesReversed = [new(), new(), new()];
private Dictionary<Vector2i, Vector2i>[] _vertLines = [new(), new(), new()];
private Dictionary<Vector2i, Vector2i>[] _vertLinesReversed = [new(), new(), new()];
private MapGridComponent? _grid;
public PowerMonitoringConsoleNavMapControl() : base()
@@ -182,28 +188,32 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
if (chunks == null)
return decodedOutput;
// We'll use the following dictionaries to combine collinear power cable lines
HorizLinesLookup.Clear();
HorizLinesLookupReversed.Clear();
VertLinesLookup.Clear();
VertLinesLookupReversed.Clear();
Array.ForEach(_horizLines, x=> x.Clear());
Array.ForEach(_horizLinesReversed, x=> x.Clear());
Array.ForEach(_vertLines, x=> x.Clear());
Array.ForEach(_vertLinesReversed, x=> x.Clear());
foreach ((var chunkOrigin, var chunk) in chunks)
foreach (var (chunkOrigin, chunk) in chunks)
{
for (int cableIdx = 0; cableIdx < chunk.PowerCableData.Length; cableIdx++)
for (var cableIdx = 0; cableIdx < 3; cableIdx++)
{
var horizLines = _horizLines[cableIdx];
var horizLinesReversed = _horizLinesReversed[cableIdx];
var vertLines = _vertLines[cableIdx];
var vertLinesReversed = _vertLinesReversed[cableIdx];
var chunkMask = chunk.PowerCableData[cableIdx];
for (var chunkIdx = 0; chunkIdx < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; chunkIdx++)
for (var chunkIdx = 0; chunkIdx < ChunkSize * ChunkSize; chunkIdx++)
{
var value = (int) Math.Pow(2, chunkIdx);
var value = 1 << chunkIdx;
var mask = chunkMask & value;
if (mask == 0x0)
continue;
var relativeTile = SharedNavMapSystem.GetTile(mask);
var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * _grid.TileSize;
var relativeTile = GetTileFromIndex(chunkIdx);
var tile = (chunk.Origin * ChunkSize + relativeTile) * _grid.TileSize;
tile = tile with { Y = -tile.Y };
PowerCableChunk neighborChunk;
@@ -212,39 +222,39 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
// Note: we only check the north and east neighbors
// East
if (relativeTile.X == SharedNavMapSystem.ChunkSize - 1)
if (relativeTile.X == ChunkSize - 1)
{
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(1, 0), out neighborChunk) &&
(neighborChunk.PowerCableData[cableIdx] & SharedNavMapSystem.GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
(neighborChunk.PowerCableData[cableIdx] & GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
}
else
{
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(1, 0));
var flag = GetFlag(relativeTile + new Vector2i(1, 0));
neighbor = (chunkMask & flag) != 0x0;
}
if (neighbor)
{
// Add points
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), HorizLinesLookup, HorizLinesLookupReversed, cableIdx);
AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), horizLines, horizLinesReversed);
}
// North
if (relativeTile.Y == SharedNavMapSystem.ChunkSize - 1)
if (relativeTile.Y == ChunkSize - 1)
{
neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(0, 1), out neighborChunk) &&
(neighborChunk.PowerCableData[cableIdx] & SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
(neighborChunk.PowerCableData[cableIdx] & GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
}
else
{
var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, 1));
var flag = GetFlag(relativeTile + new Vector2i(0, 1));
neighbor = (chunkMask & flag) != 0x0;
}
if (neighbor)
{
// Add points
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, VertLinesLookup, VertLinesLookupReversed, cableIdx);
AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, vertLines, vertLinesReversed);
}
}
@@ -253,11 +263,25 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
var gridOffset = new Vector2(_grid.TileSize * 0.5f, -_grid.TileSize * 0.5f);
foreach (var (origin, terminal) in HorizLinesLookup)
decodedOutput.Add(new PowerMonitoringConsoleLine(origin.Item2 + gridOffset, terminal.Item2 + gridOffset, (PowerMonitoringConsoleLineGroup) origin.Item1));
for (var index = 0; index < _horizLines.Length; index++)
{
var horizLines = _horizLines[index];
foreach (var (origin, terminal) in horizLines)
{
decodedOutput.Add(new PowerMonitoringConsoleLine(origin + gridOffset, terminal + gridOffset,
(PowerMonitoringConsoleLineGroup) index));
}
}
foreach (var (origin, terminal) in VertLinesLookup)
decodedOutput.Add(new PowerMonitoringConsoleLine(origin.Item2 + gridOffset, terminal.Item2 + gridOffset, (PowerMonitoringConsoleLineGroup) origin.Item1));
for (var index = 0; index < _vertLines.Length; index++)
{
var vertLines = _vertLines[index];
foreach (var (origin, terminal) in vertLines)
{
decodedOutput.Add(new PowerMonitoringConsoleLine(origin + gridOffset, terminal + gridOffset,
(PowerMonitoringConsoleLineGroup) index));
}
}
return decodedOutput;
}

View File

@@ -742,7 +742,6 @@ namespace Content.Client.Preferences.UI
CharacterSlot = _preferencesManager.Preferences.SelectedCharacterIndex;
UpdateAntagRequirements();
UpdateRoleRequirements();
UpdateControls();
ShowClothes.Pressed = true;
}

View File

@@ -1,5 +1,6 @@
<DefaultWindow xmlns="https://spacestation14.io" Title="{Loc 'ghost-target-window-title'}" MinSize="450 450" SetSize="450 450">
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="0.4">
<Button Name="GhostnadoButton" Text="{Loc 'ghost-target-window-warp-to-most-followed'}" HorizontalAlignment="Center" Margin="0 4" />
<LineEdit Name="SearchBar" PlaceHolder="Search" HorizontalExpand="True" Margin="0 4" />
<ScrollContainer VerticalExpand="True" HorizontalExpand="True" HScrollEnabled="False">
<BoxContainer Name="ButtonContainer" Orientation="Vertical" VerticalExpand="True" SeparationOverride="5">

View File

@@ -15,11 +15,14 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls
private string _searchText = string.Empty;
public event Action<NetEntity>? WarpClicked;
public event Action? OnGhostnadoClicked;
public GhostTargetWindow()
{
RobustXamlLoader.Load(this);
SearchBar.OnTextChanged += OnSearchTextChanged;
GhostnadoButton.OnPressed += _ => OnGhostnadoClicked?.Invoke();
}
public void UpdateWarps(IEnumerable<GhostWarp> warps)

View File

@@ -111,6 +111,12 @@ public sealed class GhostUIController : UIController, IOnSystemChanged<GhostSyst
_net.SendSystemNetworkMessage(msg);
}
private void OnGhostnadoClicked()
{
var msg = new GhostnadoRequestEvent();
_net.SendSystemNetworkMessage(msg);
}
public void LoadGui()
{
if (Gui == null)
@@ -120,6 +126,7 @@ public sealed class GhostUIController : UIController, IOnSystemChanged<GhostSyst
Gui.ReturnToBodyPressed += ReturnToBody;
Gui.GhostRolesPressed += GhostRolesPressed;
Gui.TargetWindow.WarpClicked += OnWarpClicked;
Gui.TargetWindow.OnGhostnadoClicked += OnGhostnadoClicked;
UpdateGui();
}

View File

@@ -6,6 +6,8 @@ namespace Content.Client.Weapons.Melee.Components;
[RegisterComponent]
public sealed partial class WeaponArcVisualsComponent : Component
{
public EntityUid? User;
[DataField("animation")]
public WeaponArcAnimation Animation = WeaponArcAnimation.None;

View File

@@ -1,4 +1,5 @@
using System.Numerics;
using Content.Client.Animations;
using Content.Client.Weapons.Melee.Components;
using Content.Shared.Weapons.Melee;
using Robust.Client.Animations;
@@ -61,45 +62,48 @@ public sealed partial class MeleeWeaponSystem
length = meleeWeaponComponent.CPAnimationLength; //CrystallPunk Melee upgrade
offset = meleeWeaponComponent.CPAnimationOffset; //CrystallPunk Melee upgrade
}
sprite.NoRotation = true;
sprite.Rotation = localPos.ToWorldAngle();
var distance = Math.Clamp(localPos.Length() / 2f, 0.2f, 1f);
var xform = _xformQuery.GetComponent(animationUid);
TrackUserComponent track;
switch (arcComponent.Animation)
{
case WeaponArcAnimation.Slash:
track = EnsureComp<TrackUserComponent>(animationUid);
track.User = user;
_animation.Play(animationUid, GetSlashAnimation(sprite, angle, spriteRotation), SlashAnimationKey);
TransformSystem.SetParent(animationUid, xform, user, userXform);
if (arcComponent.Fadeout)
_animation.Play(animationUid, GetFadeAnimation(sprite, 0.065f, 0.065f + 0.05f), FadeAnimationKey);
break;
case WeaponArcAnimation.Thrust:
track = EnsureComp<TrackUserComponent>(animationUid);
track.User = user;
_animation.Play(animationUid, GetThrustAnimation(sprite, distance, spriteRotation), ThrustAnimationKey);
TransformSystem.SetParent(animationUid, xform, user, userXform);
if (arcComponent.Fadeout)
_animation.Play(animationUid, GetFadeAnimation(sprite, 0.05f, 0.15f), FadeAnimationKey);
break;
case WeaponArcAnimation.None:
var (mapPos, mapRot) = TransformSystem.GetWorldPositionRotation(userXform);
TransformSystem.AttachToGridOrMap(animationUid, xform);
var worldPos = mapPos + (mapRot - userXform.LocalRotation).RotateVec(localPos);
var newLocalPos = TransformSystem.GetInvWorldMatrix(xform.ParentUid).Transform(worldPos);
TransformSystem.SetLocalPositionNoLerp(xform, newLocalPos);
TransformSystem.SetLocalPositionNoLerp(animationUid, newLocalPos, xform);
if (arcComponent.Fadeout)
_animation.Play(animationUid, GetFadeAnimation(sprite, 0f, 0.15f), FadeAnimationKey);
break;
//CrystallPunk MeleeUpgrade
case WeaponArcAnimation.CPSlash:
track = EnsureComp<TrackUserComponent>(animationUid);
track.User = user;
_animation.Play(animationUid, CPGetSlashAnimation(sprite, angle, spriteRotation, length, offset), SlashAnimationKey);
TransformSystem.SetParent(animationUid, xform, user, userXform);
if (arcComponent.Fadeout)
_animation.Play(animationUid, GetFadeAnimation(sprite, length * 0.5f, length + 0.15f), FadeAnimationKey);
break;
case WeaponArcAnimation.CPThrust:
track = EnsureComp<TrackUserComponent>(animationUid);
track.User = user;
_animation.Play(animationUid, CPGetThrustAnimation(sprite, -offset, spriteRotation, length), ThrustAnimationKey);
TransformSystem.SetParent(animationUid, xform, user, userXform);
if (arcComponent.Fadeout)
_animation.Play(animationUid, GetFadeAnimation(sprite, 0f, 0.15f), FadeAnimationKey);
break;
@@ -118,7 +122,6 @@ public sealed partial class MeleeWeaponSystem
var endRotationOffset = endRotation.RotateVec(new Vector2(0f, -1f));
startRotation += spriteRotation;
endRotation += spriteRotation;
sprite.NoRotation = true;
return new Animation()
{
@@ -218,7 +221,7 @@ public sealed partial class MeleeWeaponSystem
InterpolationMode = AnimationInterpolationMode.Linear,
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(Vector2.Zero, 0f), //CrystallPunk MeleeUpgrade
new AnimationTrackProperty.KeyFrame(Vector2.Zero, 0f), //CrystallPunk MeleeUpgrade
new AnimationTrackProperty.KeyFrame(direction.Normalized() * 0.15f, length*0.4f), //CrystallPunk MeleeUpgrade
new AnimationTrackProperty.KeyFrame(Vector2.Zero, length*0.8f) //CrystallPunk MeleeUpgrade
}
@@ -227,6 +230,29 @@ public sealed partial class MeleeWeaponSystem
};
}
/// <summary>
/// Updates the effect positions to follow the user
/// </summary>
private void UpdateEffects()
{
var query = EntityQueryEnumerator<TrackUserComponent, TransformComponent>();
while (query.MoveNext(out var arcComponent, out var xform))
{
if (arcComponent.User == null)
continue;
Vector2 targetPos = TransformSystem.GetWorldPosition(arcComponent.User.Value);
if (arcComponent.Offset != Vector2.Zero)
{
var entRotation = TransformSystem.GetWorldRotation(xform);
targetPos += entRotation.RotateVec(arcComponent.Offset);
}
TransformSystem.SetWorldPosition(xform, targetPos);
}
}
//CrystallPunk MeleeUpgrade start
private Animation CPGetSlashAnimation(SpriteComponent sprite, Angle arc, Angle spriteRotation, float length, float offset = -1f)
{

View File

@@ -43,6 +43,12 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
UpdatesOutsidePrediction = true;
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
UpdateEffects();
}
public override void Update(float frameTime)
{
base.Update(frameTime);

View File

@@ -1,4 +1,5 @@
using System.Numerics;
using Content.Client.Animations;
using Content.Client.Items;
using Content.Client.Weapons.Ranged.Components;
using Content.Shared.Camera;
@@ -15,6 +16,7 @@ using Robust.Client.Player;
using Robust.Shared.Animations;
using Robust.Shared.Input;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using SharedGunSystem = Content.Shared.Weapons.Ranged.Systems.SharedGunSystem;
@@ -24,13 +26,14 @@ namespace Content.Client.Weapons.Ranged.Systems;
public sealed partial class GunSystem : SharedGunSystem
{
[Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly AnimationPlayerSystem _animPlayer = default!;
[Dependency] private readonly InputSystem _inputSystem = default!;
[Dependency] private readonly SharedCameraRecoilSystem _recoil = default!;
[Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly SharedMapSystem _maps = default!;
[ValidatePrototypeId<EntityPrototype>]
public const string HitscanProto = "HitscanEffect";
@@ -123,7 +126,7 @@ public sealed partial class GunSystem : SharedGunSystem
}
};
_animPlayer.Play(ent, null, anim, "hitscan-effect");
_animPlayer.Play(ent, anim, "hitscan-effect");
}
}
@@ -189,6 +192,7 @@ public sealed partial class GunSystem : SharedGunSystem
// to just delete the spawned entities. This is for programmer sanity despite the wasted perf.
// This also means any ammo specific stuff can be grabbed as necessary.
var direction = fromCoordinates.ToMapPos(EntityManager, TransformSystem) - toCoordinates.ToMapPos(EntityManager, TransformSystem);
var worldAngle = direction.ToAngle().Opposite();
foreach (var (ent, shootable) in ammo)
{
@@ -208,7 +212,7 @@ public sealed partial class GunSystem : SharedGunSystem
if (!cartridge.Spent)
{
SetCartridgeSpent(ent!.Value, cartridge, true);
MuzzleFlash(gunUid, cartridge, user);
MuzzleFlash(gunUid, cartridge, worldAngle, user);
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
Recoil(user, direction, gun.CameraRecoilScalarModified);
// TODO: Can't predict entity deletions.
@@ -226,7 +230,7 @@ public sealed partial class GunSystem : SharedGunSystem
break;
case AmmoComponent newAmmo:
MuzzleFlash(gunUid, newAmmo, user);
MuzzleFlash(gunUid, newAmmo, worldAngle, user);
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
Recoil(user, direction, gun.CameraRecoilScalarModified);
if (IsClientSide(ent!.Value))
@@ -258,33 +262,41 @@ public sealed partial class GunSystem : SharedGunSystem
PopupSystem.PopupEntity(message, uid.Value, user.Value);
}
protected override void CreateEffect(EntityUid uid, MuzzleFlashEvent message, EntityUid? user = null)
protected override void CreateEffect(EntityUid gunUid, MuzzleFlashEvent message, EntityUid? user = null)
{
if (!Timing.IsFirstTimePredicted)
return;
var gunXform = Transform(gunUid);
var gridUid = gunXform.GridUid;
EntityCoordinates coordinates;
if (message.MatchRotation)
coordinates = new EntityCoordinates(uid, Vector2.Zero);
else if (TryComp<TransformComponent>(uid, out var xform))
coordinates = xform.Coordinates;
if (TryComp(gridUid, out MapGridComponent? mapGrid))
{
coordinates = new EntityCoordinates(gridUid.Value, _maps.LocalToGrid(gridUid.Value, mapGrid, gunXform.Coordinates));
}
else if (gunXform.MapUid != null)
{
coordinates = new EntityCoordinates(gunXform.MapUid.Value, TransformSystem.GetWorldPosition(gunXform));
}
else
{
return;
if (!coordinates.IsValid(EntityManager))
return;
}
var ent = Spawn(message.Prototype, coordinates);
TransformSystem.SetWorldRotationNoLerp(ent, message.Angle);
var effectXform = Transform(ent);
TransformSystem.SetLocalPositionRotation(effectXform,
effectXform.LocalPosition + new Vector2(0f, -0.5f),
effectXform.LocalRotation - MathF.PI / 2);
if (user != null)
{
var track = EnsureComp<TrackUserComponent>(ent);
track.User = user;
track.Offset = Vector2.UnitX / 2f;
}
var lifetime = 0.4f;
if (TryComp<TimedDespawnComponent>(uid, out var despawn))
if (TryComp<TimedDespawnComponent>(gunUid, out var despawn))
{
lifetime = despawn.Lifetime;
}
@@ -309,18 +321,17 @@ public sealed partial class GunSystem : SharedGunSystem
};
_animPlayer.Play(ent, anim, "muzzle-flash");
if (!TryComp(uid, out PointLightComponent? light))
if (!TryComp(gunUid, out PointLightComponent? light))
{
light = (PointLightComponent) _factory.GetComponent(typeof(PointLightComponent));
light.Owner = uid;
light.NetSyncEnabled = false;
AddComp(uid, light);
AddComp(gunUid, light);
}
Lights.SetEnabled(uid, true, light);
Lights.SetRadius(uid, 2f, light);
Lights.SetColor(uid, Color.FromHex("#cc8e2b"), light);
Lights.SetEnergy(uid, 5f, light);
Lights.SetEnabled(gunUid, true, light);
Lights.SetRadius(gunUid, 2f, light);
Lights.SetColor(gunUid, Color.FromHex("#cc8e2b"), light);
Lights.SetEnergy(gunUid, 5f, light);
var animTwo = new Animation()
{
@@ -352,9 +363,9 @@ public sealed partial class GunSystem : SharedGunSystem
}
};
var uidPlayer = EnsureComp<AnimationPlayerComponent>(uid);
var uidPlayer = EnsureComp<AnimationPlayerComponent>(gunUid);
_animPlayer.Stop(uid, uidPlayer, "muzzle-flash-light");
_animPlayer.Play(uid, uidPlayer, animTwo,"muzzle-flash-light");
_animPlayer.Stop(gunUid, uidPlayer, "muzzle-flash-light");
_animPlayer.Play((gunUid, uidPlayer), animTwo,"muzzle-flash-light");
}
}

View File

@@ -44,9 +44,11 @@ public sealed partial class AdminVerbSystem
if (!_adminManager.HasAdminFlag(player, AdminFlags.Fun))
return;
if (!HasComp<MindContainerComponent>(args.Target))
if (!HasComp<MindContainerComponent>(args.Target) || !TryComp<ActorComponent>(args.Target, out var targetActor))
return;
var targetPlayer = targetActor.PlayerSession;
Verb traitor = new()
{
Text = Loc.GetString("admin-verb-text-make-traitor"),
@@ -54,7 +56,7 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Structures/Wallmounts/posters.rsi"), "poster5_contraband"),
Act = () =>
{
_antag.ForceMakeAntag<TraitorRuleComponent>(player, DefaultTraitorRule);
_antag.ForceMakeAntag<TraitorRuleComponent>(targetPlayer, DefaultTraitorRule);
},
Impact = LogImpact.High,
Message = Loc.GetString("admin-verb-make-traitor"),
@@ -83,7 +85,7 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Rsi(new("/Textures/Structures/Wallmounts/signs.rsi"), "radiation"),
Act = () =>
{
_antag.ForceMakeAntag<NukeopsRuleComponent>(player, DefaultNukeOpRule);
_antag.ForceMakeAntag<NukeopsRuleComponent>(targetPlayer, DefaultNukeOpRule);
},
Impact = LogImpact.High,
Message = Loc.GetString("admin-verb-make-nuclear-operative"),
@@ -112,7 +114,7 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/job_icons.rsi"), "HeadRevolutionary"),
Act = () =>
{
_antag.ForceMakeAntag<RevolutionaryRuleComponent>(player, DefaultRevsRule);
_antag.ForceMakeAntag<RevolutionaryRuleComponent>(targetPlayer, DefaultRevsRule);
},
Impact = LogImpact.High,
Message = Loc.GetString("admin-verb-make-head-rev"),
@@ -126,7 +128,7 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Clothing/Hands/Gloves/Color/black.rsi"), "icon"),
Act = () =>
{
_antag.ForceMakeAntag<ThiefRuleComponent>(player, DefaultThiefRule);
_antag.ForceMakeAntag<ThiefRuleComponent>(targetPlayer, DefaultThiefRule);
},
Impact = LogImpact.High,
Message = Loc.GetString("admin-verb-make-thief"),

View File

@@ -1,4 +1,6 @@
namespace Content.Server.Atmos.Components
using Content.Shared.Atmos;
namespace Content.Server.Atmos.Components
{
[RegisterComponent]
public sealed partial class GasMixtureHolderComponent : Component, IGasMixtureHolder

View File

@@ -1,3 +1,4 @@
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems;

View File

@@ -36,7 +36,7 @@ namespace Content.Server.Atmos.EntitySystems
airtight.Comp.CurrentAirBlockedDirection =
(int) Rotate((AtmosDirection) airtight.Comp.InitialAirBlockedDirection, xform.LocalRotation);
UpdatePosition(airtight, xform);
var airtightEv = new AirtightChanged(airtight, airtight, default);
var airtightEv = new AirtightChanged(airtight, airtight, false, default);
RaiseLocalEvent(airtight, ref airtightEv, true);
}
@@ -64,7 +64,7 @@ namespace Content.Server.Atmos.EntitySystems
airtight.LastPosition = (gridId.Value, tilePos);
InvalidatePosition(gridId.Value, tilePos);
var airtightEv = new AirtightChanged(uid, airtight, (gridId.Value, tilePos));
var airtightEv = new AirtightChanged(uid, airtight, false, (gridId.Value, tilePos));
RaiseLocalEvent(uid, ref airtightEv, true);
}
@@ -76,7 +76,7 @@ namespace Content.Server.Atmos.EntitySystems
airtight.LastPosition = (gridId, args.TilePos);
InvalidatePosition(gridId, args.TilePos);
var airtightEv = new AirtightChanged(uid, airtight, (gridId, args.TilePos));
var airtightEv = new AirtightChanged(uid, airtight, false, (gridId, args.TilePos));
RaiseLocalEvent(uid, ref airtightEv, true);
}
}
@@ -87,7 +87,7 @@ namespace Content.Server.Atmos.EntitySystems
airtight.CurrentAirBlockedDirection = (int) Rotate((AtmosDirection)airtight.InitialAirBlockedDirection, ev.NewRotation);
var pos = airtight.LastPosition;
UpdatePosition(ent, ev.Component);
var airtightEv = new AirtightChanged(owner, airtight, pos);
var airtightEv = new AirtightChanged(owner, airtight, false, pos);
RaiseLocalEvent(owner, ref airtightEv, true);
}
@@ -102,7 +102,7 @@ namespace Content.Server.Atmos.EntitySystems
var pos = airtight.Comp.LastPosition;
airtight.Comp.AirBlocked = airblocked;
UpdatePosition(airtight, xform);
var airtightEv = new AirtightChanged(airtight, airtight, pos);
var airtightEv = new AirtightChanged(airtight, airtight, true, pos);
RaiseLocalEvent(airtight, ref airtightEv, true);
}
@@ -149,6 +149,13 @@ namespace Content.Server.Atmos.EntitySystems
}
}
/// <summary>
/// Raised upon the airtight status being changed via anchoring, movement, etc.
/// </summary>
/// <param name="Entity"></param>
/// <param name="Airtight"></param>
/// <param name="AirBlockedChanged">Whether the <see cref="AirtightComponent.AirBlocked"/> changed</param>
/// <param name="Position"></param>
[ByRefEvent]
public readonly record struct AirtightChanged(EntityUid Entity, AirtightComponent Airtight, (EntityUid Grid, Vector2i Tile) Position);
public readonly record struct AirtightChanged(EntityUid Entity, AirtightComponent Airtight, bool AirBlockedChanged, (EntityUid Grid, Vector2i Tile) Position);
}

View File

@@ -1,3 +1,4 @@
using Content.Shared.Atmos;
using Robust.Shared.Map;
namespace Content.Server.Atmos.EntitySystems

View File

@@ -1,10 +1,10 @@
using System.Linq;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.Piping.Components;
using Content.Server.Atmos.Reactions;
using Content.Server.NodeContainer.NodeGroups;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.Reactions;
using Robust.Shared.Map.Components;
using Robust.Shared.Utility;

View File

@@ -2,6 +2,7 @@ using System.Linq;
using System.Runtime.CompilerServices;
using Content.Server.Atmos.Reactions;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Reactions;
using Robust.Shared.Prototypes;
using DependencyAttribute = Robust.Shared.IoC.DependencyAttribute;

View File

@@ -2,6 +2,7 @@ using Content.Server.Atmos.Components;
using Content.Server.Atmos.Reactions;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.Reactions;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Utility;

View File

@@ -2,6 +2,7 @@ using Content.Server.Atmos.Components;
using Content.Server.Atmos.Reactions;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.Reactions;
using Content.Shared.Audio;
using Content.Shared.Database;
using Robust.Shared.Audio;

View File

@@ -1,4 +1,5 @@
using Content.Server.Atmos.Components;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Robust.Shared.GameStates;
using Robust.Shared.Map.Components;

View File

@@ -95,9 +95,9 @@ namespace Content.Server.Atmos.EntitySystems
component.Enabled = true;
Dirty(uid, component);
UpdateAppearance(uid, component);
if (!HasComp<ActiveGasAnalyzerComponent>(uid))
AddComp<ActiveGasAnalyzerComponent>(uid);
EnsureComp<ActiveGasAnalyzerComponent>(uid);
UpdateAnalyzer(uid, component);
OpenUserInterface(uid, user, component);
}
/// <summary>

View File

@@ -1,4 +1,6 @@
namespace Content.Server.Atmos
using Content.Shared.Atmos;
namespace Content.Server.Atmos
{
public interface IGasMixtureHolder
{

View File

@@ -1,5 +1,6 @@
using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Reactions;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Reactions;
namespace Content.Server.Atmos
{

View File

@@ -1,3 +1,5 @@
using Content.Shared.Atmos;
namespace Content.Server.Atmos.Piping.Binary.Components
{
[RegisterComponent]

View File

@@ -13,6 +13,7 @@ using Content.Server.NodeContainer.NodeGroups;
using Content.Server.Audio;
using Content.Server.Administration.Logs;
using Content.Server.NodeContainer.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Database;
namespace Content.Server.Atmos.Portable

View File

@@ -1,5 +1,6 @@
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Reactions;
using JetBrains.Annotations;
namespace Content.Server.Atmos.Reactions;

View File

@@ -1,5 +1,6 @@
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Reactions;
using JetBrains.Annotations;
namespace Content.Server.Atmos.Reactions;

View File

@@ -1,5 +1,6 @@
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Reactions;
using JetBrains.Annotations;
namespace Content.Server.Atmos.Reactions;

View File

@@ -1,22 +1,10 @@
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Reactions;
using Robust.Shared.Prototypes;
namespace Content.Server.Atmos.Reactions
{
[Flags]
public enum ReactionResult : byte
{
NoReaction = 0,
Reacting = 1,
StopReactions = 2,
}
public enum GasReaction : byte
{
Fire = 0,
}
[Prototype("gasReaction")]
public sealed partial class GasReactionPrototype : IPrototype
{

View File

@@ -1,5 +1,6 @@
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Reactions;
using JetBrains.Annotations;
namespace Content.Server.Atmos.Reactions;

View File

@@ -1,5 +1,6 @@
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Reactions;
using JetBrains.Annotations;
namespace Content.Server.Atmos.Reactions

View File

@@ -1,5 +1,6 @@
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Reactions;
using JetBrains.Annotations;
namespace Content.Server.Atmos.Reactions

View File

@@ -1,5 +1,7 @@
using Content.Server.Atmos.EntitySystems;
using Content.Server.Fluids.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Reactions;
using Content.Shared.Chemistry.Components;
using Content.Shared.FixedPoint;
using Content.Shared.Maps;

View File

@@ -1,4 +1,5 @@
using System.Globalization;
using Content.Shared.Atmos;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Markdown;

View File

@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using Content.Shared.Atmos;
namespace Content.Server.Atmos;

View File

@@ -6,6 +6,7 @@ using Content.Server.Fluids.Components;
using Content.Server.Ghost.Roles.Components;
using Content.Server.Kitchen.Components;
using Content.Server.Popups;
using Content.Shared.Atmos;
using Content.Shared.Botany;
using Content.Shared.Burial.Components;
using Content.Shared.Chemistry.Reagent;

View File

@@ -1,4 +1,5 @@
using Content.Server.Atmos;
using Content.Shared.Atmos;
using JetBrains.Annotations;
namespace Content.Server.Destructible.Thresholds.Behaviors;

View File

@@ -1,5 +1,6 @@
using Content.Server.Atmos;
using Content.Server.Disposal.Tube.Components;
using Content.Shared.Atmos;
using Robust.Shared.Containers;
namespace Content.Server.Disposal.Unit.Components

View File

@@ -11,7 +11,8 @@ public sealed partial class ExplosionSystem : EntitySystem
public int ThrowLimit { get; private set; }
public bool SleepNodeSys { get; private set; }
public bool IncrementalTileBreaking { get; private set; }
public int SingleTickAreaLimit {get; private set; }
public int SingleTickAreaLimit { get; private set; }
public bool CanCreateVacuum { get; private set; }
private void SubscribeCvars()
{
@@ -23,5 +24,6 @@ public sealed partial class ExplosionSystem : EntitySystem
Subs.CVar(_cfg, CCVars.ExplosionMaxProcessingTime, value => MaxProcessingTime = value, true);
Subs.CVar(_cfg, CCVars.ExplosionIncrementalTileBreaking, value => IncrementalTileBreaking = value, true);
Subs.CVar(_cfg, CCVars.ExplosionSingleTickAreaLimit, value => SingleTickAreaLimit = value, true);
Subs.CVar(_cfg, CCVars.ExplosionCanCreateVacuum, value => CanCreateVacuum = value, true);
}
}

View File

@@ -1,4 +1,5 @@
using System.Numerics;
using Content.Server.Atmos.EntitySystems;
using Content.Shared.CCVar;
using Content.Shared.Damage;
using Content.Shared.Explosion;
@@ -16,7 +17,6 @@ using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
using Content.Server.Atmos.EntitySystems;
namespace Content.Server.Explosion.EntitySystems;
@@ -490,7 +490,9 @@ public sealed partial class ExplosionSystem
if (_tileDefinitionManager[tileRef.Tile.TypeId] is not ContentTileDefinition tileDef)
return;
if (tileDef.MapAtmosphere)
if (!CanCreateVacuum)
canCreateVacuum = false;
else if (tileDef.MapAtmosphere)
canCreateVacuum = true; // is already a vacuum.
int tileBreakages = 0;

View File

@@ -144,7 +144,7 @@ public sealed class SpraySystem : EntitySystem
_audio.PlayPvs(entity.Comp.SpraySound, entity, entity.Comp.SpraySound.Params.WithVariation(0.125f));
_useDelay.SetLength((entity, useDelay), TimeSpan.FromSeconds(cooldownTime));
_useDelay.SetLength(entity.Owner, TimeSpan.FromSeconds(cooldownTime));
_useDelay.TryResetDelay((entity, useDelay));
}
}

View File

@@ -43,10 +43,16 @@ namespace Content.Server.Ghost
[Dependency] private readonly TransformSystem _transformSystem = default!;
[Dependency] private readonly VisibilitySystem _visibilitySystem = default!;
private EntityQuery<GhostComponent> _ghostQuery;
private EntityQuery<PhysicsComponent> _physicsQuery;
public override void Initialize()
{
base.Initialize();
_ghostQuery = GetEntityQuery<GhostComponent>();
_physicsQuery = GetEntityQuery<PhysicsComponent>();
SubscribeLocalEvent<GhostComponent, ComponentStartup>(OnGhostStartup);
SubscribeLocalEvent<GhostComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<GhostComponent, ComponentShutdown>(OnGhostShutdown);
@@ -62,6 +68,7 @@ namespace Content.Server.Ghost
SubscribeNetworkEvent<GhostWarpsRequestEvent>(OnGhostWarpsRequest);
SubscribeNetworkEvent<GhostReturnToBodyRequest>(OnGhostReturnToBodyRequest);
SubscribeNetworkEvent<GhostWarpToTargetRequestEvent>(OnGhostWarpToTargetRequest);
SubscribeNetworkEvent<GhostnadoRequestEvent>(OnGhostnadoRequest);
SubscribeLocalEvent<GhostComponent, BooActionEvent>(OnActionPerform);
SubscribeLocalEvent<GhostComponent, ToggleGhostHearingActionEvent>(OnGhostHearingAction);
@@ -241,7 +248,7 @@ namespace Content.Server.Ghost
private void OnGhostReturnToBodyRequest(GhostReturnToBodyRequest msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity is not {Valid: true} attached
|| !TryComp(attached, out GhostComponent? ghost)
|| !_ghostQuery.TryComp(attached, out var ghost)
|| !ghost.CanReturnToBody
|| !TryComp(attached, out ActorComponent? actor))
{
@@ -257,7 +264,7 @@ namespace Content.Server.Ghost
private void OnGhostWarpsRequest(GhostWarpsRequestEvent msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity is not {Valid: true} entity
|| !HasComp<GhostComponent>(entity))
|| !_ghostQuery.HasComp(entity))
{
Log.Warning($"User {args.SenderSession.Name} sent a {nameof(GhostWarpsRequestEvent)} without being a ghost.");
return;
@@ -270,7 +277,7 @@ namespace Content.Server.Ghost
private void OnGhostWarpToTargetRequest(GhostWarpToTargetRequestEvent msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity is not {Valid: true} attached
|| !TryComp(attached, out GhostComponent? _))
|| !_ghostQuery.HasComp(attached))
{
Log.Warning($"User {args.SenderSession.Name} tried to warp to {msg.Target} without being a ghost.");
return;
@@ -284,17 +291,37 @@ namespace Content.Server.Ghost
return;
}
if ((TryComp(target, out WarpPointComponent? warp) && warp.Follow) || HasComp<MobStateComponent>(target))
WarpTo(attached, target);
}
private void OnGhostnadoRequest(GhostnadoRequestEvent msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity is not {} uid
|| !_ghostQuery.HasComp(uid))
{
_followerSystem.StartFollowingEntity(attached, target);
Log.Warning($"User {args.SenderSession.Name} tried to ghostnado without being a ghost.");
return;
}
var xform = Transform(attached);
_transformSystem.SetCoordinates(attached, xform, Transform(target).Coordinates);
_transformSystem.AttachToGridOrMap(attached, xform);
if (TryComp(attached, out PhysicsComponent? physics))
_physics.SetLinearVelocity(attached, Vector2.Zero, body: physics);
if (_followerSystem.GetMostFollowed() is not {} target)
return;
WarpTo(uid, target);
}
private void WarpTo(EntityUid uid, EntityUid target)
{
if ((TryComp(target, out WarpPointComponent? warp) && warp.Follow) || HasComp<MobStateComponent>(target))
{
_followerSystem.StartFollowingEntity(uid, target);
return;
}
var xform = Transform(uid);
_transformSystem.SetCoordinates(uid, xform, Transform(target).Coordinates);
_transformSystem.AttachToGridOrMap(uid, xform);
if (_physicsQuery.TryComp(uid, out var physics))
_physics.SetLinearVelocity(uid, Vector2.Zero, body: physics);
}
private IEnumerable<GhostWarp> GetLocationWarps()

View File

@@ -20,6 +20,7 @@ using Robust.Shared.GameStates;
using Robust.Shared.Input.Binding;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -28,6 +29,7 @@ namespace Content.Server.Hands.Systems
public sealed class HandsSystem : SharedHandsSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly StackSystem _stackSystem = default!;
[Dependency] private readonly VirtualItemSystem _virtualItemSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
@@ -91,7 +93,8 @@ namespace Content.Server.Hands.Systems
if (TryComp(uid, out PullerComponent? puller) && TryComp(puller.Pulling, out PullableComponent? pullable))
_pullingSystem.TryStopPull(puller.Pulling.Value, pullable);
if (!_handsSystem.TryDrop(uid, component.ActiveHand!, null, checkActionBlocker: false))
var offsetRandomCoordinates = _transformSystem.GetMoverCoordinates(args.Target).Offset(_random.NextVector2(1f, 1.5f));
if (!ThrowHeldItem(args.Target, offsetRandomCoordinates))
return;
args.PopupPrefix = "disarm-action-";

View File

@@ -271,6 +271,9 @@ namespace Content.Server.Kitchen.EntitySystems
private void OnContentUpdate(EntityUid uid, MicrowaveComponent component, ContainerModifiedMessage args) // For some reason ContainerModifiedMessage just can't be used at all with Entity<T>. TODO: replace with Entity<T> syntax once that's possible
{
if (component.Storage != args.Container)
return;
UpdateUserInterfaceState(uid, component);
}

View File

@@ -8,6 +8,7 @@ using Content.Server.Materials;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Stack;
using Content.Shared.Atmos;
using Content.Shared.UserInterface;
using Content.Shared.Database;
using Content.Shared.Emag.Components;

View File

@@ -1,4 +1,5 @@
using Content.Server.Atmos;
using Content.Shared.Atmos;
namespace Content.Server.Mech.Components;

View File

@@ -13,6 +13,7 @@ using Content.Server.NodeContainer.NodeGroups;
using Content.Server.NodeContainer.Nodes;
using Content.Server.Power.Components;
using Content.Server.Temperature.Components;
using Content.Shared.Atmos;
using Content.Shared.UserInterface;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components;

View File

@@ -13,6 +13,7 @@ using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Nutrition;
using System.Threading;
using Content.Shared.Atmos;
/// <summary>
/// System for vapes

View File

@@ -12,7 +12,6 @@ using JetBrains.Annotations;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System.Diagnostics.CodeAnalysis;
namespace Content.Server.Pinpointer;
@@ -28,14 +27,27 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly ITileDefinitionManager _tileDefManager = default!;
public const float CloseDistance = 15f;
public const float FarDistance = 30f;
private EntityQuery<AirtightComponent> _airtightQuery;
private EntityQuery<MapGridComponent> _gridQuery;
private EntityQuery<NavMapComponent> _navQuery;
public override void Initialize()
{
base.Initialize();
var categories = Enum.GetNames(typeof(NavMapChunkType)).Length - 1; // -1 due to "Invalid" entry.
if (Categories != categories)
throw new Exception($"{nameof(Categories)} must be equal to the number of chunk types");
_airtightQuery = GetEntityQuery<AirtightComponent>();
_gridQuery = GetEntityQuery<MapGridComponent>();
_navQuery = GetEntityQuery<NavMapComponent>();
// Initialization events
SubscribeLocalEvent<StationGridAddedEvent>(OnStationInit);
@@ -43,8 +55,7 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
SubscribeLocalEvent<GridSplitEvent>(OnNavMapSplit);
SubscribeLocalEvent<TileChangedEvent>(OnTileChanged);
// Airtight structure change event
SubscribeLocalEvent<AirtightChanged>(OnAirtightChanged);
SubscribeLocalEvent<AirtightChanged>(OnAirtightChange);
// Beacon events
SubscribeLocalEvent<NavMapBeaconComponent, MapInitEvent>(OnNavMapBeaconMapInit);
@@ -54,82 +65,96 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
SubscribeLocalEvent<ConfigurableNavMapBeaconComponent, ExaminedEvent>(OnConfigurableExamined);
}
#region: Initialization event handling
private void OnStationInit(StationGridAddedEvent ev)
{
var comp = EnsureComp<NavMapComponent>(ev.GridId);
RefreshGrid(ev.GridId, comp, Comp<MapGridComponent>(ev.GridId));
}
#endregion
#region: Grid change event handling
private void OnNavMapSplit(ref GridSplitEvent args)
{
if (!TryComp(args.Grid, out NavMapComponent? comp))
if (!_navQuery.TryComp(args.Grid, out var comp))
return;
var gridQuery = GetEntityQuery<MapGridComponent>();
foreach (var grid in args.NewGrids)
{
var newComp = EnsureComp<NavMapComponent>(grid);
RefreshGrid(grid, newComp, gridQuery.GetComponent(grid));
RefreshGrid(grid, newComp, _gridQuery.GetComponent(grid));
}
RefreshGrid(args.Grid, comp, gridQuery.GetComponent(args.Grid));
RefreshGrid(args.Grid, comp, _gridQuery.GetComponent(args.Grid));
}
private NavMapChunk EnsureChunk(NavMapComponent component, Vector2i origin)
{
if (!component.Chunks.TryGetValue(origin, out var chunk))
{
chunk = new(origin);
component.Chunks[origin] = chunk;
}
return chunk;
}
private void OnTileChanged(ref TileChangedEvent ev)
{
if (!TryComp<NavMapComponent>(ev.NewTile.GridUid, out var navMap))
if (!ev.EmptyChanged || !_navQuery.TryComp(ev.NewTile.GridUid, out var navMap))
return;
var tile = ev.NewTile.GridIndices;
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile, ChunkSize);
if (!navMap.Chunks.TryGetValue((NavMapChunkType.Floor, chunkOrigin), out var chunk))
chunk = new(chunkOrigin);
var chunk = EnsureChunk(navMap, chunkOrigin);
// This could be easily replaced in the future to accommodate diagonal tiles
if (ev.NewTile.IsSpace())
chunk = UnsetAllEdgesForChunkTile(chunk, tile);
var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
ref var tileData = ref chunk.TileData[GetTileIndex(relative)];
else
chunk = SetAllEdgesForChunkTile(chunk, tile);
chunk.LastUpdate = _gameTiming.CurTick;
navMap.Chunks[(NavMapChunkType.Floor, chunkOrigin)] = chunk;
Dirty(ev.NewTile.GridUid, navMap);
}
private void OnAirtightChanged(ref AirtightChanged ev)
{
var gridUid = ev.Position.Grid;
if (!TryComp<NavMapComponent>(gridUid, out var navMap) ||
!TryComp<MapGridComponent>(gridUid, out var mapGrid))
return;
// Refresh the affected tile
var tile = ev.Position.Tile;
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile, ChunkSize);
RefreshTileEntityContents(gridUid, navMap, mapGrid, chunkOrigin, tile);
// Update potentially affected chunks
foreach (var category in EntityChunkTypes)
if (ev.NewTile.IsSpace(_tileDefManager))
{
if (!navMap.Chunks.TryGetValue((category, chunkOrigin), out var chunk))
continue;
chunk.LastUpdate = _gameTiming.CurTick;
navMap.Chunks[(category, chunkOrigin)] = chunk;
tileData = 0;
if (PruneEmpty((ev.NewTile.GridUid, navMap), chunk))
return;
}
else
{
tileData = FloorMask;
}
Dirty(gridUid, navMap);
DirtyChunk((ev.NewTile.GridUid, navMap), chunk);
}
private void DirtyChunk(Entity<NavMapComponent> entity, NavMapChunk chunk)
{
if (chunk.LastUpdate == _gameTiming.CurTick)
return;
chunk.LastUpdate = _gameTiming.CurTick;
Dirty(entity);
}
private void OnAirtightChange(ref AirtightChanged args)
{
if (args.AirBlockedChanged)
return;
var gridUid = args.Position.Grid;
if (!_navQuery.TryComp(gridUid, out var navMap) ||
!_gridQuery.TryComp(gridUid, out var mapGrid))
{
return;
}
var chunkOrigin = SharedMapSystem.GetChunkIndices(args.Position.Tile, ChunkSize);
var (newValue, chunk) = RefreshTileEntityContents(gridUid, navMap, mapGrid, chunkOrigin, args.Position.Tile, setFloor: false);
if (newValue == 0 && PruneEmpty((gridUid, navMap), chunk))
return;
DirtyChunk((gridUid, navMap), chunk);
}
#endregion
@@ -220,76 +245,70 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
var tile = tileRef.GridIndices;
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile, ChunkSize);
if (!component.Chunks.TryGetValue((NavMapChunkType.Floor, chunkOrigin), out var chunk))
chunk = new(chunkOrigin);
var chunk = EnsureChunk(component, chunkOrigin);
chunk.LastUpdate = _gameTiming.CurTick;
// Refresh the floor tile
component.Chunks[(NavMapChunkType.Floor, chunkOrigin)] = SetAllEdgesForChunkTile(chunk, tile);
// Refresh the contents of the tile
RefreshTileEntityContents(uid, component, mapGrid, chunkOrigin, tile);
RefreshTileEntityContents(uid, component, mapGrid, chunkOrigin, tile, setFloor: true);
}
Dirty(uid, component);
}
private void RefreshTileEntityContents(EntityUid uid, NavMapComponent component, MapGridComponent mapGrid, Vector2i chunkOrigin, Vector2i tile)
private (int NewVal, NavMapChunk Chunk) RefreshTileEntityContents(EntityUid uid,
NavMapComponent component,
MapGridComponent mapGrid,
Vector2i chunkOrigin,
Vector2i tile,
bool setFloor)
{
var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
var flag = (ushort) GetFlag(relative);
var invFlag = (ushort) ~flag;
var chunk = EnsureChunk(component, chunkOrigin);
ref var tileData = ref chunk.TileData[GetTileIndex(relative)];
// Clear stale data from the tile across all entity associated chunks
foreach (var category in EntityChunkTypes)
{
if (!component.Chunks.TryGetValue((category, chunkOrigin), out var chunk))
chunk = new(chunkOrigin);
// Clear all data except for floor bits
if (setFloor)
tileData = FloorMask;
else
tileData &= FloorMask;
foreach (var (direction, _) in chunk.TileData)
chunk.TileData[direction] &= invFlag;
chunk.LastUpdate = _gameTiming.CurTick;
component.Chunks[(category, chunkOrigin)] = chunk;
}
// Update the tile data based on what entities are still anchored to the tile
var enumerator = _mapSystem.GetAnchoredEntitiesEnumerator(uid, mapGrid, tile);
while (enumerator.MoveNext(out var ent))
{
if (!TryComp<AirtightComponent>(ent, out var entAirtight))
if (!_airtightQuery.TryComp(ent, out var airtight))
continue;
var category = GetAssociatedEntityChunkType(ent.Value);
if (!component.Chunks.TryGetValue((category, chunkOrigin), out var chunk))
var category = GetEntityType(ent.Value);
if (category == NavMapChunkType.Invalid)
continue;
foreach (var (direction, _) in chunk.TileData)
{
if ((direction & entAirtight.AirBlockedDirection) > 0)
chunk.TileData[direction] |= flag;
}
chunk.LastUpdate = _gameTiming.CurTick;
component.Chunks[(category, chunkOrigin)] = chunk;
var directions = (int)airtight.AirBlockedDirection;
tileData |= directions << (int) category;
}
// Remove walls that intersect with doors (unless they can both physically fit on the same tile)
if (component.Chunks.TryGetValue((NavMapChunkType.Wall, chunkOrigin), out var wallChunk) &&
component.Chunks.TryGetValue((NavMapChunkType.Airlock, chunkOrigin), out var airlockChunk))
{
foreach (var (direction, _) in wallChunk.TileData)
{
var airlockInvFlag = (ushort) ~airlockChunk.TileData[direction];
wallChunk.TileData[direction] &= airlockInvFlag;
}
// TODO NAVMAP why can this even happen?
// Is this for blast-doors or something?
wallChunk.LastUpdate = _gameTiming.CurTick;
component.Chunks[(NavMapChunkType.Wall, chunkOrigin)] = wallChunk;
// Shift airlock bits over to the wall bits
var shiftedAirlockBits = (tileData & AirlockMask) >> ((int) NavMapChunkType.Airlock - (int) NavMapChunkType.Wall);
// And then mask door bits
tileData &= ~shiftedAirlockBits;
return (tileData, chunk);
}
private bool PruneEmpty(Entity<NavMapComponent> entity, NavMapChunk chunk)
{
foreach (var val in chunk.TileData)
{
// TODO NAVMAP SIMD
if (val != 0)
return false;
}
entity.Comp.Chunks.Remove(chunk.Origin);
Dirty(entity);
return true;
}
#endregion
@@ -304,22 +323,15 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
if (xform.GridUid == null)
return;
if (!TryComp<NavMapComponent>(xform.GridUid, out var navMap))
if (!_navQuery.TryComp(xform.GridUid, out var navMap))
return;
var netEnt = GetNetEntity(uid);
var oldBeacon = navMap.Beacons.FirstOrNull(x => x.NetEnt == netEnt);
var changed = false;
var meta = MetaData(uid);
var changed = navMap.Beacons.Remove(meta.NetEntity);
if (oldBeacon != null)
if (TryCreateNavMapBeaconData(uid, component, xform, meta, out var beaconData))
{
navMap.Beacons.Remove(oldBeacon.Value);
changed = true;
}
if (TryCreateNavMapBeaconData(uid, component, xform, out var beaconData))
{
navMap.Beacons.Add(beaconData.Value);
navMap.Beacons.Add(meta.NetEntity, beaconData.Value);
changed = true;
}

View File

@@ -1,4 +1,6 @@
using System.Linq;
using Content.Server.Administration;
using Content.Server.Administration.Managers;
using Content.Server.Afk;
using Content.Server.Afk.Events;
using Content.Server.GameTicking;
@@ -31,6 +33,7 @@ public sealed class PlayTimeTrackingSystem : EntitySystem
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly MindSystem _minds = default!;
[Dependency] private readonly PlayTimeTrackingManager _tracking = default!;
[Dependency] private readonly IAdminManager _adminManager = default!;
public override void Initialize()
{
@@ -47,6 +50,7 @@ public sealed class PlayTimeTrackingSystem : EntitySystem
SubscribeLocalEvent<UnAFKEvent>(OnUnAFK);
SubscribeLocalEvent<MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<PlayerJoinedLobbyEvent>(OnPlayerJoinedLobby);
_adminManager.OnPermsChanged += AdminPermsChanged;
}
public override void Shutdown()
@@ -54,6 +58,7 @@ public sealed class PlayTimeTrackingSystem : EntitySystem
base.Shutdown();
_tracking.CalcTrackers -= CalcTrackers;
_adminManager.OnPermsChanged -= AdminPermsChanged;
}
private void CalcTrackers(ICommonSession player, HashSet<string> trackers)
@@ -61,6 +66,13 @@ public sealed class PlayTimeTrackingSystem : EntitySystem
if (_afk.IsAfk(player))
return;
if (_adminManager.IsAdmin(player))
{
trackers.Add(PlayTimeTrackingShared.TrackerAdmin);
trackers.Add(PlayTimeTrackingShared.TrackerOverall);
return;
}
if (!IsPlayerAlive(player))
return;
@@ -131,6 +143,11 @@ public sealed class PlayTimeTrackingSystem : EntitySystem
_tracking.QueueRefreshTrackers(ev.Session);
}
private void AdminPermsChanged(AdminPermsChangedEventArgs admin)
{
_tracking.QueueRefreshTrackers(admin.Player);
}
private void OnPlayerAttached(PlayerAttachedEvent ev)
{
_tracking.QueueRefreshTrackers(ev.Player);

View File

@@ -1,7 +1,5 @@
using Content.Server.GameTicking.Rules.Components;
using Content.Server.NodeContainer;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.NodeGroups;
using Content.Server.NodeContainer.Nodes;
using Content.Server.Power.Components;
using Content.Server.Power.Nodes;
@@ -13,10 +11,8 @@ using Content.Shared.Power;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Map.Components;
using Robust.Shared.Player;
using Robust.Shared.Utility;
using System.Linq;
using System.Diagnostics.CodeAnalysis;
using Content.Server.GameTicking.Components;
namespace Content.Server.Power.EntitySystems;
@@ -163,7 +159,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
allChunks = new();
var tile = _sharedMapSystem.LocalToTile(xform.GridUid.Value, grid, xform.Coordinates);
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile, SharedNavMapSystem.ChunkSize);
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile, ChunkSize);
if (!allChunks.TryGetValue(chunkOrigin, out var chunk))
{
@@ -171,8 +167,8 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
allChunks[chunkOrigin] = chunk;
}
var relative = SharedMapSystem.GetChunkRelative(tile, SharedNavMapSystem.ChunkSize);
var flag = SharedNavMapSystem.GetFlag(relative);
var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
var flag = GetFlag(relative);
if (args.Anchored)
chunk.PowerCableData[(int) component.CableType] |= flag;
@@ -884,7 +880,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
continue;
var tile = _sharedMapSystem.GetTileRef(gridUid, grid, entXform.Coordinates);
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile.GridIndices, SharedNavMapSystem.ChunkSize);
var chunkOrigin = SharedMapSystem.GetChunkIndices(tile.GridIndices, ChunkSize);
if (!allChunks.TryGetValue(chunkOrigin, out var chunk))
{
@@ -892,8 +888,8 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
allChunks[chunkOrigin] = chunk;
}
var relative = SharedMapSystem.GetChunkRelative(tile.GridIndices, SharedNavMapSystem.ChunkSize);
var flag = SharedNavMapSystem.GetFlag(relative);
var relative = SharedMapSystem.GetChunkRelative(tile.GridIndices, ChunkSize);
var flag = GetFlag(relative);
chunk.PowerCableData[(int) cable.CableType] |= flag;
}
@@ -910,7 +906,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
var xform = Transform(ent);
var tile = _sharedMapSystem.GetTileRef(gridUid, grid, xform.Coordinates);
var gridIndices = tile.GridIndices;
var chunkOrigin = SharedMapSystem.GetChunkIndices(gridIndices, SharedNavMapSystem.ChunkSize);
var chunkOrigin = SharedMapSystem.GetChunkIndices(gridIndices, ChunkSize);
if (!component.FocusChunks.TryGetValue(chunkOrigin, out var chunk))
{
@@ -918,8 +914,8 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
component.FocusChunks[chunkOrigin] = chunk;
}
var relative = SharedMapSystem.GetChunkRelative(gridIndices, SharedNavMapSystem.ChunkSize);
var flag = SharedNavMapSystem.GetFlag(relative);
var relative = SharedMapSystem.GetChunkRelative(gridIndices, ChunkSize);
var flag = GetFlag(relative);
if (TryComp<CableComponent>(ent, out var cable))
chunk.PowerCableData[(int) cable.CableType] |= flag;

View File

@@ -7,6 +7,7 @@ using Content.Server.DeviceNetwork.Systems;
using Content.Server.NodeContainer;
using Content.Server.NodeContainer.Nodes;
using Content.Server.Power.Components;
using Content.Shared.Atmos;
using Content.Shared.DeviceNetwork;
using Content.Shared.Examine;
using Content.Shared.Power.Generation.Teg;

View File

@@ -1,5 +1,6 @@
using Content.Server.Atmos;
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Power.Generator;
namespace Content.Server.Power.Generator;

View File

@@ -1,4 +1,5 @@
using Content.Server.Atmos;
using Content.Shared.Atmos;
using Content.Shared.Storage.Components;
using Robust.Shared.GameStates;

View File

@@ -82,7 +82,9 @@ namespace Content.Server.Storage.EntitySystems
if (component.Sound != null)
{
_audio.PlayPvs(component.Sound, uid);
// The entity is often deleted, so play the sound at its position rather than parenting
var coordinates = Transform(uid).Coordinates;
_audio.PlayPvs(component.Sound, coordinates);
}
component.Uses--;

View File

@@ -100,7 +100,7 @@ public sealed partial class StoreSystem : EntitySystem
if (args.Handled)
{
var msg = Loc.GetString("store-currency-inserted", ("used", args.Used), ("target", args.Target));
_popup.PopupEntity(msg, args.Target.Value);
_popup.PopupEntity(msg, args.Target.Value, args.User);
QueueDel(args.Used);
}
}

View File

@@ -154,7 +154,7 @@ public sealed partial class GunSystem : SharedGunSystem
});
SetCartridgeSpent(ent.Value, cartridge, true);
MuzzleFlash(gunUid, cartridge, user);
MuzzleFlash(gunUid, cartridge, mapDirection.ToAngle(), user);
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
if (cartridge.DeleteOnSpawn)
@@ -175,7 +175,7 @@ public sealed partial class GunSystem : SharedGunSystem
// Ammo shoots itself
case AmmoComponent newAmmo:
shotProjectiles.Add(ent!.Value);
MuzzleFlash(gunUid, newAmmo, user);
MuzzleFlash(gunUid, newAmmo, mapDirection.ToAngle(), user);
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
ShootOrThrow(ent.Value, mapDirection, gunVelocity, gun, gunUid, user);
break;
@@ -326,9 +326,9 @@ public sealed partial class GunSystem : SharedGunSystem
protected override void Popup(string message, EntityUid? uid, EntityUid? user) { }
protected override void CreateEffect(EntityUid uid, MuzzleFlashEvent message, EntityUid? user = null)
protected override void CreateEffect(EntityUid gunUid, MuzzleFlashEvent message, EntityUid? user = null)
{
var filter = Filter.Pvs(uid, entityManager: EntityManager);
var filter = Filter.Pvs(gunUid, entityManager: EntityManager);
if (TryComp<ActorComponent>(user, out var actor))
filter.RemovePlayer(actor.PlayerSession);

View File

@@ -2,6 +2,7 @@
using Content.Server.Atmos.EntitySystems;
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Shared.Atmos;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;

View File

@@ -2,6 +2,7 @@
using Content.Server.Atmos.EntitySystems;
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Shared.Atmos;
using Robust.Server.GameObjects;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;

View File

@@ -104,15 +104,14 @@ namespace Content.Shared.Atmos
{
return direction switch
{
AtmosDirection.East => Angle.FromDegrees(90),
AtmosDirection.North => Angle.FromDegrees(180),
AtmosDirection.West => Angle.FromDegrees(270),
AtmosDirection.South => Angle.FromDegrees(0),
AtmosDirection.NorthEast => Angle.FromDegrees(135),
AtmosDirection.NorthWest => Angle.FromDegrees(205),
AtmosDirection.SouthWest => Angle.FromDegrees(315),
AtmosDirection.SouthEast => Angle.FromDegrees(45),
AtmosDirection.South => Angle.Zero,
AtmosDirection.East => new Angle(MathHelper.PiOver2),
AtmosDirection.North => new Angle(Math.PI),
AtmosDirection.West => new Angle(-MathHelper.PiOver2),
AtmosDirection.NorthEast => new Angle(Math.PI*3/4),
AtmosDirection.NorthWest => new Angle(-Math.PI*3/4),
AtmosDirection.SouthWest => new Angle(-MathHelper.PiOver4),
AtmosDirection.SouthEast => new Angle(MathHelper.PiOver4),
_ => throw new ArgumentOutOfRangeException(nameof(direction), $"It was {direction}."),
};

View File

@@ -1,13 +1,12 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using Content.Server.Atmos.Reactions;
using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Reactions;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Server.Atmos
namespace Content.Shared.Atmos
{
/// <summary>
/// A general-purpose, variable volume gas mixture.

View File

@@ -0,0 +1,16 @@
namespace Content.Shared.Atmos;
public readonly record struct GasMixtureStringRepresentation(float TotalMoles, float Temperature, float Pressure, Dictionary<string, float> MolesPerGas) : IFormattable
{
public override string ToString()
{
return $"{Temperature}K {Pressure} kPa";
}
public string ToString(string? format, IFormatProvider? formatProvider)
{
return ToString();
}
public static implicit operator string(GasMixtureStringRepresentation rep) => rep.ToString();
}

View File

@@ -0,0 +1,14 @@
namespace Content.Shared.Atmos.Reactions;
[Flags]
public enum ReactionResult : byte
{
NoReaction = 0,
Reacting = 1,
StopReactions = 2,
}
public enum GasReaction : byte
{
Fire = 0,
}

View File

@@ -1,3 +1,4 @@
using Content.Shared.Maps;
using Robust.Shared;
using Robust.Shared.Configuration;
@@ -908,6 +909,13 @@ namespace Content.Shared.CCVar
public static readonly CVarDef<int> ExplosionSingleTickAreaLimit =
CVarDef.Create("explosion.single_tick_area_limit", 400, CVar.SERVERONLY);
/// <summary>
/// Whether or not explosions are allowed to create tiles that have
/// <see cref="ContentTileDefinition.MapAtmosphere"/> set to true.
/// </summary>
public static readonly CVarDef<bool> ExplosionCanCreateVacuum =
CVarDef.Create("explosion.can_create_vacuum", true, CVar.SERVERONLY);
/*
* Radiation
*/
@@ -2003,7 +2011,7 @@ namespace Content.Shared.CCVar
// Clippy!
public static readonly CVarDef<string> TippyEntity =
CVarDef.Create("tippy.entity", "Tippy", CVar.SERVER | CVar.REPLICATED);
/*
* DEBUG
*/

View File

@@ -247,6 +247,27 @@ public sealed class FollowerSystem : EntitySystem
StopFollowingEntity(player, uid, followed);
}
}
/// <summary>
/// Get the most followed entity.
/// </summary>
public EntityUid? GetMostFollowed()
{
EntityUid? picked = null;
int most = 0;
var query = EntityQueryEnumerator<FollowedComponent>();
while (query.MoveNext(out var uid, out var comp))
{
var count = comp.Following.Count;
if (count > most)
{
picked = uid;
most = count;
}
}
return picked;
}
}
public abstract class FollowEvent : EntityEventArgs

View File

@@ -125,6 +125,12 @@ namespace Content.Shared.Ghost
}
}
/// <summary>
/// A client to server request for their ghost to be warped to the most followed entity.
/// </summary>
[Serializable, NetSerializable]
public sealed class GhostnadoRequestEvent : EntityEventArgs;
/// <summary>
/// A client to server request for their ghost to return to body
/// </summary>

View File

@@ -7,7 +7,6 @@ namespace Content.Shared.Light.Components;
[NetworkedComponent]
public abstract partial class SharedExpendableLightComponent : Component
{
public static readonly AudioParams LoopedSoundParams = new(0, 1, 62.5f, 1, 1, true, 0.3f);
[ViewVariables(VVAccess.ReadOnly)]
public ExpendableLightState CurrentState { get; set; }

View File

@@ -1,3 +1,4 @@
using System.Linq;
using Content.Shared.Atmos;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
@@ -19,53 +20,43 @@ public sealed partial class NavMapComponent : Component
/// Bitmasks that represent chunked tiles.
/// </summary>
[ViewVariables]
public Dictionary<(NavMapChunkType, Vector2i), NavMapChunk> Chunks = new();
public Dictionary<Vector2i, NavMapChunk> Chunks = new();
/// <summary>
/// List of station beacons.
/// </summary>
[ViewVariables]
public HashSet<SharedNavMapSystem.NavMapBeacon> Beacons = new();
public Dictionary<NetEntity, SharedNavMapSystem.NavMapBeacon> Beacons = new();
}
[Serializable, NetSerializable]
public sealed class NavMapChunk
public sealed class NavMapChunk(Vector2i origin)
{
/// <summary>
/// The chunk origin
/// </summary>
public readonly Vector2i Origin;
[ViewVariables]
public readonly Vector2i Origin = origin;
/// <summary>
/// Bitmask for tiles, 1 for occupied and 0 for empty. There is a bitmask for each cardinal direction,
/// representing each edge of the tile, in case the entities inside it do not entirely fill it
/// Array containing the chunk's data. The
/// </summary>
public Dictionary<AtmosDirection, ushort> TileData;
[ViewVariables]
public int[] TileData = new int[SharedNavMapSystem.ArraySize];
/// <summary>
/// The last game tick that the chunk was updated
/// </summary>
[NonSerialized]
public GameTick LastUpdate;
public NavMapChunk(Vector2i origin)
{
Origin = origin;
TileData = new()
{
[AtmosDirection.North] = 0,
[AtmosDirection.East] = 0,
[AtmosDirection.South] = 0,
[AtmosDirection.West] = 0,
};
}
}
public enum NavMapChunkType : byte
{
Invalid,
Floor,
Wall,
Airlock,
// Values represent bit shift offsets when retrieving data in the tile array.
Invalid = byte.MaxValue,
Floor = 0, // I believe floors have directional information for diagonal tiles?
Wall = SharedNavMapSystem.Directions,
Airlock = 2 * SharedNavMapSystem.Directions,
}

View File

@@ -1,6 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using Content.Shared.Atmos;
using System.Runtime.CompilerServices;
using Content.Shared.Tag;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
@@ -11,19 +11,22 @@ namespace Content.Shared.Pinpointer;
public abstract class SharedNavMapSystem : EntitySystem
{
[Dependency] private readonly TagSystem _tagSystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
public const int Categories = 3;
public const int Directions = 4; // Not directly tied to number of atmos directions
public const byte ChunkSize = 4;
public const int ChunkSize = 8;
public const int ArraySize = ChunkSize* ChunkSize;
public readonly NavMapChunkType[] EntityChunkTypes =
{
NavMapChunkType.Invalid,
NavMapChunkType.Wall,
NavMapChunkType.Airlock,
};
public const int AllDirMask = (1 << Directions) - 1;
public const int AirlockMask = AllDirMask << (int) NavMapChunkType.Airlock;
public const int WallMask = AllDirMask << (int) NavMapChunkType.Wall;
public const int FloorMask = AllDirMask << (int) NavMapChunkType.Floor;
[Robust.Shared.IoC.Dependency] private readonly TagSystem _tagSystem = default!;
[Robust.Shared.IoC.Dependency] private readonly IGameTiming _gameTiming = default!;
private readonly string[] _wallTags = ["Wall", "Window"];
private EntityQuery<NavMapDoorComponent> _doorQuery;
public override void Initialize()
{
@@ -31,107 +34,49 @@ public abstract class SharedNavMapSystem : EntitySystem
// Data handling events
SubscribeLocalEvent<NavMapComponent, ComponentGetState>(OnGetState);
_doorQuery = GetEntityQuery<NavMapDoorComponent>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetTileIndex(Vector2i relativeTile)
{
return relativeTile.X * ChunkSize + relativeTile.Y;
}
/// <summary>
/// Converts the chunk's tile into a bitflag for the slot.
/// Inverse of <see cref="GetTileIndex"/>
/// </summary>
public static int GetFlag(Vector2i relativeTile)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2i GetTileFromIndex(int index)
{
return 1 << (relativeTile.X * ChunkSize + relativeTile.Y);
}
/// <summary>
/// Converts the chunk's tile into a bitflag for the slot.
/// </summary>
public static Vector2i GetTile(int flag)
{
var value = Math.Log2(flag);
var x = (int) value / ChunkSize;
var y = (int) value % ChunkSize;
var result = new Vector2i(x, y);
DebugTools.Assert(GetFlag(result) == flag);
var x = index / ChunkSize;
var y = index % ChunkSize;
return new Vector2i(x, y);
}
public NavMapChunk SetAllEdgesForChunkTile(NavMapChunk chunk, Vector2i tile)
public NavMapChunkType GetEntityType(EntityUid uid)
{
var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
var flag = (ushort) GetFlag(relative);
if (_doorQuery.HasComp(uid))
return NavMapChunkType.Airlock;
foreach (var (direction, _) in chunk.TileData)
chunk.TileData[direction] |= flag;
if (_tagSystem.HasAnyTag(uid, _wallTags))
return NavMapChunkType.Wall;
return chunk;
return NavMapChunkType.Invalid;
}
public NavMapChunk UnsetAllEdgesForChunkTile(NavMapChunk chunk, Vector2i tile)
{
var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
var flag = (ushort) GetFlag(relative);
var invFlag = (ushort) ~flag;
foreach (var (direction, _) in chunk.TileData)
chunk.TileData[direction] &= invFlag;
return chunk;
}
public ushort GetCombinedEdgesForChunk(Dictionary<AtmosDirection, ushort> tile)
{
ushort combined = 0;
foreach (var kvp in tile)
combined |= kvp.Value;
return combined;
}
public bool AllTileEdgesAreOccupied(Dictionary<AtmosDirection, ushort> tileData, Vector2i tile)
{
var flag = (ushort) GetFlag(tile);
foreach (var kvp in tileData)
{
if ((kvp.Value & flag) == 0)
return false;
}
return true;
}
public NavMapChunkType GetAssociatedEntityChunkType(EntityUid uid)
{
var category = NavMapChunkType.Invalid;
if (HasComp<NavMapDoorComponent>(uid))
category = NavMapChunkType.Airlock;
else if (_tagSystem.HasAnyTag(uid, _wallTags))
category = NavMapChunkType.Wall;
return category;
}
protected bool TryCreateNavMapBeaconData(EntityUid uid, NavMapBeaconComponent component, TransformComponent xform, [NotNullWhen(true)] out NavMapBeacon? beaconData)
protected bool TryCreateNavMapBeaconData(EntityUid uid, NavMapBeaconComponent component, TransformComponent xform, MetaDataComponent meta, [NotNullWhen(true)] out NavMapBeacon? beaconData)
{
beaconData = null;
if (!component.Enabled || xform.GridUid == null || !xform.Anchored)
return false;
string? name = component.Text;
var meta = MetaData(uid);
var name = component.Text;
if (string.IsNullOrEmpty(name))
name = meta.EntityName;
beaconData = new NavMapBeacon(meta.NetEntity, component.Color, name, xform.LocalPosition)
{
LastUpdate = _gameTiming.CurTick
};
beaconData = new NavMapBeacon(meta.NetEntity, component.Color, name, xform.LocalPosition);
return true;
}
@@ -140,64 +85,36 @@ public abstract class SharedNavMapSystem : EntitySystem
private void OnGetState(EntityUid uid, NavMapComponent component, ref ComponentGetState args)
{
var chunks = new Dictionary<(NavMapChunkType, Vector2i), Dictionary<AtmosDirection, ushort>>();
var beacons = new HashSet<NavMapBeacon>();
Dictionary<Vector2i, int[]> chunks;
// Should this be a full component state or a delta-state?
if (args.FromTick <= component.CreationTick)
{
foreach (var ((category, origin), chunk) in component.Chunks)
// Full state
chunks = new(component.Chunks.Count);
foreach (var (origin, chunk) in component.Chunks)
{
var chunkDatum = new Dictionary<AtmosDirection, ushort>(chunk.TileData.Count);
foreach (var (direction, tileData) in chunk.TileData)
chunkDatum[direction] = tileData;
chunks.Add((category, origin), chunkDatum);
chunks.Add(origin, chunk.TileData);
}
var beaconQuery = AllEntityQuery<NavMapBeaconComponent, TransformComponent>();
while (beaconQuery.MoveNext(out var beaconUid, out var beacon, out var xform))
{
if (xform.GridUid != uid)
continue;
if (!TryCreateNavMapBeaconData(beaconUid, beacon, xform, out var beaconData))
continue;
beacons.Add(beaconData.Value);
}
args.State = new NavMapComponentState(chunks, beacons);
args.State = new NavMapComponentState(chunks, component.Beacons);
return;
}
foreach (var ((category, origin), chunk) in component.Chunks)
chunks = new();
foreach (var (origin, chunk) in component.Chunks)
{
if (chunk.LastUpdate < args.FromTick)
continue;
var chunkDatum = new Dictionary<AtmosDirection, ushort>(chunk.TileData.Count);
foreach (var (direction, tileData) in chunk.TileData)
chunkDatum[direction] = tileData;
chunks.Add((category, origin), chunkDatum);
chunks.Add(origin, chunk.TileData);
}
foreach (var beacon in component.Beacons)
{
if (beacon.LastUpdate < args.FromTick)
continue;
beacons.Add(beacon);
}
args.State = new NavMapComponentState(chunks, beacons)
args.State = new NavMapComponentState(chunks, component.Beacons)
{
// TODO NAVMAP cache a single AllChunks hashset in the component.
// Or maybe just only send them if a chunk gets removed.
AllChunks = new(component.Chunks.Keys),
AllBeacons = new(component.Beacons)
};
}
@@ -206,22 +123,18 @@ public abstract class SharedNavMapSystem : EntitySystem
#region: System messages
[Serializable, NetSerializable]
protected sealed class NavMapComponentState : ComponentState, IComponentDeltaState
protected sealed class NavMapComponentState(
Dictionary<Vector2i, int[]> chunks,
Dictionary<NetEntity, NavMapBeacon> beacons)
: ComponentState, IComponentDeltaState
{
public Dictionary<(NavMapChunkType, Vector2i), Dictionary<AtmosDirection, ushort>> Chunks = new();
public HashSet<NavMapBeacon> Beacons = new();
public Dictionary<Vector2i, int[]> Chunks = chunks;
public Dictionary<NetEntity, NavMapBeacon> Beacons = beacons;
// Required to infer deleted/missing chunks for delta states
public HashSet<(NavMapChunkType, Vector2i)>? AllChunks;
public HashSet<NavMapBeacon>? AllBeacons;
public HashSet<Vector2i>? AllChunks;
public NavMapComponentState(Dictionary<(NavMapChunkType, Vector2i), Dictionary<AtmosDirection, ushort>> chunks, HashSet<NavMapBeacon> beacons)
{
Chunks = chunks;
Beacons = beacons;
}
public bool FullState => (AllChunks == null || AllBeacons == null);
public bool FullState => AllChunks == null;
public void ApplyToFullState(IComponentState fullState)
{
@@ -229,25 +142,25 @@ public abstract class SharedNavMapSystem : EntitySystem
var state = (NavMapComponentState) fullState;
DebugTools.Assert(state.FullState);
// Update chunks
foreach (var key in state.Chunks.Keys)
{
if (!AllChunks!.Contains(key))
state.Chunks.Remove(key);
}
foreach (var (chunk, data) in Chunks)
state.Chunks[chunk] = new(data);
// Update beacons
foreach (var beacon in state.Beacons)
foreach (var (index, data) in Chunks)
{
if (!AllBeacons!.Contains(beacon))
state.Beacons.Remove(beacon);
if (!state.Chunks.TryGetValue(index, out var stateValue))
state.Chunks[index] = stateValue = new int[data.Length];
Array.Copy(data, stateValue, data.Length);
}
foreach (var beacon in Beacons)
state.Beacons.Add(beacon);
state.Beacons.Clear();
foreach (var (nuid, beacon) in Beacons)
{
state.Beacons.Add(nuid, beacon);
}
}
public IComponentState CreateNewFullState(IComponentState fullState)
@@ -256,36 +169,26 @@ public abstract class SharedNavMapSystem : EntitySystem
var state = (NavMapComponentState) fullState;
DebugTools.Assert(state.FullState);
var chunks = new Dictionary<(NavMapChunkType, Vector2i), Dictionary<AtmosDirection, ushort>>();
var beacons = new HashSet<NavMapBeacon>();
foreach (var (chunk, data) in Chunks)
chunks[chunk] = new(data);
foreach (var (chunk, data) in state.Chunks)
var chunks = new Dictionary<Vector2i, int[]>(state.Chunks.Count);
foreach (var (index, data) in state.Chunks)
{
if (AllChunks!.Contains(chunk))
chunks.TryAdd(chunk, new(data));
if (!AllChunks!.Contains(index))
continue;
var newData = chunks[index] = new int[ArraySize];
if (Chunks.TryGetValue(index, out var updatedData))
Array.Copy(newData, updatedData, ArraySize);
else
Array.Copy(newData, data, ArraySize);
}
foreach (var beacon in Beacons)
beacons.Add(new NavMapBeacon(beacon.NetEnt, beacon.Color, beacon.Text, beacon.Position));
foreach (var beacon in state.Beacons)
{
if (AllBeacons!.Contains(beacon))
beacons.Add(new NavMapBeacon(beacon.NetEnt, beacon.Color, beacon.Text, beacon.Position));
}
return new NavMapComponentState(chunks, beacons);
return new NavMapComponentState(chunks, new(Beacons));
}
}
[Serializable, NetSerializable]
public record struct NavMapBeacon(NetEntity NetEnt, Color Color, string Text, Vector2 Position)
{
public GameTick LastUpdate;
}
public record struct NavMapBeacon(NetEntity NetEnt, Color Color, string Text, Vector2 Position);
#endregion
}

View File

@@ -1,9 +1,18 @@
namespace Content.Shared.Players.PlayTimeTracking;
using Content.Shared.Dataset;
namespace Content.Shared.Players.PlayTimeTracking;
public static class PlayTimeTrackingShared
{
/// <summary>
/// The prototype ID of the play time tracker that represents overall playtime, i.e. not tied to any one role.
/// </summary>
[ValidatePrototypeId<PlayTimeTrackerPrototype>]
public const string TrackerOverall = "Overall";
/// <summary>
/// The prototype ID of the play time tracker that represents admin time, when a player is in game as admin.
/// </summary>
[ValidatePrototypeId<PlayTimeTrackerPrototype>]
public const string TrackerAdmin = "Admin";
}

View File

@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace Content.Shared.Power;
@@ -5,4 +6,23 @@ namespace Content.Shared.Power;
[UsedImplicitly]
public abstract class SharedPowerMonitoringConsoleSystem : EntitySystem
{
// Chunk size is limited as we require ChunkSize^2 <= 32 (number of bits in an int)
public const int ChunkSize = 5;
/// <summary>
/// Converts the chunk's tile into a bitflag for the slot.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetFlag(Vector2i relativeTile)
{
return 1 << (relativeTile.X * ChunkSize + relativeTile.Y);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2i GetTileFromIndex(int index)
{
var x = index / ChunkSize;
var y = index % ChunkSize;
return new Vector2i(x, y);
}
}

View File

@@ -356,6 +356,7 @@ namespace Content.Shared.Preferences
if (Age != other.Age) return false;
if (Sex != other.Sex) return false;
if (Gender != other.Gender) return false;
if (Species != other.Species) return false;
if (PreferenceUnavailable != other.PreferenceUnavailable) return false;
if (SpawnPriority != other.SpawnPriority) return false;
if (!_jobPriorities.SequenceEqual(other._jobPriorities)) return false;

View File

@@ -137,11 +137,8 @@ public abstract class SharedStorageSystem : EntitySystem
private void OnMapInit(Entity<StorageComponent> entity, ref MapInitEvent args)
{
if (TryComp<UseDelayComponent>(entity, out var useDelayComp))
{
UseDelay.SetLength((entity, useDelayComp), entity.Comp.QuickInsertCooldown, QuickInsertUseDelayID);
UseDelay.SetLength((entity, useDelayComp), entity.Comp.OpenUiCooldown, OpenUiUseDelayID);
}
UseDelay.SetLength(entity.Owner, entity.Comp.QuickInsertCooldown, QuickInsertUseDelayID);
UseDelay.SetLength(entity.Owner, entity.Comp.OpenUiCooldown, OpenUiUseDelayID);
}
private void OnStorageGetState(EntityUid uid, StorageComponent component, ref ComponentGetState args)

View File

@@ -9,9 +9,12 @@ public sealed class TagSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
private EntityQuery<TagComponent> _tagQuery;
public override void Initialize()
{
base.Initialize();
_tagQuery = GetEntityQuery<TagComponent>();
SubscribeLocalEvent<TagComponent, ComponentGetState>(OnTagGetState);
SubscribeLocalEvent<TagComponent, ComponentHandleState>(OnTagHandleState);
@@ -124,7 +127,7 @@ public sealed class TagSystem : EntitySystem
/// </exception>
public bool TryAddTag(EntityUid entity, string id)
{
return TryComp<TagComponent>(entity, out var component) &&
return _tagQuery.TryComp(entity, out var component) &&
AddTag(entity, component, id);
}
@@ -142,7 +145,7 @@ public sealed class TagSystem : EntitySystem
/// </exception>
public bool TryAddTags(EntityUid entity, params string[] ids)
{
return TryComp<TagComponent>(entity, out var component) &&
return _tagQuery.TryComp(entity, out var component) &&
AddTags(entity, component, ids);
}
@@ -160,7 +163,7 @@ public sealed class TagSystem : EntitySystem
/// </exception>
public bool TryAddTags(EntityUid entity, IEnumerable<string> ids)
{
return TryComp<TagComponent>(entity, out var component) &&
return _tagQuery.TryComp(entity, out var component) &&
AddTags(entity, component, ids);
}
@@ -175,7 +178,7 @@ public sealed class TagSystem : EntitySystem
/// </exception>
public bool HasTag(EntityUid entity, string id)
{
return TryComp<TagComponent>(entity, out var component) &&
return _tagQuery.TryComp(entity, out var component) &&
HasTag(component, id);
}
@@ -210,7 +213,7 @@ public sealed class TagSystem : EntitySystem
/// </exception>
public bool HasAllTags(EntityUid entity, List<string> ids)
{
return TryComp<TagComponent>(entity, out var component) &&
return _tagQuery.TryComp(entity, out var component) &&
HasAllTags(component, ids);
}
@@ -225,7 +228,7 @@ public sealed class TagSystem : EntitySystem
/// </exception>
public bool HasAllTags(EntityUid entity, IEnumerable<string> ids)
{
return TryComp<TagComponent>(entity, out var component) &&
return _tagQuery.TryComp(entity, out var component) &&
HasAllTags(component, ids);
}
@@ -234,18 +237,33 @@ public sealed class TagSystem : EntitySystem
/// </summary>
/// <param name="entity">The entity to check.</param>
/// <param name="ids">The tags to check for.</param>
/// <returns>true if they all exist, false otherwise.</returns>
/// <exception cref="UnknownPrototypeException">
/// Thrown if one of the ids represents an unregistered <see cref="TagPrototype"/>.
/// </exception>
public bool HasAllTags(EntityUid entity, List<ProtoId<TagPrototype>> ids)
{
return TryComp<TagComponent>(entity, out var component) &&
HasAllTags(component, ids);
}
/// <summary>
/// Checks if any of the given tags have been added to an entity.
/// </summary>
/// <param name="entity">The entity to check.</param>
/// <param name="ids">The tags to check for.</param>
/// <returns>true if any of them exist, false otherwise.</returns>
/// <exception cref="UnknownPrototypeException">
/// Thrown if one of the ids represents an unregistered <see cref="TagPrototype"/>.
/// </exception>
public bool HasAnyTag(EntityUid entity, params string[] ids)
{
return TryComp<TagComponent>(entity, out var component) &&
return _tagQuery.TryComp(entity, out var component) &&
HasAnyTag(component, ids);
}
/// <summary>
/// Checks if all of the given tags have been added to an entity.
/// Checks if any of the given tags have been added to an entity.
/// </summary>
/// <param name="entity">The entity to check.</param>
/// <param name="id">The tag to check for.</param>
@@ -256,7 +274,7 @@ public sealed class TagSystem : EntitySystem
public bool HasAnyTag(EntityUid entity, string id) => HasTag(entity, id);
/// <summary>
/// Checks if all of the given tags have been added to an entity.
/// Checks if any of the given tags have been added to an entity.
/// </summary>
/// <param name="entity">The entity to check.</param>
/// <param name="ids">The tags to check for.</param>
@@ -265,13 +283,28 @@ public sealed class TagSystem : EntitySystem
/// Thrown if one of the ids represents an unregistered <see cref="TagPrototype"/>.
/// </exception>
public bool HasAnyTag(EntityUid entity, List<string> ids)
{
return _tagQuery.TryComp(entity, out var component) &&
HasAnyTag(component, ids);
}
/// <summary>
/// Checks if any of the given tags have been added to an entity.
/// </summary>
/// <param name="entity">The entity to check.</param>
/// <param name="ids">The tags to check for.</param>
/// <returns>true if any of them exist, false otherwise.</returns>
/// <exception cref="UnknownPrototypeException">
/// Thrown if one of the ids represents an unregistered <see cref="TagPrototype"/>.
/// </exception>
public bool HasAnyTag(EntityUid entity, List<ProtoId<TagPrototype>> ids)
{
return TryComp<TagComponent>(entity, out var component) &&
HasAnyTag(component, ids);
}
/// <summary>
/// Checks if all of the given tags have been added to an entity.
/// Checks if any of the given tags have been added to an entity.
/// </summary>
/// <param name="entity">The entity to check.</param>
/// <param name="ids">The tags to check for.</param>
@@ -281,7 +314,7 @@ public sealed class TagSystem : EntitySystem
/// </exception>
public bool HasAnyTag(EntityUid entity, IEnumerable<string> ids)
{
return TryComp<TagComponent>(entity, out var component) &&
return _tagQuery.TryComp(entity, out var component) &&
HasAnyTag(component, ids);
}
@@ -298,7 +331,7 @@ public sealed class TagSystem : EntitySystem
/// </exception>
public bool RemoveTag(EntityUid entity, string id)
{
return TryComp<TagComponent>(entity, out var component) &&
return _tagQuery.TryComp(entity, out var component) &&
RemoveTag(entity, component, id);
}
@@ -315,7 +348,7 @@ public sealed class TagSystem : EntitySystem
/// </returns>
public bool RemoveTags(EntityUid entity, params string[] ids)
{
return TryComp<TagComponent>(entity, out var component) &&
return _tagQuery.TryComp(entity, out var component) &&
RemoveTags(entity, component, ids);
}
@@ -332,7 +365,7 @@ public sealed class TagSystem : EntitySystem
/// </exception>
public bool RemoveTags(EntityUid entity, IEnumerable<string> ids)
{
return TryComp<TagComponent>(entity, out var component) &&
return _tagQuery.TryComp(entity, out var component) &&
RemoveTags(entity, component, ids);
}
@@ -478,6 +511,26 @@ public sealed class TagSystem : EntitySystem
return true;
}
/// <summary>
/// Checks if all of the given tags have been added.
/// </summary>
/// <param name="ids">The tags to check for.</param>
/// <returns>true if they all exist, false otherwise.</returns>
/// <exception cref="UnknownPrototypeException">
/// Thrown if one of the ids represents an unregistered <see cref="TagPrototype"/>.
/// </exception>
public bool HasAllTags(TagComponent component, List<ProtoId<TagPrototype>> ids)
{
var stringIds = new List<string>();
foreach (var tag in ids)
{
stringIds.Add(tag.Id);
}
return HasAllTags(component, stringIds);
}
/// <summary>
/// Checks if any of the given tags have been added.
/// </summary>
@@ -488,7 +541,15 @@ public sealed class TagSystem : EntitySystem
/// </exception>
public bool HasAnyTag(TagComponent component, params string[] ids)
{
return HasAnyTag(component, ids.AsEnumerable());
foreach (var id in ids)
{
AssertValidTag(id);
if (component.Tags.Contains(id))
return true;
}
return false;
}
@@ -548,6 +609,25 @@ public sealed class TagSystem : EntitySystem
return false;
}
/// <summary>
/// Checks if any of the given tags have been added.
/// </summary>
/// <param name="ids">The tags to check for.</param>
/// <returns>true if any of them exist, false otherwise.</returns>
/// <exception cref="UnknownPrototypeException">
/// Thrown if one of the ids represents an unregistered <see cref="TagPrototype"/>.
/// </exception>
public bool HasAnyTag(TagComponent comp, List<ProtoId<TagPrototype>> ids)
{
var stringIds = new List<string>();
foreach (var tag in ids)
{
stringIds.Add(tag.Id);
}
return HasAnyTag(comp, stringIds);
}
/// <summary>
/// Tries to remove a tag if it exists.
/// </summary>

View File

@@ -47,7 +47,7 @@ public sealed class UseDelaySystem : EntitySystem
{
// Set default delay length from the prototype
// This makes it easier for simple use cases that only need a single delay
SetLength(ent, ent.Comp.Delay, DefaultId);
SetLength((ent, ent.Comp), ent.Comp.Delay, DefaultId);
}
private void OnUnpaused(Entity<UseDelayComponent> ent, ref EntityUnpausedEvent args)
@@ -62,9 +62,14 @@ public sealed class UseDelaySystem : EntitySystem
/// <summary>
/// Sets the length of the delay with the specified ID.
/// </summary>
public bool SetLength(Entity<UseDelayComponent> ent, TimeSpan length, string id = DefaultId)
/// <remarks>
/// This will add a UseDelay component to the entity if it doesn't have one.
/// </remarks>
public bool SetLength(Entity<UseDelayComponent?> ent, TimeSpan length, string id = DefaultId)
{
if (ent.Comp.Delays.TryGetValue(id, out var entry))
EnsureComp<UseDelayComponent>(ent.Owner, out var comp);
if (comp.Delays.TryGetValue(id, out var entry))
{
if (entry.Length == length)
return true;
@@ -73,7 +78,7 @@ public sealed class UseDelaySystem : EntitySystem
}
else
{
ent.Comp.Delays.Add(id, new UseDelayInfo(length));
comp.Delays.Add(id, new UseDelayInfo(length));
}
Dirty(ent);

View File

@@ -11,15 +11,12 @@ public sealed class MuzzleFlashEvent : EntityEventArgs
public NetEntity Uid;
public string Prototype;
/// <summary>
/// Should the effect match the rotation of the entity.
/// </summary>
public bool MatchRotation;
public Angle Angle;
public MuzzleFlashEvent(NetEntity uid, string prototype, bool matchRotation = false)
public MuzzleFlashEvent(NetEntity uid, string prototype, Angle angle)
{
Uid = uid;
Prototype = prototype;
MatchRotation = matchRotation;
Angle = angle;
}
}

View File

@@ -136,7 +136,6 @@ public abstract partial class SharedGunSystem : EntitySystem
return;
gun.ShootCoordinates = GetCoordinates(msg.Coordinates);
Log.Debug($"Set shoot coordinates to {gun.ShootCoordinates}");
AttemptShoot(user.Value, ent, gun);
}
@@ -195,7 +194,6 @@ public abstract partial class SharedGunSystem : EntitySystem
if (gun.ShotCounter == 0)
return;
Log.Debug($"Stopped shooting {ToPrettyString(uid)}");
gun.ShotCounter = 0;
gun.ShootCoordinates = null;
Dirty(uid, gun);
@@ -461,7 +459,7 @@ public abstract partial class SharedGunSystem : EntitySystem
RemCompDeferred<AmmoComponent>(uid);
}
protected void MuzzleFlash(EntityUid gun, AmmoComponent component, EntityUid? user = null)
protected void MuzzleFlash(EntityUid gun, AmmoComponent component, Angle worldAngle, EntityUid? user = null)
{
var attemptEv = new GunMuzzleFlashAttemptEvent();
RaiseLocalEvent(gun, ref attemptEv);
@@ -473,7 +471,7 @@ public abstract partial class SharedGunSystem : EntitySystem
if (sprite == null)
return;
var ev = new MuzzleFlashEvent(GetNetEntity(gun), sprite, user == gun);
var ev = new MuzzleFlashEvent(GetNetEntity(gun), sprite, worldAngle);
CreateEffect(gun, ev, user);
}
@@ -522,7 +520,7 @@ public abstract partial class SharedGunSystem : EntitySystem
Dirty(gun);
}
protected abstract void CreateEffect(EntityUid uid, MuzzleFlashEvent message, EntityUid? user = null);
protected abstract void CreateEffect(EntityUid gunUid, MuzzleFlashEvent message, EntityUid? user = null);
/// <summary>
/// Used for animated effects on the client.

View File

@@ -1,103 +1,121 @@
using Content.Shared.Item;
using Content.Shared.Tag;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Whitelist
namespace Content.Shared.Whitelist;
/// <summary>
/// Used to determine whether an entity fits a certain whitelist.
/// Does not whitelist by prototypes, since that is undesirable; you're better off just adding a tag to all
/// entity prototypes that need to be whitelisted, and checking for that.
/// </summary>
/// <code>
/// whitelist:
/// tags:
/// - Cigarette
/// - FirelockElectronics
/// components:
/// - Buckle
/// - AsteroidRock
/// sizes:
/// - Tiny
/// - Large
/// </code>
[DataDefinition]
[Serializable, NetSerializable]
public sealed partial class EntityWhitelist
{
/// <summary>
/// Used to determine whether an entity fits a certain whitelist.
/// Does not whitelist by prototypes, since that is undesirable; you're better off just adding a tag to all
/// entity prototypes that need to be whitelisted, and checking for that.
/// Component names that are allowed in the whitelist.
/// </summary>
/// <code>
/// whitelist:
/// tags:
/// - Cigarette
/// - FirelockElectronics
/// components:
/// - Buckle
/// - AsteroidRock
/// </code>
[DataDefinition]
[Serializable, NetSerializable]
public sealed partial class EntityWhitelist
[DataField] public string[]? Components;
// TODO yaml validation
/// <summary>
/// Item sizes that are allowed in the whitelist.
/// </summary>
[DataField]
public List<ProtoId<ItemSizePrototype>>? Sizes;
[NonSerialized]
private List<ComponentRegistration>? _registrations;
/// <summary>
/// Tags that are allowed in the whitelist.
/// </summary>
[DataField]
public List<ProtoId<TagPrototype>>? Tags;
/// <summary>
/// If false, an entity only requires one of these components or tags to pass the whitelist. If true, an
/// entity requires to have ALL of these components and tags to pass.
/// The "Sizes" criteria will ignores this, since an item can only have one size.
/// </summary>
[DataField]
public bool RequireAll;
public void UpdateRegistrations()
{
/// <summary>
/// Component names that are allowed in the whitelist.
/// </summary>
[DataField("components")] public string[]? Components = null;
// TODO yaml validation
[NonSerialized]
private List<ComponentRegistration>? _registrations = null;
if (Components == null)
return;
/// <summary>
/// Tags that are allowed in the whitelist.
/// </summary>
[DataField("tags", customTypeSerializer:typeof(PrototypeIdListSerializer<TagPrototype>))]
public List<string>? Tags = null;
/// <summary>
/// If false, an entity only requires one of these components or tags to pass the whitelist. If true, an
/// entity requires to have ALL of these components and tags to pass.
/// </summary>
[DataField("requireAll")]
public bool RequireAll = false;
public void UpdateRegistrations()
var compFact = IoCManager.Resolve<IComponentFactory>();
_registrations = new List<ComponentRegistration>();
foreach (var name in Components)
{
if (Components == null) return;
var compfact = IoCManager.Resolve<IComponentFactory>();
_registrations = new List<ComponentRegistration>();
foreach (var name in Components)
var availability = compFact.GetComponentAvailability(name);
if (compFact.TryGetRegistration(name, out var registration)
&& availability == ComponentAvailability.Available)
{
var availability = compfact.GetComponentAvailability(name);
if (compfact.TryGetRegistration(name, out var registration)
&& availability == ComponentAvailability.Available)
{
_registrations.Add(registration);
}
else if (availability == ComponentAvailability.Unknown)
{
Logger.Warning($"Unknown component name {name} passed to EntityWhitelist!");
}
_registrations.Add(registration);
}
}
/// <summary>
/// Returns whether a given entity fits the whitelist.
/// </summary>
public bool IsValid(EntityUid uid, IEntityManager? entityManager = null)
{
if (Components != null && _registrations == null)
UpdateRegistrations();
IoCManager.Resolve(ref entityManager);
if (_registrations != null)
else if (availability == ComponentAvailability.Unknown)
{
foreach (var reg in _registrations)
{
if (entityManager.HasComponent(uid, reg.Type))
{
if (!RequireAll)
return true;
}
else if (RequireAll)
return false;
}
Logger.Warning($"Unknown component name {name} passed to EntityWhitelist!");
}
if (Tags != null && entityManager.TryGetComponent(uid, out TagComponent? tags))
{
var tagSystem = entityManager.System<TagSystem>();
return RequireAll ? tagSystem.HasAllTags(tags, Tags) : tagSystem.HasAnyTag(tags, Tags);
}
if (RequireAll)
return true;
return false;
}
}
/// <summary>
/// Returns whether a given entity fits the whitelist.
/// </summary>
public bool IsValid(EntityUid uid, IEntityManager? entityManager = null)
{
if (Components != null && _registrations == null)
UpdateRegistrations();
IoCManager.Resolve(ref entityManager);
if (_registrations != null)
{
foreach (var reg in _registrations)
{
if (entityManager.HasComponent(uid, reg.Type))
{
if (!RequireAll)
return true;
}
else if (RequireAll)
return false;
}
}
if (Sizes != null && entityManager.TryGetComponent(uid, out ItemComponent? itemComp))
{
if (Sizes.Contains(itemComp.Size))
return true;
}
if (Tags != null && entityManager.TryGetComponent(uid, out TagComponent? tags))
{
var tagSystem = entityManager.System<TagSystem>();
return RequireAll ? tagSystem.HasAllTags(tags, Tags) : tagSystem.HasAnyTag(tags, Tags);
}
if (RequireAll)
return true;
return false;
}
}

View File

@@ -7,7 +7,7 @@
license: "CC-BY-3.0"
copyright: "Modified from 'Meow 4.wav' by freesound user 'TRNGLE. The original audio was trimmed, split to mono, and converted from WAV to OGG format"
source: "https://freesound.org/people/TRNGLE/sounds/368006/"
- files: ["cat_meow2.ogg"]
license: "CC-BY-3.0"
copyright: "Created by freesound user 'TRNGLE. The original audio split to mono, and converted from WAV to OGG format"
@@ -117,24 +117,42 @@
license: "CC-BY-4.0"
copyright: "Audio is recorded/created by Pfranzen 'FreeSound.org'. The original audio was trimmed and renamed"
source: "https://freesound.org/people/pfranzen/sounds/322744/"
- files: ["dog_bark1.ogg"]
license: "CC0-1.0"
copyright: "Audio is recorded/created by KFerentchak 'FreeSound.org'. The original audio was trimmed and renamed"
source: "https://freesound.org/people/KFerentchak/sounds/235912/"
source: "https://freesound.org/people/KFerentchak/sounds/235912/"
- files: ["dog_bark2.ogg"]
license: "CC0-1.0"
copyright: "Audio is recorded/created by KFerentchak 'FreeSound.org'. The original audio was trimmed and renamed"
source: "https://freesound.org/people/KFerentchak/sounds/235912/"
source: "https://freesound.org/people/KFerentchak/sounds/235912/"
- files: ["dog_bark3.ogg"]
license: "CC0-1.0"
copyright: "Audio is recorded/created by KFerentchak 'FreeSound.org'. The original audio was trimmed and renamed"
source: "https://freesound.org/people/KFerentchak/sounds/235912/"
- files: ["nymph_chirp.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Taken from ParadiseSS13"
source: "https://github.com/ParadiseSS13/Paradise/commit/a34f1054cef5a44a67fdac3b67b811137c6071dd"
- files:
- fox1.ogg
- fox2.ogg
- fox3.ogg
- fox4.ogg
- fox5.ogg
- fox6.ogg
- fox7.ogg
- fox8.ogg
- fox9.ogg
- fox10.ogg
- fox11.ogg
- fox12.ogg
- fox13.ogg
- fox14.ogg
copyright: "Created by fujiwaranao"
license: "CC-BY-NC-SA-4.0"
source: "https://github.com/space-wizards/space-station-14/pull/27578"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -194,5 +194,12 @@ Entries:
id: 25
time: '2024-04-27T11:17:47.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/26930
- author: Titian3
changes:
- message: Added admin playtime tracking and included it in overall playtime tracking.
type: Add
id: 26
time: '2024-05-02T06:23:23.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/24945
Name: Admin
Order: 1

View File

@@ -1,153 +1,4 @@
Entries:
- author: Fildrance
changes:
- message: now cryo pod health analyzer updates atomatically!
type: Fix
id: 5997
time: '2024-02-21T14:47:23.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25109
- author: landwhale
changes:
- message: Drink jugs can no longer be used by chemistry in place of a beaker.
type: Fix
- message: Updated names of several drink jugs to differentiate them from chem-specific
variants.
type: Tweak
id: 5998
time: '2024-02-21T23:26:29.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25450
- author: Killerqu00
changes:
- message: EVA helmets are now quick-equippable again
type: Fix
id: 5999
time: '2024-02-21T23:41:26.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25448
- author: genderGeometries
changes:
- message: Crops are now harvested with decimal amounts of nutriment, vitamin, etc.
type: Fix
id: 6000
time: '2024-02-22T00:19:50.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25453
- author: Whisper
changes:
- message: Dragon ichor when eating restores the dragon's blood.
type: Add
- message: Increased Xenomorph and Dragon blood level to 650u.
type: Tweak
id: 6001
time: '2024-02-22T02:54:16.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25442
- author: Beck Thompson
changes:
- message: Radio jammer now uses 3 times less power.
type: Tweak
id: 6002
time: '2024-02-22T07:32:43.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25432
- author: metalgearsloth
changes:
- message: Added handheld crew monitor back just for CMO.
type: Add
id: 6003
time: '2024-02-22T11:00:35.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25439
- author: DrMelon
changes:
- message: Fixed incorrect job-specific Uplink items being given on occasion.
type: Fix
id: 6004
time: '2024-02-22T11:03:44.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/23179
- author: liltenhead
changes:
- message: Changed the syndicate assault borg to have a red flashlight.
type: Tweak
id: 6005
time: '2024-02-22T11:15:10.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25465
- author: lzk228
changes:
- message: Fix some items becomes bigger after turning in trash.
type: Fix
id: 6006
time: '2024-02-22T11:28:03.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25461
- author: ps3moira
changes:
- message: Added Large Wooden floors
type: Add
id: 6007
time: '2024-02-22T11:59:41.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25462
- author: Whisper
changes:
- message: Fixed reagent slime ghost role description.
type: Fix
id: 6008
time: '2024-02-22T12:18:46.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25466
- author: lzk228
changes:
- message: Galoshes are added to the Janidrobe.
type: Tweak
- message: Galoshes now make you slower.
type: Tweak
id: 6009
time: '2024-02-23T01:05:11.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25484
- author: PJB3005
changes:
- message: sorting of departments and jobs has been made consistent in various menus.
type: Tweak
id: 6010
time: '2024-02-23T04:04:44.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25486
- author: liltenhead
changes:
- message: Changed emergency welder's fuel count from 25 -> 50
type: Tweak
id: 6011
time: '2024-02-23T06:53:39.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25483
- author: Vasilis
changes:
- message: Fixed a bug where the centcom official/any job that is not supposed to
have a preference option appeared anyway.
type: Fix
id: 6012
time: '2024-02-23T13:19:52.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25496
- author: Erisfiregamer1
changes:
- message: Fixed a typo when forcefeeding someone.
type: Fix
id: 6013
time: '2024-02-24T01:45:02.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25512
- author: Beck Thompson
changes:
- message: The Fire Extinguishers safety can now only be toggled when in range.
type: Fix
id: 6014
time: '2024-02-25T02:29:16.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25534
- author: ArchPigeon
changes:
- message: Removed the ability for command or any antag-safe role to be initial
infected in zombie mode
type: Remove
id: 6015
time: '2024-02-25T02:40:49.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25529
- author: PolterTzi
changes:
- message: Cargo ordering multiple crates in one order should work now.
type: Fix
id: 6016
time: '2024-02-25T07:36:22.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/25518
- author: metalgearsloth
changes:
- message: Remove executions pending code rewrite.
@@ -3863,3 +3714,149 @@
id: 6496
time: '2024-04-29T13:12:30.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27484
- author: FungiFellow
changes:
- message: Syndi-Cats now have a Wideswing, 80% Explosion Resist, 6/6/15 Pierce/Slash/Structural
and step over glass shards with trained feline agility.
type: Tweak
id: 6497
time: '2024-04-29T17:09:30.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27408
- author: ShadowCommander
changes:
- message: Fixed microwave construction not creating a microwave on completion.
type: Fix
id: 6498
time: '2024-04-30T00:19:08.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27500
- author: lzk228
changes:
- message: Barozine is removed from hydroponics mutatuions.
type: Remove
id: 6499
time: '2024-04-30T03:01:38.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27512
- author: DogZeroX
changes:
- message: Changed the announcement of the immovable rod event.
type: Tweak
id: 6500
time: '2024-04-30T04:05:14.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27515
- author: DogZeroX
changes:
- message: Flares looped sound no longer have a massive range, and are much quieter.
type: Fix
id: 6501
time: '2024-04-30T06:49:35.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27521
- author: ERORR404V1
changes:
- message: Added chameleon projector in thief toolbox!
type: Add
id: 6502
time: '2024-04-30T12:14:06.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27491
- author: DamnFeds
changes:
- message: honkbot now uses a happy honk meal instead of box of hugs and clown's
rubber stamp. Honk!
type: Tweak
id: 6503
time: '2024-05-01T03:27:21.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27535
- author: Plykiya
changes:
- message: Inserting telecrystals no longer announces to everyone around you that
you inserted it.
type: Tweak
id: 6504
time: '2024-05-01T15:17:47.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27585
- author: Dezzzix
changes:
- message: Goldschlager empty bottle renamed to Gildlager empty bottle
type: Tweak
id: 6505
time: '2024-05-01T15:24:05.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27581
- author: FungiFellow
changes:
- message: Grilles now take Structural Damage
type: Tweak
id: 6506
time: '2024-05-01T22:23:42.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27596
- author: lzk228
changes:
- message: Immovable rod announce now happens at the end of event.
type: Tweak
id: 6507
time: '2024-05-01T22:26:22.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27587
- author: metalgearsloth
changes:
- message: Fix server performance dropping significantly.
type: Fix
id: 6508
time: '2024-05-02T00:18:38.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27528
- author: metalgearsloth
changes:
- message: Fix muzzle flash rotations.
type: Fix
- message: Fix large client performance drop.
type: Fix
id: 6509
time: '2024-05-02T02:40:07.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27533
- author: ElectroJr
changes:
- message: Fix gas analyzers not opening their UI when used in-hand.
type: Fix
id: 6510
time: '2024-05-02T06:00:01.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27610
- author: Plykiya
changes:
- message: Disarming a player now causes them to throw the disarmed item away from
them.
type: Tweak
id: 6511
time: '2024-05-02T12:32:47.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27589
- author: fujiwaranao
changes:
- message: Renault now has additional fox noises.
type: Add
id: 6512
time: '2024-05-02T12:35:11.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27578
- author: Doc-Michael
changes:
- message: CMO's Lab coat is now more resistant to chemical spills and minor cuts
type: Tweak
id: 6513
time: '2024-05-02T12:37:12.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27551
- author: Vasilis
changes:
- message: Removed airtight flaps from the construction menu.
type: Remove
id: 6514
time: '2024-05-02T14:49:54.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27619
- author: Lamrr
changes:
- message: Wine and beer bottles can now be inserted into the booze dispenser.
type: Fix
id: 6515
time: '2024-05-02T16:17:36.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27626
- author: deltanedas
changes:
- message: "Ducky slippers now make you waddle. \U0001F986\U0001F986"
type: Tweak
id: 6516
time: '2024-05-02T17:09:38.0000000+00:00'
url: https://github.com/space-wizards/space-station-14/pull/27628

View File

@@ -276,8 +276,8 @@
Tier: Nuclear Operative
- Name: "Andrew"
Tier: Revolutionary
- Name: "Jack"
Tier: Syndicate Agent
- Name: "Cooliano Rizzo"
Tier: Nuclear Operative
- Name: "Brandon Roughley"
Tier: Syndicate Agent
- Name: "Sean Lilly"
@@ -286,5 +286,37 @@
Tier: Revolutionary
- Name: "Hannah Dawson"
Tier: Syndicate Agent
- Name: "Jake Huxell"
Tier: Syndicate Agent
- Name: "Godfiend"
Tier: Revolutionary
- Name: "Jordon"
Tier: Revolutionary
- Name: "Luna Rose"
Tier: Revolutionary
- Name: "Ethan Maria"
Tier: Revolutionary
- Name: "Robyn Pothagan"
Tier: Revolutionary
- Name: "TheGungeonologist"
Tier: Revolutionary
- Name: "Ryan Kelly"
Tier: Revolutionary
- Name: "Jacob Scott"
Tier: Revolutionary
- Name: "Matthias Bechtold"
Tier: Revolutionary
- Name: "Joshua Meyer"
Tier: Nuclear Operative
- Name: "Roxy Rirumi"
Tier: Revolutionary
- Name: "Zero To template"
Tier: Nuclear Operative
- Name: "Hectik"
Tier: Revolutionary
- Name: "orange color"
Tier: Revolutionary
- Name: "Kristy Denniss"
Tier: Nuclear Operative
- Name: "Kerensky"
Tier: Revolutionary

Some files were not shown because too many files have changed in this diff Show More