Merge remote-tracking branch 'space-station-14/master' into 02-05-2024-upstream
This commit is contained in:
17
Content.Client/Animations/TrackUserComponent.cs
Normal file
17
Content.Client/Animations/TrackUserComponent.cs
Normal 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;
|
||||
}
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ public sealed class MouseRotatorSystem : SharedMouseRotatorSystem
|
||||
if (angleDir == curRot.GetCardinalDir())
|
||||
return;
|
||||
|
||||
RaisePredictiveEvent(new RequestMouseRotatorRotationSimpleEvent()
|
||||
RaisePredictiveEvent(new RequestMouseRotatorRotationSimpleEvent()
|
||||
{
|
||||
Direction = angleDir,
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -742,7 +742,6 @@ namespace Content.Client.Preferences.UI
|
||||
CharacterSlot = _preferencesManager.Preferences.SelectedCharacterIndex;
|
||||
|
||||
UpdateAntagRequirements();
|
||||
UpdateRoleRequirements();
|
||||
UpdateControls();
|
||||
ShowClothes.Pressed = true;
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Atmos.EntitySystems;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Content.Server.Atmos
|
||||
using Content.Shared.Atmos;
|
||||
|
||||
namespace Content.Server.Atmos
|
||||
{
|
||||
public interface IGasMixtureHolder
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Content.Shared.Atmos;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Binary.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Atmos;
|
||||
|
||||
namespace Content.Server.Atmos;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.Atmos;
|
||||
using Content.Shared.Atmos;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Behaviors;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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-";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.Atmos;
|
||||
using Content.Shared.Atmos;
|
||||
|
||||
namespace Content.Server.Mech.Components;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.Atmos;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
|
||||
@@ -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--;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}."),
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
16
Content.Shared/Atmos/GasMixtureStringRepresentation.cs
Normal file
16
Content.Shared/Atmos/GasMixtureStringRepresentation.cs
Normal 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();
|
||||
}
|
||||
14
Content.Shared/Atmos/Reactions/GasReactionEnums.cs
Normal file
14
Content.Shared/Atmos/Reactions/GasReactionEnums.cs
Normal 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,
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
BIN
Resources/Audio/Animals/fox1.ogg
Normal file
BIN
Resources/Audio/Animals/fox1.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Animals/fox10.ogg
Normal file
BIN
Resources/Audio/Animals/fox10.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Animals/fox11.ogg
Normal file
BIN
Resources/Audio/Animals/fox11.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Animals/fox12.ogg
Normal file
BIN
Resources/Audio/Animals/fox12.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Animals/fox13.ogg
Normal file
BIN
Resources/Audio/Animals/fox13.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Animals/fox14.ogg
Normal file
BIN
Resources/Audio/Animals/fox14.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Animals/fox2.ogg
Normal file
BIN
Resources/Audio/Animals/fox2.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Animals/fox3.ogg
Normal file
BIN
Resources/Audio/Animals/fox3.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Animals/fox4.ogg
Normal file
BIN
Resources/Audio/Animals/fox4.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Animals/fox5.ogg
Normal file
BIN
Resources/Audio/Animals/fox5.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Animals/fox6.ogg
Normal file
BIN
Resources/Audio/Animals/fox6.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Animals/fox7.ogg
Normal file
BIN
Resources/Audio/Animals/fox7.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Animals/fox8.ogg
Normal file
BIN
Resources/Audio/Animals/fox8.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Animals/fox9.ogg
Normal file
BIN
Resources/Audio/Animals/fox9.ogg
Normal file
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user