This commit is contained in:
Julian Giebel
2020-08-16 13:16:06 +02:00
844 changed files with 11970 additions and 5397 deletions

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using Content.Server.AI.Utility.AiLogic;
using Content.Server.GameObjects.Components.Movement;
using Content.Shared.GameObjects.Components.Movement;
using JetBrains.Annotations;
@@ -33,9 +32,6 @@ namespace Content.Server.GameObjects.EntitySystems.AI
{
base.Initialize();
// register entity query
EntityQuery = new TypeEntityQuery(typeof(AiControllerComponent));
var processors = _reflectionManager.GetAllChildren<AiLogicProcessor>();
foreach (var processor in processors)
{
@@ -49,18 +45,16 @@ namespace Content.Server.GameObjects.EntitySystems.AI
/// <inheritdoc />
public override void Update(float frameTime)
{
var entities = EntityManager.GetEntities(EntityQuery);
foreach (var entity in entities)
foreach (var comp in ComponentManager.EntityQuery<AiControllerComponent>())
{
if (_pauseManager.IsEntityPaused(entity))
if (_pauseManager.IsEntityPaused(comp.Owner))
{
continue;
}
ProcessorInitialize(comp);
var aiComp = entity.GetComponent<AiControllerComponent>();
ProcessorInitialize(aiComp);
var processor = aiComp.Processor;
var processor = comp.Processor;
processor.Update(frameTime);
}
@@ -111,7 +105,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI
var processorId = args[0];
var entId = new EntityUid(int.Parse(args[1]));
var ent = IoCManager.Resolve<IEntityManager>().GetEntity(entId);
var aiSystem = EntitySystem.Get<AiSystem>();
var aiSystem = Get<AiSystem>();
if (!aiSystem.ProcessorTypeExists(processorId))
{

View File

@@ -1,9 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Access;
using Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders;
using Content.Server.GameObjects.EntitySystems.Pathfinding;
using Content.Shared.AI;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
@@ -75,7 +73,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
// Plus this way we can check if everything is equal except for vision so an entity with a lower vision radius can use an entity with a higher vision radius' cached result
private Dictionary<ReachableArgs, Dictionary<PathfindingRegion, (TimeSpan CacheTime, HashSet<PathfindingRegion> Regions)>> _cachedAccessible =
new Dictionary<ReachableArgs, Dictionary<PathfindingRegion, (TimeSpan, HashSet<PathfindingRegion>)>>();
private readonly List<PathfindingRegion> _queuedCacheDeletions = new List<PathfindingRegion>();
#if DEBUG
@@ -91,7 +89,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
#endif
_mapmanager.OnGridRemoved += GridRemoved;
}
private void GridRemoved(GridId gridId)
{
_regions.Remove(gridId);
@@ -457,7 +455,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
/// <param name="y">This is already calculated in advance so may as well re-use it</param>
/// <returns></returns>
private PathfindingRegion CalculateNode(
PathfindingNode node,
PathfindingNode node,
Dictionary<PathfindingNode, PathfindingRegion> existingRegions,
HashSet<PathfindingRegion> chunkRegions,
int x, int y)
@@ -497,15 +495,15 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
!leftRegion.IsDoor)
{
// We'll try and connect the left node's region to the bottom region if they're separate (yay merge)
if (bottomNeighbor != null &&
if (bottomNeighbor != null &&
existingRegions.TryGetValue(bottomNeighbor, out bottomRegion) &&
bottomRegion != leftRegion &&
bottomRegion != leftRegion &&
!bottomRegion.IsDoor)
{
bottomRegion.Add(node);
existingRegions.Add(node, bottomRegion);
MergeInto(leftRegion, bottomRegion, existingRegions);
// Cleanup leftRegion
// MergeInto will remove it from the overall region chunk cache while we need to remove it from
// our short-term ones (chunkRegions and existingRegions)
@@ -515,7 +513,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
{
existingRegions[leftNode] = bottomRegion;
}
return bottomRegion;
}
@@ -549,7 +547,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
/// </summary>
/// <param name="source"></param>
/// <param name="target"></param>
private void MergeInto(PathfindingRegion source, PathfindingRegion target, Dictionary<PathfindingNode, PathfindingRegion> existingRegions = null)
private void MergeInto(PathfindingRegion source, PathfindingRegion target, Dictionary<PathfindingNode, PathfindingRegion> existingRegions = null)
{
DebugTools.AssertNotNull(source);
DebugTools.AssertNotNull(target);
@@ -586,7 +584,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
private void ClearCache(PathfindingRegion region)
{
DebugTools.Assert(region.Deleted);
// Need to forcibly clear cache for ourself and anything that includes us
foreach (var (_, cachedRegions) in _cachedAccessible)
{
@@ -599,7 +597,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
// We could just have GetVisionAccessible remove us if it can tell we're deleted but that
// seems like it could be unreliable
var regionsToClear = new List<PathfindingRegion>();
foreach (var (otherRegion, cache) in cachedRegions)
{
if (cache.Regions.Contains(region))
@@ -613,9 +611,9 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
cachedRegions.Remove(otherRegion);
}
}
#if DEBUG
if (_regions.TryGetValue(region.ParentChunk.GridId, out var chunks) &&
if (_regions.TryGetValue(region.ParentChunk.GridId, out var chunks) &&
chunks.TryGetValue(region.ParentChunk, out var regions))
{
DebugTools.Assert(!regions.Contains(region));
@@ -642,7 +640,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
_queuedCacheDeletions.Add(region);
region.Shutdown();
}
_regions[chunk.GridId].Remove(chunk);
}
@@ -673,7 +671,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
{
DebugTools.Assert(!region.Deleted);
}
DebugTools.Assert(chunkRegions.Count < Math.Pow(PathfindingChunk.ChunkSize, 2));
SendRegionsDebugMessage(chunk.GridId);
#endif

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic;
using Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders;
using Content.Server.GameObjects.EntitySystems.Pathfinding;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Map;
@@ -36,7 +35,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
{
startNode = pathfindingSystem.GetNode(pathfindingArgs.End);
}
PathfindingNode currentNode;
openTiles.Enqueue(startNode);
@@ -49,13 +48,13 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
// No distances stored so can just check closed tiles here
if (closedTiles.Contains(neighbor.TileRef)) continue;
closedTiles.Add(currentNode.TileRef);
// So currently tileCost gets the octile distance between the 2 so we'll also use that for our range check
var tileCost = PathfindingHelpers.GetTileCost(pathfindingArgs, startNode, neighbor);
var direction = PathfindingHelpers.RelativeDirection(neighbor, currentNode);
if (tileCost == null ||
tileCost > pathfindingArgs.Proximity ||
if (tileCost == null ||
tileCost > pathfindingArgs.Proximity ||
!PathfindingHelpers.DirectionTraversable(pathfindingArgs.CollisionMask, pathfindingArgs.Access, currentNode, direction))
{
continue;
@@ -67,4 +66,4 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
}
}
}
}
}

View File

@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Content.Server.GameObjects.EntitySystems.Pathfinding;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
{
@@ -16,7 +13,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
/// Bottom-left reference node of the region
/// </summary>
public PathfindingNode OriginNode { get; }
// The shape may be anything within the bounds of a chunk, this is just a quick way to do a bounds-check
/// <summary>
@@ -49,13 +46,13 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
{
// Tell our neighbors we no longer exist ;-/
var neighbors = new List<PathfindingRegion>(Neighbors);
for (var i = 0; i < neighbors.Count; i++)
{
var neighbor = neighbors[i];
neighbor.Neighbors.Remove(this);
}
_nodes.Clear();
Neighbors.Clear();
@@ -81,7 +78,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
{
xDistance = Math.Abs(xDistance + otherRegion.Width);
}
if (yDistance > 0)
{
yDistance -= Height;
@@ -90,7 +87,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
{
yDistance = Math.Abs(yDistance + otherRegion.Height);
}
return PathfindingHelpers.OctileDistance(xDistance, yDistance);
}
@@ -121,10 +118,10 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
{
Height = yHeight;
}
_nodes.Add(node);
}
// HashSet wasn't working correctly so uhh we got this.
public bool Equals(PathfindingRegion other)
{
@@ -141,4 +138,4 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
return OriginNode.GetHashCode();
}
}
}
}

View File

@@ -1,10 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Content.Server.GameObjects.EntitySystems.JobQueues;
using Content.Server.GameObjects.EntitySystems.Pathfinding;
using Content.Shared.AI;
using Robust.Shared.Map;
using Robust.Shared.Utility;
@@ -55,7 +53,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders
costSoFar[_startNode] = 0.0f;
var routeFound = false;
var count = 0;
while (frontier.Count > 0)
{
// Handle whether we need to pause if we've taken too long
@@ -69,7 +67,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders
return null;
}
}
// Actual pathfinding here
(_, currentNode) = frontier.Take();
if (currentNode.Equals(_endNode))

View File

@@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Content.Server.GameObjects.EntitySystems.JobQueues;
using Content.Server.GameObjects.EntitySystems.Pathfinding;
using Content.Shared.AI;
using Robust.Shared.Log;
using Robust.Shared.Map;
@@ -284,7 +282,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders
// If we're going diagonally need to check all cardinals.
// I tried just casting direction ints and offsets to make it smaller but brain no worky.
// From NorthEast we check (Closed / Open) S - SE, W - NW
PathfindingNode openNeighborOne = null;
PathfindingNode closedNeighborOne = null;
PathfindingNode openNeighborTwo = null;

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using Content.Server.GameObjects.EntitySystems.Pathfinding;
namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders
{

View File

@@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Content.Server.GameObjects.EntitySystems.Pathfinding;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Map;
@@ -23,7 +20,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
Chunk = chunk;
}
}
public class PathfindingChunk
{
public TimeSpan LastUpdate { get; private set; }
@@ -53,7 +50,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
CreateNode(tileRef);
}
}
Dirty();
}
@@ -71,7 +68,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
{
var pathfindingSystem = EntitySystem.Get<PathfindingSystem>();
var chunkGrid = pathfindingSystem.Graph[GridId];
for (var x = -1; x <= 1; x++)
{
for (var y = -1; y <= 1; y++)
@@ -159,7 +156,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
}
yield break;
}
// South edge
if (node.TileRef.Y == _indices.Y)

View File

