diff --git a/Content.Client/Alerts/ClientAlertsSystem.cs b/Content.Client/Alerts/ClientAlertsSystem.cs index 237f24e3ea..223bf7876a 100644 --- a/Content.Client/Alerts/ClientAlertsSystem.cs +++ b/Content.Client/Alerts/ClientAlertsSystem.cs @@ -49,24 +49,23 @@ public IReadOnlyDictionary? ActiveAlerts protected override void AfterShowAlert(Entity alerts) { - if (_playerManager.LocalEntity != alerts.Owner) - return; - - SyncAlerts?.Invoke(this, alerts.Comp.Alerts); + UpdateHud(alerts); } - protected override void AfterClearAlert(Entity alertsComponent) + protected override void AfterClearAlert(Entity alerts) { - if (_playerManager.LocalEntity != alertsComponent.Owner) - return; + UpdateHud(alerts); + } - SyncAlerts?.Invoke(this, alertsComponent.Comp.Alerts); + private void ClientAlertsHandleState(Entity alerts, ref AfterAutoHandleStateEvent args) + { + UpdateHud(alerts); } - private void ClientAlertsHandleState(EntityUid uid, AlertsComponent component, ref AfterAutoHandleStateEvent args) + private void UpdateHud(Entity entity) { - if (_playerManager.LocalEntity == uid) - SyncAlerts?.Invoke(this, component.Alerts); + if (_playerManager.LocalEntity == entity.Owner) + SyncAlerts?.Invoke(this, entity.Comp.Alerts); } private void OnPlayerAttached(EntityUid uid, AlertsComponent component, LocalPlayerAttachedEvent args) diff --git a/Content.Client/Audio/AmbientSoundSystem.cs b/Content.Client/Audio/AmbientSoundSystem.cs index 9d30cabb1e..0206017bae 100644 --- a/Content.Client/Audio/AmbientSoundSystem.cs +++ b/Content.Client/Audio/AmbientSoundSystem.cs @@ -50,7 +50,6 @@ protected override void QueueUpdate(EntityUid uid, AmbientSoundComponent ambienc private static AudioParams _params = AudioParams.Default .WithVariation(0.01f) .WithLoop(true) - .WithAttenuation(Attenuation.LinearDistance) .WithMaxDistance(7f); /// diff --git a/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs b/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs index 0fdcc7a86d..92c5b7a419 100644 --- a/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs +++ b/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs @@ -23,8 +23,8 @@ public sealed partial class ContentAudioSystem [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly IResourceCache _resourceCache = default!; - private readonly AudioParams _lobbySoundtrackParams = new(-5f, 1, "Master", 0, 0, 0, false, 0f); - private readonly AudioParams _roundEndSoundEffectParams = new(-5f, 1, "Master", 0, 0, 0, false, 0f); + private readonly AudioParams _lobbySoundtrackParams = new(-5f, 1, 0, 0, 0, false, 0f); + private readonly AudioParams _roundEndSoundEffectParams = new(-5f, 1, 0, 0, 0, false, 0f); /// /// EntityUid of lobby restart sound component. diff --git a/Content.Client/Clothing/ClientClothingSystem.cs b/Content.Client/Clothing/ClientClothingSystem.cs index fbe9d5ec5b..7e78ac7d70 100644 --- a/Content.Client/Clothing/ClientClothingSystem.cs +++ b/Content.Client/Clothing/ClientClothingSystem.cs @@ -133,7 +133,7 @@ private bool TryGetDefaultVisuals(EntityUid uid, ClothingComponent clothing, str else if (TryComp(uid, out SpriteComponent? sprite)) rsi = sprite.BaseRSI; - if (rsi == null || rsi.Path == null) + if (rsi == null) return false; var correctedSlot = slot; diff --git a/Content.Client/Doors/DoorSystem.cs b/Content.Client/Doors/DoorSystem.cs index 473ae97059..bc52730b0e 100644 --- a/Content.Client/Doors/DoorSystem.cs +++ b/Content.Client/Doors/DoorSystem.cs @@ -4,14 +4,12 @@ using Robust.Client.GameObjects; using Robust.Client.ResourceManagement; using Robust.Shared.Serialization.TypeSerializers.Implementations; -using Robust.Shared.Timing; namespace Content.Client.Doors; public sealed class DoorSystem : SharedDoorSystem { [Dependency] private readonly AnimationPlayerSystem _animationSystem = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IResourceCache _resourceCache = default!; public override void Initialize() diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index 8636e0eb6a..a1fc68bbd2 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -125,7 +125,6 @@ public override void Init() _prototypeManager.RegisterIgnore("alertLevels"); _prototypeManager.RegisterIgnore("nukeopsRole"); _prototypeManager.RegisterIgnore("stationGoal"); - _prototypeManager.RegisterIgnore("npcConversationTree"); _componentFactory.GenerateNetIds(); _adminManager.Initialize(); diff --git a/Content.Client/IconSmoothing/IconSmoothSystem.cs b/Content.Client/IconSmoothing/IconSmoothSystem.cs index 0ab33c6520..4b02560846 100644 --- a/Content.Client/IconSmoothing/IconSmoothSystem.cs +++ b/Content.Client/IconSmoothing/IconSmoothSystem.cs @@ -16,8 +16,6 @@ namespace Content.Client.IconSmoothing [UsedImplicitly] public sealed partial class IconSmoothSystem : EntitySystem { - [Dependency] private readonly IMapManager _mapManager = default!; - private readonly Queue _dirtyEntities = new(); private readonly Queue _anchorChangedEntities = new(); @@ -47,7 +45,7 @@ private void OnStartup(EntityUid uid, IconSmoothComponent component, ComponentSt var xform = Transform(uid); if (xform.Anchored) { - component.LastPosition = _mapManager.TryGetGrid(xform.GridUid, out var grid) + component.LastPosition = TryComp(xform.GridUid, out var grid) ? (xform.GridUid.Value, grid.TileIndicesFor(xform.Coordinates)) : (null, new Vector2i(0, 0)); @@ -134,7 +132,7 @@ public void DirtyNeighbours(EntityUid uid, IconSmoothComponent? comp = null, Tra Vector2i pos; - if (transform.Anchored && _mapManager.TryGetGrid(transform.GridUid, out var grid)) + if (transform.Anchored && TryComp(transform.GridUid, out var grid)) { pos = grid.CoordinatesToTile(transform.Coordinates); } @@ -144,7 +142,7 @@ public void DirtyNeighbours(EntityUid uid, IconSmoothComponent? comp = null, Tra if (comp.LastPosition is not (EntityUid gridId, Vector2i oldPos)) return; - if (!_mapManager.TryGetGrid(gridId, out grid)) + if (!TryComp(gridId, out grid)) return; pos = oldPos; @@ -206,7 +204,7 @@ private void CalculateNewSprite(EntityUid uid, { var directions = DirectionFlag.None; - if (_mapManager.TryGetGrid(xform.GridUid, out grid)) + if (TryComp(xform.GridUid, out grid)) { var pos = grid.TileIndicesFor(xform.Coordinates); @@ -240,7 +238,7 @@ private void CalculateNewSprite(EntityUid uid, if (xform.Anchored) { - if (!_mapManager.TryGetGrid(xform.GridUid, out grid)) + if (!TryComp(xform.GridUid, out grid)) { Log.Error($"Failed to calculate IconSmoothComponent sprite in {uid} because grid {xform.GridUid} was missing."); return; diff --git a/Content.Client/Items/Systems/ItemSystem.cs b/Content.Client/Items/Systems/ItemSystem.cs index e406ba2b55..5e60d06d0c 100644 --- a/Content.Client/Items/Systems/ItemSystem.cs +++ b/Content.Client/Items/Systems/ItemSystem.cs @@ -93,7 +93,7 @@ private bool TryGetDefaultVisuals(EntityUid uid, ItemComponent item, string defa else if (TryComp(uid, out SpriteComponent? sprite)) rsi = sprite.BaseRSI; - if (rsi == null || rsi.Path == null) + if (rsi == null) return false; var state = (item.HeldPrefix == null) diff --git a/Content.Client/Movement/Systems/JetpackSystem.cs b/Content.Client/Movement/Systems/JetpackSystem.cs index f0836ee9b6..b7f5e48821 100644 --- a/Content.Client/Movement/Systems/JetpackSystem.cs +++ b/Content.Client/Movement/Systems/JetpackSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Movement.Systems; using Robust.Client.GameObjects; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Timing; @@ -12,7 +13,6 @@ namespace Content.Client.Movement.Systems; public sealed class JetpackSystem : SharedJetpackSystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ClothingSystem _clothing = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; @@ -75,7 +75,7 @@ private void CreateParticles(EntityUid uid) var coordinates = uidXform.Coordinates; var gridUid = coordinates.GetGridUid(EntityManager); - if (_mapManager.TryGetGrid(gridUid, out var grid)) + if (TryComp(gridUid, out var grid)) { coordinates = new EntityCoordinates(gridUid.Value, grid.WorldToLocal(coordinates.ToMapPos(EntityManager, _transform))); } diff --git a/Content.Client/NPC/PathfindingSystem.cs b/Content.Client/NPC/PathfindingSystem.cs index 7bf3df1f0b..709601a57b 100644 --- a/Content.Client/NPC/PathfindingSystem.cs +++ b/Content.Client/NPC/PathfindingSystem.cs @@ -289,7 +289,6 @@ private void DrawScreen(OverlayDrawArgs args, DrawingHandleScreen screenHandle) var invGridMatrix = gridXform.InvWorldMatrix; DebugPathPoly? nearest = null; - var nearestDistance = float.MaxValue; foreach (var poly in tile) { diff --git a/Content.Client/NodeContainer/NodeVisualizationOverlay.cs b/Content.Client/NodeContainer/NodeVisualizationOverlay.cs index f10eb9ed8b..691bcb41db 100644 --- a/Content.Client/NodeContainer/NodeVisualizationOverlay.cs +++ b/Content.Client/NodeContainer/NodeVisualizationOverlay.cs @@ -80,7 +80,7 @@ private void DrawScreen(in OverlayDrawArgs args) var xform = _entityManager.GetComponent(_entityManager.GetEntity(node.Entity)); - if (!_mapManager.TryGetGrid(xform.GridUid, out var grid)) + if (!_entityManager.TryGetComponent(xform.GridUid, out var grid)) return; var gridTile = grid.TileIndicesFor(xform.Coordinates); @@ -145,7 +145,7 @@ private void DrawWorld(in OverlayDrawArgs overlayDrawArgs) foreach (var (gridId, gridDict) in _gridIndex) { - var grid = _mapManager.GetGrid(gridId); + var grid = _entityManager.GetComponent(gridId); var (_, _, worldMatrix, invMatrix) = _entityManager.GetComponent(gridId).GetWorldPositionRotationMatrixWithInv(); var lCursorBox = invMatrix.TransformBox(cursorBox); diff --git a/Content.Client/Physics/Controllers/MoverController.cs b/Content.Client/Physics/Controllers/MoverController.cs index 7a8a6e39cf..31042854d4 100644 --- a/Content.Client/Physics/Controllers/MoverController.cs +++ b/Content.Client/Physics/Controllers/MoverController.cs @@ -1,6 +1,7 @@ using Content.Shared.Movement.Components; +using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Systems; -using Content.Shared.Pulling.Components; +using Robust.Client.GameObjects; using Robust.Client.Physics; using Robust.Client.Player; using Robust.Shared.Physics.Components; @@ -24,7 +25,7 @@ public override void Initialize() SubscribeLocalEvent(OnUpdatePredicted); SubscribeLocalEvent(OnUpdateRelayTargetPredicted); - SubscribeLocalEvent(OnUpdatePullablePredicted); + SubscribeLocalEvent(OnUpdatePullablePredicted); } private void OnUpdatePredicted(EntityUid uid, InputMoverComponent component, ref UpdateIsPredictedEvent args) @@ -40,7 +41,7 @@ private void OnUpdateRelayTargetPredicted(EntityUid uid, MovementRelayTargetComp args.IsPredicted = true; } - private void OnUpdatePullablePredicted(EntityUid uid, SharedPullableComponent component, ref UpdateIsPredictedEvent args) + private void OnUpdatePullablePredicted(EntityUid uid, PullableComponent component, ref UpdateIsPredictedEvent args) { // Enable prediction if an entity is being pulled by the player. // Disable prediction if an entity is being pulled by some non-player entity. diff --git a/Content.Client/Pulling/PullingSystem.cs b/Content.Client/Pulling/PullingSystem.cs deleted file mode 100644 index 556dadd00d..0000000000 --- a/Content.Client/Pulling/PullingSystem.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; -using JetBrains.Annotations; -using Robust.Client.Physics; - -namespace Content.Client.Pulling -{ - [UsedImplicitly] - public sealed class PullingSystem : SharedPullingSystem - { - public override void Initialize() - { - base.Initialize(); - - UpdatesAfter.Add(typeof(PhysicsSystem)); - - SubscribeLocalEvent(OnPullableMove); - SubscribeLocalEvent(OnPullableStopMove); - } - } -} diff --git a/Content.Client/Radiation/Overlays/RadiationDebugOverlay.cs b/Content.Client/Radiation/Overlays/RadiationDebugOverlay.cs index 8c721fa777..ef6283b6ff 100644 --- a/Content.Client/Radiation/Overlays/RadiationDebugOverlay.cs +++ b/Content.Client/Radiation/Overlays/RadiationDebugOverlay.cs @@ -4,13 +4,12 @@ using Robust.Client.Graphics; using Robust.Client.ResourceManagement; using Robust.Shared.Enums; -using Robust.Shared.Map; +using Robust.Shared.Map.Components; namespace Content.Client.Radiation.Overlays; public sealed class RadiationDebugOverlay : Overlay { - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!; private readonly RadiationSystem _radiation; @@ -63,7 +62,7 @@ private void DrawScreenRays(OverlayDrawArgs args) { var gridUid = _entityManager.GetEntity(netGrid); - if (!_mapManager.TryGetGrid(gridUid, out var grid)) + if (!_entityManager.TryGetComponent(gridUid, out var grid)) continue; foreach (var (tile, rads) in blockers) @@ -88,7 +87,7 @@ private void DrawScreenResistance(OverlayDrawArgs args) { var gridUid = _entityManager.GetEntity(netGrid); - if (!_mapManager.TryGetGrid(gridUid, out var grid)) + if (!_entityManager.TryGetComponent(gridUid, out var grid)) continue; if (query.TryGetComponent(gridUid, out var trs) && trs.MapID != args.MapId) continue; @@ -127,7 +126,7 @@ private void DrawWorld(in OverlayDrawArgs args) { var gridUid = _entityManager.GetEntity(netGrid); - if (!_mapManager.TryGetGrid(gridUid, out var grid)) + if (!_entityManager.TryGetComponent(gridUid, out var grid)) continue; var (destTile, _) = blockers.Last(); var destWorld = grid.GridTileToWorldPos(destTile); diff --git a/Content.Client/Remotes/EntitySystems/DoorRemoteSystem.cs b/Content.Client/Remotes/EntitySystems/DoorRemoteSystem.cs new file mode 100644 index 0000000000..d6a9057f08 --- /dev/null +++ b/Content.Client/Remotes/EntitySystems/DoorRemoteSystem.cs @@ -0,0 +1,16 @@ +using Content.Client.Remote.UI; +using Content.Client.Items; +using Content.Shared.Remotes.EntitySystems; +using Content.Shared.Remotes.Components; + +namespace Content.Client.Remotes.EntitySystems; + +public sealed class DoorRemoteSystem : SharedDoorRemoteSystem +{ + public override void Initialize() + { + base.Initialize(); + + Subs.ItemStatus(ent => new DoorRemoteStatusControl(ent)); + } +} diff --git a/Content.Client/Remotes/UI/DoorRemoteStatusControl.cs b/Content.Client/Remotes/UI/DoorRemoteStatusControl.cs new file mode 100644 index 0000000000..94589ecdaa --- /dev/null +++ b/Content.Client/Remotes/UI/DoorRemoteStatusControl.cs @@ -0,0 +1,46 @@ +using Content.Client.Message; +using Content.Client.Stylesheets; +using Content.Shared.Remotes.Components; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Timing; + +namespace Content.Client.Remote.UI; + +public sealed class DoorRemoteStatusControl : Control +{ + private readonly Entity _entity; + private readonly RichTextLabel _label; + + // set to toggle bolts initially just so that it updates on first pickup of remote + private OperatingMode PrevOperatingMode = OperatingMode.placeholderForUiUpdates; + + public DoorRemoteStatusControl(Entity entity) + { + _entity = entity; + _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } }; + AddChild(_label); + } + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + + // only updates the UI if any of the details are different than they previously were + if (PrevOperatingMode == _entity.Comp.Mode) + return; + + PrevOperatingMode = _entity.Comp.Mode; + + // Update current volume and injector state + var modeStringLocalized = Loc.GetString(_entity.Comp.Mode switch + { + OperatingMode.OpenClose => "door-remote-open-close-text", + OperatingMode.ToggleBolts => "door-remote-toggle-bolt-text", + OperatingMode.ToggleEmergencyAccess => "door-remote-emergency-access-text", + _ => "door-remote-invalid-text" + }); + + _label.SetMarkup(Loc.GetString("door-remote-mode-label", ("modeString", modeStringLocalized))); + } +} diff --git a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs index 86d113defb..2fa862f3df 100644 --- a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs +++ b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs @@ -3,7 +3,7 @@ using Content.Shared.Inventory.Events; using Content.Shared.Item; using Content.Shared.Movement.Events; -using Content.Shared.Physics.Pull; +using Content.Shared.Movement.Pulling.Events; using Content.Shared.Throwing; namespace Content.Client.Replay.Spectator; diff --git a/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs b/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs index fed2a9f171..284c668190 100644 --- a/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs +++ b/Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs @@ -88,7 +88,6 @@ protected void DrawCircles(DrawingHandleScreen handle) var cornerDistance = MathF.Sqrt(WorldRange * WorldRange + WorldRange * WorldRange); var origin = ScalePosition(-new Vector2(Offset.X, -Offset.Y)); - var distOffset = -24f; for (var radius = minDistance; radius <= maxDistance; radius *= EquatorialMultiplier) { diff --git a/Content.Client/Throwing/ThrownItemVisualizerSystem.cs b/Content.Client/Throwing/ThrownItemVisualizerSystem.cs index bbd3673110..b25b4fbb7d 100644 --- a/Content.Client/Throwing/ThrownItemVisualizerSystem.cs +++ b/Content.Client/Throwing/ThrownItemVisualizerSystem.cs @@ -24,7 +24,7 @@ public override void Initialize() private void OnAutoHandleState(EntityUid uid, ThrownItemComponent component, ref AfterAutoHandleStateEvent args) { - if (!TryComp(uid, out var sprite)) + if (!TryComp(uid, out var sprite) || !component.Animate) return; var animationPlayer = EnsureComp(uid); diff --git a/Content.IntegrationTests/Pair/TestMapData.cs b/Content.IntegrationTests/Pair/TestMapData.cs index bdf1208038..343641e161 100644 --- a/Content.IntegrationTests/Pair/TestMapData.cs +++ b/Content.IntegrationTests/Pair/TestMapData.cs @@ -10,9 +10,8 @@ namespace Content.IntegrationTests.Pair; public sealed class TestMapData { public EntityUid MapUid { get; set; } - public EntityUid GridUid { get; set; } - public MapId MapId { get; set; } - public MapGridComponent MapGrid { get; set; } = default!; + public Entity Grid; + public MapId MapId; public EntityCoordinates GridCoords { get; set; } public MapCoordinates MapCoords { get; set; } public TileRef Tile { get; set; } @@ -21,4 +20,4 @@ public sealed class TestMapData public EntityUid CMapUid { get; set; } public EntityUid CGridUid { get; set; } public EntityCoordinates CGridCoords { get; set; } -} \ No newline at end of file +} diff --git a/Content.IntegrationTests/Pair/TestPair.Helpers.cs b/Content.IntegrationTests/Pair/TestPair.Helpers.cs index 554807b2d2..0ea6d3e2dc 100644 --- a/Content.IntegrationTests/Pair/TestPair.Helpers.cs +++ b/Content.IntegrationTests/Pair/TestPair.Helpers.cs @@ -1,5 +1,6 @@ #nullable enable using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -14,36 +15,37 @@ public sealed partial class TestPair /// /// Creates a map, a grid, and a tile, and gives back references to them. /// - public async Task CreateTestMap() + [MemberNotNull(nameof(TestMap))] + public async Task CreateTestMap(bool initialized = true, string tile = "Plating") { + var mapData = new TestMapData(); + TestMap = mapData; await Server.WaitIdleAsync(); var tileDefinitionManager = Server.ResolveDependency(); - var mapData = new TestMapData(); TestMap = mapData; await Server.WaitPost(() => { - mapData.MapId = Server.MapMan.CreateMap(); - mapData.MapUid = Server.MapMan.GetMapEntityId(mapData.MapId); - var mapGrid = Server.MapMan.CreateGridEntity(mapData.MapId); - mapData.MapGrid = mapGrid; - mapData.GridUid = mapGrid.Owner; // Fixing this requires an engine PR. - mapData.GridCoords = new EntityCoordinates(mapData.GridUid, 0, 0); - var plating = tileDefinitionManager["Plating"]; + mapData.MapUid = Server.System().CreateMap(out mapData.MapId, runMapInit: initialized); + mapData.Grid = Server.MapMan.CreateGridEntity(mapData.MapId); + mapData.GridCoords = new EntityCoordinates(mapData.Grid, 0, 0); + var plating = tileDefinitionManager[tile]; var platingTile = new Tile(plating.TileId); - mapData.MapGrid.SetTile(mapData.GridCoords, platingTile); + mapData.Grid.Comp.SetTile(mapData.GridCoords, platingTile); mapData.MapCoords = new MapCoordinates(0, 0, mapData.MapId); - mapData.Tile = mapData.MapGrid.GetAllTiles().First(); + mapData.Tile = mapData.Grid.Comp.GetAllTiles().First(); }); + TestMap = mapData; if (!Settings.Connected) return mapData; await RunTicksSync(10); mapData.CMapUid = ToClientUid(mapData.MapUid); - mapData.CGridUid = ToClientUid(mapData.GridUid); + mapData.CGridUid = ToClientUid(mapData.Grid); mapData.CGridCoords = new EntityCoordinates(mapData.CGridUid, 0, 0); + TestMap = mapData; return mapData; } diff --git a/Content.IntegrationTests/PoolManager.cs b/Content.IntegrationTests/PoolManager.cs index 6046ed65e4..b544fe2854 100644 --- a/Content.IntegrationTests/PoolManager.cs +++ b/Content.IntegrationTests/PoolManager.cs @@ -306,11 +306,6 @@ await testOut.WriteLineAsync( Pairs[fallback!] = true; } - if (fallback == null && _pairId > 8) - { - var x = 2; - } - return fallback; } } diff --git a/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs b/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs index 98c7363a6c..772af337a1 100644 --- a/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs +++ b/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs @@ -32,8 +32,8 @@ public async Task AddAndGetSingleLog() var guid = Guid.NewGuid(); - var testMap = await pair.CreateTestMap(); - var coordinates = testMap.GridCoords; + await pair.CreateTestMap(); + var coordinates = pair.TestMap.GridCoords; await server.WaitPost(() => { var entity = sEntities.SpawnEntity(null, coordinates); diff --git a/Content.IntegrationTests/Tests/Body/LungTest.cs b/Content.IntegrationTests/Tests/Body/LungTest.cs index f2e19849b0..dce3741c98 100644 --- a/Content.IntegrationTests/Tests/Body/LungTest.cs +++ b/Content.IntegrationTests/Tests/Body/LungTest.cs @@ -9,7 +9,6 @@ using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Map.Components; -using Robust.Shared.Maths; using System.Linq; using System.Numerics; @@ -61,12 +60,11 @@ public async Task AirConsistencyTest() var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var mapLoader = entityManager.System(); - RespiratorSystem respSys = default; - MetabolizerSystem metaSys = default; MapId mapId; EntityUid? grid = null; BodyComponent body = default; + RespiratorComponent resp = default; EntityUid human = default; GridAtmosphereComponent relevantAtmos = default; var startingMoles = 0.0f; @@ -99,17 +97,15 @@ float GetMapMoles() await server.WaitAssertion(() => { - var coords = new Vector2(0.5f, -1f); - var coordinates = new EntityCoordinates(grid.Value, coords); + var center = new Vector2(0.5f, 0.5f); + var coordinates = new EntityCoordinates(grid.Value, center); human = entityManager.SpawnEntity("HumanLungDummy", coordinates); - respSys = entityManager.System(); - metaSys = entityManager.System(); relevantAtmos = entityManager.GetComponent(grid.Value); - startingMoles = GetMapMoles(); + startingMoles = 100f; // Hardcoded because GetMapMoles returns 900 here for some reason. #pragma warning disable NUnit2045 Assert.That(entityManager.TryGetComponent(human, out body), Is.True); - Assert.That(entityManager.HasComponent(human), Is.True); + Assert.That(entityManager.TryGetComponent(human, out resp), Is.True); #pragma warning restore NUnit2045 }); @@ -118,18 +114,19 @@ await server.WaitAssertion(() => var inhaleCycles = 100; for (var i = 0; i < inhaleCycles; i++) { - await server.WaitAssertion(() => - { - // inhale - respSys.Update(2.0f); - Assert.That(GetMapMoles(), Is.LessThan(startingMoles)); - - // metabolize + exhale - metaSys.Update(1.0f); - metaSys.Update(1.0f); - respSys.Update(2.0f); - Assert.That(GetMapMoles(), Is.EqualTo(startingMoles).Within(0.0002)); - }); + // Breathe in + await PoolManager.WaitUntil(server, () => resp.Status == RespiratorStatus.Exhaling); + Assert.That( + GetMapMoles(), Is.LessThan(startingMoles), + "Did not inhale in any gas" + ); + + // Breathe out + await PoolManager.WaitUntil(server, () => resp.Status == RespiratorStatus.Inhaling); + Assert.That( + GetMapMoles(), Is.EqualTo(startingMoles).Within(0.0002), + "Did not exhale as much gas as was inhaled" + ); } await pair.CleanReturnAsync(); diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs index 6e2a080370..7c700d9fb8 100644 --- a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs +++ b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs @@ -181,9 +181,8 @@ await server.WaitAssertion(() => #pragma warning restore NUnit2045 // Move away from the chair - var xformQuery = entityManager.GetEntityQuery(); - var oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery); - xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1000, 1000), xformQuery); + var oldWorldPosition = xformSystem.GetWorldPosition(chair); + xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1000, 1000)); // Out of range #pragma warning disable NUnit2045 // Interdependent asserts. @@ -193,8 +192,8 @@ await server.WaitAssertion(() => #pragma warning restore NUnit2045 // Move near the chair - oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery); - xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(0.5f, 0), xformQuery); + oldWorldPosition = xformSystem.GetWorldPosition(chair); + xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(0.5f, 0)); // In range #pragma warning disable NUnit2045 // Interdependent asserts. @@ -220,8 +219,8 @@ await server.WaitAssertion(() => Assert.That(buckleSystem.TryBuckle(human, human, chair, buckleComp: buckle)); // Move away from the chair - oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery); - xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1, 0), xformQuery); + oldWorldPosition = xformSystem.GetWorldPosition(chair); + xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1, 0)); }); await server.WaitRunTicks(1); @@ -371,9 +370,8 @@ await server.WaitAssertion(() => }); // Move the buckled entity away - var xformQuery = entityManager.GetEntityQuery(); - var oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery); - xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(100, 0), xformQuery); + var oldWorldPosition = xformSystem.GetWorldPosition(chair); + xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(100, 0)); }); await PoolManager.WaitUntil(server, () => !buckle.Buckled, 10); @@ -383,9 +381,8 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { // Move the now unbuckled entity back onto the chair - var xformQuery = entityManager.GetEntityQuery(); - var oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery); - xformSystem.SetWorldPosition(human, oldWorldPosition, xformQuery); + var oldWorldPosition = xformSystem.GetWorldPosition(chair); + xformSystem.SetWorldPosition(human, oldWorldPosition); // Buckle Assert.That(buckleSystem.TryBuckle(human, human, chair, buckleComp: buckle)); diff --git a/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs b/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs index 26ea726211..b37f7cfa46 100644 --- a/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs +++ b/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs @@ -212,7 +212,7 @@ public async Task WiredNetworkDeviceSendAndReceive() DeviceNetworkComponent networkComponent1 = null; DeviceNetworkComponent networkComponent2 = null; WiredNetworkComponent wiredNetworkComponent = null; - var grid = testMap.MapGrid; + var grid = testMap.Grid.Comp; var testValue = "test"; var payload = new NetworkPayload diff --git a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs index 976fc2eceb..9109fdbe4f 100644 --- a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs +++ b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs @@ -163,7 +163,6 @@ public async Task Test() var entityManager = server.ResolveDependency(); var xformSystem = entityManager.System(); var disposalSystem = entityManager.System(); - await server.WaitAssertion(() => { // Spawn the entities @@ -171,8 +170,7 @@ await server.WaitAssertion(() => human = entityManager.SpawnEntity("HumanDisposalDummy", coordinates); wrench = entityManager.SpawnEntity("WrenchDummy", coordinates); disposalUnit = entityManager.SpawnEntity("DisposalUnitDummy", coordinates); - disposalTrunk = entityManager.SpawnEntity("DisposalTrunkDummy", - entityManager.GetComponent(disposalUnit).MapPosition); + disposalTrunk = entityManager.SpawnEntity("DisposalTrunkDummy", coordinates); // Test for components existing unitUid = disposalUnit; @@ -204,10 +202,10 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { - // Move the disposal trunk away - var xform = entityManager.GetComponent(disposalTrunk); var worldPos = xformSystem.GetWorldPosition(disposalTrunk); - xformSystem.SetWorldPosition(xform, worldPos + new Vector2(1, 0)); + + // Move the disposal trunk away + xformSystem.SetWorldPosition(disposalTrunk, worldPos + new Vector2(1, 0)); // Fail to flush with a mob and an item Flush(disposalUnit, unitComponent, false, disposalSystem, human, wrench); @@ -215,10 +213,12 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { - // Move the disposal trunk back var xform = entityManager.GetComponent(disposalTrunk); - var worldPos = xformSystem.GetWorldPosition(disposalTrunk); - xformSystem.SetWorldPosition(xform, worldPos - new Vector2(1, 0)); + var worldPos = xformSystem.GetWorldPosition(disposalUnit); + + // Move the disposal trunk back + xformSystem.SetWorldPosition(disposalTrunk, worldPos); + xformSystem.AnchorEntity((disposalTrunk, xform)); // Fail to flush with a mob and an item, no power Flush(disposalUnit, unitComponent, false, disposalSystem, human, wrench); @@ -240,6 +240,7 @@ await server.WaitAssertion(() => // Re-pressurizing Flush(disposalUnit, unitComponent, false, disposalSystem); }); + await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index 152eb72522..d3b1fb4722 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -354,41 +354,18 @@ public async Task AllComponentsOneToOneDeleteTest() await using var pair = await PoolManager.GetServerClient(); var server = pair.Server; - - var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var componentFactory = server.ResolveDependency(); - var tileDefinitionManager = server.ResolveDependency(); - var mapSystem = entityManager.System(); var logmill = server.ResolveDependency().GetSawmill("EntityTest"); - Entity grid = default!; - - await server.WaitPost(() => - { - // Create a one tile grid to stave off the grid 0 monsters - var mapId = mapManager.CreateMap(); - - mapManager.AddUninitializedMap(mapId); - - grid = mapManager.CreateGridEntity(mapId); - - var tileDefinition = tileDefinitionManager["Plating"]; - var tile = new Tile(tileDefinition.TileId); - var coordinates = new EntityCoordinates(grid.Owner, Vector2.Zero); - - mapSystem.SetTile(grid.Owner, grid.Comp!, coordinates, tile); - - mapManager.DoMapInitialize(mapId); - }); - + await pair.CreateTestMap(); await server.WaitRunTicks(5); + var testLocation = pair.TestMap.GridCoords; await server.WaitAssertion(() => { Assert.Multiple(() => { - var testLocation = new EntityCoordinates(grid.Owner, Vector2.Zero); foreach (var type in componentFactory.AllRegisteredTypes) { diff --git a/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs b/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs index 0d852bf2f0..6e88d6928e 100644 --- a/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs +++ b/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs @@ -72,7 +72,7 @@ await server.WaitPost(() => var puddleOrigin = new Vector2i(0, 0); await server.WaitAssertion(() => { - var grid = mapManager.GetGrid(gridId); + var grid = entityManager.GetComponent(gridId); var solution = new Solution("Blood", FixedPoint2.New(100)); var tileRef = grid.GetTileRef(puddleOrigin); #pragma warning disable NUnit2045 // Interdependent tests @@ -86,7 +86,7 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { - var grid = mapManager.GetGrid(gridId); + var grid = entityManager.GetComponent(gridId); var puddle = GetPuddleEntity(entityManager, grid, puddleOrigin); #pragma warning disable NUnit2045 // Interdependent tests diff --git a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs index 3213bba51f..a9069892df 100644 --- a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs +++ b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs @@ -46,17 +46,14 @@ public async Task SpaceNoPuddleTest() var server = pair.Server; var testMap = await pair.CreateTestMap(); + var grid = testMap.Grid.Comp; var entitySystemManager = server.ResolveDependency(); var spillSystem = entitySystemManager.GetEntitySystem(); - MapGridComponent grid = null; - // Remove all tiles await server.WaitPost(() => { - grid = testMap.MapGrid; - foreach (var tile in grid.GetAllTiles()) { grid.SetTile(tile.GridIndices, Tile.Empty); @@ -67,7 +64,7 @@ await server.WaitPost(() => await server.WaitAssertion(() => { - var coordinates = grid.ToCoordinates(); + var coordinates = grid.Owner.ToCoordinates(); var solution = new Solution("Water", FixedPoint2.New(20)); Assert.That(spillSystem.TrySpillAt(coordinates, solution, out _), Is.False); diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs index 1d5dd6d34e..c6a8e618cc 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs @@ -58,7 +58,6 @@ await server.WaitAssertion(() => var cuffableSys = entityManager.System(); var xformSys = entityManager.System(); - var xformQuery = entityManager.GetEntityQuery(); // Spawn the entities human = entityManager.SpawnEntity("HumanHandcuffDummy", coordinates); @@ -66,8 +65,8 @@ await server.WaitAssertion(() => cuffs = entityManager.SpawnEntity("HandcuffsDummy", coordinates); secondCuffs = entityManager.SpawnEntity("HandcuffsDummy", coordinates); - var coords = xformSys.GetWorldPosition(otherHuman, xformQuery); - xformSys.SetWorldPosition(human, coords, xformQuery); + var coords = xformSys.GetWorldPosition(otherHuman); + xformSys.SetWorldPosition(human, coords); // Test for components existing Assert.Multiple(() => diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs index 88448e7b80..480fd9cde6 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs @@ -989,7 +989,7 @@ protected void ToggleNeedPower(NetEntity? target = null) /// protected async Task AddGravity(EntityUid? uid = null) { - var target = uid ?? MapData.GridUid; + var target = uid ?? MapData.Grid; await Server.WaitPost(() => { var gravity = SEntMan.EnsureComponent(target); diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs index bed27ba6ef..a4ed31e998 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs @@ -184,7 +184,7 @@ public virtual async Task Setup() await Pair.CreateTestMap(); PlayerCoords = SEntMan.GetNetCoordinates(MapData.GridCoords.Offset(new Vector2(0.5f, 0.5f)).WithEntityId(MapData.MapUid, Transform, SEntMan)); TargetCoords = SEntMan.GetNetCoordinates(MapData.GridCoords.Offset(new Vector2(1.5f, 0.5f)).WithEntityId(MapData.MapUid, Transform, SEntMan)); - await SetTile(Plating, grid: MapData.MapGrid); + await SetTile(Plating, grid: MapData.Grid.Comp); // Get player data var sPlayerMan = Server.ResolveDependency(); diff --git a/Content.IntegrationTests/Tests/Interaction/MovementTest.cs b/Content.IntegrationTests/Tests/Interaction/MovementTest.cs index 553b031c2b..dc5aec92cf 100644 --- a/Content.IntegrationTests/Tests/Interaction/MovementTest.cs +++ b/Content.IntegrationTests/Tests/Interaction/MovementTest.cs @@ -31,7 +31,7 @@ public override async Task Setup() for (var i = -Tiles; i <= Tiles; i++) { - await SetTile(Plating, SEntMan.GetNetCoordinates(pCoords.Offset(new Vector2(i, 0))), MapData.MapGrid); + await SetTile(Plating, SEntMan.GetNetCoordinates(pCoords.Offset(new Vector2(i, 0))), MapData.Grid.Comp); } AssertGridCount(1); diff --git a/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs b/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs new file mode 100644 index 0000000000..30724b50a6 --- /dev/null +++ b/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs @@ -0,0 +1,150 @@ +using System.Collections.Generic; +using System.Linq; +using Content.Shared.Tag; +using Robust.Shared.Prototypes; +using Robust.Shared.Reflection; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.IntegrationTests.Tests.Linter; + +/// +/// Verify that the yaml linter successfully validates static fields +/// +[TestFixture] +public sealed class StaticFieldValidationTest +{ + [Test] + public async Task TestStaticFieldValidation() + { + await using var pair = await PoolManager.GetServerClient(); + var protoMan = pair.Server.ProtoMan; + + var protos = new Dictionary>(); + foreach (var kind in protoMan.EnumeratePrototypeKinds()) + { + var ids = protoMan.EnumeratePrototypes(kind).Select(x => x.ID).ToHashSet(); + protos.Add(kind, ids); + } + + Assert.That(protoMan.ValidateStaticFields(typeof(StringValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdSetValid), protos).Count, Is.Zero); + Assert.That(protoMan.ValidateStaticFields(typeof(PrivateProtoIdArrayValid), protos).Count, Is.Zero); + + Assert.That(protoMan.ValidateStaticFields(typeof(StringInvalid), protos).Count, Is.EqualTo(1)); + Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayInvalid), protos).Count, Is.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdInvalid), protos).Count, Is.EqualTo(1)); + Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayInvalid), protos).Count, Is.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestInvalid), protos).Count, Is.EqualTo(1)); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayInvalid), protos).Count, Is.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListInvalid), protos).Count, Is.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdSetInvalid), protos).Count, Is.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(PrivateProtoIdArrayInvalid), protos).Count, Is.EqualTo(2)); + + await pair.CleanReturnAsync(); + } + + [TestPrototypes] + private const string TestPrototypes = @" +- type: entity + id: StaticFieldTestEnt + +- type: Tag + id: StaticFieldTestTag +"; + + [Reflect(false)] private sealed class StringValid + { + [ValidatePrototypeId] public static string Tag = "StaticFieldTestTag"; + } + + [Reflect(false)] private sealed class StringInvalid + { + [ValidatePrototypeId] public static string Tag = string.Empty; + } + + [Reflect(false)] private sealed class StringArrayValid + { + [ValidatePrototypeId] public static string[] Tag = {"StaticFieldTestTag", "StaticFieldTestTag"}; + } + + [Reflect(false)] private sealed class StringArrayInvalid + { + [ValidatePrototypeId] public static string[] Tag = {string.Empty, "StaticFieldTestTag", string.Empty}; + } + + [Reflect(false)] private sealed class EntProtoIdValid + { + public static EntProtoId Tag = "StaticFieldTestEnt"; + } + + [Reflect(false)] private sealed class EntProtoIdInvalid + { + public static EntProtoId Tag = string.Empty; + } + + [Reflect(false)] private sealed class EntProtoIdArrayValid + { + public static EntProtoId[] Tag = {"StaticFieldTestEnt", "StaticFieldTestEnt"}; + } + + [Reflect(false)] private sealed class EntProtoIdArrayInvalid + { + public static EntProtoId[] Tag = {string.Empty, "StaticFieldTestEnt", string.Empty}; + } + + [Reflect(false)] private sealed class ProtoIdTestValid + { + public static ProtoId Tag = "StaticFieldTestTag"; + } + + [Reflect(false)] private sealed class ProtoIdTestInvalid + { + public static ProtoId Tag = string.Empty; + } + + [Reflect(false)] private sealed class ProtoIdArrayValid + { + public static ProtoId[] Tag = {"StaticFieldTestTag", "StaticFieldTestTag"}; + } + + [Reflect(false)] private sealed class ProtoIdArrayInvalid + { + public static ProtoId[] Tag = {string.Empty, "StaticFieldTestTag", string.Empty}; + } + + [Reflect(false)] private sealed class ProtoIdListValid + { + public static List> Tag = new() {"StaticFieldTestTag", "StaticFieldTestTag"}; + } + + [Reflect(false)] private sealed class ProtoIdListInvalid + { + public static List> Tag = new() {string.Empty, "StaticFieldTestTag", string.Empty}; + } + + [Reflect(false)] private sealed class ProtoIdSetValid + { + public static HashSet> Tag = new() {"StaticFieldTestTag", "StaticFieldTestTag"}; + } + + [Reflect(false)] private sealed class ProtoIdSetInvalid + { + public static HashSet> Tag = new() {string.Empty, "StaticFieldTestTag", string.Empty, " "}; + } + + [Reflect(false)] private sealed class PrivateProtoIdArrayValid + { + private static ProtoId[] Tag = {"StaticFieldTestTag", "StaticFieldTestTag"}; + } + + [Reflect(false)] private sealed class PrivateProtoIdArrayInvalid + { + private static ProtoId[] Tag = {string.Empty, "StaticFieldTestTag", string.Empty}; + } +} diff --git a/Content.IntegrationTests/Tests/Power/PowerTest.cs b/Content.IntegrationTests/Tests/Power/PowerTest.cs index d4e2cde9b0..a6af3e6a65 100644 --- a/Content.IntegrationTests/Tests/Power/PowerTest.cs +++ b/Content.IntegrationTests/Tests/Power/PowerTest.cs @@ -176,16 +176,18 @@ await server.WaitAssertion(() => var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; + // Power only works when anchored for (var i = 0; i < 3; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates()); - var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 1)); - var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); + var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1)); + var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2)); supplier = entityManager.GetComponent(generatorEnt); consumer1 = entityManager.GetComponent(consumerEnt1); @@ -237,16 +239,18 @@ await server.WaitAssertion(() => var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; + // Power only works when anchored for (var i = 0; i < 3; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates()); - var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 1)); - var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); + var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1)); + var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2)); supplier = entityManager.GetComponent(generatorEnt); consumer1 = entityManager.GetComponent(consumerEnt1); @@ -292,16 +296,17 @@ await server.WaitAssertion(() => { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 3; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates()); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2)); supplier = entityManager.GetComponent(generatorEnt); consumer = entityManager.GetComponent(consumerEnt); @@ -383,16 +388,17 @@ await server.WaitAssertion(() => { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 3; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.ToCoordinates()); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", gridOwner.ToCoordinates()); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2)); netBattery = entityManager.GetComponent(generatorEnt); battery = entityManager.GetComponent(generatorEnt); @@ -486,17 +492,18 @@ await server.WaitAssertion(() => { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 3; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates()); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 1)); - var batteryEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1)); + var batteryEnt = entityManager.SpawnEntity("DischargingBatteryDummy", gridOwner.ToCoordinates(0, 2)); netBattery = entityManager.GetComponent(batteryEnt); battery = entityManager.GetComponent(batteryEnt); supplier = entityManager.GetComponent(generatorEnt); @@ -577,16 +584,17 @@ await server.WaitAssertion(() => { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 3; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates()); - var batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", grid.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); + var batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", gridOwner.ToCoordinates(0, 2)); supplier = entityManager.GetComponent(generatorEnt); var netBattery = entityManager.GetComponent(batteryEnt); @@ -635,20 +643,21 @@ await server.WaitAssertion(() => { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 4; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1)); + var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0)); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 3)); + var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0)); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3)); consumer = entityManager.GetComponent(consumerEnt); supplier = entityManager.GetComponent(supplyEnt); @@ -712,20 +721,21 @@ await server.WaitAssertion(() => { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 4; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1)); + var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0)); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 3)); + var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0)); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3)); consumer = entityManager.GetComponent(consumerEnt); supplier = entityManager.GetComponent(supplyEnt); @@ -787,6 +797,7 @@ await server.WaitAssertion(() => { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Map layout here is // C - consumer @@ -800,18 +811,18 @@ await server.WaitAssertion(() => for (var i = 0; i < 5; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2)); - var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2)); + entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2)); + var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 1)); - var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 3)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 2)); - var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 0)); - var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 4)); + var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 1)); + var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 3)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 2)); + var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0)); + var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 4)); consumer1 = entityManager.GetComponent(consumerEnt1); consumer2 = entityManager.GetComponent(consumerEnt2); @@ -888,6 +899,7 @@ await server.WaitAssertion(() => { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Layout is two generators, two batteries, and one load. As to why two: because previously this test // would fail ONLY if there were more than two batteries present, because each of them tries to supply @@ -900,16 +912,16 @@ await server.WaitAssertion(() => for (var i = -2; i <= 2; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2)); - var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, -2)); + var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); + var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, -2)); - var supplyEnt1 = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 1)); - var supplyEnt2 = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, -1)); + var supplyEnt1 = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 1)); + var supplyEnt2 = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, -1)); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 0)); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0)); consumer = entityManager.GetComponent(consumerEnt); supplier1 = entityManager.GetComponent(supplyEnt1); @@ -981,6 +993,7 @@ await server.WaitAssertion(() => { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Map layout here is // C - consumer @@ -994,18 +1007,18 @@ await server.WaitAssertion(() => for (var i = 0; i < 5; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2)); - var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2)); + entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2)); + var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 1)); - var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 3)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 2)); - var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 0)); - var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 4)); + var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 1)); + var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 3)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 2)); + var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0)); + var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 4)); consumer1 = entityManager.GetComponent(consumerEnt1); consumer2 = entityManager.GetComponent(consumerEnt2); @@ -1068,20 +1081,21 @@ await server.WaitPost(() => { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 4; i++) { grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); } - var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1)); + var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0)); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 3)); + var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0)); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3)); consumer = entityManager.GetComponent(consumerEnt); supplier = entityManager.GetComponent(supplyEnt); @@ -1153,6 +1167,7 @@ await server.WaitAssertion(() => { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 4; i++) @@ -1160,15 +1175,15 @@ await server.WaitAssertion(() => grid.SetTile(new Vector2i(0, i), new Tile(1)); } - var leftEnt = entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 0)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 1)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 2)); - var rightEnt = entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 3)); + var leftEnt = entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 0)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 1)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 2)); + var rightEnt = entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 3)); - var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1)); + var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var battery = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2)); + var battery = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); var batteryNodeContainer = entityManager.GetComponent(battery); if (nodeContainer.TryGetNode(entityManager.GetComponent(leftEnt), @@ -1216,6 +1231,7 @@ await server.WaitAssertion(() => { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; // Power only works when anchored for (var i = 0; i < 3; i++) @@ -1223,14 +1239,14 @@ await server.WaitAssertion(() => grid.SetTile(new Vector2i(0, i), new Tile(1)); } - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 0)); - entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 1)); - entityManager.SpawnEntity("CableMV", grid.ToCoordinates(0, 1)); - entityManager.SpawnEntity("CableMV", grid.ToCoordinates(0, 2)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 0)); + entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 1)); + entityManager.SpawnEntity("CableMV", gridOwner.ToCoordinates(0, 1)); + entityManager.SpawnEntity("CableMV", gridOwner.ToCoordinates(0, 2)); - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0)); - var substationEnt = entityManager.SpawnEntity("SubstationDummy", grid.ToCoordinates(0, 1)); - var apcEnt = entityManager.SpawnEntity("ApcDummy", grid.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0)); + var substationEnt = entityManager.SpawnEntity("SubstationDummy", gridOwner.ToCoordinates(0, 1)); + var apcEnt = entityManager.SpawnEntity("ApcDummy", gridOwner.ToCoordinates(0, 2)); var generatorSupplier = entityManager.GetComponent(generatorEnt); substationNetBattery = entityManager.GetComponent(substationEnt); @@ -1273,6 +1289,7 @@ await server.WaitAssertion(() => { var map = mapManager.CreateMap(); var grid = mapManager.CreateGrid(map); + var gridOwner = grid.Owner; const int range = 5; @@ -1282,15 +1299,15 @@ await server.WaitAssertion(() => grid.SetTile(new Vector2i(0, i), new Tile(1)); } - var apcEnt = entityManager.SpawnEntity("ApcDummy", grid.ToCoordinates(0, 0)); - var apcExtensionEnt = entityManager.SpawnEntity("CableApcExtension", grid.ToCoordinates(0, 0)); + var apcEnt = entityManager.SpawnEntity("ApcDummy", gridOwner.ToCoordinates(0, 0)); + var apcExtensionEnt = entityManager.SpawnEntity("CableApcExtension", gridOwner.ToCoordinates(0, 0)); // Create a powered receiver in range (range is 0 indexed) - var powerReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", grid.ToCoordinates(0, range - 1)); + var powerReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", gridOwner.ToCoordinates(0, range - 1)); receiver = entityManager.GetComponent(powerReceiverEnt); // Create an unpowered receiver outside range - var unpoweredReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", grid.ToCoordinates(0, range)); + var unpoweredReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", gridOwner.ToCoordinates(0, range)); unpoweredReceiver = entityManager.GetComponent(unpoweredReceiverEnt); var battery = entityManager.GetComponent(apcEnt); diff --git a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs index 6096c497ef..9e26fa5eaa 100644 --- a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs +++ b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs @@ -38,31 +38,15 @@ public async Task UninitializedSaveTest() var mapManager = server.ResolveDependency(); var entityMan = server.ResolveDependency(); var prototypeMan = server.ResolveDependency(); - var tileDefinitionManager = server.ResolveDependency(); var seriMan = server.ResolveDependency(); var compFact = server.ResolveDependency(); var prototypes = new List(); - MapGridComponent grid = default!; EntityUid uid; - MapId mapId = default; - //Build up test environment - await server.WaitPost(() => - { - // Create a one tile grid to stave off the grid 0 monsters - mapId = mapManager.CreateMap(); - - mapManager.AddUninitializedMap(mapId); - - grid = mapManager.CreateGrid(mapId); - - var tileDefinition = tileDefinitionManager["FloorSteel"]; // Wires n such disable ambiance while under the floor - var tile = new Tile(tileDefinition.TileId); - var coordinates = grid.ToCoordinates(); - - grid.SetTile(coordinates, tile); - }); + await pair.CreateTestMap(false, "FloorSteel"); // Wires n such disable ambiance while under the floor + var mapId = pair.TestMap.MapId; + var grid = pair.TestMap.Grid; await server.WaitRunTicks(5); @@ -94,7 +78,7 @@ await server.WaitPost(() => await server.WaitAssertion(() => { Assert.That(!mapManager.IsMapInitialized(mapId)); - var testLocation = grid.ToCoordinates(); + var testLocation = grid.Owner.ToCoordinates(); Assert.Multiple(() => { diff --git a/Content.IntegrationTests/Tests/Puller/PullerTest.cs b/Content.IntegrationTests/Tests/Puller/PullerTest.cs index ba91f54ff7..87d174f727 100644 --- a/Content.IntegrationTests/Tests/Puller/PullerTest.cs +++ b/Content.IntegrationTests/Tests/Puller/PullerTest.cs @@ -1,6 +1,6 @@ using Content.Shared.Hands.Components; +using Content.Shared.Movement.Pulling.Components; using Content.Shared.Prototypes; -using Content.Shared.Pulling.Components; using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; @@ -29,7 +29,7 @@ await server.WaitAssertion(() => { foreach (var proto in protoManager.EnumeratePrototypes()) { - if (!proto.TryGetComponent(out SharedPullerComponent? puller)) + if (!proto.TryGetComponent(out PullerComponent? puller)) continue; if (!puller.NeedsHands) diff --git a/Content.IntegrationTests/Tests/Shuttle/DockTest.cs b/Content.IntegrationTests/Tests/Shuttle/DockTest.cs index b6fc273570..a1aa462a69 100644 --- a/Content.IntegrationTests/Tests/Shuttle/DockTest.cs +++ b/Content.IntegrationTests/Tests/Shuttle/DockTest.cs @@ -39,7 +39,7 @@ public async Task TestDockingConfig(Vector2 dock1Pos, Vector2 dock2Pos, Angle do await server.WaitAssertion(() => { - entManager.DeleteEntity(map.GridUid); + entManager.DeleteEntity(map.Grid); var grid1 = mapManager.CreateGridEntity(mapId); var grid2 = mapManager.CreateGridEntity(mapId); var grid1Ent = grid1.Owner; @@ -104,7 +104,7 @@ public async Task TestPlanetDock() // Spawn shuttle and affirm no valid docks. await server.WaitAssertion(() => { - entManager.DeleteEntity(map.GridUid); + entManager.DeleteEntity(map.Grid); Assert.That(entManager.System().TryLoad(otherMap.MapId, "/Maps/Shuttles/emergency.yml", out var rootUids)); shuttle = rootUids[0]; diff --git a/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs b/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs index 0a2af88887..083e817d69 100644 --- a/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs +++ b/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs @@ -37,7 +37,7 @@ public async Task CutThenPlaceLatticeNewGrid() // Remove grid await SetTile(null); await SetTile(null, PlayerCoords); - Assert.That(MapData.MapGrid.Deleted); + Assert.That(MapData.Grid.Comp.Deleted); AssertGridCount(0); // Place Lattice @@ -70,7 +70,7 @@ public async Task FloorConstructDeconstruct() // Remove grid await SetTile(null); await SetTile(null, PlayerCoords); - Assert.That(MapData.MapGrid.Deleted); + Assert.That(MapData.Grid.Comp.Deleted); AssertGridCount(0); // Space -> Lattice diff --git a/Content.Packaging/ClientPackaging.cs b/Content.Packaging/ClientPackaging.cs index a989ebd968..a66d4ec5b9 100644 --- a/Content.Packaging/ClientPackaging.cs +++ b/Content.Packaging/ClientPackaging.cs @@ -13,7 +13,7 @@ public static class ClientPackaging /// /// Be advised this can be called from server packaging during a HybridACZ build. /// - public static async Task PackageClient(bool skipBuild, IPackageLogger logger) + public static async Task PackageClient(bool skipBuild, string configuration, IPackageLogger logger) { logger.Info("Building client..."); @@ -26,7 +26,7 @@ await ProcessHelpers.RunCheck(new ProcessStartInfo { "build", Path.Combine("Content.Client", "Content.Client.csproj"), - "-c", "Release", + "-c", configuration, "--nologo", "/v:m", "/t:Rebuild", diff --git a/Content.Packaging/CommandLineArgs.cs b/Content.Packaging/CommandLineArgs.cs index 9f2b075535..23f661921e 100644 --- a/Content.Packaging/CommandLineArgs.cs +++ b/Content.Packaging/CommandLineArgs.cs @@ -31,6 +31,11 @@ public sealed class CommandLineArgs /// public bool HybridAcz { get; set; } + /// + /// Configuration used for when packaging the server. (Release, Debug, Tools) + /// + public string Configuration { get; set; } + // CommandLineArgs, 3rd of her name. public static bool TryParse(IReadOnlyList args, [NotNullWhen(true)] out CommandLineArgs? parsed) { @@ -39,6 +44,7 @@ public static bool TryParse(IReadOnlyList args, [NotNullWhen(true)] out var skipBuild = false; var wipeRelease = true; var hybridAcz = false; + var configuration = "Release"; List? platforms = null; using var enumerator = args.GetEnumerator(); @@ -89,6 +95,16 @@ public static bool TryParse(IReadOnlyList args, [NotNullWhen(true)] out platforms ??= new List(); platforms.Add(enumerator.Current); } + else if (arg == "--configuration") + { + if (!enumerator.MoveNext()) + { + Console.WriteLine("No configuration provided"); + return false; + } + + configuration = enumerator.Current; + } else if (arg == "--help") { PrintHelp(); @@ -106,7 +122,7 @@ public static bool TryParse(IReadOnlyList args, [NotNullWhen(true)] out return false; } - parsed = new CommandLineArgs(client.Value, skipBuild, wipeRelease, hybridAcz, platforms); + parsed = new CommandLineArgs(client.Value, skipBuild, wipeRelease, hybridAcz, platforms, configuration); return true; } @@ -120,6 +136,7 @@ private static void PrintHelp() --no-wipe-release Don't wipe the release folder before creating files. --hybrid-acz Use HybridACZ for server builds. --platform Platform for server builds. Default will output several x64 targets. + --configuration Configuration to use for building the server (Release, Debug, Tools). Default is Release. "); } @@ -128,12 +145,14 @@ private CommandLineArgs( bool skipBuild, bool wipeRelease, bool hybridAcz, - List? platforms) + List? platforms, + string configuration) { Client = client; SkipBuild = skipBuild; WipeRelease = wipeRelease; HybridAcz = hybridAcz; Platforms = platforms; + Configuration = configuration; } } diff --git a/Content.Packaging/Program.cs b/Content.Packaging/Program.cs index ba5924ec3e..65c0e0131a 100644 --- a/Content.Packaging/Program.cs +++ b/Content.Packaging/Program.cs @@ -17,11 +17,11 @@ if (parsed.Client) { - await ClientPackaging.PackageClient(parsed.SkipBuild, logger); + await ClientPackaging.PackageClient(parsed.SkipBuild, parsed.Configuration, logger); } else { - await ServerPackaging.PackageServer(parsed.SkipBuild, parsed.HybridAcz, logger, parsed.Platforms); + await ServerPackaging.PackageServer(parsed.SkipBuild, parsed.HybridAcz, logger, parsed.Configuration, parsed.Platforms); } void WipeBin() diff --git a/Content.Packaging/ServerPackaging.cs b/Content.Packaging/ServerPackaging.cs index ba489629f7..d9ca57c4d1 100644 --- a/Content.Packaging/ServerPackaging.cs +++ b/Content.Packaging/ServerPackaging.cs @@ -69,7 +69,7 @@ public static class ServerPackaging "zh-Hant" }; - public static async Task PackageServer(bool skipBuild, bool hybridAcz, IPackageLogger logger, List? platforms = null) + public static async Task PackageServer(bool skipBuild, bool hybridAcz, IPackageLogger logger, string configuration, List? platforms = null) { if (platforms == null) { @@ -82,7 +82,7 @@ public static async Task PackageServer(bool skipBuild, bool hybridAcz, IPackageL // Rather than hosting the client ZIP on the watchdog or on a separate server, // Hybrid ACZ uses the ACZ hosting functionality to host it as part of the status host, // which means that features such as automatic UPnP forwarding still work properly. - await ClientPackaging.PackageClient(skipBuild, logger); + await ClientPackaging.PackageClient(skipBuild, configuration, logger); } // Good variable naming right here. @@ -91,13 +91,13 @@ public static async Task PackageServer(bool skipBuild, bool hybridAcz, IPackageL if (!platforms.Contains(platform.Rid)) continue; - await BuildPlatform(platform, skipBuild, hybridAcz, logger); + await BuildPlatform(platform, skipBuild, hybridAcz, configuration, logger); } } - private static async Task BuildPlatform(PlatformReg platform, bool skipBuild, bool hybridAcz, IPackageLogger logger) + private static async Task BuildPlatform(PlatformReg platform, bool skipBuild, bool hybridAcz, string configuration, IPackageLogger logger) { - logger.Info($"Building project for {platform}..."); + logger.Info($"Building project for {platform.TargetOs}..."); if (!skipBuild) { @@ -108,7 +108,7 @@ await ProcessHelpers.RunCheck(new ProcessStartInfo { "build", Path.Combine("Content.Server", "Content.Server.csproj"), - "-c", "Release", + "-c", configuration, "--nologo", "/v:m", $"/p:TargetOs={platform.TargetOs}", @@ -118,7 +118,7 @@ await ProcessHelpers.RunCheck(new ProcessStartInfo } }); - await PublishClientServer(platform.Rid, platform.TargetOs); + await PublishClientServer(platform.Rid, platform.TargetOs, configuration); } logger.Info($"Packaging {platform.Rid} server..."); @@ -137,7 +137,7 @@ await ProcessHelpers.RunCheck(new ProcessStartInfo logger.Info($"Finished packaging server in {sw.Elapsed}"); } - private static async Task PublishClientServer(string runtime, string targetOs) + private static async Task PublishClientServer(string runtime, string targetOs, string configuration) { await ProcessHelpers.RunCheck(new ProcessStartInfo { @@ -147,7 +147,7 @@ await ProcessHelpers.RunCheck(new ProcessStartInfo "publish", "--runtime", runtime, "--no-self-contained", - "-c", "Release", + "-c", configuration, $"/p:TargetOs={targetOs}", "/p:FullRelease=True", "/m", diff --git a/Content.Server/Administration/Commands/AdminWhoCommand.cs b/Content.Server/Administration/Commands/AdminWhoCommand.cs index 9765e8385f..cf2f8c453c 100644 --- a/Content.Server/Administration/Commands/AdminWhoCommand.cs +++ b/Content.Server/Administration/Commands/AdminWhoCommand.cs @@ -19,6 +19,16 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) var adminMgr = IoCManager.Resolve(); var afk = IoCManager.Resolve(); + var seeStealth = true; + + // If null it (hopefully) means it is being called from the console. + if (shell.Player != null) + { + var playerData = adminMgr.GetAdminData(shell.Player); + + seeStealth = playerData != null && playerData.CanStealth(); + } + var sb = new StringBuilder(); var first = true; foreach (var admin in adminMgr.ActiveAdmins) @@ -30,10 +40,16 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) var adminData = adminMgr.GetAdminData(admin)!; DebugTools.AssertNotNull(adminData); + if (adminData.Stealth && !seeStealth) + continue; + sb.Append(admin.Name); if (adminData.Title is { } title) sb.Append($": [{title}]"); + if (adminData.Stealth) + sb.Append(" (S)"); + if (shell.Player is { } player && adminMgr.HasAdminFlag(player, AdminFlags.Admin)) { if (afk.IsAfk(admin)) diff --git a/Content.Server/Administration/Commands/PersistenceSaveCommand.cs b/Content.Server/Administration/Commands/PersistenceSaveCommand.cs index 2684e85d5f..7ef1932c56 100644 --- a/Content.Server/Administration/Commands/PersistenceSaveCommand.cs +++ b/Content.Server/Administration/Commands/PersistenceSaveCommand.cs @@ -1,11 +1,6 @@ -using Content.Server.GameTicking; -using Content.Server.Ghost.Components; -using Content.Server.Players; using Content.Shared.Administration; using Content.Shared.CCVar; -using Content.Shared.Ghost; using Robust.Server.GameObjects; -using Robust.Server.Player; using Robust.Shared.Configuration; using Robust.Shared.Console; using Robust.Shared.Map; @@ -17,7 +12,6 @@ namespace Content.Server.Administration.Commands; public sealed class PersistenceSave : IConsoleCommand { [Dependency] private readonly IConfigurationManager _config = default!; - [Dependency] private readonly IEntityManager _entities = default!; [Dependency] private readonly IEntitySystemManager _system = default!; [Dependency] private readonly IMapManager _map = default!; diff --git a/Content.Server/Administration/Commands/PlayGlobalSoundCommand.cs b/Content.Server/Administration/Commands/PlayGlobalSoundCommand.cs index ac55ce8325..f1a5a57aa6 100644 --- a/Content.Server/Administration/Commands/PlayGlobalSoundCommand.cs +++ b/Content.Server/Administration/Commands/PlayGlobalSoundCommand.cs @@ -6,6 +6,7 @@ using Robust.Shared.Console; using Robust.Shared.ContentPack; using Robust.Shared.Player; +using Robust.Shared.Prototypes; namespace Content.Server.Administration.Commands; @@ -14,6 +15,7 @@ public sealed class PlayGlobalSoundCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly IResourceManager _res = default!; public string Command => "playglobalsound"; @@ -95,7 +97,7 @@ public CompletionResult GetCompletion(IConsoleShell shell, string[] args) { var hint = Loc.GetString("play-global-sound-command-arg-path"); - var options = CompletionHelper.ContentFilePath(args[0], _res); + var options = CompletionHelper.AudioFilePath(args[0], _protoManager, _res); return CompletionResult.FromHintOptions(options, hint); } diff --git a/Content.Server/Administration/Commands/StealthminCommand.cs b/Content.Server/Administration/Commands/StealthminCommand.cs new file mode 100644 index 0000000000..aa0e77a794 --- /dev/null +++ b/Content.Server/Administration/Commands/StealthminCommand.cs @@ -0,0 +1,34 @@ +using Content.Server.Administration.Managers; +using Content.Shared.Administration; +using JetBrains.Annotations; +using Robust.Shared.Console; +using Robust.Shared.Utility; + +namespace Content.Server.Administration.Commands; + +[UsedImplicitly] +[AdminCommand(AdminFlags.Stealth)] +public sealed class StealthminCommand : LocalizedCommands +{ + public override string Command => "stealthmin"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) + { + var player = shell.Player; + if (player == null) + { + shell.WriteLine(Loc.GetString("cmd-stealthmin-no-console")); + return; + } + + var mgr = IoCManager.Resolve(); + var adminData = mgr.GetAdminData(player); + + DebugTools.AssertNotNull(adminData); + + if (!adminData!.Stealth) + mgr.Stealth(player); + else + mgr.UnStealth(player); + } +} diff --git a/Content.Server/Administration/Commands/VariantizeCommand.cs b/Content.Server/Administration/Commands/VariantizeCommand.cs index 7aabd76335..3f9b7efd07 100644 --- a/Content.Server/Administration/Commands/VariantizeCommand.cs +++ b/Content.Server/Administration/Commands/VariantizeCommand.cs @@ -3,7 +3,6 @@ using Robust.Shared.Console; using Robust.Shared.Map; using Robust.Shared.Map.Components; -using Robust.Shared.Random; namespace Content.Server.Administration.Commands; @@ -11,7 +10,6 @@ namespace Content.Server.Administration.Commands; public sealed class VariantizeCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; public string Command => "variantize"; diff --git a/Content.Server/Administration/Managers/AdminManager.cs b/Content.Server/Administration/Managers/AdminManager.cs index 57d6e089bd..8fb28b7ec4 100644 --- a/Content.Server/Administration/Managers/AdminManager.cs +++ b/Content.Server/Administration/Managers/AdminManager.cs @@ -98,6 +98,40 @@ public void DeAdmin(ICommonSession session) UpdateAdminStatus(session); } + public void Stealth(ICommonSession session) + { + if (!_admins.TryGetValue(session, out var reg)) + throw new ArgumentException($"Player {session} is not an admin"); + + if (reg.Data.Stealth) + return; + + var playerData = session.ContentData()!; + playerData.Stealthed = true; + reg.Data.Stealth = true; + + _chat.DispatchServerMessage(session, Loc.GetString("admin-manager-stealthed-message")); + _chat.SendAdminAnnouncement(Loc.GetString("admin-manager-self-de-admin-message", ("exAdminName", session.Name)), AdminFlags.Stealth); + _chat.SendAdminAnnouncement(Loc.GetString("admin-manager-self-enable-stealth", ("stealthAdminName", session.Name)), flagWhitelist: AdminFlags.Stealth); + } + + public void UnStealth(ICommonSession session) + { + if (!_admins.TryGetValue(session, out var reg)) + throw new ArgumentException($"Player {session} is not an admin"); + + if (!reg.Data.Stealth) + return; + + var playerData = session.ContentData()!; + playerData.Stealthed = false; + reg.Data.Stealth = false; + + _chat.DispatchServerMessage(session, Loc.GetString("admin-manager-unstealthed-message")); + _chat.SendAdminAnnouncement(Loc.GetString("admin-manager-self-re-admin-message", ("newAdminName", session.Name)), flagBlacklist: AdminFlags.Stealth); + _chat.SendAdminAnnouncement(Loc.GetString("admin-manager-self-disable-stealth", ("exStealthAdminName", session.Name)), flagWhitelist: AdminFlags.Stealth); + } + public void ReAdmin(ICommonSession session) { if (!_admins.TryGetValue(session, out var reg)) @@ -116,7 +150,16 @@ public void ReAdmin(ICommonSession session) plyData.ExplicitlyDeadminned = false; reg.Data.Active = true; - _chat.SendAdminAnnouncement(Loc.GetString("admin-manager-self-re-admin-message", ("newAdminName", session.Name))); + if (reg.Data.Stealth) + { + _chat.SendAdminAnnouncement(Loc.GetString("admin-manager-self-re-admin-message", ("newAdminName", session.Name))); + } + else + { + _chat.DispatchServerMessage(session, Loc.GetString("admin-manager-stealthed-message")); + _chat.SendAdminAnnouncement(Loc.GetString("admin-manager-self-re-admin-message", + ("newAdminName", session.Name)), flagWhitelist: AdminFlags.Stealth); + } SendPermsChangedEvent(session); UpdateAdminStatus(session); @@ -168,6 +211,9 @@ public async void ReloadAdmin(ICommonSession player) _chat.DispatchServerMessage(player, Loc.GetString("admin-manager-admin-permissions-updated-message")); } + + if (player.ContentData()!.Stealthed) + aData.Stealth = true; } SendPermsChangedEvent(player); @@ -290,9 +336,14 @@ private void PlayerStatusChanged(object? sender, SessionStatusEventArgs e) } else if (e.NewStatus == SessionStatus.Disconnected) { - if (_admins.Remove(e.Session) && _cfg.GetCVar(CCVars.AdminAnnounceLogout)) + if (_admins.Remove(e.Session, out var reg ) && _cfg.GetCVar(CCVars.AdminAnnounceLogout)) { - _chat.SendAdminAnnouncement(Loc.GetString("admin-manager-admin-logout-message", ("name", e.Session.Name))); + if (reg.Data.Stealth) + _chat.SendAdminAnnouncement(Loc.GetString("admin-manager-admin-logout-message", + ("name", e.Session.Name)), flagWhitelist: AdminFlags.Stealth); + else + _chat.SendAdminAnnouncement(Loc.GetString("admin-manager-admin-logout-message", + ("name", e.Session.Name))); } } } @@ -315,13 +366,27 @@ private async void LoginAdminMaybe(ICommonSession session) _admins.Add(session, reg); + if (session.ContentData()!.Stealthed) + reg.Data.Stealth = true; + if (!session.ContentData()?.ExplicitlyDeadminned ?? false) { reg.Data.Active = true; if (_cfg.GetCVar(CCVars.AdminAnnounceLogin)) { - _chat.SendAdminAnnouncement(Loc.GetString("admin-manager-admin-login-message", ("name", session.Name))); + if (reg.Data.Stealth) + { + + _chat.DispatchServerMessage(session, Loc.GetString("admin-manager-stealthed-message")); + _chat.SendAdminAnnouncement(Loc.GetString("admin-manager-admin-login-message", + ("name", session.Name)), flagWhitelist: AdminFlags.Stealth); + } + else + { + _chat.SendAdminAnnouncement(Loc.GetString("admin-manager-admin-login-message", + ("name", session.Name))); + } } SendPermsChangedEvent(session); diff --git a/Content.Server/Administration/Managers/IAdminManager.cs b/Content.Server/Administration/Managers/IAdminManager.cs index a52ec7b099..95967b24ac 100644 --- a/Content.Server/Administration/Managers/IAdminManager.cs +++ b/Content.Server/Administration/Managers/IAdminManager.cs @@ -41,6 +41,16 @@ public interface IAdminManager : ISharedAdminManager /// void ReAdmin(ICommonSession session); + /// + /// Make admin hidden from adminwho. + /// + void Stealth(ICommonSession session); + + /// + /// Unhide admin from adminwho. + /// + void UnStealth(ICommonSession session); + /// /// Re-loads the permissions of an player in case their admin data changed DB-side. /// diff --git a/Content.Server/Alert/Click/StopBeingPulled.cs b/Content.Server/Alert/Click/StopBeingPulled.cs index 2cf076fbee..b02da38ecf 100644 --- a/Content.Server/Alert/Click/StopBeingPulled.cs +++ b/Content.Server/Alert/Click/StopBeingPulled.cs @@ -1,7 +1,7 @@ using Content.Shared.ActionBlocker; using Content.Shared.Alert; -using Content.Shared.Pulling.Components; -using Content.Shared.Pulling; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using JetBrains.Annotations; namespace Content.Server.Alert.Click @@ -20,9 +20,9 @@ public void AlertClicked(EntityUid player) if (!entityManager.System().CanInteract(player, null)) return; - if (entityManager.TryGetComponent(player, out SharedPullableComponent? playerPullable)) + if (entityManager.TryGetComponent(player, out PullableComponent? playerPullable)) { - entityManager.System().TryStopPull(playerPullable); + entityManager.System().TryStopPull(player, playerPullable, user: player); } } } diff --git a/Content.Server/Alert/Click/StopPulling.cs b/Content.Server/Alert/Click/StopPulling.cs index 00a4149598..76f9569429 100644 --- a/Content.Server/Alert/Click/StopPulling.cs +++ b/Content.Server/Alert/Click/StopPulling.cs @@ -1,6 +1,6 @@ using Content.Shared.Alert; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using JetBrains.Annotations; namespace Content.Server.Alert.Click @@ -15,12 +15,12 @@ public sealed partial class StopPulling : IAlertClick public void AlertClicked(EntityUid player) { var entManager = IoCManager.Resolve(); + var ps = entManager.System(); - var ps = entManager.System(); - var playerTarget = ps.GetPulled(player); - if (playerTarget != default && entManager.TryGetComponent(playerTarget, out SharedPullableComponent? playerPullable)) + if (entManager.TryGetComponent(player, out PullerComponent? puller) && + entManager.TryGetComponent(puller.Pulling, out PullableComponent? pullableComp)) { - ps.TryStopPull(playerPullable); + ps.TryStopPull(puller.Pulling.Value, pullableComp, user: player); } } } diff --git a/Content.Server/Anomaly/AnomalySystem.Generator.cs b/Content.Server/Anomaly/AnomalySystem.Generator.cs index 2053a7bbbe..e03c566593 100644 --- a/Content.Server/Anomaly/AnomalySystem.Generator.cs +++ b/Content.Server/Anomaly/AnomalySystem.Generator.cs @@ -24,7 +24,7 @@ namespace Content.Server.Anomaly; /// public sealed partial class AnomalySystem { - [Dependency] private readonly MapSystem _mapSystem = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; private void InitializeGenerator() diff --git a/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs b/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs index 87c0ba4a4e..dd2da82c9d 100644 --- a/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/BluespaceAnomalySystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Teleportation.Components; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; +using Robust.Shared.Collections; using Robust.Shared.Random; namespace Content.Server.Anomaly.Effects; @@ -35,20 +36,19 @@ private void OnPulse(EntityUid uid, BluespaceAnomalyComponent component, ref Ano var range = component.MaxShuffleRadius * args.Severity; var mobs = new HashSet>(); _lookup.GetEntitiesInRange(xform.Coordinates, range, mobs); - var allEnts = new List(mobs.Select(m => m.Owner)) { uid }; - var coords = new List(); + var allEnts = new ValueList(mobs.Select(m => m.Owner)) { uid }; + var coords = new ValueList(); foreach (var ent in allEnts) { - if (xformQuery.TryGetComponent(ent, out var xf)) - coords.Add(xf.MapPosition.Position); + if (xformQuery.TryGetComponent(ent, out var allXform)) + coords.Add(_xform.GetWorldPosition(allXform)); } _random.Shuffle(coords); for (var i = 0; i < allEnts.Count; i++) { - _adminLogger.Add(LogType.Teleport, $"{ToPrettyString(allEnts[i])} has been shuffled to {coords[i]} by the {ToPrettyString(uid)} at {xform.Coordinates}"); - _xform.SetWorldPosition(allEnts[i], coords[i], xformQuery); + _xform.SetWorldPosition(allEnts[i], coords[i]); } } diff --git a/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs b/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs index 7c397d6888..90a655fbba 100644 --- a/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/EntityAnomalySystem.cs @@ -2,7 +2,6 @@ using Content.Shared.Anomaly.Components; using Content.Shared.Anomaly.Effects; using Content.Shared.Anomaly.Effects.Components; -using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Random; @@ -12,7 +11,6 @@ namespace Content.Server.Anomaly.Effects; public sealed class EntityAnomalySystem : SharedEntityAnomalySystem { [Dependency] private readonly SharedAnomalySystem _anomaly = default!; - [Dependency] private readonly IMapManager _map = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedMapSystem _mapSystem = default!; diff --git a/Content.Server/Anomaly/Effects/GasProducerAnomalySystem.cs b/Content.Server/Anomaly/Effects/GasProducerAnomalySystem.cs index a5e42be540..2408ad0b3d 100644 --- a/Content.Server/Anomaly/Effects/GasProducerAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/GasProducerAnomalySystem.cs @@ -2,11 +2,10 @@ using Content.Server.Anomaly.Components; using Content.Shared.Anomaly.Components; using Content.Shared.Atmos; -using Robust.Server.GameObjects; -using Robust.Shared.Map; using Robust.Shared.Random; using System.Linq; using System.Numerics; +using Robust.Shared.Map.Components; namespace Content.Server.Anomaly.Effects; @@ -16,8 +15,6 @@ namespace Content.Server.Anomaly.Effects; public sealed class GasProducerAnomalySystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphere = default!; - [Dependency] private readonly TransformSystem _xform = default!; - [Dependency] private readonly IMapManager _map = default!; [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() @@ -55,7 +52,7 @@ private void ReleaseGas(EntityUid uid, Gas gas, float mols, float radius, int co { var xform = Transform(uid); - if (!_map.TryGetGrid(xform.GridUid, out var grid)) + if (!TryComp(xform.GridUid, out var grid)) return; var localpos = xform.Coordinates.Position; diff --git a/Content.Server/Atmos/Commands/DeleteGasCommand.cs b/Content.Server/Atmos/Commands/DeleteGasCommand.cs index 9e7594c024..f4279db926 100644 --- a/Content.Server/Atmos/Commands/DeleteGasCommand.cs +++ b/Content.Server/Atmos/Commands/DeleteGasCommand.cs @@ -3,7 +3,7 @@ using Content.Shared.Administration; using Content.Shared.Atmos; using Robust.Shared.Console; -using Robust.Shared.Map; +using Robust.Shared.Map.Components; namespace Content.Server.Atmos.Commands { @@ -11,7 +11,6 @@ namespace Content.Server.Atmos.Commands public sealed class DeleteGasCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; public string Command => "deletegas"; public string Description => "Removes all gases from a grid, or just of one type if specified."; @@ -119,7 +118,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - if (!_mapManager.TryGetGrid(gridId, out _)) + if (!_entManager.TryGetComponent(gridId, out _)) { shell.WriteLine($"No grid exists with id {gridId}"); return; diff --git a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs index 416045fc5e..d947e60b6d 100644 --- a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs @@ -1,8 +1,6 @@ -using Content.Server.Atmos; using Content.Server.Atmos.Components; using Content.Server.Atmos.Piping.Components; using Content.Shared.Atmos; -using Robust.Shared.GameObjects; using Robust.Shared.Map; using System.Diagnostics.CodeAnalysis; @@ -15,7 +13,6 @@ public sealed class AirFilterSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly IMapManager _map = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; public override void Initialize() { diff --git a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs index dfe8447340..aed00432e1 100644 --- a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs @@ -1,4 +1,3 @@ -using System.Numerics; using Content.Server.Atmos.Components; using Content.Server.Body.Components; using Content.Server.Body.Systems; @@ -17,8 +16,6 @@ using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Player; using Robust.Shared.Random; namespace Content.Server.Atmos.EntitySystems @@ -33,7 +30,6 @@ public sealed class GasTankSystem : EntitySystem [Dependency] private readonly SharedContainerSystem _containers = default!; [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ThrowingSystem _throwing = default!; diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs index 10b9cccc09..8e478bd2b5 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs @@ -27,7 +27,6 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems public sealed class GasVolumePumpSystem : EntitySystem { [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; diff --git a/Content.Server/Atmos/Piping/EntitySystems/AtmosPipeAppearanceSystem.cs b/Content.Server/Atmos/Piping/EntitySystems/AtmosPipeAppearanceSystem.cs index 6fbf60f403..10049e273b 100644 --- a/Content.Server/Atmos/Piping/EntitySystems/AtmosPipeAppearanceSystem.cs +++ b/Content.Server/Atmos/Piping/EntitySystems/AtmosPipeAppearanceSystem.cs @@ -3,14 +3,12 @@ using Content.Server.NodeContainer.Nodes; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; -using Robust.Server.GameObjects; -using Robust.Shared.Map; +using Robust.Shared.Map.Components; namespace Content.Server.Atmos.Piping.EntitySystems; public sealed class AtmosPipeAppearanceSystem : EntitySystem { - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; public override void Initialize() @@ -31,7 +29,7 @@ private void UpdateAppearance(EntityUid uid, AppearanceComponent? appearance = n if (!Resolve(uid, ref appearance, ref container, ref xform, false)) return; - if (!_mapManager.TryGetGrid(xform.GridUid, out var grid)) + if (!TryComp(xform.GridUid, out var grid)) return; // get connected entities diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs index ad647fad1b..170586339d 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs @@ -13,7 +13,6 @@ using Content.Shared.Atmos.Piping.Binary.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; -using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Lock; using Robust.Server.GameObjects; @@ -29,8 +28,6 @@ public sealed class GasCanisterSystem : EntitySystem [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedContainerSystem _container = default!; - [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs index b1caa6c197..4ddd19dd45 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs @@ -7,15 +7,14 @@ using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Construction.Components; using JetBrains.Annotations; -using Robust.Server.GameObjects; using Robust.Shared.Map; +using Robust.Shared.Map.Components; namespace Content.Server.Atmos.Piping.Unary.EntitySystems { [UsedImplicitly] public sealed class GasPortableSystem : EntitySystem { - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; @@ -58,7 +57,7 @@ public bool FindGasPortIn(EntityUid? gridId, EntityCoordinates coordinates, [Not { port = null; - if (!_mapManager.TryGetGrid(gridId, out var grid)) + if (!TryComp(gridId, out var grid)) return false; foreach (var entityUid in grid.GetLocal(coordinates)) diff --git a/Content.Server/Bed/BedSystem.cs b/Content.Server/Bed/BedSystem.cs index 9e622ba6b8..3bb191fecd 100644 --- a/Content.Server/Bed/BedSystem.cs +++ b/Content.Server/Bed/BedSystem.cs @@ -94,9 +94,8 @@ private void OnBuckleChange(EntityUid uid, StasisBedComponent component, ref Buc if (!this.IsPowered(uid, EntityManager)) return; - var metabolicEvent = new ApplyMetabolicMultiplierEvent - {Uid = args.BuckledEntity, Multiplier = component.Multiplier, Apply = args.Buckling}; - RaiseLocalEvent(args.BuckledEntity, metabolicEvent); + var metabolicEvent = new ApplyMetabolicMultiplierEvent(args.BuckledEntity, component.Multiplier, args.Buckling); + RaiseLocalEvent(args.BuckledEntity, ref metabolicEvent); } private void OnPowerChanged(EntityUid uid, StasisBedComponent component, ref PowerChangedEvent args) @@ -122,9 +121,8 @@ private void UpdateMetabolisms(EntityUid uid, StasisBedComponent component, bool foreach (var buckledEntity in strap.BuckledEntities) { - var metabolicEvent = new ApplyMetabolicMultiplierEvent - {Uid = buckledEntity, Multiplier = component.Multiplier, Apply = shouldApply}; - RaiseLocalEvent(buckledEntity, metabolicEvent); + var metabolicEvent = new ApplyMetabolicMultiplierEvent(buckledEntity, component.Multiplier, shouldApply); + RaiseLocalEvent(buckledEntity, ref metabolicEvent); } } } diff --git a/Content.Server/Body/Commands/AddHandCommand.cs b/Content.Server/Body/Commands/AddHandCommand.cs index 655d0c88f9..3e006c539c 100644 --- a/Content.Server/Body/Commands/AddHandCommand.cs +++ b/Content.Server/Body/Commands/AddHandCommand.cs @@ -34,7 +34,6 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) switch (args.Length) { case 0: - { if (player == null) { shell.WriteLine("Only a player can run this command without arguments."); @@ -50,71 +49,68 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) entity = player.AttachedEntity.Value; hand = _entManager.SpawnEntity(DefaultHandPrototype, _entManager.GetComponent(entity).Coordinates); break; - } case 1: - { - if (NetEntity.TryParse(args[0], out var uidNet) && _entManager.TryGetEntity(uidNet, out var uid)) { - if (!_entManager.EntityExists(uid)) + if (NetEntity.TryParse(args[0], out var uidNet) && _entManager.TryGetEntity(uidNet, out var uid)) { - shell.WriteLine($"No entity found with uid {uid}"); - return; + if (!_entManager.EntityExists(uid)) + { + shell.WriteLine($"No entity found with uid {uid}"); + return; + } + + entity = uid.Value; + hand = _entManager.SpawnEntity(DefaultHandPrototype, _entManager.GetComponent(entity).Coordinates); + } + else + { + if (player == null) + { + shell.WriteLine("You must specify an entity to add a hand to when using this command from the server terminal."); + return; + } + + if (player.AttachedEntity == null) + { + shell.WriteLine("You don't have an entity to add a hand to."); + return; + } + + entity = player.AttachedEntity.Value; + hand = _entManager.SpawnEntity(args[0], _entManager.GetComponent(entity).Coordinates); } - entity = uid.Value; - hand = _entManager.SpawnEntity(DefaultHandPrototype, _entManager.GetComponent(entity).Coordinates); + break; } - else + case 2: { - if (player == null) + if (!NetEntity.TryParse(args[0], out var netEnt) || !_entManager.TryGetEntity(netEnt, out var uid)) { - shell.WriteLine("You must specify an entity to add a hand to when using this command from the server terminal."); + shell.WriteLine($"{args[0]} is not a valid entity uid."); return; } - if (player.AttachedEntity == null) + if (!_entManager.EntityExists(uid)) { - shell.WriteLine("You don't have an entity to add a hand to."); + shell.WriteLine($"No entity exists with uid {uid}."); return; } - entity = player.AttachedEntity.Value; - hand = _entManager.SpawnEntity(args[0], _entManager.GetComponent(entity).Coordinates); - } - - break; - } - case 2: - { - if (!NetEntity.TryParse(args[0], out var netEnt) || !_entManager.TryGetEntity(netEnt, out var uid)) - { - shell.WriteLine($"{args[0]} is not a valid entity uid."); - return; - } + entity = uid.Value; - if (!_entManager.EntityExists(uid)) - { - shell.WriteLine($"No entity exists with uid {uid}."); - return; - } + if (!_protoManager.HasIndex(args[1])) + { + shell.WriteLine($"No hand entity exists with id {args[1]}."); + return; + } - entity = uid.Value; + hand = _entManager.SpawnEntity(args[1], _entManager.GetComponent(entity).Coordinates); - if (!_protoManager.HasIndex(args[1])) - { - shell.WriteLine($"No hand entity exists with id {args[1]}."); - return; + break; } - - hand = _entManager.SpawnEntity(args[1], _entManager.GetComponent(entity).Coordinates); - - break; - } default: - { shell.WriteLine(Help); return; - } } if (!_entManager.TryGetComponent(entity, out BodyComponent? body) || body.RootContainer.ContainedEntity == null) @@ -139,7 +135,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) var slotId = part.GetHashCode().ToString(); - if (!bodySystem.TryCreatePartSlotAndAttach(attachAt.Id, slotId, hand, BodyPartType.Hand,attachAt.Component, part)) + if (!bodySystem.TryCreatePartSlotAndAttach(attachAt.Id, slotId, hand, BodyPartType.Hand, attachAt.Component, part)) { shell.WriteError($"Couldn't create a slot with id {slotId} on entity {_entManager.ToPrettyString(entity)}"); return; diff --git a/Content.Server/Body/Commands/AttachBodyPartCommand.cs b/Content.Server/Body/Commands/AttachBodyPartCommand.cs index 24604b88b7..82f7161937 100644 --- a/Content.Server/Body/Commands/AttachBodyPartCommand.cs +++ b/Content.Server/Body/Commands/AttachBodyPartCommand.cs @@ -103,11 +103,11 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract if (body.RootContainer.ContainedEntity != null) { - bodySystem.AttachPartToRoot(bodyId,partUid.Value, body ,part); + bodySystem.AttachPartToRoot(bodyId, partUid.Value, body, part); } else { - var (rootPartId,rootPart) = bodySystem.GetRootPartOrNull(bodyId, body)!.Value; + var (rootPartId, rootPart) = bodySystem.GetRootPartOrNull(bodyId, body)!.Value; if (!bodySystem.TryCreatePartSlotAndAttach(rootPartId, slotId, partUid.Value, part.PartType, rootPart, part)) { shell.WriteError($"Could not create slot {slotId} on entity {_entManager.ToPrettyString(bodyId)}"); diff --git a/Content.Server/Body/Components/BeingGibbedEvent.cs b/Content.Server/Body/Components/BeingGibbedEvent.cs index 66b52af47b..a010855f78 100644 --- a/Content.Server/Body/Components/BeingGibbedEvent.cs +++ b/Content.Server/Body/Components/BeingGibbedEvent.cs @@ -1,11 +1,7 @@ namespace Content.Server.Body.Components; -public sealed class BeingGibbedEvent : EntityEventArgs -{ - public readonly HashSet GibbedParts; - - public BeingGibbedEvent(HashSet gibbedParts) - { - GibbedParts = gibbedParts; - } -} +/// +/// Raised when a body gets gibbed, before it is deleted. +/// +[ByRefEvent] +public readonly record struct BeingGibbedEvent(HashSet GibbedParts); diff --git a/Content.Server/Body/Components/BloodstreamComponent.cs b/Content.Server/Body/Components/BloodstreamComponent.cs index 7041df4448..d448c4aab2 100644 --- a/Content.Server/Body/Components/BloodstreamComponent.cs +++ b/Content.Server/Body/Components/BloodstreamComponent.cs @@ -1,11 +1,13 @@ using Content.Server.Body.Systems; using Content.Server.Chemistry.EntitySystems; using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; using Robust.Shared.Audio; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.Body.Components { @@ -16,7 +18,17 @@ public sealed partial class BloodstreamComponent : Component public static string DefaultBloodSolutionName = "bloodstream"; public static string DefaultBloodTemporarySolutionName = "bloodstreamTemporary"; - public float AccumulatedFrametime = 0.0f; + /// + /// The next time that blood level will be updated and bloodloss damage dealt. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan NextUpdate; + + /// + /// The interval at which this component updates. + /// + [DataField] + public TimeSpan UpdateInterval = TimeSpan.FromSeconds(3); /// /// How much is this entity currently bleeding? @@ -32,7 +44,7 @@ public sealed partial class BloodstreamComponent : Component public float BleedAmount; /// - /// How much should bleeding should be reduced every update interval? + /// How much should bleeding be reduced every update interval? /// [DataField] public float BleedReductionAmount = 0.33f; @@ -63,18 +75,12 @@ public sealed partial class BloodstreamComponent : Component [DataField(required: true)] public DamageSpecifier BloodlossHealDamage = new(); - /// - /// How frequently should this bloodstream update, in seconds? - /// - [DataField] - public float UpdateInterval = 3.0f; - // TODO shouldn't be hardcoded, should just use some organ simulation like bone marrow or smth. /// /// How much reagent of blood should be restored each update interval? /// [DataField] - public float BloodRefreshAmount = 1.0f; + public FixedPoint2 BloodRefreshAmount = 1.0f; /// /// How much blood needs to be in the temporary solution in order to create a puddle? @@ -89,8 +95,8 @@ public sealed partial class BloodstreamComponent : Component /// /// For example, piercing damage is increased while poison damage is nullified entirely. /// - [DataField(customTypeSerializer:typeof(PrototypeIdSerializer))] - public string DamageBleedModifiers = "BloodlossHuman"; + [DataField] + public ProtoId DamageBleedModifiers = "BloodlossHuman"; /// /// The sound to be played when a weapon instantly deals blood loss damage. @@ -126,7 +132,7 @@ public sealed partial class BloodstreamComponent : Component /// Slime-people might use slime as their blood or something like that. /// [DataField] - public string BloodReagent = "Blood"; + public ProtoId BloodReagent = "Blood"; /// Name/Key that is indexed by. [DataField] @@ -164,6 +170,6 @@ public sealed partial class BloodstreamComponent : Component /// Variable that stores the amount of status time added by having a low blood level. /// [ViewVariables(VVAccess.ReadWrite)] - public float StatusTime; + public TimeSpan StatusTime; } } diff --git a/Content.Server/Body/Components/InternalsComponent.cs b/Content.Server/Body/Components/InternalsComponent.cs index 4eda008b0f..18caab8dcf 100644 --- a/Content.Server/Body/Components/InternalsComponent.cs +++ b/Content.Server/Body/Components/InternalsComponent.cs @@ -1,4 +1,3 @@ -using System.Threading; namespace Content.Server.Body.Components { /// @@ -7,14 +6,17 @@ namespace Content.Server.Body.Components [RegisterComponent] public sealed partial class InternalsComponent : Component { - [ViewVariables] public EntityUid? GasTankEntity { get; set; } - [ViewVariables] public EntityUid? BreathToolEntity { get; set; } + [ViewVariables] + public EntityUid? GasTankEntity; + + [ViewVariables] + public EntityUid? BreathToolEntity; /// - /// Toggle Internals delay (seconds) when the target is not you. + /// Toggle Internals delay when the target is not you. /// [ViewVariables(VVAccess.ReadWrite)] - [DataField("delay")] - public float Delay = 3; + [DataField] + public TimeSpan Delay = TimeSpan.FromSeconds(3); } } diff --git a/Content.Server/Body/Components/LungComponent.cs b/Content.Server/Body/Components/LungComponent.cs index 0656ef8fad..46600b3020 100644 --- a/Content.Server/Body/Components/LungComponent.cs +++ b/Content.Server/Body/Components/LungComponent.cs @@ -1,4 +1,4 @@ -using Content.Server.Atmos; +using Content.Server.Atmos; using Content.Server.Body.Systems; using Content.Shared.Alert; using Content.Shared.Atmos; @@ -11,7 +11,7 @@ public sealed partial class LungComponent : Component { [DataField] [Access(typeof(LungSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends - public GasMixture Air { get; set; } = new() + public GasMixture Air = new() { Volume = 6, Temperature = Atmospherics.NormalBodyTemperature diff --git a/Content.Server/Body/Components/MetabolizerComponent.cs b/Content.Server/Body/Components/MetabolizerComponent.cs index a8c82f3d36..90c99df7db 100644 --- a/Content.Server/Body/Components/MetabolizerComponent.cs +++ b/Content.Server/Body/Components/MetabolizerComponent.cs @@ -1,8 +1,8 @@ -using Content.Server.Body.Systems; +using Content.Server.Body.Systems; using Content.Shared.Body.Prototypes; using Content.Shared.FixedPoint; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.Body.Components { @@ -12,20 +12,24 @@ namespace Content.Server.Body.Components [RegisterComponent, Access(typeof(MetabolizerSystem))] public sealed partial class MetabolizerComponent : Component { - public float AccumulatedFrametime = 0.0f; + /// + /// The next time that reagents will be metabolized. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan NextUpdate; /// - /// How often to metabolize reagents, in seconds. + /// How often to metabolize reagents. /// /// [DataField] - public float UpdateFrequency = 1.0f; + public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1); /// /// From which solution will this metabolizer attempt to metabolize chemicals /// [DataField("solution")] - public string SolutionName { get; set; } = BloodstreamComponent.DefaultChemicalsSolutionName; + public string SolutionName = BloodstreamComponent.DefaultChemicalsSolutionName; /// /// Does this component use a solution on it's parent entity (the body) or itself @@ -39,9 +43,9 @@ public sealed partial class MetabolizerComponent : Component /// /// List of metabolizer types that this organ is. ex. Human, Slime, Felinid, w/e. /// - [DataField(customTypeSerializer:typeof(PrototypeIdHashSetSerializer))] + [DataField] [Access(typeof(MetabolizerSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends - public HashSet? MetabolizerTypes = null; + public HashSet>? MetabolizerTypes = null; /// /// Should this metabolizer remove chemicals that have no metabolisms defined? @@ -72,8 +76,8 @@ public sealed partial class MetabolizerComponent : Component [DataDefinition] public sealed partial class MetabolismGroupEntry { - [DataField(required: true, customTypeSerializer:typeof(PrototypeIdSerializer))] - public string Id = default!; + [DataField(required: true)] + public ProtoId Id = default!; [DataField("rateModifier")] public FixedPoint2 MetabolismRateModifier = 1.0; diff --git a/Content.Server/Body/Components/RespiratorComponent.cs b/Content.Server/Body/Components/RespiratorComponent.cs index 9f080a3dd9..4045e21e26 100644 --- a/Content.Server/Body/Components/RespiratorComponent.cs +++ b/Content.Server/Body/Components/RespiratorComponent.cs @@ -1,5 +1,6 @@ using Content.Server.Body.Systems; using Content.Shared.Damage; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.Body.Components { @@ -7,36 +8,49 @@ namespace Content.Server.Body.Components public sealed partial class RespiratorComponent : Component { /// - /// Saturation level. Reduced by CycleDelay each tick. + /// The next time that this body will inhale or exhale. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan NextUpdate; + + /// + /// The interval between updates. Each update is either inhale or exhale, + /// so a full cycle takes twice as long. + /// + [DataField] + public TimeSpan UpdateInterval = TimeSpan.FromSeconds(2); + + /// + /// Saturation level. Reduced by UpdateInterval each tick. /// Can be thought of as 'how many seconds you have until you start suffocating' in this configuration. /// - [DataField("saturation")] + [DataField] public float Saturation = 5.0f; /// /// At what level of saturation will you begin to suffocate? /// - [DataField("suffocationThreshold")] + [DataField] public float SuffocationThreshold; - [DataField("maxSaturation")] + [DataField] public float MaxSaturation = 5.0f; - [DataField("minSaturation")] + [DataField] public float MinSaturation = -2.0f; // TODO HYPEROXIA? - [DataField("damage", required: true)] + [DataField(required: true)] [ViewVariables(VVAccess.ReadWrite)] public DamageSpecifier Damage = default!; - [DataField("damageRecovery", required: true)] + [DataField(required: true)] [ViewVariables(VVAccess.ReadWrite)] public DamageSpecifier DamageRecovery = default!; - [DataField("gaspPopupCooldown")] - public TimeSpan GaspPopupCooldown { get; private set; } = TimeSpan.FromSeconds(8); + [DataField] + public TimeSpan GaspPopupCooldown = TimeSpan.FromSeconds(8); [ViewVariables] public TimeSpan LastGaspPopupTime; @@ -55,11 +69,6 @@ public sealed partial class RespiratorComponent : Component [ViewVariables] public RespiratorStatus Status = RespiratorStatus.Inhaling; - - [DataField("cycleDelay")] - public float CycleDelay = 2.0f; - - public float AccumulatedFrametime; } } diff --git a/Content.Server/Body/Components/StomachComponent.cs b/Content.Server/Body/Components/StomachComponent.cs index fe93468f74..d541ca4d7c 100644 --- a/Content.Server/Body/Components/StomachComponent.cs +++ b/Content.Server/Body/Components/StomachComponent.cs @@ -1,21 +1,26 @@ -using Content.Server.Body.Systems; +using Content.Server.Body.Systems; using Content.Server.Nutrition.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Content.Shared.Whitelist; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.Body.Components { [RegisterComponent, Access(typeof(StomachSystem), typeof(FoodSystem))] public sealed partial class StomachComponent : Component { - public float AccumulatedFrameTime; + /// + /// The next time that the stomach will try to digest its contents. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan NextUpdate; /// - /// How fast should this component update, in seconds? + /// The interval at which this stomach digests its contents. /// [DataField] - public float UpdateInterval = 1.0f; + public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1); /// /// The solution inside of this stomach this transfers reagents to the body. @@ -30,11 +35,11 @@ public sealed partial class StomachComponent : Component public string BodySolutionName = BloodstreamComponent.DefaultChemicalsSolutionName; /// - /// Time in seconds between reagents being ingested and them being + /// Time between reagents being ingested and them being /// transferred to /// [DataField] - public float DigestionDelay = 20; + public TimeSpan DigestionDelay = TimeSpan.FromSeconds(20); /// /// A whitelist for what special-digestible-required foods this stomach is capable of eating. @@ -54,15 +59,15 @@ public sealed partial class StomachComponent : Component public sealed class ReagentDelta { public readonly ReagentQuantity ReagentQuantity; - public float Lifetime { get; private set; } + public TimeSpan Lifetime { get; private set; } public ReagentDelta(ReagentQuantity reagentQuantity) { ReagentQuantity = reagentQuantity; - Lifetime = 0.0f; + Lifetime = TimeSpan.Zero; } - public void Increment(float delta) => Lifetime += delta; + public void Increment(TimeSpan delta) => Lifetime += delta; } } } diff --git a/Content.Server/Body/Components/ThermalRegulatorComponent.cs b/Content.Server/Body/Components/ThermalRegulatorComponent.cs index 4acdccf1ba..19b76189e0 100644 --- a/Content.Server/Body/Components/ThermalRegulatorComponent.cs +++ b/Content.Server/Body/Components/ThermalRegulatorComponent.cs @@ -1,4 +1,5 @@ using Content.Server.Body.Systems; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.Body.Components; @@ -6,48 +7,58 @@ namespace Content.Server.Body.Components; [Access(typeof(ThermalRegulatorSystem))] public sealed partial class ThermalRegulatorComponent : Component { + /// + /// The next time that the body will regulate its heat. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan NextUpdate; + + /// + /// The interval at which thermal regulation is processed. + /// + [DataField] + public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1); + /// /// Heat generated due to metabolism. It's generated via metabolism /// - [DataField("metabolismHeat")] - public float MetabolismHeat { get; private set; } + [DataField] + public float MetabolismHeat; /// /// Heat output via radiation. /// - [DataField("radiatedHeat")] - public float RadiatedHeat { get; private set; } + [DataField] + public float RadiatedHeat; /// /// Maximum heat regulated via sweat /// - [DataField("sweatHeatRegulation")] - public float SweatHeatRegulation { get; private set; } + [DataField] + public float SweatHeatRegulation; /// /// Maximum heat regulated via shivering /// - [DataField("shiveringHeatRegulation")] - public float ShiveringHeatRegulation { get; private set; } + [DataField] + public float ShiveringHeatRegulation; /// /// Amount of heat regulation that represents thermal regulation processes not /// explicitly coded. /// - [DataField("implicitHeatRegulation")] - public float ImplicitHeatRegulation { get; private set; } + [DataField] + public float ImplicitHeatRegulation; /// /// Normal body temperature /// - [DataField("normalBodyTemperature")] - public float NormalBodyTemperature { get; private set; } + [DataField] + public float NormalBodyTemperature; /// /// Deviation from normal temperature for body to start thermal regulation /// - [DataField("thermalRegulationTemperatureThreshold")] - public float ThermalRegulationTemperatureThreshold { get; private set; } - - public float AccumulatedFrametime; + [DataField] + public float ThermalRegulationTemperatureThreshold; } diff --git a/Content.Server/Body/Systems/BloodstreamSystem.cs b/Content.Server/Body/Systems/BloodstreamSystem.cs index f6fdcfedff..9e29fdf756 100644 --- a/Content.Server/Body/Systems/BloodstreamSystem.cs +++ b/Content.Server/Body/Systems/BloodstreamSystem.cs @@ -13,7 +13,6 @@ using Content.Shared.Damage.Prototypes; using Content.Shared.Drunk; using Content.Shared.FixedPoint; -using Content.Shared.IdentityManagement; using Content.Shared.Mobs.Systems; using Content.Shared.Popups; using Content.Shared.Rejuvenate; @@ -21,11 +20,13 @@ using Robust.Server.Audio; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Timing; namespace Content.Server.Body.Systems; public sealed class BloodstreamSystem : EntitySystem { + [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly AudioSystem _audio = default!; @@ -44,6 +45,8 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnUnpaused); SubscribeLocalEvent(OnDamageChanged); SubscribeLocalEvent(OnHealthBeingExamined); SubscribeLocalEvent(OnBeingGibbed); @@ -53,6 +56,16 @@ public override void Initialize() SubscribeLocalEvent(OnRejuvenate); } + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval; + } + + private void OnUnpaused(Entity ent, ref EntityUnpausedEvent args) + { + ent.Comp.NextUpdate += args.PausedTime; + } + private void OnReactionAttempt(Entity entity, ref ReactionAttemptEvent args) { if (args.Cancelled) @@ -83,7 +96,9 @@ private void OnReactionAttempt(Entity entity, ref Solution if (args.Name != entity.Comp.BloodSolutionName && args.Name != entity.Comp.ChemicalSolutionName && args.Name != entity.Comp.BloodTemporarySolutionName) + { return; + } OnReactionAttempt(entity, ref args.Event); } @@ -95,12 +110,10 @@ public override void Update(float frameTime) var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var bloodstream)) { - bloodstream.AccumulatedFrametime += frameTime; - - if (bloodstream.AccumulatedFrametime < bloodstream.UpdateInterval) + if (_gameTiming.CurTime < bloodstream.NextUpdate) continue; - bloodstream.AccumulatedFrametime -= bloodstream.UpdateInterval; + bloodstream.NextUpdate += bloodstream.UpdateInterval; if (!_solutionContainerSystem.ResolveSolution(uid, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution)) continue; @@ -128,13 +141,17 @@ public override void Update(float frameTime) // bloodloss damage is based on the base value, and modified by how low your blood level is. var amt = bloodstream.BloodlossDamage / (0.1f + bloodPercentage); - _damageableSystem.TryChangeDamage(uid, amt, false, false); + _damageableSystem.TryChangeDamage(uid, amt, + ignoreResistances: false, interruptsDoAfters: false); // Apply dizziness as a symptom of bloodloss. // The effect is applied in a way that it will never be cleared without being healthy. // Multiplying by 2 is arbitrary but works for this case, it just prevents the time from running out - _drunkSystem.TryApplyDrunkenness(uid, bloodstream.UpdateInterval*2, false); - _stutteringSystem.DoStutter(uid, TimeSpan.FromSeconds(bloodstream.UpdateInterval*2), false); + _drunkSystem.TryApplyDrunkenness( + uid, + (float) bloodstream.UpdateInterval.TotalSeconds * 2, + applySlur: false); + _stutteringSystem.DoStutter(uid, bloodstream.UpdateInterval * 2, refresh: false); // storing the drunk and stutter time so we can remove it independently from other effects additions bloodstream.StatusTime += bloodstream.UpdateInterval * 2; @@ -142,13 +159,16 @@ public override void Update(float frameTime) else if (!_mobStateSystem.IsDead(uid)) { // If they're healthy, we'll try and heal some bloodloss instead. - _damageableSystem.TryChangeDamage(uid, bloodstream.BloodlossHealDamage * bloodPercentage, true, false); + _damageableSystem.TryChangeDamage( + uid, + bloodstream.BloodlossHealDamage * bloodPercentage, + ignoreResistances: true, interruptsDoAfters: false); // Remove the drunk effect when healthy. Should only remove the amount of drunk and stutter added by low blood level - _drunkSystem.TryRemoveDrunkenessTime(uid, bloodstream.StatusTime); - _stutteringSystem.DoRemoveStutterTime(uid, bloodstream.StatusTime); + _drunkSystem.TryRemoveDrunkenessTime(uid, bloodstream.StatusTime.TotalSeconds); + _stutteringSystem.DoRemoveStutterTime(uid, bloodstream.StatusTime.TotalSeconds); // Reset the drunk and stutter time to zero - bloodstream.StatusTime = 0; + bloodstream.StatusTime = TimeSpan.Zero; } } } @@ -167,17 +187,15 @@ private void OnComponentInit(Entity entity, ref ComponentI bloodSolution.AddReagent(entity.Comp.BloodReagent, entity.Comp.BloodMaxVolume - bloodSolution.Volume); } - private void OnDamageChanged(EntityUid uid, BloodstreamComponent component, DamageChangedEvent args) + private void OnDamageChanged(Entity ent, ref DamageChangedEvent args) { - if (args.DamageDelta is null) - return; - - // definitely don't make them bleed if they got healed - if (!args.DamageIncreased) + if (args.DamageDelta is null || !args.DamageIncreased) + { return; + } // TODO probably cache this or something. humans get hurt a lot - if (!_prototypeManager.TryIndex(component.DamageBleedModifiers, out var modifiers)) + if (!_prototypeManager.TryIndex(ent.Comp.DamageBleedModifiers, out var modifiers)) return; var bloodloss = DamageSpecifier.ApplyModifierSet(args.DamageDelta, modifiers); @@ -186,10 +204,10 @@ private void OnDamageChanged(EntityUid uid, BloodstreamComponent component, Dama return; // Does the calculation of how much bleed rate should be added/removed, then applies it - var oldBleedAmount = component.BleedAmount; + var oldBleedAmount = ent.Comp.BleedAmount; var total = bloodloss.GetTotal(); var totalFloat = total.Float(); - TryModifyBleedAmount(uid, totalFloat, component); + TryModifyBleedAmount(ent, totalFloat, ent); /// /// Critical hit. Causes target to lose blood, using the bleed rate modifier of the weapon, currently divided by 5 @@ -199,8 +217,8 @@ private void OnDamageChanged(EntityUid uid, BloodstreamComponent component, Dama var prob = Math.Clamp(totalFloat / 25, 0, 1); if (totalFloat > 0 && _robustRandom.Prob(prob)) { - TryModifyBloodLevel(uid, (-total) / 5, component); - _audio.PlayPvs(component.InstantBloodSound, uid); + TryModifyBloodLevel(ent, (-total) / 5, ent); + _audio.PlayPvs(ent.Comp.InstantBloodSound, ent); } // Heat damage will cauterize, causing the bleed rate to be reduced. @@ -210,53 +228,52 @@ private void OnDamageChanged(EntityUid uid, BloodstreamComponent component, Dama // because it's burn damage that cauterized their wounds. // We'll play a special sound and popup for feedback. - _audio.PlayPvs(component.BloodHealedSound, uid); - _popupSystem.PopupEntity(Loc.GetString("bloodstream-component-wounds-cauterized"), uid, - uid, PopupType.Medium); + _audio.PlayPvs(ent.Comp.BloodHealedSound, ent); + _popupSystem.PopupEntity(Loc.GetString("bloodstream-component-wounds-cauterized"), ent, + ent, PopupType.Medium); } } /// /// Shows text on health examine, based on bleed rate and blood level. /// - private void OnHealthBeingExamined(EntityUid uid, BloodstreamComponent component, HealthBeingExaminedEvent args) + private void OnHealthBeingExamined(Entity ent, ref HealthBeingExaminedEvent args) { // Shows profusely bleeding at half the max bleed rate. - if (component.BleedAmount > component.MaxBleedAmount / 2) + if (ent.Comp.BleedAmount > ent.Comp.MaxBleedAmount / 2) { args.Message.PushNewline(); - args.Message.AddMarkup(Loc.GetString("bloodstream-component-profusely-bleeding", ("target", Identity.Entity(uid, EntityManager)))); + args.Message.AddMarkup(Loc.GetString("bloodstream-component-profusely-bleeding", ("target", ent.Owner))); } // Shows bleeding message when bleeding, but less than profusely. - else if (component.BleedAmount > 0) + else if (ent.Comp.BleedAmount > 0) { args.Message.PushNewline(); - args.Message.AddMarkup(Loc.GetString("bloodstream-component-bleeding", ("target", Identity.Entity(uid, EntityManager)))); + args.Message.AddMarkup(Loc.GetString("bloodstream-component-bleeding", ("target", ent.Owner))); } // If the mob's blood level is below the damage threshhold, the pale message is added. - if (GetBloodLevelPercentage(uid, component) < component.BloodlossThreshold) + if (GetBloodLevelPercentage(ent, ent) < ent.Comp.BloodlossThreshold) { args.Message.PushNewline(); - args.Message.AddMarkup(Loc.GetString("bloodstream-component-looks-pale", ("target", Identity.Entity(uid, EntityManager)))); + args.Message.AddMarkup(Loc.GetString("bloodstream-component-looks-pale", ("target", ent.Owner))); } } - private void OnBeingGibbed(EntityUid uid, BloodstreamComponent component, BeingGibbedEvent args) + private void OnBeingGibbed(Entity ent, ref BeingGibbedEvent args) { - SpillAllSolutions(uid, component); + SpillAllSolutions(ent, ent); } - private void OnApplyMetabolicMultiplier(EntityUid uid, BloodstreamComponent component, ApplyMetabolicMultiplierEvent args) + private void OnApplyMetabolicMultiplier( + Entity ent, + ref ApplyMetabolicMultiplierEvent args) { if (args.Apply) { - component.UpdateInterval *= args.Multiplier; + ent.Comp.UpdateInterval *= args.Multiplier; return; } - component.UpdateInterval /= args.Multiplier; - // Reset the accumulator properly - if (component.AccumulatedFrametime >= component.UpdateInterval) - component.AccumulatedFrametime = component.UpdateInterval; + ent.Comp.UpdateInterval /= args.Multiplier; } private void OnRejuvenate(Entity entity, ref RejuvenateEvent args) @@ -275,21 +292,15 @@ private void OnRejuvenate(Entity entity, ref RejuvenateEve /// public bool TryAddToChemicals(EntityUid uid, Solution solution, BloodstreamComponent? component = null) { - if (!Resolve(uid, ref component, false)) - return false; - - if (!_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution)) - return false; - - return _solutionContainerSystem.TryAddSolution(component.ChemicalSolution.Value, solution); + return Resolve(uid, ref component, logMissing: false) + && _solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution) + && _solutionContainerSystem.TryAddSolution(component.ChemicalSolution.Value, solution); } public bool FlushChemicals(EntityUid uid, string excludedReagentID, FixedPoint2 quantity, BloodstreamComponent? component = null) { - if (!Resolve(uid, ref component, false)) - return false; - - if (!_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution, out var chemSolution)) + if (!Resolve(uid, ref component, logMissing: false) + || !_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution, out var chemSolution)) return false; for (var i = chemSolution.Contents.Count - 1; i >= 0; i--) @@ -306,11 +317,11 @@ public bool FlushChemicals(EntityUid uid, string excludedReagentID, FixedPoint2 public float GetBloodLevelPercentage(EntityUid uid, BloodstreamComponent? component = null) { - if (!Resolve(uid, ref component)) - return 0.0f; - - if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution)) + if (!Resolve(uid, ref component) + || !_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution)) + { return 0.0f; + } return bloodSolution.FillFraction; } @@ -328,11 +339,11 @@ public void SetBloodLossThreshold(EntityUid uid, float threshold, BloodstreamCom /// public bool TryModifyBloodLevel(EntityUid uid, FixedPoint2 amount, BloodstreamComponent? component = null) { - if (!Resolve(uid, ref component, false)) - return false; - - if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution)) + if (!Resolve(uid, ref component, logMissing: false) + || !_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution)) + { return false; + } if (amount >= 0) return _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, amount, out _); @@ -356,9 +367,9 @@ public bool TryModifyBloodLevel(EntityUid uid, FixedPoint2 amount, BloodstreamCo tempSolution.AddSolution(temp, _prototypeManager); } - if (_puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, false)) + if (_puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, sound: false)) { - _forensicsSystem.TransferDna(puddleUid, uid, false); + _forensicsSystem.TransferDna(puddleUid, uid, canDnaBeCleaned: false); } tempSolution.RemoveAllSolution(); @@ -374,7 +385,7 @@ public bool TryModifyBloodLevel(EntityUid uid, FixedPoint2 amount, BloodstreamCo /// public bool TryModifyBleedAmount(EntityUid uid, float amount, BloodstreamComponent? component = null) { - if (!Resolve(uid, ref component, false)) + if (!Resolve(uid, ref component, logMissing: false)) return false; component.BleedAmount += amount; @@ -424,7 +435,7 @@ public void SpillAllSolutions(EntityUid uid, BloodstreamComponent? component = n if (_puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid)) { - _forensicsSystem.TransferDna(puddleUid, uid, false); + _forensicsSystem.TransferDna(puddleUid, uid, canDnaBeCleaned: false); } } @@ -433,11 +444,11 @@ public void SpillAllSolutions(EntityUid uid, BloodstreamComponent? component = n /// public void ChangeBloodReagent(EntityUid uid, string reagent, BloodstreamComponent? component = null) { - if (!Resolve(uid, ref component, false)) - return; - - if (reagent == component.BloodReagent) + if (!Resolve(uid, ref component, logMissing: false) + || reagent == component.BloodReagent) + { return; + } if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution)) { diff --git a/Content.Server/Body/Systems/BodySystem.cs b/Content.Server/Body/Systems/BodySystem.cs index 18119909ab..37f78ed81a 100644 --- a/Content.Server/Body/Systems/BodySystem.cs +++ b/Content.Server/Body/Systems/BodySystem.cs @@ -1,23 +1,19 @@ using Content.Server.Body.Components; using Content.Server.GameTicking; using Content.Server.Humanoid; -using Content.Server.Kitchen.Components; using Content.Shared.Body.Components; using Content.Shared.Body.Part; using Content.Shared.Body.Systems; using Content.Shared.Humanoid; -using Content.Shared.Kitchen.Components; using Content.Shared.Mind; using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Events; +using Content.Shared.Movement.Systems; using Robust.Shared.Audio; -using Robust.Shared.Player; +using Robust.Shared.Audio.Systems; using Robust.Shared.Random; using Robust.Shared.Timing; using System.Numerics; -using Content.Shared.Gibbing.Components; -using Content.Shared.Movement.Systems; -using Robust.Shared.Audio.Systems; namespace Content.Server.Body.Systems; @@ -28,9 +24,7 @@ public sealed class BodySystem : SharedBodySystem [Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedMindSystem _mindSystem = default!; - [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { @@ -40,7 +34,7 @@ public override void Initialize() SubscribeLocalEvent(OnApplyMetabolicMultiplier); } - private void OnRelayMoveInput(EntityUid uid, BodyComponent component, ref MoveInputEvent args) + private void OnRelayMoveInput(Entity ent, ref MoveInputEvent args) { // If they haven't actually moved then ignore it. if ((args.Component.HeldMoveButtons & @@ -49,68 +43,67 @@ private void OnRelayMoveInput(EntityUid uid, BodyComponent component, ref MoveIn return; } - if (_mobState.IsDead(uid) && _mindSystem.TryGetMind(uid, out var mindId, out var mind)) + if (_mobState.IsDead(ent) && _mindSystem.TryGetMind(ent, out var mindId, out var mind)) { mind.TimeOfDeath ??= _gameTiming.RealTime; - _ticker.OnGhostAttempt(mindId, true, mind: mind); + _ticker.OnGhostAttempt(mindId, canReturnGlobal: true, mind: mind); } } - private void OnApplyMetabolicMultiplier(EntityUid uid, BodyComponent component, - ApplyMetabolicMultiplierEvent args) + private void OnApplyMetabolicMultiplier( + Entity ent, + ref ApplyMetabolicMultiplierEvent args) { - foreach (var organ in GetBodyOrgans(uid, component)) + foreach (var organ in GetBodyOrgans(ent, ent)) { - RaiseLocalEvent(organ.Id, args); + RaiseLocalEvent(organ.Id, ref args); } } protected override void AddPart( - EntityUid bodyUid, - EntityUid partUid, - string slotId, - BodyPartComponent component, - BodyComponent? bodyComp = null) + Entity bodyEnt, + Entity partEnt, + string slotId) { // TODO: Predict this probably. - base.AddPart(bodyUid, partUid, slotId, component, bodyComp); + base.AddPart(bodyEnt, partEnt, slotId); - if (TryComp(bodyUid, out var humanoid)) + if (TryComp(bodyEnt, out var humanoid)) { - var layer = component.ToHumanoidLayers(); + var layer = partEnt.Comp.ToHumanoidLayers(); if (layer != null) { var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value); - _humanoidSystem.SetLayersVisibility(bodyUid, layers, true, true, humanoid); + _humanoidSystem.SetLayersVisibility( + bodyEnt, layers, visible: true, permanent: true, humanoid); } } } protected override void RemovePart( - EntityUid bodyUid, - EntityUid partUid, - string slotId, - BodyPartComponent component, - BodyComponent? bodyComp = null) + Entity bodyEnt, + Entity partEnt, + string slotId) { - base.RemovePart(bodyUid, partUid, slotId, component, bodyComp); + base.RemovePart(bodyEnt, partEnt, slotId); - if (!TryComp(bodyUid, out var humanoid)) + if (!TryComp(bodyEnt, out var humanoid)) return; - var layer = component.ToHumanoidLayers(); + var layer = partEnt.Comp.ToHumanoidLayers(); - if (layer == null) + if (layer is null) return; var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value); - _humanoidSystem.SetLayersVisibility(bodyUid, layers, false, true, humanoid); + _humanoidSystem.SetLayersVisibility( + bodyEnt, layers, visible: false, permanent: true, humanoid); } public override HashSet GibBody( EntityUid bodyId, bool gibOrgans = false, - BodyComponent? body = null , + BodyComponent? body = null, bool launchGibs = true, Vector2? splatDirection = null, float splatModifier = 1, @@ -118,19 +111,23 @@ public override HashSet GibBody( SoundSpecifier? gibSoundOverride = null ) { - if (!Resolve(bodyId, ref body, false)) - return new HashSet(); - - if (TerminatingOrDeleted(bodyId) || EntityManager.IsQueuedForDeletion(bodyId)) + if (!Resolve(bodyId, ref body, logMissing: false) + || TerminatingOrDeleted(bodyId) + || EntityManager.IsQueuedForDeletion(bodyId)) + { return new HashSet(); + } var xform = Transform(bodyId); - if (xform.MapUid == null) + if (xform.MapUid is null) return new HashSet(); var gibs = base.GibBody(bodyId, gibOrgans, body, launchGibs: launchGibs, splatDirection: splatDirection, splatModifier: splatModifier, splatCone:splatCone); - RaiseLocalEvent(bodyId, new BeingGibbedEvent(gibs)); + + var ev = new BeingGibbedEvent(gibs); + RaiseLocalEvent(bodyId, ref ev); + QueueDel(bodyId); return gibs; diff --git a/Content.Server/Body/Systems/BrainSystem.cs b/Content.Server/Body/Systems/BrainSystem.cs index abb5497120..86d2cb61ff 100644 --- a/Content.Server/Body/Systems/BrainSystem.cs +++ b/Content.Server/Body/Systems/BrainSystem.cs @@ -16,8 +16,8 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent((uid, _, args) => HandleMind(args.Body, uid)); - SubscribeLocalEvent((uid, _, args) => HandleMind(uid, args.OldBody)); + SubscribeLocalEvent((uid, _, args) => HandleMind(args.Body, uid)); + SubscribeLocalEvent((uid, _, args) => HandleMind(uid, args.OldBody)); SubscribeLocalEvent(OnPointAttempt); } @@ -39,7 +39,7 @@ private void HandleMind(EntityUid newEntity, EntityUid oldEntity) _mindSystem.TransferTo(mindId, newEntity, mind: mind); } - private void OnPointAttempt(EntityUid uid, BrainComponent component, PointAttemptEvent args) + private void OnPointAttempt(Entity ent, ref PointAttemptEvent args) { args.Cancel(); } diff --git a/Content.Server/Body/Systems/InternalsSystem.cs b/Content.Server/Body/Systems/InternalsSystem.cs index 007cbdf084..9607a808f6 100644 --- a/Content.Server/Body/Systems/InternalsSystem.cs +++ b/Content.Server/Body/Systems/InternalsSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Components; -using Content.Server.Hands.Systems; using Content.Server.Popups; using Content.Shared.Alert; using Content.Shared.Atmos; @@ -11,19 +10,16 @@ using Content.Shared.Inventory; using Content.Shared.Verbs; using Robust.Shared.Containers; -using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Server.Body.Systems; public sealed class InternalsSystem : EntitySystem { - [Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly AtmosphereSystem _atmos = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly GasTankSystem _gasTank = default!; - [Dependency] private readonly HandsSystem _hands = default!; [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; @@ -40,28 +36,36 @@ public override void Initialize() SubscribeLocalEvent(OnDoAfter); } - private void OnGetInteractionVerbs(EntityUid uid, InternalsComponent component, GetVerbsEvent args) + private void OnGetInteractionVerbs( + Entity ent, + ref GetVerbsEvent args) { - if (!args.CanAccess || !args.CanInteract || args.Hands == null) + if (!args.CanAccess || !args.CanInteract || args.Hands is null) return; + var user = args.User; + InteractionVerb verb = new() { Act = () => { - ToggleInternals(uid, args.User, false, component); + ToggleInternals(ent, user, force: false, ent); }, Message = Loc.GetString("action-description-internals-toggle"), - Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/dot.svg.192dpi.png")), + Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/dot.svg.192dpi.png")), Text = Loc.GetString("action-name-internals-toggle"), }; args.Verbs.Add(verb); } - public void ToggleInternals(EntityUid uid, EntityUid user, bool force, InternalsComponent? internals = null) + public void ToggleInternals( + EntityUid uid, + EntityUid user, + bool force, + InternalsComponent? internals = null) { - if (!Resolve(uid, ref internals, false)) + if (!Resolve(uid, ref internals, logMissing: false)) return; // Toggle off if they're on @@ -73,12 +77,12 @@ public void ToggleInternals(EntityUid uid, EntityUid user, bool force, Internals return; } - StartToggleInternalsDoAfter(user, uid, internals); + StartToggleInternalsDoAfter(user, (uid, internals)); return; } // If they're not on then check if we have a mask to use - if (internals.BreathToolEntity == null) + if (internals.BreathToolEntity is null) { _popupSystem.PopupEntity(Loc.GetString("internals-no-breath-tool"), uid, user); return; @@ -86,7 +90,7 @@ public void ToggleInternals(EntityUid uid, EntityUid user, bool force, Internals var tank = FindBestGasTank(uid); - if (tank == null) + if (tank is null) { _popupSystem.PopupEntity(Loc.GetString("internals-no-tank"), uid, user); return; @@ -94,20 +98,20 @@ public void ToggleInternals(EntityUid uid, EntityUid user, bool force, Internals if (!force) { - StartToggleInternalsDoAfter(user, uid, internals); + StartToggleInternalsDoAfter(user, (uid, internals)); return; } _gasTank.ConnectToInternals(tank.Value); } - private void StartToggleInternalsDoAfter(EntityUid user, EntityUid target, InternalsComponent internals) + private void StartToggleInternalsDoAfter(EntityUid user, Entity targetEnt) { // Is the target not you? If yes, use a do-after to give them time to respond. - var isUser = user == target; - var delay = !isUser ? internals.Delay : 0f; + var isUser = user == targetEnt.Owner; + var delay = !isUser ? targetEnt.Comp.Delay : TimeSpan.Zero; - _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, delay, new InternalsDoAfterEvent(), target, target: target) + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, delay, new InternalsDoAfterEvent(), targetEnt, target: targetEnt) { BreakOnUserMove = true, BreakOnDamage = true, @@ -116,66 +120,64 @@ private void StartToggleInternalsDoAfter(EntityUid user, EntityUid target, Inter }); } - private void OnDoAfter(EntityUid uid, InternalsComponent component, InternalsDoAfterEvent args) + private void OnDoAfter(Entity ent, ref InternalsDoAfterEvent args) { if (args.Cancelled || args.Handled) return; - ToggleInternals(uid, args.User, true, component); + ToggleInternals(ent, args.User, force: true, ent); args.Handled = true; } - private void OnInternalsStartup(EntityUid uid, InternalsComponent component, ComponentStartup args) + private void OnInternalsStartup(Entity ent, ref ComponentStartup args) { - _alerts.ShowAlert(uid, AlertType.Internals, GetSeverity(component)); + _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); } - private void OnInternalsShutdown(EntityUid uid, InternalsComponent component, ComponentShutdown args) + private void OnInternalsShutdown(Entity ent, ref ComponentShutdown args) { - _alerts.ClearAlert(uid, AlertType.Internals); + _alerts.ClearAlert(ent, AlertType.Internals); } - private void OnInhaleLocation(EntityUid uid, InternalsComponent component, InhaleLocationEvent args) + private void OnInhaleLocation(Entity ent, ref InhaleLocationEvent args) { - if (AreInternalsWorking(component)) + if (AreInternalsWorking(ent)) { - var gasTank = Comp(component.GasTankEntity!.Value); - args.Gas = _gasTank.RemoveAirVolume((component.GasTankEntity.Value, gasTank), Atmospherics.BreathVolume); + var gasTank = Comp(ent.Comp.GasTankEntity!.Value); + args.Gas = _gasTank.RemoveAirVolume((ent.Comp.GasTankEntity.Value, gasTank), Atmospherics.BreathVolume); // TODO: Should listen to gas tank updates instead I guess? - _alerts.ShowAlert(uid, AlertType.Internals, GetSeverity(component)); + _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); } } public void DisconnectBreathTool(Entity ent) { - var (owner, component) = ent; - var old = component.BreathToolEntity; - component.BreathToolEntity = null; + var old = ent.Comp.BreathToolEntity; + ent.Comp.BreathToolEntity = null; - if (TryComp(old, out BreathToolComponent? breathTool) ) + if (TryComp(old, out BreathToolComponent? breathTool)) { _atmos.DisconnectInternals(breathTool); DisconnectTank(ent); } - _alerts.ShowAlert(owner, AlertType.Internals, GetSeverity(component)); + _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); } public void ConnectBreathTool(Entity ent, EntityUid toolEntity) { - var (owner, component) = ent; - if (TryComp(component.BreathToolEntity, out BreathToolComponent? tool)) + if (TryComp(ent.Comp.BreathToolEntity, out BreathToolComponent? tool)) { _atmos.DisconnectInternals(tool); } - component.BreathToolEntity = toolEntity; - _alerts.ShowAlert(owner, AlertType.Internals, GetSeverity(component)); + ent.Comp.BreathToolEntity = toolEntity; + _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); } public void DisconnectTank(InternalsComponent? component) { - if (component == null) + if (component is null) return; if (TryComp(component.GasTankEntity, out GasTankComponent? tank)) @@ -187,46 +189,47 @@ public void DisconnectTank(InternalsComponent? component) public bool TryConnectTank(Entity ent, EntityUid tankEntity) { - var component = ent.Comp; - if (component.BreathToolEntity == null) + if (ent.Comp.BreathToolEntity is null) return false; - if (TryComp(component.GasTankEntity, out GasTankComponent? tank)) - _gasTank.DisconnectFromInternals((component.GasTankEntity.Value, tank)); + if (TryComp(ent.Comp.GasTankEntity, out GasTankComponent? tank)) + _gasTank.DisconnectFromInternals((ent.Comp.GasTankEntity.Value, tank)); - component.GasTankEntity = tankEntity; - _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(component)); + ent.Comp.GasTankEntity = tankEntity; + _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); return true; } public bool AreInternalsWorking(EntityUid uid, InternalsComponent? component = null) { - if (!Resolve(uid, ref component, false)) - return false; - - return AreInternalsWorking(component); + return Resolve(uid, ref component, logMissing: false) + && AreInternalsWorking(component); } public bool AreInternalsWorking(InternalsComponent component) { - return TryComp(component.BreathToolEntity, out BreathToolComponent? breathTool) && - breathTool.IsFunctional && - TryComp(component.GasTankEntity, out GasTankComponent? _); + return TryComp(component.BreathToolEntity, out BreathToolComponent? breathTool) + && breathTool.IsFunctional + && HasComp(component.GasTankEntity); } private short GetSeverity(InternalsComponent component) { - if (component.BreathToolEntity == null || !AreInternalsWorking(component)) + if (component.BreathToolEntity is null || !AreInternalsWorking(component)) return 2; // If pressure in the tank is below low pressure threshhold, flash warning on internals UI - if (TryComp(component.GasTankEntity, out var gasTank) && gasTank.IsLowPressure) + if (TryComp(component.GasTankEntity, out var gasTank) + && gasTank.IsLowPressure) + { return 0; + } return 1; } - public Entity? FindBestGasTank(Entity user) + public Entity? FindBestGasTank( + Entity user) { // Prioritise // 1. back equipped tanks @@ -234,17 +237,17 @@ private short GetSeverity(InternalsComponent component) // 3. in-hand tanks // 4. pocket/belt tanks - if (!Resolve(user.Owner, ref user.Comp1, ref user.Comp2, ref user.Comp3)) + if (!Resolve(user, ref user.Comp1, ref user.Comp2, ref user.Comp3)) return null; - if (_inventory.TryGetSlotEntity(user.Owner, "back", out var backEntity, user.Comp2, user.Comp3) && + if (_inventory.TryGetSlotEntity(user, "back", out var backEntity, user.Comp2, user.Comp3) && TryComp(backEntity, out var backGasTank) && _gasTank.CanConnectToInternals(backGasTank)) { return (backEntity.Value, backGasTank); } - if (_inventory.TryGetSlotEntity(user.Owner, "suitstorage", out var entity, user.Comp2, user.Comp3) && + if (_inventory.TryGetSlotEntity(user, "suitstorage", out var entity, user.Comp2, user.Comp3) && TryComp(entity, out var gasTank) && _gasTank.CanConnectToInternals(gasTank)) { diff --git a/Content.Server/Body/Systems/LungSystem.cs b/Content.Server/Body/Systems/LungSystem.cs index 4b60f8814b..e83d3c32a2 100644 --- a/Content.Server/Body/Systems/LungSystem.cs +++ b/Content.Server/Body/Systems/LungSystem.cs @@ -1,4 +1,4 @@ -using Content.Server.Atmos.Components; +using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Components; using Content.Server.Chemistry.Containers.EntitySystems; @@ -26,21 +26,24 @@ public override void Initialize() SubscribeLocalEvent(OnMaskToggled); } - private void OnGotUnequipped(EntityUid uid, BreathToolComponent component, GotUnequippedEvent args) + private void OnGotUnequipped(Entity ent, ref GotUnequippedEvent args) { - _atmosphereSystem.DisconnectInternals(component); + _atmosphereSystem.DisconnectInternals(ent); } - private void OnGotEquipped(EntityUid uid, BreathToolComponent component, GotEquippedEvent args) + private void OnGotEquipped(Entity ent, ref GotEquippedEvent args) { + if ((args.SlotFlags & ent.Comp.AllowedSlots) == 0) + { + return; + } - if ((args.SlotFlags & component.AllowedSlots) == 0) return; - component.IsFunctional = true; + ent.Comp.IsFunctional = true; if (TryComp(args.Equipee, out InternalsComponent? internals)) { - component.ConnectedInternalsEntity = args.Equipee; - _internals.ConnectBreathTool((args.Equipee, internals), uid); + ent.Comp.ConnectedInternalsEntity = args.Equipee; + _internals.ConnectBreathTool((args.Equipee, internals), ent); } } @@ -81,7 +84,7 @@ public void GasToReagent(EntityUid uid, LungComponent lung) if (moles <= 0) continue; var reagent = _atmosphereSystem.GasReagents[i]; - if (reagent == null) continue; + if (reagent is null) continue; var amount = moles * Atmospherics.BreathMolesToReagentMultiplier; solution.AddReagent(reagent, amount); diff --git a/Content.Server/Body/Systems/MetabolizerSystem.cs b/Content.Server/Body/Systems/MetabolizerSystem.cs index e5f604f70d..45cba5a195 100644 --- a/Content.Server/Body/Systems/MetabolizerSystem.cs +++ b/Content.Server/Body/Systems/MetabolizerSystem.cs @@ -12,11 +12,13 @@ using Robust.Shared.Collections; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Timing; namespace Content.Server.Body.Systems { public sealed class MetabolizerSystem : EntitySystem { + [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; @@ -34,9 +36,21 @@ public override void Initialize() _solutionQuery = GetEntityQuery(); SubscribeLocalEvent(OnMetabolizerInit); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnUnpaused); SubscribeLocalEvent(OnApplyMetabolicMultiplier); } + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval; + } + + private void OnUnpaused(Entity ent, ref EntityUnpausedEvent args) + { + ent.Comp.NextUpdate += args.PausedTime; + } + private void OnMetabolizerInit(Entity entity, ref ComponentInit args) { if (!entity.Comp.SolutionOnBody) @@ -49,19 +63,17 @@ private void OnMetabolizerInit(Entity entity, ref Componen } } - private void OnApplyMetabolicMultiplier(EntityUid uid, MetabolizerComponent component, - ApplyMetabolicMultiplierEvent args) + private void OnApplyMetabolicMultiplier( + Entity ent, + ref ApplyMetabolicMultiplierEvent args) { if (args.Apply) { - component.UpdateFrequency *= args.Multiplier; + ent.Comp.UpdateInterval *= args.Multiplier; return; } - component.UpdateFrequency /= args.Multiplier; - // Reset the accumulator properly - if (component.AccumulatedFrametime >= component.UpdateFrequency) - component.AccumulatedFrametime = component.UpdateFrequency; + ent.Comp.UpdateInterval /= args.Multiplier; } public override void Update(float frameTime) @@ -78,50 +90,52 @@ public override void Update(float frameTime) foreach (var (uid, metab) in metabolizers) { - metab.AccumulatedFrametime += frameTime; - // Only update as frequently as it should - if (metab.AccumulatedFrametime < metab.UpdateFrequency) + if (_gameTiming.CurTime < metab.NextUpdate) continue; - metab.AccumulatedFrametime -= metab.UpdateFrequency; - TryMetabolize(uid, metab); + metab.NextUpdate += metab.UpdateInterval; + TryMetabolize((uid, metab)); } } - private void TryMetabolize(EntityUid uid, MetabolizerComponent meta, OrganComponent? organ = null) + private void TryMetabolize(Entity ent) { - _organQuery.Resolve(uid, ref organ, false); + _organQuery.Resolve(ent, ref ent.Comp2, logMissing: false); // First step is get the solution we actually care about + var solutionName = ent.Comp1.SolutionName; Solution? solution = null; Entity? soln = default!; EntityUid? solutionEntityUid = null; - SolutionContainerManagerComponent? manager = null; - - if (meta.SolutionOnBody) + if (ent.Comp1.SolutionOnBody) { - if (organ?.Body is { } body) + if (ent.Comp2?.Body is { } body) { - if (!_solutionQuery.Resolve(body, ref manager, false)) + if (!_solutionQuery.Resolve(body, ref ent.Comp3, logMissing: false)) return; - _solutionContainerSystem.TryGetSolution((body, manager), meta.SolutionName, out soln, out solution); + _solutionContainerSystem.TryGetSolution((body, ent.Comp3), solutionName, out soln, out solution); solutionEntityUid = body; } } else { - if (!_solutionQuery.Resolve(uid, ref manager, false)) + if (!_solutionQuery.Resolve(ent, ref ent.Comp3, logMissing: false)) return; - _solutionContainerSystem.TryGetSolution((uid, manager), meta.SolutionName, out soln, out solution); - solutionEntityUid = uid; + _solutionContainerSystem.TryGetSolution((ent, ent), solutionName, out soln, out solution); + solutionEntityUid = ent; } - if (solutionEntityUid == null || soln is null || solution is null || solution.Contents.Count == 0) + if (solutionEntityUid is null + || soln is null + || solution is null + || solution.Contents.Count == 0) + { return; + } // randomize the reagent list so we don't have any weird quirks // like alphabetical order or insertion order mattering for processing @@ -135,9 +149,9 @@ private void TryMetabolize(EntityUid uid, MetabolizerComponent meta, OrganCompon continue; var mostToRemove = FixedPoint2.Zero; - if (proto.Metabolisms == null) + if (proto.Metabolisms is null) { - if (meta.RemoveEmpty) + if (ent.Comp1.RemoveEmpty) { solution.RemoveReagent(reagent, FixedPoint2.New(1)); } @@ -146,15 +160,15 @@ private void TryMetabolize(EntityUid uid, MetabolizerComponent meta, OrganCompon } // we're done here entirely if this is true - if (reagents >= meta.MaxReagentsProcessable) + if (reagents >= ent.Comp1.MaxReagentsProcessable) return; // loop over all our groups and see which ones apply - if (meta.MetabolismGroups == null) + if (ent.Comp1.MetabolismGroups is null) continue; - foreach (var group in meta.MetabolismGroups) + foreach (var group in ent.Comp1.MetabolismGroups) { if (!proto.Metabolisms.TryGetValue(group.Id, out var entry)) continue; @@ -169,14 +183,14 @@ private void TryMetabolize(EntityUid uid, MetabolizerComponent meta, OrganCompon // if it's possible for them to be dead, and they are, // then we shouldn't process any effects, but should probably // still remove reagents - if (EntityManager.TryGetComponent(solutionEntityUid.Value, out var state)) + if (TryComp(solutionEntityUid.Value, out var state)) { if (!proto.WorksOnTheDead && _mobStateSystem.IsDead(solutionEntityUid.Value, state)) continue; } - var actualEntity = organ?.Body ?? solutionEntityUid.Value; - var args = new ReagentEffectArgs(actualEntity, uid, solution, proto, mostToRemove, + var actualEntity = ent.Comp2?.Body ?? solutionEntityUid.Value; + var args = new ReagentEffectArgs(actualEntity, ent, solution, proto, mostToRemove, EntityManager, null, scale); // do all effects, if conditions apply @@ -187,8 +201,14 @@ private void TryMetabolize(EntityUid uid, MetabolizerComponent meta, OrganCompon if (effect.ShouldLog) { - _adminLogger.Add(LogType.ReagentEffect, effect.LogImpact, - $"Metabolism effect {effect.GetType().Name:effect} of reagent {proto.LocalizedName:reagent} applied on entity {actualEntity:entity} at {Transform(actualEntity).Coordinates:coordinates}"); + _adminLogger.Add( + LogType.ReagentEffect, + effect.LogImpact, + $"Metabolism effect {effect.GetType().Name:effect}" + + $" of reagent {proto.LocalizedName:reagent}" + + $" applied on entity {actualEntity:entity}" + + $" at {Transform(actualEntity).Coordinates:coordinates}" + ); } effect.Effect(args); @@ -209,15 +229,25 @@ private void TryMetabolize(EntityUid uid, MetabolizerComponent meta, OrganCompon } } - public sealed class ApplyMetabolicMultiplierEvent : EntityEventArgs + [ByRefEvent] + public readonly record struct ApplyMetabolicMultiplierEvent( + EntityUid Uid, + float Multiplier, + bool Apply) { - // The entity whose metabolism is being modified - public EntityUid Uid; - - // What the metabolism's update rate will be multiplied by - public float Multiplier; - - // Apply this multiplier or ignore / reset it? - public bool Apply; + /// + /// The entity whose metabolism is being modified. + /// + public readonly EntityUid Uid = Uid; + + /// + /// What the metabolism's update rate will be multiplied by. + /// + public readonly float Multiplier = Multiplier; + + /// + /// If true, apply the multiplier. If false, revert it. + /// + public readonly bool Apply = Apply; } } diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs index 0fd61a9cb7..c7266e2c46 100644 --- a/Content.Server/Body/Systems/RespiratorSystem.cs +++ b/Content.Server/Body/Systems/RespiratorSystem.cs @@ -35,9 +35,21 @@ public override void Initialize() // We want to process lung reagents before we inhale new reagents. UpdatesAfter.Add(typeof(MetabolizerSystem)); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnUnpaused); SubscribeLocalEvent(OnApplyMetabolicMultiplier); } + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval; + } + + private void OnUnpaused(Entity ent, ref EntityUnpausedEvent args) + { + ent.Comp.NextUpdate += args.PausedTime; + } + public override void Update(float frameTime) { base.Update(frameTime); @@ -45,17 +57,15 @@ public override void Update(float frameTime) var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var respirator, out var body)) { - if (_mobState.IsDead(uid)) - { + if (_gameTiming.CurTime < respirator.NextUpdate) continue; - } - respirator.AccumulatedFrametime += frameTime; + respirator.NextUpdate += respirator.UpdateInterval; - if (respirator.AccumulatedFrametime < respirator.CycleDelay) + if (_mobState.IsDead(uid)) continue; - respirator.AccumulatedFrametime -= respirator.CycleDelay; - UpdateSaturation(uid, -respirator.CycleDelay, respirator); + + UpdateSaturation(uid, -(float) respirator.UpdateInterval.TotalSeconds, respirator); if (!_mobState.IsIncapacitated(uid)) // cannot breathe in crit. { @@ -80,30 +90,30 @@ public override void Update(float frameTime) _popupSystem.PopupEntity(Loc.GetString("lung-behavior-gasp"), uid); } - TakeSuffocationDamage(uid, respirator); + TakeSuffocationDamage((uid, respirator)); respirator.SuffocationCycles += 1; continue; } - StopSuffocation(uid, respirator); + StopSuffocation((uid, respirator)); respirator.SuffocationCycles = 0; } } public void Inhale(EntityUid uid, BodyComponent? body = null) { - if (!Resolve(uid, ref body, false)) + if (!Resolve(uid, ref body, logMissing: false)) return; var organs = _bodySystem.GetBodyOrganComponents(uid, body); // Inhale gas var ev = new InhaleLocationEvent(); - RaiseLocalEvent(uid, ev); + RaiseLocalEvent(uid, ref ev, broadcast: false); - ev.Gas ??= _atmosSys.GetContainingMixture(uid, false, true); + ev.Gas ??= _atmosSys.GetContainingMixture(uid, excite: true); - if (ev.Gas == null) + if (ev.Gas is null) { return; } @@ -122,7 +132,7 @@ public void Inhale(EntityUid uid, BodyComponent? body = null) public void Exhale(EntityUid uid, BodyComponent? body = null) { - if (!Resolve(uid, ref body, false)) + if (!Resolve(uid, ref body, logMissing: false)) return; var organs = _bodySystem.GetBodyOrganComponents(uid, body); @@ -130,11 +140,11 @@ public void Exhale(EntityUid uid, BodyComponent? body = null) // exhale gas var ev = new ExhaleLocationEvent(); - RaiseLocalEvent(uid, ev, false); + RaiseLocalEvent(uid, ref ev, broadcast: false); - if (ev.Gas == null) + if (ev.Gas is null) { - ev.Gas = _atmosSys.GetContainingMixture(uid, false, true); + ev.Gas = _atmosSys.GetContainingMixture(uid, excite: true); // Walls and grids without atmos comp return null. I guess it makes sense to not be able to exhale in walls, // but this also means you cannot exhale on some grids. @@ -154,37 +164,37 @@ public void Exhale(EntityUid uid, BodyComponent? body = null) _atmosSys.Merge(ev.Gas, outGas); } - private void TakeSuffocationDamage(EntityUid uid, RespiratorComponent respirator) + private void TakeSuffocationDamage(Entity ent) { - if (respirator.SuffocationCycles == 2) - _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(uid):entity} started suffocating"); + if (ent.Comp.SuffocationCycles == 2) + _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} started suffocating"); - if (respirator.SuffocationCycles >= respirator.SuffocationCycleThreshold) + if (ent.Comp.SuffocationCycles >= ent.Comp.SuffocationCycleThreshold) { // TODO: This is not going work with multiple different lungs, if that ever becomes a possibility - var organs = _bodySystem.GetBodyOrganComponents(uid); + var organs = _bodySystem.GetBodyOrganComponents(ent); foreach (var (comp, _) in organs) { - _alertsSystem.ShowAlert(uid, comp.Alert); + _alertsSystem.ShowAlert(ent, comp.Alert); } } - _damageableSys.TryChangeDamage(uid, respirator.Damage, false, false); + _damageableSys.TryChangeDamage(ent, ent.Comp.Damage, interruptsDoAfters: false); } - private void StopSuffocation(EntityUid uid, RespiratorComponent respirator) + private void StopSuffocation(Entity ent) { - if (respirator.SuffocationCycles >= 2) - _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(uid):entity} stopped suffocating"); + if (ent.Comp.SuffocationCycles >= 2) + _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} stopped suffocating"); // TODO: This is not going work with multiple different lungs, if that ever becomes a possibility - var organs = _bodySystem.GetBodyOrganComponents(uid); + var organs = _bodySystem.GetBodyOrganComponents(ent); foreach (var (comp, _) in organs) { - _alertsSystem.ClearAlert(uid, comp.Alert); + _alertsSystem.ClearAlert(ent, comp.Alert); } - _damageableSys.TryChangeDamage(uid, respirator.DamageRecovery); + _damageableSys.TryChangeDamage(ent, ent.Comp.DamageRecovery); } public void UpdateSaturation(EntityUid uid, float amount, @@ -198,35 +208,29 @@ public void UpdateSaturation(EntityUid uid, float amount, Math.Clamp(respirator.Saturation, respirator.MinSaturation, respirator.MaxSaturation); } - private void OnApplyMetabolicMultiplier(EntityUid uid, RespiratorComponent component, - ApplyMetabolicMultiplierEvent args) + private void OnApplyMetabolicMultiplier( + Entity ent, + ref ApplyMetabolicMultiplierEvent args) { if (args.Apply) { - component.CycleDelay *= args.Multiplier; - component.Saturation *= args.Multiplier; - component.MaxSaturation *= args.Multiplier; - component.MinSaturation *= args.Multiplier; + ent.Comp.UpdateInterval *= args.Multiplier; + ent.Comp.Saturation *= args.Multiplier; + ent.Comp.MaxSaturation *= args.Multiplier; + ent.Comp.MinSaturation *= args.Multiplier; return; } // This way we don't have to worry about it breaking if the stasis bed component is destroyed - component.CycleDelay /= args.Multiplier; - component.Saturation /= args.Multiplier; - component.MaxSaturation /= args.Multiplier; - component.MinSaturation /= args.Multiplier; - // Reset the accumulator properly - if (component.AccumulatedFrametime >= component.CycleDelay) - component.AccumulatedFrametime = component.CycleDelay; + ent.Comp.UpdateInterval /= args.Multiplier; + ent.Comp.Saturation /= args.Multiplier; + ent.Comp.MaxSaturation /= args.Multiplier; + ent.Comp.MinSaturation /= args.Multiplier; } } -public sealed class InhaleLocationEvent : EntityEventArgs -{ - public GasMixture? Gas; -} +[ByRefEvent] +public record struct InhaleLocationEvent(GasMixture? Gas); -public sealed class ExhaleLocationEvent : EntityEventArgs -{ - public GasMixture? Gas; -} +[ByRefEvent] +public record struct ExhaleLocationEvent(GasMixture? Gas); diff --git a/Content.Server/Body/Systems/StomachSystem.cs b/Content.Server/Body/Systems/StomachSystem.cs index 4c11244c37..a4c2e8292d 100644 --- a/Content.Server/Body/Systems/StomachSystem.cs +++ b/Content.Server/Body/Systems/StomachSystem.cs @@ -3,32 +3,44 @@ using Content.Shared.Body.Organ; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; +using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Server.Body.Systems { public sealed class StomachSystem : EntitySystem { + [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; public const string DefaultSolutionName = "stomach"; public override void Initialize() { + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnUnpaused); SubscribeLocalEvent(OnApplyMetabolicMultiplier); } + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval; + } + + private void OnUnpaused(Entity ent, ref EntityUnpausedEvent args) + { + ent.Comp.NextUpdate += args.PausedTime; + } + public override void Update(float frameTime) { var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var stomach, out var organ, out var sol)) { - stomach.AccumulatedFrameTime += frameTime; - - if (stomach.AccumulatedFrameTime < stomach.UpdateInterval) + if (_gameTiming.CurTime < stomach.NextUpdate) continue; - stomach.AccumulatedFrameTime -= stomach.UpdateInterval; + stomach.NextUpdate += stomach.UpdateInterval; // Get our solutions if (!_solutionContainerSystem.ResolveSolution((uid, sol), DefaultSolutionName, ref stomach.Solution, out var stomachSolution)) @@ -70,49 +82,44 @@ public override void Update(float frameTime) } } - private void OnApplyMetabolicMultiplier(EntityUid uid, StomachComponent component, - ApplyMetabolicMultiplierEvent args) + private void OnApplyMetabolicMultiplier( + Entity ent, + ref ApplyMetabolicMultiplierEvent args) { if (args.Apply) { - component.UpdateInterval *= args.Multiplier; + ent.Comp.UpdateInterval *= args.Multiplier; return; } // This way we don't have to worry about it breaking if the stasis bed component is destroyed - component.UpdateInterval /= args.Multiplier; - // Reset the accumulator properly - if (component.AccumulatedFrameTime >= component.UpdateInterval) - component.AccumulatedFrameTime = component.UpdateInterval; + ent.Comp.UpdateInterval /= args.Multiplier; } - public bool CanTransferSolution(EntityUid uid, Solution solution, + public bool CanTransferSolution( + EntityUid uid, + Solution solution, StomachComponent? stomach = null, SolutionContainerManagerComponent? solutions = null) { - if (!Resolve(uid, ref stomach, ref solutions, false)) - return false; - - if (!_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution, out var stomachSolution)) - return false; - - // TODO: For now no partial transfers. Potentially change by design - if (!stomachSolution.CanAddSolution(solution)) - return false; - - return true; + return Resolve(uid, ref stomach, ref solutions, logMissing: false) + && _solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution, out var stomachSolution) + // TODO: For now no partial transfers. Potentially change by design + && stomachSolution.CanAddSolution(solution); } - public bool TryTransferSolution(EntityUid uid, Solution solution, + public bool TryTransferSolution( + EntityUid uid, + Solution solution, StomachComponent? stomach = null, SolutionContainerManagerComponent? solutions = null) { - if (!Resolve(uid, ref stomach, ref solutions, false)) - return false; - - if (!_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution) + if (!Resolve(uid, ref stomach, ref solutions, logMissing: false) + || !_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution) || !CanTransferSolution(uid, solution, stomach, solutions)) + { return false; + } _solutionContainerSystem.TryAddSolution(stomach.Solution.Value, solution); // Add each reagent to ReagentDeltas. Used to track how long each reagent has been in the stomach diff --git a/Content.Server/Body/Systems/ThermalRegulatorSystem.cs b/Content.Server/Body/Systems/ThermalRegulatorSystem.cs index a9556be773..a8bf4184ac 100644 --- a/Content.Server/Body/Systems/ThermalRegulatorSystem.cs +++ b/Content.Server/Body/Systems/ThermalRegulatorSystem.cs @@ -1,73 +1,95 @@ -using Content.Server.Body.Components; +using Content.Server.Body.Components; using Content.Server.Temperature.Components; using Content.Server.Temperature.Systems; using Content.Shared.ActionBlocker; +using Robust.Shared.Timing; namespace Content.Server.Body.Systems; public sealed class ThermalRegulatorSystem : EntitySystem { + [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly TemperatureSystem _tempSys = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSys = default!; + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnUnpaused); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval; + } + + private void OnUnpaused(Entity ent, ref EntityUnpausedEvent args) + { + ent.Comp.NextUpdate += args.PausedTime; + } + public override void Update(float frameTime) { var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var regulator)) { - regulator.AccumulatedFrametime += frameTime; - if (regulator.AccumulatedFrametime < 1) + if (_gameTiming.CurTime < regulator.NextUpdate) continue; - regulator.AccumulatedFrametime -= 1; - ProcessThermalRegulation(uid, regulator); + regulator.NextUpdate += regulator.UpdateInterval; + ProcessThermalRegulation((uid, regulator)); } } /// /// Processes thermal regulation for a mob /// - private void ProcessThermalRegulation(EntityUid uid, ThermalRegulatorComponent comp) + private void ProcessThermalRegulation(Entity ent) { - if (!EntityManager.TryGetComponent(uid, out TemperatureComponent? temperatureComponent)) return; + if (!Resolve(ent, ref ent.Comp2, logMissing: false)) + return; - var totalMetabolismTempChange = comp.MetabolismHeat - comp.RadiatedHeat; + var totalMetabolismTempChange = ent.Comp1.MetabolismHeat - ent.Comp1.RadiatedHeat; // implicit heat regulation - var tempDiff = Math.Abs(temperatureComponent.CurrentTemperature - comp.NormalBodyTemperature); - var heatCapacity = _tempSys.GetHeatCapacity(uid, temperatureComponent); + var tempDiff = Math.Abs(ent.Comp2.CurrentTemperature - ent.Comp1.NormalBodyTemperature); + var heatCapacity = _tempSys.GetHeatCapacity(ent, ent); var targetHeat = tempDiff * heatCapacity; - if (temperatureComponent.CurrentTemperature > comp.NormalBodyTemperature) + if (ent.Comp2.CurrentTemperature > ent.Comp1.NormalBodyTemperature) { - totalMetabolismTempChange -= Math.Min(targetHeat, comp.ImplicitHeatRegulation); + totalMetabolismTempChange -= Math.Min(targetHeat, ent.Comp1.ImplicitHeatRegulation); } else { - totalMetabolismTempChange += Math.Min(targetHeat, comp.ImplicitHeatRegulation); + totalMetabolismTempChange += Math.Min(targetHeat, ent.Comp1.ImplicitHeatRegulation); } - _tempSys.ChangeHeat(uid, totalMetabolismTempChange, true, temperatureComponent); + _tempSys.ChangeHeat(ent, totalMetabolismTempChange, ignoreHeatResistance: true, ent); // recalc difference and target heat - tempDiff = Math.Abs(temperatureComponent.CurrentTemperature - comp.NormalBodyTemperature); + tempDiff = Math.Abs(ent.Comp2.CurrentTemperature - ent.Comp1.NormalBodyTemperature); targetHeat = tempDiff * heatCapacity; // if body temperature is not within comfortable, thermal regulation // processes starts - if (tempDiff > comp.ThermalRegulationTemperatureThreshold) + if (tempDiff > ent.Comp1.ThermalRegulationTemperatureThreshold) return; - if (temperatureComponent.CurrentTemperature > comp.NormalBodyTemperature) + if (ent.Comp2.CurrentTemperature > ent.Comp1.NormalBodyTemperature) { - if (!_actionBlockerSys.CanSweat(uid)) return; - _tempSys.ChangeHeat(uid, -Math.Min(targetHeat, comp.SweatHeatRegulation), true, - temperatureComponent); + if (!_actionBlockerSys.CanSweat(ent)) + return; + + _tempSys.ChangeHeat(ent, -Math.Min(targetHeat, ent.Comp1.SweatHeatRegulation), ignoreHeatResistance: true, ent); } else { - if (!_actionBlockerSys.CanShiver(uid)) return; - _tempSys.ChangeHeat(uid, Math.Min(targetHeat, comp.ShiveringHeatRegulation), true, - temperatureComponent); + if (!_actionBlockerSys.CanShiver(ent)) + return; + + _tempSys.ChangeHeat(ent, Math.Min(targetHeat, ent.Comp1.ShiveringHeatRegulation), ignoreHeatResistance: true, ent); } } } diff --git a/Content.Server/Cargo/Components/StationCargoBountyDatabaseComponent.cs b/Content.Server/Cargo/Components/StationCargoBountyDatabaseComponent.cs index 48c58321b3..d26794a632 100644 --- a/Content.Server/Cargo/Components/StationCargoBountyDatabaseComponent.cs +++ b/Content.Server/Cargo/Components/StationCargoBountyDatabaseComponent.cs @@ -12,7 +12,7 @@ public sealed partial class StationCargoBountyDatabaseComponent : Component /// Maximum amount of bounties a station can have. /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public int MaxBounties = 5; + public int MaxBounties = 6; /// /// A list of all the bounties currently active for a station. diff --git a/Content.Server/Cargo/Systems/CargoSystem.cs b/Content.Server/Cargo/Systems/CargoSystem.cs index 2609d06b55..3d6fc96472 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.cs @@ -39,7 +39,6 @@ public sealed partial class CargoSystem : SharedCargoSystem [Dependency] private readonly PricingSystem _pricing = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedTransformSystem _xformSystem = default!; [Dependency] private readonly ShuttleConsoleSystem _console = default!; [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly StationSystem _station = default!; diff --git a/Content.Server/Cargo/Systems/PricingSystem.cs b/Content.Server/Cargo/Systems/PricingSystem.cs index 6fb36c9608..9e1970d63c 100644 --- a/Content.Server/Cargo/Systems/PricingSystem.cs +++ b/Content.Server/Cargo/Systems/PricingSystem.cs @@ -12,7 +12,6 @@ using Content.Shared.Stacks; using Robust.Shared.Console; using Robust.Shared.Containers; -using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Prototypes; using Robust.Shared.Utility; @@ -27,7 +26,6 @@ public sealed class PricingSystem : EntitySystem { [Dependency] private readonly IComponentFactory _factory = default!; [Dependency] private readonly IConsoleHost _consoleHost = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly BodySystem _bodySystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; diff --git a/Content.Server/Chat/Managers/ChatManager.cs b/Content.Server/Chat/Managers/ChatManager.cs index 6d54bedf86..d12bbfe53c 100644 --- a/Content.Server/Chat/Managers/ChatManager.cs +++ b/Content.Server/Chat/Managers/ChatManager.cs @@ -125,9 +125,23 @@ public void DispatchServerMessage(ICommonSession player, string message, bool su _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Server message to {player:Player}: {message}"); } - public void SendAdminAnnouncement(string message) + public void SendAdminAnnouncement(string message, AdminFlags? flagBlacklist, AdminFlags? flagWhitelist) { - var clients = _adminManager.ActiveAdmins.Select(p => p.Channel); + var clients = _adminManager.ActiveAdmins.Where(p => + { + var adminData = _adminManager.GetAdminData(p); + + DebugTools.AssertNotNull(adminData); + + if (adminData == null) + return false; + + if (flagBlacklist != null && adminData.HasFlag(flagBlacklist.Value)) + return false; + + return flagWhitelist == null || adminData.HasFlag(flagWhitelist.Value); + + }).Select(p => p.Channel); var wrappedMessage = Loc.GetString("chat-manager-send-admin-announcement-wrap-message", ("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")), ("message", FormattedMessage.EscapeText(message))); diff --git a/Content.Server/Chat/Managers/IChatManager.cs b/Content.Server/Chat/Managers/IChatManager.cs index e5fa8d5f4d..59945bf5ca 100644 --- a/Content.Server/Chat/Managers/IChatManager.cs +++ b/Content.Server/Chat/Managers/IChatManager.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using Content.Shared.Administration; using Content.Shared.Chat; using Robust.Shared.Network; using Robust.Shared.Player; @@ -21,7 +22,7 @@ public interface IChatManager void TrySendOOCMessage(ICommonSession player, string message, OOCChatType type); void SendHookOOC(string sender, string message); - void SendAdminAnnouncement(string message); + void SendAdminAnnouncement(string message, AdminFlags? flagBlacklist = null, AdminFlags? flagWhitelist = null); void SendAdminAlert(string message); void SendAdminAlert(EntityUid player, string message); diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index f69c95dfcb..3503ef84ca 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -423,11 +423,11 @@ private void SendEntitySpeak( name = FormattedMessage.EscapeText(name); // The chat message wrapped in a "x says y" string - var wrappedMessage = WrapPublicMessage(source, name, message); + var wrappedMessage = WrapPublicMessage(source, name, message, languageOverride: language); // The chat message obfuscated via language obfuscation var obfuscated = SanitizeInGameICMessage(source, _language.ObfuscateSpeech(message, language), out var emoteStr, true, _configurationManager.GetCVar(CCVars.ChatPunctuation), (!CultureInfo.CurrentCulture.IsNeutralCulture && CultureInfo.CurrentCulture.Parent.Name == "en") || (CultureInfo.CurrentCulture.IsNeutralCulture && CultureInfo.CurrentCulture.Name == "en")); // The language-obfuscated message wrapped in a "x says y" string - var wrappedObfuscated = WrapPublicMessage(source, name, obfuscated); + var wrappedObfuscated = WrapPublicMessage(source, name, obfuscated, languageOverride: language); SendInVoiceRange(ChatChannel.Local, name, message, wrappedMessage, obfuscated, wrappedObfuscated, source, range, languageOverride: language); @@ -514,6 +514,7 @@ private void SendEntityWhisper( // Scenario 1: the listener can clearly understand the message result = perceivedMessage; wrappedMessage = Loc.GetString("chat-manager-entity-whisper-wrap-message", + ("color", language.Color ?? Color.Gray), ("entityName", name), ("message", FormattedMessage.EscapeText(result))); } @@ -523,13 +524,14 @@ private void SendEntityWhisper( // Collisiongroup.Opaque is not ideal for this use. Preferably, there should be a check specifically with "Can Ent1 see Ent2" in mind result = ObfuscateMessageReadability(perceivedMessage); wrappedMessage = Loc.GetString("chat-manager-entity-whisper-wrap-message", - ("entityName", nameIdentity), ("message", FormattedMessage.EscapeText(result))); + ("entityName", nameIdentity), ("color", language.Color ?? Color.Gray), ("message", FormattedMessage.EscapeText(result))); } else { // Scenario 3: If listener is too far and has no line of sight, they can't identify the whisperer's identity result = ObfuscateMessageReadability(perceivedMessage); wrappedMessage = Loc.GetString("chat-manager-entity-whisper-unknown-wrap-message", + ("color", language.Color ?? Color.Gray), ("message", FormattedMessage.EscapeText(result))); } @@ -537,6 +539,7 @@ private void SendEntityWhisper( } var replayWrap = Loc.GetString("chat-manager-entity-whisper-wrap-message", + ("color", language.Color ?? Color.Gray), ("entityName", name), ("message", FormattedMessage.EscapeText(message))); _replay.RecordServerMessage(new ChatMessage(ChatChannel.Whisper, message, replayWrap, GetNetEntity(source), null, MessageRangeHideChatForReplay(range))); @@ -837,15 +840,16 @@ public string SanitizeMessageReplaceWords(string message) /// /// Wraps a message sent by the specified entity into an "x says y" string. /// - public string WrapPublicMessage(EntityUid source, string name, string message) + public string WrapPublicMessage(EntityUid source, string name, string message, LanguagePrototype? languageOverride = null) { + var language = languageOverride ?? _language.GetLanguage(source); var speech = GetSpeechVerb(source, message); - var verbName = Loc.GetString(_random.Pick(speech.SpeechVerbStrings)); return Loc.GetString(speech.Bold ? "chat-manager-entity-say-bold-wrap-message" : "chat-manager-entity-say-wrap-message", + ("color", language.Color ?? Color.White), ("entityName", name), - ("verb", verbName), - ("fontType", speech.FontId), - ("fontSize", speech.FontSize), + ("verb", Loc.GetString(_random.Pick(speech.SpeechVerbStrings))), + ("fontType", language.FontId ?? speech.FontId), + ("fontSize", language.FontSize ?? speech.FontSize), ("message", message)); } diff --git a/Content.Server/Chat/TypingIndicator/TypingIndicatorSystem.cs b/Content.Server/Chat/TypingIndicator/TypingIndicatorSystem.cs index 443923f675..c923738930 100644 --- a/Content.Server/Chat/TypingIndicator/TypingIndicatorSystem.cs +++ b/Content.Server/Chat/TypingIndicator/TypingIndicatorSystem.cs @@ -54,7 +54,7 @@ private void OnClientTypingChanged(TypingChangedEvent ev, EntitySessionEventArgs SetTypingIndicatorEnabled(uid.Value, ev.IsTyping); } - public void SetTypingIndicatorEnabled(EntityUid uid, bool isEnabled, AppearanceComponent? appearance = null) + private void SetTypingIndicatorEnabled(EntityUid uid, bool isEnabled, AppearanceComponent? appearance = null) { if (!Resolve(uid, ref appearance, false)) return; diff --git a/Content.Server/Chemistry/Containers/EntitySystems/SolutionContainerSystem.cs b/Content.Server/Chemistry/Containers/EntitySystems/SolutionContainerSystem.cs index 7926121c2b..468212f5ea 100644 --- a/Content.Server/Chemistry/Containers/EntitySystems/SolutionContainerSystem.cs +++ b/Content.Server/Chemistry/Containers/EntitySystems/SolutionContainerSystem.cs @@ -4,7 +4,6 @@ using Content.Shared.FixedPoint; using Robust.Shared.Containers; using Robust.Shared.Map; -using Robust.Shared.Network; using Robust.Shared.Utility; using System.Numerics; @@ -12,8 +11,6 @@ namespace Content.Server.Chemistry.Containers.EntitySystems; public sealed partial class SolutionContainerSystem : SharedSolutionContainerSystem { - [Dependency] private readonly INetManager _netManager = default!; - public override void Initialize() { base.Initialize(); diff --git a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs index b93498fe31..a8583e6bcb 100644 --- a/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ReagentDispenserSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.Administration.Logs; using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Nutrition.EntitySystems; @@ -30,7 +29,6 @@ public sealed class ReagentDispenserSystem : EntitySystem [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly OpenableSystem _openable = default!; public override void Initialize() diff --git a/Content.Server/Decals/Commands/EditDecalCommand.cs b/Content.Server/Decals/Commands/EditDecalCommand.cs index baaef1f3f6..2ae814113f 100644 --- a/Content.Server/Decals/Commands/EditDecalCommand.cs +++ b/Content.Server/Decals/Commands/EditDecalCommand.cs @@ -3,6 +3,7 @@ using Content.Shared.Administration; using Robust.Shared.Console; using Robust.Shared.Map; +using Robust.Shared.Map.Components; namespace Content.Server.Decals; @@ -10,7 +11,6 @@ namespace Content.Server.Decals; public sealed class EditDecalCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; public string Command => "editdecal"; public string Description => "Edits a decal."; @@ -43,7 +43,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - if (!_mapManager.GridExists(gridId)) + if (!_entManager.HasComponent(gridId)) { shell.WriteError($"No grid with gridId {gridId} exists."); return; diff --git a/Content.Server/Decals/Commands/RemoveDecalCommand.cs b/Content.Server/Decals/Commands/RemoveDecalCommand.cs index 771c66fbbd..6b6a91c9d3 100644 --- a/Content.Server/Decals/Commands/RemoveDecalCommand.cs +++ b/Content.Server/Decals/Commands/RemoveDecalCommand.cs @@ -2,6 +2,7 @@ using Content.Shared.Administration; using Robust.Shared.Console; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using SQLitePCL; namespace Content.Server.Decals.Commands @@ -10,7 +11,6 @@ namespace Content.Server.Decals.Commands public sealed class RemoveDecalCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; public string Command => "rmdecal"; public string Description => "removes a decal"; @@ -31,7 +31,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) if (!NetEntity.TryParse(args[1], out var rawGridIdNet) || !_entManager.TryGetEntity(rawGridIdNet, out var rawGridId) || - !_mapManager.GridExists(rawGridId)) + !_entManager.HasComponent(rawGridId)) { shell.WriteError("Failed parsing gridId."); return; diff --git a/Content.Server/Decals/DecalSystem.cs b/Content.Server/Decals/DecalSystem.cs index ad225afe22..da95401d20 100644 --- a/Content.Server/Decals/DecalSystem.cs +++ b/Content.Server/Decals/DecalSystem.cs @@ -105,7 +105,7 @@ private void OnGridSplit(ref PostGridSplitEvent ev) return; // Transfer decals over to the new grid. - var enumerator = MapManager.GetGrid(ev.Grid).GetAllTilesEnumerator(); + var enumerator = Comp(ev.Grid).GetAllTilesEnumerator(); var oldChunkCollection = oldComp.ChunkCollection.ChunkCollection; var chunkCollection = newComp.ChunkCollection.ChunkCollection; diff --git a/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs b/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs index f0538c47f9..ba042d8966 100644 --- a/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs +++ b/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs @@ -39,7 +39,7 @@ protected override void Started(EntityUid uid, PirateRadioSpawnRuleComponent com var xformQuery = GetEntityQuery(); var aabbs = EntityQuery().SelectMany(x => x.Grids.Select(x => - xformQuery.GetComponent(x).WorldMatrix.TransformBox(_mapManager.GetGridComp(x).LocalAABB))) + xformQuery.GetComponent(x).WorldMatrix.TransformBox(_entities.GetComponent(x).LocalAABB))) .ToArray(); if (aabbs.Length < 1) return; var aabb = aabbs[0]; diff --git a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs index 20aa8b6d2c..6c0bced53e 100644 --- a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs +++ b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs @@ -6,20 +6,16 @@ using Content.Server.Disposal.Unit.Components; using Content.Server.Disposal.Unit.EntitySystems; using Content.Server.Popups; -using Content.Server.UserInterface; using Content.Shared.Destructible; using Content.Shared.Disposal.Components; -using Content.Shared.Hands.Components; -using Content.Shared.Movement.Events; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; -using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Random; -using Robust.Shared.Timing; using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent; using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent; @@ -27,8 +23,6 @@ namespace Content.Server.Disposal.Tube { public sealed class DisposalTubeSystem : EntitySystem { - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; [Dependency] private readonly PopupSystem _popups = default!; @@ -349,7 +343,7 @@ private void UpdateAnchored(EntityUid uid, DisposalTubeComponent component, bool var oppositeDirection = nextDirection.GetOpposite(); var xform = Transform(target); - if (!_mapManager.TryGetGrid(xform.GridUid, out var grid)) + if (!TryComp(xform.GridUid, out var grid)) return null; var position = xform.Coordinates; diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs index eb3cda4db9..38e3923803 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs @@ -1,4 +1,3 @@ -using System.Linq; using Content.Server.Atmos.EntitySystems; using Content.Server.Disposal.Tube; using Content.Server.Disposal.Tube.Components; @@ -12,14 +11,12 @@ using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; -using Robust.Shared.Random; namespace Content.Server.Disposal.Unit.EntitySystems { public sealed class DisposableSystem : EntitySystem { [Dependency] private readonly ThrowingSystem _throwing = default!; - [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly DisposalUnitSystem _disposalUnitSystem = default!; diff --git a/Content.Server/Dragon/DragonSystem.cs b/Content.Server/Dragon/DragonSystem.cs index 6400472d03..79e5c0a2a9 100644 --- a/Content.Server/Dragon/DragonSystem.cs +++ b/Content.Server/Dragon/DragonSystem.cs @@ -10,18 +10,16 @@ using Content.Shared.Mind.Components; using Content.Shared.Mobs; using Content.Shared.Movement.Systems; -using Robust.Shared.Audio; +using Content.Shared.Zombies; using Robust.Shared.Audio.Systems; -using Robust.Shared.GameStates; using Robust.Shared.Map; -using Robust.Shared.Player; +using Robust.Shared.Map.Components; namespace Content.Server.Dragon; public sealed partial class DragonSystem : EntitySystem { [Dependency] private readonly CarpRiftsConditionSystem _carpRifts = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDef = default!; [Dependency] private readonly MovementSpeedModifierSystem _movement = default!; [Dependency] private readonly PopupSystem _popup = default!; @@ -138,7 +136,7 @@ private void OnSpawnRift(EntityUid uid, DragonComponent component, DragonSpawnRi var xform = Transform(uid); // Have to be on a grid fam - if (!_mapManager.TryGetGrid(xform.GridUid, out var grid)) + if (!TryComp(xform.GridUid, out var grid)) { _popup.PopupEntity(Loc.GetString("carp-rift-anchor"), uid, uid); return; diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index d410c7af96..9d8b7e416e 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -19,7 +19,6 @@ using Content.Shared.Maps; using Content.Shared.Mobs; using Content.Shared.Popups; -using Content.Shared.Pulling.Components; using Content.Shared.Speech.EntitySystems; using Content.Shared.StatusEffect; using Content.Shared.Stunnable; @@ -32,6 +31,8 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent; +using PullerComponent = Content.Shared.Movement.Pulling.Components.PullerComponent; namespace Content.Server.Electrocution; @@ -477,14 +478,14 @@ private void GetChainedElectrocutionTargetsRecurse( all.Add((entity, depth)); visited.Add(entity); - if (TryComp(entity, out var pullable) && + if (TryComp(entity, out var pullable) && pullable.Puller is { Valid: true } pullerId && !visited.Contains(pullerId)) { GetChainedElectrocutionTargetsRecurse(pullerId, depth + 1, visited, all); } - if (TryComp(entity, out var puller) && + if (TryComp(entity, out var puller) && puller.Pulling is { Valid: true } pullingId && !visited.Contains(pullingId)) { diff --git a/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs b/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs index 61c4937a27..a0bbbdf350 100644 --- a/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs +++ b/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs @@ -6,14 +6,13 @@ using Content.Shared.Maps; using Content.Shared.Stacks; using JetBrains.Annotations; -using Robust.Shared.Map; +using Robust.Shared.Map.Components; namespace Content.Server.Engineering.EntitySystems { [UsedImplicitly] public sealed class SpawnAfterInteractSystem : EntitySystem { - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly StackSystem _stackSystem = default!; @@ -30,7 +29,7 @@ private async void HandleAfterInteract(EntityUid uid, SpawnAfterInteractComponen return; if (string.IsNullOrEmpty(component.Prototype)) return; - if (!_mapManager.TryGetGrid(args.ClickLocation.GetGridUid(EntityManager), out var grid)) + if (!TryComp(args.ClickLocation.GetGridUid(EntityManager), out var grid)) return; if (!grid.TryGetTileRef(args.ClickLocation, out var tileRef)) return; diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs index af150c9325..55e8de2632 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs @@ -4,7 +4,6 @@ using Content.Shared.Damage; using Content.Shared.Explosion; using Content.Shared.FixedPoint; -using Robust.Shared.Map; using Robust.Shared.Map.Components; namespace Content.Server.Explosion.EntitySystems; @@ -102,7 +101,7 @@ private void OnAirtightDamaged(EntityUid uid, AirtightComponent airtight, Damage if (!EntityManager.TryGetComponent(uid, out TransformComponent? transform) || !transform.Anchored) return; - if (!_mapManager.TryGetGrid(transform.GridUid, out var grid)) + if (!TryComp(transform.GridUid, out var grid)) return; UpdateAirtightMap(transform.GridUid.Value, grid, grid.CoordinatesToTile(transform.Coordinates)); diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs index b04642a8db..719a2eca79 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs @@ -23,7 +23,7 @@ public sealed partial class ExplosionSystem : EntitySystem /// private void OnGridStartup(GridStartupEvent ev) { - var grid = _mapManager.GetGrid(ev.EntityUid); + var grid = Comp(ev.EntityUid); Dictionary edges = new(); _gridEdges[ev.EntityUid] = edges; diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs index 4a9477e744..ccfcee4612 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs @@ -1,4 +1,3 @@ -using System.Linq; using System.Numerics; using Content.Shared.CCVar; using Content.Shared.Damage; @@ -17,6 +16,7 @@ using Robust.Shared.Timing; using Robust.Shared.Utility; using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent; +using Content.Server.Atmos.EntitySystems; namespace Content.Server.Explosion.EntitySystems; @@ -44,13 +44,6 @@ public sealed partial class ExplosionSystem /// private Explosion? _activeExplosion; - /// - /// While processing an explosion, the "progress" is sent to clients, so that the explosion fireball effect - /// syncs up with the damage. When the tile iteration increments, an update needs to be sent to clients. - /// This integer keeps track of the last value sent to clients. - /// - private int _previousTileIteration; - /// /// This list is used when raising to avoid allocating a new list per event. /// @@ -108,8 +101,6 @@ public override void Update(float frameTime) if (_activeExplosion == null) continue; - _previousTileIteration = 0; - // just a lil nap if (SleepNodeSys) { diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs index afad0e27e0..a42dd11083 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs @@ -4,6 +4,7 @@ using Content.Shared.Explosion; using Content.Shared.Explosion.Components; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Timing; @@ -56,7 +57,7 @@ public sealed partial class ExplosionSystem : EntitySystem else if (referenceGrid != null) { // reference grid defines coordinate system that the explosion in space will use - initialTile = _mapManager.GetGrid(referenceGrid.Value).WorldToTile(epicenter.Position); + initialTile = Comp(referenceGrid.Value).WorldToTile(epicenter.Position); } else { @@ -87,7 +88,7 @@ public sealed partial class ExplosionSystem : EntitySystem var spaceAngle = Angle.Zero; if (referenceGrid != null) { - var xform = Transform(_mapManager.GetGrid(referenceGrid.Value).Owner); + var xform = Transform(Comp(referenceGrid.Value).Owner); spaceMatrix = xform.WorldMatrix; spaceAngle = xform.WorldRotation; } @@ -102,7 +103,7 @@ public sealed partial class ExplosionSystem : EntitySystem airtightMap = new(); var initialGridData = new ExplosionGridTileFlood( - _mapManager.GetGrid(epicentreGrid.Value), + Comp(epicentreGrid.Value), airtightMap, maxIntensity, stepSize, @@ -191,7 +192,7 @@ public sealed partial class ExplosionSystem : EntitySystem airtightMap = new(); data = new ExplosionGridTileFlood( - _mapManager.GetGrid(grid), + Comp(grid), airtightMap, maxIntensity, stepSize, diff --git a/Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs b/Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs index 24ee2b7154..2d54c03b51 100644 --- a/Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs +++ b/Content.Server/Eye/Blinding/EyeProtection/EyeProtectionSystem.cs @@ -1,10 +1,8 @@ using Content.Shared.StatusEffect; using Content.Shared.Inventory; -using Content.Shared.Item; using Content.Shared.Eye.Blinding.Components; using Content.Shared.Eye.Blinding.Systems; using Content.Shared.Tools.Components; -using Content.Shared.Item.ItemToggle; using Content.Shared.Item.ItemToggle.Components; namespace Content.Server.Eye.Blinding.EyeProtection @@ -13,7 +11,6 @@ public sealed class EyeProtectionSystem : EntitySystem { [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; [Dependency] private readonly BlindableSystem _blindingSystem = default!; - [Dependency] private readonly SharedItemToggleSystem _itemToggle = default!; public override void Initialize() { diff --git a/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs b/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs index 98926536b9..128f112304 100644 --- a/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs @@ -24,6 +24,7 @@ using Robust.Shared.Configuration; using Robust.Shared.Enums; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -180,7 +181,7 @@ private void OnPlayerSpawningEvent(RulePlayerSpawningEvent ev) var aabbs = EntityQuery().SelectMany(x => x.Grids.Select(x => - xformQuery.GetComponent(x).WorldMatrix.TransformBox(_mapManager.GetGridComp(x).LocalAABB))) + xformQuery.GetComponent(x).WorldMatrix.TransformBox(Comp(x).LocalAABB))) .ToArray(); var aabb = aabbs[0]; diff --git a/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs b/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs index 7558f7afc0..c934fb66bf 100644 --- a/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs +++ b/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs @@ -1,23 +1,16 @@ using System.Linq; -using System.Numerics; using Content.Server.Gateway.Components; using Content.Server.Parallax; using Content.Server.Procedural; -using Content.Server.Salvage; using Content.Shared.CCVar; using Content.Shared.Dataset; using Content.Shared.Maps; -using Content.Shared.Movement.Components; using Content.Shared.Parallax.Biomes; -using Content.Shared.Physics; using Content.Shared.Procedural; using Content.Shared.Salvage; using Robust.Shared.Configuration; using Robust.Shared.Map; using Robust.Shared.Map.Components; -using Robust.Shared.Physics.Collision.Shapes; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Systems; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -40,7 +33,6 @@ public sealed class GatewayGeneratorSystem : EntitySystem [Dependency] private readonly DungeonSystem _dungeon = default!; [Dependency] private readonly GatewaySystem _gateway = default!; [Dependency] private readonly MetaDataSystem _metadata = default!; - [Dependency] private readonly RestrictedRangeSystem _restricted = default!; [Dependency] private readonly SharedMapSystem _maps = default!; [Dependency] private readonly TileSystem _tile = default!; diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index a0e872dbeb..e836b65838 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -1,6 +1,5 @@ using System.Numerics; using Content.Server.Inventory; -using Content.Server.Pulling; using Content.Server.Stack; using Content.Server.Stunnable; using Content.Shared.ActionBlocker; @@ -11,8 +10,9 @@ using Content.Shared.Hands.EntitySystems; using Content.Shared.Input; using Content.Shared.Inventory.VirtualItem; -using Content.Shared.Physics.Pull; -using Content.Shared.Pulling.Components; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Events; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Stacks; using Content.Shared.Throwing; using Robust.Shared.GameStates; @@ -84,9 +84,8 @@ private void OnDisarmed(EntityUid uid, HandsComponent component, DisarmedEvent a return; // Break any pulls - if (TryComp(uid, out SharedPullerComponent? puller) && puller.Pulling is EntityUid pulled && - TryComp(pulled, out SharedPullableComponent? pullable)) - _pullingSystem.TryStopPull(pullable); + if (TryComp(uid, out PullerComponent? puller) && TryComp(puller.Pulling, out PullableComponent? pullable)) + _pullingSystem.TryStopPull(puller.Pulling.Value, pullable); if (!_handsSystem.TryDrop(uid, component.ActiveHand!, null, checkActionBlocker: false)) return; @@ -96,17 +95,17 @@ private void OnDisarmed(EntityUid uid, HandsComponent component, DisarmedEvent a private void HandleBodyPartAdded(EntityUid uid, HandsComponent component, ref BodyPartAddedEvent args) { - if (args.Part.PartType != BodyPartType.Hand) + if (args.Part.Comp.PartType != BodyPartType.Hand) return; // If this annoys you, which it should. // Ping Smugleaf. - var location = args.Part.Symmetry switch + var location = args.Part.Comp.Symmetry switch { BodyPartSymmetry.None => HandLocation.Middle, BodyPartSymmetry.Left => HandLocation.Left, BodyPartSymmetry.Right => HandLocation.Right, - _ => throw new ArgumentOutOfRangeException(nameof(args.Part.Symmetry)) + _ => throw new ArgumentOutOfRangeException(nameof(args.Part.Comp.Symmetry)) }; AddHand(uid, args.Slot, location); @@ -114,7 +113,7 @@ private void HandleBodyPartAdded(EntityUid uid, HandsComponent component, ref Bo private void HandleBodyPartRemoved(EntityUid uid, HandsComponent component, ref BodyPartRemovedEvent args) { - if (args.Part.PartType != BodyPartType.Hand) + if (args.Part.Comp.PartType != BodyPartType.Hand) return; RemoveHand(uid, args.Slot); @@ -124,13 +123,13 @@ private void HandleBodyPartRemoved(EntityUid uid, HandsComponent component, ref private void HandlePullStarted(EntityUid uid, HandsComponent component, PullStartedMessage args) { - if (args.Puller.Owner != uid) + if (args.PullerUid != uid) return; - if (TryComp(args.Puller.Owner, out var pullerComp) && !pullerComp.NeedsHands) + if (TryComp(args.PullerUid, out var pullerComp) && !pullerComp.NeedsHands) return; - if (!_virtualItemSystem.TrySpawnVirtualItemInHand(args.Pulled.Owner, uid)) + if (!_virtualItemSystem.TrySpawnVirtualItemInHand(args.PulledUid, uid)) { DebugTools.Assert("Unable to find available hand when starting pulling??"); } @@ -138,7 +137,7 @@ private void HandlePullStarted(EntityUid uid, HandsComponent component, PullStar private void HandlePullStopped(EntityUid uid, HandsComponent component, PullStoppedMessage args) { - if (args.Puller.Owner != uid) + if (args.PullerUid != uid) return; // Try find hand that is doing this pull. @@ -147,8 +146,10 @@ private void HandlePullStopped(EntityUid uid, HandsComponent component, PullStop { if (hand.HeldEntity == null || !TryComp(hand.HeldEntity, out VirtualItemComponent? virtualItem) - || virtualItem.BlockingEntity != args.Pulled.Owner) + || virtualItem.BlockingEntity != args.PulledUid) + { continue; + } QueueDel(hand.HeldEntity.Value); break; diff --git a/Content.Server/ImmovableRod/ImmovableRodSystem.cs b/Content.Server/ImmovableRod/ImmovableRodSystem.cs index 31aa39cf03..429361cd8c 100644 --- a/Content.Server/ImmovableRod/ImmovableRodSystem.cs +++ b/Content.Server/ImmovableRod/ImmovableRodSystem.cs @@ -6,6 +6,7 @@ using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; @@ -16,7 +17,6 @@ namespace Content.Server.ImmovableRod; public sealed class ImmovableRodSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly IMapManager _map = default!; [Dependency] private readonly BodySystem _bodySystem = default!; [Dependency] private readonly PopupSystem _popup = default!; @@ -33,7 +33,7 @@ public override void Update(float frameTime) if (!rod.DestroyTiles) continue; - if (!_map.TryGetGrid(trans.GridUid, out var grid)) + if (!TryComp(trans.GridUid, out var grid)) continue; grid.SetTile(trans.Coordinates, Tile.Empty); diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index 8eb2741448..6b58f6eb09 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -19,6 +19,8 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Random; using System.Numerics; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; namespace Content.Server.Implants; @@ -34,6 +36,7 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedTransformSystem _xform = default!; [Dependency] private readonly ForensicsSystem _forensicsSystem = default!; + [Dependency] private readonly PullingSystem _pullingSystem = default!; private EntityQuery _physicsQuery; @@ -98,6 +101,11 @@ private void OnScramImplant(EntityUid uid, SubdermalImplantComponent component, if (!TryComp(uid, out var implant)) return; + // We need stop the user from being pulled so they don't just get "attached" with whoever is pulling them. + // This can for example happen when the user is cuffed and being pulled. + if (TryComp(ent, out var pull) && _pullingSystem.IsPulled(ent, pull)) + _pullingSystem.TryStopPull(ent, pull); + var xform = Transform(ent); var entityCoords = xform.Coordinates.ToMap(EntityManager, _xform); diff --git a/Content.Server/Instruments/InstrumentComponent.cs b/Content.Server/Instruments/InstrumentComponent.cs index 4302ab6791..1b7913386d 100644 --- a/Content.Server/Instruments/InstrumentComponent.cs +++ b/Content.Server/Instruments/InstrumentComponent.cs @@ -1,6 +1,5 @@ using Content.Server.UserInterface; using Content.Shared.Instruments; -using Robust.Server.GameObjects; using Robust.Shared.Player; namespace Content.Server.Instruments; diff --git a/Content.Server/Interaction/InteractionSystem.cs b/Content.Server/Interaction/InteractionSystem.cs index 6692886dae..203781bcda 100644 --- a/Content.Server/Interaction/InteractionSystem.cs +++ b/Content.Server/Interaction/InteractionSystem.cs @@ -1,4 +1,3 @@ -using Content.Shared.ActionBlocker; using Content.Shared.Interaction; using Content.Shared.Storage; using JetBrains.Annotations; @@ -14,7 +13,6 @@ namespace Content.Server.Interaction [UsedImplicitly] public sealed partial class InteractionSystem : SharedInteractionSystem { - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; diff --git a/Content.Server/Interaction/TilePryCommand.cs b/Content.Server/Interaction/TilePryCommand.cs index 57b07e8181..006a245ead 100644 --- a/Content.Server/Interaction/TilePryCommand.cs +++ b/Content.Server/Interaction/TilePryCommand.cs @@ -4,6 +4,7 @@ using Content.Shared.Maps; using Robust.Shared.Console; using Robust.Shared.Map; +using Robust.Shared.Map.Components; namespace Content.Server.Interaction { @@ -46,7 +47,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) var xform = _entities.GetComponent(attached); var playerGrid = xform.GridUid; - if (!mapManager.TryGetGrid(playerGrid, out var mapGrid)) + if (!_entities.TryGetComponent(playerGrid, out var mapGrid)) return; var playerPosition = xform.Coordinates; diff --git a/Content.Server/Language/TranslatorSystem.cs b/Content.Server/Language/TranslatorSystem.cs index 5022e54096..adbfe2d681 100644 --- a/Content.Server/Language/TranslatorSystem.cs +++ b/Content.Server/Language/TranslatorSystem.cs @@ -1,15 +1,12 @@ using System.Linq; -using Content.Server.Language.Events; using Content.Server.Popups; using Content.Server.PowerCell; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Language; -using Content.Shared.Language.Events; using Content.Shared.Language.Systems; using Content.Shared.PowerCell; using Content.Shared.Language.Components.Translators; -using Robust.Shared.Utility; namespace Content.Server.Language; diff --git a/Content.Server/Magic/MagicSystem.cs b/Content.Server/Magic/MagicSystem.cs index 92cd794ce2..8655592471 100644 --- a/Content.Server/Magic/MagicSystem.cs +++ b/Content.Server/Magic/MagicSystem.cs @@ -21,6 +21,7 @@ using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Random; using Robust.Shared.Serialization.Manager; using Robust.Shared.Spawners; @@ -217,7 +218,7 @@ private List GetSpawnPositions(TransformComponent casterXform // This is shit but you get the idea. var directionPos = casterXform.Coordinates.Offset(casterXform.LocalRotation.ToWorldVec().Normalized()); - if (!_mapManager.TryGetGrid(casterXform.GridUid, out var mapGrid)) + if (!TryComp(casterXform.GridUid, out var mapGrid)) return new List(); if (!directionPos.TryGetTileRef(out var tileReference, EntityManager, _mapManager)) diff --git a/Content.Server/Mapping/MappingCommand.cs b/Content.Server/Mapping/MappingCommand.cs index 08ba0de833..08f3dcccf9 100644 --- a/Content.Server/Mapping/MappingCommand.cs +++ b/Content.Server/Mapping/MappingCommand.cs @@ -1,17 +1,14 @@ -// ReSharper disable once RedundantUsingDirective -// Used to warn the player in big red letters in debug mode - using System.Linq; using Content.Server.Administration; using Content.Server.GameTicking; using Content.Shared.Administration; using Content.Shared.CCVar; -using Robust.Server.Player; +using Robust.Server.GameObjects; +using Robust.Server.Maps; using Robust.Shared.Configuration; using Robust.Shared.Console; using Robust.Shared.ContentPack; using Robust.Shared.Map; -using Robust.Shared.Utility; namespace Content.Server.Mapping { @@ -19,6 +16,8 @@ namespace Content.Server.Mapping sealed class MappingCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entities = default!; + [Dependency] private readonly IMapManager _map = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; public string Command => "mapping"; public string Description => Loc.GetString("cmd-mapping-desc"); @@ -57,13 +56,13 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) shell.WriteError(Loc.GetString("cmd-mapping-warning")); #endif - var mapManager = IoCManager.Resolve(); MapId mapId; + string? toLoad = null; + var mapSys = _entities.System(); // Get the map ID to use if (args.Length is 1 or 2) { - if (!int.TryParse(args[0], out var intMapId)) { shell.WriteError(Loc.GetString("cmd-mapping-failure-integer", ("arg", args[0]))); @@ -79,35 +78,33 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - if (mapManager.MapExists(mapId)) + if (_map.MapExists(mapId)) { shell.WriteError(Loc.GetString("cmd-mapping-exists", ("mapId", mapId))); return; } - } - else - { - mapId = mapManager.NextMapId(); - } + // either load a map or create a new one. + if (args.Length <= 1) + { + mapSys.CreateMap(mapId, runMapInit: false); + } + else + { + var loadOptions = new MapLoadOptions {StoreMapUids = true}; + _entities.System().TryLoad(mapId, args[1], out _, loadOptions); + } - string? toLoad = null; - // either load a map or create a new one. - if (args.Length <= 1) - { - shell.ExecuteCommand($"addmap {mapId} false"); + // was the map actually created or did it fail somehow? + if (!_map.MapExists(mapId)) + { + shell.WriteError(Loc.GetString("cmd-mapping-error")); + return; + } } else { - toLoad = CommandParsing.Escape(args[1]); - shell.ExecuteCommand($"loadmap {mapId} \"{toLoad}\" 0 0 0 true"); - } - - // was the map actually created? - if (!mapManager.MapExists(mapId)) - { - shell.WriteError(Loc.GetString("cmd-mapping-error")); - return; + mapSys.CreateMap(out mapId, runMapInit: false); } // map successfully created. run misc helpful mapping commands @@ -117,17 +114,15 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) shell.ExecuteCommand("aghost"); } - var cfg = IoCManager.Resolve(); - // don't interrupt mapping with events or auto-shuttle shell.ExecuteCommand("sudo cvar events.enabled false"); shell.ExecuteCommand("sudo cvar shuttle.auto_call_time 0"); - if (cfg.GetCVar(CCVars.AutosaveEnabled)) + if (_cfg.GetCVar(CCVars.AutosaveEnabled)) shell.ExecuteCommand($"toggleautosave {mapId} {toLoad ?? "NEWMAP"}"); shell.ExecuteCommand($"tp 0 0 {mapId}"); shell.RemoteExecuteCommand("mappingclientsidesetup"); - mapManager.SetMapPaused(mapId, true); + _map.SetMapPaused(mapId, true); if (args.Length == 2) shell.WriteLine(Loc.GetString("cmd-mapping-success-load",("mapId",mapId),("path", args[1]))); diff --git a/Content.Server/Mech/Systems/MechSystem.cs b/Content.Server/Mech/Systems/MechSystem.cs index 1012b9727d..f592de9e7e 100644 --- a/Content.Server/Mech/Systems/MechSystem.cs +++ b/Content.Server/Mech/Systems/MechSystem.cs @@ -20,7 +20,6 @@ using Robust.Server.Containers; using Robust.Server.GameObjects; using Robust.Shared.Containers; -using Robust.Shared.Map; using Robust.Shared.Player; namespace Content.Server.Mech.Systems; @@ -33,8 +32,6 @@ public sealed partial class MechSystem : SharedMechSystem [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly ContainerSystem _container = default!; [Dependency] private readonly DamageableSystem _damageable = default!; - [Dependency] private readonly IMapManager _map = default!; - [Dependency] private readonly MapSystem _mapSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; diff --git a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs index f19b3d5b81..09497c7136 100644 --- a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs +++ b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs @@ -9,7 +9,6 @@ using Content.Server.Station.Systems; using Content.Shared.Damage; using Content.Shared.DeviceNetwork; -using Content.Shared.Emp; using Content.Shared.Examine; using Content.Shared.Inventory.Events; using Content.Shared.Medical.SuitSensor; @@ -27,7 +26,6 @@ public sealed class SuitSensorSystem : EntitySystem { [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly CrewMonitoringServerSystem _monitoringServerSystem = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!; [Dependency] private readonly IdCardSystem _idCardSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; diff --git a/Content.Server/NPC/Components/NPCConversationComponent.cs b/Content.Server/NPC/Components/NPCConversationComponent.cs deleted file mode 100644 index c2a8ca31d7..0000000000 --- a/Content.Server/NPC/Components/NPCConversationComponent.cs +++ /dev/null @@ -1,152 +0,0 @@ -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Content.Server.NPC.Events; -using Content.Server.NPC.Prototypes; -using Content.Server.NPC.Systems; - -namespace Content.Server.NPC.Components; - -[RegisterComponent] -[Access(typeof(NPCConversationSystem))] -public sealed partial class NPCConversationComponent : Component -{ - /// - /// Whether or not the listening logic is turned on. - /// - /// - /// Queued responses will still play through, but no new attempts to listen will be made. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("enabled")] - public bool Enabled = true; - - /* NYI: - /// - /// The NPC will pay attention when one of these words are said. - /// - [ViewVariables] - [DataField("aliases")] - public List Aliases = new(); - */ - - [ViewVariables] - [DataField("tree", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? ConversationTreeId; - - /// - /// This is the cached prototype. - /// - [ViewVariables] - public NPCConversationTreePrototype ConversationTree = default!; - - /// - /// Topics that are unlocked in the NPC's conversation tree. - /// - [ViewVariables] - public HashSet UnlockedTopics = new(); - - /// - /// How long until we stop paying attention to someone for a prompt. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("attentionSpan")] - public TimeSpan AttentionSpan = TimeSpan.FromSeconds(20); - - /// - /// This is the minimum delay before the NPC makes a response. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("delayBeforeResponse")] - public TimeSpan DelayBeforeResponse = TimeSpan.FromSeconds(0.3); - - /// - /// This is the approximate delay per letter typed in text. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("typingDelay")] - public TimeSpan TypingDelay = TimeSpan.FromSeconds(0.05); - - [ViewVariables] - public Stack ResponseQueue = new(); - - /// - /// This is when the NPC will respond with its top response. - /// - [ViewVariables] - [DataField("nextResponse", customTypeSerializer: typeof(TimeOffsetSerializer))] - public TimeSpan NextResponse; - - /// - /// This is the direction the NPC was facing before looking towards a conversation partner. - /// - [ViewVariables] - public Angle OriginalFacing; - - /// - /// This is who the NPC is paying attention to for conversation. - /// - [ViewVariables] - public EntityUid? AttendingTo; - - /// - /// This is when the NPC will stop paying attention to a specific person. - /// - [ViewVariables] - [DataField("nextAttentionLoss", customTypeSerializer: typeof(TimeOffsetSerializer))] - public TimeSpan NextAttentionLoss; - - /// - /// This event is fired the next time the NPC hears something from the - /// person they're speaking with and it takes control of the response. - /// - [ViewVariables] - public NPCConversationListenEvent? ListeningEvent; - -#region Idle Chatter - - /// - /// Whether or not the NPC will say things unprompted. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("idleEnabled")] - public bool IdleEnabled = true; - - /// - /// This is the approximate delay between idle chats. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("idleChatDelay")] - public TimeSpan IdleChatDelay = TimeSpan.FromMinutes(3); - - /// - /// This is the order in which idle chat lines are given. - /// - /// - /// This is randomized both on init and when the lines have been exhausted - /// to prevent repeating lines twice in a row and to avoid predictable patterns. - /// - /// It technically reduces randomness, with the benefit of less repetition. - /// - [ViewVariables(VVAccess.ReadWrite)] - public List IdleChatOrder = new(); - - /// - /// This is the next idle chat line that will be used. - /// - [ViewVariables(VVAccess.ReadWrite)] - public int IdleChatIndex = 0; - - /// - /// This is when the NPC will say something out of its list of idle lines. - /// - /// - /// This is reset every time the NPC speaks. - /// - [ViewVariables] - [DataField("nextIdleChat", customTypeSerializer: typeof(TimeOffsetSerializer))] - public TimeSpan NextIdleChat; - -#endregion - -} - diff --git a/Content.Server/NPC/Events/NPCConversationEvents.cs b/Content.Server/NPC/Events/NPCConversationEvents.cs deleted file mode 100644 index eb04f59bdd..0000000000 --- a/Content.Server/NPC/Events/NPCConversationEvents.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Robust.Shared.Audio; -using Content.Server.NPC.Systems; - -namespace Content.Server.NPC.Events; - -/// -/// This is used for dynamic responses and post-response events. -/// -[ImplicitDataDefinitionForInheritors] -[Access(typeof(NPCConversationSystem))] -public abstract partial class NPCConversationEvent : EntityEventArgs -{ - /// - /// This is the entity that the NPC is speaking to. - /// - public EntityUid? TalkingTo; -} - -/// -/// This event type is raised when an NPC hears a response when it was set to listen for one. -/// -/// -/// Set Handled to true when you want the NPC to stop listening. -/// The NPC will otherwise keep listening and block any attempt to find a prompt in the speaker's words. -/// -[ImplicitDataDefinitionForInheritors] -[Access(typeof(NPCConversationSystem))] -public abstract partial class NPCConversationListenEvent : HandledEntityEventArgs -{ - /// - /// This is the entity that said the message. - /// - public EntityUid? Speaker; - - /// - /// This is the original message that the NPC heard. - /// - public string Message = default!; - - /// - /// This is the message, parsed into separate words. - /// - public List Words = default!; -} - -public sealed partial class NPCConversationHelpEvent : NPCConversationEvent -{ - [DataField("text")] - public string? Text; - - [DataField("audio")] - public SoundSpecifier? Audio; -} - -/// -/// This event can be raised after a response to cause an NPC to stop paying attention to someone. -/// -public sealed partial class NPCConversationByeEvent : NPCConversationEvent { } - -// The following classes help demonstrate some of the features of the system. -// They may be separated out at some point. -public sealed partial class NPCConversationToldNameEvent : NPCConversationListenEvent { } - diff --git a/Content.Server/NPC/HTN/Preconditions/PulledPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/PulledPrecondition.cs index 64a72b13cf..d276be7218 100644 --- a/Content.Server/NPC/HTN/Preconditions/PulledPrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/PulledPrecondition.cs @@ -1,4 +1,5 @@ using Content.Shared.Pulling; +using PullingSystem = Content.Shared.Movement.Pulling.Systems.PullingSystem; namespace Content.Server.NPC.HTN.Preconditions; @@ -7,14 +8,14 @@ namespace Content.Server.NPC.HTN.Preconditions; /// public sealed partial class PulledPrecondition : HTNPrecondition { - private SharedPullingSystem _pulling = default!; + private PullingSystem _pulling = default!; [ViewVariables(VVAccess.ReadWrite)] [DataField("isPulled")] public bool IsPulled = true; public override void Initialize(IEntitySystemManager sysManager) { base.Initialize(sysManager); - _pulling = sysManager.GetEntitySystem(); + _pulling = sysManager.GetEntitySystem(); } public override bool IsMet(NPCBlackboard blackboard) diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/MoveToOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/MoveToOperator.cs index dd35d2112c..e64343fdd8 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/MoveToOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/MoveToOperator.cs @@ -4,6 +4,7 @@ using Content.Server.NPC.Pathfinding; using Content.Server.NPC.Systems; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators; @@ -14,7 +15,6 @@ namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators; public sealed partial class MoveToOperator : HTNOperator, IHtnConditionalShutdown { [Dependency] private readonly IEntityManager _entManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; private NPCSteeringSystem _steering = default!; private PathfindingSystem _pathfind = default!; private SharedTransformSystem _transform = default!; @@ -85,8 +85,8 @@ public override void Initialize(IEntitySystemManager sysManager) !_entManager.TryGetComponent(owner, out var body)) return (false, null); - if (!_mapManager.TryGetGrid(xform.GridUid, out var ownerGrid) || - !_mapManager.TryGetGrid(targetCoordinates.GetGridUid(_entManager), out var targetGrid)) + if (!_entManager.TryGetComponent(xform.GridUid, out var ownerGrid) || + !_entManager.TryGetComponent(targetCoordinates.GetGridUid(_entManager), out var targetGrid)) { return (false, null); } diff --git a/Content.Server/NPC/Prototypes/NPCConversationTreePrototype.cs b/Content.Server/NPC/Prototypes/NPCConversationTreePrototype.cs deleted file mode 100644 index 20a616d830..0000000000 --- a/Content.Server/NPC/Prototypes/NPCConversationTreePrototype.cs +++ /dev/null @@ -1,154 +0,0 @@ -using Robust.Shared.Audio; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; -using Content.Server.NPC.Events; - -namespace Content.Server.NPC.Prototypes; - -[Prototype("npcConversationTree")] -public sealed class NPCConversationTreePrototype : IPrototype, ISerializationHooks -{ - [ViewVariables] - [IdDataField] - public string ID { get; } = default!; - - /// - /// Dialogue contains all the topics to which an NPC can discuss. - /// - [ViewVariables] - [DataField("dialogue", required: true)] - public readonly NPCTopic[] Dialogue = default!; - - /// - /// Attention responses are what the NPC says when they start paying - /// attention to you without a specific question or prompt to respond to. - /// - [ViewVariables] - [DataField("attention", required: true)] - public readonly NPCResponse[] Attention = default!; - - /// - /// Idle responses are just things the NPC will say when nothing else is - /// going on, after some time. - /// - [ViewVariables] - [DataField("idle", required: true)] - public readonly NPCResponse[] Idle = default!; - - /// - /// Unknown responses are what the NPC says when they can't respond to a - /// particular question or prompt. - /// - [ViewVariables] - [DataField("unknown", required: true)] - public readonly NPCResponse[] Unknown = default!; - - /// - /// Custom responses are available to use in extensions to the NPC - /// Conversation system. - /// - // NOTE: This may be removed in favor of storing NPCResponses on custom - // components, i.e. an NPCShopkeeperComponent, but for now, it lives here - // to help demonstrate some features. - [ViewVariables] - [DataField("custom")] - public readonly Dictionary Custom = default!; - - /// - /// This exists as a quick way to map a prompt to a topic. - /// - public readonly Dictionary PromptToTopic = new(); - - // ISerializationHooks _is_ obsolete, but ConstructionGraphPrototype is using it as of this commit, - // and I'm not quite sure how to otherwise do this. - // - // I will look at that prototype when ISerializationHooks is phased out. - void ISerializationHooks.AfterDeserialization() - { - // Cache the strings mapping to prompts. - foreach (var topic in Dialogue) - { - foreach (var prompt in topic.Prompts) - { - PromptToTopic[prompt] = topic; - } - } - } -} - -[DataDefinition] -public sealed partial class NPCTopic -{ - [DataField] - public string[] Prompts = default!; - - /// - /// This determines the likelihood of this topic being selected over any - /// other, given the existence of multiple candidates. - /// - [DataField] - public float Weight = 1.0f; - - /// - /// Locked topics will not be accessible through dialogue until unlocked. - /// - [DataField] - public bool Locked; - - /// - /// Hidden topics won't show up in any form of "help" question. - /// - [DataField] - public bool Hidden; - - [DataField("responses", required: true)] - public NPCResponse[] Responses = default!; -} - -[DataDefinition] -public sealed partial class NPCResponse -{ - public NPCResponse() { } - - public NPCResponse(string? text, SoundSpecifier? audio = null, NPCConversationEvent? ev = null) - { - Text = text; - Audio = audio; - Event = ev; - } - - public override string ToString() - { - return $"NPCResponse({Text})"; - } - - [DataField] - public string? Text; - - [DataField] - public SoundSpecifier? Audio; - - /* [DataField("emote")] */ - /* public string? Emote; */ - - /// - /// This event is raised when the response is queued, - /// for the purpose of dynamic responses. - /// - [DataField] - public NPCConversationEvent? Is; - - /// - /// This event is raised after the response is made. - /// - [DataField] - public NPCConversationEvent? Event; - - /// - /// This event is raised when the NPC next hears a response, - /// allowing the response to be processed by other systems. - /// - [DataField] - public NPCConversationListenEvent? ListenEvent; -} - diff --git a/Content.Server/NPC/Systems/NPCConversationSystem.cs b/Content.Server/NPC/Systems/NPCConversationSystem.cs deleted file mode 100644 index 015adb19de..0000000000 --- a/Content.Server/NPC/Systems/NPCConversationSystem.cs +++ /dev/null @@ -1,558 +0,0 @@ -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Text.RegularExpressions; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Timing; -using Content.Server.Chat.Systems; -using Content.Server.Chat.TypingIndicator; -using Content.Server.NPC.HTN; -using Content.Server.NPC.Components; -using Content.Server.NPC.Events; -using Content.Server.NPC.Prototypes; -using Content.Server.Speech; -using Content.Shared.Interaction; -using Content.Server.Radio.Components; - -namespace Content.Server.NPC.Systems; - -public sealed class NPCConversationSystem : EntitySystem -{ - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly IPrototypeManager _prototype = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - [Dependency] private readonly ChatSystem _chatSystem = default!; - [Dependency] private readonly NPCSystem _npcSystem = default!; - [Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; - [Dependency] private readonly TypingIndicatorSystem _typingIndicatorSystem = default!; - - private ISawmill _sawmill = default!; - - // TODO: attention attenuation. distance, facing, visible - // TODO: attending to multiple people, multiple streams of conversation - // TODO: multi-word prompts - // TODO: nameless prompting (pointing is good) - // TODO: aliases - - public static readonly string[] QuestionWords = { "who", "what", "when", "why", "where", "how" }; - public static readonly string[] Copulae = { "is", "are" }; - - public override void Initialize() - { - base.Initialize(); - - _sawmill = Logger.GetSawmill("npc.conversation"); - - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnUnpaused); - SubscribeLocalEvent(OnListenAttempt); - SubscribeLocalEvent(OnListen); - - SubscribeLocalEvent(OnBye); - SubscribeLocalEvent(OnHelp); - - SubscribeLocalEvent(OnToldName); - } - -#region API - - /// - /// Toggle the ability of an NPC to listen for topics. - /// - public void EnableConversation(EntityUid uid, bool enable = true, NPCConversationComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - component.Enabled = enable; - } - - /// - /// Toggle the NPC's willingness to make idle comments. - /// - public void EnableIdleChat(EntityUid uid, bool enable = true, NPCConversationComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - component.IdleEnabled = enable; - } - - /// - /// Return locked status of a dialogue topic. - /// - public bool IsDialogueLocked(EntityUid uid, string option, NPCConversationComponent? component = null) - { - if (!Resolve(uid, ref component)) - return true; - - if (!component.ConversationTree.PromptToTopic.TryGetValue(option, out var topic)) - { - _sawmill.Warning($"Tried to check locked status of missing dialogue option `{option}` on {ToPrettyString(uid)}"); - return true; - } - - if (component.UnlockedTopics.Contains(topic)) - return false; - - return topic.Locked; - } - - /// - /// Unlock dialogue options normally locked in an NPC's conversation tree. - /// - public void UnlockDialogue(EntityUid uid, string option, NPCConversationComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - if (component.ConversationTree.PromptToTopic.TryGetValue(option, out var topic)) - component.UnlockedTopics.Add(topic); - else - _sawmill.Warning($"Tried to unlock missing dialogue option `{option}` on {ToPrettyString(uid)}"); - } - - /// - public void UnlockDialogue(EntityUid uid, HashSet options, NPCConversationComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - foreach (var option in options) - UnlockDialogue(uid, option, component); - } - - /// - /// Queue a response for an NPC with a visible typing indicator and delay between messages. - /// - /// - /// This can be used as opposed to the typical method. - /// - public void QueueResponse(EntityUid uid, NPCResponse response, NPCConversationComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - if (response.Is is {} ev) - { - // This is a dynamic response which will call QueueResponse with static responses of its own. - ev.TalkingTo = component.AttendingTo; - RaiseLocalEvent(uid, (object) ev); - return; - } - - if (component.ResponseQueue.Count == 0) - { - DelayResponse(uid, component, response); - _typingIndicatorSystem.SetTypingIndicatorEnabled(uid, true); - } - - component.ResponseQueue.Push(response); - } - - /// - /// Make an NPC stop paying attention to someone. - /// - public void LoseAttention(EntityUid uid, NPCConversationComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - component.AttendingTo = null; - component.ListeningEvent = null; - _rotateToFaceSystem.TryFaceAngle(uid, component.OriginalFacing); - } - -#endregion - - private void DelayResponse(EntityUid uid, NPCConversationComponent component, NPCResponse response) - { - if (response.Text == null) - return; - - component.NextResponse = _gameTiming.CurTime + - component.DelayBeforeResponse + - component.TypingDelay.TotalSeconds * TimeSpan.FromSeconds(response.Text.Length) * - _random.NextDouble(0.9, 1.1); - } - - private IEnumerable GetAvailableTopics(EntityUid uid, NPCConversationComponent component) - { - HashSet availableTopics = new(); - - foreach (var topic in component.ConversationTree.Dialogue) - { - if (!topic.Locked || component.UnlockedTopics.Contains(topic)) - availableTopics.Add(topic); - } - - return availableTopics; - } - - private IEnumerable GetVisibleTopics(EntityUid uid, NPCConversationComponent component) - { - HashSet visibleTopics = new(); - - foreach (var topic in component.ConversationTree.Dialogue) - { - if (!topic.Hidden && (!topic.Locked || component.UnlockedTopics.Contains(topic))) - visibleTopics.Add(topic); - } - - return visibleTopics; - } - - private void OnInit(EntityUid uid, NPCConversationComponent component, ComponentInit args) - { - if (component.ConversationTreeId == null) - return; - - component.ConversationTree = _prototype.Index(component.ConversationTreeId); - component.NextIdleChat = _gameTiming.CurTime + component.IdleChatDelay; - - for (var i = 0; i < component.ConversationTree.Idle.Length; ++i) - component.IdleChatOrder.Add(i); - - _random.Shuffle(component.IdleChatOrder); - } - - private void OnUnpaused(EntityUid uid, NPCConversationComponent component, ref EntityUnpausedEvent args) - { - component.NextResponse += args.PausedTime; - component.NextAttentionLoss += args.PausedTime; - component.NextIdleChat += args.PausedTime; - } - - private bool TryGetIdleChatLine(EntityUid uid, NPCConversationComponent component, [NotNullWhen(true)] out NPCResponse? line) - { - line = null; - - if (component.IdleChatOrder.Count() == 0) - return false; - - if (++component.IdleChatIndex == component.IdleChatOrder.Count()) - { - // Exhausted all lines in the pre-shuffled order. - // Reset the index and shuffle again. - component.IdleChatIndex = 0; - _random.Shuffle(component.IdleChatOrder); - } - - var index = component.IdleChatOrder[component.IdleChatIndex]; - - line = component.ConversationTree.Idle[index]; - - return true; - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var component)) - { - var curTime = _gameTiming.CurTime; - - if (curTime >= component.NextResponse && component.ResponseQueue.Count > 0) - { - // Make a response. - Respond(uid, component, component.ResponseQueue.Pop()); - } - - if (curTime >= component.NextAttentionLoss && component.AttendingTo != null) - { - // Forget who we were talking to. - LoseAttention(uid, component); - } - - if (component.IdleEnabled && - curTime >= component.NextIdleChat && - TryGetIdleChatLine(uid, component, out var line)) - { - Respond(uid, component, line); - } - } - } - - private void OnListenAttempt(EntityUid uid, NPCConversationComponent component, ListenAttemptEvent args) - { - if (!component.Enabled || - // Don't listen to myself... - uid == args.Source || - // Don't bother listening to other NPCs. For now. - HasComp(args.Source) || - // We're already "typing" a response, so do that first. - component.ResponseQueue.Count > 0) - { - args.Cancel(); - } - } - - private void PayAttentionTo(EntityUid uid, NPCConversationComponent component, EntityUid speaker) - { - component.AttendingTo = speaker; - component.NextAttentionLoss = _gameTiming.CurTime + component.AttentionSpan; - component.OriginalFacing = _transformSystem.GetWorldRotation(uid); - } - - private void Respond(EntityUid uid, NPCConversationComponent component, NPCResponse response) - { - if (component.ResponseQueue.Count == 0) - _typingIndicatorSystem.SetTypingIndicatorEnabled(uid, false); - else - DelayResponse(uid, component, component.ResponseQueue.Peek()); - - if (component.AttendingTo != null) - { - // TODO: This line is a mouthful. Maybe write a public API that supports EntityCoordinates later? - var speakerCoords = Transform(component.AttendingTo.Value).Coordinates.ToMap(EntityManager, _transformSystem).Position; - _rotateToFaceSystem.TryFaceCoordinates(uid, speakerCoords); - } - - if (response.Event is {} ev) - { - ev.TalkingTo = component.AttendingTo; - RaiseLocalEvent(uid, (object) ev); - } - - if (response.ListenEvent != null) - component.ListeningEvent = response.ListenEvent; - - if (response.Text != null) - _chatSystem.TrySendInGameICMessage(uid, Loc.GetString(response.Text), InGameICChatType.Speak, false); - - if (response.Audio != null) - _audioSystem.PlayPvs(response.Audio, uid, - // TODO: Allow this to be configured per NPC/response. - AudioParams.Default - .WithVolume(8f) - .WithMaxDistance(9f) - .WithRolloffFactor(0.5f)); - - // Refresh our attention. - component.NextAttentionLoss = _gameTiming.CurTime + component.AttentionSpan; - component.NextIdleChat = component.NextAttentionLoss + component.IdleChatDelay; - } - - private List ParseMessageIntoWords(string message) - { - return Regex.Replace(message.Trim().ToLower(), @"(\p{P})", "") - .Split() - .ToList(); - } - - private bool FindResponse(EntityUid uid, NPCConversationComponent component, List words, [NotNullWhen(true)] out NPCResponse? response) - { - response = null; - - var availableTopics = GetAvailableTopics(uid, component); - - // Some topics are more interesting than others. - var greatestWeight = 0f; - NPCTopic? candidate = null; - - foreach (var word in words) - { - if (component.ConversationTree.PromptToTopic.TryGetValue(word, out var topic) && - availableTopics.Contains(topic) && - topic.Weight > greatestWeight) - { - greatestWeight = topic.Weight; - candidate = topic; - } - } - - if (candidate != null) - { - response = _random.Pick(candidate.Responses); - return true; - } - - return false; - } - - private bool JudgeQuestionLikelihood(EntityUid uid, NPCConversationComponent component, List words, string message) - { - if (message.Length > 0 && message[^1] == '?') - // A question mark is an absolute mark of a question. - return true; - - if (words.Count == 1) - // The usefulness of this is dubious, but it's definitely a question. - return QuestionWords.Contains(words[0]); - - if (words.Count >= 2) - return QuestionWords.Contains(words[0]) && Copulae.Contains(words[1]); - - return false; - } - - private void OnBye(EntityUid uid, NPCConversationComponent component, NPCConversationByeEvent args) - { - LoseAttention(uid, component); - } - - private void OnHelp(EntityUid uid, NPCConversationComponent component, NPCConversationHelpEvent args) - { - if (args.Text == null) - { - _sawmill.Error($"{ToPrettyString(uid)} heard a Help prompt but has no text for it."); - return; - } - - var availableTopics = GetVisibleTopics(uid, component); - var availablePrompts = availableTopics.Select(topic => topic.Prompts.FirstOrDefault()).ToArray(); - - string availablePromptsText; - if (availablePrompts.Count() <= 2) - { - availablePromptsText = Loc.GetString(args.Text, - ("availablePrompts", string.Join(" or ", availablePrompts)) - ); - } - else - { - availablePrompts[^1] = $"or {availablePrompts[^1]}"; - availablePromptsText = Loc.GetString(args.Text, - ("availablePrompts", string.Join(", ", availablePrompts)) - ); - } - - // Unlikely we'll be able to do audio that isn't hard-coded, - // so best to keep it general. - var response = new NPCResponse(availablePromptsText, args.Audio); - QueueResponse(uid, response, component); - } - - private void OnToldName(EntityUid uid, NPCConversationComponent component, NPCConversationListenEvent args) - { - if (!component.ConversationTree.Custom.TryGetValue("toldName", out var responses)) - return; - - var response = _random.Pick(responses); - if (response.Text == null) - { - _sawmill.Error($"{ToPrettyString(uid)} was told a name but had no text response."); - return; - } - - // The world's simplest heuristic for names: - if (args.Words.Count > 3) - { - // It didn't seem like a name, so wait for something that does. - return; - } - - var cleanedName = string.Join(" ", args.Words); - cleanedName = char.ToUpper(cleanedName[0]) + cleanedName.Remove(0, 1); - - var formattedResponse = new NPCResponse(Loc.GetString(response.Text, - ("name", cleanedName)), - response.Audio); - - QueueResponse(uid, formattedResponse, component); - args.Handled = true; - } - - private void OnListen(EntityUid uid, NPCConversationComponent component, ListenEvent args) - { - if (HasComp(args.Source)) - return; - - if (component.AttendingTo != null && component.AttendingTo != args.Source) - // Ignore someone speaking to us if we're already paying attention to someone else. - return; - - var words = ParseMessageIntoWords(args.Message); - if (words.Count == 0) - return; - - if (component.AttendingTo == args.Source) - { - // The person we're talking to said something to us. - - if (component.ListeningEvent is {} ev) - { - // We were waiting on this person to say something, and they've said something. - ev.Handled = false; - ev.Speaker = component.AttendingTo; - ev.Message = args.Message; - ev.Words = words; - RaiseLocalEvent(uid, (object) ev); - - if (ev.Handled) - component.ListeningEvent = null; - - return; - } - - // We're already paying attention to this person, - // so try to figure out if they said something we can talk about. - if (FindResponse(uid, component, words, out var response)) - { - // A response was found so go ahead with it. - QueueResponse(uid, response, component); - } - else if(JudgeQuestionLikelihood(uid, component, words, args.Message)) - { - // The message didn't match any of the prompts, but it seemed like a question. - var unknownResponse = _random.Pick(component.ConversationTree.Unknown); - QueueResponse(uid, unknownResponse, component); - } - - // If the message didn't seem like a question, - // and it didn't raise any of our topics, - // then politely ignore who we're talking with. - // - // It's better than spamming them with "I don't understand." - return; - } - - // See if someone said our name. - var myName = MetaData(uid).EntityName.ToLower(); - - // So this is a rough heuristic, but if our name occurs within the first three words, - // or is the very last one, someone might be trying to talk to us. - var payAttention = words[0] == myName || words[^1] == myName; - if (!payAttention) - { - for (int i = 1; i < Math.Min(2, words.Count); ++i) - { - if (words[i] == myName) - { - payAttention = true; - break; - } - } - } - - if (payAttention) - { - PayAttentionTo(uid, component, args.Source); - - if (!FindResponse(uid, component, words, out var response)) - { - if(JudgeQuestionLikelihood(uid, component, words, args.Message) && - // This subcondition exists to block our name being interpreted as a question in its own right. - words.Count > 1) - { - response = _random.Pick(component.ConversationTree.Unknown); - } - else - { - response = _random.Pick(component.ConversationTree.Attention); - } - } - - QueueResponse(uid, response, component); - } - } -} - diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.Context.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.Context.cs index e7af2c9107..ce10d4f5d5 100644 --- a/Content.Server/NPC/Systems/NPCSteeringSystem.Context.cs +++ b/Content.Server/NPC/Systems/NPCSteeringSystem.Context.cs @@ -58,7 +58,7 @@ private bool IsFreeSpace( // TODO: Ideally for "FreeSpace" we check all entities on the tile and build flags dynamically (pathfinder refactor in future). var ents = _entSetPool.Get(); - _lookup.GetLocalEntitiesIntersecting(node.GraphUid, node.ChunkOrigin, ents, flags: LookupFlags.Static); + _lookup.GetLocalEntitiesIntersecting(node.GraphUid, node.Box.Enlarged(-0.04f), ents, flags: LookupFlags.Static); var result = true; if (ents.Count > 0) @@ -158,42 +158,42 @@ private bool TrySeek( } } + // Check if mapids match. + var targetMap = targetCoordinates.ToMap(EntityManager, _transform); + var ourMap = ourCoordinates.ToMap(EntityManager, _transform); + + if (targetMap.MapId != ourMap.MapId) + { + steering.Status = SteeringStatus.NoPath; + return false; + } + + var direction = targetMap.Position - ourMap.Position; + // Need to be pretty close if it's just a node to make sure LOS for door bashes or the likes. - float arrivalDistance; + bool arrived; if (targetCoordinates.Equals(steering.Coordinates)) { // What's our tolerance for arrival. // If it's a pathfinding node it might be different to the destination. - arrivalDistance = steering.Range; + arrived = direction.Length() <= steering.Range; } // If next node is a free tile then get within its bounds. // This is to avoid popping it too early else if (steering.CurrentPath.TryPeek(out var node) && IsFreeSpace(uid, steering, node)) { - arrivalDistance = MathF.Max(0.05f, MathF.Min(node.Box.Width / 2f, node.Box.Height / 2f) - 0.05f); + arrived = node.Box.Contains(ourCoordinates.Position); } // Try getting into blocked range I guess? // TODO: Consider melee range or the likes. else { - arrivalDistance = SharedInteractionSystem.InteractionRange - 0.05f; + arrived = direction.Length() <= SharedInteractionSystem.InteractionRange - 0.05f; } - // Check if mapids match. - var targetMap = targetCoordinates.ToMap(EntityManager, _transform); - var ourMap = ourCoordinates.ToMap(EntityManager, _transform); - - if (targetMap.MapId != ourMap.MapId) - { - steering.Status = SteeringStatus.NoPath; - return false; - } - - var direction = targetMap.Position - ourMap.Position; - // Are we in range - if (direction.Length() <= arrivalDistance) + if (arrived) { // Node needs some kind of special handling like access or smashing. if (steering.CurrentPath.TryPeek(out var node) && !IsFreeSpace(uid, steering, node)) diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs index 70d1e89bc4..3bc4eae9e4 100644 --- a/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs +++ b/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs @@ -6,6 +6,7 @@ using Content.Shared.DoAfter; using Content.Shared.Doors.Components; using Content.Shared.NPC; +using Robust.Shared.Map.Components; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Utility; @@ -201,7 +202,7 @@ private SteeringObstacleStatus TryHandleFlags(EntityUid uid, NPCSteeringComponen private void GetObstacleEntities(PathPoly poly, int mask, int layer, List ents) { // TODO: Can probably re-use this from pathfinding or something - if (!_mapManager.TryGetGrid(poly.GraphUid, out var grid)) + if (!TryComp(poly.GraphUid, out var grid)) { return; } diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.cs index c00375d648..447792b6ff 100644 --- a/Content.Server/NPC/Systems/NPCSteeringSystem.cs +++ b/Content.Server/NPC/Systems/NPCSteeringSystem.cs @@ -45,7 +45,6 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem [Dependency] private readonly IAdminManager _admin = default!; [Dependency] private readonly IConfigurationManager _configManager = default!; [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ClimbSystem _climb = default!; [Dependency] private readonly DoAfterSystem _doAfter = default!; @@ -273,6 +272,8 @@ private void SetDirection(InputMoverComponent component, NPCSteeringComponent st if (clear && value.Equals(Vector2.Zero)) { steering.CurrentPath.Clear(); + Array.Clear(steering.Interest); + Array.Clear(steering.Danger); } component.CurTickSprintMovement = value; diff --git a/Content.Server/Ninja/Systems/StunProviderSystem.cs b/Content.Server/Ninja/Systems/StunProviderSystem.cs index 636037060a..970ca78e2c 100644 --- a/Content.Server/Ninja/Systems/StunProviderSystem.cs +++ b/Content.Server/Ninja/Systems/StunProviderSystem.cs @@ -1,14 +1,11 @@ using Content.Server.Ninja.Events; using Content.Server.Power.EntitySystems; using Content.Shared.Damage; -using Content.Shared.Damage.Prototypes; using Content.Shared.Interaction; using Content.Shared.Ninja.Components; using Content.Shared.Ninja.Systems; using Content.Shared.Popups; using Content.Shared.Stunnable; -using Content.Shared.Whitelist; -using Robust.Shared.Audio; using Robust.Shared.Prototypes; using Robust.Shared.Audio.Systems; using Robust.Shared.Timing; @@ -23,7 +20,6 @@ public sealed class StunProviderSystem : SharedStunProviderSystem [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; diff --git a/Content.Server/NodeContainer/EntitySystems/NodeGroupSystem.cs b/Content.Server/NodeContainer/EntitySystems/NodeGroupSystem.cs index 5e31bd6872..2296de2eb6 100644 --- a/Content.Server/NodeContainer/EntitySystems/NodeGroupSystem.cs +++ b/Content.Server/NodeContainer/EntitySystems/NodeGroupSystem.cs @@ -8,7 +8,7 @@ using JetBrains.Annotations; using Robust.Server.Player; using Robust.Shared.Enums; -using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Player; using Robust.Shared.Utility; @@ -25,7 +25,6 @@ public sealed class NodeGroupSystem : EntitySystem [Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly INodeGroupFactory _nodeGroupFactory = default!; [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; private readonly List _visDeletes = new(); private readonly List _visSends = new(); @@ -343,7 +342,7 @@ private List FloodFillNode(Node rootNode) private IEnumerable GetCompatibleNodes(Node node, EntityQuery xformQuery, EntityQuery nodeQuery) { var xform = xformQuery.GetComponent(node.Owner); - _mapManager.TryGetGrid(xform.GridUid, out var grid); + TryComp(xform.GridUid, out var grid); if (!node.Connectable(EntityManager, xform)) yield break; diff --git a/Content.Server/Nuke/NukeSystem.cs b/Content.Server/Nuke/NukeSystem.cs index d6767cd2de..ad153dcf6b 100644 --- a/Content.Server/Nuke/NukeSystem.cs +++ b/Content.Server/Nuke/NukeSystem.cs @@ -18,6 +18,7 @@ using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Player; using Robust.Shared.Random; @@ -28,7 +29,6 @@ public sealed class NukeSystem : EntitySystem [Dependency] private readonly AlertLevelSystem _alertLevel = default!; [Dependency] private readonly ChatSystem _chatSystem = default!; [Dependency] private readonly ExplosionSystem _explosions = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; @@ -175,7 +175,7 @@ private async void OnAnchorButtonPressed(EntityUid uid, NukeComponent component, } else { - if (!_mapManager.TryGetGrid(xform.GridUid, out var grid)) + if (!TryComp(xform.GridUid, out var grid)) return; var worldPos = _transform.GetWorldPosition(xform); diff --git a/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs b/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs index f8d781bcff..a28679ddbc 100644 --- a/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs @@ -1,12 +1,11 @@ using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Explosion.Components; using Content.Server.Explosion.EntitySystems; using Content.Server.Fluids.EntitySystems; using Content.Server.Nutrition.Components; using Content.Server.Popups; using Content.Shared.Containers.ItemSlots; using Content.Shared.Explosion.Components; -using Content.Shared.Interaction; +using Content.Shared.Nutrition; using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Rejuvenate; @@ -32,7 +31,10 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnInteractUsing); + // activate BEFORE entity is deleted and trash is spawned + SubscribeLocalEvent(OnConsume, before: [typeof(FoodSystem)]); + SubscribeLocalEvent(OnSlice); + SubscribeLocalEvent(OnRejuvenate); } @@ -56,7 +58,12 @@ protected override void SplattedCreamPie(EntityUid uid, CreamPieComponent creamP EntityManager.QueueDeleteEntity(uid); } - private void OnInteractUsing(Entity entity, ref InteractUsingEvent args) + private void OnConsume(Entity entity, ref ConsumeDoAfterEvent args) + { + ActivatePayload(entity); + } + + private void OnSlice(Entity entity, ref SliceFoodEvent args) { ActivatePayload(entity); } diff --git a/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs b/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs index e92b046f2e..abb6f393ac 100644 --- a/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs @@ -1,11 +1,11 @@ using Content.Server.Nutrition; // DeltaV using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Nutrition.Components; +using Content.Shared.Nutrition; using Content.Shared.Nutrition.Components; using Content.Shared.Chemistry.Components; using Content.Shared.Examine; using Content.Shared.FixedPoint; -using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Robust.Server.GameObjects; using Robust.Shared.Audio; @@ -19,7 +19,6 @@ public sealed class SliceableFoodSystem : EntitySystem [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly TransformSystem _xformSystem = default!; public override void Initialize() @@ -67,6 +66,8 @@ private bool TrySliceFood(EntityUid uid, EntityUid user, EntityUid usedItem, FillSlice(sliceUid, lostSolution); _audio.PlayPvs(component.Sound, transform.Coordinates, AudioParams.Default.WithVolume(-2)); + var ev = new SliceFoodEvent(uid, user, usedItem); + RaiseLocalEvent(ev); // Decrease size of item based on count - Could implement in the future // Bug with this currently is the size in a container is not updated diff --git a/Content.Server/Nyanotrasen/Carrying/CarryingSystem.cs b/Content.Server/Nyanotrasen/Carrying/CarryingSystem.cs index 103731b1b0..ff4c097080 100644 --- a/Content.Server/Nyanotrasen/Carrying/CarryingSystem.cs +++ b/Content.Server/Nyanotrasen/Carrying/CarryingSystem.cs @@ -2,12 +2,10 @@ using System.Threading; using Content.Server.DoAfter; using Content.Server.Body.Systems; -using Content.Server.Hands.Systems; using Content.Server.Resist; using Content.Server.Popups; using Content.Server.Inventory; using Content.Server.Nyanotrasen.Item.PseudoItem; -using Content.Shared.Climbing; // Shared instead of Server using Content.Shared.Mobs; using Content.Shared.DoAfter; using Content.Shared.Buckle.Components; @@ -20,14 +18,14 @@ using Content.Shared.Carrying; using Content.Shared.Movement.Events; using Content.Shared.Movement.Systems; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; using Content.Shared.Standing; using Content.Shared.ActionBlocker; using Content.Shared.Inventory.VirtualItem; using Content.Shared.Item; using Content.Shared.Throwing; -using Content.Shared.Physics.Pull; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Events; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Mobs.Systems; using Content.Shared.Nyanotrasen.Item.PseudoItem; using Content.Shared.Storage; @@ -38,17 +36,16 @@ namespace Content.Server.Carrying { public sealed class CarryingSystem : EntitySystem { - [Dependency] private readonly VirtualItemSystem _virtualItemSystem = default!; + [Dependency] private readonly VirtualItemSystem _virtualItemSystem = default!; [Dependency] private readonly CarryingSlowdownSystem _slowdown = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly StandingStateSystem _standingState = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; - [Dependency] private readonly SharedPullingSystem _pullingSystem = default!; + [Dependency] private readonly PullingSystem _pullingSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly EscapeInventorySystem _escapeInventorySystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!; - [Dependency] private readonly RespiratorSystem _respirator = default!; [Dependency] private readonly PseudoItemSystem _pseudoItem = default!; public override void Initialize() @@ -280,8 +277,8 @@ private void StartCarryDoAfter(EntityUid carrier, EntityUid carried, CarriableCo private void Carry(EntityUid carrier, EntityUid carried) { - if (TryComp(carried, out var pullable)) - _pullingSystem.TryStopPull(pullable); + if (TryComp(carried, out var pullable)) + _pullingSystem.TryStopPull(carried, pullable); Transform(carrier).AttachToGridOrMap(); Transform(carried).AttachToGridOrMap(); diff --git a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs index aa6de572ce..80c38f4630 100644 --- a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs +++ b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs @@ -86,7 +86,7 @@ public sealed partial class DeepFryerSystem : SharedDeepfryerSystem private static readonly string MobFlavorMeat = "meaty"; private static readonly AudioParams - AudioParamsInsertRemove = new(0.5f, 1f, "Master", 5f, 1.5f, 1f, false, 0f, 0.2f); + AudioParamsInsertRemove = new(0.5f, 1f, 5f, 1.5f, 1f, false, 0f, 0.2f); private ISawmill _sawmill = default!; diff --git a/Content.Server/Nyanotrasen/Research/SophicScribe/SophicScribeSystem.cs b/Content.Server/Nyanotrasen/Research/SophicScribe/SophicScribeSystem.cs index ba5ff0a056..b1a6c1e9de 100644 --- a/Content.Server/Nyanotrasen/Research/SophicScribe/SophicScribeSystem.cs +++ b/Content.Server/Nyanotrasen/Research/SophicScribe/SophicScribeSystem.cs @@ -1,8 +1,5 @@ using Content.Server.Psionics.Abilities; using Content.Server.Chat.Systems; -using Content.Server.NPC.Events; -using Content.Server.NPC.Systems; -using Content.Server.NPC.Prototypes; using Content.Server.Radio.Components; using Content.Server.Radio.EntitySystems; using Content.Server.StationEvents.Events; @@ -21,8 +18,6 @@ public sealed partial class SophicScribeSystem : EntitySystem [Dependency] private readonly RadioSystem _radioSystem = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly NPCConversationSystem _conversationSystem = default!; - protected ISawmill Sawmill = default!; public override void Update(float frameTime) { @@ -56,32 +51,6 @@ public override void Initialize() SubscribeLocalEvent(OnInteractHand); SubscribeLocalEvent(OnGlimmerEventEnded); - SubscribeLocalEvent(OnGetGlimmer); - } - - private void OnGetGlimmer(EntityUid uid, SophicScribeComponent component, NPCConversationGetGlimmerEvent args) - { - if (args.Text == null) - { - Sawmill.Error($"{uid} heard a glimmer reading prompt but has no text for it"); - return; - } - - var tier = _glimmerSystem.GetGlimmerTier() switch - { - GlimmerTier.Minimal => Loc.GetString("glimmer-reading-minimal"), - GlimmerTier.Low => Loc.GetString("glimmer-reading-low"), - GlimmerTier.Moderate => Loc.GetString("glimmer-reading-moderate"), - GlimmerTier.High => Loc.GetString("glimmer-reading-high"), - GlimmerTier.Dangerous => Loc.GetString("glimmer-reading-dangerous"), - _ => Loc.GetString("glimmer-reading-critical"), - }; - - var glimmerReadingText = Loc.GetString(args.Text, - ("glimmer", (int) Math.Round(_glimmerSystem.GlimmerOutput)), ("tier", tier)); - - var response = new NPCResponse(glimmerReadingText); - _conversationSystem.QueueResponse(uid, response); } private void OnInteractHand(EntityUid uid, SophicScribeComponent component, InteractHandEvent args) @@ -114,9 +83,4 @@ private void OnGlimmerEventEnded(GlimmerEventEndedEvent args) _radioSystem.SendRadioMessage(speaker, message, channel, speaker); } } - public sealed partial class NPCConversationGetGlimmerEvent : NPCConversationEvent - { - [DataField] - public string? Text; - } } diff --git a/Content.Server/Objectives/Systems/NinjaConditionsSystem.cs b/Content.Server/Objectives/Systems/NinjaConditionsSystem.cs index 2bd8538af1..888a365a5d 100644 --- a/Content.Server/Objectives/Systems/NinjaConditionsSystem.cs +++ b/Content.Server/Objectives/Systems/NinjaConditionsSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Objectives.Components; using Content.Server.Warps; using Content.Shared.Objectives.Components; -using Content.Shared.Mind; using Content.Shared.Ninja.Components; using Robust.Shared.Random; using Content.Server.Roles; @@ -16,7 +15,6 @@ public sealed class NinjaConditionsSystem : EntitySystem { [Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly NumberObjectiveSystem _number = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() diff --git a/Content.Server/Objectives/Systems/StealConditionSystem.cs b/Content.Server/Objectives/Systems/StealConditionSystem.cs index 02d4ee010b..0fe6f0947c 100644 --- a/Content.Server/Objectives/Systems/StealConditionSystem.cs +++ b/Content.Server/Objectives/Systems/StealConditionSystem.cs @@ -6,11 +6,10 @@ using Robust.Shared.Containers; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Content.Shared.Pulling.Components; -using Content.Shared.Objectives; using Content.Shared.Mind.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Components; +using Content.Shared.Movement.Pulling.Components; namespace Content.Server.Objectives.Systems; @@ -100,19 +99,19 @@ private float GetProgress(MindComponent mind, StealConditionComponent condition) var count = 0; //check pulling object - if (TryComp(mind.OwnedEntity, out var pull)) //TO DO: to make the code prettier? don't like the repetition + if (TryComp(mind.OwnedEntity, out var pull)) //TO DO: to make the code prettier? don't like the repetition { - var pullid = pull.Pulling; - if (pullid != null) + var pulledEntity = pull.Pulling; + if (pulledEntity != null) { // check if this is the item - if (CheckStealTarget(pullid.Value, condition)) count++; + if (CheckStealTarget(pulledEntity.Value, condition)) count++; //we don't check the inventories of sentient entity - if (!TryComp(pullid, out var pullMind)) + if (!HasComp(pulledEntity)) { // if it is a container check its contents - if (_containerQuery.TryGetComponent(pullid, out var containerManager)) + if (_containerQuery.TryGetComponent(pulledEntity, out var containerManager)) stack.Push(containerManager); } } diff --git a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.cs b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.cs index ddc7e2a083..e9b62bc4a8 100644 --- a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.cs +++ b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Chat.Managers; using Content.Server.Projectiles; using Robust.Shared.Physics.Systems; -using Robust.Shared.Map; using Robust.Shared.Timing; using Robust.Server.GameObjects; using Robust.Shared.Configuration; @@ -13,7 +12,6 @@ public sealed partial class ParticleAcceleratorSystem : EntitySystem { [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IChatManager _chat = default!; [Dependency] private readonly ProjectileSystem _projectileSystem = default!; diff --git a/Content.Server/Physics/Controllers/MoverController.cs b/Content.Server/Physics/Controllers/MoverController.cs index c362507f19..759b8ef29c 100644 --- a/Content.Server/Physics/Controllers/MoverController.cs +++ b/Content.Server/Physics/Controllers/MoverController.cs @@ -6,17 +6,16 @@ using Content.Shared.Movement.Systems; using Content.Shared.Shuttles.Components; using Content.Shared.Shuttles.Systems; -using Robust.Shared.Map; using Robust.Shared.Physics.Components; using Robust.Shared.Player; using DroneConsoleComponent = Content.Server.Shuttles.DroneConsoleComponent; using DependencyAttribute = Robust.Shared.IoC.DependencyAttribute; +using Robust.Shared.Map.Components; namespace Content.Server.Physics.Controllers { public sealed class MoverController : SharedMoverController { - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ThrusterSystem _thruster = default!; [Dependency] private readonly SharedTransformSystem _xformSystem = default!; @@ -276,7 +275,7 @@ private void HandleShuttleMovement(float frameTime) var gridId = xform.GridUid; // This tries to see if the grid is a shuttle and if the console should work. - if (!_mapManager.TryGetGrid(gridId, out var _) || + if (!TryComp(gridId, out var _) || !shuttleQuery.TryGetComponent(gridId, out var shuttleComponent) || !shuttleComponent.Enabled) continue; diff --git a/Content.Server/Physics/Controllers/PullController.cs b/Content.Server/Physics/Controllers/PullController.cs deleted file mode 100644 index 8f58f807aa..0000000000 --- a/Content.Server/Physics/Controllers/PullController.cs +++ /dev/null @@ -1,207 +0,0 @@ -using System.Numerics; -using Content.Shared.ActionBlocker; -using Content.Shared.Gravity; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; -using Content.Shared.Rotatable; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Controllers; - -namespace Content.Server.Physics.Controllers -{ - public sealed class PullController : VirtualController - { - // Parameterization for pulling: - // Speeds. Note that the speed is mass-independent (multiplied by mass). - // Instead, tuning to mass is done via the mass values below. - // Note that setting the speed too high results in overshoots (stabilized by drag, but bad) - private const float AccelModifierHigh = 15f; - private const float AccelModifierLow = 60.0f; - // High/low-mass marks. Curve is constant-lerp-constant, i.e. if you can even pull an item, - // you'll always get at least AccelModifierLow and no more than AccelModifierHigh. - private const float AccelModifierHighMass = 70.0f; // roundstart saltern emergency closet - private const float AccelModifierLowMass = 5.0f; // roundstart saltern emergency crowbar - // Used to control settling (turns off pulling). - private const float MaximumSettleVelocity = 0.1f; - private const float MaximumSettleDistance = 0.1f; - // Settle shutdown control. - // Mustn't be too massive, as that causes severe mispredicts *and can prevent it ever resolving*. - // Exists to bleed off "I pulled my crowbar" overshoots. - // Minimum velocity for shutdown to be necessary. This prevents stuff getting stuck b/c too much shutdown. - private const float SettleMinimumShutdownVelocity = 0.25f; - // Distance in which settle shutdown multiplier is at 0. It then scales upwards linearly with closer distances. - private const float SettleShutdownDistance = 1.0f; - // Velocity change of -LinearVelocity * frameTime * this - private const float SettleShutdownMultiplier = 20.0f; - - // How much you must move for the puller movement check to actually hit. - private const float MinimumMovementDistance = 0.005f; - - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; - [Dependency] private readonly SharedPullingSystem _pullableSystem = default!; - [Dependency] private readonly SharedGravitySystem _gravity = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; - - // TODO: Move this stuff to pullingsystem - /// - /// If distance between puller and pulled entity lower that this threshold, - /// pulled entity will not change its rotation. - /// Helps with small distance jittering - /// - private const float ThresholdRotDistance = 1; - - /// - /// If difference between puller and pulled angle lower that this threshold, - /// pulled entity will not change its rotation. - /// Helps with diagonal movement jittering - /// As of further adjustments, should divide cleanly into 90 degrees - /// - private const float ThresholdRotAngle = 22.5f; - - public override void Initialize() - { - UpdatesAfter.Add(typeof(MoverController)); - SubscribeLocalEvent(OnPullerMove); - - base.Initialize(); - } - - private void OnPullerMove(EntityUid uid, SharedPullerComponent component, ref MoveEvent args) - { - if (component.Pulling is not { } pullable || !TryComp(pullable, out var pullableComponent)) - return; - - UpdatePulledRotation(uid, pullable); - - if (args.NewPosition.EntityId == args.OldPosition.EntityId && - (args.NewPosition.Position - args.OldPosition.Position).LengthSquared() < MinimumMovementDistance * MinimumMovementDistance) - return; - - if (TryComp(pullable, out var physics)) - PhysicsSystem.WakeBody(pullable, body: physics); - - _pullableSystem.StopMoveTo(pullableComponent); - } - - private void UpdatePulledRotation(EntityUid puller, EntityUid pulled) - { - // TODO: update once ComponentReference works with directed event bus. - if (!TryComp(pulled, out RotatableComponent? rotatable)) - return; - - if (!rotatable.RotateWhilePulling) - return; - - var xforms = GetEntityQuery(); - var pulledXform = xforms.GetComponent(pulled); - var pullerXform = xforms.GetComponent(puller); - - var pullerData = TransformSystem.GetWorldPositionRotation(pullerXform, xforms); - var pulledData = TransformSystem.GetWorldPositionRotation(pulledXform, xforms); - - var dir = pullerData.WorldPosition - pulledData.WorldPosition; - if (dir.LengthSquared() > ThresholdRotDistance * ThresholdRotDistance) - { - var oldAngle = pulledData.WorldRotation; - var newAngle = Angle.FromWorldVec(dir); - - var diff = newAngle - oldAngle; - if (Math.Abs(diff.Degrees) > ThresholdRotAngle / 2f) - { - // Ok, so this bit is difficult because ideally it would look like it's snapping to sane angles. - // Otherwise PIANO DOOR STUCK! happens. - // But it also needs to work with station rotation / align to the local parent. - // So... - var baseRotation = pulledData.WorldRotation - pulledXform.LocalRotation; - var localRotation = newAngle - baseRotation; - var localRotationSnapped = Angle.FromDegrees(Math.Floor((localRotation.Degrees / ThresholdRotAngle) + 0.5f) * ThresholdRotAngle); - TransformSystem.SetLocalRotation(pulledXform, localRotationSnapped); - } - } - } - - public override void UpdateBeforeSolve(bool prediction, float frameTime) - { - base.UpdateBeforeSolve(prediction, frameTime); - - foreach (var pullable in _pullableSystem.Moving) - { - // There's a 1-frame delay between stopping moving something and it leaving the Moving set. - // This can include if leaving the Moving set due to not being pulled anymore, - // or due to being deleted. - - if (pullable.Deleted) - continue; - - if (pullable.MovingTo == null) - continue; - - if (pullable.Puller is not {Valid: true} puller) - continue; - - var pullableEnt = pullable.Owner; - var pullableXform = Transform(pullableEnt); - var pullerXform = Transform(puller); - - // Now that's over with... - - var pullerPosition = pullerXform.MapPosition; - var movingTo = pullable.MovingTo.Value.ToMap(EntityManager, _transform); - if (movingTo.MapId != pullerPosition.MapId) - { - _pullableSystem.StopMoveTo(pullable); - continue; - } - - if (!TryComp(pullableEnt, out var physics) || - physics.BodyType == BodyType.Static || - movingTo.MapId != pullableXform.MapID) - { - _pullableSystem.StopMoveTo(pullable); - continue; - } - - var movingPosition = movingTo.Position; - var ownerPosition = pullableXform.MapPosition.Position; - - var diff = movingPosition - ownerPosition; - var diffLength = diff.Length(); - - if (diffLength < MaximumSettleDistance && physics.LinearVelocity.Length() < MaximumSettleVelocity) - { - PhysicsSystem.SetLinearVelocity(pullableEnt, Vector2.Zero, body: physics); - _pullableSystem.StopMoveTo(pullable); - continue; - } - - var impulseModifierLerp = Math.Min(1.0f, Math.Max(0.0f, (physics.Mass - AccelModifierLowMass) / (AccelModifierHighMass - AccelModifierLowMass))); - var impulseModifier = MathHelper.Lerp(AccelModifierLow, AccelModifierHigh, impulseModifierLerp); - var multiplier = diffLength < 1 ? impulseModifier * diffLength : impulseModifier; - // Note the implication that the real rules of physics don't apply to pulling control. - var accel = diff.Normalized() * multiplier; - // Now for the part where velocity gets shutdown... - if (diffLength < SettleShutdownDistance && physics.LinearVelocity.Length() >= SettleMinimumShutdownVelocity) - { - // Shutdown velocity increases as we get closer to centre - var scaling = (SettleShutdownDistance - diffLength) / SettleShutdownDistance; - accel -= physics.LinearVelocity * SettleShutdownMultiplier * scaling; - } - - PhysicsSystem.WakeBody(pullableEnt, body: physics); - - var impulse = accel * physics.Mass * frameTime; - PhysicsSystem.ApplyLinearImpulse(pullableEnt, impulse, body: physics); - - // if the puller is weightless or can't move, then we apply the inverse impulse (Newton's third law). - // doing it under gravity produces an unsatisfying wiggling when pulling. - // If player can't move, assume they are on a chair and we need to prevent pull-moving. - if ((_gravity.IsWeightless(puller) && pullerXform.GridUid == null) || !_actionBlockerSystem.CanMove(puller)) - { - PhysicsSystem.WakeBody(puller); - PhysicsSystem.ApplyLinearImpulse(puller, -impulse); - } - } - } - } -} diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.cs index 66dc9dab99..5fe29dcd30 100644 --- a/Content.Server/Polymorph/Systems/PolymorphSystem.cs +++ b/Content.Server/Polymorph/Systems/PolymorphSystem.cs @@ -32,7 +32,6 @@ public sealed partial class PolymorphSystem : EntitySystem [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly ActionsSystem _actions = default!; - [Dependency] private readonly ActionContainerSystem _actionContainer = default!; [Dependency] private readonly AudioSystem _audio = default!; [Dependency] private readonly SharedBuckleSystem _buckle = default!; [Dependency] private readonly ContainerSystem _container = default!; diff --git a/Content.Server/Power/EntitySystems/CableSystem.Placer.cs b/Content.Server/Power/EntitySystems/CableSystem.Placer.cs index c5ca36c3a1..263d626ef5 100644 --- a/Content.Server/Power/EntitySystems/CableSystem.Placer.cs +++ b/Content.Server/Power/EntitySystems/CableSystem.Placer.cs @@ -4,6 +4,7 @@ using Content.Shared.Interaction; using Content.Shared.Maps; using Content.Shared.Stacks; +using Robust.Shared.Map.Components; namespace Content.Server.Power.EntitySystems; @@ -25,7 +26,7 @@ private void OnCablePlacerAfterInteract(Entity placer, ref if (component.CablePrototypeId == null) return; - if(!_mapManager.TryGetGrid(args.ClickLocation.GetGridUid(EntityManager), out var grid)) + if(!TryComp(args.ClickLocation.GetGridUid(EntityManager), out var grid)) return; var snapPos = grid.TileIndicesFor(args.ClickLocation); diff --git a/Content.Server/Power/EntitySystems/CableSystem.cs b/Content.Server/Power/EntitySystems/CableSystem.cs index dd478753be..62eb08d7cb 100644 --- a/Content.Server/Power/EntitySystems/CableSystem.cs +++ b/Content.Server/Power/EntitySystems/CableSystem.cs @@ -5,10 +5,7 @@ using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.Interaction; -using Content.Shared.Tools; -using Content.Shared.Tools.Components; using Robust.Shared.Map; -using System.Xml.Schema; using CableCuttingFinishedEvent = Content.Shared.Tools.Systems.CableCuttingFinishedEvent; using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem; @@ -16,13 +13,11 @@ namespace Content.Server.Power.EntitySystems; public sealed partial class CableSystem : EntitySystem { - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ITileDefinitionManager _tileManager = default!; [Dependency] private readonly SharedToolSystem _toolSystem = default!; [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly ElectrocutionSystem _electrocutionSystem = default!; [Dependency] private readonly IAdminLogManager _adminLogs = default!; - [Dependency] private readonly PowerMonitoringConsoleSystem _powerMonitoringSystem = default!; public override void Initialize() { diff --git a/Content.Server/Power/EntitySystems/CableVisSystem.cs b/Content.Server/Power/EntitySystems/CableVisSystem.cs index fcf0ae3d58..ec08523d44 100644 --- a/Content.Server/Power/EntitySystems/CableVisSystem.cs +++ b/Content.Server/Power/EntitySystems/CableVisSystem.cs @@ -4,15 +4,13 @@ using Content.Server.Power.Nodes; using Content.Shared.Wires; using JetBrains.Annotations; -using Robust.Server.GameObjects; -using Robust.Shared.Map; +using Robust.Shared.Map.Components; namespace Content.Server.Power.EntitySystems { [UsedImplicitly] public sealed class CableVisSystem : EntitySystem { - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; @@ -32,7 +30,7 @@ private void UpdateAppearance(EntityUid uid, CableVisComponent cableVis, ref Nod return; var transform = Transform(uid); - if (!_mapManager.TryGetGrid(transform.GridUid, out var grid)) + if (!TryComp(transform.GridUid, out var grid)) return; var mask = WireVisDirFlags.None; diff --git a/Content.Server/Power/EntitySystems/ExtensionCableSystem.cs b/Content.Server/Power/EntitySystems/ExtensionCableSystem.cs index acfb8ff87b..85e553031f 100644 --- a/Content.Server/Power/EntitySystems/ExtensionCableSystem.cs +++ b/Content.Server/Power/EntitySystems/ExtensionCableSystem.cs @@ -1,6 +1,5 @@ using System.Diagnostics.CodeAnalysis; using Content.Server.Power.Components; -using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; @@ -9,8 +8,6 @@ namespace Content.Server.Power.EntitySystems { public sealed class ExtensionCableSystem : EntitySystem { - [Dependency] private readonly IMapManager _mapManager = default!; - public override void Initialize() { base.Initialize(); diff --git a/Content.Server/Power/Generator/PortableGeneratorSystem.cs b/Content.Server/Power/Generator/PortableGeneratorSystem.cs index e8e9c5b45e..e7dfa35178 100644 --- a/Content.Server/Power/Generator/PortableGeneratorSystem.cs +++ b/Content.Server/Power/Generator/PortableGeneratorSystem.cs @@ -1,5 +1,4 @@ using Content.Server.DoAfter; -using Content.Server.NodeContainer.NodeGroups; using Content.Server.Popups; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -28,7 +27,6 @@ public sealed class PortableGeneratorSystem : SharedPortableGeneratorSystem [Dependency] private readonly GeneratorSystem _generator = default!; [Dependency] private readonly PowerSwitchableSystem _switchable = default!; [Dependency] private readonly ActiveGeneratorRevvingSystem _revving = default!; - [Dependency] private readonly PowerNetSystem _powerNet = default!; public override void Initialize() { diff --git a/Content.Server/Psionics/Abilities/DispelPowerSystem.cs b/Content.Server/Psionics/Abilities/DispelPowerSystem.cs index 73c6f5d339..3d7d5c20c7 100644 --- a/Content.Server/Psionics/Abilities/DispelPowerSystem.cs +++ b/Content.Server/Psionics/Abilities/DispelPowerSystem.cs @@ -32,7 +32,7 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); + SubscribeLocalEvent(OnPowerUsed); SubscribeLocalEvent(OnDispelled); SubscribeLocalEvent(OnDmgDispelled); @@ -44,18 +44,17 @@ public override void Initialize() private void OnInit(EntityUid uid, DispelPowerComponent component, ComponentInit args) { - _actions.AddAction(uid, ref component.DispelActionEntity, component.DispelActionId ); - _actions.TryGetActionData( component.DispelActionEntity, out var actionData ); + EnsureComp(uid, out var psionic); + _actions.AddAction(uid, ref component.DispelActionEntity, component.DispelActionId); + _actions.TryGetActionData(component.DispelActionEntity, out var actionData); if (actionData is { UseDelay: not null }) - _actions.StartUseDelay(component.DispelActionEntity); - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Add(component); - psionic.PsychicFeedback.Add(component.DispelFeedback); - //It's fully intended that Dispel doesn't increase Amplification, and instead heavily spikes Dampening - //Antimage archetype. - psionic.Dampening += 1f; - } + _actions.SetCooldown(component.DispelActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); + + psionic.ActivePowers.Add(component); + psionic.PsychicFeedback.Add(component.DispelFeedback); + //It's fully intended that Dispel doesn't increase Amplification, and instead heavily spikes Dampening + //Antimage archetype. + psionic.Dampening += 1f; } private void OnShutdown(EntityUid uid, DispelPowerComponent component, ComponentShutdown args) @@ -70,11 +69,9 @@ private void OnShutdown(EntityUid uid, DispelPowerComponent component, Component } } - private void OnPowerUsed(DispelPowerActionEvent args) + private void OnPowerUsed(EntityUid uid, DispelPowerComponent component, DispelPowerActionEvent args) { - if (HasComp(args.Target) || HasComp(args.Performer)) - return; - if (!TryComp(args.Performer, out var psionic) || !HasComp(args.Target)) + if (!_psionics.CheckCanTargetCast(uid, args.Target, out var psionic)) return; var ev = new DispelledEvent(); @@ -82,6 +79,9 @@ private void OnPowerUsed(DispelPowerActionEvent args) if (ev.Handled) { + _actions.TryGetActionData(component.DispelActionEntity, out var actionData); + if (actionData is { UseDelay: not null }) + _actions.SetCooldown(component.DispelActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); args.Handled = true; _psionics.LogPowerUsed(args.Performer, "dispel", psionic, 1, 1, true); diff --git a/Content.Server/Psionics/Abilities/MetapsionicPowerSystem.cs b/Content.Server/Psionics/Abilities/MetapsionicPowerSystem.cs index 8c72a02737..a1b40b9232 100644 --- a/Content.Server/Psionics/Abilities/MetapsionicPowerSystem.cs +++ b/Content.Server/Psionics/Abilities/MetapsionicPowerSystem.cs @@ -33,32 +33,30 @@ public override void Initialize() private void OnInit(EntityUid uid, MetapsionicPowerComponent component, ComponentInit args) { + EnsureComp(uid, out var psionic); if (!TryComp(uid, out ActionsComponent? comp)) return; _actions.AddAction(uid, ref component.ActionWideMetapsionicEntity, component.ActionWideMetapsionic, component: comp); _actions.AddAction(uid, ref component.ActionFocusedMetapsionicEntity, component.ActionFocusedMetapsionic, component: comp); - _actions.TryGetActionData(component.ActionWideMetapsionicEntity, out var actionData); - if (actionData is { UseDelay: not null }) - { - _actions.StartUseDelay(component.ActionWideMetapsionicEntity); - _actions.StartUseDelay(component.ActionFocusedMetapsionicEntity); - } - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Add(component); - psionic.PsychicFeedback.Add(component.MetapsionicFeedback); - psionic.Amplification += 0.1f; - psionic.Dampening += 0.5f; - } + UpdateActions(uid, component, psionic); + psionic.ActivePowers.Add(component); + psionic.PsychicFeedback.Add(component.MetapsionicFeedback); + psionic.Amplification += 0.1f; + psionic.Dampening += 0.5f; } - private void UpdateActions(EntityUid uid, MetapsionicPowerComponent? component = null) + private void UpdateActions(EntityUid uid, MetapsionicPowerComponent? component = null, PsionicComponent? psionic = null) { - if (!Resolve(uid, ref component)) + if (!Resolve(uid, ref component) || !Resolve(uid, ref psionic) + || !_actions.TryGetActionData(component.ActionWideMetapsionicEntity, out var actionData)) return; - _actions.StartUseDelay(component.ActionWideMetapsionicEntity); - _actions.StartUseDelay(component.ActionFocusedMetapsionicEntity); + + if (actionData is { UseDelay: not null }) + { + _actions.SetCooldown(component.ActionWideMetapsionicEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); + _actions.SetCooldown(component.ActionFocusedMetapsionicEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); + } } private void OnShutdown(EntityUid uid, MetapsionicPowerComponent component, ComponentShutdown args) @@ -95,7 +93,7 @@ private void OnWidePowerUsed(EntityUid uid, MetapsionicPowerComponent component, } _popups.PopupEntity(Loc.GetString("metapsionic-pulse-failure"), uid, uid, PopupType.Large); _psionics.LogPowerUsed(uid, "metapsionic pulse", psionic, 2, 4); - UpdateActions(uid, component); + UpdateActions(uid, component, psionic); args.Handled = true; } @@ -128,7 +126,7 @@ private void OnFocusedPowerUsed(FocusedMetapsionicPowerActionEvent args) _psionics.LogPowerUsed(args.Performer, "focused metapsionic pulse", psionic, 3, 6); args.Handled = true; - UpdateActions(args.Performer, component); + UpdateActions(args.Performer, component, psionic); } private void OnDoAfter(EntityUid uid, MetapsionicPowerComponent component, FocusedMetapsionicDoAfterEvent args) diff --git a/Content.Server/Psionics/Abilities/MindSwapPowerSystem.cs b/Content.Server/Psionics/Abilities/MindSwapPowerSystem.cs index 25f0434c79..b4129581ec 100644 --- a/Content.Server/Psionics/Abilities/MindSwapPowerSystem.cs +++ b/Content.Server/Psionics/Abilities/MindSwapPowerSystem.cs @@ -29,7 +29,7 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); + SubscribeLocalEvent(OnPowerUsed); SubscribeLocalEvent(OnPowerReturned); SubscribeLocalEvent(OnDispelled); SubscribeLocalEvent(OnMobStateChanged); @@ -41,16 +41,15 @@ public override void Initialize() private void OnInit(EntityUid uid, MindSwapPowerComponent component, ComponentInit args) { + EnsureComp(uid, out var psionic); _actions.AddAction(uid, ref component.MindSwapActionEntity, component.MindSwapActionId); - _actions.TryGetActionData( component.MindSwapActionEntity, out var actionData); + _actions.TryGetActionData(component.MindSwapActionEntity, out var actionData); if (actionData is { UseDelay: not null }) - _actions.StartUseDelay(component.MindSwapActionEntity); - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Add(component); - psionic.PsychicFeedback.Add(component.MindSwapFeedback); - psionic.Amplification += 1f; - } + _actions.SetCooldown(component.MindSwapActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); + + psionic.ActivePowers.Add(component); + psionic.PsychicFeedback.Add(component.MindSwapFeedback); + psionic.Amplification += 1f; } private void OnShutdown(EntityUid uid, MindSwapPowerComponent component, ComponentShutdown args) @@ -64,17 +63,16 @@ private void OnShutdown(EntityUid uid, MindSwapPowerComponent component, Compone } } - private void OnPowerUsed(MindSwapPowerActionEvent args) + private void OnPowerUsed(EntityUid uid, MindSwapPowerComponent component, MindSwapPowerActionEvent args) { - if (!(TryComp(args.Target, out var damageable) && damageable.DamageContainerID == "Biological")) + if (!(TryComp(args.Target, out var damageable) && damageable.DamageContainerID == "Biological") + || !_psionics.CheckCanTargetCast(uid, args.Target, out var psionic)) return; - if (HasComp(args.Target)) - return; - - if (!TryComp(args.Performer, out var psionic)) - return; + _actions.TryGetActionData(component.MindSwapActionEntity, out var actionData); + if (actionData is { UseDelay: not null }) + _actions.SetCooldown(component.MindSwapActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); Swap(args.Performer, args.Target); @@ -159,6 +157,8 @@ private void OnSwapInit(EntityUid uid, MindSwappedComponent component, Component { psionic.ActivePowers.Add(component); psionic.PsychicFeedback.Add(component.MindSwappedFeedback); + if (actionData is { UseDelay: not null }) + _actions.SetCooldown(component.MindSwapReturnActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); } } diff --git a/Content.Server/Psionics/Abilities/NoosphericZapPowerSystem.cs b/Content.Server/Psionics/Abilities/NoosphericZapPowerSystem.cs index ffadc61c19..a1776acbd3 100644 --- a/Content.Server/Psionics/Abilities/NoosphericZapPowerSystem.cs +++ b/Content.Server/Psionics/Abilities/NoosphericZapPowerSystem.cs @@ -23,21 +23,20 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); + SubscribeLocalEvent(OnPowerUsed); } private void OnInit(EntityUid uid, NoosphericZapPowerComponent component, ComponentInit args) { + EnsureComp(uid, out var psionic); _actions.AddAction(uid, ref component.NoosphericZapActionEntity, component.NoosphericZapActionId ); - _actions.TryGetActionData( component.NoosphericZapActionEntity, out var actionData ); + _actions.TryGetActionData(component.NoosphericZapActionEntity, out var actionData); if (actionData is { UseDelay: not null }) - _actions.StartUseDelay(component.NoosphericZapActionEntity); - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Add(component); - psionic.PsychicFeedback.Add(component.NoosphericZapFeedback); - psionic.Amplification += 1f; - } + _actions.SetCooldown(component.NoosphericZapActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); + + psionic.ActivePowers.Add(component); + psionic.PsychicFeedback.Add(component.NoosphericZapFeedback); + psionic.Amplification += 1f; } private void OnShutdown(EntityUid uid, NoosphericZapPowerComponent component, ComponentShutdown args) @@ -51,13 +50,14 @@ private void OnShutdown(EntityUid uid, NoosphericZapPowerComponent component, Co } } - private void OnPowerUsed(NoosphericZapPowerActionEvent args) + private void OnPowerUsed(EntityUid uid, NoosphericZapPowerComponent component, NoosphericZapPowerActionEvent args) { - if (!TryComp(args.Performer, out var psionic)) - return; - - if (!HasComp(args.Performer)) + if (_psionics.CheckCanTargetCast(uid, args.Target, out var psionic)) { + + _actions.TryGetActionData(component.NoosphericZapActionEntity, out var actionData); + if (actionData is { UseDelay: not null }) + _actions.SetCooldown(component.NoosphericZapActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); _beam.TryCreateBeam(args.Performer, args.Target, "LightningNoospheric"); _stunSystem.TryParalyze(args.Target, TimeSpan.FromSeconds(1 * psionic.Amplification), false); diff --git a/Content.Server/Psionics/Abilities/PsionicRegenerationPowerSystem.cs b/Content.Server/Psionics/Abilities/PsionicRegenerationPowerSystem.cs index 15fc092ebc..f675cc6ed8 100644 --- a/Content.Server/Psionics/Abilities/PsionicRegenerationPowerSystem.cs +++ b/Content.Server/Psionics/Abilities/PsionicRegenerationPowerSystem.cs @@ -39,17 +39,16 @@ public override void Initialize() private void OnInit(EntityUid uid, PsionicRegenerationPowerComponent component, ComponentInit args) { - _actions.AddAction(uid, ref component.PsionicRegenerationActionEntity, component.PsionicRegenerationActionId ); - _actions.TryGetActionData( component.PsionicRegenerationActionEntity, out var actionData ); + EnsureComp(uid, out var psionic); + _actions.AddAction(uid, ref component.PsionicRegenerationActionEntity, component.PsionicRegenerationActionId); + _actions.TryGetActionData(component.PsionicRegenerationActionEntity, out var actionData); if (actionData is { UseDelay: not null }) - _actions.StartUseDelay(component.PsionicRegenerationActionEntity); - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Add(component); - psionic.PsychicFeedback.Add(component.RegenerationFeedback); - psionic.Amplification += 0.5f; - psionic.Dampening += 0.5f; - } + _actions.SetCooldown(component.PsionicRegenerationActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); + + psionic.ActivePowers.Add(component); + psionic.PsychicFeedback.Add(component.RegenerationFeedback); + psionic.Amplification += 0.5f; + psionic.Dampening += 0.5f; } private void OnPowerUsed(EntityUid uid, PsionicRegenerationPowerComponent component, PsionicRegenerationPowerActionEvent args) @@ -66,6 +65,9 @@ private void OnPowerUsed(EntityUid uid, PsionicRegenerationPowerComponent compon if (actionData != null && actionData.Cooldown.HasValue && actionData.Cooldown.Value.End > curTime) return; + if (actionData is { UseDelay: not null }) + _actions.SetCooldown(component.PsionicRegenerationActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); + _doAfterSystem.TryStartDoAfter(doAfterArgs, out var doAfterId); component.DoAfter = doAfterId; @@ -116,7 +118,7 @@ private void OnMobStateChangedEvent(EntityUid uid, PsionicRegenerationPowerCompo _psionics.LogPowerUsed(uid, "psionic regeneration", psionic, 10, 20); - _actions.StartUseDelay(component.PsionicRegenerationActionEntity); + _actions.SetCooldown(component.PsionicRegenerationActionEntity, 2 * (actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification))); } } } diff --git a/Content.Server/Psionics/Abilities/PyrokinesisPowerSystem.cs b/Content.Server/Psionics/Abilities/PyrokinesisPowerSystem.cs index f3e2cc69fd..62e0c3ce56 100644 --- a/Content.Server/Psionics/Abilities/PyrokinesisPowerSystem.cs +++ b/Content.Server/Psionics/Abilities/PyrokinesisPowerSystem.cs @@ -4,10 +4,9 @@ using Content.Shared.Psionics.Glimmer; using Content.Server.Atmos.Components; using Content.Server.Weapons.Ranged.Systems; -using Robust.Server.GameObjects; using Content.Shared.Actions.Events; using Content.Server.Explosion.Components; -using Robust.Server.Audio; +using Robust.Shared.Audio.Systems; using Robust.Shared.Timing; using Content.Shared.Popups; using Content.Shared.Psionics.Events; @@ -17,13 +16,13 @@ namespace Content.Server.Psionics.Abilities { public sealed class PyrokinesisPowerSystem : EntitySystem { - [Dependency] private readonly TransformSystem _xform = default!; + [Dependency] private readonly SharedTransformSystem _xform = default!; [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; [Dependency] private readonly GunSystem _gunSystem = default!; [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly AudioSystem _audioSystem = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; public override void Initialize() @@ -38,28 +37,26 @@ public override void Initialize() private void OnInit(EntityUid uid, PyrokinesisPowerComponent component, ComponentInit args) { + EnsureComp(uid, out var psionic); _actions.AddAction(uid, ref component.PyrokinesisPrechargeActionEntity, component.PyrokinesisPrechargeActionId); _actions.TryGetActionData(component.PyrokinesisPrechargeActionEntity, out var actionData); if (actionData is { UseDelay: not null }) - _actions.StartUseDelay(component.PyrokinesisPrechargeActionEntity); - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Add(component); - psionic.PsychicFeedback.Add(component.PyrokinesisFeedback); - psionic.Amplification += 1f; - } + _actions.SetCooldown(component.PyrokinesisPrechargeActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); + + psionic.ActivePowers.Add(component); + psionic.PsychicFeedback.Add(component.PyrokinesisFeedback); + psionic.Amplification += 1f; } private void OnPrecharge(PyrokinesisPrechargeActionEvent args) { - if (!HasComp(args.Performer) - && TryComp(args.Performer, out var psionic) + if (_psionics.CheckCanSelfCast(args.Performer, out var psionic) && TryComp(args.Performer, out var pyroComp)) { _actions.AddAction(args.Performer, ref pyroComp.PyrokinesisActionEntity, pyroComp.PyrokinesisActionId); _actions.TryGetActionData(pyroComp.PyrokinesisActionEntity, out var actionData); if (actionData is { UseDelay: not null }) - _actions.StartUseDelay(pyroComp.PyrokinesisActionEntity); + _actions.SetCooldown(pyroComp.PyrokinesisActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); _actions.TryGetActionData(pyroComp.PyrokinesisPrechargeActionEntity, out var prechargeData); if (prechargeData is { UseDelay: not null }) _actions.StartUseDelay(pyroComp.PyrokinesisPrechargeActionEntity); @@ -100,15 +97,14 @@ private void OnShutdown(EntityUid uid, PyrokinesisPowerComponent component, Comp private void OnPowerUsed(PyrokinesisPowerActionEvent args) { - if (!HasComp(args.Performer) - && TryComp(args.Performer, out var psionic) + if (_psionics.CheckCanSelfCast(args.Performer, out var psionic) && TryComp(args.Performer, out var pyroComp)) { var spawnCoords = Transform(args.Performer).Coordinates; var ent = Spawn("ProjectileAnomalyFireball", spawnCoords); - if (_glimmerSystem.GlimmerOutput >= 25 * psionic.Dampening) + if (_glimmerSystem.GlimmerOutput <= 25 * psionic.Dampening) EnsureComp(ent); if (TryComp(ent, out var fireball)) diff --git a/Content.Server/Psionics/Abilities/RegenerativeStasisPowerSystem.cs b/Content.Server/Psionics/Abilities/RegenerativeStasisPowerSystem.cs index cc67badbe7..87ec5adbce 100644 --- a/Content.Server/Psionics/Abilities/RegenerativeStasisPowerSystem.cs +++ b/Content.Server/Psionics/Abilities/RegenerativeStasisPowerSystem.cs @@ -26,17 +26,16 @@ public override void Initialize() private void OnInit(EntityUid uid, RegenerativeStasisPowerComponent component, ComponentInit args) { + EnsureComp(uid, out var psionic); _actions.AddAction(uid, ref component.RegenerativeStasisActionEntity, component.RegenerativeStasisActionId); _actions.TryGetActionData(component.RegenerativeStasisActionEntity, out var actionData); if (actionData is { UseDelay: not null }) - _actions.StartUseDelay(component.RegenerativeStasisActionEntity); - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Add(component); - psionic.PsychicFeedback.Add(component.RegenerativeStasisFeedback); - psionic.Amplification += 0.5f; - psionic.Dampening += 0.5f; - } + _actions.SetCooldown(component.RegenerativeStasisActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); + + psionic.ActivePowers.Add(component); + psionic.PsychicFeedback.Add(component.RegenerativeStasisFeedback); + psionic.Amplification += 0.5f; + psionic.Dampening += 0.5f; } private void OnShutdown(EntityUid uid, RegenerativeStasisPowerComponent component, ComponentShutdown args) @@ -63,8 +62,11 @@ private void OnPowerUsed(EntityUid uid, RegenerativeStasisPowerComponent compone solution.AddReagent("Epinephrine", FixedPoint2.New(MathF.Min(2.5f * psionic.Dampening + psionic.Amplification, 15f))); solution.AddReagent("Nocturine", 10f + (1 * psionic.Amplification + psionic.Dampening)); _bloodstreamSystem.TryAddToChemicals(args.Target, solution, stream); - _popupSystem.PopupEntity(Loc.GetString("regenerative-stasis-begin", ("entity", uid)), uid, PopupType.Medium); + _popupSystem.PopupEntity(Loc.GetString("regenerative-stasis-begin", ("entity", args.Target)), args.Target, PopupType.Medium); _psionics.LogPowerUsed(uid, "regenerative stasis", psionic, 4, 6); + _actions.TryGetActionData(component.RegenerativeStasisActionEntity, out var actionData); + if (actionData is { UseDelay: not null }) + _actions.SetCooldown(component.RegenerativeStasisActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); args.Handled = true; } } diff --git a/Content.Server/Psionics/Abilities/TelegnosisPowerSystem.cs b/Content.Server/Psionics/Abilities/TelegnosisPowerSystem.cs index c5f8471a65..95b7995e67 100644 --- a/Content.Server/Psionics/Abilities/TelegnosisPowerSystem.cs +++ b/Content.Server/Psionics/Abilities/TelegnosisPowerSystem.cs @@ -27,17 +27,16 @@ public override void Initialize() private void OnInit(EntityUid uid, TelegnosisPowerComponent component, ComponentInit args) { + EnsureComp(uid, out var psionic); _actions.AddAction(uid, ref component.TelegnosisActionEntity, component.TelegnosisActionId ); _actions.TryGetActionData( component.TelegnosisActionEntity, out var actionData ); if (actionData is { UseDelay: not null }) - _actions.StartUseDelay(component.TelegnosisActionEntity); - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Add(component); - psionic.PsychicFeedback.Add(component.TelegnosisFeedback); - psionic.Amplification += 0.3f; - psionic.Dampening += 0.3f; - } + _actions.SetCooldown(component.TelegnosisActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); + + psionic.ActivePowers.Add(component); + psionic.PsychicFeedback.Add(component.TelegnosisFeedback); + psionic.Amplification += 0.3f; + psionic.Dampening += 0.3f; } private void OnShutdown(EntityUid uid, TelegnosisPowerComponent component, ComponentShutdown args) @@ -67,6 +66,10 @@ private void OnPowerUsed(EntityUid uid, TelegnosisPowerComponent component, Tele component.ProjectionUid = projection; _mindSwap.Swap(uid, projection); + _actions.TryGetActionData( component.TelegnosisActionEntity, out var actionData ); + if (actionData is { UseDelay: not null }) + _actions.SetCooldown(component.TelegnosisActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); + if (EnsureComp(projection, out var projectionComponent)) projectionComponent.OriginalEntity = uid; diff --git a/Content.Server/Psionics/Glimmer/Structures/GlimmerSourceComponent.cs b/Content.Server/Psionics/Glimmer/Structures/GlimmerSourceComponent.cs index 5babb6c446..34cdba29bb 100644 --- a/Content.Server/Psionics/Glimmer/Structures/GlimmerSourceComponent.cs +++ b/Content.Server/Psionics/Glimmer/Structures/GlimmerSourceComponent.cs @@ -6,22 +6,42 @@ namespace Content.Server.Psionics.Glimmer /// public sealed partial class GlimmerSourceComponent : Component { - [DataField("accumulator")] + [DataField] public float Accumulator = 0f; - [DataField("active")] + [DataField] public bool Active = true; /// /// Since glimmer is an int, we'll do it like this. /// - [DataField("secondsPerGlimmer")] + [DataField] public float SecondsPerGlimmer = 10f; /// /// True if it produces glimmer, false if it subtracts it. /// - [DataField("addToGlimmer")] + [DataField] public bool AddToGlimmer = true; + + /// + /// If not null, this entity generates this value as a baseline number of research points per second, eg: Probers. + /// Actual glimmer research sources will scale with GlimmerEquilibriumRatio + /// + [DataField] + public int? ResearchPointGeneration = null; + + /// + /// Controls whether this entity requires electrical power to generate research points. + /// + [DataField] + public bool RequiresPower = true; + + /// + /// Above GlimmerEquilibrium, glimmer generation is increased exponentially, but has an offset to prevent things from spiralling out of control. + /// Increasing the offset will make this entity's exponential growth weaker, while decreasing it makes it stronger. Negative numbers are valid by the way :) + /// + [DataField] + public int GlimmerExponentOffset = 0; } } diff --git a/Content.Server/Psionics/Glimmer/Structures/GlimmerStructuresSystem.cs b/Content.Server/Psionics/Glimmer/Structures/GlimmerStructuresSystem.cs index 309bd732ef..76ff5a823b 100644 --- a/Content.Server/Psionics/Glimmer/Structures/GlimmerStructuresSystem.cs +++ b/Content.Server/Psionics/Glimmer/Structures/GlimmerStructuresSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Anomaly.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; +using Content.Server.Research.Components; using Content.Shared.Anomaly.Components; using Content.Shared.Mobs; using Content.Shared.Psionics.Glimmer; @@ -24,6 +25,17 @@ public override void Initialize() SubscribeLocalEvent(OnAnomalyPulse); SubscribeLocalEvent(OnAnomalySupercritical); SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnInit); + } + + private void OnInit(EntityUid uid, GlimmerSourceComponent component, ComponentStartup args) + { + if (component.ResearchPointGeneration != null) + { + EnsureComp(uid, out var points); + points.PointsPerSecond = component.ResearchPointGeneration.Value; + points.Active = true; + } } private void OnAnomalyVesselPowerChanged(EntityUid uid, AnomalyVesselComponent component, ref PowerChangedEvent args) @@ -47,7 +59,7 @@ private void OnAnomalyPulse(EntityUid uid, GlimmerSourceComponent component, ref // component. if (TryComp(uid, out var anomaly)) - _glimmerSystem.DeltaGlimmerInput(5f * anomaly.Severity); + _glimmerSystem.DeltaGlimmerOutput(5f * anomaly.Severity); } private void OnAnomalySupercritical(EntityUid uid, GlimmerSourceComponent component, ref AnomalySupercriticalEvent args) @@ -64,31 +76,56 @@ private void OnMobStateChanged(EntityUid uid, GlimmerSourceComponent component, public override void Update(float frameTime) { base.Update(frameTime); + var glimmerSources = Count(); foreach (var source in EntityQuery()) { - if (!_powerReceiverSystem.IsPowered(source.Owner)) + if (!_powerReceiverSystem.IsPowered(source.Owner) + && source.RequiresPower) + { + glimmerSources--; continue; + } if (!source.Active) + { + glimmerSources--; continue; + } source.Accumulator += frameTime; if (source.Accumulator > source.SecondsPerGlimmer) { - var glimmerEquilibrium = GlimmerSystem.GlimmerEquilibrium; source.Accumulator -= source.SecondsPerGlimmer; + // https://www.desmos.com/calculator/zjzefpue03 + // In Short: 1 prober makes 20 research points. 4 probers makes twice as many points as 1 prober. 9 probers makes 69 points in total between all 9. + // This is then modified by afterwards by GlimmerEquilibrium, to help smooth out the curves. But also, now if you have more drainers than probers, the probers won't generate research! + // Also, this counts things like Anomalies & Glimmer Mites! Which means scientists should be more encouraged to actively hunt mites. + // As a fun novelty, this means that a highly psionic Epistemics department can essentially "Study" their powers for actual research points! + if (source.ResearchPointGeneration != null + && TryComp(source.Owner, out var research)) + research.PointsPerSecond = (int) MathF.Round( + source.ResearchPointGeneration.Value / (MathF.Log(glimmerSources, 4) + 1) + * _glimmerSystem.GetGlimmerEquilibriumRatio()); + // Shorthand explanation: // This makes glimmer far more "Swingy", by making both positive and negative glimmer sources scale quite dramatically with glimmer + if (!_glimmerSystem.GetGlimmerEnabled()) + return; + + var glimmerEquilibrium = GlimmerSystem.GlimmerEquilibrium; + if (source.AddToGlimmer) { - _glimmerSystem.DeltaGlimmerInput((_glimmerSystem.GlimmerOutput > glimmerEquilibrium ? _glimmerSystem.GetGlimmerOutputInteger() : 1f) + _glimmerSystem.DeltaGlimmerInput((_glimmerSystem.GlimmerOutput > glimmerEquilibrium + ? MathF.Pow(_glimmerSystem.GetGlimmerOutputInteger() - source.GlimmerExponentOffset + glimmerSources, 2) : 1f) * (_glimmerSystem.GlimmerOutput < glimmerEquilibrium ? _glimmerSystem.GetGlimmerEquilibriumRatio() : 1f)); } else { - _glimmerSystem.DeltaGlimmerInput(-(_glimmerSystem.GlimmerOutput > glimmerEquilibrium ? _glimmerSystem.GetGlimmerOutputInteger() : 1f) + _glimmerSystem.DeltaGlimmerInput(-(_glimmerSystem.GlimmerOutput > glimmerEquilibrium + ? MathF.Pow(_glimmerSystem.GetGlimmerOutputInteger() - source.GlimmerExponentOffset + glimmerSources, 2) : 1f) * (_glimmerSystem.GlimmerOutput > glimmerEquilibrium ? _glimmerSystem.GetGlimmerEquilibriumRatio() : 1f)); } } diff --git a/Content.Server/Pulling/PullingSystem.cs b/Content.Server/Pulling/PullingSystem.cs deleted file mode 100644 index 69bb7c9370..0000000000 --- a/Content.Server/Pulling/PullingSystem.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Content.Shared.Input; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; -using JetBrains.Annotations; -using Robust.Server.GameObjects; -using Robust.Shared.Input.Binding; -using Robust.Shared.Player; - -namespace Content.Server.Pulling -{ - [UsedImplicitly] - public sealed class PullingSystem : SharedPullingSystem - { - public override void Initialize() - { - base.Initialize(); - - UpdatesAfter.Add(typeof(PhysicsSystem)); - - SubscribeLocalEvent(OnPullableMove); - SubscribeLocalEvent(OnPullableStopMove); - - CommandBinds.Builder - .Bind(ContentKeyFunctions.ReleasePulledObject, InputCmdHandler.FromDelegate(HandleReleasePulledObject)) - .Register(); - } - - private void HandleReleasePulledObject(ICommonSession? session) - { - if (session?.AttachedEntity is not {Valid: true} player) - { - return; - } - - if (!TryGetPulled(player, out var pulled)) - { - return; - } - - if (!EntityManager.TryGetComponent(pulled.Value, out SharedPullableComponent? pullable)) - { - return; - } - - TryStopPull(pullable); - } - } -} diff --git a/Content.Server/Radio/EntitySystems/RadioSystem.cs b/Content.Server/Radio/EntitySystems/RadioSystem.cs index 7ed7574a9a..bfdd49213e 100644 --- a/Content.Server/Radio/EntitySystems/RadioSystem.cs +++ b/Content.Server/Radio/EntitySystems/RadioSystem.cs @@ -115,12 +115,12 @@ public void SendRadioMessage(EntityUid messageSource, string message, RadioChann ? FormattedMessage.EscapeText(message) : message; - var wrappedMessage = WrapRadioMessage(messageSource, channel, name, content); + var wrappedMessage = WrapRadioMessage(messageSource, channel, name, content, language); var msg = new ChatMessage(ChatChannel.Radio, content, wrappedMessage, NetEntity.Invalid, null); // ... you guess it var obfuscated = _language.ObfuscateSpeech(content, language); - var obfuscatedWrapped = WrapRadioMessage(messageSource, channel, name, obfuscated); + var obfuscatedWrapped = WrapRadioMessage(messageSource, channel, name, obfuscated, language); var notUdsMsg = new ChatMessage(ChatChannel.Radio, obfuscated, obfuscatedWrapped, NetEntity.Invalid, null); var ev = new RadioReceiveEvent(messageSource, channel, msg, notUdsMsg, language); @@ -173,13 +173,14 @@ public void SendRadioMessage(EntityUid messageSource, string message, RadioChann _messages.Remove(message); } - private string WrapRadioMessage(EntityUid source, RadioChannelPrototype channel, string name, string message) + private string WrapRadioMessage(EntityUid source, RadioChannelPrototype channel, string name, string message, LanguagePrototype language) { var speech = _chat.GetSpeechVerb(source, message); return Loc.GetString(speech.Bold ? "chat-radio-message-wrap-bold" : "chat-radio-message-wrap", ("color", channel.Color), - ("fontType", speech.FontId), - ("fontSize", speech.FontSize), + ("languageColor", language.Color ?? channel.Color), + ("fontType", language.FontId ?? speech.FontId), + ("fontSize", language.FontSize ?? speech.FontSize), ("verb", Loc.GetString(_random.Pick(speech.SpeechVerbStrings))), ("channel", $"\\[{channel.LocalizedName}\\]"), ("name", name), diff --git a/Content.Server/RatKing/RatKingSystem.cs b/Content.Server/RatKing/RatKingSystem.cs index f676e89ac3..4b82dba335 100644 --- a/Content.Server/RatKing/RatKingSystem.cs +++ b/Content.Server/RatKing/RatKingSystem.cs @@ -11,7 +11,6 @@ using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Pointing; using Content.Shared.RatKing; -using Robust.Server.GameObjects; using Robust.Shared.Map; using Robust.Shared.Random; @@ -26,7 +25,6 @@ public sealed class RatKingSystem : SharedRatKingSystem [Dependency] private readonly HungerSystem _hunger = default!; [Dependency] private readonly NPCSystem _npc = default!; [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly TransformSystem _xform = default!; public override void Initialize() { diff --git a/Content.Server/Remotes/DoorRemoteComponent.cs b/Content.Server/Remotes/DoorRemoteComponent.cs deleted file mode 100644 index 91cb7ccad1..0000000000 --- a/Content.Server/Remotes/DoorRemoteComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Content.Server.Remotes -{ - [RegisterComponent] - [Access(typeof(DoorRemoteSystem))] - public sealed partial class DoorRemoteComponent : Component - { - public OperatingMode Mode = OperatingMode.OpenClose; - - public enum OperatingMode : byte - { - OpenClose, - ToggleBolts, - ToggleEmergencyAccess - } - } -} diff --git a/Content.Server/Remotes/DoorRemoteSystem.cs b/Content.Server/Remotes/DoorRemoteSystem.cs index d335911901..e42bc70091 100644 --- a/Content.Server/Remotes/DoorRemoteSystem.cs +++ b/Content.Server/Remotes/DoorRemoteSystem.cs @@ -1,74 +1,43 @@ using Content.Server.Administration.Logs; -using Robust.Shared.Player; using Content.Shared.Interaction; -using Content.Shared.Popups; using Content.Shared.Doors.Components; -using Content.Shared.Doors.Systems; -using Content.Shared.Physics; using Content.Shared.Access.Components; using Content.Server.Doors.Systems; using Content.Server.Power.EntitySystems; using Content.Shared.Database; -using Content.Shared.Interaction.Events; using Content.Shared.Examine; -using static Content.Server.Remotes.DoorRemoteComponent; +using Content.Shared.Remotes.EntitySystems; +using Content.Shared.Remotes.Components; -namespace Content.Server.Remotes +namespace Content.Shared.Remotes { - public sealed class DoorRemoteSystem : EntitySystem + public sealed class DoorRemoteSystem : SharedDoorRemoteSystem { [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly AirlockSystem _airlock = default!; - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly DoorSystem _doorSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly ExamineSystemShared _examine = default!; // I'm so sorry [Dependency] private readonly SharedAirlockSystem _sharedAirlockSystem = default!; - + public override void Initialize() { - SubscribeLocalEvent(OnInHandActivation); - SubscribeLocalEvent(OnBeforeInteract); - } - - public void OnInHandActivation(EntityUid user, DoorRemoteComponent component, UseInHandEvent args) - { - string switchMessageId; - switch (component.Mode) - { - case OperatingMode.OpenClose: - component.Mode = OperatingMode.ToggleBolts; - switchMessageId = "door-remote-switch-state-toggle-bolts"; - break; + base.Initialize(); - // Skip toggle bolts mode and move on from there (to emergency access) - case OperatingMode.ToggleBolts: - component.Mode = OperatingMode.ToggleEmergencyAccess; - switchMessageId = "door-remote-switch-state-toggle-emergency-access"; - break; - - // Skip ToggleEmergencyAccess mode and move on from there (to door toggle) - case OperatingMode.ToggleEmergencyAccess: - component.Mode = OperatingMode.OpenClose; - switchMessageId = "door-remote-switch-state-open-close"; - break; - default: - throw new InvalidOperationException( - $"{nameof(DoorRemoteComponent)} had invalid mode {component.Mode}"); - } - ShowPopupToUser(switchMessageId, args.User); + SubscribeLocalEvent(OnBeforeInteract); } - private void OnBeforeInteract(EntityUid uid, DoorRemoteComponent component, BeforeRangedInteractEvent args) + private void OnBeforeInteract(Entity entity, ref BeforeRangedInteractEvent args) { bool isAirlock = TryComp(args.Target, out var airlockComp); if (args.Handled || args.Target == null || !TryComp(args.Target, out var doorComp) // If it isn't a door we don't use it - // Only able to control doors if they are within your vision and within your max range. - // Not affected by mobs or machines anymore. + // Only able to control doors if they are within your vision and within your max range. + // Not affected by mobs or machines anymore. || !_examine.InRangeUnOccluded(args.User, args.Target.Value, SharedInteractionSystem.MaxRaycastRange, null)) + { return; } @@ -77,7 +46,7 @@ private void OnBeforeInteract(EntityUid uid, DoorRemoteComponent component, Befo if (!this.IsPowered(args.Target.Value, EntityManager)) { - ShowPopupToUser("door-remote-no-power", args.User); + Popup.PopupEntity(Loc.GetString("door-remote-no-power"), args.User, args.User); return; } @@ -85,11 +54,11 @@ private void OnBeforeInteract(EntityUid uid, DoorRemoteComponent component, Befo && !_doorSystem.HasAccess(args.Target.Value, args.Used, doorComp, accessComponent)) { _doorSystem.Deny(args.Target.Value, doorComp, args.User); - ShowPopupToUser("door-remote-denied", args.User); + Popup.PopupEntity(Loc.GetString("door-remote-denied"), args.User, args.User); return; } - switch (component.Mode) + switch (entity.Comp.Mode) { case OperatingMode.OpenClose: if (_doorSystem.TryToggleDoor(args.Target.Value, doorComp, args.Used)) @@ -115,11 +84,8 @@ private void OnBeforeInteract(EntityUid uid, DoorRemoteComponent component, Befo break; default: throw new InvalidOperationException( - $"{nameof(DoorRemoteComponent)} had invalid mode {component.Mode}"); + $"{nameof(DoorRemoteComponent)} had invalid mode {entity.Comp.Mode}"); } } - - private void ShowPopupToUser(string messageId, EntityUid user) => - _popupSystem.PopupEntity(Loc.GetString(messageId), user, user); } } diff --git a/Content.Server/Research/Systems/ResearchStealerSystem.cs b/Content.Server/Research/Systems/ResearchStealerSystem.cs index 5bab6048de..d40134f1e9 100644 --- a/Content.Server/Research/Systems/ResearchStealerSystem.cs +++ b/Content.Server/Research/Systems/ResearchStealerSystem.cs @@ -1,11 +1,13 @@ using Content.Shared.Research.Components; using Content.Shared.Research.Systems; +using Robust.Shared.Random; namespace Content.Server.Research.Systems; public sealed class ResearchStealerSystem : SharedResearchStealerSystem { [Dependency] private readonly SharedResearchSystem _research = default!; + [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { @@ -24,16 +26,26 @@ private void OnDoAfter(EntityUid uid, ResearchStealerComponent comp, ResearchSte if (!TryComp(target, out var database)) return; - var ev = new ResearchStolenEvent(uid, target, database.UnlockedTechnologies); + var ev = new ResearchStolenEvent(uid, target, new()); + var count = _random.Next(comp.MinToSteal, comp.MaxToSteal + 1); + for (var i = 0; i < count; i++) + { + if (database.UnlockedTechnologies.Count == 0) + break; + + var toRemove = _random.Pick(database.UnlockedTechnologies); + if (_research.TryRemoveTechnology((target, database), toRemove)) + ev.Techs.Add(toRemove); + } RaiseLocalEvent(uid, ref ev); - // oops, no more advanced lasers! - _research.ClearTechs(target, database); + + args.Handled = true; } } /// -/// Event raised on the user when research is stolen from a R&D server. +/// Event raised on the user when research is stolen from a RND server. /// Techs contains every technology id researched. /// [ByRefEvent] -public record struct ResearchStolenEvent(EntityUid Used, EntityUid Target, List Techs); +public record struct ResearchStolenEvent(EntityUid Used, EntityUid Target, List Techs); diff --git a/Content.Server/Respawn/SpecialRespawnSystem.cs b/Content.Server/Respawn/SpecialRespawnSystem.cs index 2822c94093..2463bcd740 100644 --- a/Content.Server/Respawn/SpecialRespawnSystem.cs +++ b/Content.Server/Respawn/SpecialRespawnSystem.cs @@ -9,13 +9,13 @@ using Content.Shared.Physics; using Content.Shared.Respawn; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Random; namespace Content.Server.Respawn; public sealed class SpecialRespawnSystem : SharedSpecialRespawnSystem { - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IAdminLogManager _adminLog = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly AtmosphereSystem _atmosphere = default!; @@ -84,7 +84,7 @@ private void OnTermination(EntityUid uid, SpecialRespawnComponent component, ref if (!component.Respawn || !HasComp(entityGridUid) || entityMapUid == null) return; - if (!_mapManager.TryGetGrid(entityGridUid, out var grid) || MetaData(entityGridUid.Value).EntityLifeStage >= EntityLifeStage.Terminating) + if (!TryComp(entityGridUid, out var grid) || MetaData(entityGridUid.Value).EntityLifeStage >= EntityLifeStage.Terminating) return; if (TryFindRandomTile(entityGridUid.Value, entityMapUid.Value, 10, out var coords)) @@ -146,7 +146,7 @@ public bool TryFindRandomTile(EntityUid targetGrid, EntityUid targetMap, int max { targetCoords = EntityCoordinates.Invalid; - if (!_mapManager.TryGetGrid(targetGrid, out var grid)) + if (!TryComp(targetGrid, out var grid)) return false; var xform = Transform(targetGrid); diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs index eb6eb5a426..cd64f043a0 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs @@ -2,7 +2,6 @@ using Content.Shared.Damage; using Content.Shared.Revenant; using Robust.Shared.Random; -using Robust.Shared.Map; using Content.Shared.Tag; using Content.Server.Storage.Components; using Content.Server.Light.Components; @@ -15,7 +14,6 @@ using Content.Shared.Bed.Sleep; using System.Linq; using System.Numerics; -using Content.Server.Maps; using Content.Server.Revenant.Components; using Content.Shared.DoAfter; using Content.Shared.Emag.Systems; @@ -28,12 +26,12 @@ using Content.Shared.Revenant.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Utility; +using Robust.Shared.Map.Components; namespace Content.Server.Revenant.EntitySystems; public sealed partial class RevenantSystem { - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ThrowingSystem _throwing = default!; [Dependency] private readonly EntityStorageSystem _entityStorage = default!; [Dependency] private readonly EmagSystem _emag = default!; @@ -213,7 +211,7 @@ private void OnDefileAction(EntityUid uid, RevenantComponent component, Revenant //var coords = Transform(uid).Coordinates; //var gridId = coords.GetGridUid(EntityManager); var xform = Transform(uid); - if (!_mapManager.TryGetGrid(xform.GridUid, out var map)) + if (!TryComp(xform.GridUid, out var map)) return; var tiles = map.GetTilesIntersecting(Box2.CenteredAround(xform.WorldPosition, new Vector2(component.DefileRadius * 2, component.DefileRadius))).ToArray(); diff --git a/Content.Server/Roles/RemoveRoleCommand.cs b/Content.Server/Roles/RemoveRoleCommand.cs index edb29da624..feba63a253 100644 --- a/Content.Server/Roles/RemoveRoleCommand.cs +++ b/Content.Server/Roles/RemoveRoleCommand.cs @@ -5,14 +5,12 @@ using Content.Shared.Roles.Jobs; using Robust.Server.Player; using Robust.Shared.Console; -using Robust.Shared.Prototypes; namespace Content.Server.Roles { [AdminCommand(AdminFlags.Admin)] public sealed class RemoveRoleCommand : IConsoleCommand { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!; public string Command => "rmrole"; diff --git a/Content.Server/Salvage/SalvageSystem.Expeditions.cs b/Content.Server/Salvage/SalvageSystem.Expeditions.cs index 7e4a9c9310..36e3a574ea 100644 --- a/Content.Server/Salvage/SalvageSystem.Expeditions.cs +++ b/Content.Server/Salvage/SalvageSystem.Expeditions.cs @@ -162,6 +162,8 @@ private void SpawnMission(SalvageMissionParams missionParams, EntityUid station) _biome, _dungeon, _metaData, + _transform, + _mapSystem, station, missionParams, cancelToken.Token); diff --git a/Content.Server/Salvage/SalvageSystem.cs b/Content.Server/Salvage/SalvageSystem.cs index a1a3b686b2..5a68005dd3 100644 --- a/Content.Server/Salvage/SalvageSystem.cs +++ b/Content.Server/Salvage/SalvageSystem.cs @@ -53,6 +53,7 @@ public sealed partial class SalvageSystem : SharedSalvageSystem [Dependency] private readonly RadioSystem _radioSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; [Dependency] private readonly ShuttleSystem _shuttle = default!; [Dependency] private readonly ShuttleConsoleSystem _shuttleConsoles = default!; [Dependency] private readonly StationSystem _station = default!; diff --git a/Content.Server/Salvage/SpawnSalvageMissionJob.cs b/Content.Server/Salvage/SpawnSalvageMissionJob.cs index e2b17b5872..5f7e356830 100644 --- a/Content.Server/Salvage/SpawnSalvageMissionJob.cs +++ b/Content.Server/Salvage/SpawnSalvageMissionJob.cs @@ -46,6 +46,8 @@ public sealed class SpawnSalvageMissionJob : Job private readonly BiomeSystem _biome; private readonly DungeonSystem _dungeon; private readonly MetaDataSystem _metaData; + private readonly SharedTransformSystem _xforms; + private readonly SharedMapSystem _map; public readonly EntityUid Station; private readonly SalvageMissionParams _missionParams; @@ -63,6 +65,8 @@ public SpawnSalvageMissionJob( BiomeSystem biome, DungeonSystem dungeon, MetaDataSystem metaData, + SharedTransformSystem xform, + SharedMapSystem map, EntityUid station, SalvageMissionParams missionParams, CancellationToken cancellation = default) : base(maxTime, cancellation) @@ -75,6 +79,8 @@ public SpawnSalvageMissionJob( _biome = biome; _dungeon = dungeon; _metaData = metaData; + _xforms = xform; + _map = map; Station = station; _missionParams = missionParams; _sawmill = logManager.GetSawmill("salvage_job"); @@ -86,9 +92,7 @@ public SpawnSalvageMissionJob( protected override async Task Process() { _sawmill.Debug("salvage", $"Spawning salvage mission with seed {_missionParams.Seed}"); - var mapId = _mapManager.CreateMap(); - var mapUid = _mapManager.GetMapEntityId(mapId); - _mapManager.AddUninitializedMap(mapId); + var mapUid = _map.CreateMap(out var mapId, runMapInit: false); MetaDataComponent? metadata = null; var grid = _entManager.EnsureComponent(mapUid); var random = new Random(_missionParams.Seed); diff --git a/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs b/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs index 2ab29d1b2f..6ce8edd1d8 100644 --- a/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs +++ b/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs @@ -11,7 +11,6 @@ namespace Content.Server.Sandbox.Commands [AnyCommand] public sealed class ColorNetworkCommand : IConsoleCommand { - [Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly IEntityManager _entManager = default!; public string Command => "colornetwork"; diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index f4dd502b37..ae742cf1f9 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -1,13 +1,10 @@ using System.Linq; -using System.Numerics; using Content.Server.Administration; using Content.Server.GameTicking; using Content.Server.GameTicking.Events; using Content.Server.Parallax; -using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; -using Content.Server.Salvage; using Content.Server.Screens.Components; using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Events; @@ -22,7 +19,6 @@ using Content.Shared.Parallax.Biomes; using Content.Shared.Salvage; using Content.Shared.Shuttles.Components; -using Robust.Shared.Spawners; using Content.Shared.Tiles; using Robust.Server.GameObjects; using Robust.Shared.Collections; @@ -51,7 +47,6 @@ public sealed class ArrivalsSystem : EntitySystem [Dependency] private readonly GameTicker _ticker = default!; [Dependency] private readonly MapLoaderSystem _loader = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!; - [Dependency] private readonly RestrictedRangeSystem _restricted = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly ShuttleSystem _shuttles = default!; [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index f8d995b8a4..39b76f7d32 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -492,7 +492,7 @@ private void AddEmergencyShuttle(EntityUid uid, StationEmergencyShuttleComponent return; } - centcomm.ShuttleIndex += _mapManager.GetGrid(shuttle.Value).LocalAABB.Width + ShuttleSpawnBuffer; + centcomm.ShuttleIndex += Comp(shuttle.Value).LocalAABB.Width + ShuttleSpawnBuffer; // Update indices for all centcomm comps pointing to same map var query = AllEntityQuery(); diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs index e4e4534b0c..cb322ac396 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs @@ -559,7 +559,7 @@ private void UpdateHyperspace(float frameTime) private float GetSoundRange(EntityUid uid) { - if (!_mapManager.TryGetGrid(uid, out var grid)) + if (!TryComp(uid, out var grid)) return 4f; return MathF.Max(grid.LocalAABB.Width, grid.LocalAABB.Height) + 12.5f; diff --git a/Content.Server/Shuttles/Systems/ThrusterSystem.cs b/Content.Server/Shuttles/Systems/ThrusterSystem.cs index 1baffd4690..74c42ccbc5 100644 --- a/Content.Server/Shuttles/Systems/ThrusterSystem.cs +++ b/Content.Server/Shuttles/Systems/ThrusterSystem.cs @@ -1,6 +1,5 @@ using System.Numerics; using Content.Server.Audio; -using Content.Server.Construction; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Shuttles.Components; @@ -12,6 +11,7 @@ using Content.Shared.Shuttles.Components; using Content.Shared.Temperature; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Physics.Collision.Shapes; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; @@ -24,7 +24,6 @@ namespace Content.Server.Shuttles.Systems; public sealed class ThrusterSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; [Dependency] private readonly AmbientSoundSystem _ambient = default!; [Dependency] private readonly FixtureSystem _fixtureSystem = default!; @@ -95,7 +94,7 @@ private void OnShuttleTileChange(EntityUid uid, ShuttleComponent component, ref return; var tilePos = args.NewTile.GridIndices; - var grid = _mapManager.GetGrid(uid); + var grid = Comp(uid); var xformQuery = GetEntityQuery(); var thrusterQuery = GetEntityQuery(); @@ -436,7 +435,7 @@ private bool NozzleExposed(TransformComponent xform) return true; var (x, y) = xform.LocalPosition + xform.LocalRotation.Opposite().ToWorldVec(); - var tile = _mapManager.GetGrid(xform.GridUid.Value).GetTileRef(new Vector2i((int) Math.Floor(x), (int) Math.Floor(y))); + var tile = Comp(xform.GridUid.Value).GetTileRef(new Vector2i((int) Math.Floor(x), (int) Math.Floor(y))); return tile.Tile.IsSpace(); } diff --git a/Content.Server/Silicons/Laws/SiliconLawSystem.cs b/Content.Server/Silicons/Laws/SiliconLawSystem.cs index 4584a9e88b..010682bc0d 100644 --- a/Content.Server/Silicons/Laws/SiliconLawSystem.cs +++ b/Content.Server/Silicons/Laws/SiliconLawSystem.cs @@ -19,7 +19,6 @@ using Content.Shared.Stunnable; using Content.Shared.Wires; using Robust.Server.GameObjects; -using Robust.Shared.Audio.Systems; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Toolshed; @@ -38,7 +37,6 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem [Dependency] private readonly SharedStunSystem _stunSystem = default!; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly SharedRoleSystem _roles = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; /// public override void Initialize() diff --git a/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs b/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs index ba07375699..ed53c3e101 100644 --- a/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs @@ -232,7 +232,7 @@ public void ConsumeEntitiesInContainer(EntityUid hungry, BaseContainer container /// public void ConsumeTile(EntityUid hungry, TileRef tile, EventHorizonComponent eventHorizon) { - ConsumeTiles(hungry, new List<(Vector2i, Tile)>(new[] { (tile.GridIndices, Tile.Empty) }), tile.GridUid, _mapMan.GetGrid(tile.GridUid), eventHorizon); + ConsumeTiles(hungry, new List<(Vector2i, Tile)>(new[] { (tile.GridIndices, Tile.Empty) }), tile.GridUid, Comp(tile.GridUid), eventHorizon); } /// @@ -240,7 +240,7 @@ public void ConsumeTile(EntityUid hungry, TileRef tile, EventHorizonComponent ev /// public void AttemptConsumeTile(EntityUid hungry, TileRef tile, EventHorizonComponent eventHorizon) { - AttemptConsumeTiles(hungry, new TileRef[1] { tile }, tile.GridUid, _mapMan.GetGrid(tile.GridUid), eventHorizon); + AttemptConsumeTiles(hungry, new TileRef[1] { tile }, tile.GridUid, Comp(tile.GridUid), eventHorizon); } /// diff --git a/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs b/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs index 92b963e201..b26ab301c6 100644 --- a/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs +++ b/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs @@ -24,7 +24,6 @@ public sealed class RadiationCollectorSystem : EntitySystem [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; - [Dependency] private readonly BatterySystem _batterySystem = default!; private const string GasTankContainer = "gas_tank"; diff --git a/Content.Server/Species/Systems/NymphSystem.cs b/Content.Server/Species/Systems/NymphSystem.cs index b7751afbf1..d491b957bf 100644 --- a/Content.Server/Species/Systems/NymphSystem.cs +++ b/Content.Server/Species/Systems/NymphSystem.cs @@ -19,10 +19,10 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnRemovedFromPart); + SubscribeLocalEvent(OnRemovedFromPart); } - private void OnRemovedFromPart(EntityUid uid, NymphComponent comp, RemovedFromPartInBodyEvent args) + private void OnRemovedFromPart(EntityUid uid, NymphComponent comp, ref OrganRemovedFromBodyEvent args) { if (!_timing.IsFirstTimePredicted) return; diff --git a/Content.Server/Spreader/SpreaderSystem.cs b/Content.Server/Spreader/SpreaderSystem.cs index 5b2f3298a2..671c281d1f 100644 --- a/Content.Server/Spreader/SpreaderSystem.cs +++ b/Content.Server/Spreader/SpreaderSystem.cs @@ -18,7 +18,6 @@ namespace Content.Server.Spreader; /// public sealed class SpreaderSystem : EntitySystem { - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly SharedMapSystem _map = default!; diff --git a/Content.Server/Station/Systems/StationJobsSystem.cs b/Content.Server/Station/Systems/StationJobsSystem.cs index a3b7a57354..debac8902e 100644 --- a/Content.Server/Station/Systems/StationJobsSystem.cs +++ b/Content.Server/Station/Systems/StationJobsSystem.cs @@ -25,7 +25,6 @@ public sealed partial class StationJobsSystem : EntitySystem [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly GameTicker _gameTicker = default!; - [Dependency] private readonly StationSystem _stationSystem = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; /// diff --git a/Content.Server/Station/Systems/StationSystem.cs b/Content.Server/Station/Systems/StationSystem.cs index b9ff8a4339..492f15c8e2 100644 --- a/Content.Server/Station/Systems/StationSystem.cs +++ b/Content.Server/Station/Systems/StationSystem.cs @@ -29,7 +29,6 @@ public sealed class StationSystem : EntitySystem { [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ChatSystem _chatSystem = default!; diff --git a/Content.Server/StationEvents/Components/RampingStationEventSchedulerComponent.cs b/Content.Server/StationEvents/Components/RampingStationEventSchedulerComponent.cs index 53bc8b62a4..282ee5b612 100644 --- a/Content.Server/StationEvents/Components/RampingStationEventSchedulerComponent.cs +++ b/Content.Server/StationEvents/Components/RampingStationEventSchedulerComponent.cs @@ -3,6 +3,30 @@ [RegisterComponent, Access(typeof(RampingStationEventSchedulerSystem))] public sealed partial class RampingStationEventSchedulerComponent : Component { + /// + /// The maximum number by which the event rate will be multiplied when shift time reaches the end time. + /// + [DataField] + public float ChaosModifier = 3f; + + /// + /// The minimum number by which the event rate will be multiplied when the shift has just begun. + /// + [DataField] + public float StartingChaosRatio = 0.1f; + + /// + /// The number by which all event delays will be multiplied. Unlike chaos, remains constant throughout the shift. + /// + [DataField] + public float EventDelayModifier = 1f; + + /// + /// The number by which average expected shift length is multiplied. Higher values lead to slower chaos growth. + /// + public float ShiftLengthModifier = 1f; + + // Everything below is overridden in the RampingStationEventSchedulerSystem based on CVars [DataField("endTime"), ViewVariables(VVAccess.ReadWrite)] public float EndTime; diff --git a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs b/Content.Server/StationEvents/Events/NinjaSpawnRule.cs index c60f3298e7..8ad5c8602e 100644 --- a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs +++ b/Content.Server/StationEvents/Events/NinjaSpawnRule.cs @@ -2,10 +2,8 @@ using Content.Server.Ninja.Systems; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; -using Robust.Server.GameObjects; using Robust.Shared.Map; using Robust.Shared.Map.Components; -using Robust.Shared.Random; namespace Content.Server.StationEvents.Events; @@ -14,7 +12,6 @@ namespace Content.Server.StationEvents.Events; /// public sealed class NinjaSpawnRule : StationEventSystem { - [Dependency] private readonly SpaceNinjaSystem _ninja = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; protected override void Started(EntityUid uid, NinjaSpawnRuleComponent comp, GameRuleComponent gameRule, GameRuleStartedEvent args) diff --git a/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs b/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs index 53a98e8b76..aa0c9b214b 100644 --- a/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs +++ b/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs @@ -29,15 +29,15 @@ protected override void Started(EntityUid uid, RampingStationEventSchedulerCompo { base.Started(uid, component, gameRule, args); - var avgChaos = _cfg.GetCVar(CCVars.EventsRampingAverageChaos); - var avgTime = _cfg.GetCVar(CCVars.EventsRampingAverageEndTime); + var avgChaos = _cfg.GetCVar(CCVars.EventsRampingAverageChaos) * component.ChaosModifier; + var avgTime = _cfg.GetCVar(CCVars.EventsRampingAverageEndTime) * component.ShiftLengthModifier; // Worlds shittiest probability distribution // Got a complaint? Send them to - component.MaxChaos = _random.NextFloat(avgChaos - avgChaos / 4, avgChaos + avgChaos / 4); + component.MaxChaos = avgChaos * _random.NextFloat(0.75f, 1.25f); // This is in minutes, so *60 for seconds (for the chaos calc) - component.EndTime = _random.NextFloat(avgTime - avgTime / 4, avgTime + avgTime / 4) * 60f; - component.StartingChaos = component.MaxChaos / 10; + component.EndTime = avgTime * _random.NextFloat(0.75f, 1.25f) * 60f; + component.StartingChaos = component.MaxChaos * component.StartingChaosRatio; PickNextEventTime(uid, component); } @@ -68,9 +68,10 @@ public override void Update(float frameTime) private void PickNextEventTime(EntityUid uid, RampingStationEventSchedulerComponent component) { - var mod = GetChaosModifier(uid, component); + component.TimeUntilNextEvent = _random.NextFloat( + _cfg.GetCVar(CCVars.GameEventsRampingMinimumTime), + _cfg.GetCVar(CCVars.GameEventsRampingMaximumTime)); - component.TimeUntilNextEvent = _random.NextFloat(_cfg.GetCVar(CCVars.GameEventsRampingMinimumTime) / mod, - _cfg.GetCVar(CCVars.GameEventsRampingMaximumTime)) / mod; + component.TimeUntilNextEvent *= component.EventDelayModifier / GetChaosModifier(uid, component); } } diff --git a/Content.Server/SubFloor/SubFloorHideSystem.cs b/Content.Server/SubFloor/SubFloorHideSystem.cs index 7820badceb..2767f500f9 100644 --- a/Content.Server/SubFloor/SubFloorHideSystem.cs +++ b/Content.Server/SubFloor/SubFloorHideSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Construction.Components; using Content.Shared.SubFloor; +using Robust.Shared.Map.Components; namespace Content.Server.SubFloor; @@ -17,7 +18,7 @@ private void OnAnchorAttempt(EntityUid uid, SubFloorHideComponent component, Anc // No teleporting entities through floor tiles when anchoring them. var xform = Transform(uid); - if (MapManager.TryGetGrid(xform.GridUid, out var grid) + if (TryComp(xform.GridUid, out var grid) && HasFloorCover(grid, grid.TileIndicesFor(xform.Coordinates))) { args.Cancel(); diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSpeakerSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSpeakerSystem.cs index 7544fc376b..0e694a801e 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSpeakerSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSpeakerSystem.cs @@ -1,10 +1,7 @@ using Content.Server.Chat.Systems; using Content.Server.Speech; using Content.Shared.Speech; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; using Robust.Shared.Timing; namespace Content.Server.SurveillanceCamera; @@ -18,8 +15,6 @@ public sealed class SurveillanceCameraSpeakerSystem : EntitySystem [Dependency] private readonly SpeechSoundSystem _speechSound = default!; [Dependency] private readonly ChatSystem _chatSystem = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; /// public override void Initialize() diff --git a/Content.Server/Temperature/Systems/TemperatureSystem.cs b/Content.Server/Temperature/Systems/TemperatureSystem.cs index aca902b9d3..0f57da4b88 100644 --- a/Content.Server/Temperature/Systems/TemperatureSystem.cs +++ b/Content.Server/Temperature/Systems/TemperatureSystem.cs @@ -11,7 +11,6 @@ using Content.Shared.Inventory; using Content.Shared.Rejuvenate; using Content.Shared.Temperature; -using Robust.Server.GameObjects; using Robust.Shared.Physics.Components; namespace Content.Server.Temperature.Systems; @@ -22,7 +21,6 @@ public sealed class TemperatureSystem : EntitySystem [Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly TransformSystem _transform = default!; /// /// All the components that will have their damage updated at the end of the tick. diff --git a/Content.Server/Traits/Assorted/ForeignerTraitComponent.cs b/Content.Server/Traits/Assorted/ForeignerTraitComponent.cs new file mode 100644 index 0000000000..e2d74ba5d9 --- /dev/null +++ b/Content.Server/Traits/Assorted/ForeignerTraitComponent.cs @@ -0,0 +1,36 @@ +using Content.Shared.Language; +using Content.Shared.Language.Systems; +using Robust.Shared.Prototypes; + +namespace Content.Server.Traits.Assorted; + +/// +/// When applied to a not-yet-spawned player entity, removes from the lists of their languages +/// and gives them a translator instead. +/// +[RegisterComponent] +public sealed partial class ForeignerTraitComponent : Component +{ + /// + /// The "base" language that is to be removed and substituted with a translator. + /// By default, equals to the fallback language, which is GalacticCommon. + /// + [DataField] + public ProtoId BaseLanguage = SharedLanguageSystem.FallbackLanguagePrototype; + + /// + /// Whether this trait prevents the entity from understanding the base language. + /// + public bool CantUnderstand = true; + + /// + /// Whether this trait prevents the entity from speaking the base language. + /// + public bool CantSpeak = true; + + /// + /// The base translator prototype to use when creating a translator for the entity. + /// + [DataField(required: true)] + public ProtoId BaseTranslator = default!; +} diff --git a/Content.Server/Traits/Assorted/ForeignerTraitSystem.cs b/Content.Server/Traits/Assorted/ForeignerTraitSystem.cs new file mode 100644 index 0000000000..58e974227c --- /dev/null +++ b/Content.Server/Traits/Assorted/ForeignerTraitSystem.cs @@ -0,0 +1,105 @@ +using System.Linq; +using Content.Server.Hands.Systems; +using Content.Server.Language; +using Content.Server.Storage.EntitySystems; +using Content.Shared.Clothing.Components; +using Content.Shared.Inventory; +using Content.Shared.Language; +using Content.Shared.Language.Components; +using Content.Shared.Language.Components.Translators; +using Content.Shared.Storage; +using Robust.Shared.Prototypes; + +namespace Content.Server.Traits.Assorted; + + +public sealed partial class ForeignerTraitSystem : EntitySystem +{ + [Dependency] private readonly EntityManager _entMan = default!; + [Dependency] private readonly HandsSystem _hands = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly LanguageSystem _languages = default!; + [Dependency] private readonly StorageSystem _storage = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnSpawn); // TraitSystem adds it after PlayerSpawnCompleteEvent so it's fine + } + + private void OnSpawn(Entity entity, ref ComponentInit args) + { + if (entity.Comp.CantUnderstand && !entity.Comp.CantSpeak) + Log.Warning($"Allowing entity {entity.Owner} to speak a language but not understand it leads to undefined behavior."); + + if (!TryComp(entity, out var knowledge)) + { + Log.Warning($"Entity {entity.Owner} does not have a LanguageKnowledge but has a ForeignerTrait!"); + return; + } + + var alternateLanguage = knowledge.SpokenLanguages.Find(it => it != entity.Comp.BaseLanguage); + if (alternateLanguage == null) + { + Log.Warning($"Entity {entity.Owner} does not have an alternative language to choose from (must have at least one non-GC for ForeignerTrait)!"); + return; + } + + if (TryGiveTranslator(entity.Owner, entity.Comp.BaseTranslator, entity.Comp.BaseLanguage, alternateLanguage, out var translator)) + { + _languages.RemoveLanguage(entity, entity.Comp.BaseLanguage, entity.Comp.CantSpeak, entity.Comp.CantUnderstand, knowledge); + } + } + + /// + /// Tries to create and give the entity a translator to translator that translates speech between the two specified languages. + /// + public bool TryGiveTranslator( + EntityUid uid, + string baseTranslatorPrototype, + ProtoId translatorLanguage, + ProtoId entityLanguage, + out EntityUid result) + { + result = EntityUid.Invalid; + if (translatorLanguage == entityLanguage) + return false; + + var translator = _entMan.SpawnNextToOrDrop(baseTranslatorPrototype, uid); + result = translator; + + if (!TryComp(translator, out var handheld)) + { + handheld = AddComp(translator); + handheld.ToggleOnInteract = true; + handheld.SetLanguageOnInteract = true; + } + + // Allows to speak the specified language and requires entities language. + handheld.SpokenLanguages = [translatorLanguage]; + handheld.UnderstoodLanguages = [translatorLanguage]; + handheld.RequiredLanguages = [entityLanguage]; + + // Try to put it in entities hand + if (_hands.TryPickupAnyHand(uid, translator, false, false, false)) + return true; + + // Try to find a valid clothing slot on the entity and equip the translator there + if (TryComp(translator, out var clothing) + && clothing.Slots != SlotFlags.NONE + && _inventory.TryGetSlots(uid, out var slots) + && slots.Any(it => _inventory.TryEquip(uid, translator, it.Name, true, false))) + return true; + + // Try to put the translator into entities bag, if it has one + if (_inventory.TryGetSlotEntity(uid, "back", out var bag) + && TryComp(bag, out var storage) + && _storage.Insert(bag.Value, translator, out _, null, storage, false, false)) + return true; + + // If all of the above has failed, just drop it at the same location as the entity + // This should ideally never happen, but who knows. + Transform(translator).Coordinates = Transform(uid).Coordinates; + + return true; + } +} diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index c471fb00f4..9db00d2949 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -1,6 +1,5 @@ using System.Linq; using System.Numerics; -using Content.Server.Administration.Logs; using Content.Server.Cargo.Systems; using Content.Server.Interaction; using Content.Server.Power.EntitySystems; @@ -30,7 +29,6 @@ namespace Content.Server.Weapons.Ranged.Systems; public sealed partial class GunSystem : SharedGunSystem { - [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IComponentFactory _factory = default!; [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly DamageExamineSystem _damageExamine = default!; diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ThrowArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ThrowArtifactSystem.cs index 85783b552d..57a30a2fd9 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ThrowArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ThrowArtifactSystem.cs @@ -1,12 +1,10 @@ using System.Numerics; -using Content.Server.Maps; using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components; using Content.Server.Xenoarchaeology.XenoArtifacts.Events; -using Content.Shared.Ghost; using Content.Shared.Maps; using Content.Shared.Physics; using Content.Shared.Throwing; -using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Random; @@ -14,7 +12,6 @@ namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems; public sealed class ThrowArtifactSystem : EntitySystem { - [Dependency] private readonly IMapManager _map = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly ThrowingSystem _throwing = default!; @@ -29,7 +26,7 @@ public override void Initialize() private void OnActivated(EntityUid uid, ThrowArtifactComponent component, ArtifactActivatedEvent args) { var xform = Transform(uid); - if (_map.TryGetGrid(xform.GridUid, out var grid)) + if (TryComp(xform.GridUid, out var grid)) { var tiles = grid.GetTilesIntersecting( Box2.CenteredAround(xform.WorldPosition, new Vector2(component.Range * 2, component.Range))); diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactGasTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactGasTriggerSystem.cs index 96f1dc3783..00f409f553 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactGasTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactGasTriggerSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Xenoarchaeology.XenoArtifacts.Events; using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; -using Robust.Server.GameObjects; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; @@ -9,7 +8,6 @@ public sealed class ArtifactGasTriggerSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly ArtifactSystem _artifactSystem = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; public override void Initialize() { diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactHeatTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactHeatTriggerSystem.cs index 33d1a43c12..5525cdf359 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactHeatTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactHeatTriggerSystem.cs @@ -3,7 +3,6 @@ using Content.Shared.Interaction; using Content.Shared.Temperature; using Content.Shared.Weapons.Melee.Events; -using Robust.Server.GameObjects; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; @@ -11,7 +10,6 @@ public sealed class ArtifactHeatTriggerSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly ArtifactSystem _artifactSystem = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; public override void Initialize() { diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactInteractionTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactInteractionTriggerSystem.cs index 239b674160..9976d56da0 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactInteractionTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactInteractionTriggerSystem.cs @@ -1,6 +1,6 @@ using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; using Content.Shared.Interaction; -using Content.Shared.Physics.Pull; +using Content.Shared.Movement.Pulling.Events; using Content.Shared.Weapons.Melee.Events; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; @@ -22,7 +22,7 @@ private void OnPull(EntityUid uid, ArtifactInteractionTriggerComponent component if (!component.PullActivation) return; - _artifactSystem.TryActivateArtifact(uid, args.Puller.Owner); + _artifactSystem.TryActivateArtifact(uid, args.PullerUid); } private void OnAttack(EntityUid uid, ArtifactInteractionTriggerComponent component, AttackedEvent args) diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactPressureTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactPressureTriggerSystem.cs index 4388756cce..8777ab0a8c 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactPressureTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactPressureTriggerSystem.cs @@ -1,6 +1,5 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; -using Robust.Server.GameObjects; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; @@ -11,7 +10,6 @@ public sealed class ArtifactPressureTriggerSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly ArtifactSystem _artifactSystem = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; public override void Update(float frameTime) { diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index 21f2508042..c7e29166a0 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -26,12 +26,12 @@ using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; +using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Systems; using Content.Shared.Nutrition.AnimalHusbandry; using Content.Shared.Nutrition.Components; using Content.Shared.Popups; using Content.Shared.Roles; -using Content.Shared.Pulling.Components; using Content.Shared.Weapons.Melee; using Content.Shared.Zombies; using Content.Shared.Prying.Components; @@ -280,7 +280,9 @@ public void ZombifyEntity(EntityUid target, MobStateComponent? mobState = null) RemComp(target, handsComp); } - RemComp(target); + // Sloth: What the fuck? + // How long until compregistry lmao. + RemComp(target); // No longer waiting to become a zombie: // Requires deferral because this is (probably) the event which called ZombifyEntity in the first place. diff --git a/Content.Server/Zombies/ZombieSystem.cs b/Content.Server/Zombies/ZombieSystem.cs index bef57eceb3..080bef44e7 100644 --- a/Content.Server/Zombies/ZombieSystem.cs +++ b/Content.Server/Zombies/ZombieSystem.cs @@ -2,9 +2,7 @@ using Content.Server.Body.Systems; using Content.Server.Chat; using Content.Server.Chat.Systems; -using Content.Server.Cloning; using Content.Server.Emoting.Systems; -using Content.Server.Inventory; using Content.Server.Speech.EntitySystems; using Content.Shared.Bed.Sleep; using Content.Shared.Cloning; @@ -31,7 +29,6 @@ public sealed partial class ZombieSystem : SharedZombieSystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly BloodstreamSystem _bloodstream = default!; [Dependency] private readonly DamageableSystem _damageable = default!; - [Dependency] private readonly ServerInventorySystem _inv = default!; [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly AutoEmoteSystem _autoEmote = default!; [Dependency] private readonly EmoteOnDamageSystem _emoteOnDamage = default!; diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index a914a8f267..f5ed2df227 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -202,7 +202,7 @@ public bool CanChangeDirection(EntityUid uid) public bool CanShiver(EntityUid uid) { var ev = new ShiverAttemptEvent(uid); - RaiseLocalEvent(uid, ev); + RaiseLocalEvent(uid, ref ev); return !ev.Cancelled; } @@ -210,7 +210,7 @@ public bool CanShiver(EntityUid uid) public bool CanSweat(EntityUid uid) { var ev = new SweatAttemptEvent(uid); - RaiseLocalEvent(uid, ev); + RaiseLocalEvent(uid, ref ev); return !ev.Cancelled; } diff --git a/Content.Shared/Administration/AdminData.cs b/Content.Shared/Administration/AdminData.cs index 5b5873bce3..229edbcd4c 100644 --- a/Content.Shared/Administration/AdminData.cs +++ b/Content.Shared/Administration/AdminData.cs @@ -12,6 +12,11 @@ public sealed class AdminData /// public bool Active; + /// + /// Whether the admin is in stealth mode and won't appear in adminwho to admins without the Stealth flag. + /// + public bool Stealth; + /// /// The admin's title. /// @@ -56,6 +61,14 @@ public bool CanAdminMenu() return HasFlag(AdminFlags.Admin); } + /// + /// Check if this admin can be hidden and see other hidden admins. + /// + public bool CanStealth() + { + return HasFlag(AdminFlags.Stealth); + } + public bool CanAdminReloadPrototypes() { return HasFlag(AdminFlags.Host); diff --git a/Content.Shared/Administration/AdminFlags.cs b/Content.Shared/Administration/AdminFlags.cs index 64cf522faa..9842e638c2 100644 --- a/Content.Shared/Administration/AdminFlags.cs +++ b/Content.Shared/Administration/AdminFlags.cs @@ -96,7 +96,12 @@ public enum AdminFlags : uint MassBan = 1 << 15, /// - /// DeltaV - The ability to whitelist people. Either this permission or +BAN is required for remove. + /// Allows you to remain hidden from adminwho except to other admins with this flag. + /// + Stealth = 1 << 16, + + /// + /// DeltaV - The ability to whitelist people. Either this permission or +BAN is required for remove. /// Whitelist = 1 << 20, diff --git a/Content.Shared/Administration/AdminFrozenSystem.cs b/Content.Shared/Administration/AdminFrozenSystem.cs index 14438cc591..4ec9600b0b 100644 --- a/Content.Shared/Administration/AdminFrozenSystem.cs +++ b/Content.Shared/Administration/AdminFrozenSystem.cs @@ -1,13 +1,10 @@ using Content.Shared.ActionBlocker; using Content.Shared.Interaction.Events; using Content.Shared.Item; -using Content.Shared.Movement; using Content.Shared.Movement.Events; -using Content.Shared.Physics.Pull; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; -using Content.Shared.Pulling.Events; -using Content.Shared.Stunnable; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Events; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Throwing; namespace Content.Shared.Administration; @@ -15,7 +12,7 @@ namespace Content.Shared.Administration; public sealed class AdminFrozenSystem : EntitySystem { [Dependency] private readonly ActionBlockerSystem _blocker = default!; - [Dependency] private readonly SharedPullingSystem _pulling = default!; + [Dependency] private readonly PullingSystem _pulling = default!; public override void Initialize() { @@ -45,9 +42,9 @@ private void OnPullAttempt(EntityUid uid, AdminFrozenComponent component, PullAt private void OnStartup(EntityUid uid, AdminFrozenComponent component, ComponentStartup args) { - if (TryComp(uid, out var pullable)) + if (TryComp(uid, out var pullable)) { - _pulling.TryStopPull(pullable); + _pulling.TryStopPull(uid, pullable); } UpdateCanMove(uid, component, args); diff --git a/Content.Shared/Body/Events/MechanismBodyEvents.cs b/Content.Shared/Body/Events/MechanismBodyEvents.cs index b52a333613..968b172aef 100644 --- a/Content.Shared/Body/Events/MechanismBodyEvents.cs +++ b/Content.Shared/Body/Events/MechanismBodyEvents.cs @@ -1,61 +1,28 @@ -namespace Content.Shared.Body.Events -{ - // All of these events are raised on a mechanism entity when added/removed to a body in different - // ways. - - /// - /// Raised on a mechanism when it is added to a body part. - /// - public sealed class AddedToPartEvent : EntityEventArgs - { - public EntityUid Part; - - public AddedToPartEvent(EntityUid part) - { - Part = part; - } - } - - /// - /// Raised on a mechanism when it is added to a body part within a body. - /// - public sealed class AddedToPartInBodyEvent : EntityEventArgs - { - public EntityUid Body; - public EntityUid Part; - - public AddedToPartInBodyEvent(EntityUid body, EntityUid part) - { - Body = body; - Part = part; - } - } - - /// - /// Raised on a mechanism when it is removed from a body part. - /// - public sealed class RemovedFromPartEvent : EntityEventArgs - { - public EntityUid OldPart; - - public RemovedFromPartEvent(EntityUid oldPart) - { - OldPart = oldPart; - } - } - - /// - /// Raised on a mechanism when it is removed from a body part within a body. - /// - public sealed class RemovedFromPartInBodyEvent : EntityEventArgs - { - public EntityUid OldBody; - public EntityUid OldPart; - - public RemovedFromPartInBodyEvent(EntityUid oldBody, EntityUid oldPart) - { - OldBody = oldBody; - OldPart = oldPart; - } - } -} +namespace Content.Shared.Body.Events; + +// All of these events are raised on a mechanism entity when added/removed to a body in different +// ways. + +/// +/// Raised on a mechanism when it is added to a body part. +/// +[ByRefEvent] +public readonly record struct OrganAddedEvent(EntityUid Part); + +/// +/// Raised on a mechanism when it is added to a body part within a body. +/// +[ByRefEvent] +public readonly record struct OrganAddedToBodyEvent(EntityUid Body, EntityUid Part); + +/// +/// Raised on a mechanism when it is removed from a body part. +/// +[ByRefEvent] +public readonly record struct OrganRemovedEvent(EntityUid OldPart); + +/// +/// Raised on a mechanism when it is removed from a body part within a body. +/// +[ByRefEvent] +public readonly record struct OrganRemovedFromBodyEvent(EntityUid OldBody, EntityUid OldPart); diff --git a/Content.Shared/Body/Events/ShiverAttemptEvent.cs b/Content.Shared/Body/Events/ShiverAttemptEvent.cs index 8c2761f545..e9400bc48d 100644 --- a/Content.Shared/Body/Events/ShiverAttemptEvent.cs +++ b/Content.Shared/Body/Events/ShiverAttemptEvent.cs @@ -1,12 +1,8 @@ -namespace Content.Shared.Body.Events -{ - public sealed class ShiverAttemptEvent : CancellableEntityEventArgs - { - public ShiverAttemptEvent(EntityUid uid) - { - Uid = uid; - } +namespace Content.Shared.Body.Events; - public EntityUid Uid { get; } - } +[ByRefEvent] +public record struct ShiverAttemptEvent(EntityUid Uid) +{ + public readonly EntityUid Uid = Uid; + public bool Cancelled = false; } diff --git a/Content.Shared/Body/Events/SweatAttemptEvent.cs b/Content.Shared/Body/Events/SweatAttemptEvent.cs index 7f4b3fab15..7506538c43 100644 --- a/Content.Shared/Body/Events/SweatAttemptEvent.cs +++ b/Content.Shared/Body/Events/SweatAttemptEvent.cs @@ -1,12 +1,8 @@ -namespace Content.Shared.Body.Events -{ - public sealed class SweatAttemptEvent : CancellableEntityEventArgs - { - public SweatAttemptEvent(EntityUid uid) - { - Uid = uid; - } +namespace Content.Shared.Body.Events; - public EntityUid Uid { get; } - } +[ByRefEvent] +public record struct SweatAttemptEvent(EntityUid Uid) +{ + public readonly EntityUid Uid = Uid; + public bool Cancelled = false; } diff --git a/Content.Shared/Body/Organ/OrganComponent.cs b/Content.Shared/Body/Organ/OrganComponent.cs index 9e1de6b355..3048927b5f 100644 --- a/Content.Shared/Body/Organ/OrganComponent.cs +++ b/Content.Shared/Body/Organ/OrganComponent.cs @@ -1,4 +1,4 @@ -using Content.Shared.Body.Systems; +using Content.Shared.Body.Systems; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -11,6 +11,6 @@ public sealed partial class OrganComponent : Component /// /// Relevant body this organ is attached to. /// - [DataField("body"), AutoNetworkedField] + [DataField, AutoNetworkedField] public EntityUid? Body; } diff --git a/Content.Shared/Body/Part/BodyPartEvents.cs b/Content.Shared/Body/Part/BodyPartEvents.cs index 4dbc543fc8..0d8d2c8a26 100644 --- a/Content.Shared/Body/Part/BodyPartEvents.cs +++ b/Content.Shared/Body/Part/BodyPartEvents.cs @@ -1,7 +1,7 @@ -namespace Content.Shared.Body.Part; +namespace Content.Shared.Body.Part; [ByRefEvent] -public readonly record struct BodyPartAddedEvent(string Slot, BodyPartComponent Part); +public readonly record struct BodyPartAddedEvent(string Slot, Entity Part); [ByRefEvent] -public readonly record struct BodyPartRemovedEvent(string Slot, BodyPartComponent Part); +public readonly record struct BodyPartRemovedEvent(string Slot, Entity Part); diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index bc7cf63124..1a35afdbe0 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -9,7 +9,6 @@ using Content.Shared.Gibbing.Events; using Content.Shared.Gibbing.Systems; using Content.Shared.Inventory; -using Content.Shared.Inventory.Events; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; @@ -30,8 +29,10 @@ public partial class SharedBodySystem [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly GibbingSystem _gibbingSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + private const float GibletLaunchImpulse = 8; private const float GibletLaunchImpulseVariance = 3; + private void InitializeBody() { // Body here to handle root body parts. @@ -43,7 +44,7 @@ private void InitializeBody() SubscribeLocalEvent(OnBodyCanDrag); } - private void OnBodyInserted(EntityUid uid, BodyComponent component, EntInsertedIntoContainerMessage args) + private void OnBodyInserted(Entity ent, ref EntInsertedIntoContainerMessage args) { // Root body part? var slotId = args.Container.ID; @@ -51,21 +52,21 @@ private void OnBodyInserted(EntityUid uid, BodyComponent component, EntInsertedI if (slotId != BodyRootContainerId) return; - var entity = args.Entity; + var insertedUid = args.Entity; - if (TryComp(entity, out BodyPartComponent? childPart)) + if (TryComp(insertedUid, out BodyPartComponent? part)) { - AddPart(uid, entity, slotId, childPart); - RecursiveBodyUpdate(entity, uid, childPart); + AddPart((ent, ent), (insertedUid, part), slotId); + RecursiveBodyUpdate((insertedUid, part), ent); } - if (TryComp(entity, out OrganComponent? organ)) + if (TryComp(insertedUid, out OrganComponent? organ)) { - AddOrgan(entity, uid, uid, organ); + AddOrgan((insertedUid, organ), ent, ent); } } - private void OnBodyRemoved(EntityUid uid, BodyComponent component, EntRemovedFromContainerMessage args) + private void OnBodyRemoved(Entity ent, ref EntRemovedFromContainerMessage args) { // Root body part? var slotId = args.Container.ID; @@ -73,55 +74,55 @@ private void OnBodyRemoved(EntityUid uid, BodyComponent component, EntRemovedFro if (slotId != BodyRootContainerId) return; - var entity = args.Entity; - DebugTools.Assert(!TryComp(entity, out BodyPartComponent? b) || b.Body == uid); - DebugTools.Assert(!TryComp(entity, out OrganComponent? o) || o.Body == uid); + var removedUid = args.Entity; + DebugTools.Assert(!TryComp(removedUid, out BodyPartComponent? b) || b.Body == ent); + DebugTools.Assert(!TryComp(removedUid, out OrganComponent? o) || o.Body == ent); - if (TryComp(entity, out BodyPartComponent? childPart)) + if (TryComp(removedUid, out BodyPartComponent? part)) { - RemovePart(uid, entity, slotId, childPart); - RecursiveBodyUpdate(entity, null, childPart); + RemovePart((ent, ent), (removedUid, part), slotId); + RecursiveBodyUpdate((removedUid, part), null); } - if (TryComp(entity, out OrganComponent? organ)) - RemoveOrgan(entity, uid, organ); + if (TryComp(removedUid, out OrganComponent? organ)) + RemoveOrgan((removedUid, organ), ent); } - private void OnBodyInit(EntityUid bodyId, BodyComponent body, ComponentInit args) + private void OnBodyInit(Entity ent, ref ComponentInit args) { // Setup the initial container. - body.RootContainer = Containers.EnsureContainer(bodyId, BodyRootContainerId); + ent.Comp.RootContainer = Containers.EnsureContainer(ent, BodyRootContainerId); } - private void OnBodyMapInit(EntityUid bodyId, BodyComponent body, MapInitEvent args) + private void OnBodyMapInit(Entity ent, ref MapInitEvent args) { - if (body.Prototype == null) + if (ent.Comp.Prototype is null) return; // One-time setup // Obviously can't run in Init to avoid double-spawns on save / load. - var prototype = Prototypes.Index(body.Prototype.Value); - MapInitBody(bodyId, prototype); + var prototype = Prototypes.Index(ent.Comp.Prototype.Value); + MapInitBody(ent, prototype); } private void MapInitBody(EntityUid bodyEntity, BodyPrototype prototype) { var protoRoot = prototype.Slots[prototype.Root]; - if (protoRoot.Part == null) + if (protoRoot.Part is null) return; // This should already handle adding the entity to the root. - var rootPartEntity = SpawnInContainerOrDrop(protoRoot.Part, bodyEntity, BodyRootContainerId); - var rootPart = Comp(rootPartEntity); + var rootPartUid = SpawnInContainerOrDrop(protoRoot.Part, bodyEntity, BodyRootContainerId); + var rootPart = Comp(rootPartUid); rootPart.Body = bodyEntity; - Dirty(rootPartEntity, rootPart); + Dirty(rootPartUid, rootPart); // Setup the rest of the body entities. - SetupOrgans(rootPartEntity, rootPart, protoRoot.Organs); - MapInitParts(rootPartEntity, prototype); + SetupOrgans((rootPartUid, rootPart), protoRoot.Organs); + MapInitParts(rootPartUid, prototype); } - private void OnBodyCanDrag(EntityUid uid, BodyComponent component, ref CanDragEvent args) + private void OnBodyCanDrag(Entity ent, ref CanDragEvent args) { args.Handled = true; } @@ -169,7 +170,7 @@ private void MapInitParts(EntityUid rootPartId, BodyPrototype prototype) var partSlot = CreatePartSlot(parentEntity, connection, childPartComponent.PartType, parentPartComponent); var cont = Containers.GetContainer(parentEntity, GetPartSlotContainerId(connection)); - if (partSlot == null || !Containers.Insert(childPart, cont)) + if (partSlot is null || !Containers.Insert(childPart, cont)) { Log.Error($"Could not create slot for connection {connection} in body {prototype.ID}"); QueueDel(childPart); @@ -177,7 +178,7 @@ private void MapInitParts(EntityUid rootPartId, BodyPrototype prototype) } // Add organs - SetupOrgans(childPart, childPartComponent, connectionSlot.Organs); + SetupOrgans((childPart, childPartComponent), connectionSlot.Organs); // Enqueue it so we can also get its neighbors. frontier.Enqueue(connection); @@ -185,16 +186,16 @@ private void MapInitParts(EntityUid rootPartId, BodyPrototype prototype) } } - private void SetupOrgans(EntityUid partId, BodyPartComponent partComponent, Dictionary organs) + private void SetupOrgans(Entity ent, Dictionary organs) { foreach (var (organSlotId, organProto) in organs) { - var slot = CreateOrganSlot(organSlotId, partId, partComponent); - SpawnInContainerOrDrop(organProto, partId, GetOrganContainerId(organSlotId)); + var slot = CreateOrganSlot((ent, ent), organSlotId); + SpawnInContainerOrDrop(organProto, ent, GetOrganContainerId(organSlotId)); - if (slot == null) + if (slot is null) { - Log.Error($"Could not create organ for slot {organSlotId} in {ToPrettyString(partId)}"); + Log.Error($"Could not create organ for slot {organSlotId} in {ToPrettyString(ent)}"); } } } @@ -202,12 +203,14 @@ private void SetupOrgans(EntityUid partId, BodyPartComponent partComponent, Dict /// /// Gets all body containers on this entity including the root one. /// - public IEnumerable GetBodyContainers(EntityUid id, BodyComponent? body = null, + public IEnumerable GetBodyContainers( + EntityUid id, + BodyComponent? body = null, BodyPartComponent? rootPart = null) { - if (!Resolve(id, ref body, false) || - body.RootContainer.ContainedEntity == null || - !Resolve(body.RootContainer.ContainedEntity.Value, ref rootPart)) + if (!Resolve(id, ref body, logMissing: false) + || body.RootContainer.ContainedEntity is null + || !Resolve(body.RootContainer.ContainedEntity.Value, ref rootPart)) { yield break; } @@ -223,13 +226,15 @@ public IEnumerable GetBodyContainers(EntityUid id, BodyComponent? /// /// Gets all child body parts of this entity, including the root entity. /// - public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyChildren(EntityUid? id, BodyComponent? body = null, + public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyChildren( + EntityUid? id, + BodyComponent? body = null, BodyPartComponent? rootPart = null) { - if (id == null || - !Resolve(id.Value, ref body, false) || - body.RootContainer.ContainedEntity == null || - !Resolve(body.RootContainer.ContainedEntity.Value, ref rootPart)) + if (id is null + || !Resolve(id.Value, ref body, logMissing: false) + || body.RootContainer.ContainedEntity is null + || !Resolve(body.RootContainer.ContainedEntity.Value, ref rootPart)) { yield break; } @@ -240,9 +245,11 @@ public IEnumerable GetBodyContainers(EntityUid id, BodyComponent? } } - public IEnumerable<(EntityUid Id, OrganComponent Component)> GetBodyOrgans(EntityUid? bodyId, BodyComponent? body = null) + public IEnumerable<(EntityUid Id, OrganComponent Component)> GetBodyOrgans( + EntityUid? bodyId, + BodyComponent? body = null) { - if (bodyId == null || !Resolve(bodyId.Value, ref body, false)) + if (bodyId is null || !Resolve(bodyId.Value, ref body, logMissing: false)) yield break; foreach (var part in GetBodyChildren(bodyId, body)) @@ -260,10 +267,15 @@ public IEnumerable GetBodyContainers(EntityUid id, BodyComponent? /// /// /// - public IEnumerable GetBodyAllSlots(EntityUid bodyId, BodyComponent? body = null) + public IEnumerable GetBodyAllSlots( + EntityUid bodyId, + BodyComponent? body = null) { - if (!Resolve(bodyId, ref body, false) || body.RootContainer.ContainedEntity == null) + if (!Resolve(bodyId, ref body, logMissing: false) + || body.RootContainer.ContainedEntity is null) + { yield break; + } foreach (var slot in GetAllBodyPartSlots(body.RootContainer.ContainedEntity.Value)) { @@ -279,12 +291,11 @@ public virtual HashSet GibBody( Vector2? splatDirection = null, float splatModifier = 1, Angle splatCone = default, - SoundSpecifier? gibSoundOverride = null - ) + SoundSpecifier? gibSoundOverride = null) { var gibs = new HashSet(); - if (!Resolve(bodyId, ref body, false)) + if (!Resolve(bodyId, ref body, logMissing: false)) return gibs; var root = GetRootPartOrNull(bodyId, body); @@ -311,7 +322,7 @@ public virtual HashSet GibBody( launchImpulseVariance:GibletLaunchImpulseVariance, launchCone: splatCone); } } - if(TryComp(bodyId, out var inventory)) + if (TryComp(bodyId, out var inventory)) { foreach (var item in _inventory.GetHandOrInventoryEntities(bodyId)) { diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs b/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs index fa11390705..efabebfc85 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Organs.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using Content.Shared.Body.Components; using Content.Shared.Body.Events; using Content.Shared.Body.Organ; @@ -9,41 +9,50 @@ namespace Content.Shared.Body.Systems; public partial class SharedBodySystem { - private void AddOrgan(EntityUid uid, EntityUid bodyUid, EntityUid parentPartUid, OrganComponent component) + private void AddOrgan( + Entity organEnt, + EntityUid bodyUid, + EntityUid parentPartUid) { - component.Body = bodyUid; - RaiseLocalEvent(uid, new AddedToPartEvent(parentPartUid)); + organEnt.Comp.Body = bodyUid; + var addedEv = new OrganAddedEvent(parentPartUid); + RaiseLocalEvent(organEnt, ref addedEv); - if (component.Body != null) - RaiseLocalEvent(uid, new AddedToPartInBodyEvent(component.Body.Value, parentPartUid)); + if (organEnt.Comp.Body is not null) + { + var addedInBodyEv = new OrganAddedToBodyEvent(bodyUid, parentPartUid); + RaiseLocalEvent(organEnt, ref addedInBodyEv); + } - Dirty(uid, component); + Dirty(organEnt, organEnt.Comp); } - private void RemoveOrgan(EntityUid uid, EntityUid parentPartUid, OrganComponent component) + private void RemoveOrgan(Entity organEnt, EntityUid parentPartUid) { - RaiseLocalEvent(uid, new RemovedFromPartEvent(parentPartUid)); + var removedEv = new OrganRemovedEvent(parentPartUid); + RaiseLocalEvent(organEnt, ref removedEv); - if (component.Body != null) + if (organEnt.Comp.Body is { Valid: true } bodyUid) { - RaiseLocalEvent(uid, new RemovedFromPartInBodyEvent(component.Body.Value, parentPartUid)); + var removedInBodyEv = new OrganRemovedFromBodyEvent(bodyUid, parentPartUid); + RaiseLocalEvent(organEnt, ref removedInBodyEv); } - component.Body = null; - Dirty(uid, component); + organEnt.Comp.Body = null; + Dirty(organEnt, organEnt.Comp); } /// /// Creates the specified organ slot on the parent entity. /// - private OrganSlot? CreateOrganSlot(string slotId, EntityUid parent, BodyPartComponent? part = null) + private OrganSlot? CreateOrganSlot(Entity parentEnt, string slotId) { - if (!Resolve(parent, ref part, false)) + if (!Resolve(parentEnt, ref parentEnt.Comp, logMissing: false)) return null; - Containers.EnsureContainer(parent, GetOrganContainerId(slotId)); + Containers.EnsureContainer(parentEnt, GetOrganContainerId(slotId)); var slot = new OrganSlot(slotId); - part.Organs.Add(slotId, slot); + parentEnt.Comp.Organs.Add(slotId, slot); return slot; } @@ -58,20 +67,23 @@ public bool TryCreateOrganSlot( { slot = null; - if (parent == null || !Resolve(parent.Value, ref part, false)) + if (parent is null || !Resolve(parent.Value, ref part, logMissing: false)) { return false; } Containers.EnsureContainer(parent.Value, GetOrganContainerId(slotId)); slot = new OrganSlot(slotId); - return part.Organs.TryAdd(slotId,slot.Value); + return part.Organs.TryAdd(slotId, slot.Value); } /// /// Returns whether the slotId exists on the partId. /// - public bool CanInsertOrgan(EntityUid partId, string slotId, BodyPartComponent? part = null) + public bool CanInsertOrgan( + EntityUid partId, + string slotId, + BodyPartComponent? part = null) { return Resolve(partId, ref part) && part.Organs.ContainsKey(slotId); } @@ -79,26 +91,32 @@ public bool CanInsertOrgan(EntityUid partId, string slotId, BodyPartComponent? p /// /// Returns whether the specified organ slot exists on the partId. /// - public bool CanInsertOrgan(EntityUid partId, OrganSlot slot, BodyPartComponent? part = null) + public bool CanInsertOrgan( + EntityUid partId, + OrganSlot slot, + BodyPartComponent? part = null) { return CanInsertOrgan(partId, slot.Id, part); } - public bool InsertOrgan(EntityUid partId, EntityUid organId, string slotId, BodyPartComponent? part = null, OrganComponent? organ = null) + public bool InsertOrgan( + EntityUid partId, + EntityUid organId, + string slotId, + BodyPartComponent? part = null, + OrganComponent? organ = null) { - if (!Resolve(organId, ref organ, false) || - !Resolve(partId, ref part, false) || - !CanInsertOrgan(partId, slotId, part)) + if (!Resolve(organId, ref organ, logMissing: false) + || !Resolve(partId, ref part, logMissing: false) + || !CanInsertOrgan(partId, slotId, part)) { return false; } var containerId = GetOrganContainerId(slotId); - if (!Containers.TryGetContainer(partId, containerId, out var container)) - return false; - - return Containers.Insert(organId, container); + return Containers.TryGetContainer(partId, containerId, out var container) + && Containers.Insert(organId, container); } /// @@ -111,10 +129,8 @@ public bool RemoveOrgan(EntityUid organId, OrganComponent? organ = null) var parent = container.Owner; - if (!HasComp(parent)) - return false; - - return Containers.Remove(organId, container); + return HasComp(parent) + && Containers.Remove(organId, container); } /// @@ -126,8 +142,8 @@ public bool AddOrganToFirstValidSlot( BodyPartComponent? part = null, OrganComponent? organ = null) { - if (!Resolve(partId, ref part, false) || - !Resolve(organId, ref organ, false)) + if (!Resolve(partId, ref part, logMissing: false) + || !Resolve(organId, ref organ, logMissing: false)) { return false; } diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs index e07aac0622..ee79faa0b8 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.Body.Components; using Content.Shared.Body.Events; @@ -23,52 +23,52 @@ private void InitializeParts() SubscribeLocalEvent(OnBodyPartRemoved); } - private void OnBodyPartInserted(EntityUid uid, BodyPartComponent component, EntInsertedIntoContainerMessage args) + private void OnBodyPartInserted(Entity ent, ref EntInsertedIntoContainerMessage args) { // Body part inserted into another body part. - var entity = args.Entity; + var insertedUid = args.Entity; var slotId = args.Container.ID; - if (component.Body == null) + if (ent.Comp.Body is null) return; - if (TryComp(entity, out BodyPartComponent? childPart)) + if (TryComp(insertedUid, out BodyPartComponent? part)) { - AddPart(component.Body.Value, entity, slotId, childPart); - RecursiveBodyUpdate(entity, component.Body.Value, childPart); + AddPart(ent.Comp.Body.Value, (insertedUid, part), slotId); + RecursiveBodyUpdate((insertedUid, part), ent.Comp.Body.Value); } - if (TryComp(entity, out OrganComponent? organ)) - AddOrgan(entity, component.Body.Value, uid, organ); + if (TryComp(insertedUid, out OrganComponent? organ)) + AddOrgan((insertedUid, organ), ent.Comp.Body.Value, ent); } - private void OnBodyPartRemoved(EntityUid uid, BodyPartComponent component, EntRemovedFromContainerMessage args) + private void OnBodyPartRemoved(Entity ent, ref EntRemovedFromContainerMessage args) { // Body part removed from another body part. - var entity = args.Entity; + var removedUid = args.Entity; var slotId = args.Container.ID; - DebugTools.Assert(!TryComp(entity, out BodyPartComponent? b) || b.Body == component.Body); - DebugTools.Assert(!TryComp(entity, out OrganComponent? o) || o.Body == component.Body); + DebugTools.Assert(!TryComp(removedUid, out BodyPartComponent? b) || b.Body == ent.Comp.Body); + DebugTools.Assert(!TryComp(removedUid, out OrganComponent? o) || o.Body == ent.Comp.Body); - if (TryComp(entity, out BodyPartComponent? childPart) && childPart.Body != null) + if (TryComp(removedUid, out BodyPartComponent? part) && part.Body is not null) { - RemovePart(childPart.Body.Value, entity, slotId, childPart); - RecursiveBodyUpdate(entity, null, childPart); + RemovePart(part.Body.Value, (removedUid, part), slotId); + RecursiveBodyUpdate((removedUid, part), null); } - if (TryComp(entity, out OrganComponent? organ)) - RemoveOrgan(entity, uid, organ); + if (TryComp(removedUid, out OrganComponent? organ)) + RemoveOrgan((removedUid, organ), ent); } - private void RecursiveBodyUpdate(EntityUid uid, EntityUid? bodyUid, BodyPartComponent component) + private void RecursiveBodyUpdate(Entity ent, EntityUid? bodyUid) { - component.Body = bodyUid; - Dirty(uid, component); + ent.Comp.Body = bodyUid; + Dirty(ent, ent.Comp); - foreach (var slotId in component.Organs.Keys) + foreach (var slotId in ent.Comp.Organs.Keys) { - if (!Containers.TryGetContainer(uid, GetOrganContainerId(slotId), out var container)) + if (!Containers.TryGetContainer(ent, GetOrganContainerId(slotId), out var container)) continue; foreach (var organ in container.ContainedEntities) @@ -78,105 +78,108 @@ private void RecursiveBodyUpdate(EntityUid uid, EntityUid? bodyUid, BodyPartComp Dirty(organ, organComp); - if (organComp.Body != null) - RaiseLocalEvent(organ, new RemovedFromPartInBodyEvent(organComp.Body.Value, uid)); + if (organComp.Body is { Valid: true } oldBodyUid) + { + var removedEv = new OrganRemovedFromBodyEvent(oldBodyUid, ent); + RaiseLocalEvent(organ, ref removedEv); + } organComp.Body = bodyUid; - if (bodyUid != null) - RaiseLocalEvent(organ, new AddedToPartInBodyEvent(bodyUid.Value, uid)); + if (bodyUid is not null) + { + var addedEv = new OrganAddedToBodyEvent(bodyUid.Value, ent); + RaiseLocalEvent(organ, ref addedEv); + } } } - foreach (var slotId in component.Children.Keys) + foreach (var slotId in ent.Comp.Children.Keys) { - if (!Containers.TryGetContainer(uid, GetPartSlotContainerId(slotId), out var container)) + if (!Containers.TryGetContainer(ent, GetPartSlotContainerId(slotId), out var container)) continue; - foreach (var containedEnt in container.ContainedEntities) + foreach (var containedUid in container.ContainedEntities) { - if (TryComp(containedEnt, out BodyPartComponent? childPart)) - RecursiveBodyUpdate(containedEnt, bodyUid, childPart); + if (TryComp(containedUid, out BodyPartComponent? childPart)) + RecursiveBodyUpdate((containedUid, childPart), bodyUid); } } } protected virtual void AddPart( - EntityUid bodyUid, - EntityUid partUid, - string slotId, - BodyPartComponent component, - BodyComponent? bodyComp = null) + Entity bodyEnt, + Entity partEnt, + string slotId) { - DebugTools.AssertOwner(partUid, component); - Dirty(partUid, component); - component.Body = bodyUid; + Dirty(partEnt, partEnt.Comp); + partEnt.Comp.Body = bodyEnt; - var ev = new BodyPartAddedEvent(slotId, component); - RaiseLocalEvent(bodyUid, ref ev); + var ev = new BodyPartAddedEvent(slotId, partEnt); + RaiseLocalEvent(bodyEnt, ref ev); - AddLeg(partUid, bodyUid, component, bodyComp); + AddLeg(partEnt, bodyEnt); } protected virtual void RemovePart( - EntityUid bodyUid, - EntityUid partUid, - string slotId, - BodyPartComponent component, - BodyComponent? bodyComp = null) + Entity bodyEnt, + Entity partEnt, + string slotId) { - DebugTools.AssertOwner(partUid, component); - Resolve(bodyUid, ref bodyComp, false); - Dirty(partUid, component); - component.Body = null; + Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false); + Dirty(partEnt, partEnt.Comp); + partEnt.Comp.Body = null; - var ev = new BodyPartRemovedEvent(slotId, component); - RaiseLocalEvent(bodyUid, ref ev); + var ev = new BodyPartRemovedEvent(slotId, partEnt); + RaiseLocalEvent(bodyEnt, ref ev); - RemoveLeg(partUid, bodyUid, component); - PartRemoveDamage(bodyUid, component, bodyComp); + RemoveLeg(partEnt, bodyEnt); + PartRemoveDamage(bodyEnt, partEnt); } - private void AddLeg(EntityUid uid, EntityUid bodyUid, BodyPartComponent component, BodyComponent? bodyComp = null) + private void AddLeg(Entity legEnt, Entity bodyEnt) { - if (!Resolve(bodyUid, ref bodyComp, false)) + if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false)) return; - if (component.PartType == BodyPartType.Leg) + if (legEnt.Comp.PartType == BodyPartType.Leg) { - bodyComp.LegEntities.Add(uid); - UpdateMovementSpeed(bodyUid); - Dirty(bodyUid, bodyComp); + bodyEnt.Comp.LegEntities.Add(legEnt); + UpdateMovementSpeed(bodyEnt); + Dirty(bodyEnt, bodyEnt.Comp); } } - private void RemoveLeg(EntityUid uid, EntityUid bodyUid, BodyPartComponent component, BodyComponent? bodyComp = null) + private void RemoveLeg(Entity legEnt, Entity bodyEnt) { - if (!Resolve(bodyUid, ref bodyComp, false)) + if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false)) return; - if (component.PartType == BodyPartType.Leg) + if (legEnt.Comp.PartType == BodyPartType.Leg) { - bodyComp.LegEntities.Remove(uid); - UpdateMovementSpeed(bodyUid); - Dirty(bodyUid, bodyComp); + bodyEnt.Comp.LegEntities.Remove(legEnt); + UpdateMovementSpeed(bodyEnt); + Dirty(bodyEnt, bodyEnt.Comp); - if (!bodyComp.LegEntities.Any()) + if (!bodyEnt.Comp.LegEntities.Any()) { - Standing.Down(bodyUid); + Standing.Down(bodyEnt); } } } - private void PartRemoveDamage(EntityUid parent, BodyPartComponent component, BodyComponent? bodyComp = null) + private void PartRemoveDamage(Entity bodyEnt, Entity partEnt) { - if (!Resolve(parent, ref bodyComp, false)) + if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false)) return; - if (!_timing.ApplyingState && component.IsVital && !GetBodyChildrenOfType(parent, component.PartType, bodyComp).Any()) + if (!_timing.ApplyingState + && partEnt.Comp.IsVital + && !GetBodyChildrenOfType(bodyEnt, partEnt.Comp.PartType, bodyEnt.Comp).Any() + ) { // TODO BODY SYSTEM KILL : remove this when wounding and required parts are implemented properly var damage = new DamageSpecifier(Prototypes.Index("Bloodloss"), 300); - Damageable.TryChangeDamage(parent, damage); + Damageable.TryChangeDamage(bodyEnt, damage); } } @@ -212,7 +215,8 @@ private void PartRemoveDamage(EntityUid parent, BodyPartComponent component, Bod var parent = container.Owner; - if (!TryComp(parent, out var parentBody) || !parentBody.Children.ContainsKey(slotId)) + if (!TryComp(parent, out var parentBody) + || !parentBody.Children.ContainsKey(slotId)) return null; return (parent, slotId); @@ -252,7 +256,7 @@ public bool TryGetParentBodyPart( BodyPartType partType, BodyPartComponent? part = null) { - if (!Resolve(partUid, ref part, false)) + if (!Resolve(partUid, ref part, logMissing: false)) return null; Containers.EnsureContainer(partUid, GetPartSlotContainerId(slotId)); @@ -275,8 +279,8 @@ public bool TryCreatePartSlot( { slot = null; - if (partId == null || - !Resolve(partId.Value, ref part, false)) + if (partId is null + || !Resolve(partId.Value, ref part, logMissing: false)) { return false; } @@ -310,24 +314,31 @@ public bool TryCreatePartSlotAndAttach( /// /// Returns true if the partId is the root body container for the specified bodyId. /// - public bool IsPartRoot(EntityUid bodyId, EntityUid partId, BodyComponent? body = null, BodyPartComponent? part = null) + public bool IsPartRoot( + EntityUid bodyId, + EntityUid partId, + BodyComponent? body = null, + BodyPartComponent? part = null) { - if (!Resolve(partId, ref part)|| !Resolve(bodyId, ref body)) - return false; - - return Containers.TryGetContainingContainer(bodyId, partId, out var container) && container.ID == BodyRootContainerId; + return Resolve(partId, ref part) + && Resolve(bodyId, ref body) + && Containers.TryGetContainingContainer(bodyId, partId, out var container) + && container.ID == BodyRootContainerId; } /// /// Returns true if we can attach the partId to the bodyId as the root entity. /// - public bool CanAttachToRoot(EntityUid bodyId, EntityUid partId, BodyComponent? body = null, + public bool CanAttachToRoot( + EntityUid bodyId, + EntityUid partId, + BodyComponent? body = null, BodyPartComponent? part = null) { - return Resolve(bodyId, ref body) && - Resolve(partId, ref part) && - body.RootContainer.ContainedEntity == null && - bodyId != part.Body; + return Resolve(bodyId, ref body) + && Resolve(partId, ref part) + && body.RootContainer.ContainedEntity is null + && bodyId != part.Body; } /// @@ -335,8 +346,11 @@ public bool CanAttachToRoot(EntityUid bodyId, EntityUid partId, BodyComponent? b /// public (EntityUid Entity, BodyPartComponent BodyPart)? GetRootPartOrNull(EntityUid bodyId, BodyComponent? body = null) { - if (!Resolve(bodyId, ref body) || body.RootContainer.ContainedEntity == null) + if (!Resolve(bodyId, ref body) + || body.RootContainer.ContainedEntity is null) + { return null; + } return (body.RootContainer.ContainedEntity.Value, Comp(body.RootContainer.ContainedEntity.Value)); @@ -352,13 +366,9 @@ public bool CanAttachPart( BodyPartComponent? parentPart = null, BodyPartComponent? part = null) { - if (!Resolve(partId, ref part, false) || - !Resolve(parentId, ref parentPart, false)) - { - return false; - } - - return CanAttachPart(parentId, slot.Id, partId, parentPart, part); + return Resolve(partId, ref part, logMissing: false) + && Resolve(parentId, ref parentPart, logMissing: false) + && CanAttachPart(parentId, slot.Id, partId, parentPart, part); } /// @@ -371,16 +381,12 @@ public bool CanAttachPart( BodyPartComponent? parentPart = null, BodyPartComponent? part = null) { - if (!Resolve(partId, ref part, false) || - !Resolve(parentId, ref parentPart, false) || - !parentPart.Children.TryGetValue(slotId, out var parentSlotData)) - { - return false; - } - - return part.PartType == parentSlotData.Type && - Containers.TryGetContainer(parentId, GetPartSlotContainerId(slotId), out var container) && - Containers.CanInsert(partId, container); + return Resolve(partId, ref part, logMissing: false) + && Resolve(parentId, ref parentPart, logMissing: false) + && parentPart.Children.TryGetValue(slotId, out var parentSlotData) + && part.PartType == parentSlotData.Type + && Containers.TryGetContainer(parentId, GetPartSlotContainerId(slotId), out var container) + && Containers.CanInsert(partId, container); } public bool AttachPartToRoot( @@ -389,14 +395,10 @@ public bool AttachPartToRoot( BodyComponent? body = null, BodyPartComponent? part = null) { - if (!Resolve(bodyId, ref body) || - !Resolve(partId, ref part) || - !CanAttachToRoot(bodyId, partId, body, part)) - { - return false; - } - - return Containers.Insert(partId, body.RootContainer); + return Resolve(bodyId, ref body) + && Resolve(partId, ref part) + && CanAttachToRoot(bodyId, partId, body, part) + && Containers.Insert(partId, body.RootContainer); } #endregion @@ -406,20 +408,16 @@ public bool AttachPartToRoot( /// /// Attaches a body part to the specified body part parent. /// - public bool AttachPart( - EntityUid parentPartId, - string slotId, - EntityUid partId, - BodyPartComponent? parentPart = null, - BodyPartComponent? part = null) - { - if (!Resolve(parentPartId, ref parentPart, false) || - !parentPart.Children.TryGetValue(slotId, out var slot)) - { - return false; - } - - return AttachPart(parentPartId, slot, partId, parentPart, part); + public bool AttachPart( + EntityUid parentPartId, + string slotId, + EntityUid partId, + BodyPartComponent? parentPart = null, + BodyPartComponent? part = null) + { + return Resolve(parentPartId, ref parentPart, logMissing: false) + && parentPart.Children.TryGetValue(slotId, out var slot) + && AttachPart(parentPartId, slot, partId, parentPart, part); } /// @@ -432,10 +430,10 @@ public bool AttachPart( BodyPartComponent? parentPart = null, BodyPartComponent? part = null) { - if (!Resolve(parentPartId, ref parentPart, false) || - !Resolve(partId, ref part, false) || - !CanAttachPart(parentPartId, slot.Id, partId, parentPart, part) || - !parentPart.Children.ContainsKey(slot.Id)) + if (!Resolve(parentPartId, ref parentPart, logMissing: false) + || !Resolve(partId, ref part, logMissing: false) + || !CanAttachPart(parentPartId, slot.Id, partId, parentPart, part) + || !parentPart.Children.ContainsKey(slot.Id)) { return false; } @@ -453,13 +451,16 @@ public bool AttachPart( #region Misc - public void UpdateMovementSpeed(EntityUid bodyId, BodyComponent? body = null, MovementSpeedModifierComponent? movement = null) + public void UpdateMovementSpeed( + EntityUid bodyId, + BodyComponent? body = null, + MovementSpeedModifierComponent? movement = null) { - if (!Resolve(bodyId, ref body, ref movement, false)) - return; - - if (body.RequiredLegs <= 0) + if (!Resolve(bodyId, ref body, ref movement, logMissing: false) + || body.RequiredLegs <= 0) + { return; + } var walkSpeed = 0f; var sprintSpeed = 0f; @@ -488,7 +489,7 @@ public void UpdateMovementSpeed(EntityUid bodyId, BodyComponent? body = null, Mo /// public IEnumerable<(EntityUid Id, OrganComponent Component)> GetPartOrgans(EntityUid partId, BodyPartComponent? part = null) { - if (!Resolve(partId, ref part, false)) + if (!Resolve(partId, ref part, logMissing: false)) yield break; foreach (var slotId in part.Organs.Keys) @@ -513,7 +514,7 @@ public void UpdateMovementSpeed(EntityUid bodyId, BodyComponent? body = null, Mo /// public IEnumerable GetPartContainers(EntityUid id, BodyPartComponent? part = null) { - if (!Resolve(id, ref part, false) || + if (!Resolve(id, ref part, logMissing: false) || part.Children.Count == 0) { yield break; @@ -541,9 +542,11 @@ public IEnumerable GetPartContainers(EntityUid id, BodyPartCompon /// /// Returns all body part components for this entity including itself. /// - public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyPartChildren(EntityUid partId, BodyPartComponent? part = null) + public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyPartChildren( + EntityUid partId, + BodyPartComponent? part = null) { - if (!Resolve(partId, ref part, false)) + if (!Resolve(partId, ref part, logMissing: false)) yield break; yield return (partId, part); @@ -571,9 +574,11 @@ public IEnumerable GetPartContainers(EntityUid id, BodyPartCompon /// /// Returns all body part slots for this entity. /// - public IEnumerable GetAllBodyPartSlots(EntityUid partId, BodyPartComponent? part = null) + public IEnumerable GetAllBodyPartSlots( + EntityUid partId, + BodyPartComponent? part = null) { - if (!Resolve(partId, ref part, false)) + if (!Resolve(partId, ref part, logMissing: false)) yield break; foreach (var (slotId, slot) in part.Children) @@ -601,7 +606,10 @@ public IEnumerable GetAllBodyPartSlots(EntityUid partId, BodyPartC /// /// Returns true if the bodyId has any parts of this type. /// - public bool BodyHasPartType(EntityUid bodyId, BodyPartType type, BodyComponent? body = null) + public bool BodyHasPartType( + EntityUid bodyId, + BodyPartType type, + BodyComponent? body = null) { return GetBodyChildrenOfType(bodyId, type, body).Any(); } @@ -615,8 +623,8 @@ public bool PartHasChild( BodyPartComponent? parent, BodyPartComponent? child) { - if (!Resolve(parentId, ref parent, false) || - !Resolve(childId, ref child, false)) + if (!Resolve(parentId, ref parent, logMissing: false) + || !Resolve(childId, ref child, logMissing: false)) { return false; } @@ -638,15 +646,11 @@ public bool BodyHasChild( BodyComponent? body = null, BodyPartComponent? part = null) { - if (!Resolve(bodyId, ref body, false) || - body.RootContainer.ContainedEntity == null || - !Resolve(partId, ref part, false) || - !TryComp(body.RootContainer.ContainedEntity, out BodyPartComponent? rootPart)) - { - return false; - } - - return PartHasChild(body.RootContainer.ContainedEntity.Value, partId, rootPart, part); + return Resolve(bodyId, ref body, logMissing: false) + && body.RootContainer.ContainedEntity is not null + && Resolve(partId, ref part, logMissing: false) + && TryComp(body.RootContainer.ContainedEntity, out BodyPartComponent? rootPart) + && PartHasChild(body.RootContainer.ContainedEntity.Value, partId, rootPart, part); } public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyChildrenOfType( @@ -721,9 +725,11 @@ public bool TryGetBodyPartOrganComponents( /// /// Gets the parent body part and all immediate child body parts for the partId. /// - public IEnumerable GetBodyPartAdjacentParts(EntityUid partId, BodyPartComponent? part = null) + public IEnumerable GetBodyPartAdjacentParts( + EntityUid partId, + BodyPartComponent? part = null) { - if (!Resolve(partId, ref part, false)) + if (!Resolve(partId, ref part, logMissing: false)) yield break; if (TryGetParentBodyPart(partId, out var parentUid, out _)) @@ -745,7 +751,7 @@ public IEnumerable GetBodyPartAdjacentParts(EntityUid partId, BodyPar BodyPartComponent? part = null) where T : IComponent { - if (!Resolve(partId, ref part, false)) + if (!Resolve(partId, ref part, logMissing: false)) yield break; var query = GetEntityQuery(); @@ -762,7 +768,7 @@ public bool TryGetBodyPartAdjacentPartsComponents( BodyPartComponent? part = null) where T : IComponent { - if (!Resolve(partId, ref part, false)) + if (!Resolve(partId, ref part, logMissing: false)) { comps = null; return false; diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs index 8172947a03..3355713bb2 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs @@ -10,7 +10,6 @@ using Content.Shared.Mobs.Components; using Content.Shared.Movement.Events; using Content.Shared.Popups; -using Content.Shared.Pulling.Components; using Content.Shared.Standing; using Content.Shared.Storage.Components; using Content.Shared.Stunnable; @@ -19,6 +18,7 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; using Robust.Shared.Utility; +using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent; namespace Content.Shared.Buckle; @@ -348,11 +348,11 @@ public bool TryBuckle(EntityUid buckleUid, EntityUid userUid, EntityUid strapUid RaiseLocalEvent(ev.BuckledEntity, ref ev); RaiseLocalEvent(ev.StrapEntity, ref ev); - if (TryComp(buckleUid, out var ownerPullable)) + if (TryComp(buckleUid, out var ownerPullable)) { if (ownerPullable.Puller != null) { - _pulling.TryStopPull(ownerPullable); + _pulling.TryStopPull(buckleUid, ownerPullable); } } @@ -361,12 +361,12 @@ public bool TryBuckle(EntityUid buckleUid, EntityUid userUid, EntityUid strapUid _physics.ResetDynamics(buckleUid, physics); } - if (!buckleComp.PullStrap && TryComp(strapUid, out var toPullable)) + if (!buckleComp.PullStrap && TryComp(strapUid, out var toPullable)) { if (toPullable.Puller == buckleUid) { // can't pull it and buckle to it at the same time - _pulling.TryStopPull(toPullable); + _pulling.TryStopPull(strapUid, toPullable); } } diff --git a/Content.Shared/Buckle/SharedBuckleSystem.cs b/Content.Shared/Buckle/SharedBuckleSystem.cs index 8f68335663..67218657e5 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.cs @@ -15,6 +15,7 @@ using Robust.Shared.Physics.Systems; using Robust.Shared.Player; using Robust.Shared.Timing; +using PullingSystem = Content.Shared.Movement.Pulling.Systems.PullingSystem; namespace Content.Shared.Buckle; @@ -35,7 +36,7 @@ public abstract partial class SharedBuckleSystem : EntitySystem [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SharedJointSystem _joints = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly SharedPullingSystem _pulling = default!; + [Dependency] private readonly PullingSystem _pulling = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly StandingStateSystem _standing = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; diff --git a/Content.Shared/Chat/V2/Moderation/ChatCensor.cs b/Content.Shared/Chat/V2/Moderation/ChatCensor.cs new file mode 100644 index 0000000000..b5d6aa0344 --- /dev/null +++ b/Content.Shared/Chat/V2/Moderation/ChatCensor.cs @@ -0,0 +1,59 @@ +using System.Linq; + +namespace Content.Shared.Chat.V2.Moderation; + +public interface IChatCensor +{ + public bool Censor(string input, out string output, char replaceWith = '*'); +} + +public sealed class CompoundChatCensor(IEnumerable censors) : IChatCensor +{ + public bool Censor(string input, out string output, char replaceWith = '*') + { + var censored = false; + + foreach (var censor in censors) + { + if (censor.Censor(input, out output, replaceWith)) + { + censored = true; + } + } + + output = input; + + return censored; + } +} + +public sealed class ChatCensorFactory +{ + private List _censors = new(); + + public void With(IChatCensor censor) + { + _censors.Add(censor); + } + + /// + /// Builds a ChatCensor that combines all the censors that have been added to this. + /// + public IChatCensor Build() + { + return new CompoundChatCensor(_censors.ToArray()); + } + + /// + /// Resets the build state to zero, allowing for different rules to be provided to the next censor(s) built. + /// + /// True if the builder had any setup prior to the reset. + public bool Reset() + { + var notEmpty = _censors.Count > 0; + + _censors = new List(); + + return notEmpty; + } +} diff --git a/Content.Shared/Chat/V2/Moderation/RegexCensor.cs b/Content.Shared/Chat/V2/Moderation/RegexCensor.cs new file mode 100644 index 0000000000..cd47bf0c33 --- /dev/null +++ b/Content.Shared/Chat/V2/Moderation/RegexCensor.cs @@ -0,0 +1,15 @@ +using System.Text.RegularExpressions; + +namespace Content.Shared.Chat.V2.Moderation; + +public sealed class RegexCensor(Regex censorInstruction) : IChatCensor +{ + private readonly Regex _censorInstruction = censorInstruction; + + public bool Censor(string input, out string output, char replaceWith = '*') + { + output = _censorInstruction.Replace(input, replaceWith.ToString()); + + return !string.Equals(input, output); + } +} diff --git a/Content.Shared/Chat/V2/Moderation/SimpleCensor.cs b/Content.Shared/Chat/V2/Moderation/SimpleCensor.cs new file mode 100644 index 0000000000..a6bb70dd9f --- /dev/null +++ b/Content.Shared/Chat/V2/Moderation/SimpleCensor.cs @@ -0,0 +1,340 @@ +using System.Collections.Frozen; +using System.Linq; +using System.Text; +using System.Text.Unicode; + +namespace Content.Shared.Chat.V2.Moderation; + +/// +/// A basic censor. Not bullet-proof. +/// +public sealed class SimpleCensor : IChatCensor +{ + // Common substitution symbols are replaced with one of the characters they commonly substitute. + private bool _shouldSanitizeLeetspeak; + private FrozenDictionary _leetspeakReplacements = FrozenDictionary.Empty; + + // Special characters are replaced with spaces. + private bool _shouldSanitizeSpecialCharacters; + private HashSet _specialCharacterReplacements = []; + + // Censored words are removed unless they're a false positive (e.g. Scunthorpe) + private string[] _censoredWords = Array.Empty(); + private string[] _falsePositives = Array.Empty(); + + // False negatives are censored words that contain a false positives. + private string[] _falseNegatives = Array.Empty(); + + // What unicode ranges are allowed? If this array is empty, don't filter by range. + private UnicodeRange[] _allowedUnicodeRanges= Array.Empty(); + + /// + /// Censors the input string. + /// + /// The input string + /// The output string + /// The character to replace with + /// If output is valid + public bool Censor(string input, out string output, char replaceWith = '*') + { + output = Censor(input, replaceWith); + + return !string.Equals(input, output); + } + + public string Censor(string input, char replaceWith = '*') + { + // We flat-out ban anything not in the allowed unicode ranges, stripping them + input = SanitizeOutBlockedUnicode(input); + + var originalInput = input.ToCharArray(); + + input = SanitizeInput(input); + + var censored = input.ToList(); + + // Remove false negatives + input = CheckProfanity(input, censored, _falseNegatives, replaceWith); + + // Get false positives + var falsePositives = FindFalsePositives(censored, replaceWith); + + // Remove censored words + CheckProfanity(input, censored, _censoredWords, replaceWith); + + // Reconstruct + // Reconstruct false positives + for (var i = 0; i < falsePositives.Length; i++) + { + if (falsePositives[i] != replaceWith) + { + censored[i] = falsePositives[i]; + } + } + + for (var i = 0; i < originalInput.Length; i++) + { + if (originalInput[i] == ' ') + { + censored.Insert(i, ' '); + + continue; + } + + if (_shouldSanitizeSpecialCharacters && _specialCharacterReplacements.Contains(originalInput[i])) + { + censored.Insert(i, originalInput[i]); + + continue; + } + + if (_shouldSanitizeLeetspeak || _shouldSanitizeSpecialCharacters) + { + // detect "()" + if (originalInput[i] == '(' && i != originalInput.Length - 1 && originalInput[i+1] == ')') + { + // censored has now had "o" replaced with "o) so both strings line up again..." + censored.Insert(i+1, censored[i] != replaceWith ? ')' : replaceWith); + } + } + + if (censored[i] != replaceWith) + { + censored[i] = originalInput[i]; + } + } + + // SO says this is fast... + return string.Concat(censored); + } + + /// + /// Adds a l33tsp34k sanitization rule + /// + /// The censor for further configuration + public SimpleCensor WithSanitizeLeetSpeak() + { + _shouldSanitizeLeetspeak = true; + + return BuildCharacterReplacements(); + } + + /// + /// Adds a l33tsp34k sanitization rule + /// + /// The censor for further configuration + public SimpleCensor WithSanitizeSpecialCharacters() + { + _shouldSanitizeSpecialCharacters = true; + + return BuildCharacterReplacements(); + } + + public SimpleCensor WithRanges(UnicodeRange[] ranges) + { + _allowedUnicodeRanges = ranges; + + return this; + } + + public SimpleCensor WithCustomDictionary(string[] naughtyWords) + { + _censoredWords = naughtyWords; + + return this; + } + + public SimpleCensor WithFalsePositives(string[] falsePositives) + { + _falsePositives = falsePositives; + + return this; + } + + public SimpleCensor WithFalseNegatives(string[] falseNegatives) + { + _falseNegatives = falseNegatives; + + return this; + } + + public SimpleCensor WithLeetspeakReplacements(Dictionary replacements) + { + _leetspeakReplacements = replacements.ToFrozenDictionary(); + + return this; + } + + public SimpleCensor WithSpecialCharacterReplacements(Dictionary replacements) + { + _leetspeakReplacements = replacements.ToFrozenDictionary(); + + return this; + } + + private string CheckProfanity(string input, List censored, string[] words, char replaceWith = '*') + { + foreach (var word in words) + { + var wordLength = word.Length; + var endOfFoundWord = 0; + var foundIndex = input.IndexOf(word, endOfFoundWord, StringComparison.OrdinalIgnoreCase); + + while(foundIndex > -1) + { + endOfFoundWord = foundIndex + wordLength; + + for (var i = 0; i < wordLength; i++) + { + censored[foundIndex+i] = replaceWith; + } + + foundIndex = input.IndexOf(word, endOfFoundWord, StringComparison.OrdinalIgnoreCase); + } + } + + return input; + } + + private char[] FindFalsePositives(List chars, char replaceWith = '*') + { + var input = string.Concat(chars); + + var output = Enumerable.Repeat(replaceWith, input.Length).ToArray(); + var inputAsARr = input.ToArray(); + + foreach (var word in _falsePositives) + { + var wordLength = word.Length; + var endOfFoundWord = 0; + var foundIndex = input.IndexOf(word, endOfFoundWord, StringComparison.OrdinalIgnoreCase); + + while(foundIndex > -1) + { + endOfFoundWord = foundIndex + wordLength; + + for (var i = foundIndex; i < endOfFoundWord; i++) + { + output[i] = inputAsARr[i]; + } + + foundIndex = input.IndexOf(word, endOfFoundWord, StringComparison.OrdinalIgnoreCase); + } + } + + return output; + } + + private string SanitizeInput(string input) + { + // "()" is a broad enough trick to beat censors that we we should check for it broadly. + if (_shouldSanitizeLeetspeak || _shouldSanitizeSpecialCharacters) + { + input = input.Replace("()", "o"); + } + + var sb = new StringBuilder(); + + // ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator + foreach (var character in input) + { + if (character == ' ' || _shouldSanitizeSpecialCharacters && _specialCharacterReplacements.Contains(character)) + { + continue; + } + + if (_shouldSanitizeLeetspeak && _leetspeakReplacements.TryGetValue(character, out var leetRepl)) + { + sb.Append(leetRepl); + + continue; + } + + sb.Append(character); + } + + return sb.ToString(); + } + + /// + /// Returns a string with all characters not in ISO-8851-1 replaced with question marks + /// + private string SanitizeOutBlockedUnicode(string input) + { + if (_allowedUnicodeRanges.Length <= 0) + { + return input; + } + + var sb = new StringBuilder(); + + foreach (var symbol in input.EnumerateRunes()) + { + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (var range in _allowedUnicodeRanges) + { + if (symbol.Value < range.FirstCodePoint || symbol.Value >= range.FirstCodePoint + range.Length) + continue; + + sb.Append(symbol); + + break; + } + } + + return sb.ToString(); + } + + private SimpleCensor BuildCharacterReplacements() + { + if (_shouldSanitizeSpecialCharacters) + { + _specialCharacterReplacements = + [ + '-', + '_', + '|', + '.', + ',', + '(', + ')', + '<', + '>', + '"', + '`', + '~', + '*', + '&', + '%', + '$', + '#', + '@', + '!', + '?', + '+' + ]; + } + + if (_shouldSanitizeLeetspeak) + { + _leetspeakReplacements = new Dictionary + { + ['4'] = 'a', + ['$'] = 's', + ['!'] = 'i', + ['+'] = 't', + ['#'] = 'h', + ['@'] = 'a', + ['0'] = 'o', + ['1'] = 'i', // also obviously can be l; gamer-words need i's more though. + ['7'] = 'l', + ['3'] = 'e', + ['5'] = 's', + ['9'] = 'g', + ['<'] = 'c' + }.ToFrozenDictionary(); + } + + return this; + } +} diff --git a/Content.Shared/Climbing/Systems/ClimbSystem.cs b/Content.Shared/Climbing/Systems/ClimbSystem.cs index 6a2976a838..fceb6143e1 100644 --- a/Content.Shared/Climbing/Systems/ClimbSystem.cs +++ b/Content.Shared/Climbing/Systems/ClimbSystem.cs @@ -34,7 +34,6 @@ public sealed partial class ClimbSystem : VirtualController [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly FixtureSystem _fixtureSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedBodySystem _bodySystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; diff --git a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs index 70bcfbab43..c041cf1ba0 100644 --- a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs +++ b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs @@ -6,16 +6,15 @@ using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.Interaction; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Popups; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; using Content.Shared.Tools; using Content.Shared.Tools.Components; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Content.Shared.Tag; -using Robust.Shared.Player; using Robust.Shared.Serialization; using Robust.Shared.Utility; using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem; @@ -27,7 +26,7 @@ public sealed partial class AnchorableSystem : EntitySystem [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly SharedPullingSystem _pulling = default!; + [Dependency] private readonly PullingSystem _pulling = default!; [Dependency] private readonly SharedToolSystem _tool = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly TagSystem _tagSystem = default!; @@ -132,9 +131,9 @@ private void OnAnchorComplete(EntityUid uid, AnchorableComponent component, TryA var rot = xform.LocalRotation; xform.LocalRotation = Math.Round(rot / (Math.PI / 2)) * (Math.PI / 2); - if (TryComp(uid, out var pullable) && pullable.Puller != null) + if (TryComp(uid, out var pullable) && pullable.Puller != null) { - _pulling.TryStopPull(pullable); + _pulling.TryStopPull(uid, pullable); } // TODO: Anchoring snaps rn anyway! @@ -175,7 +174,7 @@ private void OnAnchorComplete(EntityUid uid, AnchorableComponent component, TryA public void TryToggleAnchor(EntityUid uid, EntityUid userUid, EntityUid usingUid, AnchorableComponent? anchorable = null, TransformComponent? transform = null, - SharedPullableComponent? pullable = null, + PullableComponent? pullable = null, ToolComponent? usingTool = null) { if (!Resolve(uid, ref transform)) @@ -198,7 +197,7 @@ public void TryToggleAnchor(EntityUid uid, EntityUid userUid, EntityUid usingUid private void TryAnchor(EntityUid uid, EntityUid userUid, EntityUid usingUid, AnchorableComponent? anchorable = null, TransformComponent? transform = null, - SharedPullableComponent? pullable = null, + PullableComponent? pullable = null, ToolComponent? usingTool = null) { if (!Resolve(uid, ref anchorable, ref transform)) @@ -271,7 +270,7 @@ private bool TileFree(EntityCoordinates coordinates, PhysicsComponent anchorBody // Probably ignore CanCollide on the anchoring body? var gridUid = coordinates.GetGridUid(EntityManager); - if (!_mapManager.TryGetGrid(gridUid, out var grid)) + if (!TryComp(gridUid, out var grid)) return false; var tileIndices = grid.TileIndicesFor(coordinates); diff --git a/Content.Shared/Construction/SharedFlatpackSystem.cs b/Content.Shared/Construction/SharedFlatpackSystem.cs index a62488d6f3..8b21bca52a 100644 --- a/Content.Shared/Construction/SharedFlatpackSystem.cs +++ b/Content.Shared/Construction/SharedFlatpackSystem.cs @@ -114,8 +114,7 @@ public void SetupFlatpack(Entity ent, EntityUid? board) if (!Resolve(ent, ref ent.Comp)) return; - EntProtoId machinePrototypeId; - string? entityPrototype; + var machinePrototypeId = new EntProtoId(); if (TryComp(board, out var machineBoard) && machineBoard.Prototype is not null) machinePrototypeId = machineBoard.Prototype; else if (TryComp(board, out var computerBoard) && computerBoard.Prototype is not null) diff --git a/Content.Shared/Coordinates/EntityCoordinatesExtensions.cs b/Content.Shared/Coordinates/EntityCoordinatesExtensions.cs index b9083eabe1..47d359d387 100644 --- a/Content.Shared/Coordinates/EntityCoordinatesExtensions.cs +++ b/Content.Shared/Coordinates/EntityCoordinatesExtensions.cs @@ -1,6 +1,5 @@ using System.Numerics; using Robust.Shared.Map; -using Robust.Shared.Map.Components; namespace Content.Shared.Coordinates { @@ -20,17 +19,5 @@ public static EntityCoordinates ToCoordinates(this EntityUid id, float x, float { return new EntityCoordinates(id, x, y); } - - [Obsolete] - public static EntityCoordinates ToCoordinates(this MapGridComponent grid, float x, float y) - { - return ToCoordinates(grid.Owner, x, y); - } - - [Obsolete] - public static EntityCoordinates ToCoordinates(this MapGridComponent grid) - { - return ToCoordinates(grid.Owner, Vector2.Zero); - } } } diff --git a/Content.Shared/Coordinates/Helpers/SnapgridHelper.cs b/Content.Shared/Coordinates/Helpers/SnapgridHelper.cs index 567a600388..db9ee85a0c 100644 --- a/Content.Shared/Coordinates/Helpers/SnapgridHelper.cs +++ b/Content.Shared/Coordinates/Helpers/SnapgridHelper.cs @@ -22,7 +22,7 @@ public static EntityCoordinates SnapToGrid(this EntityCoordinates coordinates, I return EntityCoordinates.FromMap(coordinates.EntityId, mapPos, xformSys); } - var grid = mapManager.GetGrid(gridId.Value); + var grid = entMan.GetComponent(gridId.Value); var tileSize = grid.TileSize; var localPos = coordinates.WithEntityId(gridId.Value).Position; var x = (int)Math.Floor(localPos.X / tileSize) + tileSize / 2f; diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index 92f4576c8b..8ee34dbcc5 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -22,9 +22,8 @@ using Content.Shared.Item; using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Events; -using Content.Shared.Physics.Pull; +using Content.Shared.Movement.Pulling.Events; using Content.Shared.Popups; -using Content.Shared.Pulling.Components; using Content.Shared.Pulling.Events; using Content.Shared.Rejuvenate; using Content.Shared.Stunnable; @@ -37,6 +36,7 @@ using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Serialization; +using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent; namespace Content.Shared.Cuffs { @@ -72,7 +72,7 @@ public override void Initialize() SubscribeLocalEvent(OnCuffsInsertedIntoContainer); SubscribeLocalEvent(OnRejuvenate); SubscribeLocalEvent(OnStartup); - SubscribeLocalEvent(HandleStopPull); + SubscribeLocalEvent(HandleStopPull); SubscribeLocalEvent(HandleMoveAttempt); SubscribeLocalEvent(OnEquipAttempt); SubscribeLocalEvent(OnUnequipAttempt); @@ -184,7 +184,7 @@ public void UpdateCuffState(EntityUid uid, CuffableComponent component) private void OnBeingPulledAttempt(EntityUid uid, CuffableComponent component, BeingPulledAttemptEvent args) { - if (!TryComp(uid, out var pullable)) + if (!TryComp(uid, out var pullable)) return; if (pullable.Puller != null && !component.CanStillInteract) // If we are being pulled already and cuffed, we can't get pulled again. @@ -216,19 +216,19 @@ private void OnPull(EntityUid uid, CuffableComponent component, PullMessage args private void HandleMoveAttempt(EntityUid uid, CuffableComponent component, UpdateCanMoveEvent args) { - if (component.CanStillInteract || !EntityManager.TryGetComponent(uid, out SharedPullableComponent? pullable) || !pullable.BeingPulled) + if (component.CanStillInteract || !EntityManager.TryGetComponent(uid, out PullableComponent? pullable) || !pullable.BeingPulled) return; args.Cancel(); } - private void HandleStopPull(EntityUid uid, CuffableComponent component, StopPullingEvent args) + private void HandleStopPull(EntityUid uid, CuffableComponent component, AttemptStopPullingEvent args) { if (args.User == null || !Exists(args.User.Value)) return; if (args.User.Value == uid && !component.CanStillInteract) - args.Cancel(); + args.Cancelled = true; } private void AddUncuffVerb(EntityUid uid, CuffableComponent component, GetVerbsEvent args) diff --git a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs index 600036a891..9afd683cbd 100644 --- a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs +++ b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs @@ -127,9 +127,6 @@ public virtual bool CanInsert(EntityUid uid, SharedDisposalUnitComponent compone return damageState != null && (!component.MobsCanEnter || _mobState.IsDead(entity, damageState)); } - /// - /// TODO: Proper prediction - /// public abstract void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null); [Serializable, NetSerializable] diff --git a/Content.Shared/Follower/FollowerSystem.cs b/Content.Shared/Follower/FollowerSystem.cs index 5656778a3f..fc7cccf9bd 100644 --- a/Content.Shared/Follower/FollowerSystem.cs +++ b/Content.Shared/Follower/FollowerSystem.cs @@ -4,8 +4,8 @@ using Content.Shared.Ghost; using Content.Shared.Hands; using Content.Shared.Movement.Events; +using Content.Shared.Movement.Pulling.Events; using Content.Shared.Movement.Systems; -using Content.Shared.Physics.Pull; using Content.Shared.Tag; using Content.Shared.Verbs; using Robust.Shared.Containers; diff --git a/Content.Shared/Friction/TileFrictionController.cs b/Content.Shared/Friction/TileFrictionController.cs index ba4d9fc24f..3583947ee3 100644 --- a/Content.Shared/Friction/TileFrictionController.cs +++ b/Content.Shared/Friction/TileFrictionController.cs @@ -2,8 +2,8 @@ using Content.Shared.CCVar; using Content.Shared.Gravity; using Content.Shared.Movement.Events; +using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Systems; -using Content.Shared.Pulling.Components; using JetBrains.Annotations; using Robust.Shared.Configuration; using Robust.Shared.Map; @@ -23,6 +23,12 @@ public sealed class TileFrictionController : VirtualController [Dependency] private readonly SharedMoverController _mover = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; + private EntityQuery _frictionQuery; + private EntityQuery _xformQuery; + private EntityQuery _pullerQuery; + private EntityQuery _pullableQuery; + private EntityQuery _gridQuery; + private float _stopSpeed; private float _frictionModifier; public const float DefaultFriction = 0.3f; @@ -33,18 +39,17 @@ public override void Initialize() Subs.CVar(_configManager, CCVars.TileFrictionModifier, value => _frictionModifier = value, true); Subs.CVar(_configManager, CCVars.StopSpeed, value => _stopSpeed = value, true); + _frictionQuery = GetEntityQuery(); + _xformQuery = GetEntityQuery(); + _pullerQuery = GetEntityQuery(); + _pullableQuery = GetEntityQuery(); + _gridQuery = GetEntityQuery(); } public override void UpdateBeforeMapSolve(bool prediction, PhysicsMapComponent mapComponent, float frameTime) { base.UpdateBeforeMapSolve(prediction, mapComponent, frameTime); - var frictionQuery = GetEntityQuery(); - var xformQuery = GetEntityQuery(); - var pullerQuery = GetEntityQuery(); - var pullableQuery = GetEntityQuery(); - var gridQuery = GetEntityQuery(); - foreach (var body in mapComponent.AwakeBodies) { var uid = body.Owner; @@ -60,16 +65,16 @@ public override void UpdateBeforeMapSolve(bool prediction, PhysicsMapComponent m if (body.LinearVelocity.Equals(Vector2.Zero) && body.AngularVelocity.Equals(0f)) continue; - if (!xformQuery.TryGetComponent(uid, out var xform)) + if (!_xformQuery.TryGetComponent(uid, out var xform)) { Log.Error($"Unable to get transform for {ToPrettyString(uid)} in tilefrictioncontroller"); continue; } - var surfaceFriction = GetTileFriction(uid, body, xform, gridQuery, frictionQuery); + var surfaceFriction = GetTileFriction(uid, body, xform); var bodyModifier = 1f; - if (frictionQuery.TryGetComponent(uid, out var frictionComp)) + if (_frictionQuery.TryGetComponent(uid, out var frictionComp)) { bodyModifier = frictionComp.Modifier; } @@ -82,8 +87,8 @@ public override void UpdateBeforeMapSolve(bool prediction, PhysicsMapComponent m // If we're sandwiched between 2 pullers reduce friction // Might be better to make this dynamic and check how many are in the pull chain? // Either way should be much faster for now. - if (pullerQuery.TryGetComponent(uid, out var puller) && puller.Pulling != null && - pullableQuery.TryGetComponent(uid, out var pullable) && pullable.BeingPulled) + if (_pullerQuery.TryGetComponent(uid, out var puller) && puller.Pulling != null && + _pullableQuery.TryGetComponent(uid, out var pullable) && pullable.BeingPulled) { bodyModifier *= 0.2f; } @@ -163,9 +168,7 @@ private void ReduceAngularVelocity(EntityUid uid, bool prediction, PhysicsCompon private float GetTileFriction( EntityUid uid, PhysicsComponent body, - TransformComponent xform, - EntityQuery gridQuery, - EntityQuery frictionQuery) + TransformComponent xform) { // TODO: Make IsWeightless event-based; we already have grid traversals tracked so just raise events if (_gravity.IsWeightless(uid, body, xform)) @@ -175,9 +178,9 @@ private float GetTileFriction( return 0.0f; // If not on a grid then return the map's friction. - if (!gridQuery.TryGetComponent(xform.GridUid, out var grid)) + if (!_gridQuery.TryGetComponent(xform.GridUid, out var grid)) { - return frictionQuery.TryGetComponent(xform.MapUid, out var friction) + return _frictionQuery.TryGetComponent(xform.MapUid, out var friction) ? friction.Modifier : DefaultFriction; } @@ -197,7 +200,7 @@ private float GetTileFriction( while (anc.MoveNext(out var tileEnt)) { - if (frictionQuery.TryGetComponent(tileEnt, out var friction)) + if (_frictionQuery.TryGetComponent(tileEnt, out var friction)) return friction.Modifier; } diff --git a/Content.Shared/IdentityManagement/Components/IdentityBlockerComponent.cs b/Content.Shared/IdentityManagement/Components/IdentityBlockerComponent.cs index 3857063783..e7a88b6ef2 100644 --- a/Content.Shared/IdentityManagement/Components/IdentityBlockerComponent.cs +++ b/Content.Shared/IdentityManagement/Components/IdentityBlockerComponent.cs @@ -6,6 +6,7 @@ namespace Content.Shared.IdentityManagement.Components; [RegisterComponent, NetworkedComponent] public sealed partial class IdentityBlockerComponent : Component { + [DataField] public bool Enabled = true; /// diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 0e390ecea4..e4864b1f7f 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -15,10 +15,10 @@ using Content.Shared.Inventory.Events; using Content.Shared.Item; using Content.Shared.Movement.Components; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Physics; using Content.Shared.Popups; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; using Content.Shared.Tag; using Content.Shared.Timing; using Content.Shared.Verbs; @@ -60,7 +60,7 @@ public abstract partial class SharedInteractionSystem : EntitySystem [Dependency] private readonly SharedVerbSystem _verbSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; - [Dependency] private readonly SharedPullingSystem _pullSystem = default!; + [Dependency] private readonly PullingSystem _pullSystem = default!; [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly TagSystem _tagSystem = default!; @@ -185,10 +185,10 @@ private bool HandleTryPullObject(ICommonSession? session, EntityCoordinates coor if (!InRangeUnobstructed(userEntity.Value, uid, popup: true)) return false; - if (!TryComp(uid, out SharedPullableComponent? pull)) + if (!TryComp(uid, out PullableComponent? pull)) return false; - _pullSystem.TogglePull(userEntity.Value, pull); + _pullSystem.TogglePull(uid, userEntity.Value, pull); return false; } diff --git a/Content.Shared/Language/Components/LanguageKnowledgeComponent.cs b/Content.Shared/Language/Components/LanguageKnowledgeComponent.cs index 0632f5d9cb..ddbdc742be 100644 --- a/Content.Shared/Language/Components/LanguageKnowledgeComponent.cs +++ b/Content.Shared/Language/Components/LanguageKnowledgeComponent.cs @@ -2,6 +2,8 @@ namespace Content.Shared.Language.Components; +// TODO: move to server side, it's never synchronized! + /// /// Stores data about entities' intrinsic language knowledge. /// diff --git a/Content.Shared/Language/LanguagePrototype.cs b/Content.Shared/Language/LanguagePrototype.cs index 9342c07e91..be54b45aa1 100644 --- a/Content.Shared/Language/LanguagePrototype.cs +++ b/Content.Shared/Language/LanguagePrototype.cs @@ -8,6 +8,15 @@ public sealed class LanguagePrototype : IPrototype [IdDataField] public string ID { get; private set; } = default!; + [DataField("color")] + public Color? Color; + + [DataField("fontId")] + public string? FontId; + + [DataField("fontSize")] + public int? FontSize; + /// /// Obfuscation method used by this language. By default, uses /// diff --git a/Content.Shared/Language/Systems/SharedTranslatorSystem.cs b/Content.Shared/Language/Systems/SharedTranslatorSystem.cs index 08a016efa9..4a72de791f 100644 --- a/Content.Shared/Language/Systems/SharedTranslatorSystem.cs +++ b/Content.Shared/Language/Systems/SharedTranslatorSystem.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Shared.Examine; using Content.Shared.Toggleable; using Content.Shared.Language.Components.Translators; @@ -17,11 +18,20 @@ public override void Initialize() private void OnExamined(EntityUid uid, HandheldTranslatorComponent component, ExaminedEvent args) { - var state = Loc.GetString(component.Enabled - ? "translator-enabled" - : "translator-disabled"); + var understoodLanguageNames = component.UnderstoodLanguages + .Select(it => Loc.GetString($"language-{it}-name")); + var spokenLanguageNames = component.SpokenLanguages + .Select(it => Loc.GetString($"language-{it}-name")); + var requiredLanguageNames = component.RequiredLanguages + .Select(it => Loc.GetString($"language-{it}-name")); - args.PushMarkup(state); + args.PushMarkup(Loc.GetString("translator-examined-langs-understood", ("languages", string.Join(", ", understoodLanguageNames)))); + args.PushMarkup(Loc.GetString("translator-examined-langs-spoken", ("languages", string.Join(", ", spokenLanguageNames)))); + + args.PushMarkup(Loc.GetString(component.RequiresAllLanguages ? "translator-examined-requires-all" : "translator-examined-requires-any", + ("languages", string.Join(", ", requiredLanguageNames)))); + + args.PushMarkup(Loc.GetString(component.Enabled ? "translator-examined-enabled" : "translator-examined-disabled")); } protected void OnAppearanceChange(EntityUid translator, HandheldTranslatorComponent? comp = null) diff --git a/Content.Shared/Light/Components/SharedExpendableLightComponent.cs b/Content.Shared/Light/Components/SharedExpendableLightComponent.cs index c802700b62..e40174ab78 100644 --- a/Content.Shared/Light/Components/SharedExpendableLightComponent.cs +++ b/Content.Shared/Light/Components/SharedExpendableLightComponent.cs @@ -7,7 +7,7 @@ namespace Content.Shared.Light.Components; [NetworkedComponent] public abstract partial class SharedExpendableLightComponent : Component { - public static readonly AudioParams LoopedSoundParams = new(0, 1, "Master", 62.5f, 1, 1, true, 0.3f); + public static readonly AudioParams LoopedSoundParams = new(0, 1, 62.5f, 1, 1, true, 0.3f); [ViewVariables(VVAccess.ReadOnly)] public ExpendableLightState CurrentState { get; set; } diff --git a/Content.Shared/Maps/TurfHelpers.cs b/Content.Shared/Maps/TurfHelpers.cs index 58a5d133b5..f1c1beef7d 100644 --- a/Content.Shared/Maps/TurfHelpers.cs +++ b/Content.Shared/Maps/TurfHelpers.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using Content.Shared.Physics; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Random; namespace Content.Shared.Maps @@ -11,6 +12,22 @@ namespace Content.Shared.Maps // That, or make the interface arguments non-optional so people stop failing to pass them in. public static class TurfHelpers { + /// + /// Attempts to get the turf at map indices with grid id or null if no such turf is found. + /// + public static TileRef GetTileRef(this Vector2i vector2i, EntityUid gridId, IEntityManager? entityManager = null) + { + entityManager ??= IoCManager.Resolve(); + + if (!entityManager.TryGetComponent(gridId, out var grid)) + return default; + + if (!grid.TryGetTileRef(vector2i, out var tile)) + return default; + + return tile; + } + /// /// Attempts to get the turf at a certain coordinates or null if no such turf is found. /// @@ -119,9 +136,8 @@ public static bool IsBlockedTurf(this TileRef turf, bool filterMobs, EntityLooku private static bool GetWorldTileBox(TileRef turf, out Box2Rotated res) { var entManager = IoCManager.Resolve(); - var map = IoCManager.Resolve(); - if (map.TryGetGrid(turf.GridUid, out var tileGrid)) + if (entManager.TryGetComponent(turf.GridUid, out var tileGrid)) { var gridRot = entManager.GetComponent(turf.GridUid).WorldRotation; diff --git a/Content.Shared/Maps/TurfSystem.cs b/Content.Shared/Maps/TurfSystem.cs index a344193f12..ad8b3ddea8 100644 --- a/Content.Shared/Maps/TurfSystem.cs +++ b/Content.Shared/Maps/TurfSystem.cs @@ -13,7 +13,6 @@ public sealed class TurfSystem : EntitySystem { [Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly IMapManager _mapMan = default!; /// /// Returns true if a given tile is blocked by physics-enabled entities. @@ -92,7 +91,7 @@ public bool IsTileBlocked(EntityUid gridUid, /// public EntityCoordinates GetTileCenter(TileRef turf) { - var grid = _mapMan.GetGrid(turf.GridUid); + var grid = Comp(turf.GridUid); var center = (turf.GridIndices + new Vector2(0.5f, 0.5f)) * grid.TileSize; return new EntityCoordinates(turf.GridUid, center); } diff --git a/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs b/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs index 5a9ada7f58..50dce3c766 100644 --- a/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs +++ b/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs @@ -1,7 +1,8 @@ -using System.Linq; +using System.Linq; using Content.Shared.Administration.Logs; using Content.Shared.Audio; using Content.Shared.Body.Components; +using Content.Shared.Coordinates; using Content.Shared.Database; using Content.Shared.Emag.Components; using Content.Shared.Emag.Systems; @@ -11,6 +12,7 @@ using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; +using Robust.Shared.Map; using Robust.Shared.Physics.Events; using Robust.Shared.Timing; @@ -110,6 +112,9 @@ public bool TryStartProcessItem(EntityUid uid, EntityUid item, MaterialReclaimer component.NextSound = Timing.CurTime + component.SoundCooldown; } + var reclaimedEvent = new GotReclaimedEvent(Transform(uid).Coordinates); + RaiseLocalEvent(item, ref reclaimedEvent); + var duration = GetReclaimingDuration(uid, item, component); // if it's instant, don't bother with all the active comp stuff. if (duration == TimeSpan.Zero) @@ -238,3 +243,6 @@ public override void Update(float frameTime) } } } + +[ByRefEvent] +public record struct GotReclaimedEvent(EntityCoordinates ReclaimerCoordinates); diff --git a/Content.Shared/Movement/Pulling/Components/PullableComponent.cs b/Content.Shared/Movement/Pulling/Components/PullableComponent.cs new file mode 100644 index 0000000000..db889e7e3b --- /dev/null +++ b/Content.Shared/Movement/Pulling/Components/PullableComponent.cs @@ -0,0 +1,39 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Movement.Pulling.Components; + +/// +/// Specifies an entity as being pullable by an entity with +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(Systems.PullingSystem))] +public sealed partial class PullableComponent : Component +{ + /// + /// The current entity pulling this component. + /// + [AutoNetworkedField, DataField] + public EntityUid? Puller; + + /// + /// The pull joint. + /// + [AutoNetworkedField, DataField] + public string? PullJointId; + + public bool BeingPulled => Puller != null; + + /// + /// If the physics component has FixedRotation should we keep it upon being pulled + /// + [Access(typeof(Systems.PullingSystem), Other = AccessPermissions.ReadExecute)] + [ViewVariables(VVAccess.ReadWrite), DataField("fixedRotation")] + public bool FixedRotationOnPull; + + /// + /// What the pullable's fixedrotation was set to before being pulled. + /// + [Access(typeof(Systems.PullingSystem), Other = AccessPermissions.ReadExecute)] + [AutoNetworkedField, DataField] + public bool PrevFixedRotation; +} diff --git a/Content.Shared/Movement/Pulling/Components/PullerComponent.cs b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs new file mode 100644 index 0000000000..1fc9b731bd --- /dev/null +++ b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs @@ -0,0 +1,41 @@ +using Content.Shared.Movement.Pulling.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Shared.Movement.Pulling.Components; + +/// +/// Specifies an entity as being able to pull another entity with +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(PullingSystem))] +public sealed partial class PullerComponent : Component +{ + // My raiding guild + /// + /// Next time the puller can throw what is being pulled. + /// Used to avoid spamming it for infinite spin + velocity. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] + public TimeSpan NextThrow; + + [DataField] + public TimeSpan ThrowCooldown = TimeSpan.FromSeconds(1); + + // Before changing how this is updated, please see SharedPullerSystem.RefreshMovementSpeed + public float WalkSpeedModifier => Pulling == default ? 1.0f : 0.95f; + + public float SprintSpeedModifier => Pulling == default ? 1.0f : 0.95f; + + /// + /// Entity currently being pulled if applicable. + /// + [AutoNetworkedField, DataField] + public EntityUid? Pulling; + + /// + /// Does this entity need hands to be able to pull something? + /// + [DataField] + public bool NeedsHands = true; +} diff --git a/Content.Shared/Movement/Pulling/Events/AttemptPullEvent.cs b/Content.Shared/Movement/Pulling/Events/AttemptPullEvent.cs new file mode 100644 index 0000000000..b0101c4699 --- /dev/null +++ b/Content.Shared/Movement/Pulling/Events/AttemptPullEvent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.Physics.Components; + +namespace Content.Shared.Movement.Pulling.Events; + +/// +/// Raised directed on puller and pullable to determine if it can be pulled. +/// +public sealed class PullAttemptEvent : PullMessage +{ + public PullAttemptEvent(EntityUid pullerUid, EntityUid pullableUid) : base(pullerUid, pullableUid) { } + + public bool Cancelled { get; set; } +} diff --git a/Content.Shared/Movement/Pulling/Events/AttemptStopPullingEvent.cs b/Content.Shared/Movement/Pulling/Events/AttemptStopPullingEvent.cs new file mode 100644 index 0000000000..cd7edc5f62 --- /dev/null +++ b/Content.Shared/Movement/Pulling/Events/AttemptStopPullingEvent.cs @@ -0,0 +1,10 @@ +namespace Content.Shared.Pulling.Events; + +/// +/// Raised when a request is made to stop pulling an entity. +/// +public record struct AttemptStopPullingEvent(EntityUid? User = null) +{ + public readonly EntityUid? User = User; + public bool Cancelled; +} \ No newline at end of file diff --git a/Content.Shared/Pulling/Events/BeingPulledAttemptEvent.cs b/Content.Shared/Movement/Pulling/Events/BeingPulledAttemptEvent.cs similarity index 100% rename from Content.Shared/Pulling/Events/BeingPulledAttemptEvent.cs rename to Content.Shared/Movement/Pulling/Events/BeingPulledAttemptEvent.cs diff --git a/Content.Shared/Movement/Pulling/Events/PullMessage.cs b/Content.Shared/Movement/Pulling/Events/PullMessage.cs new file mode 100644 index 0000000000..a427e448d5 --- /dev/null +++ b/Content.Shared/Movement/Pulling/Events/PullMessage.cs @@ -0,0 +1,13 @@ +namespace Content.Shared.Movement.Pulling.Events; + +public abstract class PullMessage : EntityEventArgs +{ + public readonly EntityUid PullerUid; + public readonly EntityUid PulledUid; + + protected PullMessage(EntityUid pullerUid, EntityUid pulledUid) + { + PullerUid = pullerUid; + PulledUid = pulledUid; + } +} diff --git a/Content.Shared/Movement/Pulling/Events/PullStartedMessage.cs b/Content.Shared/Movement/Pulling/Events/PullStartedMessage.cs new file mode 100644 index 0000000000..29460e1dfc --- /dev/null +++ b/Content.Shared/Movement/Pulling/Events/PullStartedMessage.cs @@ -0,0 +1,9 @@ +namespace Content.Shared.Movement.Pulling.Events; + +public sealed class PullStartedMessage : PullMessage +{ + public PullStartedMessage(EntityUid pullerUid, EntityUid pullableUid) : + base(pullerUid, pullableUid) + { + } +} diff --git a/Content.Shared/Movement/Pulling/Events/PullStoppedMessage.cs b/Content.Shared/Movement/Pulling/Events/PullStoppedMessage.cs new file mode 100644 index 0000000000..47aa34562f --- /dev/null +++ b/Content.Shared/Movement/Pulling/Events/PullStoppedMessage.cs @@ -0,0 +1,13 @@ +using Robust.Shared.Physics.Components; + +namespace Content.Shared.Movement.Pulling.Events; + +/// +/// Raised directed on both puller and pullable. +/// +public sealed class PullStoppedMessage : PullMessage +{ + public PullStoppedMessage(EntityUid pullerUid, EntityUid pulledUid) : base(pullerUid, pulledUid) + { + } +} diff --git a/Content.Shared/Pulling/Events/StartPullAttemptEvent.cs b/Content.Shared/Movement/Pulling/Events/StartPullAttemptEvent.cs similarity index 100% rename from Content.Shared/Pulling/Events/StartPullAttemptEvent.cs rename to Content.Shared/Movement/Pulling/Events/StartPullAttemptEvent.cs diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs new file mode 100644 index 0000000000..b347c6da16 --- /dev/null +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -0,0 +1,494 @@ +using System.Numerics; +using Content.Shared.ActionBlocker; +using Content.Shared.Administration.Logs; +using Content.Shared.Alert; +using Content.Shared.Buckle.Components; +using Content.Shared.Database; +using Content.Shared.Hands; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Input; +using Content.Shared.Interaction; +using Content.Shared.Movement.Events; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Events; +using Content.Shared.Movement.Systems; +using Content.Shared.Pulling.Events; +using Content.Shared.Throwing; +using Content.Shared.Verbs; +using Robust.Shared.Containers; +using Robust.Shared.Input.Binding; +using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Events; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Player; +using Robust.Shared.Timing; + +namespace Content.Shared.Movement.Pulling.Systems; + +/// +/// Allows one entity to pull another behind them via a physics distance joint. +/// +public sealed class PullingSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly ActionBlockerSystem _blocker = default!; + [Dependency] private readonly AlertsSystem _alertsSystem = default!; + [Dependency] private readonly MovementSpeedModifierSystem _modifierSystem = default!; + [Dependency] private readonly SharedJointSystem _joints = default!; + [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly SharedInteractionSystem _interaction = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _xformSys = default!; + [Dependency] private readonly ThrowingSystem _throwing = default!; + + public override void Initialize() + { + base.Initialize(); + + UpdatesAfter.Add(typeof(SharedPhysicsSystem)); + UpdatesOutsidePrediction = true; + + SubscribeLocalEvent(OnPullableMoveInput); + SubscribeLocalEvent(OnPullableCollisionChange); + SubscribeLocalEvent(OnJointRemoved); + SubscribeLocalEvent>(AddPullVerbs); + SubscribeLocalEvent(OnPullableContainerInsert); + + SubscribeLocalEvent(OnPullerContainerInsert); + SubscribeLocalEvent(OnPullerUnpaused); + SubscribeLocalEvent(OnVirtualItemDeleted); + SubscribeLocalEvent(OnRefreshMovespeed); + + CommandBinds.Builder + .Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(OnRequestMovePulledObject)) + .Bind(ContentKeyFunctions.ReleasePulledObject, InputCmdHandler.FromDelegate(OnReleasePulledObject, handle: false)) + .Register(); + } + + private void OnPullerContainerInsert(Entity ent, ref EntGotInsertedIntoContainerMessage args) + { + if (ent.Comp.Pulling == null) return; + + if (!TryComp(ent.Comp.Pulling.Value, out PullableComponent? pulling)) + return; + + TryStopPull(ent.Comp.Pulling.Value, pulling, ent.Owner); + } + + private void OnPullableContainerInsert(Entity ent, ref EntGotInsertedIntoContainerMessage args) + { + TryStopPull(ent.Owner, ent.Comp); + } + + public override void Shutdown() + { + base.Shutdown(); + CommandBinds.Unregister(); + } + + private void OnPullerUnpaused(EntityUid uid, PullerComponent component, ref EntityUnpausedEvent args) + { + component.NextThrow += args.PausedTime; + } + + private void OnVirtualItemDeleted(EntityUid uid, PullerComponent component, VirtualItemDeletedEvent args) + { + // If client deletes the virtual hand then stop the pull. + if (component.Pulling == null) + return; + + if (component.Pulling != args.BlockingEntity) + return; + + if (EntityManager.TryGetComponent(args.BlockingEntity, out PullableComponent? comp)) + { + TryStopPull(args.BlockingEntity, comp, uid); + } + } + + private void AddPullVerbs(EntityUid uid, PullableComponent component, GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + // Are they trying to pull themselves up by their bootstraps? + if (args.User == args.Target) + return; + + //TODO VERB ICONS add pulling icon + if (component.Puller == args.User) + { + Verb verb = new() + { + Text = Loc.GetString("pulling-verb-get-data-text-stop-pulling"), + Act = () => TryStopPull(uid, component, user: args.User), + DoContactInteraction = false // pulling handle its own contact interaction. + }; + args.Verbs.Add(verb); + } + else if (CanPull(args.User, args.Target)) + { + Verb verb = new() + { + Text = Loc.GetString("pulling-verb-get-data-text"), + Act = () => TryStartPull(args.User, args.Target), + DoContactInteraction = false // pulling handle its own contact interaction. + }; + args.Verbs.Add(verb); + } + } + + private void OnRefreshMovespeed(EntityUid uid, PullerComponent component, RefreshMovementSpeedModifiersEvent args) + { + args.ModifySpeed(component.WalkSpeedModifier, component.SprintSpeedModifier); + } + + private void OnPullableMoveInput(EntityUid uid, PullableComponent component, ref MoveInputEvent args) + { + // If someone moves then break their pulling. + if (!component.BeingPulled) + return; + + var entity = args.Entity; + + if (!_blocker.CanMove(entity)) + return; + + TryStopPull(uid, component, user: uid); + } + + private void OnPullableCollisionChange(EntityUid uid, PullableComponent component, ref CollisionChangeEvent args) + { + // IDK what this is supposed to be. + if (!_timing.ApplyingState && component.PullJointId != null && !args.CanCollide) + { + _joints.RemoveJoint(uid, component.PullJointId); + } + } + + private void OnJointRemoved(EntityUid uid, PullableComponent component, JointRemovedEvent args) + { + // Just handles the joint getting nuked without going through pulling system (valid behavior). + + // Not relevant / pullable state handle it. + if (component.Puller != args.OtherEntity || + args.Joint.ID != component.PullJointId || + _timing.ApplyingState) + { + return; + } + + if (args.Joint.ID != component.PullJointId || component.Puller == null) + return; + + StopPulling(uid, component); + } + + /// + /// Forces pulling to stop and handles cleanup. + /// + private void StopPulling(EntityUid pullableUid, PullableComponent pullableComp) + { + if (!_timing.ApplyingState) + { + if (TryComp(pullableUid, out var pullablePhysics)) + { + _physics.SetFixedRotation(pullableUid, pullableComp.PrevFixedRotation, body: pullablePhysics); + } + } + + var oldPuller = pullableComp.Puller; + pullableComp.PullJointId = null; + pullableComp.Puller = null; + Dirty(pullableUid, pullableComp); + + // No more joints with puller -> force stop pull. + if (TryComp(oldPuller, out var pullerComp)) + { + var pullerUid = oldPuller.Value; + _alertsSystem.ClearAlert(pullerUid, AlertType.Pulling); + pullerComp.Pulling = null; + Dirty(oldPuller.Value, pullerComp); + + // Messaging + var message = new PullStoppedMessage(pullerUid, pullableUid); + _modifierSystem.RefreshMovementSpeedModifiers(pullerUid); + _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(pullerUid):user} stopped pulling {ToPrettyString(pullableUid):target}"); + + RaiseLocalEvent(pullerUid, message); + RaiseLocalEvent(pullableUid, message); + } + + + _alertsSystem.ClearAlert(pullableUid, AlertType.Pulled); + } + + public bool IsPulled(EntityUid uid, PullableComponent? component = null) + { + return Resolve(uid, ref component, false) && component.BeingPulled; + } + + private bool OnRequestMovePulledObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid) + { + if (session?.AttachedEntity is not { } player || + !player.IsValid()) + { + return false; + } + + if (!TryComp(player, out var pullerComp)) + return false; + + var pulled = pullerComp.Pulling; + + if (!HasComp(pulled)) + return false; + + if (_containerSystem.IsEntityInContainer(player)) + return false; + + // Cooldown buddy + if (_timing.CurTime < pullerComp.NextThrow) + return false; + + pullerComp.NextThrow = _timing.CurTime + pullerComp.ThrowCooldown; + + // Cap the distance + const float range = 2f; + var fromUserCoords = coords.WithEntityId(player, EntityManager); + var userCoords = new EntityCoordinates(player, Vector2.Zero); + + if (!userCoords.InRange(EntityManager, _xformSys, fromUserCoords, range)) + { + var userDirection = fromUserCoords.Position - userCoords.Position; + fromUserCoords = userCoords.Offset(userDirection.Normalized() * range); + } + + Dirty(player, pullerComp); + _throwing.TryThrow(pulled.Value, fromUserCoords, user: player, strength: 4f, animated: false, recoil: false, playSound: false); + return false; + } + + public bool IsPulling(EntityUid puller, PullerComponent? component = null) + { + return Resolve(puller, ref component, false) && component.Pulling != null; + } + + private void OnReleasePulledObject(ICommonSession? session) + { + if (session?.AttachedEntity is not {Valid: true} player) + { + return; + } + + if (!TryComp(player, out PullerComponent? pullerComp) || + !TryComp(pullerComp.Pulling, out PullableComponent? pullableComp)) + { + return; + } + + TryStopPull(pullerComp.Pulling.Value, pullableComp, user: player); + } + + public bool CanPull(EntityUid puller, EntityUid pullableUid, PullerComponent? pullerComp = null) + { + if (!Resolve(puller, ref pullerComp, false)) + { + return false; + } + + if (pullerComp.NeedsHands && !_handsSystem.TryGetEmptyHand(puller, out _)) + { + return false; + } + + if (!_blocker.CanInteract(puller, pullableUid)) + { + return false; + } + + if (!EntityManager.TryGetComponent(pullableUid, out var physics)) + { + return false; + } + + if (physics.BodyType == BodyType.Static) + { + return false; + } + + if (puller == pullableUid) + { + return false; + } + + if (!_containerSystem.IsInSameOrNoContainer(puller, pullableUid)) + { + return false; + } + + if (EntityManager.TryGetComponent(puller, out BuckleComponent? buckle)) + { + // Prevent people pulling the chair they're on, etc. + if (buckle is { PullStrap: false, Buckled: true } && (buckle.LastEntityBuckledTo == pullableUid)) + { + return false; + } + } + + var getPulled = new BeingPulledAttemptEvent(puller, pullableUid); + RaiseLocalEvent(pullableUid, getPulled, true); + var startPull = new StartPullAttemptEvent(puller, pullableUid); + RaiseLocalEvent(puller, startPull, true); + return !startPull.Cancelled && !getPulled.Cancelled; + } + + public bool TogglePull(EntityUid pullableUid, EntityUid pullerUid, PullableComponent pullable) + { + if (pullable.Puller == pullerUid) + { + return TryStopPull(pullableUid, pullable); + } + + return TryStartPull(pullerUid, pullableUid, pullableComp: pullable); + } + + public bool TogglePull(EntityUid pullerUid, PullerComponent puller) + { + if (!TryComp(puller.Pulling, out var pullable)) + return false; + + return TogglePull(puller.Pulling.Value, pullerUid, pullable); + } + + public bool TryStartPull(EntityUid pullerUid, EntityUid pullableUid, EntityUid? user = null, + PullerComponent? pullerComp = null, PullableComponent? pullableComp = null) + { + if (!Resolve(pullerUid, ref pullerComp, false) || + !Resolve(pullableUid, ref pullableComp, false)) + { + return false; + } + + if (pullerComp.Pulling == pullableUid) + return true; + + if (!CanPull(pullerUid, pullableUid)) + return false; + + if (!EntityManager.TryGetComponent(pullerUid, out var pullerPhysics) || + !EntityManager.TryGetComponent(pullableUid, out var pullablePhysics)) + { + return false; + } + + // Ensure that the puller is not currently pulling anything. + var oldPullable = pullerComp.Pulling; + + if (oldPullable != null) + { + // Well couldn't stop the old one. + if (!TryStopPull(oldPullable.Value, pullableComp, user)) + return false; + } + + // Is the pullable currently being pulled by something else? + if (pullableComp.Puller != null) + { + // Uhhh + if (pullableComp.Puller == pullerUid) + return false; + + if (!TryStopPull(pullableUid, pullableComp, pullerUid)) + return false; + } + + var pullAttempt = new PullAttemptEvent(pullerUid, pullableUid); + RaiseLocalEvent(pullerUid, pullAttempt); + + if (pullAttempt.Cancelled) + return false; + + RaiseLocalEvent(pullableUid, pullAttempt); + + if (pullAttempt.Cancelled) + return false; + + // Pulling confirmed + + _interaction.DoContactInteraction(pullableUid, pullerUid); + + // Use net entity so it's consistent across client and server. + pullableComp.PullJointId = $"pull-joint-{GetNetEntity(pullableUid)}"; + + pullerComp.Pulling = pullableUid; + pullableComp.Puller = pullerUid; + + // joint state handling will manage its own state + if (!_timing.ApplyingState) + { + // Joint startup + var union = _physics.GetHardAABB(pullerUid).Union(_physics.GetHardAABB(pullableUid, body: pullablePhysics)); + var length = Math.Max((float) union.Size.X, (float) union.Size.Y) * 0.75f; + + var joint = _joints.CreateDistanceJoint(pullableUid, pullerUid, id: pullableComp.PullJointId); + joint.CollideConnected = false; + // This maximum has to be there because if the object is constrained too closely, the clamping goes backwards and asserts. + joint.MaxLength = Math.Max(1.0f, length); + joint.Length = length * 0.75f; + joint.MinLength = 0f; + joint.Stiffness = 1f; + + _physics.SetFixedRotation(pullableUid, pullableComp.FixedRotationOnPull, body: pullablePhysics); + } + + pullableComp.PrevFixedRotation = pullablePhysics.FixedRotation; + + // Messaging + var message = new PullStartedMessage(pullerUid, pullableUid); + _alertsSystem.ShowAlert(pullerUid, AlertType.Pulling); + _alertsSystem.ShowAlert(pullableUid, AlertType.Pulled); + + RaiseLocalEvent(pullerUid, message); + RaiseLocalEvent(pullableUid, message); + + Dirty(pullerUid, pullerComp); + Dirty(pullableUid, pullableComp); + + _adminLogger.Add(LogType.Action, LogImpact.Low, + $"{ToPrettyString(pullerUid):user} started pulling {ToPrettyString(pullableUid):target}"); + return true; + } + + public bool TryStopPull(EntityUid pullableUid, PullableComponent pullable, EntityUid? user = null) + { + var pullerUidNull = pullable.Puller; + + if (pullerUidNull == null) + return false; + + var msg = new AttemptStopPullingEvent(user); + RaiseLocalEvent(pullableUid, msg, true); + + if (msg.Cancelled) + return false; + + // Stop pulling confirmed! + if (!_timing.ApplyingState) + { + // Joint shutdown + if (pullable.PullJointId != null) + { + _joints.RemoveJoint(pullableUid, pullable.PullJointId); + pullable.PullJointId = null; + } + } + + StopPulling(pullableUid, pullable); + return true; + } +} diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index 4d9eedbf7c..79d2e9f255 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -9,7 +9,7 @@ using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Components; using Content.Shared.Movement.Events; -using Content.Shared.Pulling.Components; +using Content.Shared.StepTrigger.Components; using Content.Shared.Tag; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; @@ -23,7 +23,7 @@ using Robust.Shared.Physics.Systems; using Robust.Shared.Timing; using Robust.Shared.Utility; -using Content.Shared.StepTrigger.Components; // Delta V-NoShoesSilentFootstepsComponent +using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent; namespace Content.Shared.Movement.Systems { @@ -55,7 +55,7 @@ public abstract partial class SharedMoverController : VirtualController protected EntityQuery ModifierQuery; protected EntityQuery PhysicsQuery; protected EntityQuery RelayQuery; - protected EntityQuery PullableQuery; + protected EntityQuery PullableQuery; protected EntityQuery XformQuery; protected EntityQuery CanMoveInAirQuery; protected EntityQuery NoRotateQuery; @@ -87,7 +87,7 @@ public override void Initialize() RelayTargetQuery = GetEntityQuery(); PhysicsQuery = GetEntityQuery(); RelayQuery = GetEntityQuery(); - PullableQuery = GetEntityQuery(); + PullableQuery = GetEntityQuery(); XformQuery = GetEntityQuery(); NoRotateQuery = GetEntityQuery(); CanMoveInAirQuery = GetEntityQuery(); @@ -378,7 +378,7 @@ private bool IsAroundCollider(SharedPhysicsSystem broadPhaseSystem, TransformCom !otherCollider.CanCollide || ((collider.CollisionMask & otherCollider.CollisionLayer) == 0 && (otherCollider.CollisionMask & collider.CollisionLayer) == 0) || - (TryComp(otherCollider.Owner, out SharedPullableComponent? pullable) && pullable.BeingPulled)) + (TryComp(otherCollider.Owner, out PullableComponent? pullable) && pullable.BeingPulled)) { continue; } @@ -440,7 +440,7 @@ private bool TryGetSound( sound = moverModifier.FootstepSoundCollection; return true; } - + // If got the component in yml and no shoes = no sound. Delta V if (_entities.TryGetComponent(uid, out NoShoesSilentFootstepsComponent? _) & !_inventory.TryGetSlotEntity(uid, "shoes", out var _)) @@ -469,7 +469,7 @@ private bool TryGetFootstepSound( sound = null; // Fallback to the map? - if (!_mapManager.TryGetGrid(xform.GridUid, out var grid)) + if (!TryComp(xform.GridUid, out var grid)) { if (TryComp(xform.MapUid, out var modifier)) { diff --git a/Content.Shared/Nutrition/Events.cs b/Content.Shared/Nutrition/Events.cs index e27603763f..f2936d603d 100644 --- a/Content.Shared/Nutrition/Events.cs +++ b/Content.Shared/Nutrition/Events.cs @@ -53,3 +53,9 @@ public VapeDoAfterEvent(Solution solution, bool forced) public override DoAfterEvent Clone() => this; } + +/// +/// Raised before food is sliced +/// +[ByRefEvent] +public record struct SliceFoodEvent(); diff --git a/Content.Shared/OfferItem/SharedOfferItemSystem.cs b/Content.Shared/OfferItem/SharedOfferItemSystem.cs index c9d7dc5abc..ffea2a67c0 100644 --- a/Content.Shared/OfferItem/SharedOfferItemSystem.cs +++ b/Content.Shared/OfferItem/SharedOfferItemSystem.cs @@ -10,18 +10,18 @@ public abstract partial class SharedOfferItemSystem : EntitySystem public override void Initialize() { - SubscribeLocalEvent(SetInReceiveMode); + SubscribeLocalEvent(SetInReceiveMode); SubscribeLocalEvent(OnMove); InitializeInteractions(); } - private void SetInReceiveMode(EntityUid uid, OfferItemComponent component, AfterInteractUsingEvent args) + private void SetInReceiveMode(EntityUid uid, OfferItemComponent component, InteractUsingEvent args) { if (!TryComp(args.User, out var offerItem)) return; - if (args.User == uid || component.IsInReceiveMode || + if (args.User == uid || component.IsInReceiveMode || !offerItem.IsInOfferMode || (offerItem.IsInReceiveMode && offerItem.Target != uid)) return; @@ -44,6 +44,8 @@ private void SetInReceiveMode(EntityUid uid, OfferItemComponent component, After _popup.PopupEntity(Loc.GetString("offer-item-try-give-target", ("user", Identity.Entity(component.Target.Value, EntityManager)), ("item", Identity.Entity(offerItem.Item.Value, EntityManager))), component.Target.Value, uid); + + args.Handled = true; } private void OnMove(EntityUid uid, OfferItemComponent component, MoveEvent args) diff --git a/Content.Shared/Physics/Controllers/SharedConveyorController.cs b/Content.Shared/Physics/Controllers/SharedConveyorController.cs index ec17df7a24..c9ec77ba1c 100644 --- a/Content.Shared/Physics/Controllers/SharedConveyorController.cs +++ b/Content.Shared/Physics/Controllers/SharedConveyorController.cs @@ -3,6 +3,7 @@ using Content.Shared.Gravity; using Content.Shared.Movement.Systems; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Controllers; @@ -146,7 +147,7 @@ private static Vector2 Convey(Vector2 direction, float speed, float frameTime, V EntityQuery bodyQuery) { // Check if the thing's centre overlaps the grid tile. - var grid = MapManager.GetGrid(xform.GridUid!.Value); + var grid = Comp(xform.GridUid!.Value); var tile = grid.GetTileRef(xform.Coordinates); var conveyorBounds = Lookup.GetLocalBounds(tile, grid.TileSize); diff --git a/Content.Shared/Players/ContentPlayerData.cs b/Content.Shared/Players/ContentPlayerData.cs index e207447987..cc7a7e9780 100644 --- a/Content.Shared/Players/ContentPlayerData.cs +++ b/Content.Shared/Players/ContentPlayerData.cs @@ -1,4 +1,5 @@ -using Content.Shared.GameTicking; +using Content.Shared.Administration; +using Content.Shared.GameTicking; using Content.Shared.Mind; using Robust.Shared.Network; @@ -38,7 +39,12 @@ public sealed class ContentPlayerData public bool ExplicitlyDeadminned { get; set; } /// - /// Nyanotrasen - Are they whitelisted? Lets us avoid async. + /// If true, the admin will not show up in adminwho except to admins with the flag. + /// + public bool Stealthed { get; set; } + + /// + /// Nyanotrasen - Are they whitelisted? Lets us avoid async. /// [ViewVariables] public bool Whitelisted { get; set; } diff --git a/Content.Shared/Psionics/Abilities/PsionicInvisibility/PsionicInvisibilityPowerSystem.cs b/Content.Shared/Psionics/Abilities/PsionicInvisibility/PsionicInvisibilityPowerSystem.cs index 540dc03341..0eeb7ec280 100644 --- a/Content.Shared/Psionics/Abilities/PsionicInvisibility/PsionicInvisibilityPowerSystem.cs +++ b/Content.Shared/Psionics/Abilities/PsionicInvisibility/PsionicInvisibilityPowerSystem.cs @@ -40,20 +40,20 @@ public override void Initialize() SubscribeLocalEvent(OnShootAttempt); SubscribeLocalEvent(OnThrowAttempt); SubscribeLocalEvent(OnInsulated); + SubscribeLocalEvent(OnDoAfter); } private void OnInit(EntityUid uid, PsionicInvisibilityPowerComponent component, ComponentInit args) { + EnsureComp(uid, out var psionic); _actions.AddAction(uid, ref component.PsionicInvisibilityActionEntity, component.PsionicInvisibilityActionId); - _actions.TryGetActionData( component.PsionicInvisibilityActionEntity, out var actionData); + _actions.TryGetActionData(component.PsionicInvisibilityActionEntity, out var actionData); if (actionData is { UseDelay: not null }) - _actions.StartUseDelay(component.PsionicInvisibilityActionEntity); - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Add(component); - psionic.PsychicFeedback.Add(component.InvisibilityFeedback); - psionic.Amplification += 0.5f; - } + _actions.SetCooldown(component.PsionicInvisibilityActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); + + psionic.ActivePowers.Add(component); + psionic.PsychicFeedback.Add(component.InvisibilityFeedback); + psionic.Amplification += 0.5f; } private void OnShutdown(EntityUid uid, PsionicInvisibilityPowerComponent component, ComponentShutdown args) @@ -71,10 +71,7 @@ private void OnShutdown(EntityUid uid, PsionicInvisibilityPowerComponent compone private void OnPowerUsed(EntityUid uid, PsionicInvisibilityPowerComponent component, PsionicInvisibilityPowerActionEvent args) { - if (!TryComp(uid, out var psionic)) - return; - - if (HasComp(uid)) + if (!_psionics.CheckCanSelfCast(uid, out var psionic)) return; var ev = new PsionicInvisibilityTimerEvent(_gameTiming.CurTime); @@ -91,6 +88,10 @@ private void OnPowerUsed(EntityUid uid, PsionicInvisibilityPowerComponent compon _psionics.LogPowerUsed(uid, "psionic invisibility", psionic, 8, 12); args.Handled = true; } + + _actions.TryGetActionData(component.PsionicInvisibilityActionEntity, out var actionData); + if (actionData is { UseDelay: not null }) + _actions.SetCooldown(component.PsionicInvisibilityActionEntity, actionData.UseDelay.Value - TimeSpan.FromSeconds(psionic.Dampening + psionic.Amplification)); } private void OnPowerOff(RemovePsionicInvisibilityOffPowerActionEvent args) @@ -162,7 +163,7 @@ public void ToggleInvisibility(EntityUid uid) } } - public void OnDoAfter(EntityUid uid, PsionicInvisibilityPowerComponent component, PsionicInvisibilityTimerEvent args) + private void OnDoAfter(EntityUid uid, PsionicInvisibilityPowerComponent component, PsionicInvisibilityTimerEvent args) { if (!args.Cancelled) RemComp(uid); diff --git a/Content.Shared/Psionics/SharedPsionicAbilitiesSystem.cs b/Content.Shared/Psionics/SharedPsionicAbilitiesSystem.cs index 4dd8fa2442..7a87978582 100644 --- a/Content.Shared/Psionics/SharedPsionicAbilitiesSystem.cs +++ b/Content.Shared/Psionics/SharedPsionicAbilitiesSystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Shared.Actions; using Content.Shared.Administration.Logs; using Content.Shared.Mobs; @@ -82,6 +83,23 @@ private bool IsEligibleForPsionics(EntityUid uid) && (!TryComp(uid, out var mobstate) || mobstate.CurrentState == MobState.Alive); } + public bool CheckCanSelfCast(EntityUid uid, [NotNullWhen(true)] out PsionicComponent? psiComp) + { + if (!HasComp(uid)) + return TryComp(uid, out psiComp); + psiComp = null; + return false; + } + + public bool CheckCanTargetCast(EntityUid performer, EntityUid target, [NotNullWhen(true)] out PsionicComponent? psiComp) + { + if (!HasComp(performer) + && !HasComp(target)) + return TryComp(performer, out psiComp); + psiComp = null; + return false; + } + public void LogPowerUsed(EntityUid uid, string power, PsionicComponent? psionic = null, int minGlimmer = 8, int maxGlimmer = 12, bool overrideGlimmer = false) { _adminLogger.Add(Database.LogType.Psionics, Database.LogImpact.Medium, $"{ToPrettyString(uid):player} used {power}"); diff --git a/Content.Shared/Pulling/Components/PullableComponent.cs b/Content.Shared/Pulling/Components/PullableComponent.cs deleted file mode 100644 index c5c3068869..0000000000 --- a/Content.Shared/Pulling/Components/PullableComponent.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Map; -using Robust.Shared.Serialization; - -namespace Content.Shared.Pulling.Components -{ - // Before you try to add another type than SharedPullingStateManagementSystem, consider the can of worms you may be opening! - [NetworkedComponent, AutoGenerateComponentState] - [Access(typeof(SharedPullingStateManagementSystem))] - [RegisterComponent] - public sealed partial class SharedPullableComponent : Component - { - /// - /// The current entity pulling this component. - /// - [DataField, AutoNetworkedField] - public EntityUid? Puller { get; set; } - - /// - /// The pull joint. - /// - [DataField, AutoNetworkedField] - public string? PullJointId { get; set; } - - public bool BeingPulled => Puller != null; - - [Access(typeof(SharedPullingStateManagementSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends - public EntityCoordinates? MovingTo { get; set; } - - /// - /// If the physics component has FixedRotation should we keep it upon being pulled - /// - [Access(typeof(SharedPullingSystem), Other = AccessPermissions.ReadExecute)] - [ViewVariables(VVAccess.ReadWrite), DataField("fixedRotation")] - public bool FixedRotationOnPull { get; set; } - - /// - /// What the pullable's fixedrotation was set to before being pulled. - /// - [Access(typeof(SharedPullingSystem), Other = AccessPermissions.ReadExecute)] - [ViewVariables] - public bool PrevFixedRotation; - } - - /// - /// Raised when a request is made to stop pulling an entity. - /// - public sealed class StopPullingEvent : CancellableEntityEventArgs - { - public EntityUid? User { get; } - - public StopPullingEvent(EntityUid? uid = null) - { - User = uid; - } - } -} diff --git a/Content.Shared/Pulling/Components/SharedPullerComponent.cs b/Content.Shared/Pulling/Components/SharedPullerComponent.cs deleted file mode 100644 index 57a86e7f7a..0000000000 --- a/Content.Shared/Pulling/Components/SharedPullerComponent.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Pulling.Components -{ - [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] - [Access(typeof(SharedPullingStateManagementSystem))] - public sealed partial class SharedPullerComponent : Component - { - // Before changing how this is updated, please see SharedPullerSystem.RefreshMovementSpeed - public float WalkSpeedModifier => Pulling == default ? 1.0f : 0.95f; - - public float SprintSpeedModifier => Pulling == default ? 1.0f : 0.95f; - - [DataField, AutoNetworkedField] - public EntityUid? Pulling { get; set; } - - /// - /// Does this entity need hands to be able to pull something? - /// - [DataField("needsHands")] - public bool NeedsHands = true; - } -} diff --git a/Content.Shared/Pulling/Events/PullAttemptEvent.cs b/Content.Shared/Pulling/Events/PullAttemptEvent.cs deleted file mode 100644 index 6296dc2f14..0000000000 --- a/Content.Shared/Pulling/Events/PullAttemptEvent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.Physics.Components; - -namespace Content.Shared.Physics.Pull -{ - public sealed class PullAttemptEvent : PullMessage - { - public PullAttemptEvent(PhysicsComponent puller, PhysicsComponent pulled) : base(puller, pulled) { } - - public bool Cancelled { get; set; } - } -} diff --git a/Content.Shared/Pulling/Events/PullMessage.cs b/Content.Shared/Pulling/Events/PullMessage.cs deleted file mode 100644 index b11de7c170..0000000000 --- a/Content.Shared/Pulling/Events/PullMessage.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Robust.Shared.Physics.Components; - -namespace Content.Shared.Physics.Pull -{ - public abstract class PullMessage : EntityEventArgs - { - public readonly PhysicsComponent Puller; - public readonly PhysicsComponent Pulled; - - protected PullMessage(PhysicsComponent puller, PhysicsComponent pulled) - { - Puller = puller; - Pulled = pulled; - } - } -} diff --git a/Content.Shared/Pulling/Events/PullStartedMessage.cs b/Content.Shared/Pulling/Events/PullStartedMessage.cs deleted file mode 100644 index 0ede284bb0..0000000000 --- a/Content.Shared/Pulling/Events/PullStartedMessage.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Robust.Shared.Physics.Components; - -namespace Content.Shared.Physics.Pull -{ - public sealed class PullStartedMessage : PullMessage - { - public PullStartedMessage(PhysicsComponent puller, PhysicsComponent pulled) : - base(puller, pulled) - { - } - } -} diff --git a/Content.Shared/Pulling/Events/PullStoppedMessage.cs b/Content.Shared/Pulling/Events/PullStoppedMessage.cs deleted file mode 100644 index afcbcb7074..0000000000 --- a/Content.Shared/Pulling/Events/PullStoppedMessage.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.Physics.Components; - -namespace Content.Shared.Physics.Pull -{ - public sealed class PullStoppedMessage : PullMessage - { - public PullStoppedMessage(PhysicsComponent puller, PhysicsComponent pulled) : base(puller, pulled) - { - } - } -} diff --git a/Content.Shared/Pulling/PullableMoveMessage.cs b/Content.Shared/Pulling/PullableMoveMessage.cs deleted file mode 100644 index 46c6b1291d..0000000000 --- a/Content.Shared/Pulling/PullableMoveMessage.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Content.Shared.Pulling -{ - public sealed class PullableMoveMessage : EntityEventArgs - { - } -} diff --git a/Content.Shared/Pulling/PullableStopMovingMessage.cs b/Content.Shared/Pulling/PullableStopMovingMessage.cs deleted file mode 100644 index 0233e32f2b..0000000000 --- a/Content.Shared/Pulling/PullableStopMovingMessage.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Content.Shared.Pulling -{ - public sealed class PullableStopMovingMessage : EntityEventArgs - { - } -} diff --git a/Content.Shared/Pulling/Systems/SharedPullableSystem.cs b/Content.Shared/Pulling/Systems/SharedPullableSystem.cs deleted file mode 100644 index 3dab476337..0000000000 --- a/Content.Shared/Pulling/Systems/SharedPullableSystem.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Content.Shared.ActionBlocker; -using Content.Shared.Mobs.Systems; -using Content.Shared.Pulling.Components; -using Content.Shared.Movement.Events; - -namespace Content.Shared.Pulling.Systems -{ - public sealed class SharedPullableSystem : EntitySystem - { - [Dependency] private readonly ActionBlockerSystem _blocker = default!; - [Dependency] private readonly MobStateSystem _mobState = default!; - [Dependency] private readonly SharedPullingSystem _pullSystem = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnRelayMoveInput); - } - - private void OnRelayMoveInput(EntityUid uid, SharedPullableComponent component, ref MoveInputEvent args) - { - var entity = args.Entity; - if (_mobState.IsIncapacitated(entity) || !_blocker.CanMove(entity)) return; - - _pullSystem.TryStopPull(component); - } - } -} diff --git a/Content.Shared/Pulling/Systems/SharedPullerSystem.cs b/Content.Shared/Pulling/Systems/SharedPullerSystem.cs deleted file mode 100644 index e388d7a57c..0000000000 --- a/Content.Shared/Pulling/Systems/SharedPullerSystem.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Content.Shared.Administration.Logs; -using Content.Shared.Alert; -using Content.Shared.Database; -using Content.Shared.Hands; -using Content.Shared.Movement.Systems; -using Content.Shared.Physics.Pull; -using Content.Shared.Pulling.Components; -using JetBrains.Annotations; - -namespace Content.Shared.Pulling.Systems -{ - [UsedImplicitly] - public sealed class SharedPullerSystem : EntitySystem - { - [Dependency] private readonly SharedPullingStateManagementSystem _why = default!; - [Dependency] private readonly SharedPullingSystem _pullSystem = default!; - [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifierSystem = default!; - [Dependency] private readonly AlertsSystem _alertsSystem = default!; - [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(PullerHandlePullStarted); - SubscribeLocalEvent(PullerHandlePullStopped); - SubscribeLocalEvent(OnVirtualItemDeleted); - SubscribeLocalEvent(OnRefreshMovespeed); - SubscribeLocalEvent(OnPullerShutdown); - } - - private void OnPullerShutdown(EntityUid uid, SharedPullerComponent component, ComponentShutdown args) - { - _why.ForceDisconnectPuller(component); - } - - private void OnVirtualItemDeleted(EntityUid uid, SharedPullerComponent component, VirtualItemDeletedEvent args) - { - if (component.Pulling == null) - return; - - if (component.Pulling == args.BlockingEntity) - { - if (EntityManager.TryGetComponent(args.BlockingEntity, out var comp)) - { - _pullSystem.TryStopPull(comp, uid); - } - } - } - - private void PullerHandlePullStarted( - EntityUid uid, - SharedPullerComponent component, - PullStartedMessage args) - { - if (args.Puller.Owner != uid) - return; - - _alertsSystem.ShowAlert(component.Owner, AlertType.Pulling); - - RefreshMovementSpeed(component); - } - - private void PullerHandlePullStopped( - EntityUid uid, - SharedPullerComponent component, - PullStoppedMessage args) - { - if (args.Puller.Owner != uid) - return; - - var euid = component.Owner; - if (_alertsSystem.IsShowingAlert(euid, AlertType.Pulling)) - _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(euid):user} stopped pulling {ToPrettyString(args.Pulled.Owner):target}"); - _alertsSystem.ClearAlert(euid, AlertType.Pulling); - - RefreshMovementSpeed(component); - } - - private void OnRefreshMovespeed(EntityUid uid, SharedPullerComponent component, RefreshMovementSpeedModifiersEvent args) - { - args.ModifySpeed(component.WalkSpeedModifier, component.SprintSpeedModifier); - } - - private void RefreshMovementSpeed(SharedPullerComponent component) - { - _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(component.Owner); - } - } -} diff --git a/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs b/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs deleted file mode 100644 index 38ed899889..0000000000 --- a/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs +++ /dev/null @@ -1,196 +0,0 @@ -using Content.Shared.Physics.Pull; -using Content.Shared.Pulling.Components; -using JetBrains.Annotations; -using Robust.Shared.Map; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Timing; - -namespace Content.Shared.Pulling -{ - /// - /// This is the core of pulling state management. - /// Because pulling state is such a mess to get right, all writes to pulling state must go through this class. - /// - [UsedImplicitly] - public sealed class SharedPullingStateManagementSystem : EntitySystem - { - [Dependency] private readonly SharedJointSystem _jointSystem = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly IGameTiming _timing = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnShutdown); - } - - private void OnShutdown(EntityUid uid, SharedPullableComponent component, ComponentShutdown args) - { - if (component.Puller != null) - ForceRelationship(null, component); - } - - // A WARNING: - // The following 2 functions are the most internal part of the pulling system's relationship management. - // They do not expect to be cancellable. - private void ForceDisconnect(SharedPullerComponent puller, SharedPullableComponent pullable) - { - var pullerPhysics = EntityManager.GetComponent(puller.Owner); - var pullablePhysics = EntityManager.GetComponent(pullable.Owner); - - // MovingTo shutdown - ForceSetMovingTo(pullable, null); - - // Joint shutdown - if (!_timing.ApplyingState && // During state-handling, joint component will handle its own state. - pullable.PullJointId != null && - TryComp(puller.Owner, out JointComponent? jointComp)) - { - if (jointComp.GetJoints.TryGetValue(pullable.PullJointId, out var j)) - _jointSystem.RemoveJoint(j); - } - pullable.PullJointId = null; - - // State shutdown - puller.Pulling = null; - pullable.Puller = null; - - // Messaging - var message = new PullStoppedMessage(pullerPhysics, pullablePhysics); - - RaiseLocalEvent(puller.Owner, message, broadcast: false); - - if (Initialized(pullable.Owner)) - RaiseLocalEvent(pullable.Owner, message, true); - - // Networking - Dirty(puller); - Dirty(pullable); - } - - public void ForceRelationship(SharedPullerComponent? puller, SharedPullableComponent? pullable) - { - if (_timing.ApplyingState) - return; - ; - if (pullable != null && puller != null && (puller.Pulling == pullable.Owner)) - { - // Already done - return; - } - - // Start by disconnecting the pullable from whatever it is currently connected to. - var pullableOldPullerE = pullable?.Puller; - if (pullableOldPullerE != null) - { - ForceDisconnect(EntityManager.GetComponent(pullableOldPullerE.Value), pullable!); - } - - // Continue with the puller. - var pullerOldPullableE = puller?.Pulling; - if (pullerOldPullableE != null) - { - ForceDisconnect(puller!, EntityManager.GetComponent(pullerOldPullableE.Value)); - } - - // And now for the actual connection (if any). - - if (puller != null && pullable != null) - { - var pullerPhysics = EntityManager.GetComponent(puller.Owner); - var pullablePhysics = EntityManager.GetComponent(pullable.Owner); - pullable.PullJointId = $"pull-joint-{pullable.Owner}"; - - // State startup - puller.Pulling = pullable.Owner; - pullable.Puller = puller.Owner; - - // joint state handling will manage its own state - if (!_timing.ApplyingState) - { - // Joint startup - var union = _physics.GetHardAABB(puller.Owner).Union(_physics.GetHardAABB(pullable.Owner, body: pullablePhysics)); - var length = Math.Max(union.Size.X, union.Size.Y) * 0.75f; - - var joint = _jointSystem.CreateDistanceJoint(pullablePhysics.Owner, pullerPhysics.Owner, id: pullable.PullJointId); - joint.CollideConnected = false; - // This maximum has to be there because if the object is constrained too closely, the clamping goes backwards and asserts. - joint.MaxLength = Math.Max(1.0f, length); - joint.Length = length * 0.75f; - joint.MinLength = 0f; - joint.Stiffness = 1f; - } - - // Messaging - var message = new PullStartedMessage(pullerPhysics, pullablePhysics); - - RaiseLocalEvent(puller.Owner, message, broadcast: false); - RaiseLocalEvent(pullable.Owner, message, true); - - // Networking - Dirty(puller); - Dirty(pullable); - } - } - - // For OnRemove use only. - public void ForceDisconnectPuller(SharedPullerComponent puller) - { - // DO NOT ADD ADDITIONAL LOGIC IN THIS FUNCTION. Do it in ForceRelationship. - ForceRelationship(puller, null); - } - - // For OnRemove use only. - public void ForceDisconnectPullable(SharedPullableComponent pullable) - { - // DO NOT ADD ADDITIONAL LOGIC IN THIS FUNCTION. Do it in ForceRelationship. - ForceRelationship(null, pullable); - } - - public void ForceSetMovingTo(SharedPullableComponent pullable, EntityCoordinates? movingTo) - { - if (_timing.ApplyingState) - return; - - if (pullable.MovingTo == movingTo) - { - return; - } - - // Don't allow setting a MovingTo if there's no puller. - // The other half of this guarantee (shutting down a MovingTo if the puller goes away) is enforced in ForceRelationship. - if (pullable.Puller == null && movingTo != null) - { - return; - } - - pullable.MovingTo = movingTo; - Dirty(pullable); - - if (movingTo == null) - { - RaiseLocalEvent(pullable.Owner, new PullableStopMovingMessage(), true); - } - else - { - RaiseLocalEvent(pullable.Owner, new PullableMoveMessage(), true); - } - } - - /// - /// Changes if the entity needs a hand in order to be able to pull objects. - /// - public void ChangeHandRequirement(EntityUid uid, bool needsHands, SharedPullerComponent? comp) - { - if (!Resolve(uid, ref comp, false)) - return; - - comp.NeedsHands = needsHands; - - Dirty(uid, comp); - } - } -} diff --git a/Content.Shared/Pulling/Systems/SharedPullingSystem.Actions.cs b/Content.Shared/Pulling/Systems/SharedPullingSystem.Actions.cs deleted file mode 100644 index 1e2bb90c61..0000000000 --- a/Content.Shared/Pulling/Systems/SharedPullingSystem.Actions.cs +++ /dev/null @@ -1,239 +0,0 @@ -using Content.Shared.ActionBlocker; -using Content.Shared.Administration.Logs; -using Content.Shared.Buckle.Components; -using Content.Shared.Database; -using Content.Shared.Hands.EntitySystems; -using Content.Shared.Interaction; -using Content.Shared.Physics.Pull; -using Content.Shared.Pulling.Components; -using Content.Shared.Pulling.Events; -using Robust.Shared.Containers; -using Robust.Shared.Map; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Timing; -using Robust.Shared.Utility; - -namespace Content.Shared.Pulling -{ - public abstract partial class SharedPullingSystem - { - [Dependency] private readonly ActionBlockerSystem _blocker = default!; - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; - [Dependency] private readonly SharedInteractionSystem _interaction = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - [Dependency] private readonly IGameTiming _timing = default!; - - public bool CanPull(EntityUid puller, EntityUid pulled) - { - if (!EntityManager.TryGetComponent(puller, out var comp)) - { - return false; - } - - if (comp.NeedsHands && !_handsSystem.TryGetEmptyHand(puller, out _)) - { - return false; - } - - if (!_blocker.CanInteract(puller, pulled)) - { - return false; - } - - if (!EntityManager.TryGetComponent(pulled, out var physics)) - { - return false; - } - - if (physics.BodyType == BodyType.Static) - { - return false; - } - - if (puller == pulled) - { - return false; - } - - if(_containerSystem.IsEntityInContainer(puller) || _containerSystem.IsEntityInContainer(pulled)) - { - return false; - } - - if (EntityManager.TryGetComponent(puller, out BuckleComponent? buckle)) - { - // Prevent people pulling the chair they're on, etc. - if (buckle is { PullStrap: false, Buckled: true } && (buckle.LastEntityBuckledTo == pulled)) - { - return false; - } - } - - var getPulled = new BeingPulledAttemptEvent(puller, pulled); - RaiseLocalEvent(pulled, getPulled, true); - var startPull = new StartPullAttemptEvent(puller, pulled); - RaiseLocalEvent(puller, startPull, true); - return (!startPull.Cancelled && !getPulled.Cancelled); - } - - public bool TogglePull(EntityUid puller, SharedPullableComponent pullable) - { - if (pullable.Puller == puller) - { - return TryStopPull(pullable); - } - return TryStartPull(puller, pullable.Owner); - } - - // -- Core attempted actions -- - - public bool TryStopPull(SharedPullableComponent pullable, EntityUid? user = null) - { - if (_timing.ApplyingState) - return false; - - if (!pullable.BeingPulled) - { - return false; - } - - var msg = new StopPullingEvent(user); - RaiseLocalEvent(pullable.Owner, msg, true); - - if (msg.Cancelled) return false; - - // Stop pulling confirmed! - - if (TryComp(pullable.Owner, out var pullablePhysics)) - { - _physics.SetFixedRotation(pullable.Owner, pullable.PrevFixedRotation, body: pullablePhysics); - } - - _pullSm.ForceRelationship(null, pullable); - return true; - } - - public bool TryStartPull(EntityUid puller, EntityUid pullable) - { - if (!EntityManager.TryGetComponent(puller, out SharedPullerComponent? pullerComp)) - { - return false; - } - if (!EntityManager.TryGetComponent(pullable, out SharedPullableComponent? pullableComp)) - { - return false; - } - return TryStartPull(pullerComp, pullableComp); - } - - // The main "start pulling" function. - public bool TryStartPull(SharedPullerComponent puller, SharedPullableComponent pullable) - { - if (_timing.ApplyingState) - return false; - - if (puller.Pulling == pullable.Owner) - return true; - - // Pulling a new object : Perform sanity checks. - - if (!CanPull(puller.Owner, pullable.Owner)) - { - return false; - } - - if (!EntityManager.TryGetComponent(puller.Owner, out var pullerPhysics)) - { - return false; - } - - if (!EntityManager.TryGetComponent(pullable.Owner, out var pullablePhysics)) - { - return false; - } - - // Ensure that the puller is not currently pulling anything. - // If this isn't done, then it happens too late, and the start/stop messages go out of order, - // and next thing you know it thinks it's not pulling anything even though it is! - - var oldPullable = puller.Pulling; - if (oldPullable != null) - { - if (EntityManager.TryGetComponent(oldPullable.Value, out SharedPullableComponent? oldPullableComp)) - { - if (!TryStopPull(oldPullableComp)) - { - return false; - } - } - else - { - Log.Warning("Well now you've done it, haven't you? Someone transferred pulling (onto {0}) while presently pulling something that has no Pullable component (on {1})!", pullable.Owner, oldPullable); - return false; - } - } - - // Ensure that the pullable is not currently being pulled. - // Same sort of reasons as before. - - var oldPuller = pullable.Puller; - if (oldPuller != null) - { - if (!TryStopPull(pullable)) - { - return false; - } - } - - // Continue with pulling process. - - var pullAttempt = new PullAttemptEvent(pullerPhysics, pullablePhysics); - - RaiseLocalEvent(puller.Owner, pullAttempt, broadcast: false); - - if (pullAttempt.Cancelled) - { - return false; - } - - RaiseLocalEvent(pullable.Owner, pullAttempt, true); - - if (pullAttempt.Cancelled) - return false; - - _interaction.DoContactInteraction(pullable.Owner, puller.Owner); - - _pullSm.ForceRelationship(puller, pullable); - pullable.PrevFixedRotation = pullablePhysics.FixedRotation; - _physics.SetFixedRotation(pullable.Owner, pullable.FixedRotationOnPull, body: pullablePhysics); - _adminLogger.Add(LogType.Action, LogImpact.Low, - $"{ToPrettyString(puller.Owner):user} started pulling {ToPrettyString(pullable.Owner):target}"); - return true; - } - - public bool TryMoveTo(SharedPullableComponent pullable, EntityCoordinates to) - { - if (pullable.Puller == null) - { - return false; - } - - if (!EntityManager.HasComponent(pullable.Owner)) - { - return false; - } - - _pullSm.ForceSetMovingTo(pullable, to); - return true; - } - - public void StopMoveTo(SharedPullableComponent pullable) - { - _pullSm.ForceSetMovingTo(pullable, null); - } - } -} diff --git a/Content.Shared/Pulling/Systems/SharedPullingSystem.cs b/Content.Shared/Pulling/Systems/SharedPullingSystem.cs deleted file mode 100644 index 0c139ee9e3..0000000000 --- a/Content.Shared/Pulling/Systems/SharedPullingSystem.cs +++ /dev/null @@ -1,243 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Content.Shared.Alert; -using Content.Shared.GameTicking; -using Content.Shared.Input; -using Content.Shared.Physics.Pull; -using Content.Shared.Pulling.Components; -using Content.Shared.Verbs; -using JetBrains.Annotations; -using Robust.Shared.Containers; -using Robust.Shared.Input.Binding; -using Robust.Shared.Map; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Events; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Player; - -namespace Content.Shared.Pulling -{ - [UsedImplicitly] - public abstract partial class SharedPullingSystem : EntitySystem - { - [Dependency] private readonly SharedPullingStateManagementSystem _pullSm = default!; - [Dependency] private readonly AlertsSystem _alertsSystem = default!; - [Dependency] private readonly SharedJointSystem _joints = default!; - - /// - /// A mapping of pullers to the entity that they are pulling. - /// - private readonly Dictionary _pullers = - new(); - - private readonly HashSet _moving = new(); - private readonly HashSet _stoppedMoving = new(); - - public IReadOnlySet Moving => _moving; - - public override void Initialize() - { - base.Initialize(); - - UpdatesOutsidePrediction = true; - - SubscribeLocalEvent(Reset); - SubscribeLocalEvent(OnPullStarted); - SubscribeLocalEvent(OnPullStopped); - SubscribeLocalEvent(HandleContainerInsert); - SubscribeLocalEvent(OnJointRemoved); - SubscribeLocalEvent(OnPullableCollisionChange); - - SubscribeLocalEvent(PullableHandlePullStarted); - SubscribeLocalEvent(PullableHandlePullStopped); - - SubscribeLocalEvent>(AddPullVerbs); - - CommandBinds.Builder - .Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(HandleMovePulledObject)) - .Register(); - } - - private void OnPullableCollisionChange(EntityUid uid, SharedPullableComponent component, ref CollisionChangeEvent args) - { - if (component.PullJointId != null && !args.CanCollide) - { - _joints.RemoveJoint(uid, component.PullJointId); - } - } - - private void OnJointRemoved(EntityUid uid, SharedPullableComponent component, JointRemovedEvent args) - { - if (component.Puller != args.OtherEntity) - return; - - // Do we have some other join with our Puller? - // or alternatively: - // TODO track the relevant joint. - - if (TryComp(uid, out JointComponent? joints)) - { - foreach (var jt in joints.GetJoints.Values) - { - if (jt.BodyAUid == component.Puller || jt.BodyBUid == component.Puller) - return; - } - } - - // No more joints with puller -> force stop pull. - _pullSm.ForceDisconnectPullable(component); - } - - private void AddPullVerbs(EntityUid uid, SharedPullableComponent component, GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - // Are they trying to pull themselves up by their bootstraps? - if (args.User == args.Target) - return; - - //TODO VERB ICONS add pulling icon - if (component.Puller == args.User) - { - Verb verb = new() - { - Text = Loc.GetString("pulling-verb-get-data-text-stop-pulling"), - Act = () => TryStopPull(component, args.User), - DoContactInteraction = false // pulling handle its own contact interaction. - }; - args.Verbs.Add(verb); - } - else if (CanPull(args.User, args.Target)) - { - Verb verb = new() - { - Text = Loc.GetString("pulling-verb-get-data-text"), - Act = () => TryStartPull(args.User, args.Target), - DoContactInteraction = false // pulling handle its own contact interaction. - }; - args.Verbs.Add(verb); - } - } - - // Raise a "you are being pulled" alert if the pulled entity has alerts. - private void PullableHandlePullStarted(EntityUid uid, SharedPullableComponent component, PullStartedMessage args) - { - if (args.Pulled.Owner != uid) - return; - - _alertsSystem.ShowAlert(uid, AlertType.Pulled); - } - - private void PullableHandlePullStopped(EntityUid uid, SharedPullableComponent component, PullStoppedMessage args) - { - if (args.Pulled.Owner != uid) - return; - - _alertsSystem.ClearAlert(uid, AlertType.Pulled); - } - - public bool IsPulled(EntityUid uid, SharedPullableComponent? component = null) - { - return Resolve(uid, ref component, false) && component.BeingPulled; - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - _moving.ExceptWith(_stoppedMoving); - _stoppedMoving.Clear(); - } - - public void Reset(RoundRestartCleanupEvent ev) - { - _pullers.Clear(); - _moving.Clear(); - _stoppedMoving.Clear(); - } - - private void OnPullStarted(PullStartedMessage message) - { - SetPuller(message.Puller.Owner, message.Pulled.Owner); - } - - private void OnPullStopped(PullStoppedMessage message) - { - RemovePuller(message.Puller.Owner); - } - - protected void OnPullableMove(EntityUid uid, SharedPullableComponent component, PullableMoveMessage args) - { - _moving.Add(component); - } - - protected void OnPullableStopMove(EntityUid uid, SharedPullableComponent component, PullableStopMovingMessage args) - { - _stoppedMoving.Add(component); - } - - // TODO: When Joint networking is less shitcodey fix this to use a dedicated joints message. - private void HandleContainerInsert(EntInsertedIntoContainerMessage message) - { - if (TryComp(message.Entity, out SharedPullableComponent? pullable)) - { - TryStopPull(pullable); - } - - if (TryComp(message.Entity, out SharedPullerComponent? puller)) - { - if (puller.Pulling == null) return; - - if (!TryComp(puller.Pulling.Value, out SharedPullableComponent? pulling)) - return; - - TryStopPull(pulling); - } - } - - private bool HandleMovePulledObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid) - { - if (session?.AttachedEntity is not { } player || - !player.IsValid()) - return false; - - if (!TryGetPulled(player, out var pulled)) - return false; - - if (!TryComp(pulled.Value, out SharedPullableComponent? pullable)) - return false; - - if (_containerSystem.IsEntityInContainer(player)) - return false; - - TryMoveTo(pullable, coords); - - return false; - } - - private void SetPuller(EntityUid puller, EntityUid pulled) - { - _pullers[puller] = pulled; - } - - private bool RemovePuller(EntityUid puller) - { - return _pullers.Remove(puller); - } - - public EntityUid GetPulled(EntityUid by) - { - return _pullers.GetValueOrDefault(by); - } - - public bool TryGetPulled(EntityUid by, [NotNullWhen(true)] out EntityUid? pulled) - { - return (pulled = GetPulled(by)) != null; - } - - public bool IsPulling(EntityUid puller) - { - return _pullers.ContainsKey(puller); - } - } -} diff --git a/Content.Shared/RCD/Systems/RCDSystem.cs b/Content.Shared/RCD/Systems/RCDSystem.cs index 187c8d8a9d..9e78451207 100644 --- a/Content.Shared/RCD/Systems/RCDSystem.cs +++ b/Content.Shared/RCD/Systems/RCDSystem.cs @@ -25,7 +25,6 @@ namespace Content.Shared.RCD.Systems; public sealed class RCDSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IMapManager _mapMan = default!; [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ITileDefinitionManager _tileDefMan = default!; @@ -39,7 +38,7 @@ public sealed class RCDSystem : EntitySystem [Dependency] private readonly TurfSystem _turf = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - private readonly int RcdModeCount = Enum.GetValues(typeof(RcdMode)).Length; + private readonly int _rcdModeCount = Enum.GetValues(typeof(RcdMode)).Length; public override void Initialize() { @@ -133,7 +132,7 @@ private void OnDoAfterAttempt(EntityUid uid, RCDComponent comp, DoAfterAttemptEv return; } - var mapGrid = _mapMan.GetGrid(gridId.Value); + var mapGrid = Comp(gridId.Value); var tile = mapGrid.GetTileRef(location); if (!IsRCDStillValid(uid, comp, args.Event.User, args.Event.Target, mapGrid, tile, args.Event.StartingMode)) @@ -158,7 +157,7 @@ private void OnDoAfter(EntityUid uid, RCDComponent comp, RCDDoAfterEvent args) return; } - var mapGrid = _mapMan.GetGrid(gridId.Value); + var mapGrid = Comp(gridId.Value); var tile = mapGrid.GetTileRef(location); var snapPos = mapGrid.TileIndicesFor(location); @@ -311,7 +310,7 @@ private void NextMode(EntityUid uid, RCDComponent comp, EntityUid user) _audio.PlayPredicted(comp.SwapModeSound, uid, user); var mode = (int) comp.Mode; - mode = ++mode % RcdModeCount; + mode = ++mode % _rcdModeCount; comp.Mode = (RcdMode) mode; Dirty(comp); diff --git a/Content.Shared/Remotes/Components/DoorRemoteComponent.cs b/Content.Shared/Remotes/Components/DoorRemoteComponent.cs new file mode 100644 index 0000000000..b157596e3b --- /dev/null +++ b/Content.Shared/Remotes/Components/DoorRemoteComponent.cs @@ -0,0 +1,19 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Remotes.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class DoorRemoteComponent : Component +{ + [AutoNetworkedField] + [DataField] + public OperatingMode Mode = OperatingMode.OpenClose; +} + +public enum OperatingMode : byte +{ + OpenClose, + ToggleBolts, + ToggleEmergencyAccess, + placeholderForUiUpdates +} diff --git a/Content.Shared/Remotes/EntitySystems/SharedDoorRemoteSystem.cs b/Content.Shared/Remotes/EntitySystems/SharedDoorRemoteSystem.cs new file mode 100644 index 0000000000..e9bbd27ada --- /dev/null +++ b/Content.Shared/Remotes/EntitySystems/SharedDoorRemoteSystem.cs @@ -0,0 +1,44 @@ +using Content.Shared.Popups; +using Content.Shared.Interaction.Events; +using Content.Shared.Remotes.Components; + +namespace Content.Shared.Remotes.EntitySystems; + +public abstract class SharedDoorRemoteSystem : EntitySystem +{ + [Dependency] protected readonly SharedPopupSystem Popup = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnInHandActivation); + } + + private void OnInHandActivation(Entity entity, ref UseInHandEvent args) + { + string switchMessageId; + switch (entity.Comp.Mode) + { + case OperatingMode.OpenClose: + entity.Comp.Mode = OperatingMode.ToggleBolts; + switchMessageId = "door-remote-switch-state-toggle-bolts"; + break; + + // Skip toggle bolts mode and move on from there (to emergency access) + case OperatingMode.ToggleBolts: + entity.Comp.Mode = OperatingMode.ToggleEmergencyAccess; + switchMessageId = "door-remote-switch-state-toggle-emergency-access"; + break; + + // Skip ToggleEmergencyAccess mode and move on from there (to door toggle) + case OperatingMode.ToggleEmergencyAccess: + entity.Comp.Mode = OperatingMode.OpenClose; + switchMessageId = "door-remote-switch-state-open-close"; + break; + default: + throw new InvalidOperationException( + $"{nameof(DoorRemoteComponent)} had invalid mode {entity.Comp.Mode}"); + } + Dirty(entity); + Popup.PopupClient(Loc.GetString(switchMessageId), entity, args.User); + } +} diff --git a/Content.Shared/Research/Components/ResearchStealerComponent.cs b/Content.Shared/Research/Components/ResearchStealerComponent.cs index e0331fad1b..df8878e651 100644 --- a/Content.Shared/Research/Components/ResearchStealerComponent.cs +++ b/Content.Shared/Research/Components/ResearchStealerComponent.cs @@ -14,4 +14,16 @@ public sealed partial class ResearchStealerComponent : Component /// [DataField("delay"), ViewVariables(VVAccess.ReadWrite)] public TimeSpan Delay = TimeSpan.FromSeconds(20); + + /// + /// The minimum number of technologies that will be stolen + /// + [DataField] + public int MinToSteal = 4; + + /// + /// The maximum number of technologies that will be stolen + /// + [DataField] + public int MaxToSteal = 8; } diff --git a/Content.Shared/Research/Systems/SharedResearchSystem.cs b/Content.Shared/Research/Systems/SharedResearchSystem.cs index 12f27d0b9c..9819e949b8 100644 --- a/Content.Shared/Research/Systems/SharedResearchSystem.cs +++ b/Content.Shared/Research/Systems/SharedResearchSystem.cs @@ -1,6 +1,7 @@ using System.Linq; using Content.Shared.Research.Components; using Content.Shared.Research.Prototypes; +using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; @@ -219,16 +220,58 @@ public void TrySetMainDiscipline(TechnologyPrototype prototype, EntityUid uid, T if (!Resolve(uid, ref component)) return; - var discipline = PrototypeManager.Index(prototype.Discipline); + var discipline = PrototypeManager.Index(prototype.Discipline); if (prototype.Tier < discipline.LockoutTier) return; component.MainDiscipline = prototype.Discipline; Dirty(uid, component); } + /// + /// Removes a technology and its recipes from a technology database. + /// + public bool TryRemoveTechnology(Entity entity, ProtoId tech) + { + return TryRemoveTechnology(entity, PrototypeManager.Index(tech)); + } + + /// + /// Removes a technology and its recipes from a technology database. + /// + [PublicAPI] + public bool TryRemoveTechnology(Entity entity, TechnologyPrototype tech) + { + if (!entity.Comp.UnlockedTechnologies.Remove(tech.ID)) + return false; + + // check to make sure we didn't somehow get the recipe from another tech. + // unlikely, but whatever + var recipes = tech.RecipeUnlocks; + foreach (var recipe in recipes) + { + var hasTechElsewhere = false; + foreach (var unlockedTech in entity.Comp.UnlockedTechnologies) + { + var unlockedTechProto = PrototypeManager.Index(unlockedTech); + + if (!unlockedTechProto.RecipeUnlocks.Contains(recipe)) + continue; + hasTechElsewhere = true; + break; + } + + if (!hasTechElsewhere) + entity.Comp.UnlockedRecipes.Remove(recipe); + } + Dirty(entity, entity.Comp); + UpdateTechnologyCards(entity, entity); + return true; + } + /// /// Clear all unlocked technologies from the database. /// + [PublicAPI] public void ClearTechs(EntityUid uid, TechnologyDatabaseComponent? comp = null) { if (!Resolve(uid, ref comp) || comp.UnlockedTechnologies.Count == 0) diff --git a/Content.Shared/Security/Systems/DeployableBarrierSystem.cs b/Content.Shared/Security/Systems/DeployableBarrierSystem.cs index 7b9ce841a9..622edc4b62 100644 --- a/Content.Shared/Security/Systems/DeployableBarrierSystem.cs +++ b/Content.Shared/Security/Systems/DeployableBarrierSystem.cs @@ -1,6 +1,6 @@ using Content.Shared.Lock; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Security.Components; using Robust.Shared.Physics.Systems; @@ -11,7 +11,7 @@ public sealed class DeployableBarrierSystem : EntitySystem [Dependency] private readonly FixtureSystem _fixtures = default!; [Dependency] private readonly SharedPointLightSystem _pointLight = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly SharedPullingSystem _pulling = default!; + [Dependency] private readonly PullingSystem _pulling = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; public override void Initialize() @@ -54,8 +54,8 @@ private void ToggleBarrierDeploy(EntityUid uid, bool isDeployed, DeployableBarri _physics.SetHard(uid, fixture, false); } - if (TryComp(uid, out SharedPullableComponent? pullable)) - _pulling.TryStopPull(pullable); + if (TryComp(uid, out PullableComponent? pullable)) + _pulling.TryStopPull(uid, pullable); SharedPointLightComponent? pointLight = null; if (_pointLight.ResolveLight(uid, ref pointLight)) diff --git a/Content.Shared/Shuttles/Components/IFFComponent.cs b/Content.Shared/Shuttles/Components/IFFComponent.cs index a7e6ac1152..6bacbd2b5b 100644 --- a/Content.Shared/Shuttles/Components/IFFComponent.cs +++ b/Content.Shared/Shuttles/Components/IFFComponent.cs @@ -10,11 +10,6 @@ namespace Content.Shared.Shuttles.Components; [Access(typeof(SharedShuttleSystem))] public sealed partial class IFFComponent : Component { - /// - /// Should we show IFF by default? - /// - public const bool ShowIFFDefault = true; - public static readonly Color SelfColor = Color.MediumSpringGreen; /// diff --git a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.IFF.cs b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.IFF.cs index ed687d48f4..8231e48e2d 100644 --- a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.IFF.cs +++ b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.IFF.cs @@ -28,11 +28,6 @@ public Color GetIFFColor(EntityUid gridUid, bool self = false, IFFComponent? com public string? GetIFFLabel(EntityUid gridUid, bool self = false, IFFComponent? component = null) { - if (!IFFComponent.ShowIFFDefault) - { - return null; - } - var entName = MetaData(gridUid).EntityName; if (self) diff --git a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs index 324fd65c86..ca25a49b23 100644 --- a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs +++ b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs @@ -146,7 +146,6 @@ public bool FTLFree(EntityUid shuttleUid, EntityCoordinates coordinates, Angle a // Just checks if any grids inside of a buffer range at the target position. _grids.Clear(); - var ftlRange = FTLRange; var mapCoordinates = coordinates.ToMap(EntityManager, XformSystem); var ourPos = Maps.GetGridPosition((shuttleUid, shuttlePhysics, shuttleXform)); diff --git a/Content.Shared/Sound/SharedEmitSoundSystem.cs b/Content.Shared/Sound/SharedEmitSoundSystem.cs index 22ba8e0e3e..56a51744ac 100644 --- a/Content.Shared/Sound/SharedEmitSoundSystem.cs +++ b/Content.Shared/Sound/SharedEmitSoundSystem.cs @@ -9,6 +9,7 @@ using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Network; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; @@ -25,7 +26,6 @@ public abstract class SharedEmitSoundSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly INetManager _netMan = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDefMan = default!; [Dependency] protected readonly IRobustRandom Random = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; @@ -54,7 +54,7 @@ private void OnEmitSoundOnLand(EntityUid uid, BaseEmitSoundComponent component, { if (!args.PlaySound || !TryComp(uid, out var xform) || - !_mapManager.TryGetGrid(xform.GridUid, out var grid)) + !TryComp(xform.GridUid, out var grid)) { return; } diff --git a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs index 04f7231416..568d9dab3b 100644 --- a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs +++ b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs @@ -19,17 +19,16 @@ public sealed class DumpableSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedDisposalUnitSystem _disposalUnitSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - private EntityQuery _xformQuery; + private EntityQuery _itemQuery; public override void Initialize() { base.Initialize(); - _xformQuery = GetEntityQuery(); + _itemQuery = GetEntityQuery(); SubscribeLocalEvent(OnAfterInteract, after: new[]{ typeof(SharedEntityStorageSystem) }); SubscribeLocalEvent>(AddDumpVerb); SubscribeLocalEvent>(AddUtilityVerbs); @@ -111,7 +110,7 @@ private void AddUtilityVerbs(EntityUid uid, DumpableComponent dumpable, GetVerbs } } - private void StartDoAfter(EntityUid storageUid, EntityUid? targetUid, EntityUid userUid, DumpableComponent dumpable) + private void StartDoAfter(EntityUid storageUid, EntityUid targetUid, EntityUid userUid, DumpableComponent dumpable) { if (!TryComp(storageUid, out var storage)) return; @@ -120,7 +119,7 @@ private void StartDoAfter(EntityUid storageUid, EntityUid? targetUid, EntityUid foreach (var entity in storage.Container.ContainedEntities) { - if (!TryComp(entity, out var itemComp) || + if (!_itemQuery.TryGetComponent(entity, out var itemComp) || !_prototypeManager.TryIndex(itemComp.Size, out var itemSize)) { continue; @@ -139,33 +138,16 @@ private void StartDoAfter(EntityUid storageUid, EntityUid? targetUid, EntityUid }); } - private void OnDoAfter(EntityUid uid, DumpableComponent component, DoAfterEvent args) + private void OnDoAfter(EntityUid uid, DumpableComponent component, DumpableDoAfterEvent args) { - if (args.Handled || args.Cancelled || !TryComp(uid, out var storage)) + if (args.Handled || args.Cancelled || !TryComp(uid, out var storage) || storage.Container.ContainedEntities.Count == 0) return; - Queue dumpQueue = new(); - foreach (var entity in storage.Container.ContainedEntities) - { - dumpQueue.Enqueue(entity); - } - - if (dumpQueue.Count == 0) - return; - - foreach (var entity in dumpQueue) - { - var transform = Transform(entity); - _container.AttachParentToContainerOrGrid((entity, transform)); - _transformSystem.SetLocalPositionRotation(entity, transform.LocalPosition + _random.NextVector2Box() / 2, _random.NextAngle(), transform); - } - - if (args.Args.Target == null) - return; + var dumpQueue = new Queue(storage.Container.ContainedEntities); var dumped = false; - if (_disposalUnitSystem.HasDisposals(args.Args.Target.Value)) + if (_disposalUnitSystem.HasDisposals(args.Args.Target)) { dumped = true; @@ -174,22 +156,31 @@ private void OnDoAfter(EntityUid uid, DumpableComponent component, DoAfterEvent _disposalUnitSystem.DoInsertDisposalUnit(args.Args.Target.Value, entity, args.Args.User); } } - else if (HasComp(args.Args.Target.Value)) + else if (HasComp(args.Args.Target)) { dumped = true; - var targetPos = _xformQuery.GetComponent(args.Args.Target.Value).LocalPosition; + var targetPos = _transformSystem.GetWorldPosition(args.Args.Target.Value); + + foreach (var entity in dumpQueue) + { + _transformSystem.SetWorldPosition(entity, targetPos + _random.NextVector2Box() / 4); + } + } + else + { + var targetPos = _transformSystem.GetWorldPosition(uid); foreach (var entity in dumpQueue) { - _transformSystem.SetLocalPosition(entity, targetPos + _random.NextVector2Box() / 4); + var transform = Transform(entity); + _transformSystem.SetWorldPositionRotation(entity, targetPos + _random.NextVector2Box() / 4, _random.NextAngle(), transform); } } if (dumped) { - // TODO: Predicted when above predicted - _audio.PlayPvs(component.DumpSound, uid); + _audio.PlayPredicted(component.DumpSound, uid, args.User); } } } diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 11075b4a4c..799fb7e33e 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -3,6 +3,7 @@ using System.Linq; using Content.Shared.ActionBlocker; using Content.Shared.Containers.ItemSlots; +using Content.Shared.Coordinates; using Content.Shared.Destructible; using Content.Shared.DoAfter; using Content.Shared.Hands.Components; @@ -12,6 +13,7 @@ using Content.Shared.Item; using Content.Shared.Lock; using Content.Shared.Nyanotrasen.Item.PseudoItem; +using Content.Shared.Materials; using Content.Shared.Placeable; using Content.Shared.Popups; using Content.Shared.Stacks; @@ -96,6 +98,9 @@ public override void Initialize() SubscribeAllEvent(OnSetItemLocation); SubscribeAllEvent(OnInsertItemIntoLocation); SubscribeAllEvent(OnRemoveItem); + + SubscribeLocalEvent(OnReclaimed); + UpdatePrototypeCache(); } @@ -389,6 +394,11 @@ private void OnDoAfter(EntityUid uid, StorageComponent component, AreaPickupDoAf args.Handled = true; } + private void OnReclaimed(EntityUid uid, StorageComponent storageComp, GotReclaimedEvent args) + { + _containerSystem.EmptyContainer(storageComp.Container, destination: args.ReclaimerCoordinates); + } + private void OnDestroy(EntityUid uid, StorageComponent storageComp, DestructionEventArgs args) { var coordinates = TransformSystem.GetMoverCoordinates(uid); diff --git a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs index 02b4e61790..ba78ff651f 100644 --- a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs +++ b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs @@ -15,7 +15,6 @@ namespace Content.Shared.SubFloor [UsedImplicitly] public abstract class SharedSubFloorHideSystem : EntitySystem { - [Dependency] protected readonly IMapManager MapManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; @@ -93,7 +92,7 @@ private void OnTileChanged(ref TileChangedEvent args) if (args.NewTile.Tile.IsEmpty) return; // Anything that was here will be unanchored anyways. - UpdateTile(MapManager.GetGrid(args.NewTile.GridUid), args.NewTile.GridIndices); + UpdateTile(Comp(args.NewTile.GridUid), args.NewTile.GridIndices); } /// @@ -104,7 +103,7 @@ private void UpdateFloorCover(EntityUid uid, SubFloorHideComponent? component = if (!Resolve(uid, ref component, ref xform)) return; - if (xform.Anchored && MapManager.TryGetGrid(xform.GridUid, out var grid)) + if (xform.Anchored && TryComp(xform.GridUid, out var grid)) component.IsUnderCover = HasFloorCover(grid, grid.TileIndicesFor(xform.Coordinates)); else component.IsUnderCover = false; diff --git a/Content.Shared/Teleportation/Systems/SharedPortalSystem.cs b/Content.Shared/Teleportation/Systems/SharedPortalSystem.cs index ebd8362411..8d67aec518 100644 --- a/Content.Shared/Teleportation/Systems/SharedPortalSystem.cs +++ b/Content.Shared/Teleportation/Systems/SharedPortalSystem.cs @@ -1,9 +1,9 @@ using System.Linq; using Content.Shared.Ghost; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Popups; using Content.Shared.Projectiles; -using Content.Shared.Pulling; -using Content.Shared.Pulling.Components; using Content.Shared.Teleportation.Components; using Content.Shared.Verbs; using Robust.Shared.Audio; @@ -28,7 +28,7 @@ public abstract class SharedPortalSystem : EntitySystem [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly SharedPullingSystem _pulling = default!; + [Dependency] private readonly PullingSystem _pulling = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; private const string PortalFixture = "portalFixture"; @@ -93,15 +93,15 @@ private void OnCollide(EntityUid uid, PortalComponent component, ref StartCollid return; // break pulls before portal enter so we dont break shit - if (TryComp(subject, out var pullable) && pullable.BeingPulled) + if (TryComp(subject, out var pullable) && pullable.BeingPulled) { - _pulling.TryStopPull(pullable); + _pulling.TryStopPull(subject, pullable); } - if (TryComp(subject, out var pulling) - && pulling.Pulling != null && TryComp(pulling.Pulling.Value, out var subjectPulling)) + if (TryComp(subject, out var pullerComp) + && TryComp(pullerComp.Pulling, out var subjectPulling)) { - _pulling.TryStopPull(subjectPulling); + _pulling.TryStopPull(subject, subjectPulling); } // if they came from another portal, just return and wait for them to exit the portal diff --git a/Content.Shared/Throwing/ThrowingSystem.cs b/Content.Shared/Throwing/ThrowingSystem.cs index 0168286338..38772eaf34 100644 --- a/Content.Shared/Throwing/ThrowingSystem.cs +++ b/Content.Shared/Throwing/ThrowingSystem.cs @@ -20,6 +20,11 @@ public sealed class ThrowingSystem : EntitySystem { public const float ThrowAngularImpulse = 5f; + /// + /// Speed cap on rotation in case of click-spam. + /// + public const float ThrowAngularCap = 3f * MathF.PI; + public const float PushbackDefault = 2f; /// @@ -42,15 +47,17 @@ public void TryThrow( float strength = 1.0f, EntityUid? user = null, float pushbackRatio = PushbackDefault, + bool recoil = true, + bool animated = true, bool playSound = true) { - var thrownPos = Transform(uid).MapPosition; - var mapPos = coordinates.ToMap(EntityManager, _transform); + var thrownPos = _transform.GetMapCoordinates(uid); + var mapPos = _transform.ToMapCoordinates(coordinates); if (mapPos.MapId != thrownPos.MapId) return; - TryThrow(uid, mapPos.Position - thrownPos.Position, strength, user, pushbackRatio, playSound); + TryThrow(uid, mapPos.Position - thrownPos.Position, strength, user, pushbackRatio, recoil: recoil, animated: animated, playSound: playSound); } /// @@ -65,6 +72,8 @@ public void TryThrow(EntityUid uid, float strength = 1.0f, EntityUid? user = null, float pushbackRatio = PushbackDefault, + bool recoil = true, + bool animated = true, bool playSound = true) { var physicsQuery = GetEntityQuery(); @@ -72,7 +81,6 @@ public void TryThrow(EntityUid uid, return; var projectileQuery = GetEntityQuery(); - var tagQuery = GetEntityQuery(); TryThrow( uid, @@ -82,8 +90,7 @@ public void TryThrow(EntityUid uid, projectileQuery, strength, user, - pushbackRatio, - playSound); + pushbackRatio, recoil: recoil, animated: animated, playSound: playSound); } /// @@ -101,6 +108,8 @@ public void TryThrow(EntityUid uid, float strength = 1.0f, EntityUid? user = null, float pushbackRatio = PushbackDefault, + bool recoil = true, + bool animated = true, bool playSound = true) { if (strength <= 0 || direction == Vector2Helpers.Infinity || direction == Vector2Helpers.NaN || direction == Vector2.Zero) @@ -116,12 +125,17 @@ public void TryThrow(EntityUid uid, if (projectileQuery.TryGetComponent(uid, out var proj) && !proj.OnlyCollideWhenShot) return; - var comp = new ThrownItemComponent(); - comp.Thrower = user; + var comp = new ThrownItemComponent + { + Thrower = user, + Animate = animated, + }; // Estimate time to arrival so we can apply OnGround status and slow it much faster. var time = direction.Length() / strength; comp.ThrownTime = _gameTiming.CurTime; + // TODO: This is a bandaid, don't do this. + // if you want to force landtime have the caller handle it or add a new method. // did we launch this with something stronger than our hands? if (TryComp(comp.Thrower, out var hands) && strength > hands.ThrowForceMultiplier) comp.LandTime = comp.ThrownTime + TimeSpan.FromSeconds(time); @@ -166,7 +180,8 @@ public void TryThrow(EntityUid uid, if (user == null) return; - _recoil.KickCamera(user.Value, -direction * 0.04f); + if (recoil) + _recoil.KickCamera(user.Value, -direction * 0.04f); // Give thrower an impulse in the other direction if (pushbackRatio != 0.0f && diff --git a/Content.Shared/Throwing/ThrownItemComponent.cs b/Content.Shared/Throwing/ThrownItemComponent.cs index 16c9b13254..f7defaa4aa 100644 --- a/Content.Shared/Throwing/ThrownItemComponent.cs +++ b/Content.Shared/Throwing/ThrownItemComponent.cs @@ -8,6 +8,12 @@ namespace Content.Shared.Throwing [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), AutoGenerateComponentPause] public sealed partial class ThrownItemComponent : Component { + /// + /// Should the in-air throwing animation play. + /// + [DataField, AutoNetworkedField] + public bool Animate = true; + /// /// The entity that threw this entity. /// diff --git a/Content.Shared/Throwing/ThrownItemSystem.cs b/Content.Shared/Throwing/ThrownItemSystem.cs index cc50094e3d..ef28db2672 100644 --- a/Content.Shared/Throwing/ThrownItemSystem.cs +++ b/Content.Shared/Throwing/ThrownItemSystem.cs @@ -3,8 +3,7 @@ using Content.Shared.Database; using Content.Shared.Gravity; using Content.Shared.Physics; -using Content.Shared.Physics.Pull; -using Robust.Shared.GameStates; +using Content.Shared.Movement.Pulling.Events; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; @@ -85,8 +84,8 @@ private void OnSleep(EntityUid uid, ThrownItemComponent thrownItem, ref PhysicsS private void HandlePullStarted(PullStartedMessage message) { // TODO: this isn't directed so things have to be done the bad way - if (EntityManager.TryGetComponent(message.Pulled.Owner, out ThrownItemComponent? thrownItemComponent)) - StopThrow(message.Pulled.Owner, thrownItemComponent); + if (EntityManager.TryGetComponent(message.PulledUid, out ThrownItemComponent? thrownItemComponent)) + StopThrow(message.PulledUid, thrownItemComponent); } public void StopThrow(EntityUid uid, ThrownItemComponent thrownItemComponent) diff --git a/Content.Shared/Tiles/FloorTileSystem.cs b/Content.Shared/Tiles/FloorTileSystem.cs index 1f8408319d..0d368495f1 100644 --- a/Content.Shared/Tiles/FloorTileSystem.cs +++ b/Content.Shared/Tiles/FloorTileSystem.cs @@ -116,7 +116,7 @@ private void OnAfterInteract(EntityUid uid, FloorTileComponent component, AfterI } } } - _mapManager.TryGetGrid(location.EntityId, out var mapGrid); + TryComp(location.EntityId, out var mapGrid); foreach (var currentTile in component.OutputTiles) { diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Interactions.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Interactions.cs index d47d024de5..274828a208 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Interactions.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Interactions.cs @@ -102,7 +102,7 @@ public void CycleFire(EntityUid uid, GunComponent component, EntityUid? user = n // TODO: Actions need doing for guns anyway. private sealed partial class CycleModeEvent : InstantActionEvent { - public SelectiveFire Mode; + public SelectiveFire Mode = default; } private void OnCycleMode(EntityUid uid, GunComponent component, CycleModeEvent args) diff --git a/Content.Tests/Shared/Chat/V2/Moderation/SimpleCensor.cs b/Content.Tests/Shared/Chat/V2/Moderation/SimpleCensor.cs new file mode 100644 index 0000000000..09870af317 --- /dev/null +++ b/Content.Tests/Shared/Chat/V2/Moderation/SimpleCensor.cs @@ -0,0 +1,162 @@ +using System.Text.Unicode; +using Content.Shared.Chat.V2.Moderation; +using NUnit.Framework; + +namespace Content.Tests.Shared.Chat.V2.Moderation; + +public sealed class SimpleCensorTests +{ + [Test] + public void CanCensorASingleWord() + { + var sut = new SimpleCensor().WithCustomDictionary(["amogus"]); + var output = sut.Censor("hello amogus"); + + Assert.That(output, Is.EqualTo("hello ******")); + } + + // Basics - use custom dictionary + + [Test] + public void CanCensorMultipleWordInstances() + { + var sut= new SimpleCensor().WithCustomDictionary(["amogus"]); + var output = sut.Censor("amogus hello amogus"); + + Assert.That(output, Is.EqualTo("****** hello ******")); + } + + [Test] + public void CanCensorMultipleWords() + { + var sut= new SimpleCensor().WithCustomDictionary(["amogus", "sus"]); + var output = sut.Censor("amogus hello sus"); + + Assert.That(output, Is.EqualTo("****** hello ***")); + } + + [Test] + public void CanUseDifferentCensorSymbols() + { + var sut= new SimpleCensor().WithCustomDictionary(["amogus", "sus"]); + var output = sut.Censor("amogus hello sus", '#'); + + Assert.That(output, Is.EqualTo("###### hello ###")); + } + + [Test] + public void CanCatchCapitalizedWords() + { + var sut= new SimpleCensor().WithCustomDictionary(["amogus", "sus"]); + var output = sut.Censor("AMOGUS hello SUS"); + + Assert.That(output, Is.EqualTo("****** hello ***")); + } + + [Test] + public void CanCatchWordsWithSomeCaptialsInThem() + { + var sut= new SimpleCensor().WithCustomDictionary(["amogus", "sus"]); + var output = sut.Censor("AmoGuS hello SuS"); + + Assert.That(output, Is.EqualTo("****** hello ***")); + } + + [Test] + public void CanCatchWordsHiddenInsideOtherWords() + { + var sut= new SimpleCensor().WithCustomDictionary(["amogus", "sus"]); + var output = sut.Censor("helamoguslo suspicious"); + + Assert.That(output, Is.EqualTo("hel******lo ***picious")); + } + + // Sanitizing Leetspeak + + [Test] + public void CanSanitizeLeetspeak() + { + var sut = new SimpleCensor().WithCustomDictionary(["amogus", "sus"]).WithSanitizeLeetSpeak(); + var output = sut.Censor("am0gu5 hello 5u5"); + + Assert.That(output, Is.EqualTo("****** hello ***")); + } + + [Test] + public void SanitizingLeetspeakOnlyOccursWhenTheWordIsBlocked() + { + var sut = new SimpleCensor().WithCustomDictionary(["amogus", "sus"]).WithSanitizeLeetSpeak(); + var output = sut.Censor("he110"); + + Assert.That(output, Is.EqualTo("he110")); + } + + [Test] + public void CanCatchLeetspeakReplacementsWithMoreThanOneLetter() + { + var sut = new SimpleCensor().WithCustomDictionary(["amogus", "sus"]).WithSanitizeLeetSpeak(); + var output = sut.Censor("am()gu5 hello 5u5"); + + Assert.That(output, Is.EqualTo("******* hello ***")); + } + + // Sanitizing special characters + + [Test] + public void DoesNotSanitizeOutUncensoredSpecialCharacters() + { + var sut = new SimpleCensor().WithCustomDictionary(["amogus", "sus"]).WithSanitizeSpecialCharacters(); + var output = sut.Censor("amogus!hello!sus"); + + Assert.That(output, Is.EqualTo("******!hello!***")); + } + + [Test] + public void DoesSanitizeOutCensoredSpecialCharacters() + { + var sut = new SimpleCensor().WithCustomDictionary(["amogus", "sus"]).WithSanitizeSpecialCharacters(); + var output = sut.Censor("amo!gus hello s?us"); + + Assert.That(output, Is.EqualTo("***!*** hello *?**")); + } + + // Unicode ranges + + [Test] + public void SanitizesOutNonLatinCharaters() + { + var sut = new SimpleCensor().WithRanges([UnicodeRanges.BasicLatin, UnicodeRanges.Latin1Supplement]); + var output = sut.Censor("amogus Україна sus 日本"); + + Assert.That(output, Is.EqualTo("amogus sus ")); + } + + [Test] + public void SanitizesOutNonLatinOrCyrillicCharaters() + { + var sut = new SimpleCensor().WithRanges([UnicodeRanges.BasicLatin, UnicodeRanges.Latin1Supplement, UnicodeRanges.Cyrillic]); + var output = sut.Censor("amogus Україна sus 日本"); + + Assert.That(output, Is.EqualTo("amogus Україна sus ")); + } + + // False positives + [Test] + public void CanHandleFalsePositives() + { + var sut = new SimpleCensor().WithCustomDictionary(["amogus", "sus"]).WithFalsePositives(["amogusus"]); + var output = sut.Censor("amogusus hello amogus hello sus"); + + Assert.That(output, Is.EqualTo("amogusus hello ****** hello ***")); + } + + // False negatives + [Test] + public void CanHandleFalseNegatives() + { + var sut = new SimpleCensor().WithCustomDictionary(["amogus", "sus"]).WithFalsePositives(["amogusus"]).WithFalseNegatives(["susamogusus"]); + var output = sut.Censor("susamogusus hello amogus hello sus amogusus"); + + Assert.That(output, Is.EqualTo("*********** hello ****** hello *** ********")); + } +} diff --git a/Content.YAMLLinter/Program.cs b/Content.YAMLLinter/Program.cs index b7b70bd118..78867fcb8a 100644 --- a/Content.YAMLLinter/Program.cs +++ b/Content.YAMLLinter/Program.cs @@ -97,7 +97,7 @@ await instance.WaitPost(() => yamlErrors[kind] = set; } - fieldErrors = protoMan.ValidateFields(prototypes); + fieldErrors = protoMan.ValidateStaticFields(prototypes); }); return (yamlErrors, fieldErrors); diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 0a5a726a6a..49aa0a6c97 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -4373,3 +4373,53 @@ Entries: message: Changed asteroids to have more ore...and some danger id: 6147 time: '2024-07-08T07:03:39.0000000+00:00' +- author: FoxxoTrystan + changes: + - type: Add + message: Languages are now marked in the chat! + id: 6148 + time: '2024-07-09T19:01:38.0000000+00:00' +- author: Spatison + changes: + - type: Fix + message: Food and handcuffs can now be given + id: 6149 + time: '2024-07-09T19:32:11.0000000+00:00' +- author: FoxxoTrrystan + changes: + - type: Tweak + message: >- + Power Resprites! (New SMES, APC, Airlarm, Camera, Substation, LightTube, + Intercom, FireAlarm and Substation) + id: 6150 + time: '2024-07-09T19:37:03.0000000+00:00' +- author: CodedCrow + changes: + - type: Add + message: Plushie + id: 6151 + time: '2024-07-11T17:23:58.0000000+00:00' +- author: osjarw + changes: + - type: Add + message: Wallmount substation electronics can now be created at an autolathe. + id: 6152 + time: '2024-07-11T17:24:30.0000000+00:00' +- author: Mnemotechnician + changes: + - type: Tweak + message: 'Players can now vote on two more gamemodes: Traitors and Hellshift.' + id: 6153 + time: '2024-07-11T17:26:41.0000000+00:00' +- author: Mnemotechnician + changes: + - type: Add + message: >- + Added two new foreigner traits that make your character unable to speak + Galactic Common and give you a translator instead. + - type: Tweak + message: >- + Translators can now be equipped in the neck slot and display useful info + when examined. + id: 6154 + time: '2024-07-12T01:19:09.0000000+00:00' diff --git a/Resources/Fonts/Copperplate.otf b/Resources/Fonts/Copperplate.otf new file mode 100644 index 0000000000..40d45aa46b Binary files /dev/null and b/Resources/Fonts/Copperplate.otf differ diff --git a/Resources/Fonts/Mangat.ttf b/Resources/Fonts/Mangat.ttf new file mode 100644 index 0000000000..de4c11beba Binary files /dev/null and b/Resources/Fonts/Mangat.ttf differ diff --git a/Resources/Fonts/Noganas.ttf b/Resources/Fonts/Noganas.ttf new file mode 100644 index 0000000000..afa0c82f03 Binary files /dev/null and b/Resources/Fonts/Noganas.ttf differ diff --git a/Resources/Fonts/RubikBubbles.ttf b/Resources/Fonts/RubikBubbles.ttf new file mode 100644 index 0000000000..a653715c6c Binary files /dev/null and b/Resources/Fonts/RubikBubbles.ttf differ diff --git a/Resources/Locale/en-US/administration/commands/stealthmin-command.ftl b/Resources/Locale/en-US/administration/commands/stealthmin-command.ftl new file mode 100644 index 0000000000..4fb5e52105 --- /dev/null +++ b/Resources/Locale/en-US/administration/commands/stealthmin-command.ftl @@ -0,0 +1,3 @@ +cmd-stealthmin-desc = Toggle whether others can see you in adminwho. +cmd-stealthmin-help = Usage: stealthmin\nUse stealthmin to toggle whether you appear in the output of the adminwho command. +cmd-stealthmin-no-console = You cannot use this command from the server console. diff --git a/Resources/Locale/en-US/administration/managers/admin-manager.ftl b/Resources/Locale/en-US/administration/managers/admin-manager.ftl index b1bbcc4c8c..b70f550fc3 100644 --- a/Resources/Locale/en-US/administration/managers/admin-manager.ftl +++ b/Resources/Locale/en-US/administration/managers/admin-manager.ftl @@ -6,4 +6,8 @@ admin-manager-no-longer-admin-message = You are no longer an admin. admin-manager-admin-permissions-updated-message = Your admin permission have been updated. admin-manager-admin-logout-message = Admin logout: {$name} admin-manager-admin-login-message = Admin login: {$name} -admin-manager-admin-data-host-title = Host \ No newline at end of file +admin-manager-admin-data-host-title = Host +admin-manager-stealthed-message = You are now a hidden admin. +admin-manager-unstealthed-message = You are no longer hidden. +admin-manager-self-enable-stealth = {$stealthAdminName} is now hidden. +admin-manager-self-disable-stealth = {$exStealthAdminName} is no longer hidden. diff --git a/Resources/Locale/en-US/cargo/bounties.ftl b/Resources/Locale/en-US/cargo/bounties.ftl index 63c68de105..b332517c70 100644 --- a/Resources/Locale/en-US/cargo/bounties.ftl +++ b/Resources/Locale/en-US/cargo/bounties.ftl @@ -47,8 +47,24 @@ bounty-item-organs = Organ bounty-item-labeler = Hand labeler bounty-item-warm-cloth = Warm clothes bounty-item-battery = Battery -bounty-lasergun = Laser gun -bounty-food = Meat food +bounty-item-lasergun = Laser gun +bounty-item-food = Meat food +bounty-item-fruit = Fruit +bounty-item-vegetable = Vegetable +bounty-item-chili = Bowl of chili +bounty-item-rollerskates = Roller skates +bounty-item-bedsheet = Bedsheet +bounty-item-bandana = Bandana +bounty-item-steak = Steak +bounty-item-banana = Banana +bounty-item-beer = Beer +bounty-item-hi-viz-vest = Hi-viz vest +bounty-item-torch = Torch +bounty-item-medkit-box = Medkit box +bounty-item-cardboard-box = Cardboard box +bounty-item-wine = Wine bottle +bounty-item-cotton-boll = Cotton boll +bounty-item-microwave-machine-board = Microwave machine board bounty-description-artifact = NanoTrasen is in some hot water for stealing artifacts from non-spacefaring planets. Return one and we'll compensate you for it. bounty-description-baseball-bat = Baseball fever is going on at CentCom! Be a dear and ship them some baseball bats, so that management can live out their childhood dream. @@ -100,3 +116,19 @@ bounty-description-warm-cloth = The Unath construction crew freezes and is unabl bounty-description-battery = As the Arachnid settlement prepares for a solar flare, they are requesting a large shipment of power batteries. We're sending out a request for delivery. bounty-description-lasergun = The Salvage Caravan requests a large shipment of laser weapons to mop up a hive of xenomorphs. bounty-description-food = After the rat king invasion, a neighboring unathi station was left completely without food. A large meat food shipment is needed. +bounty-description-fruit = A heroic monkey helped the chaplain catch a troublemaker hiding in the chapel, and the crew wants to reward him for his good work. +bounty-description-vegetable = The new chef is a vegetarian, and botany can't keep up with their demands. We need some additional veggies to help keep things stocked. +bounty-description-chili = Today's the Centcomm Chili Cookoff, and, well, a few of us forgot to make some. Please help cover for us. +bounty-description-rollerskates = CentComm Security is proposing a new strategy for helping officers win foot pursuits. Send them a couple so they cna learn how bad an idea this is. +bounty-description-bedsheet = Someone in Atmos keeps turning down the heater, and we're all shivering in our beds. Please send us some extra sheets to stay warm. +bounty-description-bandana = Bzzzt... Transmission from prison planet OC-1001: We're... reorganizing our command structure. Send us some bandanas so we can tell gan- I mean, departments apart. +bounty-description-steak = The vegetarian cook is refusing to make us anything with meat, and the lizards are getting restless. Can you smuggle us a few steaks to keep them happy? +bounty-description-banana = Hi station! Botany won't gimme any more. They said slipping the HoS out an open airlock wasn't funny! Can you believe it? Help me out! HONK. +bounty-description-beer = Some nefarious agent has stolen every single drink in the bar. Yes, everything. Help tide us over until we can find them. +bounty-description-hi-viz-vest = The clown stole the AME controller and won't back. It's pretty dark in here. Some hi-viz vests would make seeing each other in the dark a little mroe bearable. +bounty-description-torch = The chef made all the monkeys and kobolds at once, and they rebelled and took over the cargo shuttle. They're demanding supplies and free passage to a jungle planet, and we're giving in to their demands. All they need now is a few torches. +bounty-description-medkit-box = CentComm is putting on a play set in a hospital, and needs some props. Just send us some empty medkit boxes, and the show will go on! +bounty-description-cardobard-box = "The Cardborgs Cometh" is a new play premiering tomorrow, and the costuming team is woefully unprepared. Send us some boxes to work with. +bounty-description-wine = The new librarian and the Quartermaster are falling head over heels for each other after she caught him disassembling the bookshelves for wood. Send a couple bottles of wine (Or cans, if you must) to help make the date go well. +bounty-description-cotton-boll = A massive swarm of mothroaches ate all the paper and cloth on the station. Send us some cotton to help keep our winged crewmembers fed. +bounty-description-microwave-machine-board = Mr. Giggles thought it'd be funny to stick forks in all the kitchen microwaves. Help us replace them before the chefs start making clown burgers. diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl index fab815b4f9..2c8b326b07 100644 --- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl +++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl @@ -21,11 +21,11 @@ chat-manager-whisper-headset-on-message = You can't whisper on the radio! chat-manager-server-wrap-message = [bold]{$message}[/bold] chat-manager-sender-announcement-wrap-message = [font size=14][bold]{$sender} Announcement:[/font][font size=12] {$message}[/bold][/font] -chat-manager-entity-say-wrap-message = [BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]"[BubbleContent]{$message}[/BubbleContent]"[/font] -chat-manager-entity-say-bold-wrap-message = [BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]"[BubbleContent][bold]{$message}[/bold][/BubbleContent]"[/font] +chat-manager-entity-say-wrap-message = [BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] {$verb}, [font="{$fontType}" size={$fontSize}]"[color={$color}][BubbleContent]{$message}[/BubbleContent][/color]"[/font] +chat-manager-entity-say-bold-wrap-message = [BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] {$verb}, [font="{$fontType}" size={$fontSize}]"[color={$color}][BubbleContent][bold]{$message}[/bold][/BubbleContent][/color]"[/font] -chat-manager-entity-whisper-wrap-message = [font size=11][italic][BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] whispers,"[BubbleContent]{$message}[/BubbleContent]"[/italic][/font] -chat-manager-entity-whisper-unknown-wrap-message = [font size=11][italic][BubbleHeader]Someone[/BubbleHeader] whispers, "[BubbleContent]{$message}[/BubbleContent]"[/italic][/font] +chat-manager-entity-whisper-wrap-message = [font size=11][italic][BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] whispers,"[color={$color}][BubbleContent]{$message}[/BubbleContent][/color]"[/italic][/font] +chat-manager-entity-whisper-unknown-wrap-message = [font size=11][italic][BubbleHeader]Someone[/BubbleHeader] whispers, "[color={$color}][BubbleContent]{$message}[/BubbleContent][/color]"[/italic][/font] # THE() is not used here because the entity and its name can technically be disconnected if a nameOverride is passed... chat-manager-entity-me-wrap-message = [italic]{ PROPER($entity) -> diff --git a/Resources/Locale/en-US/door-remote/door-remote.ftl b/Resources/Locale/en-US/door-remote/door-remote.ftl index bf2fc11861..2c4ccd0805 100644 --- a/Resources/Locale/en-US/door-remote/door-remote.ftl +++ b/Resources/Locale/en-US/door-remote/door-remote.ftl @@ -1,3 +1,12 @@ +## UI +door-remote-open-close-text = Opens and Closes Doors +door-remote-toggle-bolt-text = Toggles Bolts +door-remote-emergency-access-text = Toggles Emergency Access +door-remote-invalid-text = Invalid +door-remote-mode-label = Mode: [color=white]{$modeString}[/color] + +## Entity + door-remote-switch-state-open-close = You switch the remote to open and close doors door-remote-switch-state-toggle-bolts = You switch the remote to toggle bolts door-remote-switch-state-toggle-emergency-access = You switch the remote to toggle emergency access diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-survival.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-survival.ftl index 231733eabf..0b8fa83ae8 100644 --- a/Resources/Locale/en-US/game-ticking/game-presets/preset-survival.ftl +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-survival.ftl @@ -1,2 +1,5 @@ survival-title = Survival survival-description = No internal threats, but how long can the station survive increasingly chaotic and frequent events? + +hellshift-title = Hellshift +hellshift-description = The station rolled a "one" in a luck check. Can the crew make it to the end? diff --git a/Resources/Locale/en-US/headset/headset-component.ftl b/Resources/Locale/en-US/headset/headset-component.ftl index a220737f18..75c83643d6 100644 --- a/Resources/Locale/en-US/headset/headset-component.ftl +++ b/Resources/Locale/en-US/headset/headset-component.ftl @@ -1,6 +1,6 @@ # Chat window radio wrap (prefix and postfix) -chat-radio-message-wrap = [color={$color}]{$channel} {$name} {$verb}, [font={$fontType} size={$fontSize}]"{$message}"[/font][/color] -chat-radio-message-wrap-bold = [color={$color}]{$channel} {$name} {$verb}, [font={$fontType} size={$fontSize}][bold]"{$message}"[/bold][/font][/color] +chat-radio-message-wrap = [color={$color}]{$channel} {$name} {$verb}, [font="{$fontType}" size={$fontSize}]"[/color][color={$languageColor}]{$message}[/color][color={$color}]"[/font][/color] +chat-radio-message-wrap-bold = [color={$color}]{$channel} {$name} {$verb}, [font="{$fontType}" size={$fontSize}][bold]"[/color][color={$languageColor}]{$message}[/color][color={$color}]"[/bold][/font][/color] examine-headset-default-channel = Use {$prefix} for the default channel ([color={$color}]{$channel}[/color]). diff --git a/Resources/Locale/en-US/language/translator.ftl b/Resources/Locale/en-US/language/translator.ftl index b2a1e9b2b8..8070d03be2 100644 --- a/Resources/Locale/en-US/language/translator.ftl +++ b/Resources/Locale/en-US/language/translator.ftl @@ -1,8 +1,13 @@ translator-component-shutoff = The {$translator} shuts off. translator-component-turnon = The {$translator} turns on. -translator-enabled = It appears to be active. -translator-disabled = It appears to be disabled. translator-implanter-refuse = The {$implanter} has no effect on {$target}. translator-implanter-success = The {$implanter} successfully injected {$target}. translator-implanter-ready = This implanter appears to be ready to use. translator-implanter-used = This implanter seems empty. + +translator-examined-langs-understood = It can translate from: [color=green]{$languages}[/color]. +translator-examined-langs-spoken = It can translate to: [color=green]{$languages}[/color]. +translator-examined-requires-any = It requires you to know at least one of these languages: [color=yellow]{$languages}[/color]. +translator-examined-requires-all = It requires you to know all of these languages: [color=yellow]{$languages}[/color]. +translator-examined-enabled = It appears to be [color=green]active[/color]. +translator-examined-disabled = It appears to be [color=red]turned off[/color]. diff --git a/Resources/Locale/en-US/npc/conversation/sophia.ftl b/Resources/Locale/en-US/npc/conversation/sophia.ftl deleted file mode 100644 index c832d9fc17..0000000000 --- a/Resources/Locale/en-US/npc/conversation/sophia.ftl +++ /dev/null @@ -1,82 +0,0 @@ -sophia-response-name = You may call me Sophia. -sophia-response-help = You may inquire about one of the following topics: {$availablePrompts}. - -sophia-response-hello-1 = Greetings. -sophia-response-hello-2 = Salutations. - -sophia-response-bye-1 = Fare thee well. -sophia-response-bye-2 = Gods be with you. -sophia-response-bye-3 = Come back wiser. - -sophia-idle-phrase-1 = Mmmm, another portent. -sophia-idle-phrase-2 = The noösphere is quite beautiful today. However, I don't think I could describe it in a way you could understand. -sophia-idle-phrase-3 = I've been here before. You have, too. - -sophia-response-attention-1 = What is it? -sophia-response-attention-2 = What do you seek? -sophia-response-attention-3 = Out with it. - -sophia-response-sorry-1 = That's not a question for me. -sophia-response-sorry-2 = Ask someone else. -sophia-response-sorry-3 = Maybe I know the answer, maybe I do not. Either way, I will not be answering that question. - -sophia-response-nature = My nature doesn't really matter, does it? I'm fulfilling my purpose. Can you say the same, or are you just wasting time? - -sophia-response-epi = 'Epistemics' is a word. Aspiring Hellenes they are, they wished to displace the Latin 'science.' However, in English, epistemics has undesired connotations as a study of knowledge itself, even though the Greek word is a literal replacement for 'science.' - -sophia-response-mantis = 'Mantis' means seer, soothsayer, or prophet. They must be so named because they seek to uncover the truth. And, fittingly with their psionic aptitude, 'mantis' and 'mind' both descend, to the best of our knowledge, from an absolutely ancient word that sounded something like 'mentis.' - -sophia-response-mystagogue = 'Mystagogue' literally means 'leader of the mystics.' You may know the suffix -gogue from 'demogogue.' - -sophia-response-oracle = Oracle? I don't know much about her, and she isn't keen to share her secrets with me. - -sophia-response-psionics = Psionics are extraordinary abilities originating from one's mind. There doesn't seem to be any dominant word to refer to someone with the ability to practice these, although I prefer 'psion' or 'psychic.' - -sophia-response-noosphere = The noösphere is a field connecting all of consciousness. It's the medium through which psionics works. Its strength and effects on the illusory world of the material are based on its pressure. Colloquially, noöspheric pressure is called 'glimmer.' - -sophia-response-god = 'God' is such a vague term. There are so many entities out there that have defeated mortality. How you choose to regard them is your business. - -sophia-response-morphotype = In the first century PCC, several entities reshaped men into their image. I had done the same, if you would believe it. I can offer no evidence of their existence, other than faint memories. Any specific morphotype you want to know about? - -sophia-response-calendar = It's currently 417 PCC. The casuality crisis neccesitated a new year to count from. Due to the nature of the crisis, it can only be said with certainty that 1 PCC is between 2400 and 2700 CE. - -sophia-response-crisis = The first FTL travel was incompatible with the old ways. Fortunately, its resolution made more apparent the inherent futility in trying to give one history, one narrative, one account. Truth cannot be found in the material world, only higher ones. - -sophia-response-metempsychosis = You've died thousands of times, and you'll die thousands more. Some of those lives you may dedicate to trying to stop the cycle. We all carry at least some memory of past lives, usually temporally recent ones. One of the great mysteries of the persistence of fragments is the high concentration of memories from the early 21st century CE, which, inverse to other periods, seem to be more common among the ignorant. - -sophia-response-truth = If you seek the truth, you're in the wrong place. From a perspective tainted by material reality, the best you can hope is to try and divine higher truths that are not dependent on it. - -sophia-response-job = I observe the glimmer here, and record it. - -sophia-response-human = Humans were the base for all the others. But they, too, were shaped. Long, long before the others. - -sophia-response-felinid = Felinids were the first, and the most willing. In true feline nature, they shaped themselves. - -sophia-response-oni = Oni, it is said, originated in Sirius. The brightest star in the night sky from Earth may have attracted some chromatically inclined entities, explaining their vivid coloring. But, that's just speculation. - -sophia-response-arachne = Arachne are the strangest of them. They're not fully mortal. They took the form of humans, but not their genes. Their creator wrote his name in their stead. - -sophia-response-moth = Moths scarecely look human, but, strangely, their genes confirm they are. Their creator shares his name with a genus of moths, and was responsible for the other outlier. - -sophia-response-lamiae = So, you remember? You must be remembering their mythological namesake. If you've really retained that fleeting memory over so many metempsychoses... Perhaps I've said too much. - -sophia-response-cyno = Were those... no... So faint. Ignorance! You cannot remember them! It's impossible! - -sophia-response-harpy = Harpies, it is said, were once men and women, sculpted by greed for a purpose long gone. They were abandoned by their creators on a world named Valerian 4b. - -sophia-response-valerian = The Harpy homeworld? Magestic mountains gleaming in white, forests of brilliant scarlet, oceans wine dark, yet no light to be seen by mortal eyes. The Harpies were made to thrive there. To them, their world was bathed in beautiful silver light. - -sophia-response-grue = You do not know of those. You cannot. I had so hoped to live a few cycles under normal causality. - -sophia-response-abraxas = That's a name of power, and I avoid speaking of him. He's the least content to rest, and the most infatuated with creating things from ignorance. - -sophia-response-zork = You wander into the slavering fangs of a hungry grue. There, did you enjoy this game? - -sophia-response-glimmer = The current glimmer reading is {$glimmer}. {$tier} - -glimmer-reading-minimal = That is extremely low. Nothing bad will happen, but I hope this is not at the cost of progression in your understanding of the universe. -glimmer-reading-low = That is quite low. Just barely enough to register any psionic activity here. -glimmer-reading-moderate = That is about the expected level on a psionically active station. You may notice manageable, minor effects. -glimmer-reading-high = That is sure to start attracting attention, although still quite manageable. -glimmer-reading-dangerous = That's a bit concerning. You may want to redirect efforts to reducing it. -glimmer-reading-critical = That's apocalyptic, in the original sense of the word. That is, to say, revealing. This is the sort of time and place to acquire secret knowledge. diff --git a/Resources/Locale/en-US/store/categories.ftl b/Resources/Locale/en-US/store/categories.ftl index 437fc03ae0..b6abc3e428 100644 --- a/Resources/Locale/en-US/store/categories.ftl +++ b/Resources/Locale/en-US/store/categories.ftl @@ -12,6 +12,7 @@ store-category-implants = Implants store-category-job = Job store-category-armor = Armor store-category-pointless = Pointless +store-category-deception = Deception # Revenant store-category-abilities = Abilities diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 4836a57d6b..592cf59d2f 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -109,6 +109,9 @@ uplink-holoclown-kit-desc = A joint venture between Cybersun and Honk.co. Contai uplink-holster-name = Shoulder Holster uplink-holster-desc = A deep shoulder holster capable of holding many types of ballistics. +uplink-chest-rig-name = Chest Rig +uplink-chest-rig-desc = Explosion-resistant tactical webbing used for holding traitor goods. + uplink-emag-name = Emag uplink-emag-desc = The business card of the syndicate, this sequencer is able to break open airlocks and tamper with a variety of station devices. Recharges automatically. diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index 80d98a1b3e..24f5f47127 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -63,3 +63,13 @@ trait-name-Thieving = Thieving trait-description-Thieving = You are deft with your hands, and talented at convincing people of their belongings. You can identify pocketed items, steal them quieter, and steal ~33% faster. + +trait-name-ForeignerLight = Foreigner (light) +trait-description-ForeignerLight = + You struggle to learn this station's primary language, and as such, cannot speak it. You can, however, comprehend what others say in that language. + To help you overcome this obstacle, you are equipped with a translator that helps you speak in this station's primary language. + +trait-name-Foreigner = Foreigner +trait-description-Foreigner = + For one reason or another you do not speak this station's primary language. + Instead, you have a translator issued to you that only you can use. diff --git a/Resources/Prototypes/Catalog/Bounties/bounties.yml b/Resources/Prototypes/Catalog/Bounties/bounties.yml index 62a3a4162f..c8c35aabfe 100644 --- a/Resources/Prototypes/Catalog/Bounties/bounties.yml +++ b/Resources/Prototypes/Catalog/Bounties/bounties.yml @@ -347,11 +347,11 @@ - type: cargoBounty id: BountyRadio - reward: 7500 + reward: 6500 description: bounty-description-radio entries: - name: bounty-item-radio - amount: 12 + amount: 7 whitelist: components: - Headset @@ -536,7 +536,7 @@ description: bounty-description-lasergun idPrefix: IV entries: - - name: bounty-lasergun + - name: bounty-item-lasergun amount: 6 whitelist: components: @@ -548,8 +548,185 @@ description: bounty-description-food idPrefix: UNTH entries: - - name: bounty-food + - name: bounty-item-food amount: 30 whitelist: tags: - Meat + +- type: cargoBounty + id: BountyFruit + reward: 5000 + description: bounty-description-fruit + entries: + - name: bounty-item-fruit + amount: 12 + whitelist: + tags: + - Fruit + +- type: cargoBounty + id: BountyVegetable + reward: 6000 + description: bounty-description-vegetable + entries: + - name: bounty-item-vegetable + amount: 14 + whitelist: + tags: + - Vegetable + +- type: cargoBounty + id: BountyChili + reward: 5555 + description: bounty-description-chili + entries: + - name: bounty-item-chili + amount: 3 + whitelist: + tags: + - ChiliBowl + +- type: cargoBounty + id: BountyRollerskates + reward: 6500 + description: bounty-description-rollerskates + entries: + - name: bounty-item-rollerskates + amount: 2 + whitelist: + components: + - Skates + +- type: cargoBounty + id: BountyBedsheet + reward: 4100 + description: bounty-description-bedsheet + entries: + - name: bounty-item-bedsheet + amount: 5 + whitelist: + tags: + - Bedsheet + +- type: cargoBounty + id: BountyBandana + reward: 4000 + description: bounty-description-bandana + entries: + - name: bounty-item-bandana + amount: 7 + whitelist: + tags: + - Bandana + +- type: cargoBounty + id: BountySteak + reward: 3200 + description: bounty-description-steak + entries: + - name: bounty-item-steak + amount: 4 + whitelist: + tags: + - Steak + +- type: cargoBounty + id: BountyBanana + reward: 6009 + description: bounty-description-banana + entries: + - name: bounty-item-banana + amount: 9 + whitelist: + tags: + - Banana + +- type: cargoBounty + id: BountyBeer + reward: 3100 + description: bounty-description-beer + entries: + - name: bounty-item-beer + amount: 6 + whitelist: + tags: + - Beer + +- type: cargoBounty + id: BountyHiVizVest + reward: 3030 + description: bounty-description-hi-viz-vest + entries: + - name: bounty-item-hi-viz-vest + amount: 3 + whitelist: + tags: + - HiViz + +- type: cargoBounty + id: BountyTorch + reward: 2220 + description: bounty-description-torch + entries: + - name: bounty-item-torch + amount: 6 + whitelist: + tags: + - Torch + +- type: cargoBounty + id: BountyMedkitBox + reward: 2300 + description: bounty-description-medkit-box + entries: + - name: bounty-item-medkit-box + amount: 4 + whitelist: + tags: + - Medkit + +- type: cargoBounty + id: BountyCardboardBox + reward: 1500 + description: bounty-description-cardobard-box + entries: + - name: bounty-item-cardboard-box + amount: 12 + whitelist: + tags: + - BoxCardboard + +- type: cargoBounty + id: BountyWine + reward: 3000 + description: bounty-description-wine + entries: + - name: bounty-item-wine + amount: 2 + whitelist: + tags: + - Wine + +- type: cargoBounty + id: BountyCottonBoll + reward: 8600 + description: bounty-description-cotton-boll + entries: + - name: bounty-item-cotton-boll + amount: 9 + whitelist: + tags: + - CottonBoll + +- type: cargoBounty + id: BountyMicrowaveMachineBoard + reward: 4000 + description: bounty-description-microwave-machine-board + entries: + - name: bounty-item-microwave-machine-board + amount: 2 + whitelist: + tags: + - MicrowaveMachineBoard + diff --git a/Resources/Prototypes/Catalog/Fills/Boxes/general.yml b/Resources/Prototypes/Catalog/Fills/Boxes/general.yml index 0c810a52f5..d6e2c45939 100644 --- a/Resources/Prototypes/Catalog/Fills/Boxes/general.yml +++ b/Resources/Prototypes/Catalog/Fills/Boxes/general.yml @@ -20,6 +20,9 @@ sound: /Audio/SimpleStation14/Items/Handling/cardboardbox_drop.ogg - type: EmitSoundOnLand sound: /Audio/SimpleStation14/Items/Handling/cardboardbox_drop.ogg + - type: Tag + tags: + - BoxCardboard - type: entity name: mousetrap box diff --git a/Resources/Prototypes/Catalog/Fills/Crates/fun.yml b/Resources/Prototypes/Catalog/Fills/Crates/fun.yml index cc5e3b1d17..26d0f47315 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/fun.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/fun.yml @@ -30,6 +30,7 @@ amount: 2 - id: PlushieArachind - id: PlushiePenguin + - id: PlushieArachne - type: entity id: CrateFunLizardPlushieBulk diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index a8ebd6e2ba..60fa6c15fd 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -265,16 +265,6 @@ tags: - NukeOpsUplink -- type: listing - id: UplinkSyndicateBombFake - name: uplink-exploding-syndicate-bomb-fake-name - description: uplink-exploding-syndicate-bomb-fake-desc - productEntity: SyndicateBombFake - cost: - Telecrystal: 4 - categories: - - UplinkExplosives - - type: listing id: UplinkClusterGrenade name: uplink-cluster-grenade-name @@ -787,7 +777,19 @@ cost: Telecrystal: 6 categories: - - UplinkBundles + - UplinkBundles + +- type: listing + id: UplinkSyndicateBombFake + name: uplink-exploding-syndicate-bomb-fake-name + description: uplink-exploding-syndicate-bomb-fake-desc + productEntity: SyndicateBombFake + cost: + Telecrystal: 4 + categories: + - UplinkDeception + +# Disruption - type: listing id: UplinkAmmoBundle @@ -1239,6 +1241,16 @@ # Armor +- type: listing + id: UplinkChestRig + name: uplink-chest-rig-name + description: uplink-chest-rig-desc + productEntity: ClothingBeltMilitaryWebbing + cost: + Telecrystal: 1 + categories: + - UplinkArmor + - type: listing id: UplinkChameleon name: uplink-chameleon-name diff --git a/Resources/Prototypes/Entities/Clothing/Head/bandanas.yml b/Resources/Prototypes/Entities/Clothing/Head/bandanas.yml index 8ee6479ee6..51a56f1f1d 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/bandanas.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/bandanas.yml @@ -20,6 +20,9 @@ - state: icon_mask map: [ "unfoldedLayer" ] visible: false + - type: Tag + tags: + - Bandana - type: entity parent: [ClothingHeadBandBase, ClothingMaskBandBlack] @@ -69,4 +72,4 @@ - type: entity parent: [ClothingHeadBandBase, ClothingMaskBandBrown] id: ClothingHeadBandBrown - name: brown bandana \ No newline at end of file + name: brown bandana diff --git a/Resources/Prototypes/Entities/Clothing/Masks/bandanas.yml b/Resources/Prototypes/Entities/Clothing/Masks/bandanas.yml index 2d65e67982..246b47b800 100644 --- a/Resources/Prototypes/Entities/Clothing/Masks/bandanas.yml +++ b/Resources/Prototypes/Entities/Clothing/Masks/bandanas.yml @@ -24,6 +24,7 @@ visible: false - type: Tag tags: + - Bandana - HidesNose - type: entity diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml index f49f5f4804..b867abfeed 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml @@ -63,6 +63,9 @@ sprite: Clothing/OuterClothing/Vests/hazard.rsi - type: Clothing sprite: Clothing/OuterClothing/Vests/hazard.rsi + - type: Tag + tags: + - HiViz #(Bartender) vest - type: entity diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml index 241fc45352..fdc49dc061 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml @@ -202,7 +202,7 @@ - type: FootstepModifier footstepSoundCollection: collection: FootstepSpurs - + - type: entity parent: ClothingShoesBootsCowboyBrown id: ClothingShoesBootsCowboyBlack @@ -212,7 +212,7 @@ sprite: Clothing/Shoes/Boots/cowboybootsblack.rsi - type: Clothing sprite: Clothing/Shoes/Boots/cowboybootsblack.rsi - + - type: entity parent: ClothingShoesBootsCowboyBrown id: ClothingShoesBootsCowboyWhite @@ -222,7 +222,7 @@ sprite: Clothing/Shoes/Boots/cowboybootswhite.rsi - type: Clothing sprite: Clothing/Shoes/Boots/cowboybootswhite.rsi - + - type: entity parent: ClothingShoesBootsCowboyBrown id: ClothingShoesBootsCowboyFancy @@ -231,4 +231,4 @@ - type: Sprite sprite: Clothing/Shoes/Boots/cowboybootsfancy.rsi - type: Clothing - sprite: Clothing/Shoes/Boots/cowboybootsfancy.rsi \ No newline at end of file + sprite: Clothing/Shoes/Boots/cowboybootsfancy.rsi diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/toy.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/toy.yml index eba1e300ed..d12b85cde4 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/toy.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/toy.yml @@ -36,6 +36,7 @@ - PlushieMoth - PlushieMothRandom # Nyanotrasen Random Moth Plushies - PlushieArachind + - PlushieArachne chance: 0.5 offset: 0.2 diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml index 06b0e6fad8..17e2a4df5e 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml @@ -375,6 +375,9 @@ - type: Icon sprite: Objects/Consumable/Drinks/beerglass.rsi state: icon + - type: Tag + tags: + - Beer - type: entity parent: DrinkGlass @@ -949,6 +952,9 @@ - type: Icon sprite: Objects/Consumable/Drinks/iced_beerglass.rsi state: icon + - type: Tag + tags: + - Beer - type: entity parent: DrinkGlass diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml index 07828ff5ba..face999df8 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml @@ -355,6 +355,9 @@ - type: Sprite sprite: Objects/Consumable/Drinks/pwinebottle.rsi - type: Sealable + - type: Tag + tags: + - Wine - type: entity parent: [DrinkBottleVisualsAll, DrinkBottleGlassBaseFull] @@ -501,6 +504,9 @@ - type: Sprite sprite: Objects/Consumable/Drinks/winebottle.rsi - type: Sealable + - type: Tag + tags: + - Wine # Small Bottles @@ -523,6 +529,9 @@ sprite: Objects/Consumable/Drinks/beer.rsi - type: Openable closeable: false + - type: Tag + tags: + - Beer - type: entity parent: [DrinkBottleVisualsAll, DrinkBottleGlassBaseFull] @@ -543,6 +552,9 @@ sprite: Objects/Consumable/Drinks/beer.rsi - type: Openable closeable: false + - type: Tag + tags: + - Beer - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml index b7e3aa3a48..192be68c8c 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml @@ -188,6 +188,9 @@ sprite: Objects/Consumable/Drinks/rootbeer.rsi - type: Item sprite: Objects/Consumable/Drinks/rootbeer.rsi + - type: Tag + tags: + - Beer - type: entity parent: DrinkCanBaseFull @@ -486,6 +489,9 @@ sprite: Objects/Consumable/Drinks/beer_can.rsi - type: Item sprite: Objects/Consumable/Drinks/beer_can.rsi + - type: Tag + tags: + - Beer - type: entity parent: DrinkCanBaseFull @@ -505,3 +511,6 @@ sprite: Objects/Consumable/Drinks/wine_can.rsi - type: Item sprite: Objects/Consumable/Drinks/wine_can.rsi + - type: Tag + tags: + - Wine diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml index fe88859584..8cd1c5dfab 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml @@ -156,6 +156,8 @@ path: /Audio/Weapons/Guns/Empty/empty.ogg ejectSound: path: /Audio/Weapons/Guns/Empty/empty.ogg + swap: false + disableEject: true - type: Tag tags: - Fruit diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml index 85de407988..58af9cf3bd 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/meat.yml @@ -718,6 +718,7 @@ tags: - Cooked - Meat + - Steak - type: Sprite layers: - state: plain-cooked @@ -774,6 +775,7 @@ tags: - Cooked - Meat + - Steak - type: Sprite layers: - state: product-cooked @@ -942,6 +944,7 @@ tags: - Cooked - Meat + - Steak - type: Sprite layers: - state: goliath-cooked @@ -971,6 +974,7 @@ tags: - Cooked - Meat + - Steak - type: Sprite layers: - state: rouny-cooked @@ -996,6 +1000,7 @@ tags: - Cooked - Meat + - Steak - type: Sprite layers: - state: lizard-cooked diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml index eec7711d4a..21eb0fb942 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml @@ -124,6 +124,9 @@ sprite: Objects/Specific/Hydroponics/laughin_pea.rsi - type: Produce seedId: laughinPea + - type: Tag + tags: + - Vegetable - type: entity name: tower-cap log @@ -258,6 +261,7 @@ - type: Tag tags: - Fruit + - Banana - type: entity name: mimana @@ -413,6 +417,7 @@ - type: Tag tags: - Carrot + - Vegetable - type: SolutionContainerManager solutions: food: @@ -458,6 +463,9 @@ sprite: Objects/Specific/Hydroponics/cabbage.rsi - type: Produce seedId: cabbage + - type: Tag + tags: + - Vegetable - type: entity name: garlic @@ -483,6 +491,9 @@ sprite: Objects/Specific/Hydroponics/garlic.rsi - type: Produce seedId: garlic + - type: Tag + tags: + - Vegetable - type: entity name: lemon @@ -658,6 +669,8 @@ - type: Tag tags: - Potato + - Vegetable + - type: entity name: tomato @@ -712,6 +725,7 @@ - type: Tag tags: - Fruit + - Vegetable - type: entity name: blue tomato @@ -756,6 +770,7 @@ - type: Tag tags: - Fruit + - Vegetable - type: entity name: blood tomato @@ -798,6 +813,7 @@ - type: Tag tags: - Fruit # Fuck you they're a fruit + - Vegetable - type: entity name: eggplant @@ -824,6 +840,7 @@ - type: Tag tags: - Fruit + - Vegetable - type: entity name: apple @@ -912,6 +929,7 @@ - type: Tag tags: - Corn + - Vegetable - type: Sprite sprite: Objects/Specific/Hydroponics/corn.rsi - type: Produce @@ -974,6 +992,9 @@ - type: SliceableFood count: 5 slice: FoodOnionSlice + - type: Tag + tags: + - Vegetable - type: entity name: red onion @@ -1002,6 +1023,9 @@ - type: SliceableFood count: 5 slice: FoodOnionRedSlice + - type: Tag + tags: + - Vegetable - type: entity name: chanterelle cluster @@ -1019,6 +1043,9 @@ sprite: Objects/Specific/Hydroponics/chanterelle.rsi - type: Produce seedId: chanterelle + - type: Tag + tags: + - Vegetable # Slices @@ -1129,6 +1156,9 @@ sprite: Objects/Specific/Hydroponics/chili.rsi - type: Produce seedId: chili + - type: Tag + tags: + - Vegetable - type: entity name: chilly pepper @@ -1180,6 +1210,9 @@ seedId: aloe - type: Extractable grindableSolutionName: food + - type: Tag + tags: + - Vegetable - type: entity name: poppy @@ -1459,6 +1492,9 @@ reagents: - ReagentId: MilkSoy Quantity: 5 + - type: Tag + tags: + - Vegetable - type: entity name: spaceman's trumpet @@ -1512,6 +1548,9 @@ reagents: - ReagentId: CarpoToxin Quantity: 2 + - type: Tag + tags: + - Vegetable - type: entity name: watermelon @@ -1604,9 +1643,6 @@ reagents: - ReagentId: JuiceWatermelon Quantity: 4 - - type: Tag - tags: - - Fruit - type: entity name: grapes @@ -1635,6 +1671,9 @@ reagents: - ReagentId: JuiceGrape Quantity: 10 + - type: Tag + tags: + - Fruit - type: entity name: berries @@ -1707,7 +1746,6 @@ tags: - Recyclable - Trash - - Fruit - type: SolutionContainerManager solutions: food: @@ -1746,6 +1784,9 @@ sprite: Objects/Specific/Hydroponics/pea.rsi - type: Produce seedId: pea + - type: Tag + tags: + - Vegetable - type: entity name: pumpkin @@ -1794,6 +1835,7 @@ - type: Tag tags: - Fruit + - Vegetable - type: entity name: cotton boll @@ -1822,3 +1864,4 @@ - type: Tag tags: - ClothMade + - CottonBoll diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml index 3b080e843b..6b96f3bcb3 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml @@ -806,6 +806,10 @@ Quantity: 4 - ReagentId: Allicin Quantity: 3 + - type: Tag + tags: + - ChiliBowl + - type: entity name: cold chili @@ -830,6 +834,9 @@ Quantity: 8 - ReagentId: Vitamin Quantity: 4 + - type: Tag + tags: + - ChiliBowl - type: entity name: chili con carnival @@ -860,6 +867,9 @@ Quantity: 4 - ReagentId: Allicin Quantity: 3 + - type: Tag + tags: + - ChiliBowl - type: entity name: monkey's delight diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml index c2a1fe6e7d..4cd8a85099 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml @@ -1175,9 +1175,9 @@ materialRequirements: Glass: 2 Cable: 2 - - type: ReverseEngineering # Nyano - recipes: - - MicrowaveMachineCircuitboard + - type: Tag + tags: + - MicrowaveMachineBoard - type: entity id: FatExtractorMachineCircuitboard @@ -1433,7 +1433,7 @@ materialRequirements: Steel: 5 CableHV: 5 - + - type: entity id: ShuttleGunPerforatorCircuitboard parent: BaseMachineCircuitboard @@ -1450,7 +1450,7 @@ materialRequirements: Steel: 10 CableHV: 5 - + - type: entity id: ShuttleGunFriendshipCircuitboard parent: BaseMachineCircuitboard @@ -1466,8 +1466,8 @@ Manipulator: 2 materialRequirements: Steel: 7 - CableHV: 5 - + CableHV: 5 + - type: entity id: ShuttleGunDusterCircuitboard parent: BaseMachineCircuitboard @@ -1485,7 +1485,7 @@ Steel: 10 CableHV: 5 Uranium: 2 - + - type: entity id: ShuttleGunKineticCircuitboard parent: BaseMachineCircuitboard @@ -1502,4 +1502,3 @@ materialRequirements: Steel: 5 CableHV: 2 - \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Devices/translators.yml b/Resources/Prototypes/Entities/Objects/Devices/translators.yml index 664626ea4b..b28541253d 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/translators.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/translators.yml @@ -1,5 +1,5 @@ - type: entity - abstract: true + noSpawn: true id: TranslatorUnpowered parent: BaseItem name: translator @@ -23,9 +23,14 @@ False: { visible: false } - type: HandheldTranslator enabled: false + - type: Clothing # To allow equipping translators on the neck slot + slots: [neck, pocket] + equipDelay: 0.3 + unequipDelay: 0.3 + quickEquip: false # Would conflict - type: entity - abstract: true + noSpawn: true id: Translator parent: [ TranslatorUnpowered, PowerCellSlotMediumItem ] suffix: Powered @@ -34,7 +39,7 @@ drawRate: 1 - type: entity - abstract: true + noSpawn: true id: TranslatorEmpty parent: Translator suffix: Empty @@ -44,6 +49,13 @@ cell_slot: name: power-cell-slot-component-slot-name-default +- type: entity + noSpawn: true + id: TranslatorForeigner + parent: [ Translator, PowerCellSlotHighItem ] + name: foreigner's translator + description: A special-issue translator that helps foreigner's speak and understand this station's primary language. + - type: entity id: CanilunztTranslator diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index 67c6e1194b..9ced553941 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -750,6 +750,34 @@ sound: path: /Audio/Voice/Human/malescream_5.ogg +- type: entity + parent: BasePlushie + id: PlushieArachne + name: Arachne plushie + description: A plushie of an Arachne, a half human, half spider creature. Why does it look familiar? + components: + - type: Sprite + state: plushie_arachne + - type: EmitSoundOnUse + sound: + path: /Audio/Voice/Human/womanlaugh.ogg + - type: EmitSoundOnLand + sound: + path: /Audio/Voice/Human/female_sigh.ogg + - type: EmitSoundOnActivate + sound: + path: /Audio/Voice/Human/womanlaugh.ogg + - type: Food + requiresSpecialDigestion: true + useSound: + path: /Audio/Voice/Human/femalescream_4.ogg + - type: MeleeWeapon + soundHit: + path: /Audio/Voice/Human/femalescream_2.ogg + - type: EmitSoundOnTrigger + sound: + path: /Audio/Voice/Human/femalescream_5.ogg + - type: entity parent: BasePlushie id: PlushieMoth @@ -1432,6 +1460,9 @@ reagents: - ReagentId: Nothing Quantity: 100 + - type: Tag + tags: + - Banana - type: entity parent: DrinkBase diff --git a/Resources/Prototypes/Entities/Objects/Misc/bedsheets.yml b/Resources/Prototypes/Entities/Objects/Misc/bedsheets.yml index a4f1e5e687..02ee00a07f 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/bedsheets.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/bedsheets.yml @@ -20,6 +20,9 @@ - neck - type: StaticPrice price: 100 + - type: Tag + tags: + - Bedsheet - type: entity id: BedsheetBlack diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Bombs/funny.yml b/Resources/Prototypes/Entities/Objects/Weapons/Bombs/funny.yml index 4aff7363a4..630354f23d 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Bombs/funny.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Bombs/funny.yml @@ -117,3 +117,6 @@ - id: TrashBananaPeelExplosive sound: path: /Audio/Effects/unwrap.ogg + - type: Tag + tags: + - Banana diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index b0c4047ccd..4e87776e9f 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -157,6 +157,7 @@ - APCElectronics - SMESMachineCircuitboard - SubstationMachineCircuitboard + - WallmountSubstationElectronics - CellRechargerCircuitboard - BorgChargerCircuitboard - WeaponCapacitorRechargerCircuitboard diff --git a/Resources/Prototypes/Entities/Structures/Power/substation.yml b/Resources/Prototypes/Entities/Structures/Power/substation.yml index 94de12be18..04c83bd991 100644 --- a/Resources/Prototypes/Entities/Structures/Power/substation.yml +++ b/Resources/Prototypes/Entities/Structures/Power/substation.yml @@ -244,6 +244,16 @@ - type: Battery maxCharge: 2000000 startingCharge: 2000000 + - type: ContainerFill + containers: + board: [ WallmountSubstationElectronics ] + capacitor: [ CapacitorStockPart ] + powercell: [ PowerCellSmall ] + - type: ContainerContainer + containers: + board: !type:Container + capacitor: !type:Container + powercell: !type:Container # Construction Frame - type: entity diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index 9ed1889154..8e518ab2f1 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -132,6 +132,16 @@ components: - type: RampingStationEventScheduler +- type: entity + id: HellshiftStationEventScheduler + parent: BaseGameRule + noSpawn: true + components: + - type: RampingStationEventScheduler + chaosModifier: 4 # By default, one event each 30-10 seconds after two hours. Changing CVars will cause this to deviate. + startingChaosRatio: 0.025 # Starts as slow as survival, but quickly ramps up + shiftLengthModifier: 2.5 + # variation passes - type: entity id: BasicRoundstartVariation diff --git a/Resources/Prototypes/Language/languages.yml b/Resources/Prototypes/Language/languages.yml index 1a874612c2..048fdc6f24 100644 --- a/Resources/Prototypes/Language/languages.yml +++ b/Resources/Prototypes/Language/languages.yml @@ -38,6 +38,8 @@ # Spoken by slimes. - type: language id: Bubblish + color: "#0077aa" + fontId: RubikBubbles obfuscation: !type:SyllableObfuscation minSyllables: 1 @@ -52,6 +54,8 @@ # Spoken by moths. - type: language id: Moffic + color: "#869b29" + fontId: Copperplate obfuscation: !type:SyllableObfuscation minSyllables: 2 # Replacements are really short @@ -118,6 +122,8 @@ # Spoken by dionas. - type: language id: RootSpeak + color: "#804000" + fontId: Noganas obfuscation: !type:SyllableObfuscation minSyllables: 1 @@ -132,6 +138,8 @@ # A mess of broken Japanese, spoken by Felinds and Oni - type: language id: Nekomimetic + color: "#803B56" + fontId: Manga obfuscation: !type:SyllableObfuscation minSyllables: 1 @@ -189,6 +197,7 @@ # Spoken by the Lizard race. - type: language id: Draconic + color: "#228b22" obfuscation: !type:SyllableObfuscation minSyllables: 2 @@ -282,6 +291,7 @@ # Spoken by the Vulpkanin race. - type: language id: Canilunzt + color: "#b97a57" obfuscation: !type:SyllableObfuscation minSyllables: 1 @@ -314,7 +324,6 @@ - vor - nic - gro - # - lll - enem - zandt - tzch @@ -349,6 +358,7 @@ # The common language of the Sol system. - type: language id: SolCommon + color: "#8282fb" obfuscation: !type:SyllableObfuscation minSyllables: 1 @@ -374,6 +384,7 @@ - type: language id: RobotTalk + fontId: Monospace obfuscation: !type:SyllableObfuscation minSyllables: 1 diff --git a/Resources/Prototypes/Nyanotrasen/Catalog/Cargo/cargo_epistemics.yml b/Resources/Prototypes/Nyanotrasen/Catalog/Cargo/cargo_epistemics.yml index 4be21cec30..d7d0b66538 100644 --- a/Resources/Prototypes/Nyanotrasen/Catalog/Cargo/cargo_epistemics.yml +++ b/Resources/Prototypes/Nyanotrasen/Catalog/Cargo/cargo_epistemics.yml @@ -4,6 +4,6 @@ sprite: Nyanotrasen/Objects/Consumable/Drinks/flaskholywater.rsi state: icon product: CrateHolyWaterKit - cost: 1000 - category: Service + cost: 3000 + category: Epistemics group: market diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/glimmer_prober.yml b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/glimmer_prober.yml index eca5b5e375..161c5b4ef5 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/glimmer_prober.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/glimmer_prober.yml @@ -11,6 +11,7 @@ psychicFeedback: - "prober-feedback" - type: GlimmerSource + researchPointGeneration: 20 - type: Construction graph: GlimmerDevices node: glimmerProber diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml index 5213608d95..8e34a07ea5 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml @@ -1,7 +1,7 @@ - type: entity parent: BaseStructure id: SophicScribe - name: Sophie + name: sophie description: Latest reports on the Noösphere! components: - type: Sprite @@ -27,10 +27,6 @@ channels: - Common - Science - - type: ActiveListener - - type: TypingIndicator - - type: NPCConversation - tree: SophiaTree - type: PotentialPsionic #this makes her easier to access for glimmer events, dw about it - type: Psionic psychicFeedback: @@ -43,191 +39,3 @@ - type: GuideHelp guides: - Psionics - -- type: npcConversationTree - id: SophiaTree - dialogue: - - prompts: [glimmer, reading] - responses: - - is: !type:NPCConversationGetGlimmerEvent - text: sophia-response-glimmer - - - prompts: [purpose, job, occupation, profession] - weight: 0.5 - responses: - - text: sophia-response-job - - - prompts: [help, topics] - weight: 0.5 - hidden: true - responses: - - is: !type:NPCConversationHelpEvent - text: sophia-response-help - - - prompts: [nature, statue, snake, being] - weight: 0.3 - responses: - - text: sophia-response-nature - - - prompts: [epistemics, epi] - weight: 0.2 - responses: - - text: sophia-response-epi - - - prompts: [mantis] - weight: 0.2 - responses: - - text: sophia-response-mantis - - - prompts: [mystagogue, mysta] - weight: 0.2 - responses: - - text: sophia-response-mystagogue - - - prompts: [psionics, psychic] - weight: 0.2 - responses: - - text: sophia-response-psionics - - - prompts: [noösphere, noosphere] - weight: 0.2 - responses: - - text: sophia-response-noosphere - - - prompts: [metempsychosis, metempsychoses, reincarnation, death, dying, afterlife] - weight: 0.2 - responses: - - text: sophia-response-metempsychosis - - - prompts: [calendar] - weight: 0.2 - responses: - - text: sophia-response-calendar - - - prompts: [morphotypes, morphotype, species] - weight: 0.2 - responses: - - text: sophia-response-morphotype - - - prompts: [gods, god] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-god - - - prompts: [truth, "true", "false", falsity, falsehood] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-truth - - - prompts: [human, humans, humanoid, unmutated] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-human - - - prompts: [felinid, felinids, felid, felids, catperson, catpeople] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-felinid - - - prompts: [oni, onis] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-oni - - - prompts: [arachne, arachnid, arachnids, spiderperson, spiderpeople] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-arachne - - - prompts: [moth, moths, moff, moths] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-moth - - - prompts: [lamiae, lamia, lamias] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-lamiae - - - prompts: [grue, grues, batperson, batpeople] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-grue - - - prompts: [cynocephalus, cynocephali, cyno, cynos] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-cyno - - - prompts: [harpy, harpies] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-harpy - - - prompts: [valerian, Valerian, 4b] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-valerian - - - prompts: [crisis, causality] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-crisis - - - prompts: [oracle] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-oracle - - - prompts: [abraxas] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-abraxas - - - prompts: [hi, hello, hey, greetings, salutations] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-hello-1 - - text: sophia-response-hello-2 - - - prompts: [bye, goodbye, done, farewell, later, seeya] - weight: 0.1 - hidden: true - responses: - - text: sophia-response-bye-1 - event: !type:NPCConversationByeEvent - - text: sophia-response-bye-2 - event: !type:NPCConversationByeEvent - - text: sophia-response-bye-3 - event: !type:NPCConversationByeEvent - - attention: - - text: sophia-response-attention-1 - - text: sophia-response-attention-2 - - text: sophia-response-attention-3 - - idle: - - text: sophia-idle-phrase-1 - - text: sophia-idle-phrase-2 - - text: sophia-idle-phrase-3 - - unknown: - - text: sophia-response-sorry-1 - - text: sophia-response-sorry-2 - - text: sophia-response-sorry-3 diff --git a/Resources/Prototypes/Objectives/ninja.yml b/Resources/Prototypes/Objectives/ninja.yml index 43def65d7a..f2ac97be58 100644 --- a/Resources/Prototypes/Objectives/ninja.yml +++ b/Resources/Prototypes/Objectives/ninja.yml @@ -39,8 +39,8 @@ sprite: Structures/Machines/server.rsi state: server - type: NumberObjective - min: 5 - max: 10 + min: 9 + max: 13 title: objective-condition-steal-research-title - type: StealResearchCondition diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/wallmount_substation.yml b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/wallmount_substation.yml index 381871f94a..7e4087b20a 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/wallmount_substation.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/wallmount_substation.yml @@ -26,7 +26,13 @@ steps: - material: Cable amount: 5 - doAfter: 2 + doAfter: 0.5 + - material: CableMV + amount: 5 + doAfter: 0.5 + - material: CableHV + amount: 5 + doAfter: 0.5 - tool: Screwing doAfter: 2 @@ -41,12 +47,34 @@ icon: sprite: "Objects/Misc/module.rsi" state: "charger_APC" - doAfter: 1 + doAfter: 0.5 + - anyTags: + - PowerCell + - PowerCellSmall + store: powercell + name: a powercell + icon: + sprite: "Objects/Power/power_cells.rsi" + state: "medium" + doAfter: 0.5 + - tag: CapacitorStockPart + name: a capacitor + store: capacitor + icon: + sprite: "Objects/Misc/stock_parts.rsi" + state: "capacitor" + doAfter: 0.5 - to: frame completed: - !type:GivePrototype prototype: CableApcStack1 amount: 5 + - !type:GivePrototype + prototype: CableMVStack1 + amount: 5 + - !type:GivePrototype + prototype: CableHVStack1 + amount: 5 steps: - tool: Cutting doAfter: 1 diff --git a/Resources/Prototypes/Recipes/Lathes/electronics.yml b/Resources/Prototypes/Recipes/Lathes/electronics.yml index f0c59a0bdf..b6184272dc 100644 --- a/Resources/Prototypes/Recipes/Lathes/electronics.yml +++ b/Resources/Prototypes/Recipes/Lathes/electronics.yml @@ -604,7 +604,7 @@ completetime: 4 materials: Steel: 50 - Glass: 350 + Glass: 450 - type: latheRecipe id: SMESMachineCircuitboard diff --git a/Resources/Prototypes/Store/categories.yml b/Resources/Prototypes/Store/categories.yml index c16972c8a3..11f8d509af 100644 --- a/Resources/Prototypes/Store/categories.yml +++ b/Resources/Prototypes/Store/categories.yml @@ -63,6 +63,11 @@ name: store-category-pointless priority: 9 +- type: storeCategory + id: UplinkDeception + name: store-category-deception + priority: 10 + #revenant - type: storeCategory id: RevenantAbilities diff --git a/Resources/Prototypes/Traits/inconveniences.yml b/Resources/Prototypes/Traits/inconveniences.yml index dcf53d9ab7..8dc0264ffe 100644 --- a/Resources/Prototypes/Traits/inconveniences.yml +++ b/Resources/Prototypes/Traits/inconveniences.yml @@ -26,3 +26,26 @@ fourRandomProb: 0 threeRandomProb: 0 cutRandomProb: 0 + +- type: trait + id: ForeignerLight + category: Mental + points: 1 + requirements: + - !type:TraitGroupExclusionRequirement + prototypes: [ Foreigner ] + components: + - type: ForeignerTrait + cantUnderstand: false # Allows to understand + baseTranslator: TranslatorForeigner + +- type: trait + id: Foreigner + category: Mental + points: 2 + requirements: # TODO: Add a requirement to know at least 1 non-gc language + - !type:TraitGroupExclusionRequirement + prototypes: [ ForeignerLight ] + components: + - type: ForeignerTrait + baseTranslator: TranslatorForeigner diff --git a/Resources/Prototypes/fonts.yml b/Resources/Prototypes/fonts.yml index 03102cd341..92c2947258 100644 --- a/Resources/Prototypes/fonts.yml +++ b/Resources/Prototypes/fonts.yml @@ -45,3 +45,19 @@ - type: font id: Emoji path: /Fonts/NotoEmoji.ttf + +- type: font + id: RubikBubbles + path: /Fonts/RubikBubbles.ttf + +- type: font + id: Copperplate + path: /Fonts/Copperplate.otf + +- type: font + id: Manga + path: /Fonts/Mangat.ttf + +- type: font + id: Noganas + path: /Fonts/Noganas.ttf diff --git a/Resources/Prototypes/game_presets.yml b/Resources/Prototypes/game_presets.yml index e99b51f82c..7d7169bf10 100644 --- a/Resources/Prototypes/game_presets.yml +++ b/Resources/Prototypes/game_presets.yml @@ -9,6 +9,17 @@ - RampingStationEventScheduler - BasicRoundstartVariation +- type: gamePreset + id: SurvivalHellshift + alias: + - hellshift + showInVote: true + name: hellshift-title + description: hellshift-description + rules: + - HellshiftStationEventScheduler + - BasicRoundstartVariation + - type: gamePreset id: AllAtOnce name: all-at-once-title @@ -90,7 +101,7 @@ - traitor name: traitor-title description: traitor-description - showInVote: false + showInVote: true rules: - Traitor - SubGamemodesRule diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index b7fa0fa2b6..e48fa2f2f2 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -34,18 +34,30 @@ - type: Tag id: Balloon +- type: Tag + id: Banana + - type: Tag id: BananaPeel +- type: Tag + id: Bandana + - type: Tag id: BaseballBat - type: Tag id: BBQsauce +- type: Tag + id: Bedsheet + - type: Tag id: Bee +- type: Tag + id: Beer + - type: Tag id: BikeHorn @@ -205,6 +217,9 @@ - type: Tag id: Bottle +- type: Tag + id: BoxCardboard + - type: Tag id: BoxHug @@ -350,6 +365,9 @@ - type: Tag id: ChemDispensable # container that can go into the chem dispenser +- type: Tag + id: ChiliBowl + - type: Tag id: Cigarette @@ -386,11 +404,14 @@ - type: Tag id: CluwneHorn -- type: Tag #Ohioans die happy +- type: Tag + id: Coldsauce + +- type: Tag id: Corn - type: Tag - id: Coldsauce + id: CottonBoll - type: Tag id: Cow @@ -683,6 +704,9 @@ - type: Tag id: HighSecDoor +- type: Tag + id: HiViz + - type: Tag id: Hoe @@ -770,9 +794,6 @@ - type: Tag id: MacroBomb -- type: Tag - id: MicroBomb - - type: Tag id: MimeBelt @@ -853,6 +874,12 @@ - type: Tag id: Metal +- type: Tag + id: MicroBomb + +- type: Tag + id: MicrowaveMachineBoard + - type: Tag id: MindShield @@ -1151,6 +1178,9 @@ - type: Tag id: StationMapElectronics +- type: Tag + id: Steak + - type: Tag id: SubdermalImplant @@ -1220,6 +1250,9 @@ - type: Tag id: UraniumGlassShard +- type: Tag + id: Vegetable + - type: Tag id: VimPilot @@ -1259,6 +1292,9 @@ - type: Tag id: Window +- type: Tag + id: Wine + - type: Tag id: WizardWand # that evil vvizard vvand diff --git a/Resources/Textures/Objects/Fun/toys.rsi/meta.json b/Resources/Textures/Objects/Fun/toys.rsi/meta.json index fc92a47936..cae7880e41 100644 --- a/Resources/Textures/Objects/Fun/toys.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/toys.rsi/meta.json @@ -104,6 +104,9 @@ { "name": "plushie_diona" }, + { + "name": "plushie_arachne" + }, { "name": "plushie_human" }, diff --git a/Resources/Textures/Objects/Fun/toys.rsi/plushie_arachne.png b/Resources/Textures/Objects/Fun/toys.rsi/plushie_arachne.png new file mode 100644 index 0000000000..4f72c31ddf Binary files /dev/null and b/Resources/Textures/Objects/Fun/toys.rsi/plushie_arachne.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/base.png b/Resources/Textures/Structures/Power/apc.rsi/base.png index 2b42c54935..ea2f17c062 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/base.png and b/Resources/Textures/Structures/Power/apc.rsi/base.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/broken.png b/Resources/Textures/Structures/Power/apc.rsi/broken.png index 092694c371..4bcd2475cd 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/broken.png and b/Resources/Textures/Structures/Power/apc.rsi/broken.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/channel0-auto_off.png b/Resources/Textures/Structures/Power/apc.rsi/channel0-auto_off.png index ba84b7d6db..0d181878fc 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/channel0-auto_off.png and b/Resources/Textures/Structures/Power/apc.rsi/channel0-auto_off.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/channel0-auto_on.png b/Resources/Textures/Structures/Power/apc.rsi/channel0-auto_on.png index 6cce72f795..c7a0d87ccc 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/channel0-auto_on.png and b/Resources/Textures/Structures/Power/apc.rsi/channel0-auto_on.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/channel0-manual_off.png b/Resources/Textures/Structures/Power/apc.rsi/channel0-manual_off.png index 61ae057d50..4be0bbfb0b 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/channel0-manual_off.png and b/Resources/Textures/Structures/Power/apc.rsi/channel0-manual_off.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/channel0-manual_on.png b/Resources/Textures/Structures/Power/apc.rsi/channel0-manual_on.png index 6cc48bd1a5..720dc4976a 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/channel0-manual_on.png and b/Resources/Textures/Structures/Power/apc.rsi/channel0-manual_on.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/channel1-auto_off.png b/Resources/Textures/Structures/Power/apc.rsi/channel1-auto_off.png index b31bed6c6b..7f0a13a7a4 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/channel1-auto_off.png and b/Resources/Textures/Structures/Power/apc.rsi/channel1-auto_off.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/channel1-auto_on.png b/Resources/Textures/Structures/Power/apc.rsi/channel1-auto_on.png index 959a05cde0..87191be520 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/channel1-auto_on.png and b/Resources/Textures/Structures/Power/apc.rsi/channel1-auto_on.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/channel1-manual_off.png b/Resources/Textures/Structures/Power/apc.rsi/channel1-manual_off.png index 3bb505ecb8..031213f74a 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/channel1-manual_off.png and b/Resources/Textures/Structures/Power/apc.rsi/channel1-manual_off.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/channel1-manual_on.png b/Resources/Textures/Structures/Power/apc.rsi/channel1-manual_on.png index bd8b013310..ac5f1c1bef 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/channel1-manual_on.png and b/Resources/Textures/Structures/Power/apc.rsi/channel1-manual_on.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/channel2-auto_off.png b/Resources/Textures/Structures/Power/apc.rsi/channel2-auto_off.png index 0cca860220..ff8522d2c5 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/channel2-auto_off.png and b/Resources/Textures/Structures/Power/apc.rsi/channel2-auto_off.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/channel2-auto_on.png b/Resources/Textures/Structures/Power/apc.rsi/channel2-auto_on.png index 56802dc043..4e04387ed3 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/channel2-auto_on.png and b/Resources/Textures/Structures/Power/apc.rsi/channel2-auto_on.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/channel2-manual_off.png b/Resources/Textures/Structures/Power/apc.rsi/channel2-manual_off.png index 5ac134cb54..2f9802281e 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/channel2-manual_off.png and b/Resources/Textures/Structures/Power/apc.rsi/channel2-manual_off.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/channel2-manual_on.png b/Resources/Textures/Structures/Power/apc.rsi/channel2-manual_on.png index 5ebec4feee..fb9e2180bb 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/channel2-manual_on.png and b/Resources/Textures/Structures/Power/apc.rsi/channel2-manual_on.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/display-charging.png b/Resources/Textures/Structures/Power/apc.rsi/display-charging.png index 67e3b3df19..d5e356d943 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/display-charging.png and b/Resources/Textures/Structures/Power/apc.rsi/display-charging.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/display-full.png b/Resources/Textures/Structures/Power/apc.rsi/display-full.png index 1739853e2b..4a9bd7ed64 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/display-full.png and b/Resources/Textures/Structures/Power/apc.rsi/display-full.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/display-lack.png b/Resources/Textures/Structures/Power/apc.rsi/display-lack.png index c4c103eeb9..d1d2df4858 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/display-lack.png and b/Resources/Textures/Structures/Power/apc.rsi/display-lack.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/display-remote.png b/Resources/Textures/Structures/Power/apc.rsi/display-remote.png index be343a987b..07265ab67d 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/display-remote.png and b/Resources/Textures/Structures/Power/apc.rsi/display-remote.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/emag-unlit.png b/Resources/Textures/Structures/Power/apc.rsi/emag-unlit.png index 2713c5506a..77c2518e63 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/emag-unlit.png and b/Resources/Textures/Structures/Power/apc.rsi/emag-unlit.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/frame.png b/Resources/Textures/Structures/Power/apc.rsi/frame.png index 7d3529f63f..e4c9c1b31c 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/frame.png and b/Resources/Textures/Structures/Power/apc.rsi/frame.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/meta.json b/Resources/Textures/Structures/Power/apc.rsi/meta.json index 82f78ccb87..867e5ce0ff 100644 --- a/Resources/Textures/Structures/Power/apc.rsi/meta.json +++ b/Resources/Textures/Structures/Power/apc.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/9c7d509354ee030300f63c701da63c17928c3b3b and modified by Swept", + "copyright": "Taken from TauCetiClassic at commit https://github.com/TauCetiStation/TauCetiClassic/commit/fca7f2a0f9154c0c6c4efc2f8f58441fb9b34759", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Structures/Power/apc.rsi/sparks-unlit.png b/Resources/Textures/Structures/Power/apc.rsi/sparks-unlit.png index 212fcb78bf..f442757bea 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/sparks-unlit.png and b/Resources/Textures/Structures/Power/apc.rsi/sparks-unlit.png differ diff --git a/Resources/Textures/Structures/Power/apc.rsi/static.png b/Resources/Textures/Structures/Power/apc.rsi/static.png index 43089d0d81..a7137ec2f3 100644 Binary files a/Resources/Textures/Structures/Power/apc.rsi/static.png and b/Resources/Textures/Structures/Power/apc.rsi/static.png differ diff --git a/Resources/Textures/Structures/Power/power.rsi/meta.json b/Resources/Textures/Structures/Power/power.rsi/meta.json index 773a2b5a3e..027655dd35 100644 --- a/Resources/Textures/Structures/Power/power.rsi/meta.json +++ b/Resources/Textures/Structures/Power/power.rsi/meta.json @@ -5,7 +5,7 @@ "y": 32 }, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation", + "copyright": "Taken from tgstation/paradise", "states": [ { "name": "eightdirwire" diff --git a/Resources/Textures/Structures/Power/power.rsi/storage.png b/Resources/Textures/Structures/Power/power.rsi/storage.png index 5f42ce89bc..71e7cf9195 100644 Binary files a/Resources/Textures/Structures/Power/power.rsi/storage.png and b/Resources/Textures/Structures/Power/power.rsi/storage.png differ diff --git a/Resources/Textures/Structures/Power/smes.rsi/meta.json b/Resources/Textures/Structures/Power/smes.rsi/meta.json index 2ca8d1fb22..4b4193ec72 100644 --- a/Resources/Textures/Structures/Power/smes.rsi/meta.json +++ b/Resources/Textures/Structures/Power/smes.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/9c7d509354ee030300f63c701da63c17928c3b3b and modified by Swept", + "copyright": "Taken from Paradise at https://github.com/McRamon/Paradise/blob/b727162ed9dead12de42be5c5b04872934a474e1/icons/obj/power.dmi", "size": { "x": 32, "y": 32 @@ -9,33 +9,9 @@ "states": [ { "name": "smes" - }, - { - "name": "static" - }, - { - "name": "smes-open" - }, - { - "name": "smes-oc0" }, { - "name": "smes-oc1", - "delays": [ - [ - 0.5, - 0.5 - ] - ] - }, - { - "name": "smes-oc2", - "delays": [ - [ - 0.5, - 0.5 - ] - ] + "name": "static" }, { "name": "smes-og1", @@ -62,16 +38,50 @@ "name": "smes-op0" }, { - "name": "smes-op1", + "name": "smes-op1" + }, + { + "name": "smes-op2" + }, + { + "name": "smes-oc1", "delays": [ [ - 1, - 1 + 0.3, + 0.3 ] ] }, { - "name": "smes-op2" + "name": "smes-oc2", + "delays": [ + [ + 0.3, + 0.3 + ] + ] + }, + { + "name": "smes-oc0", + "delays": [ + [ + 0.5, + 0.3 + ] + ] + }, + { + "name": "smes-open", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] } ] } diff --git a/Resources/Textures/Structures/Power/smes.rsi/smes-oc0.png b/Resources/Textures/Structures/Power/smes.rsi/smes-oc0.png index 5579432d3f..9d62074f1d 100644 Binary files a/Resources/Textures/Structures/Power/smes.rsi/smes-oc0.png and b/Resources/Textures/Structures/Power/smes.rsi/smes-oc0.png differ diff --git a/Resources/Textures/Structures/Power/smes.rsi/smes-oc1.png b/Resources/Textures/Structures/Power/smes.rsi/smes-oc1.png index d624672613..10f4c4678e 100644 Binary files a/Resources/Textures/Structures/Power/smes.rsi/smes-oc1.png and b/Resources/Textures/Structures/Power/smes.rsi/smes-oc1.png differ diff --git a/Resources/Textures/Structures/Power/smes.rsi/smes-oc2.png b/Resources/Textures/Structures/Power/smes.rsi/smes-oc2.png index d624672613..10f4c4678e 100644 Binary files a/Resources/Textures/Structures/Power/smes.rsi/smes-oc2.png and b/Resources/Textures/Structures/Power/smes.rsi/smes-oc2.png differ diff --git a/Resources/Textures/Structures/Power/smes.rsi/smes-og1.png b/Resources/Textures/Structures/Power/smes.rsi/smes-og1.png index c65f011a07..472b013ce7 100644 Binary files a/Resources/Textures/Structures/Power/smes.rsi/smes-og1.png and b/Resources/Textures/Structures/Power/smes.rsi/smes-og1.png differ diff --git a/Resources/Textures/Structures/Power/smes.rsi/smes-og2.png b/Resources/Textures/Structures/Power/smes.rsi/smes-og2.png index 0023750405..c2d3dbbc46 100644 Binary files a/Resources/Textures/Structures/Power/smes.rsi/smes-og2.png and b/Resources/Textures/Structures/Power/smes.rsi/smes-og2.png differ diff --git a/Resources/Textures/Structures/Power/smes.rsi/smes-og3.png b/Resources/Textures/Structures/Power/smes.rsi/smes-og3.png index 51df688e4a..263d8e98f7 100644 Binary files a/Resources/Textures/Structures/Power/smes.rsi/smes-og3.png and b/Resources/Textures/Structures/Power/smes.rsi/smes-og3.png differ diff --git a/Resources/Textures/Structures/Power/smes.rsi/smes-og4.png b/Resources/Textures/Structures/Power/smes.rsi/smes-og4.png index ceb3db1bc3..e31794f5c7 100644 Binary files a/Resources/Textures/Structures/Power/smes.rsi/smes-og4.png and b/Resources/Textures/Structures/Power/smes.rsi/smes-og4.png differ diff --git a/Resources/Textures/Structures/Power/smes.rsi/smes-og5.png b/Resources/Textures/Structures/Power/smes.rsi/smes-og5.png index e6c0c8a2ea..26955f70a1 100644 Binary files a/Resources/Textures/Structures/Power/smes.rsi/smes-og5.png and b/Resources/Textures/Structures/Power/smes.rsi/smes-og5.png differ diff --git a/Resources/Textures/Structures/Power/smes.rsi/smes-op0.png b/Resources/Textures/Structures/Power/smes.rsi/smes-op0.png index 573b5c404f..ab0ae9e114 100644 Binary files a/Resources/Textures/Structures/Power/smes.rsi/smes-op0.png and b/Resources/Textures/Structures/Power/smes.rsi/smes-op0.png differ diff --git a/Resources/Textures/Structures/Power/smes.rsi/smes-op1.png b/Resources/Textures/Structures/Power/smes.rsi/smes-op1.png index f5c8e10489..58d8dbf1f5 100644 Binary files a/Resources/Textures/Structures/Power/smes.rsi/smes-op1.png and b/Resources/Textures/Structures/Power/smes.rsi/smes-op1.png differ diff --git a/Resources/Textures/Structures/Power/smes.rsi/smes-op2.png b/Resources/Textures/Structures/Power/smes.rsi/smes-op2.png index af560237f1..58d8dbf1f5 100644 Binary files a/Resources/Textures/Structures/Power/smes.rsi/smes-op2.png and b/Resources/Textures/Structures/Power/smes.rsi/smes-op2.png differ diff --git a/Resources/Textures/Structures/Power/smes.rsi/smes-open.png b/Resources/Textures/Structures/Power/smes.rsi/smes-open.png index 472e3fd720..03868889f6 100644 Binary files a/Resources/Textures/Structures/Power/smes.rsi/smes-open.png and b/Resources/Textures/Structures/Power/smes.rsi/smes-open.png differ diff --git a/Resources/Textures/Structures/Power/smes.rsi/smes.png b/Resources/Textures/Structures/Power/smes.rsi/smes.png index ecfbc4cd58..71e7cf9195 100644 Binary files a/Resources/Textures/Structures/Power/smes.rsi/smes.png and b/Resources/Textures/Structures/Power/smes.rsi/smes.png differ diff --git a/Resources/Textures/Structures/Power/smes.rsi/static.png b/Resources/Textures/Structures/Power/smes.rsi/static.png index 3e52b6c43e..0fcc42db54 100644 Binary files a/Resources/Textures/Structures/Power/smes.rsi/static.png and b/Resources/Textures/Structures/Power/smes.rsi/static.png differ diff --git a/Resources/Textures/Structures/Power/substation.rsi/charging.png b/Resources/Textures/Structures/Power/substation.rsi/charging.png index 538dc2b7a8..fdf01dbe8d 100644 Binary files a/Resources/Textures/Structures/Power/substation.rsi/charging.png and b/Resources/Textures/Structures/Power/substation.rsi/charging.png differ diff --git a/Resources/Textures/Structures/Power/substation.rsi/dead.png b/Resources/Textures/Structures/Power/substation.rsi/dead.png index 9fbff744ca..ba6f81f1a1 100644 Binary files a/Resources/Textures/Structures/Power/substation.rsi/dead.png and b/Resources/Textures/Structures/Power/substation.rsi/dead.png differ diff --git a/Resources/Textures/Structures/Power/substation.rsi/full.png b/Resources/Textures/Structures/Power/substation.rsi/full.png index d179742669..42a670ff23 100644 Binary files a/Resources/Textures/Structures/Power/substation.rsi/full.png and b/Resources/Textures/Structures/Power/substation.rsi/full.png differ diff --git a/Resources/Textures/Structures/Power/substation.rsi/meta.json b/Resources/Textures/Structures/Power/substation.rsi/meta.json index 0125458641..a2ade57116 100644 --- a/Resources/Textures/Structures/Power/substation.rsi/meta.json +++ b/Resources/Textures/Structures/Power/substation.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Drawn by Ubaser.", + "copyright": "Taken from cev-eris at commit https://github.com/discordia-space/CEV-Eris/commit/b63634bc17efe2f09cf06ef0e9a90d24d37f6203, wall modified by Peptide90", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Structures/Power/substation.rsi/screen.png b/Resources/Textures/Structures/Power/substation.rsi/screen.png index 60c922c53f..c28bcbceda 100644 Binary files a/Resources/Textures/Structures/Power/substation.rsi/screen.png and b/Resources/Textures/Structures/Power/substation.rsi/screen.png differ diff --git a/Resources/Textures/Structures/Power/substation.rsi/substation.png b/Resources/Textures/Structures/Power/substation.rsi/substation.png index b9d1eb18d8..45524ad8b1 100644 Binary files a/Resources/Textures/Structures/Power/substation.rsi/substation.png and b/Resources/Textures/Structures/Power/substation.rsi/substation.png differ diff --git a/Resources/Textures/Structures/Power/substation.rsi/substation_static.png b/Resources/Textures/Structures/Power/substation.rsi/substation_static.png index 9d5b8598f4..2a1de1a066 100644 Binary files a/Resources/Textures/Structures/Power/substation.rsi/substation_static.png and b/Resources/Textures/Structures/Power/substation.rsi/substation_static.png differ diff --git a/Resources/Textures/Structures/Power/substation.rsi/substation_wall.png b/Resources/Textures/Structures/Power/substation.rsi/substation_wall.png index 155ced7128..8e8ae3fe3c 100644 Binary files a/Resources/Textures/Structures/Power/substation.rsi/substation_wall.png and b/Resources/Textures/Structures/Power/substation.rsi/substation_wall.png differ diff --git a/Resources/Textures/Structures/Power/substation.rsi/substation_wall_static.png b/Resources/Textures/Structures/Power/substation.rsi/substation_wall_static.png index e810e6a5a4..ee24ff45e6 100644 Binary files a/Resources/Textures/Structures/Power/substation.rsi/substation_wall_static.png and b/Resources/Textures/Structures/Power/substation.rsi/substation_wall_static.png differ diff --git a/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/base.png b/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/base.png index 02e8764173..0a2e1ae76b 100644 Binary files a/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/base.png and b/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/base.png differ diff --git a/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/broken.png b/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/broken.png index 9d10428af8..1565cdc86d 100644 Binary files a/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/broken.png and b/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/broken.png differ diff --git a/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/burned.png b/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/burned.png index 160db7c5cb..7e14e2c2b3 100644 Binary files a/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/burned.png and b/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/burned.png differ diff --git a/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/empty.png b/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/empty.png index 0f8f9478bd..5ead53beb0 100644 Binary files a/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/empty.png and b/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/empty.png differ diff --git a/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/glow.png b/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/glow.png index e1258d83dc..50cc6725c7 100644 Binary files a/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/glow.png and b/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/glow.png differ diff --git a/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/meta.json b/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/meta.json index 4b8aa5fa2f..be5f4a804b 100644 --- a/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/meta.json +++ b/Resources/Textures/Structures/Wallmounts/Lighting/light_tube.rsi/meta.json @@ -1,11 +1,11 @@ { "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c6e3401f2e7e1e55c57060cdf956a98ef1fefc24", "size": { "x": 32, "y": 32 }, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/discordia-space/CEV-Eris/blob/2b969adc2dfd3e9621bf3597c5cbffeb3ac8c9f0/icons/obj/lighting.dmi", "states": [ { "name": "broken", @@ -16,14 +16,14 @@ "directions": 4 }, { - "name": "base", + "name": "empty", "directions": 4 }, { - "name": "empty", + "name": "base", "directions": 4 }, - { + { "name": "glow", "directions": 4 } diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm0.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm0.png index 3b714153de..fdc9376d67 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm0.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm0.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm1.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm1.png index e7a2301488..d371695e12 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm1.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm1.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm2.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm2.png index 716536cfcb..9d8640f85b 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm2.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm2.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm_b1.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm_b1.png index fc185734af..5fbc157750 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm_b1.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm_b1.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm_b2.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm_b2.png index 0cdb046606..38b22ccd81 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm_b2.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm_b2.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm_bitem.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm_bitem.png index 192c589f26..6f1292bb05 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm_bitem.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarm_bitem.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarmp.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarmp.png index 793babbbe6..0b3192122b 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarmp.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarmp.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarmx.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarmx.png index 0f3e60bc0e..61ecb87ff4 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarmx.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/alarmx.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire0.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire0.png index aa9559b7dc..278e5cf03b 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire0.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire0.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_0.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_0.png index 39d4e98971..621410d043 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_0.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_0.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_1.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_1.png index 765fdc6e59..fb775d3098 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_1.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_1.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_3.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_3.png index ad74165ec4..71b96f2130 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_3.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_3.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_b0.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_b0.png index cc554c7394..cfe596102d 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_b0.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_b0.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_b1.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_b1.png index f732817d47..1a620adc14 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_b1.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_b1.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_b2.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_b2.png index d862c40a4b..c199a7ff31 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_b2.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_b2.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_bitem.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_bitem.png index cc2a57c495..5be7e11fc8 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_bitem.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_bitem.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_emagged.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_emagged.png index 310bb89d41..bafb424956 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_emagged.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_emagged.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_off.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_off.png index 8695c8a35d..3eb61db8b6 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_off.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_off.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_on.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_on.png index a98566070e..ce3697456f 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_on.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/fire_on.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/firex.png b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/firex.png index 5ac3fad6af..dd31be02f8 100644 Binary files a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/firex.png and b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/firex.png differ diff --git a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/meta.json b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/meta.json index 62e0a281bf..7484be2ffe 100644 --- a/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/meta.json +++ b/Resources/Textures/Structures/Wallmounts/air_monitors.rsi/meta.json @@ -1,48 +1,20 @@ { "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TauCetiClassic at commit https://github.com/TauCetiStation/TauCetiClassic/commit/b7ac5798ae0d411b0cdeaf7efbc4ac86d24b4634, fire_emagged.png edited by github:Morb0", "size": { "x": 32, "y": 32 }, - "copyright": "Taken from tgstation", - "license": "CC-BY-SA-3.0", "states": [ { "name": "alarm0", "directions": 4, "delays": [ - [ - 0.5, - 0.1, - 0.1, - 1.0, - 0.2, - 0.1 - ], - [ - 0.5, - 0.1, - 0.1, - 1.0, - 0.2, - 0.1 - ], - [ - 0.5, - 0.1, - 0.1, - 1.0, - 0.2, - 0.1 - ], - [ - 0.5, - 0.1, - 0.1, - 1.0, - 0.2, - 0.1 - ] + [2.8, 2.8], + [2.8, 2.8], + [2.8, 2.8], + [2.8, 2.8] ] }, { @@ -50,20 +22,20 @@ "directions": 4, "delays": [ [ - 0.5, - 0.5 + 0.6, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.2, 0.2, 0.2, 0.2, 0.2, 0.6, 0.6, 0.6 ], [ - 0.5, - 0.5 + 0.6, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.2, 0.2, 0.2, 0.2, 0.2, 0.6, 0.6, 0.6 ], [ - 0.5, - 0.5 + 0.6, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.2, 0.2, 0.2, 0.2, 0.2, 0.6, 0.6, 0.6 ], [ - 0.5, - 0.5 + 0.6, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.2, 0.2, 0.2, 0.2, 0.2, 0.6, 0.6, 0.6 ] ] }, @@ -72,20 +44,20 @@ "directions": 4, "delays": [ [ - 0.5, - 0.5 + 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.4, 0.4, 0.4, + 0.4, 0.4 ], [ - 0.5, - 0.5 + 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.4, 0.4, 0.4, + 0.4, 0.4 ], [ - 0.5, - 0.5 + 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.4, 0.4, 0.4, + 0.4, 0.4 ], [ - 0.5, - 0.5 + 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.4, 0.4, 0.4, + 0.4, 0.4 ] ] }, @@ -98,7 +70,8 @@ "directions": 4 }, { - "name": "alarm_bitem" + "name": "alarm_bitem", + "directions": 1 }, { "name": "alarmp", @@ -109,17 +82,14 @@ "directions": 4 }, { - "name": "auth_off" + "name": "auth_off", + "directions": 1, + "delays": [[1]] }, { "name": "auth_on", "directions": 1, - "delays": [ - [ - 0.1, - 0.1 - ] - ] + "delays": [[0.1, 0.1]] }, { "name": "fire0", @@ -137,44 +107,20 @@ "name": "fire_2", "directions": 4, "delays": [ - [ - 0.5, - 0.5 - ], - [ - 0.5, - 0.5 - ], - [ - 0.5, - 0.5 - ], - [ - 0.5, - 0.5 - ] + [0.5, 0.5], + [0.5, 0.5], + [0.5, 0.5], + [0.5, 0.5] ] }, { "name": "fire_3", "directions": 4, "delays": [ - [ - 0.5, - 0.5 - ], - [ - 0.5, - 0.5 - ], - [ - 0.5, - 0.5 - ], - [ - 0.5, - 0.5 - ] + [0.3, 0.3], + [0.3, 0.3], + [0.3, 0.3], + [0.3, 0.3] ] }, { @@ -190,67 +136,21 @@ "directions": 4 }, { - "name": "fire_bitem" + "name": "fire_bitem", + "directions": 1 }, { - "name": "fire_detected", + "name": "fire_emagged", "directions": 4, "delays": [ - [ - 0.4, - 0.4 - ], - [ - 0.4, - 0.4 - ], - [ - 0.4, - 0.4 - ], - [ - 0.4, - 0.4 - ] + [0.3, 0.3], + [0.3, 0.3], + [0.3, 0.3], + [0.3, 0.3] ] }, { - "name": "fire_emagged", - "directions": 4, - "delays": [ - [ - 0.5, - 0.5, - 0.3, - 0.5, - 0.2, - 0.5 - ], - [ - 0.5, - 0.5, - 0.3, - 0.5, - 0.2, - 0.5 - ], - [ - 0.5, - 0.5, - 0.3, - 0.5, - 0.2, - 0.5 - ], - [ - 0.5, - 0.5, - 0.3, - 0.5, - 0.2, - 0.5 - ] - ] + "name": "fire_detected" }, { "name": "fire_off", @@ -260,27 +160,14 @@ "name": "fire_on", "directions": 4, "delays": [ - [ - 0.5, - 0.5 - ], - [ - 0.5, - 0.5 - ], - [ - 0.5, - 0.5 - ], - [ - 0.5, - 0.5 - ] + [0.2, 0.2, 0.2, 0.2, 0.2, 0.2], + [0.2, 0.2, 0.2, 0.2, 0.2, 0.2], + [0.2, 0.2, 0.2, 0.2, 0.2, 0.2], + [0.2, 0.2, 0.2, 0.2, 0.2, 0.2] ] }, { - "name": "fire_overlay", - "directions": 4 + "name": "fire_overlay" }, { "name": "firex", diff --git a/Resources/Textures/Structures/Wallmounts/camera.rsi/camera.png b/Resources/Textures/Structures/Wallmounts/camera.rsi/camera.png index 19481d55a0..f3054b57ed 100644 Binary files a/Resources/Textures/Structures/Wallmounts/camera.rsi/camera.png and b/Resources/Textures/Structures/Wallmounts/camera.rsi/camera.png differ diff --git a/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_assembly.png b/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_assembly.png index 44d8460f5f..404e4dad19 100644 Binary files a/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_assembly.png and b/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_assembly.png differ diff --git a/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_emp.png b/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_emp.png index 6a7c1a24c5..59c7f453eb 100644 Binary files a/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_emp.png and b/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_emp.png differ diff --git a/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_in_use.png b/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_in_use.png index 42a6a6e098..3ed85a4a6b 100644 Binary files a/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_in_use.png and b/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_in_use.png differ diff --git a/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_off.png b/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_off.png index 7fa16ed3fd..487a2602aa 100644 Binary files a/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_off.png and b/Resources/Textures/Structures/Wallmounts/camera.rsi/camera_off.png differ diff --git a/Resources/Textures/Structures/Wallmounts/camera.rsi/cameracase.png b/Resources/Textures/Structures/Wallmounts/camera.rsi/cameracase.png index ac3f200d1f..f93042f15f 100644 Binary files a/Resources/Textures/Structures/Wallmounts/camera.rsi/cameracase.png and b/Resources/Textures/Structures/Wallmounts/camera.rsi/cameracase.png differ diff --git a/Resources/Textures/Structures/Wallmounts/camera.rsi/meta.json b/Resources/Textures/Structures/Wallmounts/camera.rsi/meta.json index b8cedc6db6..95d2b24378 100644 --- a/Resources/Textures/Structures/Wallmounts/camera.rsi/meta.json +++ b/Resources/Textures/Structures/Wallmounts/camera.rsi/meta.json @@ -1,73 +1,490 @@ { - "version": 1, - "size": { - "x": 32, - "y": 32 + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TauCetiClassic at commit https://github.com/TauCetiStation/TauCetiClassic/commit/f878e95210d81e1328b023feb96ec0d0b6113d8c", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "camera", + "directions": 8, + "delays": [ + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25, + 0.25 + ] + ] }, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from /tg/station at https://github.com/tgstation/tgstation/commit/6bfe3b2e4fcbcdac9159dc4f0327a82ddf05ba7bi", - "states": [ - { - "name": "camera", - "directions": 1, - "delays": [ - [ 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3 ] - ] - }, - { - "name": "camera_assembly", - "directions": 1 - }, - { - "name": "camera_emp", - "directions": 1, - "delays": [ - [ 0.2, 0.2, 0.1, 0.1 ] - ] - }, - { - "name": "camera_in_use", - "directions": 1, - "delays": [ - [ 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3 ] - ] - }, - { - "name": "camera_off", - "directions": 1 - }, - { - "name": "cameracase", - "directions": 1 - }, - { - "name": "xraycamera", - "directions": 1, - "delays": [ - [ 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3 ] - ] - }, - { - "name": "xraycamera_assembly", - "directions": 1 - }, - { - "name": "xraycamera_emp", - "directions": 1, - "delays": [ - [ 0.2, 0.2, 0.1, 0.1 ] - ] - }, - { - "name": "xraycamera_in_use", - "directions": 1, - "delays": [ - [ 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3 ] - ] - }, - { - "name": "xraycamera_off", - "directions": 1 - } - ] -} \ No newline at end of file + { + "name": "camera_in_use", + "directions": 8, + "delays": [ + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ] + ] + }, + { + "name": "camera_off", + "directions": 8 + }, + { + "name": "camera_emp", + "directions": 8, + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "camera_assembly", + "directions": 8 + }, + { + "name": "xraycamera", + "directions": 8, + "delays": [ + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ] + ] + }, + { + "name": "xraycamera_emp", + "directions": 8, + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ], + [ + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "xraycamera_off", + "directions": 8 + }, + { + "name": "xraycamera_assembly", + "directions": 8 + }, + { + "name": "cameracase" + }, + { + "name": "xraycamera_in_use", + "directions": 8, + "delays": [ + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ], + [ + 0.25, + 0.25, + 0.6, + 0.25, + 0.25, + 0.25, + 0.6, + 0.25 + ] + ] + } + ] +} diff --git a/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera.png b/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera.png index 31d9892d9f..b543be5d7b 100644 Binary files a/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera.png and b/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera.png differ diff --git a/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_assembly.png b/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_assembly.png index d2f690d3c1..5dfddb08f7 100644 Binary files a/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_assembly.png and b/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_assembly.png differ diff --git a/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_emp.png b/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_emp.png index af6f3e2cec..434dbd575b 100644 Binary files a/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_emp.png and b/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_emp.png differ diff --git a/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_in_use.png b/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_in_use.png index 96dc134c7f..dadc30eba7 100644 Binary files a/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_in_use.png and b/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_in_use.png differ diff --git a/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_off.png b/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_off.png index 9b8a1aed79..487a2602aa 100644 Binary files a/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_off.png and b/Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_off.png differ diff --git a/Resources/Textures/Structures/Wallmounts/intercom.rsi/base.png b/Resources/Textures/Structures/Wallmounts/intercom.rsi/base.png index 787af3f538..7ddea57ede 100644 Binary files a/Resources/Textures/Structures/Wallmounts/intercom.rsi/base.png and b/Resources/Textures/Structures/Wallmounts/intercom.rsi/base.png differ diff --git a/Resources/Textures/Structures/Wallmounts/intercom.rsi/broadcasting.png b/Resources/Textures/Structures/Wallmounts/intercom.rsi/broadcasting.png index 0566c70e35..fb01e70861 100644 Binary files a/Resources/Textures/Structures/Wallmounts/intercom.rsi/broadcasting.png and b/Resources/Textures/Structures/Wallmounts/intercom.rsi/broadcasting.png differ diff --git a/Resources/Textures/Structures/Wallmounts/intercom.rsi/build.png b/Resources/Textures/Structures/Wallmounts/intercom.rsi/build.png index cfd5d5fffa..21fc9a1bad 100644 Binary files a/Resources/Textures/Structures/Wallmounts/intercom.rsi/build.png and b/Resources/Textures/Structures/Wallmounts/intercom.rsi/build.png differ diff --git a/Resources/Textures/Structures/Wallmounts/intercom.rsi/meta.json b/Resources/Textures/Structures/Wallmounts/intercom.rsi/meta.json index f5a9d7b89c..d4440c99a0 100644 --- a/Resources/Textures/Structures/Wallmounts/intercom.rsi/meta.json +++ b/Resources/Textures/Structures/Wallmounts/intercom.rsi/meta.json @@ -5,7 +5,7 @@ "y": 32 }, "license": "CC-BY-SA-3.0", - "copyright": "Taken from /tg/station at https://github.com/tgstation/tgstation/commit/6bfe3b2e4fcbcdac9159dc4f0327a82ddf05ba7bi", + "copyright": "Taken from https://github.com/Skyrat-SS13/Skyrat-tg/blob/6e7197f550aac62a165c0a8c6b0835de19342623/modular_skyrat/modules/aesthetics/intercom/icons/intercom.dmi & build state from DenLemp#2965", "states": [ { "name": "base", diff --git a/Resources/Textures/Structures/Wallmounts/intercom.rsi/unshaded.png b/Resources/Textures/Structures/Wallmounts/intercom.rsi/unshaded.png index 7b0bb63072..9c4c3f199e 100644 Binary files a/Resources/Textures/Structures/Wallmounts/intercom.rsi/unshaded.png and b/Resources/Textures/Structures/Wallmounts/intercom.rsi/unshaded.png differ diff --git a/RobustToolbox b/RobustToolbox index 25bbb21dc8..eb63809999 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 25bbb21dc868b4e0b43b6d28e899710891b35868 +Subproject commit eb638099999dce3a43d90772ca976ae010d649c0