diff --git a/Content.Client/Alerts/ClientAlertsSystem.cs b/Content.Client/Alerts/ClientAlertsSystem.cs index 237f24e3eae..223bf7876ac 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 9d30cabb1ec..0206017baef 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 0fdcc7a86dd..92c5b7a4191 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 fbe9d5ec5bb..7e78ac7d707 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 473ae97059a..bc52730b0e7 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 8636e0eb6aa..a1fc68bbd2f 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 0ab33c65202..4b025608465 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 e406ba2b557..5e60d06d0ce 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 f0836ee9b6f..b7f5e48821f 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 7bf3df1f0b9..709601a57b6 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 f10eb9ed8b1..691bcb41dbd 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 7a8a6e39cff..31042854d4a 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 556dadd00da..00000000000 --- 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 8c721fa7771..ef6283b6ff4 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 00000000000..d6a9057f08e --- /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 00000000000..94589ecdaab --- /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 86d113defb1..2fa862f3df7 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 fed2a9f1716..284c6681905 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 bbd36731104..b25b4fbb7de 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 bdf12080388..343641e1613 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 554807b2d25..0ea6d3e2dcc 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 6046ed65e44..b544fe28547 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 98c7363a6c4..772af337a1a 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 f2e19849b00..dce3741c98d 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 6e2a080370d..7c700d9fb8a 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 26ea726211b..b37f7cfa468 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 976fc2eceb5..9109fdbe4f7 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 152eb725221..d3b1fb47221 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 0d852bf2f0e..6e88d6928e6 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 3213bba51f9..a9069892dff 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 1d5dd6d34e2..c6a8e618cc1 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 88448e7b800..480fd9cde6f 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 bed27ba6efe..a4ed31e9983 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 553b031c2b7..dc5aec92cfc 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 00000000000..30724b50a6d --- /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 d4e2cde9b0d..a6af3e6a65b 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 6096c497efa..9e26fa5eaa2 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 ba91f54ff77..87d174f7272 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 b6fc273570a..a1aa462a697 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 0a2af88887a..083e817d697 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 a989ebd968e..a66d4ec5b91 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 9f2b0755357..23f661921e0 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 ba5924ec3e9..65c0e0131a4 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 ba489629f79..d9ca57c4d11 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 9765e8385f0..cf2f8c453c8 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 2684e85d5f1..7ef1932c568 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 ac55ce83259..f1a5a57aa65 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 00000000000..aa0e77a794a --- /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 7aabd76335e..3f9b7efd07e 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 57d6e089bd4..8fb28b7ec4d 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 a52ec7b099c..95967b24aca 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 2cf076fbeed..b02da38ecfa 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 00a41495985..76f9569429f 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 2053a7bbbe6..e03c566593e 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 87c0ba4a4ee..dd2da82c9d6 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 7c397d68887..90a655fbba7 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 a5e42be5400..2408ad0b3dd 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 9e7594c024b..f4279db926a 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 416045fc5ed..d947e60b6da 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 dfe84473402..aed00432e1f 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 10b9cccc099..8e478bd2b54 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 6fbf60f403d..10049e273bc 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 ad647fad1b8..170586339db 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 b1caa6c197e..4ddd19dd45e 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 9e622ba6b82..3bb191fecdd 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 655d0c88f9b..3e006c539c7 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 24604b88b7a..82f71619370 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 66b52af47bd..a010855f784 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 7041df44481..d448c4aab21 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 4eda008b0f9..18caab8dcf0 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 0656ef8fad3..46600b30207 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 a8c82f3d369..90c99df7db2 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 9f080a3dd9d..4045e21e26a 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 fe93468f74e..d541ca4d7c4 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 4acdccf1ba7..19b76189e09 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 f6fdcfedff4..9e29fdf7568 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 18119909abc..37f78ed81a0 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 abb54971209..86d2cb61ffe 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 007cbdf0848..9607a808f65 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 4b60f8814b5..e83d3c32a25 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 e5f604f70df..45cba5a195f 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 0fd61a9cb7b..c7266e2c463 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 4c11244c379..a4c2e8292dd 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 a9556be7738..a8bf4184ac8 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 48c58321b38..d26794a6323 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 2609d06b55d..3d6fc964725 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 6fb36c96083..9e1970d63c9 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 6d54bedf86c..d12bbfe53c9 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 e5fa8d5f4dc..59945bf5ca6 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 f69c95dfcb8..3503ef84ca3 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 443923f675c..c923738930a 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 7926121c2b3..468212f5eaf 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 b93498fe31b..a8583e6bcb3 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 baaef1f3f64..2ae814113f0 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 771c66fbbd5..6b6a91c9d33 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 ad225afe224..da95401d206 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 f0538c47f95..ba042d89662 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 20aa8b6d2c5..6c0bced53e0 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 eb3cda4db9b..38e39238039 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 6400472d036..79e5c0a2a9c 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 d410c7af961..9d8b7e416e6 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 61c4937a271..a0bbbdf350a 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 af150c93250..55e8de26321 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 b04642a8db0..719a2eca797 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 4a9477e7443..ccfcee46124 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 afad0e27e09..a42dd110831 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 24ee2b71541..2d54c03b51b 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 98926536b9d..128f1123043 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 7558f7afc0b..c934fb66bfb 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 a0e872dbeb1..e836b658381 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 31aa39cf03a..429361cd8cd 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 8eb27414481..6b58f6eb092 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 4302ab6791b..1b7913386d2 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 6692886daee..203781bcdaa 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 57b07e8181b..006a245ead3 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 5022e540960..adbfe2d681f 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 92cd794ce2c..86555924712 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 08ba0de8334..08f3dcccf9f 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 1012b9727df..f592de9e7eb 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 f19b3d5b814..09497c7136b 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 c2a8ca31d7d..00000000000 --- 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 eb04f59bdd5..00000000000 --- 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 64a72b13cfa..d276be72187 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 dd35d2112c9..e64343fdd8a 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 20a616d8308..00000000000 --- 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 015adb19de5..00000000000 --- 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 e7af2c91073..ce10d4f5d5e 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 70d1e89bc4f..3bc4eae9e49 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 c00375d6488..447792b6ff2 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 636037060a9..970ca78e2cc 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 5e31bd68722..2296de2eb6a 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 d6767cd2de8..ad153dcf6b6 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 f8d781bcfff..a28679ddbc9 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 e92b046f2e3..abb6f393ac5 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 103731b1b04..ff4c097080c 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 aa6de572cee..80c38f4630e 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 ba5ff0a056d..b1a6c1e9de1 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 2bd8538af16..888a365a5dd 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 02d4ee010b5..0fe6f0947c8 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 ddc7e2a0830..e9b62bc4a80 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 c362507f19a..759b8ef29c6 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 8f58f807aae..00000000000 --- 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 66dc9dab99d..5fe29dcd30e 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 c5ca36c3a15..263d626ef55 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 dd478753be3..62eb08d7cbc 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 fcf0ae3d58d..ec08523d447 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 acfb8ff87b3..85e553031fc 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 e8e9c5b45e3..e7dfa351789 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 73c6f5d339c..3d7d5c20c7c 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 8c72a02737b..a1b40b92320 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 25f0434c797..b4129581ec1 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 ffadc61c199..a1776acbd3c 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 15fc092ebcf..f675cc6ed89 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 f3e2cc69fd5..62e0c3ce56c 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 cc67badbe72..87ec5adbce8 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 c5f8471a654..95b7995e673 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 5babb6c446d..34cdba29bb2 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 309bd732ef7..76ff5a823b3 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 69bb7c93704..00000000000 --- 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 7ed7574a9ae..bfdd49213ee 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 f676e89ac3f..4b82dba3359 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 91cb7ccad10..00000000000 --- 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 d335911901a..e42bc700912 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 5bab6048de5..d40134f1e9f 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 2822c94093f..2463bcd7402 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 eb6eb5a426f..cd64f043a08 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 edb29da624f..feba63a253f 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 7e4a9c9310e..36e3a574ea0 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 a1a3b686b2f..5a68005dd3a 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 e2b17b58724..5f7e356830c 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 2ab29d1b2f0..6ce8edd1d84 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 f4dd502b375..ae742cf1f9e 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 f8d995b8a4b..39b76f7d324 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 e4e4534b0c5..cb322ac3964 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 1baffd4690e..74c42ccbc53 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 4584a9e88b5..010682bc0d3 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 ba07375699b..ed53c3e1018 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 92b963e2017..b26ab301c64 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 b7751afbf18..d491b957bfb 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 5b2f3298a2b..671c281d1f4 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 a3b7a573545..debac8902e2 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 b9ff8a4339d..492f15c8e2b 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 53bc8b62a43..282ee5b612a 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 c60f3298e74..8ad5c8602e3 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 53a98e8b762..aa0c9b214b4 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 7820badcebf..2767f500f9a 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 7544fc376ba..0e694a801eb 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 aca902b9d38..0f57da4b881 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 00000000000..e2d74ba5d9b --- /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 00000000000..58e974227ce --- /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 c471fb00f4f..9db00d2949c 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 85783b552da..57a30a2fd9e 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 96f1dc37835..00f409f5533 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 33d1a43c125..5525cdf3591 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 239b6741608..9976d56da0b 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 4388756cce0..8777ab0a8c7 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 21f25080426..c7e29166a0e 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 bef57eceb39..080bef44e7a 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 a914a8f267d..f5ed2df227c 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 5b5873bce36..229edbcd4c3 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 64cf522faaf..9842e638c2c 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 14438cc5912..4ec9600b0bd 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 b52a333613f..968b172aef5 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 8c2761f545d..e9400bc48d7 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 7f4b3fab15d..7506538c439 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 9e1de6b3559..3048927b5fb 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 4dbc543fc8e..0d8d2c8a268 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 bc7cf63124c..1a35afdbe00 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 fa113907058..efabebfc858 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 e07aac06226..ee79faa0b8e 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 8172947a039..3355713bb2e 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 8f683356637..67218657e52 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 00000000000..b5d6aa03441 --- /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 00000000000..cd47bf0c33c --- /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 00000000000..a6bb70dd9f6 --- /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 6a2976a8387..fceb6143e19 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 70bcfbab43f..c041cf1ba06 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 a62488d6f38..8b21bca52ae 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 b9083eabe19..47d359d3875 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 567a600388e..db9ee85a0cd 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 92f4576c8b6..8ee34dbcc5e 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 600036a8910..9afd683cbdc 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 5656778a3f9..fc7cccf9bd6 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 ba4d9fc24f8..3583947ee36 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 3857063783d..e7a88b6ef29 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 0e390ecea45..e4864b1f7fc 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 0632f5d9cb2..ddbdc742be4 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 9342c07e91f..be54b45aa1f 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 08a016efa9c..4a72de791f0 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 c802700b62c..e40174ab783 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 58a5d133b55..f1c1beef7d1 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 a344193f123..ad8b3ddea8d 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 5a9ada7f586..50dce3c7669 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 00000000000..db889e7e3bd --- /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 00000000000..1fc9b731bd5 --- /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 00000000000..b0101c46996 --- /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 00000000000..cd7edc5f623 --- /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 00000000000..a427e448d5c --- /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 00000000000..29460e1dfc1 --- /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 00000000000..47aa34562fb --- /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 00000000000..b347c6da164 --- /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 4d9eedbf7c4..79d2e9f2551 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 e27603763fa..f2936d603d9 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 c9d7dc5abc1..ffea2a67c0e 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 ec17df7a24f..c9ec77ba1c9 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 e2074479871..cc7a7e9780f 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 540dc03341b..0eeb7ec280d 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 4dd8fa2442f..7a879785821 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 c5c30688699..00000000000 --- 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 57a86e7f7a8..00000000000 --- 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 6296dc2f14f..00000000000 --- 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 b11de7c1702..00000000000 --- 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 0ede284bb0c..00000000000 --- 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 afcbcb70740..00000000000 --- 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 46c6b1291d6..00000000000 --- 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 0233e32f2b4..00000000000 --- 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 3dab476337b..00000000000 --- 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 e388d7a57c6..00000000000 --- 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 38ed8998898..00000000000 --- 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 1e2bb90c61e..00000000000 --- 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 0c139ee9e35..00000000000 --- 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 187c8d8a9d8..9e784512076 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 00000000000..b157596e3b9 --- /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 00000000000..e9bbd27ada4 --- /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 e0331fad1bb..df8878e6516 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 12f27d0b9c9..9819e949b8b 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 7b9ce841a99..622edc4b62e 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 a7e6ac1152b..6bacbd2b5b1 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 ed687d48f4b..8231e48e2db 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 324fd65c860..ca25a49b23f 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 22ba8e0e3ee..56a51744acf 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 04f7231416f..568d9dab3bd 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 11075b4a4c7..799fb7e33e9 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 02b4e617901..ba78ff651f5 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 ebd83624114..8d67aec518a 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 01682863389..38772eaf340 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 16c9b13254c..f7defaa4aab 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 cc50094e3dd..ef28db26727 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 1f8408319d3..0d368495f18 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 d47d024de5e..274828a2086 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 00000000000..09870af317c --- /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 b7b70bd1188..78867fcb8ab 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 0a5a726a6a2..49aa0a6c97c 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 00000000000..40d45aa46b6 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 00000000000..de4c11beba5 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 00000000000..afa0c82f03e 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 00000000000..a653715c6cd 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 00000000000..4fb5e521057 --- /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 b1bbcc4c8c1..b70f550fc37 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 63c68de1053..b332517c70d 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 fab815b4f90..2c8b326b076 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 bf2fc118619..2c4ccd08052 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 231733eabfb..0b8fa83ae8b 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 a220737f18a..75c83643d6d 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 b2a1e9b2b8c..8070d03be29 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 c832d9fc17f..00000000000 --- 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 437fc03ae09..b6abc3e4288 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 4836a57d6b1..592cf59d2fe 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 80d98a1b3e8..24f5f47127f 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 62a3a4162f3..c8c35aabfe4 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 0c810a52f5d..d6e2c459391 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 cc5e3b1d174..26d0f47315f 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 a8ebd6e2ba9..60fa6c15fd1 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 8ee6479ee65..51a56f1f1d6 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 2d65e67982f..246b47b8003 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 f49f5f4804b..b867abfeed3 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 241fc45352a..fdc49dc0616 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 eba1e300edf..d12b85cde41 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 06b0e6fad8f..17e2a4df5ee 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 07828ff5ba5..face999df82 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 b7e3aa3a489..192be68c8c7 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 fe888595847..8cd1c5dfab6 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 85de407988b..58af9cf3bd8 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 eec7711d4af..21eb0fb9423 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 3b080e843b4..6b96f3bcb36 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 c2a1fe6e7dc..4cd8a850993 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 664626ea4b4..b28541253d4 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 67c6e1194b7..9ced5539417 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 a4f1e5e6879..02ee00a07fd 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 4aff7363a45..630354f23d9 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 b0c4047ccd8..4e87776e9fc 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 94de12be185..04c83bd991e 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 9ed1889154d..8e518ab2f18 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 1a874612c2f..048fdc6f24c 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 4be21cec302..d7d0b665381 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 eca5b5e3758..161c5b4ef52 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 5213608d95e..8e34a07ea5e 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 43def65d7a0..f2ac97be58b 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 381871f94af..7e4087b20a2 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 f0c59a0bdf9..b6184272dc0 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 c16972c8a31..11f8d509af3 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 dcf53d9ab7f..8dc0264ffea 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 03102cd341b..92c2947258c 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 e99b51f82cd..7d7169bf10a 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 b7fa0fa2b6c..e48fa2f2f2b 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 fc92a479367..cae7880e414 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 00000000000..4f72c31ddf6 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 2b42c54935a..ea2f17c062f 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 092694c371f..4bcd2475cdf 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 ba84b7d6db5..0d181878fcf 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 6cce72f795f..c7a0d87ccc3 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 61ae057d508..4be0bbfb0bc 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 6cc48bd1a55..720dc4976ab 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 b31bed6c6ba..7f0a13a7a4c 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 959a05cde08..87191be5207 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 3bb505ecb86..031213f74ad 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 bd8b013310c..ac5f1c1befe 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 0cca8602208..ff8522d2c5a 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 56802dc0437..4e04387ed3d 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 5ac134cb54b..2f9802281e0 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 5ebec4feeeb..fb9e2180bb6 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 67e3b3df19b..d5e356d943e 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 1739853e2b7..4a9bd7ed643 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 c4c103eeb96..d1d2df4858d 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 be343a987b0..07265ab67de 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 2713c5506a2..77c2518e63d 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 7d3529f63f5..e4c9c1b31cf 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 82f78ccb875..867e5ce0ffd 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 212fcb78bf8..f442757bea6 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 43089d0d811..a7137ec2f3a 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 773a2b5a3e7..027655dd356 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 5f42ce89bc2..71e7cf91956 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 2ca8d1fb226..4b4193ec726 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 5579432d3fe..9d62074f1d8 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 d624672613b..10f4c4678ed 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 d624672613b..10f4c4678ed 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 c65f011a070..472b013ce71 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 00237504051..c2d3dbbc46e 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 51df688e4a8..263d8e98f78 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 ceb3db1bc31..e31794f5c70 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 e6c0c8a2eac..26955f70a16 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 573b5c404ff..ab0ae9e1145 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 f5c8e104894..58d8dbf1f5c 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 af560237f17..58d8dbf1f5c 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 472e3fd7201..03868889f61 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 ecfbc4cd58f..71e7cf91956 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 3e52b6c43e4..0fcc42db544 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 538dc2b7a84..fdf01dbe8de 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 9fbff744ca7..ba6f81f1a1a 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 d1797426699..42a670ff234 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 0125458641c..a2ade57116e 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 60c922c53f6..c28bcbcedae 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 b9d1eb18d8f..45524ad8b11 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 9d5b8598f4b..2a1de1a0661 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 155ced7128a..8e8ae3fe3c2 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 e810e6a5a4a..ee24ff45e68 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 02e8764173f..0a2e1ae76b9 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 9d10428af8d..1565cdc86d7 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 160db7c5cbb..7e14e2c2b39 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 0f8f9478bd8..5ead53beb0e 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 e1258d83dc3..50cc6725c71 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 4b8aa5fa2f0..be5f4a804b1 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 3b714153de9..fdc9376d67f 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 e7a23014888..d371695e129 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 716536cfcb2..9d8640f85b0 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 fc185734afd..5fbc157750b 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 0cdb0466062..38b22ccd815 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 192c589f26c..6f1292bb058 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 793babbbe6a..0b3192122be 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 0f3e60bc0e8..61ecb87ff48 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 aa9559b7dce..278e5cf03b2 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 39d4e989710..621410d043e 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 765fdc6e594..fb775d3098b 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 ad74165ec40..71b96f2130d 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 cc554c73944..cfe596102d2 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 f732817d47e..1a620adc144 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 d862c40a4bc..c199a7ff31e 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 cc2a57c4956..5be7e11fc85 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 310bb89d41b..bafb4249565 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 8695c8a35dc..3eb61db8b6c 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 a98566070e5..ce3697456ff 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 5ac3fad6af8..dd31be02f80 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 62e0a281bf0..7484be2ffed 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 19481d55a0d..f3054b57ed3 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 44d8460f5fc..404e4dad197 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 6a7c1a24c51..59c7f453ebc 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 42a6a6e098e..3ed85a4a6b2 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 7fa16ed3fdd..487a2602aa5 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 ac3f200d1fe..f93042f15f2 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 b8cedc6db63..95d2b24378e 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 31d9892d9f6..b543be5d7b3 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 d2f690d3c11..5dfddb08f7d 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 af6f3e2ceca..434dbd575b6 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 96dc134c7f2..dadc30eba7c 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 9b8a1aed798..487a2602aa5 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 787af3f5387..7ddea57eded 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 0566c70e35c..fb01e708619 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 cfd5d5fffa0..21fc9a1bad5 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 f5a9d7b89c2..d4440c99a04 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 7b0bb630722..9c4c3f199e6 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 25bbb21dc86..eb638099999 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 25bbb21dc868b4e0b43b6d28e899710891b35868 +Subproject commit eb638099999dce3a43d90772ca976ae010d649c0