@@ -1,9 +1,7 @@
using System;
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Access;
using Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible;
using Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders;
using Content.Server.GameObjects.EntitySystems.Pathfinding;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
@@ -38,40 +36,40 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
// If it's a diagonal we need to check NSEW to see if we can get to it and stop corner cutting, NE needs N and E etc.
// Given there's different collision layers stored for each node in the graph it's probably not worth it to cache this
// Also this will help with corner-cutting
PathfindingNode northNeighbor = null;
PathfindingNode southNeighbor = null;
PathfindingNode eastNeighbor = null;
PathfindingNode westNeighbor = null;
foreach (var neighbor in currentNode.GetNeighbors())
{
if (neighbor.TileRef.X == currentNode.TileRef.X &&
if (neighbor.TileRef.X == currentNode.TileRef.X &&
neighbor.TileRef.Y == currentNode.TileRef.Y + 1)
{
northNeighbor = neighbor;
continue;
}
if (neighbor.TileRef.X == currentNode.TileRef.X + 1 &&
}
if (neighbor.TileRef.X == currentNode.TileRef.X + 1 &&
neighbor.TileRef.Y == currentNode.TileRef.Y)
{
eastNeighbor = neighbor;
continue;
}
if (neighbor.TileRef.X == currentNode.TileRef.X &&
if (neighbor.TileRef.X == currentNode.TileRef.X &&
neighbor.TileRef.Y == currentNode.TileRef.Y - 1)
{
southNeighbor = neighbor;
continue;
}
if (neighbor.TileRef.X == currentNode.TileRef.X - 1 &&
}
if (neighbor.TileRef.X == currentNode.TileRef.X - 1 &&
neighbor.TileRef.Y == currentNode.TileRef.Y)
{
westNeighbor = neighbor;
continue;
}
}
}
switch (direction)
@@ -130,7 +128,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
return true;
}
public static Queue<TileRef> ReconstructPath(Dictionary<PathfindingNode, PathfindingNode> cameFrom, PathfindingNode current)
{
var running = new Stack<TileRef>();
@@ -244,7 +242,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
return 1.4f * dstX + (dstY - dstX);
}
public static float OctileDistance(TileRef endTile, TileRef startTile)
{
// "Fast Euclidean" / octile.

View File

@@ -3,16 +3,13 @@ using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Access;
using Content.Server.GameObjects.Components.Doors;
using Content.Server.GameObjects.EntitySystems.AI.Pathfinding;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.EntitySystems.Pathfinding
namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
{
public class PathfindingNode
{
@@ -20,7 +17,7 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
private readonly PathfindingChunk _parentChunk;
public TileRef TileRef { get; private set; }
/// <summary>
/// Whenever there's a change in the collision layers we update the mask as the graph has more reads than writes
/// </summary>
@@ -46,7 +43,7 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
public static bool IsRelevant(IEntity entity, ICollidableComponent collidableComponent)
{
if (entity.Transform.GridID == GridId.Invalid ||
if (entity.Transform.GridID == GridId.Invalid ||
(PathfindingSystem.TrackedCollisionLayers & collidableComponent.CollisionLayer) == 0)
{
return false;
@@ -66,7 +63,7 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
{
neighborChunks = ParentChunk.RelevantChunks(this).ToList();
}
for (var x = -1; x <= 1; x++)
{
for (var y = -1; y <= 1; y++)
@@ -112,7 +109,7 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
{
return ParentChunk.Nodes[chunkXOffset + 1, chunkYOffset];
}
neighborMapIndices = new MapIndices(TileRef.X + 1, TileRef.Y);
foreach (var neighbor in ParentChunk.GetNeighbors())
{
@@ -129,7 +126,7 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
{
return ParentChunk.Nodes[chunkXOffset + 1, chunkYOffset + 1];
}
neighborMapIndices = new MapIndices(TileRef.X + 1, TileRef.Y + 1);
foreach (var neighbor in ParentChunk.GetNeighbors())
{
@@ -146,7 +143,7 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
{
return ParentChunk.Nodes[chunkXOffset, chunkYOffset + 1];
}
neighborMapIndices = new MapIndices(TileRef.X, TileRef.Y + 1);
foreach (var neighbor in ParentChunk.GetNeighbors())
{
@@ -163,7 +160,7 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
{
return ParentChunk.Nodes[chunkXOffset - 1, chunkYOffset + 1];
}
neighborMapIndices = new MapIndices(TileRef.X - 1, TileRef.Y + 1);
foreach (var neighbor in ParentChunk.GetNeighbors())
{
@@ -180,7 +177,7 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
{
return ParentChunk.Nodes[chunkXOffset - 1, chunkYOffset];
}
neighborMapIndices = new MapIndices(TileRef.X - 1, TileRef.Y);
foreach (var neighbor in ParentChunk.GetNeighbors())
{
@@ -197,7 +194,7 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
{
return ParentChunk.Nodes[chunkXOffset - 1, chunkYOffset - 1];
}
neighborMapIndices = new MapIndices(TileRef.X - 1, TileRef.Y - 1);
foreach (var neighbor in ParentChunk.GetNeighbors())
{
@@ -214,7 +211,7 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
{
return ParentChunk.Nodes[chunkXOffset, chunkYOffset - 1];
}
neighborMapIndices = new MapIndices(TileRef.X, TileRef.Y - 1);
foreach (var neighbor in ParentChunk.GetNeighbors())
{
@@ -231,7 +228,7 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
{
return ParentChunk.Nodes[chunkXOffset + 1, chunkYOffset - 1];
}
neighborMapIndices = new MapIndices(TileRef.X + 1, TileRef.Y - 1);
foreach (var neighbor in ParentChunk.GetNeighbors())
{
@@ -276,9 +273,9 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
}
return;
}
DebugTools.Assert((PathfindingSystem.TrackedCollisionLayers & collidableComponent.CollisionLayer) != 0);
if (!collidableComponent.Anchored)
{
_physicsLayers.Add(entity, collidableComponent.CollisionLayer);
@@ -304,12 +301,12 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding
if (_physicsLayers.ContainsKey(entity))
{
_physicsLayers.Remove(entity);
}
}
else if (_accessReaders.ContainsKey(entity))
{
_accessReaders.Remove(entity);
ParentChunk.Dirty();
}
}
else if (_blockedCollidables.ContainsKey(entity))
{
_blockedCollidables.Remove(entity);

View File

@@ -1,15 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using Content.Server.GameObjects.Components.Access;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders;
using Content.Server.GameObjects.EntitySystems.JobQueues;
using Content.Server.GameObjects.EntitySystems.JobQueues.Queues;
using Content.Server.GameObjects.EntitySystems.Pathfinding;
using Content.Shared.Physics;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems;
@@ -275,9 +271,9 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
/// <param name="entity"></param>
private void HandleEntityAdd(IEntity entity)
{
if (entity.Deleted ||
if (entity.Deleted ||
_lastKnownPositions.ContainsKey(entity) ||
!entity.TryGetComponent(out ICollidableComponent collidableComponent) ||
!entity.TryGetComponent(out ICollidableComponent collidableComponent) ||
!PathfindingNode.IsRelevant(entity, collidableComponent))
{
return;
@@ -315,23 +311,23 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
private void HandleEntityMove(MoveEvent moveEvent)
{
// If we've moved to space or the likes then remove us.
if (moveEvent.Sender.Deleted ||
if (moveEvent.Sender.Deleted ||
!moveEvent.Sender.TryGetComponent(out ICollidableComponent collidableComponent) ||
!PathfindingNode.IsRelevant(moveEvent.Sender, collidableComponent))
{
HandleEntityRemove(moveEvent.Sender);
return;
}
// Memory leak protection until grid parenting confirmed fix / you REALLY need the performance
var gridBounds = _mapManager.GetGrid(moveEvent.Sender.Transform.GridID).WorldBounds;
if (!gridBounds.Contains(moveEvent.Sender.Transform.WorldPosition))
{
HandleEntityRemove(moveEvent.Sender);
return;
}
// If we move from space to a grid we may need to start tracking it.
if (!_lastKnownPositions.TryGetValue(moveEvent.Sender, out var oldNode))
{
@@ -342,7 +338,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
// The pathfinding graph is tile-based so first we'll check if they're on a different tile and if we need to update.
// If you get entities bigger than 1 tile wide you'll need some other system so god help you.
var newTile = _mapManager.GetGrid(moveEvent.NewPosition.GridID).GetTileRef(moveEvent.NewPosition);
if (oldNode == null || oldNode.TileRef == newTile)
{
return;

View File

@@ -8,14 +8,11 @@ using Content.Server.GameObjects.EntitySystems.AI.Pathfinding;
using Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders;
using Content.Server.GameObjects.EntitySystems.JobQueues;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Server.GameObjects;
using Robust.Server.Interfaces.Timing;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
@@ -27,10 +24,9 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
public sealed class AiSteeringSystem : EntitySystem
{
// http://www.red3d.com/cwr/papers/1999/gdc99steer.html for a steering overview
#pragma warning disable 649
[Dependency] private IMapManager _mapManager;
[Dependency] private IEntityManager _entityManager;
[Dependency] private IPauseManager _pauseManager;
#pragma warning restore 649
private PathfindingSystem _pathfindingSystem;
@@ -45,9 +41,9 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
/// How close we need to get to the center of each tile
/// </summary>
private const float TileTolerance = 0.8f;
private Dictionary<IEntity, IAiSteeringRequest> RunningAgents => _agentLists[_listIndex];
// We'll cycle the running list every tick as all we're doing is getting a vector2 for the
// agent's steering. Should help a lot given this is the most expensive operator by far.
// The AI will keep moving, it's just it'll keep moving in its existing direction.
@@ -55,31 +51,31 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
private readonly List<Dictionary<IEntity, IAiSteeringRequest>> _agentLists = new List<Dictionary<IEntity, IAiSteeringRequest>>(AgentListCount);
private const int AgentListCount = 2;
private int _listIndex;
// Cache nextGrid
private readonly Dictionary<IEntity, GridCoordinates> _nextGrid = new Dictionary<IEntity, GridCoordinates>();
/// <summary>
/// Current live paths for AI
/// </summary>
private readonly Dictionary<IEntity, Queue<TileRef>> _paths = new Dictionary<IEntity, Queue<TileRef>>();
/// <summary>
/// Pathfinding request jobs we're waiting on
/// </summary>
private readonly Dictionary<IEntity, (CancellationTokenSource CancelToken, Job<Queue<TileRef>> Job)> _pathfindingRequests =
private readonly Dictionary<IEntity, (CancellationTokenSource CancelToken, Job<Queue<TileRef>> Job)> _pathfindingRequests =
new Dictionary<IEntity, (CancellationTokenSource, Job<Queue<TileRef>>)>();
/// <summary>
/// Keep track of how long we've been in 1 position and re-path if it's been too long
/// </summary>
private readonly Dictionary<IEntity, int> _stuckCounter = new Dictionary<IEntity, int>();
/// <summary>
/// Get a fixed position for the target entity; if they move then re-path
/// </summary>
private readonly Dictionary<IEntity, GridCoordinates> _entityTargetPosition = new Dictionary<IEntity, GridCoordinates>();
// Anti-Stuck
// Given the collision avoidance can lead to twitching need to store a reference position and check if we've been near this too long
private readonly Dictionary<IEntity, GridCoordinates> _stuckPositions = new Dictionary<IEntity, GridCoordinates>();
@@ -88,7 +84,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
{
base.Initialize();
_pathfindingSystem = Get<PathfindingSystem>();
for (var i = 0; i < AgentListCount; i++)
{
_agentLists.Add(new Dictionary<IEntity, IAiSteeringRequest>());
@@ -111,14 +107,14 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
var agentList = _agentLists[i];
// Register shouldn't be called twice; if it is then someone dun fucked up
DebugTools.Assert(!agentList.ContainsKey(entity));
if (agentList.Count < lowestListCount)
{
lowestListCount = agentList.Count;
lowestListIndex = i;
}
}
_agentLists[lowestListIndex].Add(entity, steeringRequest);
}
@@ -133,7 +129,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
{
controller.VelocityDir = Vector2.Zero;
}
if (_pathfindingRequests.TryGetValue(entity, out var request))
{
switch (request.Job.Status)
@@ -157,7 +153,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
}
_pathfindingRequests.Remove(entity);
}
if (_paths.ContainsKey(entity))
{
_paths.Remove(entity);
@@ -177,7 +173,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
{
_entityTargetPosition.Remove(entity);
}
foreach (var agentList in _agentLists)
{
if (agentList.ContainsKey(entity))
@@ -214,7 +210,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
{
var result = Steer(agent, steering);
steering.Status = result;
switch (result)
{
case SteeringStatus.Pending:
@@ -255,7 +251,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
controller.VelocityDir = Vector2.Zero;
return SteeringStatus.Pending;
}
// Validation
// Check if we can even arrive -> Currently only samegrid movement supported
if (entity.Transform.GridID != steeringRequest.TargetGrid.GridID)
@@ -263,7 +259,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
controller.VelocityDir = Vector2.Zero;
return SteeringStatus.NoPath;
}
// Check if we have arrived
var targetDistance = (entity.Transform.MapPosition.Position - steeringRequest.TargetMap.Position).Length;
if (targetDistance <= steeringRequest.ArrivalDistance)
@@ -274,7 +270,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
controller.VelocityDir = Vector2.Zero;
return SteeringStatus.Arrived;
}
// Handle pathfinding job
// If we still have an existing path then keep following that until the new path arrives
if (_pathfindingRequests.TryGetValue(entity, out var pathRequest) && pathRequest.Job.Status == JobStatus.Finished)
@@ -297,7 +293,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
controller.VelocityDir = Vector2.Zero;
return SteeringStatus.NoPath;
}
// If we're closer to next tile then we don't want to walk backwards to our tile's center
UpdatePath(entity, path);
@@ -319,7 +315,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
RequestPath(entity, steeringRequest);
return SteeringStatus.Pending;
}
var ignoredCollision = new List<IEntity>();
// Check if the target entity has moved - If so then re-path
// TODO: Patch the path from the target's position back towards us, stopping if it ever intersects the current path
@@ -331,19 +327,19 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
controller.VelocityDir = Vector2.Zero;
return SteeringStatus.NoPath;
}
// Check if target's moved too far
if (_entityTargetPosition.TryGetValue(entity, out var targetGrid) && (entitySteer.TargetGrid.Position - targetGrid.Position).Length >= entitySteer.TargetMaxMove)
{
// We'll just repath and keep following the existing one until we get a new one
RequestPath(entity, steeringRequest);
}
ignoredCollision.Add(entitySteer.Target);
}
HandleStuck(entity);
// TODO: Probably need a dedicated queuing solver (doorway congestion FML)
// Get the target grid (either next tile or target itself) and pass it in to the steering behaviors
// If there's nowhere to go then just stop and wait
@@ -353,14 +349,14 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
controller.VelocityDir = Vector2.Zero;
return SteeringStatus.NoPath;
}
// Validate that we can even get to the next grid (could probably just check if we can use nextTile if we're not near the target grid)
if (!_pathfindingSystem.CanTraverse(entity, nextGrid.Value))
{
controller.VelocityDir = Vector2.Zero;
return SteeringStatus.NoPath;
}
// Now we can /finally/ move
var movementVector = Vector2.Zero;
@@ -370,10 +366,10 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
movementVector += Seek(entity, nextGrid.Value);
if (CollisionAvoidanceEnabled)
{
movementVector += CollisionAvoidance(entity, movementVector, ignoredCollision);
movementVector += CollisionAvoidance(entity, movementVector, ignoredCollision);
}
// Group behaviors would also go here e.g. separation, cohesion, alignment
// Move towards it
DebugTools.Assert(movementVector != new Vector2(float.NaN, float.NaN));
controller.VelocityDir = movementVector.Normalized;
@@ -391,7 +387,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
{
return;
}
var cancelToken = new CancellationTokenSource();
var gridManager = _mapManager.GetGrid(entity.Transform.GridID);
var startTile = gridManager.GetTileRef(entity.Transform.GridPosition);
@@ -403,7 +399,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
}
var access = AccessReader.FindAccessTags(entity);
var job = _pathfindingSystem.RequestPath(new PathfindingArgs(
entity.Uid,
access,
@@ -423,11 +419,11 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
private void UpdatePath(IEntity entity, Queue<TileRef> path)
{
_pathfindingRequests.Remove(entity);
var entityTile = _mapManager.GetGrid(entity.Transform.GridID).GetTileRef(entity.Transform.GridPosition);
var tile = path.Dequeue();
var closestDistance = PathfindingHelpers.OctileDistance(entityTile, tile);
for (var i = 0; i < path.Count; i++)
{
tile = path.Peek();
@@ -441,7 +437,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
break;
}
}
_paths[entity] = path;
}
@@ -458,20 +454,20 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
{
_nextGrid.Remove(entity);
}
// If no tiles left just move towards the target (if we're close)
if (!_paths.ContainsKey(entity) || _paths[entity].Count == 0)
{
if ((steeringRequest.TargetGrid.Position - entity.Transform.GridPosition.Position).Length <= 2.0f)
{
return steeringRequest.TargetGrid;
return steeringRequest.TargetGrid;
}
// Too far so we need a re-path
return null;
}
if (!_nextGrid.TryGetValue(entity, out var nextGrid) ||
if (!_nextGrid.TryGetValue(entity, out var nextGrid) ||
(nextGrid.Position - entity.Transform.GridPosition.Position).Length <= TileTolerance)
{
UpdateGridCache(entity);
@@ -526,7 +522,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
{
return;
}
// Okay now we're stuck
_paths.Remove(entity);
_stuckCounter[entity] = 0;
@@ -580,8 +576,8 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
{
return Vector2.Zero;
}
if (target.TryGetComponent(out IPhysicsComponent physicsComponent))
if (target.TryGetComponent(out ICollidableComponent physicsComponent))
{
var targetDistance = (targetPos.Position - entityPos.Position);
targetPos = targetPos.Offset(physicsComponent.LinearVelocity * targetDistance);
@@ -603,7 +599,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
{
return Vector2.Zero;
}
// We'll check tile-by-tile
// Rewriting this frequently so not many comments as they'll go stale
// I realise this is bad so please rewrite it ;-;
@@ -636,21 +632,22 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
//Pathfinding updates are deferred so this may not be done yet.
if (physicsEntity.Deleted) continue;
// if we're moving in the same direction then ignore
// So if 2 entities are moving towards each other and both detect a collision they'll both move in the same direction
// i.e. towards the right
if (physicsEntity.TryGetComponent(out IPhysicsComponent physicsComponent) &&
if (physicsEntity.TryGetComponent(out ICollidableComponent physicsComponent) &&
Vector2.Dot(physicsComponent.LinearVelocity, direction) > 0)
{
continue;
}
var centerGrid = physicsEntity.Transform.GridPosition;
// Check how close we are to center of tile and get the inverse; if we're closer this is stronger
var additionalVector = (centerGrid.Position - entityGridCoords.Position);
var distance = additionalVector.Length;
// If we're too far no point, if we're close then cap it at the normalized vector
distance = Math.Clamp(2.5f - distance, 0.0f, 1.0f);
distance = FloatMath.Clamp(2.5f - distance, 0.0f, 1.0f);
additionalVector = new Angle(90 * distance).RotateVec(additionalVector);
avoidanceVector += additionalVector;
// if we do need to avoid that means we'll have to lookahead for the next tile

View File

@@ -5,7 +5,7 @@ using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Map;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
/// <summary>
/// This interface gives components behavior on getting destoyed.

View File

@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using Content.Server.Atmos;
using Content.Server.GameObjects.Components.Atmos;
using JetBrains.Annotations;
using Robust.Server.Interfaces.Timing;
using Robust.Shared.GameObjects;
@@ -10,6 +9,7 @@ using Robust.Shared.GameObjects.Components.Map;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
namespace Content.Server.GameObjects.EntitySystems
@@ -17,18 +17,14 @@ namespace Content.Server.GameObjects.EntitySystems
[UsedImplicitly]
public class AtmosphereSystem : EntitySystem
{
#pragma warning disable 649
[Robust.Shared.IoC.Dependency] private readonly IMapManager _mapManager = default!;
[Robust.Shared.IoC.Dependency] private readonly IEntityManager _entityManager = default!;
[Robust.Shared.IoC.Dependency] private readonly IPauseManager _pauseManager = default!;
#pragma warning restore 649
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPauseManager _pauseManager = default!;
public override void Initialize()
{
base.Initialize();
_mapManager.TileChanged += OnTileChanged;
EntityQuery = new MultipleTypeEntityQuery(new List<Type>(){typeof(IGridAtmosphereComponent)});
}
public IGridAtmosphereComponent? GetGridAtmosphere(GridId gridId)
@@ -36,7 +32,7 @@ namespace Content.Server.GameObjects.EntitySystems
// TODO Return space grid atmosphere for invalid grids or grids with no atmos
var grid = _mapManager.GetGrid(gridId);
if (!_entityManager.TryGetEntity(grid.GridEntityId, out var gridEnt)) return null;
if (!EntityManager.TryGetEntity(grid.GridEntityId, out var gridEnt)) return null;
return gridEnt.TryGetComponent(out IGridAtmosphereComponent atmos) ? atmos : null;
}
@@ -45,13 +41,12 @@ namespace Content.Server.GameObjects.EntitySystems
{
base.Update(frameTime);
foreach (var gridEnt in RelevantEntities)
foreach (var (mapGridComponent, gridAtmosphereComponent) in EntityManager.ComponentManager.EntityQuery<IMapGridComponent, IGridAtmosphereComponent>())
{
var grid = gridEnt.GetComponent<IMapGridComponent>();
if (_pauseManager.IsGridPaused(grid.GridIndex))
if (_pauseManager.IsGridPaused(mapGridComponent.GridIndex))
continue;
gridEnt.GetComponent<IGridAtmosphereComponent>().Update(frameTime);
gridAtmosphereComponent.Update(frameTime);
}
}

View File

@@ -1,23 +1,17 @@
using Content.Server.GameObjects.Components.Power.Chargers;
using Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerReceiverUsers;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
internal class BaseChargerSystem : EntitySystem
internal sealed class BaseChargerSystem : EntitySystem
{
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(BaseCharger));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var comp in ComponentManager.EntityQuery<BaseCharger>())
{
entity.GetComponent<BaseCharger>().OnUpdate(frameTime);
comp.OnUpdate(frameTime);
}
}
}

View File

@@ -1,31 +1,26 @@
using Content.Server.GameObjects.Components.Power.PowerNetComponents;
using JetBrains.Annotations;
using Robust.Server.Interfaces.Timing;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.IoC;
namespace Content.Server.GameObjects.EntitySystems
{
internal class BatteryDischargerSystem : EntitySystem
[UsedImplicitly]
internal sealed class BatteryDischargerSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IPauseManager _pauseManager;
#pragma warning restore 649
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(BatteryDischargerComponent));
}
[Dependency] private readonly IPauseManager _pauseManager = default!;
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var comp in ComponentManager.EntityQuery<BatteryDischargerComponent>())
{
if (_pauseManager.IsEntityPaused(entity))
if (_pauseManager.IsEntityPaused(comp.Owner))
{
continue;
}
entity.GetComponent<BatteryDischargerComponent>().Update(frameTime);
comp.Update(frameTime);
}
}
}

View File

@@ -1,31 +1,26 @@
using Content.Server.GameObjects.Components.Power.PowerNetComponents;
using JetBrains.Annotations;
using Robust.Server.Interfaces.Timing;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.IoC;
namespace Content.Server.GameObjects.EntitySystems
{
internal class BatteryStorageSystem : EntitySystem
[UsedImplicitly]
internal sealed class BatteryStorageSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IPauseManager _pauseManager;
#pragma warning restore 649
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(BatteryStorageComponent));
}
[Dependency] private readonly IPauseManager _pauseManager = default!;
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var comp in ComponentManager.EntityQuery<BatteryStorageComponent>())
{
if (_pauseManager.IsEntityPaused(entity))
if (_pauseManager.IsEntityPaused(comp.Owner))
{
continue;
}
entity.GetComponent<BatteryStorageComponent>().Update(frameTime);
comp.Update(frameTime);
}
}
}

View File

@@ -1,21 +1,16 @@
using Content.Server.GameObjects.Components.Metabolism;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
/// <summary>
/// Triggers metabolism updates for <see cref="BloodstreamComponent"/>
/// Triggers metabolism updates for <see cref="BloodstreamComponent"/>
/// </summary>
[UsedImplicitly]
public class BloodstreamSystem : EntitySystem
internal sealed class BloodstreamSystem : EntitySystem
{
private float _accumulatedFrameTime;
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(BloodstreamComponent));
}
public override void Update(float frameTime)
{
@@ -23,12 +18,11 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
_accumulatedFrameTime += frameTime;
if (_accumulatedFrameTime > 1.0f)
{
foreach (var entity in RelevantEntities)
foreach (var component in ComponentManager.EntityQuery<BloodstreamComponent>())
{
var comp = entity.GetComponent<BloodstreamComponent>();
comp.OnUpdate(_accumulatedFrameTime);
component.OnUpdate(_accumulatedFrameTime);
}
_accumulatedFrameTime = 0.0f;
_accumulatedFrameTime -= 1.0f;
}
}
}

View File

@@ -2,36 +2,25 @@
using Content.Server.GameObjects.EntitySystems.Click;
using JetBrains.Annotations;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class BuckleSystem : EntitySystem
internal sealed class BuckleSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(BuckleComponent));
UpdatesAfter.Add(typeof(InteractionSystem));
UpdatesAfter.Add(typeof(InputSystem));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var buckle in ComponentManager.EntityQuery<BuckleComponent>())
{
if (!entity.TryGetComponent(out BuckleComponent buckle))
{
continue;
}
buckle.Update();
}
}

View File

@@ -4,7 +4,7 @@ using JetBrains.Annotations;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
/// <summary>
/// This interface gives components behavior on whether entities solution (implying SolutionComponent is in place) is changed

View File

@@ -4,7 +4,7 @@ using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Localization;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.EntitySystems.Click
@@ -20,7 +20,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click
static ExamineSystem()
{
_entityNotFoundMessage = new FormattedMessage();
_entityNotFoundMessage.AddText("That entity doesn't exist");
_entityNotFoundMessage.AddText(Loc.GetString("That entity doesn't exist"));
}
public override void Initialize()

View File

@@ -1,17 +1,7 @@
using Content.Server.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Input;
using Content.Shared.GameObjects.EntitySystems;
using JetBrains.Annotations;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Players;
using Robust.Shared.Random;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public sealed class CombatModeSystem : SharedCombatModeSystem

View File

@@ -1,15 +1,16 @@
using System;
using System.Collections.Generic;
using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.Construction;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Interactable;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Stack;
using Content.Server.GameObjects.EntitySystems.Click;
using Content.Server.Utility;
using Content.Shared.Construction;
using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.Components.Interactable;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Interfaces.GameObjects.Components;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
@@ -32,7 +33,7 @@ namespace Content.Server.GameObjects.EntitySystems
/// The server-side implementation of the construction system, which is used for constructing entities in game.
/// </summary>
[UsedImplicitly]
internal class ConstructionSystem : Shared.GameObjects.EntitySystems.SharedConstructionSystem
internal class ConstructionSystem : SharedConstructionSystem
{
#pragma warning disable 649
[Dependency] private readonly IPrototypeManager _prototypeManager;
@@ -337,7 +338,7 @@ namespace Content.Server.GameObjects.EntitySystems
}
// OK WE'RE GOOD CONSTRUCTION STARTED.
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Items/deconstruct.ogg", placingEnt);
Get<AudioSystem>().PlayFromEntity("/Audio/Items/deconstruct.ogg", placingEnt);
if (prototype.Stages.Count == 2)
{
// Exactly 2 stages, so don't make an intermediate frame.

View File

@@ -1,33 +1,19 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Conveyor;
using Content.Shared.GameObjects.Components.Conveyor;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class ConveyorSystem : EntitySystem
internal sealed class ConveyorSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(ConveyorComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var comp in ComponentManager.EntityQuery<ConveyorComponent>())
{
if (!entity.TryGetComponent(out ConveyorComponent conveyor))
{
continue;
}
conveyor.Update(frameTime);
comp.Update(frameTime);
}
}
}

View File

@@ -1,25 +1,18 @@
using Content.Server.GameObjects.Components.Disposal;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class DisposableSystem : EntitySystem
internal sealed class DisposableSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(DisposalHolderComponent));
}
public override void Update(float frameTime)
{
foreach (var disposable in RelevantEntities)
foreach (var comp in ComponentManager.EntityQuery<DisposalHolderComponent>())
{
disposable.GetComponent<DisposalHolderComponent>().Update(frameTime);
comp.Update(frameTime);
}
}
}

View File

@@ -1,25 +1,17 @@
using Content.Server.GameObjects.Components.Disposal;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class DisposalUnitSystem : EntitySystem
internal sealed class DisposalUnitSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(DisposalUnitComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var comp in ComponentManager.EntityQuery<DisposalUnitComponent>())
{
entity.GetComponent<DisposalUnitComponent>().Update(frameTime);
comp.Update(frameTime);
}
}
}

View File

@@ -1,39 +1,40 @@
#nullable enable
using System;
using System.Threading.Tasks;
using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.Damage;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Mobs;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Map;
namespace Content.Server.GameObjects.EntitySystems
namespace Content.Server.GameObjects.EntitySystems.DoAfter
{
public sealed class DoAfter
{
public Task<DoAfterStatus> AsTask { get; }
private TaskCompletionSource<DoAfterStatus> Tcs { get;}
public DoAfterEventArgs EventArgs;
public TimeSpan StartTime { get; }
public float Elapsed { get; set; }
public GridCoordinates UserGrid { get; }
public GridCoordinates TargetGrid { get; }
private bool _tookDamage;
public DoAfterStatus Status => AsTask.IsCompletedSuccessfully ? AsTask.Result : DoAfterStatus.Running;
// NeedHand
private string? _activeHand;
private ItemComponent? _activeItem;
public DoAfter(DoAfterEventArgs eventArgs)
{
EventArgs = eventArgs;
@@ -57,7 +58,7 @@ namespace Content.Server.GameObjects.EntitySystems
_activeHand = handsComponent.ActiveHand;
_activeItem = handsComponent.GetActiveHand;
}
Tcs = new TaskCompletionSource<DoAfterStatus>();
AsTask = Tcs.Task;
}
@@ -79,15 +80,15 @@ namespace Content.Server.GameObjects.EntitySystems
default:
throw new ArgumentOutOfRangeException();
}
Elapsed += frameTime;
if (IsFinished())
{
Tcs.SetResult(DoAfterStatus.Finished);
return;
}
if (IsCancelled())
{
Tcs.SetResult(DoAfterStatus.Cancelled);
@@ -101,13 +102,13 @@ namespace Content.Server.GameObjects.EntitySystems
{
return true;
}
// TODO :Handle inertia in space.
if (EventArgs.BreakOnUserMove && EventArgs.User.Transform.GridPosition != UserGrid)
{
return true;
}
if (EventArgs.BreakOnTargetMove && EventArgs.Target!.Transform.GridPosition != TargetGrid)
{
return true;
@@ -129,7 +130,7 @@ namespace Content.Server.GameObjects.EntitySystems
{
return true;
}
if (EventArgs.NeedHand)
{
if (!EventArgs.User.TryGetComponent(out HandsComponent handsComponent))
@@ -169,4 +170,4 @@ namespace Content.Server.GameObjects.EntitySystems
return true;
}
}
}
}

View File

@@ -2,9 +2,10 @@
using System;
using System.Threading;
using Robust.Shared.Interfaces.GameObjects;
// ReSharper disable UnassignedReadonlyField
namespace Content.Server.GameObjects.EntitySystems
namespace Content.Server.GameObjects.EntitySystems.DoAfter
{
public sealed class DoAfterEventArgs
{

View File

@@ -3,13 +3,13 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.Damage;
using JetBrains.Annotations;
using Robust.Server.Interfaces.Timing;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.IoC;
namespace Content.Server.GameObjects.EntitySystems
namespace Content.Server.GameObjects.EntitySystems.DoAfter
{
[UsedImplicitly]
public sealed class DoAfterSystem : EntitySystem
@@ -19,18 +19,18 @@ namespace Content.Server.GameObjects.EntitySystems
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var comp in ComponentManager.EntityQuery<DoAfterComponent>())
{
if (_pauseManager.IsGridPaused(comp.Owner.Transform.GridID)) continue;
var cancelled = new List<DoAfter>(0);
var finished = new List<DoAfter>(0);
foreach (var doAfter in comp.DoAfters)
{
doAfter.Run(frameTime);
switch (doAfter.Status)
{
case DoAfterStatus.Running:
@@ -59,7 +59,7 @@ namespace Content.Server.GameObjects.EntitySystems
finished.Clear();
}
}
/// <summary>
/// Tasks that are delayed until the specified time has passed
/// These can be potentially cancelled by the user moving or when other things happen.
@@ -74,7 +74,7 @@ namespace Content.Server.GameObjects.EntitySystems
var doAfterComponent = eventArgs.User.GetComponent<DoAfterComponent>();
doAfterComponent.Add(doAfter);
DamageableComponent? damageableComponent = null;
// TODO: If the component's deleted this may not get unsubscribed?
if (eventArgs.BreakOnDamage && eventArgs.User.TryGetComponent(out damageableComponent))
{
@@ -82,12 +82,12 @@ namespace Content.Server.GameObjects.EntitySystems
}
await doAfter.AsTask;
if (damageableComponent != null)
{
damageableComponent.Damaged -= doAfter.HandleDamage;
}
return doAfter.Status;
}
}
@@ -98,4 +98,4 @@ namespace Content.Server.GameObjects.EntitySystems
Cancelled,
Finished,
}
}
}

View File

@@ -1,4 +1,5 @@
using Robust.Shared.GameObjects.Systems;
using Content.Server.GameObjects.Components.Doors;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{

View File

@@ -1,10 +1,5 @@
using Content.Server.GameObjects.Components.Atmos;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using System;
using System.Collections.Generic;
using System.Text;
namespace Content.Server.GameObjects.EntitySystems
{

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Content.Server.Atmos;
using Content.Server.GameObjects.Components.Atmos;
using Content.Shared.Atmos;
using Content.Shared.GameObjects.EntitySystems;
@@ -10,13 +9,10 @@ using JetBrains.Annotations;
using Robust.Server.Interfaces.Player;
using Robust.Server.Player;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.EntitySystems
{

View File

@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Gravity;
@@ -6,9 +5,7 @@ using Content.Server.GameObjects.Components.Mobs;
using JetBrains.Annotations;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
@@ -16,45 +13,37 @@ using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Random;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class GravitySystem: EntitySystem
internal sealed class GravitySystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager;
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IRobustRandom _random;
#pragma warning restore 649
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
private const float GravityKick = 100.0f;
private const uint ShakeTimes = 10;
private Dictionary<GridId, uint> _gridsToShake;
private Dictionary<GridId, uint> _gridsToShake = new Dictionary<GridId, uint>();
private float internalTimer = 0.0f;
public override void Initialize()
{
EntityQuery = new TypeEntityQuery<GravityGeneratorComponent>();
_gridsToShake = new Dictionary<GridId, uint>();
}
private float _internalTimer = 0.0f;
public override void Update(float frameTime)
{
internalTimer += frameTime;
_internalTimer += frameTime;
var gridsWithGravity = new List<GridId>();
foreach (var entity in RelevantEntities)
foreach (var generator in ComponentManager.EntityQuery<GravityGeneratorComponent>())
{
var generator = entity.GetComponent<GravityGeneratorComponent>();
if (generator.NeedsUpdate)
{
generator.UpdateState();
}
if (generator.Status == GravityGeneratorStatus.On)
{
gridsWithGravity.Add(entity.Transform.GridID);
gridsWithGravity.Add(generator.Owner.Transform.GridID);
}
}
@@ -71,10 +60,10 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
}
}
if (internalTimer > 0.2f)
if (_internalTimer > 0.2f)
{
ShakeGrids();
internalTimer = 0.0f;
_internalTimer = 0.0f;
}
}
@@ -93,7 +82,7 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
{
if (player.AttachedEntity == null
|| player.AttachedEntity.Transform.GridID != gridId) continue;
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Effects/alert.ogg", player.AttachedEntity);
Get<AudioSystem>().PlayFromEntity("/Audio/Effects/alert.ogg", player.AttachedEntity);
}
}

View File

@@ -1,21 +1,16 @@
using Content.Server.GameObjects.Components.Interactable;
using Robust.Shared.GameObjects;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
public class HandHeldLightSystem : EntitySystem
[UsedImplicitly]
internal sealed class HandHeldLightSystem : EntitySystem
{
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(HandheldLightComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var comp in ComponentManager.EntityQuery<HandheldLightComponent>())
{
var comp = entity.GetComponent<HandheldLightComponent>();
comp.OnUpdate(frameTime);
}
}

View File

@@ -1,8 +1,14 @@
using System.Linq;
using System;
using System.Linq;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Stack;
using Content.Server.GameObjects.EntitySystems.Click;
using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Server.Throw;
using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Input;
using JetBrains.Annotations;
using Robust.Server.GameObjects.EntitySystemMessages;
@@ -15,18 +21,8 @@ using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Players;
using System;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.GameObjects.EntitySystems;
using Content.Server.GameObjects;
using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.EntitySystems.Click;
using Content.Shared.Interfaces;
using Robust.Shared.Maths;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
internal sealed class HandsSystem : EntitySystem

View File

@@ -1,30 +1,24 @@
using Content.Server.GameObjects.Components.Nutrition;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class HungerSystem : EntitySystem
internal sealed class HungerSystem : EntitySystem
{
private float _accumulatedFrameTime;
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(HungerComponent));
}
public override void Update(float frameTime)
{
_accumulatedFrameTime += frameTime;
if (_accumulatedFrameTime > 1.0f)
{
foreach (var entity in RelevantEntities)
foreach (var comp in ComponentManager.EntityQuery<HungerComponent>())
{
var comp = entity.GetComponent<HungerComponent>();
comp.OnUpdate(_accumulatedFrameTime);
}
_accumulatedFrameTime = 0.0f;
_accumulatedFrameTime -= 1.0f;
}
}
}

View File

@@ -1,24 +1,19 @@
using Content.Server.GameObjects.Components.Instruments;
using Robust.Shared.GameObjects;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
public class InstrumentSystem : EntitySystem
[UsedImplicitly]
internal sealed class InstrumentSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(InstrumentComponent));
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var entity in RelevantEntities)
foreach (var component in ComponentManager.EntityQuery<InstrumentComponent>())
{
entity.GetComponent<InstrumentComponent>().Update(frameTime);
component.Update(frameTime);
}
}
}

View File

@@ -1,5 +1,3 @@
using System.Collections;
namespace Content.Server.GameObjects.EntitySystems.JobQueues
{
public interface IJob

View File

@@ -1,21 +1,16 @@
using Content.Server.GameObjects.Components.Research;
using Robust.Shared.GameObjects;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
public class LatheSystem : EntitySystem
[UsedImplicitly]
internal sealed class LatheSystem : EntitySystem
{
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(LatheComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var comp in ComponentManager.EntityQuery<LatheComponent>())
{
var comp = entity.GetComponent<LatheComponent>();
if (comp.Producing == false && comp.Queue.Count > 0)
{
comp.Produce(comp.Queue.Dequeue());

View File

@@ -1,37 +1,23 @@
using Content.Server.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using System;
using System.Collections.Generic;
using System.Text;
namespace Content.Server.GameObjects.EntitySystems
{
class ListeningSystem : EntitySystem
internal sealed class ListeningSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager;
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
#pragma warning restore 649
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(ListeningComponent));
}
[Dependency] private readonly IMapManager _mapManager = default!;
public void PingListeners(IEntity source, GridCoordinates sourcePos, string message)
{
foreach (var listener in RelevantEntities)
foreach (var listener in ComponentManager.EntityQuery<ListeningComponent>())
{
var dist = sourcePos.Distance(_mapManager, listener.Transform.GridPosition);
var dist = sourcePos.Distance(_mapManager, listener.Owner.Transform.GridPosition);
listener.GetComponent<ListeningComponent>()
.PassSpeechData(message, source, dist);
listener.PassSpeechData(message, source, dist);
}
}
}

View File

@@ -1,21 +1,16 @@
using Content.Server.GameObjects.Components.Medical;
using Robust.Shared.GameObjects;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
public class MedicalScannerSystem : EntitySystem
[UsedImplicitly]
internal sealed class MedicalScannerSystem : EntitySystem
{
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(MedicalScannerComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var comp in ComponentManager.EntityQuery<MedicalScannerComponent>())
{
var comp = entity.GetComponent<MedicalScannerComponent>();
comp.Update(frameTime);
}
}

View File

@@ -5,7 +5,7 @@ using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Maths;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
public sealed class MeleeWeaponSystem : EntitySystem
{

View File

@@ -1,23 +1,17 @@
using Content.Server.GameObjects.Components.Kitchen;
using Robust.Shared.GameObjects;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
public class MicrowaveSystem : EntitySystem
[UsedImplicitly]
internal sealed class MicrowaveSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(MicrowaveComponent));
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var entity in RelevantEntities)
foreach (var comp in ComponentManager.EntityQuery<MicrowaveComponent>())
{
var comp = entity.GetComponent<MicrowaveComponent>();
comp.OnUpdate();
}
}

View File

@@ -1,6 +1,6 @@
#nullable enable
using Content.Server.GameObjects;
using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Movement;
@@ -58,23 +58,13 @@ namespace Content.Server.GameObjects.EntitySystems
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var (moverComponent, collidableComponent) in EntityManager.ComponentManager.EntityQuery<IMoverComponent, ICollidableComponent>())
{
var entity = moverComponent.Owner;
if (_pauseManager.IsEntityPaused(entity))
{
continue;
}
var mover = entity.GetComponent<IMoverComponent>();
var physics = entity.GetComponent<IPhysicsComponent>();
if (entity.TryGetComponent<ICollidableComponent>(out var collider))
{
UpdateKinematics(entity.Transform, mover, physics, collider);
}
else
{
UpdateKinematics(entity.Transform, mover, physics);
}
UpdateKinematics(entity.Transform, moverComponent, collidableComponent);
}
}
@@ -93,7 +83,7 @@ namespace Content.Server.GameObjects.EntitySystems
ev.Entity.RemoveComponent<PlayerInputMoverComponent>();
}
if (ev.Entity.TryGetComponent(out IPhysicsComponent physics) &&
if (ev.Entity.TryGetComponent(out ICollidableComponent physics) &&
physics.TryGetController(out MoverController controller))
{
controller.StopMoving();

View File

@@ -24,14 +24,12 @@ using Robust.Shared.Players;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class PointingSystem : EntitySystem
internal sealed class PointingSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
#pragma warning restore 649
private static readonly TimeSpan PointDelay = TimeSpan.FromSeconds(0.5f);
@@ -156,8 +154,6 @@ namespace Content.Server.GameObjects.EntitySystems
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
EntityQuery = new TypeEntityQuery(typeof(PointingArrowComponent));
CommandBinds.Builder
.Bind(ContentKeyFunctions.Point, new PointerInputCmdHandler(TryPoint))
.Register<PointingSystem>();
@@ -173,9 +169,9 @@ namespace Content.Server.GameObjects.EntitySystems
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var component in ComponentManager.EntityQuery<PointingArrowComponent>())
{
entity.GetComponent<PointingArrowComponent>().Update(frameTime);
component.Update(frameTime);
}
}
}

View File

@@ -1,23 +1,17 @@
using Content.Server.GameObjects.Components.Movement;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class PortalSystem : EntitySystem
internal sealed class PortalSystem : EntitySystem
{
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(ServerPortalComponent));
}
// TODO: Someone refactor portals
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var comp in ComponentManager.EntityQuery<ServerPortalComponent>())
{
var comp = entity.GetComponent<ServerPortalComponent>();
comp.OnUpdate();
}
}

View File

@@ -1,37 +1,32 @@
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using System.Collections.Generic;
using Content.Server.GameObjects.Components.NodeContainer.NodeGroups;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using System.Collections.Generic;
using Robust.Shared.IoC;
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using JetBrains.Annotations;
using Robust.Server.Interfaces.Timing;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.IoC;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
public sealed class ApcSystem : EntitySystem
[UsedImplicitly]
internal sealed class PowerApcSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IPauseManager _pauseManager;
#pragma warning restore 649
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(ApcComponent));
}
[Dependency] private readonly IPauseManager _pauseManager = default!;
public override void Update(float frameTime)
{
var uniqueApcNets = new HashSet<IApcNet>(); //could be improved by maintaining set instead of getting collection every frame
foreach (var entity in RelevantEntities)
foreach (var apc in ComponentManager.EntityQuery<ApcComponent>())
{
if (_pauseManager.IsEntityPaused(entity))
if (_pauseManager.IsEntityPaused(apc.Owner))
{
continue;
}
var apc = entity.GetComponent<ApcComponent>();
uniqueApcNets.Add(apc.Net);
entity.GetComponent<ApcComponent>().Update();
apc.Update();
}
foreach (var apcNet in uniqueApcNets)
{
apcNet.Update(frameTime);

View File

@@ -1,21 +1,17 @@
using Content.Server.GameObjects.Components.Power;
using Robust.Shared.GameObjects;
using Content.Server.GameObjects.Components.Power.PowerNetComponents;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
internal class PowerSmesSystem : EntitySystem
{
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(SmesComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var comp in ComponentManager.EntityQuery<SmesComponent>())
{
entity.GetComponent<SmesComponent>().OnUpdate();
comp.OnUpdate();
}
}
}

View File

@@ -1,16 +1,6 @@
using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.Components.Power.PowerNetComponents;
using JetBrains.Annotations;
using Content.Shared.Physics;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Physics;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.Physics;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using System;
namespace Content.Server.GameObjects.EntitySystems
{
@@ -18,27 +8,22 @@ namespace Content.Server.GameObjects.EntitySystems
/// Responsible for updating solar control consoles.
/// </summary>
[UsedImplicitly]
public class PowerSolarControlConsoleSystem : EntitySystem
internal sealed class PowerSolarControlConsoleSystem : EntitySystem
{
/// <summary>
/// Timer used to avoid updating the UI state every frame (which would be overkill)
/// </summary>
private float UpdateTimer = 0f;
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(SolarControlConsoleComponent));
}
private float _updateTimer;
public override void Update(float frameTime)
{
UpdateTimer += frameTime;
if (UpdateTimer >= 1)
_updateTimer += frameTime;
if (_updateTimer >= 1)
{
UpdateTimer = 0;
foreach (var entity in RelevantEntities)
_updateTimer -= 1;
foreach (var component in ComponentManager.EntityQuery<SolarControlConsoleComponent>())
{
entity.GetComponent<SolarControlConsoleComponent>().UpdateUIState();
component.UpdateUIState();
}
}
}

View File

@@ -1,33 +1,26 @@
using Content.Server.GameObjects.Components.Power;
using JetBrains.Annotations;
using System;
using System.Linq;
using Content.Server.GameObjects.Components.Power.PowerNetComponents;
using Content.Shared.Physics;
using Robust.Shared.GameObjects;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Physics;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.Physics;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using System;
using System.Linq;
using CannyFastMath;
using Math = CannyFastMath.Math;
using MathF = CannyFastMath.MathF;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
/// <summary>
/// Responsible for maintaining the solar-panel sun angle and updating <see cref='SolarPanelComponent'/> coverage.
/// </summary>
[UsedImplicitly]
public class PowerSolarSystem : EntitySystem
internal sealed class PowerSolarSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private IGameTiming _gameTiming;
[Dependency] private IRobustRandom _robustRandom;
#pragma warning restore 649
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
/// <summary>
/// The current sun angle.
@@ -78,9 +71,8 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(SolarPanelComponent));
// Initialize the sun to something random
TowardsSun = Math.TAU * _robustRandom.NextDouble();
TowardsSun = MathHelper.TwoPi * _robustRandom.NextDouble();
SunAngularVelocity = Angle.FromDegrees(0.1 + ((_robustRandom.NextDouble() - 0.5) * 0.05));
}
@@ -94,12 +86,11 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
TotalPanelPower = 0;
foreach (var entity in RelevantEntities)
foreach (var panel in ComponentManager.EntityQuery<SolarPanelComponent>())
{
// There's supposed to be rotational logic here, but that implies putting it somewhere.
entity.Transform.WorldRotation = TargetPanelRotation;
panel.Owner.Transform.WorldRotation = TargetPanelRotation;
var panel = entity.GetComponent<SolarPanelComponent>();
if (panel.TimeOfNextCoverageUpdate < _gameTiming.CurTime)
{
// Setup the next coverage check.

View File

@@ -1,32 +1,23 @@
using Content.Server.GameObjects.Components.Projectiles;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
internal sealed class ProjectileSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(ProjectileComponent));
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var entity in RelevantEntities)
foreach (var component in ComponentManager.EntityQuery<ProjectileComponent>())
{
var component = entity.GetComponent<ProjectileComponent>();
component.TimeLeft -= frameTime;
if (component.TimeLeft <= 0)
{
entity.Delete();
component.Owner.Delete();
}
}
}

View File

@@ -1,19 +1,19 @@
using Content.Server.GameObjects.Components.Fluids;
using Robust.Shared.GameObjects;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
public class PuddleSystem : EntitySystem
[UsedImplicitly]
internal sealed class PuddleSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(PuddleComponent));
var mapManager = IoCManager.Resolve<IMapManager>();
mapManager.TileChanged += HandleTileChanged;
}
@@ -28,17 +28,14 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
private void HandleTileChanged(object sender, TileChangedEventArgs eventArgs)
{
// If this gets hammered you could probably queue up all the tile changes every tick but I doubt that would ever happen.
var entities = EntityManager.GetEntities(EntityQuery);
foreach (var entity in entities)
foreach (var (puddle, snapGrid) in ComponentManager.EntityQuery<PuddleComponent, SnapGridComponent>())
{
// If the tile becomes space then delete it (potentially change by design)
if (eventArgs.NewTile.GridIndex == entity.Transform.GridID &&
entity.TryGetComponent(out SnapGridComponent snapGridComponent) &&
snapGridComponent.Position == eventArgs.NewTile.GridIndices &&
if (eventArgs.NewTile.GridIndex == puddle.Owner.Transform.GridID &&
snapGrid.Position == eventArgs.NewTile.GridIndices &&
eventArgs.NewTile.Tile.IsEmpty)
{
entity.Delete();
puddle.Owner.Delete();
break; // Currently it's one puddle per tile, if that changes remove this
}
}

View File

@@ -1,27 +1,13 @@
using Content.Server.GameObjects.Components.Interactable;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Robust.Shared.GameObjects;
using System.Collections.Generic;
using Content.Server.GameObjects.Components;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Content.Server.GameObjects.EntitySystems
{
class RadioSystem : EntitySystem
internal sealed class RadioSystem : EntitySystem
{
private List<string> _messages;
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(RadioComponent));
_messages = new List<string>();
}
private readonly List<string> _messages = new List<string>();
public void SpreadMessage(IEntity source, string message)
{
@@ -32,10 +18,9 @@ namespace Content.Server.GameObjects.EntitySystems
_messages.Add(message);
foreach (var radioEntity in RelevantEntities)
foreach (var radio in ComponentManager.EntityQuery<RadioComponent>())
{
var radio = radioEntity.GetComponent<RadioComponent>();
if (radioEntity == source || !radio.RadioOn)
if (radio.Owner == source || !radio.RadioOn)
{
continue;
}

View File

@@ -1,25 +1,17 @@
using Content.Server.GameObjects.Components.Recycling;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class RecyclerSystem : EntitySystem
internal sealed class RecyclerSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(RecyclerComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var component in ComponentManager.EntityQuery<RecyclerComponent>())
{
entity.GetComponent<RecyclerComponent>().Update(frameTime);
component.Update(frameTime);
}
}
}

View File

@@ -4,7 +4,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
public class ResearchSystem : EntitySystem
{

View File

@@ -1,25 +1,17 @@
using Content.Server.GameObjects.Components.Pointing;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class RoguePointingSystem : EntitySystem
internal sealed class RoguePointingSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(RoguePointingArrowComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var component in ComponentManager.EntityQuery<RoguePointingArrowComponent>())
{
entity.GetComponent<RoguePointingArrowComponent>().Update(frameTime);
component.Update(frameTime);
}
}
}

View File

@@ -6,7 +6,7 @@ using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Timer = Robust.Shared.Timers.Timer;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
public class RoundEndSystem : EntitySystem
{

View File

@@ -0,0 +1,89 @@
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Damage;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.StationEvents;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Damage;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.GameObjects.EntitySystems.StationEvents
{
[UsedImplicitly]
public sealed class RadiationPulseSystem : EntitySystem
{
// Rather than stuffing around with collidables and checking entities on initialize etc. we'll just tick over
// for each entity in range. Seemed easier than checking entities on spawn, then checking collidables, etc.
// Especially considering each pulse is a big chonker, + no circle hitboxes yet.
private TypeEntityQuery _speciesQuery;
/// <summary>
/// Damage works with ints so we'll just accumulate damage and once we hit this threshold we'll apply it.
/// </summary>
/// This also server to stop spamming the damagethreshold with 1 damage continuously.
private const int DamageThreshold = 10;
private Dictionary<IEntity, float> _accumulatedDamage = new Dictionary<IEntity, float>();
public override void Initialize()
{
base.Initialize();
_speciesQuery = new TypeEntityQuery(typeof(SpeciesComponent));
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var anyPulses = false;
foreach (var comp in ComponentManager.EntityQuery<RadiationPulseComponent>())
{
anyPulses = true;
foreach (var species in EntityManager.GetEntities(_speciesQuery))
{
// Work out if we're in range and accumulate more damage
// If we've hit the DamageThreshold we'll also apply that damage to the mob
// If we're really lagging server can apply multiples of the DamageThreshold at once
if (species.Transform.MapID != comp.Owner.Transform.MapID) continue;
if ((species.Transform.WorldPosition - comp.Owner.Transform.WorldPosition).Length > comp.Range)
{
continue;
}
var totalDamage = frameTime * comp.DPS;
if (!_accumulatedDamage.TryGetValue(species, out var accumulatedSpecies))
{
_accumulatedDamage[species] = 0.0f;
}
totalDamage += accumulatedSpecies;
_accumulatedDamage[species] = totalDamage;
if (totalDamage < DamageThreshold) continue;
if (!species.TryGetComponent(out DamageableComponent damageableComponent)) continue;
var damageMultiple = (int) (totalDamage / DamageThreshold);
_accumulatedDamage[species] = totalDamage % DamageThreshold;
damageableComponent.TakeDamage(DamageType.Heat, damageMultiple * DamageThreshold, comp.Owner, comp.Owner);
}
}
if (anyPulses)
{
return;
}
// probably don't need to worry about clearing this at roundreset unless you have a radiation pulse at roundstart
// (which is currently not possible)
_accumulatedDamage.Clear();
}
}
}

View File

@@ -0,0 +1,321 @@
using System;
using System.Collections.Generic;
using System.Text;
using Content.Server.StationEvents;
using JetBrains.Annotations;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
namespace Content.Server.GameObjects.EntitySystems.StationEvents
{
[UsedImplicitly]
public sealed class StationEventSystem : EntitySystem
{
// Somewhat based off of TG's implementation of events
public StationEvent CurrentEvent { get; private set; }
public IReadOnlyCollection<StationEvent> StationEvents => _stationEvents;
private List<StationEvent> _stationEvents = new List<StationEvent>();
private const float MinimumTimeUntilFirstEvent = 600;
/// <summary>
/// How long until the next check for an event runs
/// </summary>
/// Default value is how long until first event is allowed
private float _timeUntilNextEvent = MinimumTimeUntilFirstEvent;
/// <summary>
/// Whether random events can run
/// </summary>
/// If disabled while an event is running (even if admin run) it will disable it
public bool Enabled
{
get => _enabled;
set
{
if (_enabled == value)
{
return;
}
_enabled = value;
CurrentEvent?.Shutdown();
CurrentEvent = null;
}
}
private bool _enabled = true;
/// <summary>
/// Admins can get a list of all events available to run, regardless of whether their requirements have been met
/// </summary>
/// <returns></returns>
public string GetEventNames()
{
StringBuilder result = new StringBuilder();
foreach (var stationEvent in _stationEvents)
{
result.Append(stationEvent.Name + "\n");
}
return result.ToString();
}
/// <summary>
/// Admins can forcibly run events by passing in the Name
/// </summary>
/// <param name="name">The exact string for Name, without localization</param>
/// <returns></returns>
public string RunEvent(string name)
{
// Could use a dictionary but it's such a minor thing, eh.
// Wasn't sure on whether to localize this given it's a command
var upperName = name.ToUpperInvariant();
foreach (var stationEvent in _stationEvents)
{
if (stationEvent.Name.ToUpperInvariant() != upperName)
{
continue;
}
CurrentEvent?.Shutdown();
CurrentEvent = stationEvent;
stationEvent.Startup();
return Loc.GetString("Running event ") + stationEvent.Name;
}
// I had string interpolation but lord it made it hard to read
return Loc.GetString("No event named ") + name;
}
/// <summary>
/// Randomly run a valid event immediately, ignoring earlieststart
/// </summary>
/// <returns></returns>
public string RunRandomEvent()
{
var availableEvents = AvailableEvents(true);
var randomEvent = FindEvent(availableEvents);
if (randomEvent == null)
{
return Loc.GetString("No valid events available");
}
CurrentEvent?.Shutdown();
CurrentEvent = randomEvent;
CurrentEvent.Startup();
return Loc.GetString("Running ") + randomEvent.Name;
}
/// <summary>
/// Admins can stop the currently running event (if applicable) and reset the timer
/// </summary>
/// <returns></returns>
public string StopEvent()
{
string resultText;
if (CurrentEvent == null)
{
resultText = Loc.GetString("No event running currently");
}
else
{
resultText = Loc.GetString("Stopped event ") + CurrentEvent.Name;
CurrentEvent.Shutdown();
CurrentEvent = null;
}
ResetTimer();
return resultText;
}
public override void Initialize()
{
base.Initialize();
var reflectionManager = IoCManager.Resolve<IReflectionManager>();
var typeFactory = IoCManager.Resolve<IDynamicTypeFactory>();
foreach (var type in reflectionManager.GetAllChildren(typeof(StationEvent)))
{
if (type.IsAbstract) continue;
var stationEvent = (StationEvent) typeFactory.CreateInstance(type);
_stationEvents.Add(stationEvent);
}
}
public override void Update(float frameTime)
{
base.Update(frameTime);
if (!Enabled)
{
return;
}
// Keep running the current event
if (CurrentEvent != null)
{
CurrentEvent.Update(frameTime);
// Shutdown the event and set the timer for the next event
if (!CurrentEvent.Running)
{
CurrentEvent.Shutdown();
CurrentEvent = null;
ResetTimer();
}
return;
}
if (_timeUntilNextEvent > 0)
{
_timeUntilNextEvent -= frameTime;
return;
}
// No point hammering this trying to find events if none are available
var stationEvent = FindEvent(AvailableEvents());
if (stationEvent == null)
{
ResetTimer();
}
else
{
CurrentEvent = stationEvent;
}
}
/// <summary>
/// Reset the event timer once the event is done.
/// </summary>
private void ResetTimer()
{
var robustRandom = IoCManager.Resolve<IRobustRandom>();
// 5 - 15 minutes. TG does 3-10 but that's pretty frequent
_timeUntilNextEvent = robustRandom.Next(300, 900);
}
/// <summary>
/// Pick a random event from the available events at this time, also considering their weightings.
/// </summary>
/// <returns></returns>
private StationEvent FindEvent(List<StationEvent> availableEvents)
{
if (availableEvents.Count == 0)
{
return null;
}
var sumOfWeights = 0;
foreach (var stationEvent in availableEvents)
{
sumOfWeights += (int) stationEvent.Weight;
}
var robustRandom = IoCManager.Resolve<IRobustRandom>();
sumOfWeights = robustRandom.Next(sumOfWeights);
foreach (var stationEvent in availableEvents)
{
sumOfWeights -= (int) stationEvent.Weight;
if (sumOfWeights <= 0)
{
return stationEvent;
}
}
return null;
}
/// <summary>
/// Gets the events that have met their player count, time-until start, etc.
/// </summary>
/// <param name="ignoreEarliestStart"></param>
/// <returns></returns>
private List<StationEvent> AvailableEvents(bool ignoreEarliestStart = false)
{
TimeSpan currentTime;
var playerCount = IoCManager.Resolve<IPlayerManager>().PlayerCount;
// playerCount does a lock so we'll just keep the variable here
if (!ignoreEarliestStart)
{
currentTime = IoCManager.Resolve<IGameTiming>().CurTime;
}
else
{
currentTime = TimeSpan.Zero;
}
var result = new List<StationEvent>();
foreach (var stationEvent in _stationEvents)
{
if (CanRun(stationEvent, playerCount, currentTime))
{
result.Add(stationEvent);
}
}
return result;
}
private bool CanRun(StationEvent stationEvent, int playerCount, TimeSpan currentTime)
{
if (stationEvent.MaxOccurrences.HasValue && stationEvent.Occurrences >= stationEvent.MaxOccurrences.Value)
{
return false;
}
if (playerCount < stationEvent.MinimumPlayers)
{
return false;
}
if (currentTime != TimeSpan.Zero && currentTime.TotalMinutes < stationEvent.EarliestStart)
{
return false;
}
return true;
}
public void ResettingCleanup()
{
if (CurrentEvent != null && CurrentEvent.Running)
{
CurrentEvent.Shutdown();
CurrentEvent = null;
}
foreach (var stationEvent in _stationEvents)
{
stationEvent.Occurrences = 0;
}
_timeUntilNextEvent = MinimumTimeUntilFirstEvent;
}
public override void Shutdown()
{
base.Shutdown();
CurrentEvent?.Shutdown();
}
}
}

View File

@@ -1,21 +1,16 @@
using Content.Server.GameObjects.Components.Nutrition;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
/// <summary>
/// Triggers digestion updates on <see cref="StomachComponent"/>
/// Triggers digestion updates on <see cref="StomachComponent"/>
/// </summary>
[UsedImplicitly]
public class StomachSystem : EntitySystem
internal sealed class StomachSystem : EntitySystem
{
private float _accumulatedFrameTime;
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(StomachComponent));
}
public override void Update(float frameTime)
{
@@ -23,12 +18,11 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
_accumulatedFrameTime += frameTime;
if (_accumulatedFrameTime > 1.0f)
{
foreach (var entity in RelevantEntities)
foreach (var component in ComponentManager.EntityQuery<StomachComponent>())
{
var comp = entity.GetComponent<StomachComponent>();
comp.OnUpdate(_accumulatedFrameTime);
component.OnUpdate(_accumulatedFrameTime);
}
_accumulatedFrameTime = 0.0f;
_accumulatedFrameTime -= 1.0f;
}
}
}

View File

@@ -1,16 +1,15 @@
using System.Collections.Generic;
using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.EntitySystems.Click;
using JetBrains.Annotations;
using Robust.Server.GameObjects.EntitySystemMessages;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
class StorageSystem : EntitySystem
[UsedImplicitly]
internal sealed class StorageSystem : EntitySystem
{
private readonly List<IPlayerSession> _sessionCache = new List<IPlayerSession>();
@@ -19,16 +18,14 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
{
SubscribeLocalEvent<EntRemovedFromContainerMessage>(HandleEntityRemovedFromContainer);
SubscribeLocalEvent<EntInsertedIntoContainerMessage>(HandleEntityInsertedIntoContainer);
EntityQuery = new TypeEntityQuery(typeof(ServerStorageComponent));
}
/// <inheritdoc />
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var component in ComponentManager.EntityQuery<ServerStorageComponent>())
{
CheckSubscribedEntities(entity);
CheckSubscribedEntities(component);
}
}
@@ -52,9 +49,8 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
}
}
private void CheckSubscribedEntities(IEntity entity)
private void CheckSubscribedEntities(ServerStorageComponent storageComp)
{
var storageComp = entity.GetComponent<ServerStorageComponent>();
// We have to cache the set of sessions because Unsubscribe modifies the original.
_sessionCache.Clear();
@@ -63,8 +59,8 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
if (_sessionCache.Count == 0)
return;
var storagePos = entity.Transform.WorldPosition;
var storageMap = entity.Transform.MapID;
var storagePos = storageComp.Owner.Transform.WorldPosition;
var storageMap = storageComp.Owner.Transform.MapID;
foreach (var session in _sessionCache)
{

View File

@@ -1,30 +1,21 @@
using System;
using Content.Server.GameObjects.Components;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Maths;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class StressTestMovementSystem : EntitySystem
internal sealed class StressTestMovementSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery<StressTestMovementComponent>();
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var entity in RelevantEntities)
foreach (var stressTest in ComponentManager.EntityQuery<StressTestMovementComponent>())
{
var stressTest = entity.GetComponent<StressTestMovementComponent>();
var transform = entity.Transform;
var transform = stressTest.Owner.Transform;
stressTest.Progress += frameTime;

View File

@@ -1,27 +1,19 @@
using Content.Server.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Shared.GameObjects;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
public class StunSystem : EntitySystem
[UsedImplicitly]
internal sealed class StunSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(StunnableComponent));
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var entity in RelevantEntities)
foreach (var component in ComponentManager.EntityQuery<StunnableComponent>())
{
entity.GetComponent<StunnableComponent>().Update(frameTime);
component.Update(frameTime);
}
}
}

View File

@@ -1,21 +1,16 @@
using Content.Server.GameObjects;
using Robust.Shared.GameObjects;
using Content.Server.GameObjects.Components.Temperature;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
class TemperatureSystem : EntitySystem
[UsedImplicitly]
internal sealed class TemperatureSystem : EntitySystem
{
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(TemperatureComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
foreach (var comp in ComponentManager.EntityQuery<TemperatureComponent>())
{
var comp = entity.GetComponent<TemperatureComponent>();
comp.OnUpdate(frameTime);
}
}

View File

@@ -1,30 +1,24 @@
using Content.Server.GameObjects.Components.Nutrition;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class ThirstSystem : EntitySystem
internal sealed class ThirstSystem : EntitySystem
{
private float _accumulatedFrameTime;
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(ThirstComponent));
}
public override void Update(float frameTime)
{
_accumulatedFrameTime += frameTime;
if (_accumulatedFrameTime > 1.0f)
{
foreach (var entity in RelevantEntities)
foreach (var component in ComponentManager.EntityQuery<ThirstComponent>())
{
var comp = entity.GetComponent<ThirstComponent>();
comp.OnUpdate(_accumulatedFrameTime);
component.OnUpdate(_accumulatedFrameTime);
}
_accumulatedFrameTime = 0.0f;
_accumulatedFrameTime -= 1.0f;
}
}
}

View File

@@ -1,7 +1,6 @@
using Content.Server.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.Components.Mobs;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
@@ -9,33 +8,24 @@ using Robust.Shared.IoC;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class TimedOverlayRemovalSystem : EntitySystem
internal sealed class TimedOverlayRemovalSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IGameTiming _gameTiming;
#pragma warning restore 649
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(ServerOverlayEffectsComponent));
}
[Dependency] private readonly IGameTiming _gameTiming = default!;
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var entity in RelevantEntities)
foreach (var component in ComponentManager.EntityQuery<ServerOverlayEffectsComponent>())
{
var effectsComponent = entity.GetComponent<ServerOverlayEffectsComponent>();
foreach (var overlay in effectsComponent.ActiveOverlays.ToArray())
foreach (var overlay in component.ActiveOverlays.ToArray())
{
if (overlay.TryGetOverlayParameter<TimedOverlayParameter>(out var parameter))
{
if (parameter.StartedAt + parameter.Length <= _gameTiming.CurTime.TotalMilliseconds)
{
effectsComponent.RemoveOverlay(overlay);
component.RemoveOverlay(overlay);
}
}
}

View File

@@ -5,7 +5,7 @@ using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Timers;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
/// <summary>
/// This interface gives components behavior when being "triggered" by timer or other conditions

View File

@@ -1,5 +1,4 @@
using Content.Server.GameObjects.Components.Chemistry;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems

View File

@@ -1,15 +1,16 @@
using System.Collections.Generic;
using System.Reflection;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Verbs;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using static Content.Shared.GameObjects.EntitySystemMessages.VerbSystemMessages;
using Logger = Robust.Shared.Log.Logger;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
public class VerbSystem : EntitySystem
{
@@ -37,6 +38,12 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
var session = eventArgs.SenderSession;
var userEntity = session.AttachedEntity;
if (userEntity == null)
{
Logger.Warning($"{nameof(UseVerb)} called by player {session} with no attached entity.");
return;
}
foreach (var (component, verb) in VerbUtility.GetVerbs(entity))
{
if ($"{component.GetType()}:{verb.GetType()}" != use.VerbKey)
@@ -44,14 +51,14 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
continue;
}
if (verb.RequireInteractionRange)
if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity))
{
var distanceSquared = (userEntity.Transform.WorldPosition - entity.Transform.WorldPosition)
.LengthSquared;
if (distanceSquared > VerbUtility.InteractionRangeSquared)
{
break;
}
break;
}
if (verb.BlockedByContainers && !userEntity.IsInSameOrNoContainer(entity))
{
break;
}
verb.Activate(userEntity, component);
@@ -65,14 +72,15 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
continue;
}
if (globalVerb.RequireInteractionRange)
if (globalVerb.RequireInteractionRange &&
!VerbUtility.InVerbUseRange(userEntity, entity))
{
var distanceSquared = (userEntity.Transform.WorldPosition - entity.Transform.WorldPosition)
.LengthSquared;
if (distanceSquared > VerbUtility.InteractionRangeSquared)
{
break;
}
break;
}
if (globalVerb.BlockedByContainers && !userEntity.IsInSameOrNoContainer(entity))
{
break;
}
globalVerb.Activate(userEntity, entity);
@@ -92,6 +100,12 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
var userEntity = player.AttachedEntity;
if (userEntity == null)
{
Logger.Warning($"{nameof(UseVerb)} called by player {player} with no attached entity.");
return;
}
var data = new List<VerbsResponseMessage.NetVerbData>();
//Get verbs, component dependent.
foreach (var (component, verb) in VerbUtility.GetVerbs(entity))
@@ -99,6 +113,9 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity))
continue;
if (verb.BlockedByContainers && !userEntity.IsInSameOrNoContainer(entity))
continue;
var verbData = verb.GetData(userEntity, component);
if (verbData.IsInvisible)
continue;
@@ -113,6 +130,9 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction
if (globalVerb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity))
continue;
if (globalVerb.BlockedByContainers && !userEntity.IsInSameOrNoContainer(entity))
continue;
var verbData = globalVerb.GetData(userEntity, entity);
if (verbData.IsInvisible)
continue;

View File

@@ -3,7 +3,7 @@ using System.Linq;
using Content.Server.GameObjects.Components.Interactable;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
/// <summary>
/// Despite the name, it's only really used for the welder logic in tools. Go figure.

View File

@@ -3,7 +3,7 @@ using Robust.Shared.GameObjects.Systems;
using Robust.Shared.ViewVariables;
using static Content.Shared.GameObjects.Components.SharedWiresComponent;
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
namespace Content.Server.GameObjects.EntitySystems
{
public class WireHackingSystem : EntitySystem
{