diff --git a/Content.Client/Alerts/ClientAlertsSystem.cs b/Content.Client/Alerts/ClientAlertsSystem.cs index 9c4ebb9cd2b..223bf7876ac 100644 --- a/Content.Client/Alerts/ClientAlertsSystem.cs +++ b/Content.Client/Alerts/ClientAlertsSystem.cs @@ -4,7 +4,6 @@ using Robust.Client.Player; using Robust.Shared.Player; using Robust.Shared.Prototypes; -using Robust.Shared.Timing; namespace Content.Client.Alerts; @@ -13,7 +12,6 @@ public sealed class ClientAlertsSystem : AlertsSystem { public AlertOrderPrototype? AlertOrder { get; set; } - [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; diff --git a/Content.Client/Announcements/Systems/AnnouncerSystem.cs b/Content.Client/Announcements/Systems/AnnouncerSystem.cs new file mode 100644 index 00000000000..de76396f705 --- /dev/null +++ b/Content.Client/Announcements/Systems/AnnouncerSystem.cs @@ -0,0 +1,69 @@ +using Content.Client.Audio; +using Content.Shared.Announcements.Events; +using Content.Shared.Announcements.Systems; +using Content.Shared.CCVar; +using Robust.Client.Audio; +using Robust.Client.Player; +using Robust.Client.ResourceManagement; +using Robust.Shared.Audio.Sources; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Configuration; + +namespace Content.Client.Announcements.Systems; + +public sealed class AnnouncerSystem : SharedAnnouncerSystem +{ + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IConfigurationManager _config = default!; + [Dependency] private readonly IResourceCache _cache = default!; + [Dependency] private readonly IAudioManager _audioManager = default!; + + private IAudioSource? AnnouncerSource { get; set; } + private float AnnouncerVolume { get; set; } + + + public override void Initialize() + { + base.Initialize(); + + AnnouncerVolume = _config.GetCVar(CCVars.AnnouncerVolume) * 100f / ContentAudioSystem.AnnouncerMultiplier; + + SubscribeNetworkEvent(OnAnnouncementReceived); + _config.OnValueChanged(CCVars.AnnouncerVolume, OnAnnouncerVolumeChanged); + } + + public override void Shutdown() + { + base.Shutdown(); + + _config.UnsubValueChanged(CCVars.AnnouncerVolume, OnAnnouncerVolumeChanged); + } + + + private void OnAnnouncerVolumeChanged(float value) + { + AnnouncerVolume = value; + + if (AnnouncerSource != null) + AnnouncerSource.Gain = AnnouncerVolume; + } + + private void OnAnnouncementReceived(AnnouncementSendEvent ev) + { + if (!ev.Recipients.Contains(_player.LocalSession!.UserId) + || !_cache.TryGetResource(GetAnnouncementPath(ev.AnnouncementId, ev.AnnouncerId), + out var resource)) + return; + + var source = _audioManager.CreateAudioSource(resource); + if (source != null) + { + source.Gain = AnnouncerVolume * SharedAudioSystem.VolumeToGain(ev.AudioParams.Volume); + source.Global = true; + } + + AnnouncerSource?.Dispose(); + AnnouncerSource = source; + AnnouncerSource?.StartPlaying(); + } +} 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/Audio/ContentAudioSystem.cs b/Content.Client/Audio/ContentAudioSystem.cs index f62b34b492c..a79ff74e797 100644 --- a/Content.Client/Audio/ContentAudioSystem.cs +++ b/Content.Client/Audio/ContentAudioSystem.cs @@ -29,7 +29,8 @@ public sealed partial class ContentAudioSystem : SharedContentAudioSystem public const float AmbientMusicMultiplier = 3f; public const float LobbyMultiplier = 3f; public const float InterfaceMultiplier = 2f; - + public const float AnnouncerMultiplier = 3f; + public override void Initialize() { base.Initialize(); 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/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/Options/UI/Tabs/AudioTab.xaml b/Content.Client/Options/UI/Tabs/AudioTab.xaml index e54b0dc34ee..8dd723d446d 100644 --- a/Content.Client/Options/UI/Tabs/AudioTab.xaml +++ b/Content.Client/Options/UI/Tabs/AudioTab.xaml @@ -100,6 +100,19 @@ 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/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.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/Administration/UI/AdminAnnounceEui.cs b/Content.Server/Administration/UI/AdminAnnounceEui.cs index b6a6ba99840..e6d17b5af40 100644 --- a/Content.Server/Administration/UI/AdminAnnounceEui.cs +++ b/Content.Server/Administration/UI/AdminAnnounceEui.cs @@ -5,6 +5,8 @@ using Content.Server.EUI; using Content.Shared.Administration; using Content.Shared.Eui; +using Content.Server.Announcements.Systems; +using Robust.Shared.Player; namespace Content.Server.Administration.UI { @@ -12,11 +14,14 @@ public sealed class AdminAnnounceEui : BaseEui { [Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly IChatManager _chatManager = default!; + private readonly AnnouncerSystem _announcer; private readonly ChatSystem _chatSystem; public AdminAnnounceEui() { IoCManager.InjectDependencies(this); + + _announcer = IoCManager.Resolve().GetEntitySystem(); _chatSystem = IoCManager.Resolve().GetEntitySystem(); } @@ -50,7 +55,8 @@ public override void HandleMessage(EuiMessageBase msg) break; // TODO: Per-station announcement support case AdminAnnounceType.Station: - _chatSystem.DispatchGlobalAnnouncement(doAnnounce.Announcement, doAnnounce.Announcer, colorOverride: Color.Gold); + _announcer.SendAnnouncement(_announcer.GetAnnouncementId("Announce"), Filter.Broadcast(), + doAnnounce.Announcement, doAnnounce.Announcer, Color.Gold); break; } diff --git a/Content.Server/AlertLevel/AlertLevelSystem.cs b/Content.Server/AlertLevel/AlertLevelSystem.cs index 848170ba9dc..d856fab9da7 100644 --- a/Content.Server/AlertLevel/AlertLevelSystem.cs +++ b/Content.Server/AlertLevel/AlertLevelSystem.cs @@ -6,6 +6,7 @@ using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; using Robust.Shared.Prototypes; +using Content.Server.Announcements.Systems; namespace Content.Server.AlertLevel; @@ -16,6 +17,7 @@ public sealed class AlertLevelSystem : EntitySystem [Dependency] private readonly ChatSystem _chatSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly StationSystem _stationSystem = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; // Until stations are a prototype, this is how it's going to have to be. public const string DefaultAlertLevelSet = "stationAlerts"; @@ -122,18 +124,14 @@ public void SetLevel(EntityUid station, string level, bool playSound, bool annou || component.AlertLevels == null || !component.AlertLevels.Levels.TryGetValue(level, out var detail) || component.CurrentLevel == level) - { return; - } if (!force) { if (!detail.Selectable || component.CurrentDelay > 0 || component.IsLevelLocked) - { return; - } component.CurrentDelay = _cfg.GetCVar(CCVars.GameAlertLevelChangeDelay); component.ActiveDelay = true; @@ -142,64 +140,31 @@ public void SetLevel(EntityUid station, string level, bool playSound, bool annou component.CurrentLevel = level; component.IsLevelLocked = locked; - var stationName = dataComponent.EntityName; - var name = level.ToLower(); - if (Loc.TryGetString($"alert-level-{level}", out var locName)) - { name = locName.ToLower(); - } // Announcement text. Is passed into announcementFull. var announcement = detail.Announcement; if (Loc.TryGetString(detail.Announcement, out var locAnnouncement)) - { announcement = locAnnouncement; - } - // The full announcement to be spat out into chat. - var announcementFull = Loc.GetString("alert-level-announcement", ("name", name), ("announcement", announcement)); - - var playDefault = false; + var alert = _announcer.GetAnnouncementId($"Alert{level}"); if (playSound) - { - if (detail.Sound != null) - { - var filter = _stationSystem.GetInOwningStation(station); - _audio.PlayGlobal(detail.Sound, filter, true, detail.Sound.Params); - } - else - { - playDefault = true; - } - } - + _announcer.SendAnnouncementAudio(alert, _stationSystem.GetInOwningStation(station)); if (announce) - { - _chatSystem.DispatchStationAnnouncement(station, announcementFull, playDefaultSound: playDefault, - colorOverride: detail.Color, sender: stationName); - } + _announcer.SendAnnouncementMessage(alert, "alert-level-announcement", null, detail.Color, null, null, + ("name", name), ("announcement", announcement)); RaiseLocalEvent(new AlertLevelChangedEvent(station, level)); } } -public sealed class AlertLevelDelayFinishedEvent : EntityEventArgs -{} - -public sealed class AlertLevelPrototypeReloadedEvent : EntityEventArgs -{} - -public sealed class AlertLevelChangedEvent : EntityEventArgs +public sealed class AlertLevelDelayFinishedEvent : EntityEventArgs; +public sealed class AlertLevelPrototypeReloadedEvent : EntityEventArgs; +public sealed class AlertLevelChangedEvent(EntityUid station, string alertLevel) : EntityEventArgs { - public EntityUid Station { get; } - public string AlertLevel { get; } - - public AlertLevelChangedEvent(EntityUid station, string alertLevel) - { - Station = station; - AlertLevel = alertLevel; - } + public EntityUid Station { get; } = station; + public string AlertLevel { get; } = alertLevel; } diff --git a/Content.Server/Announcements/AnnounceCommand.cs b/Content.Server/Announcements/AnnounceCommand.cs index cedde3fc140..c7158eac009 100644 --- a/Content.Server/Announcements/AnnounceCommand.cs +++ b/Content.Server/Announcements/AnnounceCommand.cs @@ -1,8 +1,11 @@ +using System.Linq; using Content.Server.Administration; -using Content.Server.Chat; -using Content.Server.Chat.Systems; +using Content.Server.Announcements.Systems; using Content.Shared.Administration; +using Content.Shared.Announcements.Prototypes; using Robust.Shared.Console; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; namespace Content.Server.Announcements { @@ -11,27 +14,77 @@ public sealed class AnnounceCommand : IConsoleCommand { public string Command => "announce"; public string Description => "Send an in-game announcement."; - public string Help => $"{Command} or {Command} to send announcement as CentCom."; + public string Help => $"{Command} "; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var chat = IoCManager.Resolve().GetEntitySystem(); + var announcer = IoCManager.Resolve().GetEntitySystem(); + var proto = IoCManager.Resolve(); - if (args.Length == 0) + switch (args.Length) { - shell.WriteError("Not enough arguments! Need at least 1."); - return; + case 0: + shell.WriteError("Not enough arguments! Need at least 1."); + return; + case 1: + announcer.SendAnnouncement(announcer.GetAnnouncementId("CommandReport"), Filter.Broadcast(), + args[0], "Central Command", Color.Gold); + break; + case 2: + announcer.SendAnnouncement(announcer.GetAnnouncementId("CommandReport"), Filter.Broadcast(), + args[1], args[0], Color.Gold); + break; + case 3: + announcer.SendAnnouncement(announcer.GetAnnouncementId(args[2]), Filter.Broadcast(), args[1], + args[0], Color.Gold); + break; + case 4: + if (!proto.TryIndex(args[3], out AnnouncerPrototype? prototype)) + { + shell.WriteError($"No announcer prototype with ID {args[3]} found!"); + return; + } + announcer.SendAnnouncement(args[2], Filter.Broadcast(), args[1], args[0], Color.Gold, null, + prototype); + break; } - if (args.Length == 1) - { - chat.DispatchGlobalAnnouncement(args[0], colorOverride: Color.Gold); - } - else + shell.WriteLine("Sent!"); + } + + public CompletionResult GetCompletion(IConsoleShell shell, string[] args) + { + switch (args.Length) { - var message = string.Join(' ', new ArraySegment(args, 1, args.Length-1)); - chat.DispatchGlobalAnnouncement(message, args[0], colorOverride: Color.Gold); + case 3: + { + var list = new List(); + + foreach (var prototype in IoCManager.Resolve() + .EnumeratePrototypes() + .SelectMany(p => p.Announcements.Select(a => a.ID))) + { + if (!list.Contains(prototype)) + list.Add(prototype); + } + + return CompletionResult.FromHintOptions(list, Loc.GetString("admin-announce-hint-sound")); + } + case 4: + { + var list = new List(); + + foreach (var prototype in IoCManager.Resolve() + .EnumeratePrototypes()) + { + if (!list.Contains(prototype.ID)) + list.Add(prototype.ID); + } + + return CompletionResult.FromHintOptions(list, Loc.GetString("admin-announce-hint-voice")); + } + default: + return CompletionResult.Empty; } - shell.WriteLine("Sent!"); } } } diff --git a/Content.Server/Announcements/Systems/AnnouncerSystem.Announce.cs b/Content.Server/Announcements/Systems/AnnouncerSystem.Announce.cs new file mode 100644 index 00000000000..d705b86384d --- /dev/null +++ b/Content.Server/Announcements/Systems/AnnouncerSystem.Announce.cs @@ -0,0 +1,96 @@ +using System.Linq; +using Content.Shared.Announcements.Events; +using Content.Shared.Announcements.Prototypes; +using Robust.Shared.Audio; +using Robust.Shared.Player; + +namespace Content.Server.Announcements.Systems; + +public sealed partial class AnnouncerSystem +{ + /// + /// Gets an announcement message from the announcer + /// + /// ID of the announcement from the announcer to get information from + private string? GetAnnouncementMessage(string announcementId) + { + // Get the announcement data from the announcer + // Will be the fallback if the data for the announcementId is not found + var announcementType = Announcer.Announcements.FirstOrDefault(a => a.ID == announcementId) ?? + Announcer.Announcements.First(a => a.ID == "fallback"); + + // Return the announcementType.MessageOverride if it exists, otherwise return null + return announcementType.MessageOverride != null ? Loc.GetString(announcementType.MessageOverride) : null; + } + + + /// + /// Sends an announcement audio + /// + /// ID of the announcement to get information from + /// Who hears the announcement audio + /// Uses this announcer instead of the current global one + public void SendAnnouncementAudio(string announcementId, Filter filter, AnnouncerPrototype? announcerOverride = null) + { + var ev = new AnnouncementSendEvent( + announcerOverride?.ID ?? Announcer.ID, + announcementId, + filter.Recipients.ToList().ConvertAll(p => p.UserId), // I hate this but IEnumerable isn't serializable, and then ICommonSession wasn't, so you get the User ID + GetAudioParams(announcementId, Announcer) ?? AudioParams.Default + ); + + RaiseNetworkEvent(ev); + } + + /// + /// Sends an announcement message + /// + /// ID of the announcement to get information from + /// Text to send in the announcement + /// Who to show as the announcement announcer, defaults to the current announcer's name + /// What color the announcement should be + /// Station ID to send the announcement to + /// Uses this announcer instead of the current global one + /// Locale arguments to pass to the announcement message + public void SendAnnouncementMessage(string announcementId, string locale, string? sender = null, + Color? colorOverride = null, EntityUid? station = null, AnnouncerPrototype? announcerOverride = null, + params (string, object)[] localeArgs) + { + sender ??= Loc.GetString($"announcer-{announcerOverride?.ID ?? Announcer.ID}-name"); + + // If the announcement has a message override, use that instead of the message parameter + if (GetAnnouncementMessage(announcementId, announcerOverride?.ID ?? Announcer.ID, localeArgs) is { } announcementMessage) + locale = announcementMessage; + else + locale = Loc.GetString(locale, localeArgs); + + // Don't send nothing + if (string.IsNullOrEmpty(locale)) + return; + + // If there is a station, send the announcement to the station, otherwise send it to everyone + if (station == null) + _chat.DispatchGlobalAnnouncement(locale, sender, false, colorOverride: colorOverride); + else + _chat.DispatchStationAnnouncement(station.Value, locale, sender, false, colorOverride: colorOverride); + } + + /// + /// Sends an announcement with a message and audio + /// + /// ID of the announcement to get information from + /// Who hears the announcement audio + /// Text to send in the announcement + /// Who to show as the announcement announcer, defaults to the current announcer's name + /// What color the announcement should be + /// Station ID to send the announcement to + /// Uses this announcer instead of the current global one + /// Locale arguments to pass to the announcement message + public void SendAnnouncement(string announcementId, Filter filter, string locale, string? sender = null, + Color? colorOverride = null, EntityUid? station = null, AnnouncerPrototype? announcerOverride = null, + params (string, object)[] localeArgs) + { + SendAnnouncementAudio(announcementId, filter, announcerOverride); + SendAnnouncementMessage(announcementId, locale, sender, colorOverride, station, announcerOverride, localeArgs); + } +} diff --git a/Content.Server/Announcements/Systems/AnnouncerSystem.Announcer.cs b/Content.Server/Announcements/Systems/AnnouncerSystem.Announcer.cs new file mode 100644 index 00000000000..30969361110 --- /dev/null +++ b/Content.Server/Announcements/Systems/AnnouncerSystem.Announcer.cs @@ -0,0 +1,68 @@ +using System.Linq; +using Content.Shared.GameTicking; +using Content.Shared.Announcements.Prototypes; +using Content.Shared.CCVar; +using Content.Shared.Random; +using Content.Shared.Random.Helpers; +using Robust.Shared.Utility; + +namespace Content.Server.Announcements.Systems; + +public sealed partial class AnnouncerSystem +{ + private void OnRoundRestarting(RoundRestartCleanupEvent ev) + { + NewAnnouncer(); + } + + + /// + /// Sets the announcer to a random one or the CVar + /// + private void NewAnnouncer() + { + var announcer = _config.GetCVar(CCVars.Announcer); + if (string.IsNullOrEmpty(announcer) || !_proto.TryIndex(announcer, out _)) + SetAnnouncer(PickAnnouncer()); + else + SetAnnouncer(announcer); + } + + /// + /// Picks a random announcer prototype following blacklists + /// + public AnnouncerPrototype PickAnnouncer() + { + var list = _proto.Index(_config.GetCVar(CCVars.AnnouncerList)); + var blacklist = _config.GetCVar(CCVars.AnnouncerBlacklist).Split(',').Select(a => a.Trim()).ToList(); + var modWeights = list.Weights.Where(a => !blacklist.Contains(a.Key)); + + list = new WeightedRandomPrototype(); + foreach (var (key, value) in modWeights) + list.Weights.Add(key, value); + + return _proto.Index(list.Pick()); + } + + + /// + /// Sets the announcer + /// + /// ID of the announcer to choose + public void SetAnnouncer(string announcerId) + { + if (!_proto.TryIndex(announcerId, out var announcer)) + DebugTools.Assert($"Set announcer {announcerId} does not exist, attempting to use previously set one."); + else + Announcer = announcer; + } + + /// + /// Sets the announcer + /// + /// The announcer prototype to set the current announcer to + public void SetAnnouncer(AnnouncerPrototype announcer) + { + Announcer = announcer; + } +} diff --git a/Content.Server/Announcements/Systems/AnnouncerSystem.cs b/Content.Server/Announcements/Systems/AnnouncerSystem.cs new file mode 100644 index 00000000000..1770a2ce937 --- /dev/null +++ b/Content.Server/Announcements/Systems/AnnouncerSystem.cs @@ -0,0 +1,33 @@ +using Content.Server.Chat.Systems; +using Content.Shared.GameTicking; +using Content.Shared.Announcements.Prototypes; +using Content.Shared.Announcements.Systems; +using Content.Shared.CCVar; +using Robust.Shared.Configuration; +using Robust.Shared.Prototypes; + +namespace Content.Server.Announcements.Systems; + +public sealed partial class AnnouncerSystem : SharedAnnouncerSystem +{ + [Dependency] private readonly IConfigurationManager _config = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly ChatSystem _chat = default!; + + /// + /// The currently selected announcer + /// + [Access(typeof(AnnouncerSystem))] + public AnnouncerPrototype Announcer { get; set; } = default!; + + + public override void Initialize() + { + base.Initialize(); + NewAnnouncer(); + + _config.OnValueChanged(CCVars.Announcer, _ => NewAnnouncer()); + + SubscribeLocalEvent(OnRoundRestarting); + } +} 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 131bd4b1829..49021c142f4 100644 --- a/Content.Server/Bed/BedSystem.cs +++ b/Content.Server/Bed/BedSystem.cs @@ -93,9 +93,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) @@ -121,9 +120,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..066bf0a1c5b 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,15 +183,18 @@ 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, - EntityManager, null, scale); + var actualEntity = ent.Comp2?.Body ?? solutionEntityUid.Value; + var ev = new TryMetabolizeReagent(reagent, proto, quantity); + RaiseLocalEvent(actualEntity, ref ev); + + var args = new ReagentEffectArgs(actualEntity, ent, solution, proto, mostToRemove, + EntityManager, null, scale * ev.Scale, ev.QuantityMultiplier); // do all effects, if conditions apply foreach (var effect in entry.Effects) @@ -187,8 +204,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 +232,28 @@ 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; } } + +[ByRefEvent] +public record struct TryMetabolizeReagent(ReagentId Reagent, ReagentPrototype Prototype, FixedPoint2 Quantity, float Scale = 1f, float QuantityMultiplier = 1f); 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/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/AnnounceOnSpawnSystem.cs b/Content.Server/Chat/Systems/AnnounceOnSpawnSystem.cs index 0f0365e56ba..11dcf42dfd7 100644 --- a/Content.Server/Chat/Systems/AnnounceOnSpawnSystem.cs +++ b/Content.Server/Chat/Systems/AnnounceOnSpawnSystem.cs @@ -1,10 +1,13 @@ using Content.Server.Chat; +using Content.Server.Announcements.Systems; +using Robust.Shared.Player; namespace Content.Server.Chat.Systems; public sealed class AnnounceOnSpawnSystem : EntitySystem { [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; public override void Initialize() { @@ -15,8 +18,8 @@ public override void Initialize() private void OnInit(EntityUid uid, AnnounceOnSpawnComponent comp, MapInitEvent args) { - var message = Loc.GetString(comp.Message); var sender = comp.Sender != null ? Loc.GetString(comp.Sender) : "Central Command"; - _chat.DispatchGlobalAnnouncement(message, sender, playSound: true, comp.Sound, comp.Color); + _announcer.SendAnnouncement(_announcer.GetAnnouncementId("SpawnAnnounceCaptain"), Filter.Broadcast(), + comp.Message, sender, comp.Color); } } 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/Chemistry/ReagentEffectConditions/ReagentThreshold.cs b/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs index 664569d7be4..50be89581fb 100644 --- a/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs +++ b/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs @@ -31,7 +31,7 @@ public override bool Condition(ReagentEffectArgs args) var quant = FixedPoint2.Zero; if (args.Source != null) - quant = args.Source.GetTotalPrototypeQuantity(reagent); + quant = args.Source.GetTotalPrototypeQuantity(reagent) * args.QuantityMultiplier; return quant >= Min && quant <= Max; } diff --git a/Content.Server/Chemistry/ReagentEffects/Drunk.cs b/Content.Server/Chemistry/ReagentEffects/Drunk.cs index dbce995ca2e..6088db5787c 100644 --- a/Content.Server/Chemistry/ReagentEffects/Drunk.cs +++ b/Content.Server/Chemistry/ReagentEffects/Drunk.cs @@ -25,7 +25,7 @@ public override void Effect(ReagentEffectArgs args) { var boozePower = BoozePower; - boozePower *= args.Scale; + boozePower *= Math.Max(args.Scale, 1); var drunkSys = args.EntityManager.EntitySysManager.GetEntitySystem(); drunkSys.TryApplyDrunkenness(args.SolutionEntity, boozePower, SlurSpeech); diff --git a/Content.Server/Communications/CommsHackerSystem.cs b/Content.Server/Communications/CommsHackerSystem.cs index 1248d214003..bbe64a7987a 100644 --- a/Content.Server/Communications/CommsHackerSystem.cs +++ b/Content.Server/Communications/CommsHackerSystem.cs @@ -9,6 +9,8 @@ using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization; +using Content.Server.Announcements.Systems; +using Robust.Shared.Player; namespace Content.Server.Communications; @@ -21,6 +23,7 @@ public sealed class CommsHackerSystem : SharedCommsHackerSystem // TODO: remove when generic check event is used [Dependency] private readonly NinjaGlovesSystem _gloves = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; public override void Initialize() { @@ -79,7 +82,8 @@ private void OnDoAfter(EntityUid uid, CommsHackerComponent comp, TerrorDoAfterEv public void CallInThreat(NinjaHackingThreatPrototype ninjaHackingThreat) { _gameTicker.StartGameRule(ninjaHackingThreat.Rule, out _); - _chat.DispatchGlobalAnnouncement(Loc.GetString(ninjaHackingThreat.Announcement), playSound: true, colorOverride: Color.Red); + _announcer.SendAnnouncement(_announcer.GetAnnouncementId("NinjaHacking"), Filter.Broadcast(), + ninjaHackingThreat.Announcement, colorOverride: Color.Red); } } diff --git a/Content.Server/Communications/CommunicationsConsoleSystem.cs b/Content.Server/Communications/CommunicationsConsoleSystem.cs index 6b745c8cd95..17573c3da64 100644 --- a/Content.Server/Communications/CommunicationsConsoleSystem.cs +++ b/Content.Server/Communications/CommunicationsConsoleSystem.cs @@ -24,6 +24,9 @@ using Content.Shared.Popups; using Robust.Server.GameObjects; using Robust.Shared.Configuration; +using Content.Server.Announcements.Systems; +using Robust.Shared.Player; +using Content.Server.Station.Components; namespace Content.Server.Communications { @@ -42,6 +45,7 @@ public sealed class CommunicationsConsoleSystem : EntitySystem [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; private const float UIUpdateInterval = 5.0f; @@ -276,17 +280,19 @@ private void OnAnnounceMessage(EntityUid uid, CommunicationsConsoleComponent com Loc.TryGetString(comp.Title, out var title); title ??= comp.Title; - msg += "\n" + Loc.GetString("comms-console-announcement-sent-by") + " " + author; + msg += $"\n{Loc.GetString("comms-console-announcement-sent-by")} {author}"; if (comp.Global) { - _chatSystem.DispatchGlobalAnnouncement(msg, title, announcementSound: comp.Sound, colorOverride: comp.Color); + _announcer.SendAnnouncement("announce", Filter.Broadcast(), msg, title, comp.Color); if (message.Session.AttachedEntity != null) _adminLogger.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(message.Session.AttachedEntity.Value):player} has sent the following global announcement: {msg}"); return; } - _chatSystem.DispatchStationAnnouncement(uid, msg, title, colorOverride: comp.Color); + if (TryComp(_stationSystem.GetOwningStation(uid), out var stationData)) + _announcer.SendAnnouncement("announce", _stationSystem.GetInStation(stationData), msg, title, + comp.Color); if (message.Session.AttachedEntity != null) _adminLogger.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(message.Session.AttachedEntity.Value):player} has sent the following station announcement: {msg}"); 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/DragonRiftSystem.cs b/Content.Server/Dragon/DragonRiftSystem.cs index f7d5cd783d4..819e7f833f3 100644 --- a/Content.Server/Dragon/DragonRiftSystem.cs +++ b/Content.Server/Dragon/DragonRiftSystem.cs @@ -13,6 +13,7 @@ using System.Numerics; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; +using Content.Server.Announcements.Systems; namespace Content.Server.Dragon; @@ -27,6 +28,7 @@ public sealed class DragonRiftSystem : EntitySystem [Dependency] private readonly NavMapSystem _navMap = default!; [Dependency] private readonly NPCSystem _npc = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; public override void Initialize() { @@ -70,7 +72,8 @@ public override void Update(float frameTime) Dirty(comp); var location = xform.LocalPosition; - _chat.DispatchGlobalAnnouncement(Loc.GetString("carp-rift-warning", ("location", location)), playSound: false, colorOverride: Color.Red); + _announcer.SendAnnouncement(_announcer.GetAnnouncementId("CarpRift"), Filter.Broadcast(), + "carp-rift-warning", colorOverride: Color.Red, localeArgs: ("location", location)); _audio.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true); _navMap.SetBeaconEnabled(uid, true); } 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/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/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index 6e02c4ac404..a154765f34b 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -19,6 +19,7 @@ using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Utility; +using Content.Server.Announcements.Systems; namespace Content.Server.GameTicking { @@ -26,6 +27,7 @@ public sealed partial class GameTicker { [Dependency] private readonly DiscordWebhook _discord = default!; [Dependency] private readonly ITaskManager _taskManager = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; private static readonly Counter RoundNumberMetric = Metrics.CreateCounter( "ss14_round_number", @@ -601,11 +603,8 @@ private void AnnounceRound() var proto = _robustRandom.Pick(options); - if (proto.Message != null) - _chatSystem.DispatchGlobalAnnouncement(Loc.GetString(proto.Message), playSound: true); - - if (proto.Sound != null) - _audio.PlayGlobal(proto.Sound, Filter.Broadcast(), true); + _announcer.SendAnnouncement(_announcer.GetAnnouncementId(proto.ID), Filter.Broadcast(), + proto.Message ?? "game-ticker-welcome-to-the-station"); } private async void SendRoundStartedDiscordMessage() 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/GameTicking/Rules/ZombieRuleSystem.cs b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs index 0b7cb9cf8f7..edeafb45847 100644 --- a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs @@ -22,6 +22,7 @@ using Robust.Shared.Random; using Robust.Shared.Timing; using System.Globalization; +using Content.Server.Announcements.Systems; namespace Content.Server.GameTicking.Rules; @@ -41,6 +42,7 @@ public sealed class ZombieRuleSystem : GameRuleSystem [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly AntagSelectionSystem _antagSelection = default!; [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; public override void Initialize() { @@ -123,7 +125,9 @@ private void CheckRoundEnd(ZombieRuleComponent zombieRuleComponent) { foreach (var station in _station.GetStations()) { - _chat.DispatchStationAnnouncement(station, Loc.GetString("zombie-shuttle-call"), colorOverride: Color.Crimson); + _announcer.SendAnnouncement(_announcer.GetAnnouncementId("ShuttleCalled"), + _station.GetInOwningStation(station), "zombie-shuttle-call", + colorOverride: Color.Crimson); } _roundEnd.RequestRoundEnd(null, false); } @@ -249,7 +253,7 @@ private void InfectInitialPlayers(ZombieRuleComponent component) _playerManager.Sessions, component.PatientZeroPrototypeId, includeAllJobs: false, - customExcludeCondition: player => HasComp(player) || HasComp(player) + customExcludeCondition: player => HasComp(player) || HasComp(player) ); //And get all players, excluding ZombieImmune and roles with CanBeAntag = False - to fill any leftover initial infected slots @@ -259,7 +263,7 @@ private void InfectInitialPlayers(ZombieRuleComponent component) acceptableAntags: Shared.Antag.AntagAcceptability.All, includeAllJobs: false , ignorePreferences: true, - customExcludeCondition: HasComp + customExcludeCondition: HasComp ); //If there are no players to choose, abort 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 50736e9bd21..e836b658381 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -95,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); @@ -113,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); 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/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/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/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 f28731be54b..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!; 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/NukeCodePaperSystem.cs b/Content.Server/Nuke/NukeCodePaperSystem.cs index 8df25feebfa..36268d5648d 100644 --- a/Content.Server/Nuke/NukeCodePaperSystem.cs +++ b/Content.Server/Nuke/NukeCodePaperSystem.cs @@ -7,6 +7,8 @@ using Content.Shared.Paper; using Robust.Shared.Random; using Robust.Shared.Utility; +using Content.Server.Announcements.Systems; +using Robust.Shared.Player; namespace Content.Server.Nuke { @@ -17,6 +19,7 @@ public sealed class NukeCodePaperSystem : EntitySystem [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly PaperSystem _paper = default!; [Dependency] private readonly FaxSystem _faxSystem = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; public override void Initialize() { @@ -57,9 +60,7 @@ public bool SendNukeCodes(EntityUid station) while (faxes.MoveNext(out var faxEnt, out var fax)) { if (!fax.ReceiveNukeCodes || !TryGetRelativeNukeCode(faxEnt, out var paperContent, station)) - { continue; - } var printout = new FaxPrintout( paperContent, @@ -77,10 +78,8 @@ public bool SendNukeCodes(EntityUid station) } if (wasSent) - { - var msg = Loc.GetString("nuke-component-announcement-send-codes"); - _chatSystem.DispatchStationAnnouncement(station, msg, colorOverride: Color.Red); - } + _announcer.SendAnnouncement(_announcer.GetAnnouncementId("NukeCodes"), Filter.Broadcast(), + "nuke-component-announcement-send-codes", colorOverride: Color.Red); return wasSent; } diff --git a/Content.Server/Nuke/NukeSystem.cs b/Content.Server/Nuke/NukeSystem.cs index d6767cd2de8..3a1331774d8 100644 --- a/Content.Server/Nuke/NukeSystem.cs +++ b/Content.Server/Nuke/NukeSystem.cs @@ -18,8 +18,10 @@ 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; +using Content.Server.Announcements.Systems; namespace Content.Server.Nuke; @@ -28,7 +30,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!; @@ -42,6 +43,7 @@ public sealed class NukeSystem : EntitySystem [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; /// /// Used to calculate when the nuke song should start playing for maximum kino with the nuke sfx @@ -175,7 +177,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); @@ -461,11 +463,15 @@ public void ArmBomb(EntityUid uid, NukeComponent? component = null) // We are collapsing the randomness here, otherwise we would get separate random song picks for checking duration and when actually playing the song afterwards _selectedNukeSong = _audio.GetSound(component.ArmMusic); - // warn a crew - var announcement = Loc.GetString("nuke-component-announcement-armed", - ("time", (int) component.RemainingTime), ("position", posText)); - var sender = Loc.GetString("nuke-component-announcement-sender"); - _chatSystem.DispatchStationAnnouncement(stationUid ?? uid, announcement, sender, false, null, Color.Red); + _announcer.SendAnnouncementMessage( + _announcer.GetAnnouncementId("NukeArm"), + "nuke-component-announcement-armed", + Loc.GetString("nuke-component-announcement-sender"), + Color.Red, + stationUid ?? uid, + null, + ("time", (int) component.RemainingTime), ("position", posText) + ); _sound.PlayGlobalOnStation(uid, _audio.GetSound(component.ArmSound)); _nukeSongLength = (float) _audio.GetAudioLength(_selectedNukeSong).TotalSeconds; @@ -502,10 +508,12 @@ public void DisarmBomb(EntityUid uid, NukeComponent? component = null) if (stationUid != null) _alertLevel.SetLevel(stationUid.Value, component.AlertLevelOnDeactivate, true, true, true); - // warn a crew - var announcement = Loc.GetString("nuke-component-announcement-unarmed"); - var sender = Loc.GetString("nuke-component-announcement-sender"); - _chatSystem.DispatchStationAnnouncement(uid, announcement, sender, false); + _announcer.SendAnnouncementMessage( + _announcer.GetAnnouncementId("NukeDisarm"), + "nuke-component-announcement-unarmed", + Loc.GetString("nuke-component-announcement-sender"), + station: stationUid ?? uid + ); component.PlayedNukeSong = false; _sound.PlayGlobalOnStation(uid, _audio.GetSound(component.DisarmSound)); @@ -647,4 +655,3 @@ public sealed class NukeDisarmSuccessEvent : EntityEventArgs { } - diff --git a/Content.Server/NukeOps/WarDeclaratorSystem.cs b/Content.Server/NukeOps/WarDeclaratorSystem.cs index bcc0b9c0ea6..dee0c10c40e 100644 --- a/Content.Server/NukeOps/WarDeclaratorSystem.cs +++ b/Content.Server/NukeOps/WarDeclaratorSystem.cs @@ -10,6 +10,8 @@ using Robust.Server.GameObjects; using Robust.Shared.Configuration; using Robust.Shared.Timing; +using Content.Server.Announcements.Systems; +using Robust.Shared.Player; namespace Content.Server.NukeOps; @@ -25,6 +27,7 @@ public sealed class WarDeclaratorSystem : EntitySystem [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; public override void Initialize() { @@ -74,7 +77,7 @@ private void OnActivated(Entity ent, ref WarDeclaratorAc if (ev.Status == WarConditionStatus.WarReady) { var title = Loc.GetString(ent.Comp.SenderTitle); - _chat.DispatchGlobalAnnouncement(ent.Comp.Message, title, true, ent.Comp.Sound, ent.Comp.Color); + _announcer.SendAnnouncement("war", Filter.Broadcast(), ent.Comp.Message, title, ent.Comp.Color); _adminLogger.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(playerEntity):player} has declared war with this text: {ent.Comp.Message}"); } diff --git a/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs b/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs index dc848ccf9f1..abb6f393ac5 100644 --- a/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs @@ -6,7 +6,6 @@ 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; @@ -20,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() diff --git a/Content.Server/Nyanotrasen/Item/ShockCollar/ShockCollarComponent.cs b/Content.Server/Nyanotrasen/Item/ShockCollar/ShockCollarComponent.cs index 781f2d00030..1dcbc48676a 100644 --- a/Content.Server/Nyanotrasen/Item/ShockCollar/ShockCollarComponent.cs +++ b/Content.Server/Nyanotrasen/Item/ShockCollar/ShockCollarComponent.cs @@ -2,5 +2,11 @@ namespace Content.Server.ShockCollar; [RegisterComponent] public sealed partial class ShockCollarComponent : Component -{} +{ + [DataField] + public int ShockDamage = 1; + + [DataField] + public TimeSpan ShockTime = TimeSpan.FromSeconds(2); +} diff --git a/Content.Server/Nyanotrasen/Item/ShockCollar/ShockCollarSystem.cs b/Content.Server/Nyanotrasen/Item/ShockCollar/ShockCollarSystem.cs index 4b24f45401a..eeb0f59d42a 100644 --- a/Content.Server/Nyanotrasen/Item/ShockCollar/ShockCollarSystem.cs +++ b/Content.Server/Nyanotrasen/Item/ShockCollar/ShockCollarSystem.cs @@ -33,7 +33,7 @@ private void OnTrigger(EntityUid uid, ShockCollarComponent component, TriggerEve && !_useDelay.TryResetDelay((uid, useDelay), true)) return; - _electrocutionSystem.TryDoElectrocution(containerEnt, null, 5, TimeSpan.FromSeconds(2), true); + _electrocutionSystem.TryDoElectrocution(containerEnt, null, component.ShockDamage, component.ShockTime, true, ignoreInsulation: true); } } 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/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/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/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/PowerSink/PowerSinkSystem.cs b/Content.Server/PowerSink/PowerSinkSystem.cs index 298e35db469..903cb7ce12e 100644 --- a/Content.Server/PowerSink/PowerSinkSystem.cs +++ b/Content.Server/PowerSink/PowerSinkSystem.cs @@ -8,6 +8,7 @@ using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Content.Server.Power.EntitySystems; +using Content.Server.Announcements.Systems; namespace Content.Server.PowerSink { @@ -33,6 +34,7 @@ public sealed class PowerSinkSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly BatterySystem _battery = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; public override void Initialize() { @@ -126,12 +128,9 @@ private void NotifyStationOfImminentExplosion(EntityUid uid, PowerSinkComponent if (station == null) return; - _chat.DispatchStationAnnouncement( - station.Value, - Loc.GetString("powersink-immiment-explosion-announcement"), - playDefaultSound: true, - colorOverride: Color.Yellow - ); + _announcer.SendAnnouncement(_announcer.GetAnnouncementId("PowerSinkExplosion"), + _station.GetInOwningStation(station.Value), "powersink-immiment-explosion-announcement", + colorOverride: Color.Yellow, station: station.Value); } } } 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/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/RoundEnd/RoundEndSystem.cs b/Content.Server/RoundEnd/RoundEndSystem.cs index 3a8331f3f7a..d79fe2d2cca 100644 --- a/Content.Server/RoundEnd/RoundEndSystem.cs +++ b/Content.Server/RoundEnd/RoundEndSystem.cs @@ -22,6 +22,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Timing; using Timer = Robust.Shared.Timing.Timer; +using Content.Server.Announcements.Systems; namespace Content.Server.RoundEnd { @@ -42,6 +43,7 @@ public sealed class RoundEndSystem : EntitySystem [Dependency] private readonly EmergencyShuttleSystem _shuttle = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly StationSystem _stationSystem = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; public TimeSpan DefaultCooldownDuration { get; set; } = TimeSpan.FromSeconds(30); @@ -176,15 +178,16 @@ public void RequestRoundEnd(TimeSpan countdownTime, EntityUid? requester = null, units = "eta-units-minutes"; } - _chatSystem.DispatchGlobalAnnouncement(Loc.GetString(text, - ("time", time), - ("units", Loc.GetString(units))), + _announcer.SendAnnouncement(_announcer.GetAnnouncementId("ShuttleCalled"), + Filter.Broadcast(), + text, name, - false, + Color.Gold, null, - Color.Gold); - - _audio.PlayGlobal("/Audio/Announcements/shuttlecalled.ogg", Filter.Broadcast(), true); + null, + ("time", time), + ("units", Loc.GetString(units)) + ); LastCountdownStart = _gameTiming.CurTime; ExpectedCountdownEnd = _gameTiming.CurTime + countdownTime; @@ -227,10 +230,13 @@ public void CancelRoundEndCountdown(EntityUid? requester = null, bool checkCoold _adminLogger.Add(LogType.ShuttleRecalled, LogImpact.High, $"Shuttle recalled"); } - _chatSystem.DispatchGlobalAnnouncement(Loc.GetString("round-end-system-shuttle-recalled-announcement"), - Loc.GetString("Station"), false, colorOverride: Color.Gold); - - _audio.PlayGlobal("/Audio/Announcements/shuttlerecalled.ogg", Filter.Broadcast(), true); + _announcer.SendAnnouncement( + _announcer.GetAnnouncementId("ShuttleRecalled"), + Filter.Broadcast(), + "round-end-system-shuttle-recalled-announcement", + Loc.GetString("Station"), + Color.Gold + ); LastCountdownStart = null; ExpectedCountdownEnd = null; @@ -309,9 +315,13 @@ public void DoRoundEndBehavior(RoundEndBehavior behavior, // Check is shuttle called or not. We should only dispatch announcement if it's already called if (IsRoundEndRequested()) { - _chatSystem.DispatchGlobalAnnouncement(Loc.GetString(textAnnounce), + _announcer.SendAnnouncement( + _announcer.GetAnnouncementId("ShuttleCalled"), + Filter.Broadcast(), + textAnnounce, Loc.GetString(sender), - colorOverride: Color.Gold); + Color.Gold + ); } else { 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.Console.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs index aeb2ebdbba1..3f6eafb454c 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.Console.cs @@ -209,7 +209,13 @@ private void UpdateEmergencyConsole(float frameTime) if (!ShuttlesLeft && _consoleAccumulator <= 0f) { ShuttlesLeft = true; - _chatSystem.DispatchGlobalAnnouncement(Loc.GetString("emergency-shuttle-left", ("transitTime", $"{TransitTime:0}"))); + _announcer.SendAnnouncement( + _announcer.GetAnnouncementId("ShuttleLeft"), + Filter.Broadcast(), + "emergency-shuttle-left", + null, null, null, null, + ("transitTime", $"{TransitTime:0}") + ); Timer.Spawn((int) (TransitTime * 1000) + _bufferTime.Milliseconds, () => _roundEnd.EndRound(), _roundEndCancelToken?.Token ?? default); } @@ -248,7 +254,13 @@ private void OnEmergencyRepealAll(EntityUid uid, EmergencyShuttleConsoleComponen return; _logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle early launch REPEAL ALL by {args.Session:user}"); - _chatSystem.DispatchGlobalAnnouncement(Loc.GetString("emergency-shuttle-console-auth-revoked", ("remaining", component.AuthorizationsRequired))); + _announcer.SendAnnouncement( + _announcer.GetAnnouncementId("ShuttleAuthRevoked"), + Filter.Broadcast(), + "emergency-shuttle-console-auth-revoked", + null, null, null, null, + ("remaining", component.AuthorizationsRequired) + ); component.AuthorizedEntities.Clear(); UpdateAllEmergencyConsoles(); } @@ -271,7 +283,13 @@ private void OnEmergencyRepeal(EntityUid uid, EmergencyShuttleConsoleComponent c _logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle early launch REPEAL by {args.Session:user}"); var remaining = component.AuthorizationsRequired - component.AuthorizedEntities.Count; - _chatSystem.DispatchGlobalAnnouncement(Loc.GetString("emergency-shuttle-console-auth-revoked", ("remaining", remaining))); + _announcer.SendAnnouncement( + _announcer.GetAnnouncementId("ShuttleAuthRevoked"), + Filter.Broadcast(), + "emergency-shuttle-console-auth-revoked", + null, null, null, null, + ("remaining", remaining) + ); CheckForLaunch(component); UpdateAllEmergencyConsoles(); } @@ -296,9 +314,14 @@ private void OnEmergencyAuthorize(EntityUid uid, EmergencyShuttleConsoleComponen var remaining = component.AuthorizationsRequired - component.AuthorizedEntities.Count; if (remaining > 0) - _chatSystem.DispatchGlobalAnnouncement( - Loc.GetString("emergency-shuttle-console-auth-left", ("remaining", remaining)), - playSound: false, colorOverride: DangerColor); + _announcer.SendAnnouncement(_announcer.GetAnnouncementId("ShuttleAuthAdded"), + Filter.Broadcast(), + "emergency-shuttle-console-auth-left", + null, + DangerColor, + null, null, + ("remaining", remaining) + ); if (!CheckForLaunch(component)) _audio.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), recordReplay: true); @@ -399,12 +422,13 @@ private void AnnounceLaunch() if (_announced) return; _announced = true; - _chatSystem.DispatchGlobalAnnouncement( - Loc.GetString("emergency-shuttle-launch-time", ("consoleAccumulator", $"{_consoleAccumulator:0}")), - playSound: false, - colorOverride: DangerColor); - - _audio.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), recordReplay: true); + _announcer.SendAnnouncement( + _announcer.GetAnnouncementId("ShuttleAlmostLaunching"), + Filter.Broadcast(), + "emergency-shuttle-launch-time", + null, null, null, null, + ("consoleAccumulator", $"{_consoleAccumulator:0}") + ); } public bool DelayEmergencyRoundEnd() diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index f8d995b8a4b..5cbc10e4f60 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -33,6 +33,7 @@ using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Timing; +using Content.Server.Announcements.Systems; namespace Content.Server.Shuttles.Systems; @@ -63,6 +64,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; private const float ShuttleSpawnBuffer = 1f; @@ -268,9 +270,8 @@ public void CallEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleCo if (targetGrid == null) { _logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} unable to dock with station {ToPrettyString(stationUid)}"); - _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-good-luck"), playDefaultSound: false); - // TODO: Need filter extensions or something don't blame me. - _audio.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true); + _announcer.SendAnnouncement(_announcer.GetAnnouncementId("ShuttleGoodLuck"), Filter.Broadcast(), + "emergency-shuttle-good-luck", colorOverride: DangerColor); return; } @@ -281,7 +282,13 @@ public void CallEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleCo if (TryComp(targetGrid.Value, out var targetXform)) { var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery); - _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-docked", ("time", $"{_consoleAccumulator:0}"), ("direction", angle.GetDir())), playDefaultSound: false); + _announcer.SendAnnouncementMessage( + _announcer.GetAnnouncementId("ShuttleDock"), + "emergency-shuttle-docked", + null, null, null, null, + ("time", $"{_consoleAccumulator:0}"), + ("direction", angle.GetDir()) + ); } // shuttle timers @@ -302,20 +309,23 @@ public void CallEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleCo } _logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} docked with stations"); - // TODO: Need filter extensions or something don't blame me. - _audio.PlayGlobal("/Audio/Announcements/shuttle_dock.ogg", Filter.Broadcast(), true); + _announcer.SendAnnouncementAudio(_announcer.GetAnnouncementId("ShuttleDock"), Filter.Broadcast()); } else { if (TryComp(targetGrid.Value, out var targetXform)) { var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery); - _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-nearby", ("direction", angle.GetDir())), playDefaultSound: false); + _announcer.SendAnnouncementMessage( + _announcer.GetAnnouncementId("ShuttleNearby"), + "emergency-shuttle-nearby", + null, null, null, null, + ("direction", angle.GetDir()) + ); } _logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} unable to find a valid docking port for {ToPrettyString(stationUid)}"); - // TODO: Need filter extensions or something don't blame me. - _audio.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true); + _announcer.SendAnnouncementAudio(_announcer.GetAnnouncementId("ShuttleNearby"), Filter.Broadcast()); } } @@ -492,7 +502,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 7784db015d3..c74a3c49d63 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/Components/StationEventComponent.cs b/Content.Server/StationEvents/Components/StationEventComponent.cs index b4456a4b5b7..980034aa7b7 100644 --- a/Content.Server/StationEvents/Components/StationEventComponent.cs +++ b/Content.Server/StationEvents/Components/StationEventComponent.cs @@ -19,16 +19,10 @@ public sealed partial class StationEventComponent : Component public float Weight = WeightNormal; [DataField("startAnnouncement")] - public string? StartAnnouncement; + public bool StartAnnouncement; [DataField("endAnnouncement")] - public string? EndAnnouncement; - - [DataField("startAudio")] - public SoundSpecifier? StartAudio; - - [DataField("endAudio")] - public SoundSpecifier? EndAudio; + public bool EndAnnouncement; /// /// In minutes, when is the first round time this event can start diff --git a/Content.Server/StationEvents/Events/AnomalySpawnRule.cs b/Content.Server/StationEvents/Events/AnomalySpawnRule.cs index 48a3b900c44..4cd94d3e719 100644 --- a/Content.Server/StationEvents/Events/AnomalySpawnRule.cs +++ b/Content.Server/StationEvents/Events/AnomalySpawnRule.cs @@ -2,20 +2,29 @@ using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; +using Content.Server.Announcements.Systems; +using Robust.Shared.Player; namespace Content.Server.StationEvents.Events; public sealed class AnomalySpawnRule : StationEventSystem { [Dependency] private readonly AnomalySystem _anomaly = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; protected override void Added(EntityUid uid, AnomalySpawnRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) { base.Added(uid, component, gameRule, args); - var str = Loc.GetString("anomaly-spawn-event-announcement", - ("sighting", Loc.GetString($"anomaly-spawn-sighting-{RobustRandom.Next(1, 6)}"))); - ChatSystem.DispatchGlobalAnnouncement(str, colorOverride: Color.FromHex("#18abf5")); + _announcer.SendAnnouncement( + _announcer.GetAnnouncementId(args.RuleId), + Filter.Broadcast(), + "anomaly-spawn-event-announcement", + null, + Color.FromHex("#18abf5"), + null, null, + ("sighting", Loc.GetString($"anomaly-spawn-sighting-{RobustRandom.Next(1, 6)}")) + ); } protected override void Started(EntityUid uid, AnomalySpawnRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) diff --git a/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs b/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs index 0eed77f1543..b25c1d6561c 100644 --- a/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs +++ b/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs @@ -1,18 +1,28 @@ using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Robust.Shared.Random; +using Content.Server.Announcements.Systems; +using Robust.Shared.Player; namespace Content.Server.StationEvents.Events; public sealed class BluespaceArtifactRule : StationEventSystem { + [Dependency] private readonly AnnouncerSystem _announcer = default!; + protected override void Added(EntityUid uid, BluespaceArtifactRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) { base.Added(uid, component, gameRule, args); - var str = Loc.GetString("bluespace-artifact-event-announcement", - ("sighting", Loc.GetString(RobustRandom.Pick(component.PossibleSighting)))); - ChatSystem.DispatchGlobalAnnouncement(str, colorOverride: Color.FromHex("#18abf5")); + _announcer.SendAnnouncement( + _announcer.GetAnnouncementId(args.RuleId), + Filter.Broadcast(), + "bluespace-artifact-event-announcement", + null, + Color.FromHex("#18abf5"), + null, null, + ("sighting", Loc.GetString(RobustRandom.Pick(component.PossibleSighting))) + ); } protected override void Started(EntityUid uid, BluespaceArtifactRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) diff --git a/Content.Server/StationEvents/Events/BreakerFlipRule.cs b/Content.Server/StationEvents/Events/BreakerFlipRule.cs index 494779fe350..b465cc1f556 100644 --- a/Content.Server/StationEvents/Events/BreakerFlipRule.cs +++ b/Content.Server/StationEvents/Events/BreakerFlipRule.cs @@ -4,6 +4,8 @@ using Content.Server.Station.Components; using Content.Server.StationEvents.Components; using JetBrains.Annotations; +using Content.Server.Announcements.Systems; +using Robust.Shared.Player; namespace Content.Server.StationEvents.Events; @@ -11,13 +13,21 @@ namespace Content.Server.StationEvents.Events; public sealed class BreakerFlipRule : StationEventSystem { [Dependency] private readonly ApcSystem _apcSystem = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; protected override void Added(EntityUid uid, BreakerFlipRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) { base.Added(uid, component, gameRule, args); - var str = Loc.GetString("station-event-breaker-flip-announcement", ("data", Loc.GetString(Loc.GetString($"random-sentience-event-data-{RobustRandom.Next(1, 6)}")))); - ChatSystem.DispatchGlobalAnnouncement(str, playSound: false, colorOverride: Color.Gold); + _announcer.SendAnnouncement( + _announcer.GetAnnouncementId(args.RuleId), + Filter.Broadcast(), + "station-event-breaker-flip-announcement", + null, + Color.Gold, + null, null, + ("data", Loc.GetString(Loc.GetString($"random-sentience-event-data-{RobustRandom.Next(1, 6)}"))) + ); } protected override void Started(EntityUid uid, BreakerFlipRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) diff --git a/Content.Server/StationEvents/Events/CargoGiftsRule.cs b/Content.Server/StationEvents/Events/CargoGiftsRule.cs index c174cc48c09..80af23c6fa4 100644 --- a/Content.Server/StationEvents/Events/CargoGiftsRule.cs +++ b/Content.Server/StationEvents/Events/CargoGiftsRule.cs @@ -6,6 +6,8 @@ using Content.Server.Station.Components; using Content.Server.StationEvents.Components; using Robust.Shared.Prototypes; +using Content.Server.Announcements.Systems; +using Robust.Shared.Player; namespace Content.Server.StationEvents.Events; @@ -14,14 +16,23 @@ public sealed class CargoGiftsRule : StationEventSystem [Dependency] private readonly CargoSystem _cargoSystem = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly GameTicker _ticker = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; protected override void Added(EntityUid uid, CargoGiftsRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) { base.Added(uid, component, gameRule, args); - var str = Loc.GetString(component.Announce, - ("sender", Loc.GetString(component.Sender)), ("description", Loc.GetString(component.Description)), ("dest", Loc.GetString(component.Dest))); - ChatSystem.DispatchGlobalAnnouncement(str, colorOverride: Color.FromHex("#18abf5")); + _announcer.SendAnnouncement( + _announcer.GetAnnouncementId(args.RuleId), + Filter.Broadcast(), + component.Announce, + null, + Color.FromHex("#18abf5"), + null, null, + ("sender", Loc.GetString(component.Sender)), + ("description", Loc.GetString(component.Description)), + ("dest", Loc.GetString(component.Dest)) + ); } /// diff --git a/Content.Server/StationEvents/Events/FalseAlarmRule.cs b/Content.Server/StationEvents/Events/FalseAlarmRule.cs index 05e9435b40a..e971a0aa982 100644 --- a/Content.Server/StationEvents/Events/FalseAlarmRule.cs +++ b/Content.Server/StationEvents/Events/FalseAlarmRule.cs @@ -4,6 +4,7 @@ using JetBrains.Annotations; using Robust.Shared.Player; using Robust.Shared.Random; +using Content.Server.Announcements.Systems; namespace Content.Server.StationEvents.Events; @@ -11,18 +12,20 @@ namespace Content.Server.StationEvents.Events; public sealed class FalseAlarmRule : StationEventSystem { [Dependency] private readonly EventManagerSystem _event = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; protected override void Started(EntityUid uid, FalseAlarmRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { base.Started(uid, component, gameRule, args); - var allEv = _event.AllEvents().Select(p => p.Value).ToList(); + var allEv = _event.AllEvents().Select(p => p.Key).ToList(); var picked = RobustRandom.Pick(allEv); - if (picked.StartAnnouncement != null) - { - ChatSystem.DispatchGlobalAnnouncement(Loc.GetString(picked.StartAnnouncement), playSound: false, colorOverride: Color.Gold); - } - Audio.PlayGlobal(picked.StartAudio, Filter.Broadcast(), true); + _announcer.SendAnnouncement( + _announcer.GetAnnouncementId(picked.ID), + Filter.Broadcast(), + _announcer.GetEventLocaleString(_announcer.GetAnnouncementId(picked.ID)), + colorOverride: Color.Gold + ); } } 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/Events/PowerGridCheckRule.cs b/Content.Server/StationEvents/Events/PowerGridCheckRule.cs index 5503438df8a..97e89484612 100644 --- a/Content.Server/StationEvents/Events/PowerGridCheckRule.cs +++ b/Content.Server/StationEvents/Events/PowerGridCheckRule.cs @@ -54,13 +54,6 @@ protected override void Ended(EntityUid uid, PowerGridCheckRuleComponent compone } } - // Can't use the default EndAudio - component.AnnounceCancelToken?.Cancel(); - component.AnnounceCancelToken = new CancellationTokenSource(); - Timer.Spawn(3000, () => - { - Audio.PlayGlobal("/Audio/Announcements/power_on.ogg", Filter.Broadcast(), true, AudioParams.Default.WithVolume(-4f)); - }, component.AnnounceCancelToken.Token); component.Unpowered.Clear(); } diff --git a/Content.Server/StationEvents/Events/RandomSentienceRule.cs b/Content.Server/StationEvents/Events/RandomSentienceRule.cs index 4b7606d01f9..f667ad79750 100644 --- a/Content.Server/StationEvents/Events/RandomSentienceRule.cs +++ b/Content.Server/StationEvents/Events/RandomSentienceRule.cs @@ -2,11 +2,15 @@ using Content.Server.GameTicking.Rules.Components; using Content.Server.Ghost.Roles.Components; using Content.Server.StationEvents.Components; +using Content.Server.Announcements.Systems; +using Content.Server.Station.Components; namespace Content.Server.StationEvents.Events; public sealed class RandomSentienceRule : StationEventSystem { + [Dependency] private readonly AnnouncerSystem _announcer = default!; + protected override void Started(EntityUid uid, RandomSentienceRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { HashSet stationsToNotify = new(); @@ -53,14 +57,16 @@ protected override void Started(EntityUid uid, RandomSentienceRuleComponent comp } foreach (var station in stationsToNotify) { - ChatSystem.DispatchStationAnnouncement( - station, - Loc.GetString("station-event-random-sentience-announcement", - ("kind1", kind1), ("kind2", kind2), ("kind3", kind3), ("amount", groupList.Count), + _announcer.SendAnnouncement( + _announcer.GetAnnouncementId(args.RuleId), + StationSystem.GetInStation(EntityManager.GetComponent(station)), + "station-event-random-sentience-announcement", + null, + Color.Gold, + null, null, + ("kind1", kind1), ("kind2", kind2), ("kind3", kind3), ("amount", groupList.Count), ("data", Loc.GetString($"random-sentience-event-data-{RobustRandom.Next(1, 6)}")), - ("strength", Loc.GetString($"random-sentience-event-strength-{RobustRandom.Next(1, 8)}"))), - playDefaultSound: false, - colorOverride: Color.Gold + ("strength", Loc.GetString($"random-sentience-event-strength-{RobustRandom.Next(1, 8)}")) ); } } diff --git a/Content.Server/StationEvents/Events/StationEventSystem.cs b/Content.Server/StationEvents/Events/StationEventSystem.cs index 7f05f8940d9..6de8024bd0a 100644 --- a/Content.Server/StationEvents/Events/StationEventSystem.cs +++ b/Content.Server/StationEvents/Events/StationEventSystem.cs @@ -9,6 +9,9 @@ using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Content.Server.Announcements.Systems; +using Robust.Shared.Player; +using Content.Server.Station.Components; namespace Content.Server.StationEvents.Events; @@ -23,6 +26,7 @@ public abstract class StationEventSystem : GameRuleSystem where T : ICompo [Dependency] protected readonly ChatSystem ChatSystem = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] protected readonly StationSystem StationSystem = default!; + [Dependency] private readonly AnnouncerSystem _announcer = default!; protected ISawmill Sawmill = default!; @@ -43,12 +47,6 @@ protected override void Added(EntityUid uid, T component, GameRuleComponent game AdminLogManager.Add(LogType.EventAnnounced, $"Event added / announced: {ToPrettyString(uid)}"); - if (stationEvent.StartAnnouncement != null) - { - ChatSystem.DispatchGlobalAnnouncement(Loc.GetString(stationEvent.StartAnnouncement), playSound: false, colorOverride: Color.Gold); - } - - Audio.PlayGlobal(stationEvent.StartAudio, Filter.Broadcast(), true); stationEvent.StartTime = Timing.CurTime + stationEvent.StartDelay; } @@ -62,6 +60,16 @@ protected override void Started(EntityUid uid, T component, GameRuleComponent ga AdminLogManager.Add(LogType.EventStarted, LogImpact.High, $"Event started: {ToPrettyString(uid)}"); + if (stationEvent.StartAnnouncement) + { + _announcer.SendAnnouncement( + _announcer.GetAnnouncementId(args.RuleId), + Filter.Broadcast(), + _announcer.GetEventLocaleString(_announcer.GetAnnouncementId(args.RuleId)), + colorOverride: Color.Gold + ); + } + if (stationEvent.Duration != null) { var duration = stationEvent.MaxDuration == null @@ -82,12 +90,14 @@ protected override void Ended(EntityUid uid, T component, GameRuleComponent game AdminLogManager.Add(LogType.EventStopped, $"Event ended: {ToPrettyString(uid)}"); - if (stationEvent.EndAnnouncement != null) + if (stationEvent.EndAnnouncement) { - ChatSystem.DispatchGlobalAnnouncement(Loc.GetString(stationEvent.EndAnnouncement), playSound: false, colorOverride: Color.Gold); + _announcer.SendAnnouncement( + _announcer.GetAnnouncementId(args.RuleId, true), + Filter.Broadcast(), + _announcer.GetEventLocaleString(_announcer.GetAnnouncementId(args.RuleId, true)), + colorOverride: Color.Gold); } - - Audio.PlayGlobal(stationEvent.EndAudio, Filter.Broadcast(), true); } /// 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 aef4b89d509..6c9e99e5f3b 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..756f44e7429 --- /dev/null +++ b/Content.Server/Traits/Assorted/ForeignerTraitComponent.cs @@ -0,0 +1,38 @@ +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. + /// + [DataField] + public bool CantUnderstand = true; + + /// + /// Whether this trait prevents the entity from speaking the base language. + /// + [DataField] + 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.Shared/Traits/Assorted/Components/LightweightDrunkComponent.cs b/Content.Server/Traits/Assorted/LightweightDrunkComponent.cs similarity index 50% rename from Content.Shared/Traits/Assorted/Components/LightweightDrunkComponent.cs rename to Content.Server/Traits/Assorted/LightweightDrunkComponent.cs index 62d2f5899a4..4daff2e1338 100644 --- a/Content.Shared/Traits/Assorted/Components/LightweightDrunkComponent.cs +++ b/Content.Server/Traits/Assorted/LightweightDrunkComponent.cs @@ -1,13 +1,9 @@ -using Content.Shared.Drunk; -using Robust.Shared.GameStates; - namespace Content.Shared.Traits.Assorted.Components; /// -/// Used for the lightweight trait. DrunkSystem will check for this component and modify the boozePower accordingly if it finds it. +/// Used for the lightweight trait. LightweightDrunkSystem will multiply the effects of ethanol being metabolized /// -[RegisterComponent, NetworkedComponent] -[Access(typeof(SharedDrunkSystem))] +[RegisterComponent] public sealed partial class LightweightDrunkComponent : Component { [DataField("boozeStrengthMultiplier"), ViewVariables(VVAccess.ReadWrite)] diff --git a/Content.Server/Traits/Assorted/LightweightDrunkSystem.cs b/Content.Server/Traits/Assorted/LightweightDrunkSystem.cs new file mode 100644 index 00000000000..b5e9b877764 --- /dev/null +++ b/Content.Server/Traits/Assorted/LightweightDrunkSystem.cs @@ -0,0 +1,23 @@ +using Content.Server.Body.Components; +using Content.Server.Body.Systems; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Traits.Assorted.Components; + +namespace Content.Shared.Traits.Assorted.Systems; +public sealed class LightweightDrunkSystem : EntitySystem +{ + public override void Initialize() + { + SubscribeLocalEvent(OnTryMetabolizeReagent); + } + + private void OnTryMetabolizeReagent(EntityUid uid, LightweightDrunkComponent comp, ref TryMetabolizeReagent args) + { + Log.Debug(args.Prototype.ID); + if (args.Prototype.ID != "Ethanol") + return; + + args.Scale *= comp.BoozeStrengthMultiplier; + args.QuantityMultiplier *= comp.BoozeStrengthMultiplier; + } +} \ No newline at end of file diff --git a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs index 31c9b0d2b86..4749fd8b4b3 100644 --- a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Actions.Events; using Content.Shared.Administration.Components; using Content.Shared.CombatMode; +using Content.Shared.Contests; using Content.Shared.Damage.Events; using Content.Shared.Damage.Systems; using Content.Shared.Database; @@ -43,6 +44,7 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem [Dependency] private readonly SharedColorFlashEffectSystem _color = default!; [Dependency] private readonly SolutionContainerSystem _solutions = default!; [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly ContestsSystem _contests = default!; public override void Initialize() { @@ -138,7 +140,7 @@ protected override bool DoDisarm(EntityUid user, DisarmAttackEvent ev, EntityUid if (attemptEvent.Cancelled) return false; - var chance = CalculateDisarmChance(user, target, inTargetHand, combatMode); + var chance = CalculateDisarmChance(user, target, inTargetHand, combatMode) * _contests.MassContest(user, target); if (_random.Prob(chance)) { diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index c0adf730f11..9db00d2949c 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -1,11 +1,11 @@ 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; using Content.Server.Stunnable; using Content.Server.Weapons.Ranged.Components; +using Content.Shared.Contests; using Content.Shared.Damage; using Content.Shared.Damage.Systems; using Content.Shared.Database; @@ -29,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!; @@ -39,6 +38,7 @@ public sealed partial class GunSystem : SharedGunSystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly StaminaSystem _stamina = default!; [Dependency] private readonly StunSystem _stun = default!; + [Dependency] private readonly ContestsSystem _contests = default!; public const float DamagePitchVariation = SharedMeleeWeaponSystem.DamagePitchVariation; public const float GunClumsyChance = 0.5f; @@ -97,7 +97,7 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? var toMap = toCoordinates.ToMapPos(EntityManager, TransformSystem); var mapDirection = toMap - fromMap.Position; var mapAngle = mapDirection.ToAngle(); - var angle = GetRecoilAngle(Timing.CurTime, gun, mapDirection.ToAngle()); + var angle = GetRecoilAngle(Timing.CurTime, gun, mapDirection.ToAngle(), user); // If applicable, this ensures the projectile is parented to grid on spawn, instead of the map. var fromEnt = MapManager.TryFindGridAt(fromMap, out var gridUid, out var grid) @@ -318,7 +318,7 @@ private Angle[] LinearSpread(Angle start, Angle end, int intervals) return angles; } - private Angle GetRecoilAngle(TimeSpan curTime, GunComponent component, Angle direction) + private Angle GetRecoilAngle(TimeSpan curTime, GunComponent component, Angle direction, EntityUid? shooter) { var timeSinceLastFire = (curTime - component.LastFire).TotalSeconds; var newTheta = MathHelper.Clamp(component.CurrentAngle.Theta + component.AngleIncreaseModified.Theta - component.AngleDecayModified.Theta * timeSinceLastFire, component.MinAngleModified.Theta, component.MaxAngleModified.Theta); @@ -326,7 +326,8 @@ private Angle GetRecoilAngle(TimeSpan curTime, GunComponent component, Angle dir component.LastFire = component.NextFire; // Convert it so angle can go either side. - var random = Random.NextFloat(-0.5f, 0.5f); + + var random = Random.NextFloat(-0.5f, 0.5f) / _contests.MassContest(shooter); var spread = component.CurrentAngle.Theta * random; var angle = new Angle(direction.Theta + component.CurrentAngle.Theta * random); DebugTools.Assert(spread <= component.MaxAngleModified.Theta); 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/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.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/Announcements/Events/AnnouncementSendEvent.cs b/Content.Shared/Announcements/Events/AnnouncementSendEvent.cs new file mode 100644 index 00000000000..ca75742aed6 --- /dev/null +++ b/Content.Shared/Announcements/Events/AnnouncementSendEvent.cs @@ -0,0 +1,23 @@ +using Robust.Shared.Audio; +using Robust.Shared.Network; +using Robust.Shared.Serialization; + +namespace Content.Shared.Announcements.Events; + + +[Serializable, NetSerializable] +public sealed class AnnouncementSendEvent : EntityEventArgs +{ + public string AnnouncerId { get; } + public string AnnouncementId { get; } + public List Recipients { get; } + public AudioParams AudioParams { get; } + + public AnnouncementSendEvent(string announcerId, string announcementId, List recipients, AudioParams audioParams) + { + AnnouncerId = announcerId; + AnnouncementId = announcementId; + Recipients = recipients; + AudioParams = audioParams; + } +} diff --git a/Content.Shared/Announcements/Prototypes/AnnouncerPrototype.cs b/Content.Shared/Announcements/Prototypes/AnnouncerPrototype.cs new file mode 100644 index 00000000000..42db148df9c --- /dev/null +++ b/Content.Shared/Announcements/Prototypes/AnnouncerPrototype.cs @@ -0,0 +1,71 @@ +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Announcements.Prototypes; + +/// +/// Defines an announcer and their announcement file paths +/// +[Prototype("announcer")] +public sealed class AnnouncerPrototype : IPrototype +{ + [IdDataField] + public string ID { get; } = default!; + + /// + /// A prefix to add to all announcement paths unless told not to by + /// + /// Paths always start in Resources/ + [DataField("basePath")] + public string BasePath { get; } = default!; + + /// + /// Audio parameters to apply to all announcement sounds unless overwritten by + /// + [DataField("baseAudioParams")] + public AudioParams? BaseAudioParams { get; } + + [DataField("announcements")] + public AnnouncementData[] Announcements { get; } = default!; +} + +/// +/// Defines a path to an announcement file and that announcement's ID +/// +[DataDefinition] +public sealed partial class AnnouncementData +{ + [DataField("id")] + public string ID = default!; + + /// + /// If true, the will not be prepended to this announcement's path + /// + [DataField("ignoreBasePath")] + public bool IgnoreBasePath = false; + + /// + /// Where to look for the announcement audio file + /// + [DataField("path")] + public string? Path; + + /// + /// Use a soundCollection instead of a single sound + /// + [DataField("collection"), ValidatePrototypeId] + public string? Collection; + + /// + /// Overrides the default announcement message for this announcement type + /// + [DataField("message")] + public string? MessageOverride; + + /// + /// Audio parameters to apply to this announcement sound + /// Will override + /// + [DataField("audioParams")] + public AudioParams? AudioParams; +} diff --git a/Content.Shared/Announcements/Systems/SharedAnnouncerSystem.cs b/Content.Shared/Announcements/Systems/SharedAnnouncerSystem.cs new file mode 100644 index 00000000000..c29513285e1 --- /dev/null +++ b/Content.Shared/Announcements/Systems/SharedAnnouncerSystem.cs @@ -0,0 +1,152 @@ +using System.Linq; +using System.Text.RegularExpressions; +using Content.Shared.Announcements.Prototypes; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Announcements.Systems; + +public abstract class SharedAnnouncerSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + + + /// + /// Gets an announcement path from the announcer + /// + /// ID of the announcement from the announcer to get information for + /// ID of the announcer to use instead of the current one + public string GetAnnouncementPath(string announcementId, string announcerId) + { + if (!_proto.TryIndex(announcerId, out var announcer)) + return ""; + + // Get the announcement data from the announcer + // Will be the fallback if the data for the announcementId is not found + var announcementType = announcer.Announcements.FirstOrDefault(a => a.ID == announcementId) ?? + announcer.Announcements.First(a => a.ID.ToLower() == "fallback"); + + // If the greedy announcementType wants to do the job of announcer, ignore the base path and just return the path + if (announcementType.IgnoreBasePath) + return announcementType.Path!; + // If the announcementType has a collection, get the sound from the collection + if (announcementType.Collection != null) + return _audio.GetSound(new SoundCollectionSpecifier(announcementType.Collection)); + // If nothing is overriding the base paths, return the base path + the announcement file path + return $"{announcer.BasePath}/{announcementType.Path}"; + } + + /// + /// Gets audio params from the announcer + /// + /// ID of the announcement from the announcer to get information for + /// Announcer prototype to get information from + public string GetAnnouncementPath(string announcementId, AnnouncerPrototype announcer) + { + // Get the announcement data from the announcer + // Will be the fallback if the data for the announcementId is not found + var announcementType = announcer.Announcements.FirstOrDefault(a => a.ID == announcementId) ?? + announcer.Announcements.First(a => a.ID.ToLower() == "fallback"); + + // If the greedy announcementType wants to do the job of announcer, ignore the base path and just return the path + if (announcementType.IgnoreBasePath) + return announcementType.Path!; + // If the announcementType has a collection, get the sound from the collection + if (announcementType.Collection != null) + return _audio.GetSound(new SoundCollectionSpecifier(announcementType.Collection)); + // If nothing is overriding the base paths, return the base path + the announcement file path + return $"{announcer.BasePath}/{announcementType.Path}"; + } + + /// + /// Converts a prototype ID to a consistently used format for announcements + /// + public string GetAnnouncementId(string announcementId, bool ended = false) + { + // Replace the first letter with lowercase + var id = char.ToLowerInvariant(announcementId[0]) + announcementId[1..]; + + // If the event has ended, add "Complete" to the end + if (ended) + id += "Complete"; + + return id; + } + + + /// + /// Gets audio params from the announcer + /// + /// ID of the announcement from the announcer to get information from + /// ID of the announcer to use instead of the current one + public AudioParams? GetAudioParams(string announcementId, string announcerId) + { + if (!_proto.TryIndex(announcerId, out var announcer)) + return null; + + // Get the announcement data from the announcer + // Will be the fallback if the data for the announcementId is not found + var announcementType = announcer.Announcements.FirstOrDefault(a => a.ID == announcementId) ?? + announcer.Announcements.First(a => a.ID == "fallback"); + + // Return the announcer.BaseAudioParams if the announcementType doesn't have an override + return announcementType.AudioParams ?? announcer.BaseAudioParams ?? null; // For some reason the formatter doesn't warn me about "?? null" being redundant, so it stays + } + + /// + /// Gets audio params from the announcer + /// + /// ID of the announcement from the announcer to get information from + /// Announcer prototype to get information from + public AudioParams? GetAudioParams(string announcementId, AnnouncerPrototype announcer) + { + // Get the announcement data from the announcer + // Will be the fallback if the data for the announcementId is not found + var announcementType = announcer.Announcements.FirstOrDefault(a => a.ID == announcementId) ?? + announcer.Announcements.First(a => a.ID == "fallback"); + + // Return the announcer.BaseAudioParams if the announcementType doesn't have an override + return announcementType.AudioParams ?? announcer.BaseAudioParams; + } + + + /// + /// Gets an announcement message from the announcer + /// + /// ID of the announcement from the announcer to get information from + /// ID of the announcer to get information from + /// Locale arguments to pass to the overwritten announcement + public string? GetAnnouncementMessage(string announcementId, string announcerId, + params (string, object)[] localeArgs) + { + if (!_proto.TryIndex(announcerId, out var announcer)) + return null; + + // Get the announcement data from the announcer + // Will be the fallback if the data for the announcementId is not found + var announcementType = announcer.Announcements.FirstOrDefault(a => a.ID == announcementId) ?? + announcer.Announcements.First(a => a.ID == "fallback"); + + // Return the announcementType.MessageOverride if it exists, otherwise return null + return announcementType.MessageOverride != null ? Loc.GetString(announcementType.MessageOverride, localeArgs) : null; + } + + /// + /// Gets an announcement message from an event ID + /// + /// ID of the event to convert + /// Format for the locale string, replaces "{}" with the converted ID + /// The IDs use a hardcoded format, you can probably handle other formats yourself + /// Localized announcement + public string GetEventLocaleString(string eventId, string localeBase = "station-event-{}-announcement") + { + // Replace capital letters with lowercase plus a hyphen before it + var capsCapture = new Regex("([A-Z])"); + var id = capsCapture.Replace(eventId, "-$1").ToLower(); + + // Replace {} with the converted ID + return localeBase.Replace("{}", id); + } +} 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/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index c29cf2ce2fa..bb8b7481f0b 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -435,6 +435,36 @@ public static readonly CVarDef CVarDef.Create("game.round_end_sound_collection", "RoundEnd", CVar.SERVERONLY); + /* + * Announcers + */ + + /// + /// Weighted list of announcers to choose from + /// + public static readonly CVarDef AnnouncerList = + CVarDef.Create("announcer.list", "RandomAnnouncers", CVar.REPLICATED); + + /// + /// Optionally force set an announcer + /// + public static readonly CVarDef Announcer = + CVarDef.Create("announcer.announcer", "", CVar.SERVERONLY); + + /// + /// Optionally blacklist announcers + /// List of IDs separated by commas + /// + public static readonly CVarDef AnnouncerBlacklist = + CVarDef.Create("announcer.blacklist", "", CVar.SERVERONLY); + + /// + /// Changes how loud the announcers are for the client + /// + public static readonly CVarDef AnnouncerVolume = + CVarDef.Create("announcer.volume", 0.5f, CVar.ARCHIVE | CVar.CLIENTONLY); + + /* * Queue */ @@ -443,8 +473,8 @@ public static readonly CVarDef /// Controls if the connections queue is enabled /// If enabled plyaers will be added to a queue instead of being kicked after SoftMaxPlayers is reached /// - public static readonly CVarDef - QueueEnabled = CVarDef.Create("queue.enabled", false, CVar.SERVERONLY); + public static readonly CVarDef QueueEnabled = + CVarDef.Create("queue.enabled", false, CVar.SERVERONLY); /* @@ -2180,5 +2210,18 @@ public static readonly CVarDef /// public static readonly CVarDef StationGoalsChance = CVarDef.Create("game.station_goals_chance", 0.1f, CVar.SERVERONLY); + + /// + /// Toggles all MassContest functions. All mass contests output 1f when false + /// + public static readonly CVarDef DoMassContests = + CVarDef.Create("contests.do_mass_contests", true, CVar.REPLICATED | CVar.SERVER); + + /// + /// The maximum amount that Mass Contests can modify a physics multiplier, given as a +/- percentage + /// Default of 0.25f outputs between * 0.75f and 1.25f + /// + public static readonly CVarDef MassContestsMaxPercentage = + CVarDef.Create("contests.max_percentage", 0.25f, CVar.REPLICATED | CVar.SERVER); } } 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/Chemistry/Reagent/ReagentEffect.cs b/Content.Shared/Chemistry/Reagent/ReagentEffect.cs index 5bcb21fedb3..41eea55cca4 100644 --- a/Content.Shared/Chemistry/Reagent/ReagentEffect.cs +++ b/Content.Shared/Chemistry/Reagent/ReagentEffect.cs @@ -107,6 +107,7 @@ public readonly record struct ReagentEffectArgs( FixedPoint2 Quantity, IEntityManager EntityManager, ReactionMethod? Method, - float Scale + float Scale = 1f, + float QuantityMultiplier = 1f ); } 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 f06278c6d73..c041cf1ba06 100644 --- a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs +++ b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs @@ -270,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/Contests/ContestsSystem.cs b/Content.Shared/Contests/ContestsSystem.cs new file mode 100644 index 00000000000..6386bfd7a23 --- /dev/null +++ b/Content.Shared/Contests/ContestsSystem.cs @@ -0,0 +1,116 @@ +using Content.Shared.CCVar; +using Robust.Shared.Configuration; +using Robust.Shared.Physics.Components; + +namespace Content.Shared.Contests +{ + public sealed partial class ContestsSystem : EntitySystem + { + [Dependency] private readonly IConfigurationManager _cfg = default!; + + /// + /// The presumed average mass of a player entity + /// Defaulted to the average mass of an adult human + /// + private const float AverageMass = 71f; + + #region Mass Contests + /// + /// Outputs the ratio of mass between a performer and the average human mass + /// + /// Uid of Performer + public float MassContest(EntityUid performerUid, float otherMass = AverageMass) + { + if (_cfg.GetCVar(CCVars.DoMassContests) + && TryComp(performerUid, out var performerPhysics) + && performerPhysics.Mass != 0) + return Math.Clamp(performerPhysics.Mass / otherMass, 1 - _cfg.GetCVar(CCVars.MassContestsMaxPercentage), 1 + _cfg.GetCVar(CCVars.MassContestsMaxPercentage)); + + return 1f; + } + + /// + /// + /// MaybeMassContest, in case your entity doesn't exist + /// + public float MassContest(EntityUid? performerUid, float otherMass = AverageMass) + { + if (_cfg.GetCVar(CCVars.DoMassContests)) + { + var ratio = performerUid is { } uid ? MassContest(uid, otherMass) : 1f; + return ratio; + } + + return 1f; + } + + /// + /// Outputs the ratio of mass between a performer and the average human mass + /// If a function already has the performer's physics component, this is faster + /// + /// + public float MassContest(PhysicsComponent performerPhysics, float otherMass = AverageMass) + { + if (_cfg.GetCVar(CCVars.DoMassContests) + && performerPhysics.Mass != 0) + return Math.Clamp(performerPhysics.Mass / otherMass, 1 - _cfg.GetCVar(CCVars.MassContestsMaxPercentage), 1 + _cfg.GetCVar(CCVars.MassContestsMaxPercentage)); + + return 1f; + } + + /// + /// Outputs the ratio of mass between a performer and a target, accepts either EntityUids or PhysicsComponents in any combination + /// If you have physics components already in your function, use instead + /// + /// + /// + public float MassContest(EntityUid performerUid, EntityUid targetUid) + { + if (_cfg.GetCVar(CCVars.DoMassContests) + && TryComp(performerUid, out var performerPhysics) + && TryComp(targetUid, out var targetPhysics) + && performerPhysics.Mass != 0 + && targetPhysics.InvMass != 0) + return Math.Clamp(performerPhysics.Mass * targetPhysics.InvMass, 1 - _cfg.GetCVar(CCVars.MassContestsMaxPercentage), 1 + _cfg.GetCVar(CCVars.MassContestsMaxPercentage)); + + return 1f; + } + + /// + public float MassContest(EntityUid performerUid, PhysicsComponent targetPhysics) + { + if (_cfg.GetCVar(CCVars.DoMassContests) + && TryComp(performerUid, out var performerPhysics) + && performerPhysics.Mass != 0 + && targetPhysics.InvMass != 0) + return Math.Clamp(performerPhysics.Mass * targetPhysics.InvMass, 1 - _cfg.GetCVar(CCVars.MassContestsMaxPercentage), 1 + _cfg.GetCVar(CCVars.MassContestsMaxPercentage)); + + return 1f; + } + + /// + public float MassContest(PhysicsComponent performerPhysics, EntityUid targetUid) + { + if (_cfg.GetCVar(CCVars.DoMassContests) + && TryComp(targetUid, out var targetPhysics) + && performerPhysics.Mass != 0 + && targetPhysics.InvMass != 0) + return Math.Clamp(performerPhysics.Mass * targetPhysics.InvMass, 1 - _cfg.GetCVar(CCVars.MassContestsMaxPercentage), 1 + _cfg.GetCVar(CCVars.MassContestsMaxPercentage)); + + return 1f; + } + + /// + public float MassContest(PhysicsComponent performerPhysics, PhysicsComponent targetPhysics) + { + if (_cfg.GetCVar(CCVars.DoMassContests) + && performerPhysics.Mass != 0 + && targetPhysics.InvMass != 0) + return Math.Clamp(performerPhysics.Mass * targetPhysics.InvMass, 1 - _cfg.GetCVar(CCVars.MassContestsMaxPercentage), 1 + _cfg.GetCVar(CCVars.MassContestsMaxPercentage)); + + return 1f; + } + + #endregion + } +} 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/Components/HandcuffComponent.cs b/Content.Shared/Cuffs/Components/HandcuffComponent.cs index 77a77cf2f84..d305f6067d0 100644 --- a/Content.Shared/Cuffs/Components/HandcuffComponent.cs +++ b/Content.Shared/Cuffs/Components/HandcuffComponent.cs @@ -12,37 +12,37 @@ public sealed partial class HandcuffComponent : Component /// /// The time it takes to cuff an entity. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public float CuffTime = 3.5f; /// /// The time it takes to uncuff an entity. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public float UncuffTime = 3.5f; /// /// The time it takes for a cuffed entity to uncuff itself. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public float BreakoutTime = 15f; /// /// If an entity being cuffed is stunned, this amount of time is subtracted from the time it takes to add/remove their cuffs. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public float StunBonus = 2f; /// /// Will the cuffs break when removed? /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public bool BreakOnRemove; /// /// Will the cuffs break when removed? /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public EntProtoId? BrokenPrototype; /// @@ -55,35 +55,42 @@ public sealed partial class HandcuffComponent : Component /// /// The path of the RSI file used for the player cuffed overlay. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public string? CuffedRSI = "Objects/Misc/handcuffs.rsi"; /// /// The iconstate used with the RSI file for the player cuffed overlay. /// - [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + [DataField, AutoNetworkedField] public string? BodyIconState = "body-overlay"; /// /// An opptional color specification for /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public Color Color = Color.White; - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public SoundSpecifier StartCuffSound = new SoundPathSpecifier("/Audio/Items/Handcuffs/cuff_start.ogg"); - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public SoundSpecifier EndCuffSound = new SoundPathSpecifier("/Audio/Items/Handcuffs/cuff_end.ogg"); - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public SoundSpecifier StartBreakoutSound = new SoundPathSpecifier("/Audio/Items/Handcuffs/cuff_breakout_start.ogg"); - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public SoundSpecifier StartUncuffSound = new SoundPathSpecifier("/Audio/Items/Handcuffs/cuff_takeoff_start.ogg"); - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public SoundSpecifier EndUncuffSound = new SoundPathSpecifier("/Audio/Items/Handcuffs/cuff_takeoff_end.ogg"); + + /// + /// Acts as a two-state option for handcuff speed. When true, handcuffs will be easier to get out of if you are larger than average. Representing the use of strength to break things like zipties. + /// When false, handcuffs are easier to get out of if you are smaller than average, representing the use of dexterity to slip the cuffs. + /// + [DataField] + public bool UncuffEasierWhenLarge = false; } /// diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index 5cade56aca1..641d2bffc2d 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Alert; using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Buckle.Components; +using Content.Shared.Contests; using Content.Shared.Cuffs.Components; using Content.Shared.Database; using Content.Shared.DoAfter; @@ -58,6 +59,7 @@ public abstract partial class SharedCuffableSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly UseDelaySystem _delay = default!; + [Dependency] private readonly ContestsSystem _contests = default!; public override void Initialize() { @@ -559,7 +561,7 @@ public void TryUncuff(EntityUid target, EntityUid user, EntityUid? cuffsToRemove return; } - var uncuffTime = isOwner ? cuff.BreakoutTime : cuff.UncuffTime; + var uncuffTime = (isOwner ? cuff.BreakoutTime : cuff.UncuffTime) * (cuff.UncuffEasierWhenLarge ? 1 / _contests.MassContest(user) : _contests.MassContest(user)); if (isOwner) { 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/Drunk/DrunkSystem.cs b/Content.Shared/Drunk/DrunkSystem.cs index 161d4729ede..ed022cae31b 100644 --- a/Content.Shared/Drunk/DrunkSystem.cs +++ b/Content.Shared/Drunk/DrunkSystem.cs @@ -18,9 +18,6 @@ public void TryApplyDrunkenness(EntityUid uid, float boozePower, bool applySlur if (!Resolve(uid, ref status, false)) return; - if (TryComp(uid, out var trait)) - boozePower *= trait.BoozeStrengthMultiplier; - if (applySlur) { _slurredSystem.DoSlur(uid, TimeSpan.FromSeconds(boozePower), status); 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/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/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/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index ba640408dca..79d2e9f2551 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -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/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/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/EntitySystems/SharedDoorRemoteSystem.cs b/Content.Shared/Remotes/EntitySystems/SharedDoorRemoteSystem.cs index 72d807e6a0c..e9bbd27ada4 100644 --- a/Content.Shared/Remotes/EntitySystems/SharedDoorRemoteSystem.cs +++ b/Content.Shared/Remotes/EntitySystems/SharedDoorRemoteSystem.cs @@ -1,4 +1,3 @@ -using Content.Shared.Interaction; using Content.Shared.Popups; using Content.Shared.Interaction.Events; using Content.Shared.Remotes.Components; @@ -8,8 +7,6 @@ namespace Content.Shared.Remotes.EntitySystems; public abstract class SharedDoorRemoteSystem : EntitySystem { [Dependency] protected readonly SharedPopupSystem Popup = default!; - [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; - // I'm so sorry [Dependency] private readonly SharedAirlockSystem _sharedAirlockSystem = default!; public override void Initialize() { 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/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/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/Audio/Announcers/Intern/comms/announce.ogg b/Resources/Audio/Announcers/Intern/comms/announce.ogg new file mode 100644 index 00000000000..0ee0f36d56f Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/announce/1.ogg b/Resources/Audio/Announcers/Intern/comms/announce/1.ogg new file mode 100644 index 00000000000..c4d182bc8c9 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce/1.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/announce/10.ogg b/Resources/Audio/Announcers/Intern/comms/announce/10.ogg new file mode 100644 index 00000000000..7380ccdeefd Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce/10.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/announce/11.ogg b/Resources/Audio/Announcers/Intern/comms/announce/11.ogg new file mode 100644 index 00000000000..ca548dcc20a Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce/11.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/announce/12.ogg b/Resources/Audio/Announcers/Intern/comms/announce/12.ogg new file mode 100644 index 00000000000..8d71419798f Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce/12.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/announce/13.ogg b/Resources/Audio/Announcers/Intern/comms/announce/13.ogg new file mode 100644 index 00000000000..128c7aa424d Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce/13.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/announce/14.ogg b/Resources/Audio/Announcers/Intern/comms/announce/14.ogg new file mode 100644 index 00000000000..81d54101be5 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce/14.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/announce/2.ogg b/Resources/Audio/Announcers/Intern/comms/announce/2.ogg new file mode 100644 index 00000000000..a2ef615d56c Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce/2.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/announce/3.ogg b/Resources/Audio/Announcers/Intern/comms/announce/3.ogg new file mode 100644 index 00000000000..51613ff0367 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce/3.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/announce/4.ogg b/Resources/Audio/Announcers/Intern/comms/announce/4.ogg new file mode 100644 index 00000000000..874536ca72f Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce/4.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/announce/5.ogg b/Resources/Audio/Announcers/Intern/comms/announce/5.ogg new file mode 100644 index 00000000000..0af0d28ce18 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce/5.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/announce/6.ogg b/Resources/Audio/Announcers/Intern/comms/announce/6.ogg new file mode 100644 index 00000000000..a65006a8c01 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce/6.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/announce/7.ogg b/Resources/Audio/Announcers/Intern/comms/announce/7.ogg new file mode 100644 index 00000000000..4a1d3f013ae Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce/7.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/announce/8.ogg b/Resources/Audio/Announcers/Intern/comms/announce/8.ogg new file mode 100644 index 00000000000..83ca80f4939 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce/8.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/announce/9.ogg b/Resources/Audio/Announcers/Intern/comms/announce/9.ogg new file mode 100644 index 00000000000..3c0c45b25d0 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/announce/9.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/commandReport/1.ogg b/Resources/Audio/Announcers/Intern/comms/commandReport/1.ogg new file mode 100644 index 00000000000..e3108b13d17 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/commandReport/1.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/commandReport/2.ogg b/Resources/Audio/Announcers/Intern/comms/commandReport/2.ogg new file mode 100644 index 00000000000..cd67500426c Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/commandReport/2.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/commandReport/3.ogg b/Resources/Audio/Announcers/Intern/comms/commandReport/3.ogg new file mode 100644 index 00000000000..94241c5ba52 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/commandReport/3.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/welcome/1.ogg b/Resources/Audio/Announcers/Intern/comms/welcome/1.ogg new file mode 100644 index 00000000000..758f1967e09 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/welcome/1.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/welcome/2.ogg b/Resources/Audio/Announcers/Intern/comms/welcome/2.ogg new file mode 100644 index 00000000000..c2e72be510e Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/welcome/2.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/welcome/3.ogg b/Resources/Audio/Announcers/Intern/comms/welcome/3.ogg new file mode 100644 index 00000000000..004f57371de Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/welcome/3.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/welcome/4.ogg b/Resources/Audio/Announcers/Intern/comms/welcome/4.ogg new file mode 100644 index 00000000000..c4e1f7667cd Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/welcome/4.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/welcome/5.ogg b/Resources/Audio/Announcers/Intern/comms/welcome/5.ogg new file mode 100644 index 00000000000..641b8208a4e Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/welcome/5.ogg differ diff --git a/Resources/Audio/Announcers/Intern/comms/welcome/6.ogg b/Resources/Audio/Announcers/Intern/comms/welcome/6.ogg new file mode 100644 index 00000000000..b0fc38237f8 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/comms/welcome/6.ogg differ diff --git a/Resources/Audio/Announcers/Intern/events/aliens.ogg b/Resources/Audio/Announcers/Intern/events/aliens.ogg new file mode 100644 index 00000000000..9dd3c076978 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/events/aliens.ogg differ diff --git a/Resources/Audio/Announcers/Intern/events/anomaly.ogg b/Resources/Audio/Announcers/Intern/events/anomaly.ogg new file mode 100644 index 00000000000..9bed8eae3aa Binary files /dev/null and b/Resources/Audio/Announcers/Intern/events/anomaly.ogg differ diff --git a/Resources/Audio/Announcers/Intern/events/ion_storm.ogg b/Resources/Audio/Announcers/Intern/events/ion_storm.ogg new file mode 100644 index 00000000000..9e7b5c6b23e Binary files /dev/null and b/Resources/Audio/Announcers/Intern/events/ion_storm.ogg differ diff --git a/Resources/Audio/Announcers/Intern/events/meteors.ogg b/Resources/Audio/Announcers/Intern/events/meteors.ogg new file mode 100644 index 00000000000..c68c4bd8cc4 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/events/meteors.ogg differ diff --git a/Resources/Audio/Announcers/Intern/events/power_grid_check-complete.ogg b/Resources/Audio/Announcers/Intern/events/power_grid_check-complete.ogg new file mode 100644 index 00000000000..509cd398e6e Binary files /dev/null and b/Resources/Audio/Announcers/Intern/events/power_grid_check-complete.ogg differ diff --git a/Resources/Audio/Announcers/Intern/events/power_grid_check.ogg b/Resources/Audio/Announcers/Intern/events/power_grid_check.ogg new file mode 100644 index 00000000000..4b71053653f Binary files /dev/null and b/Resources/Audio/Announcers/Intern/events/power_grid_check.ogg differ diff --git a/Resources/Audio/Announcers/Intern/shuttle/shuttlecalled.ogg b/Resources/Audio/Announcers/Intern/shuttle/shuttlecalled.ogg new file mode 100644 index 00000000000..c903367cdff Binary files /dev/null and b/Resources/Audio/Announcers/Intern/shuttle/shuttlecalled.ogg differ diff --git a/Resources/Audio/Announcers/Intern/shuttle/shuttledock.ogg b/Resources/Audio/Announcers/Intern/shuttle/shuttledock.ogg new file mode 100644 index 00000000000..9f6ccd1a937 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/shuttle/shuttledock.ogg differ diff --git a/Resources/Audio/Announcers/Intern/shuttle/shuttlerecalled.ogg b/Resources/Audio/Announcers/Intern/shuttle/shuttlerecalled.ogg new file mode 100644 index 00000000000..e259a79f35e Binary files /dev/null and b/Resources/Audio/Announcers/Intern/shuttle/shuttlerecalled.ogg differ diff --git a/Resources/Audio/Announcers/Intern/unused/animes.ogg b/Resources/Audio/Announcers/Intern/unused/animes.ogg new file mode 100644 index 00000000000..36102c3e60e Binary files /dev/null and b/Resources/Audio/Announcers/Intern/unused/animes.ogg differ diff --git a/Resources/Audio/Announcers/Intern/unused/intercept.ogg b/Resources/Audio/Announcers/Intern/unused/intercept.ogg new file mode 100644 index 00000000000..a87274abd97 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/unused/intercept.ogg differ diff --git a/Resources/Audio/Announcers/Intern/unused/newai.ogg b/Resources/Audio/Announcers/Intern/unused/newai.ogg new file mode 100644 index 00000000000..c40b0990206 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/unused/newai.ogg differ diff --git a/Resources/Audio/Announcers/Intern/unused/outbreak7.ogg b/Resources/Audio/Announcers/Intern/unused/outbreak7.ogg new file mode 100644 index 00000000000..297a1bbe8db Binary files /dev/null and b/Resources/Audio/Announcers/Intern/unused/outbreak7.ogg differ diff --git a/Resources/Audio/Announcers/Intern/unused/radiation.ogg b/Resources/Audio/Announcers/Intern/unused/radiation.ogg new file mode 100644 index 00000000000..08db53ebfd2 Binary files /dev/null and b/Resources/Audio/Announcers/Intern/unused/radiation.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/comms/announce.ogg b/Resources/Audio/Announcers/MedBot/comms/announce.ogg new file mode 100644 index 00000000000..0ee0f36d56f Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/comms/announce.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/comms/attention.ogg b/Resources/Audio/Announcers/MedBot/comms/attention.ogg new file mode 100644 index 00000000000..d4d5a270852 Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/comms/attention.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/comms/command_report.ogg b/Resources/Audio/Announcers/MedBot/comms/command_report.ogg new file mode 100644 index 00000000000..4e5c2e1d1ff Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/comms/command_report.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/comms/welcome.ogg b/Resources/Audio/Announcers/MedBot/comms/welcome.ogg new file mode 100644 index 00000000000..f9a698fd080 Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/comms/welcome.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/events/aliens.ogg b/Resources/Audio/Announcers/MedBot/events/aliens.ogg new file mode 100644 index 00000000000..57fa70c3cae Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/events/aliens.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/events/anomaly.ogg b/Resources/Audio/Announcers/MedBot/events/anomaly.ogg new file mode 100644 index 00000000000..d710999e1e1 Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/events/anomaly.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/events/ion_storm.ogg b/Resources/Audio/Announcers/MedBot/events/ion_storm.ogg new file mode 100644 index 00000000000..15aeac9f7ff Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/events/ion_storm.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/events/meteors.ogg b/Resources/Audio/Announcers/MedBot/events/meteors.ogg new file mode 100644 index 00000000000..91208cae122 Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/events/meteors.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/events/power_grid_check-complete.ogg b/Resources/Audio/Announcers/MedBot/events/power_grid_check-complete.ogg new file mode 100644 index 00000000000..4b1605b1c74 Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/events/power_grid_check-complete.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/events/power_grid_check.ogg b/Resources/Audio/Announcers/MedBot/events/power_grid_check.ogg new file mode 100644 index 00000000000..875df350025 Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/events/power_grid_check.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/fallback.ogg b/Resources/Audio/Announcers/MedBot/fallback.ogg new file mode 100644 index 00000000000..d4d5a270852 Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/fallback.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/shuttle/shuttlecalled.ogg b/Resources/Audio/Announcers/MedBot/shuttle/shuttlecalled.ogg new file mode 100644 index 00000000000..a775567abed Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/shuttle/shuttlecalled.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/shuttle/shuttledock.ogg b/Resources/Audio/Announcers/MedBot/shuttle/shuttledock.ogg new file mode 100644 index 00000000000..933928db067 Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/shuttle/shuttledock.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/shuttle/shuttlerecalled.ogg b/Resources/Audio/Announcers/MedBot/shuttle/shuttlerecalled.ogg new file mode 100644 index 00000000000..53b622576d4 Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/shuttle/shuttlerecalled.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/unused/animes.ogg b/Resources/Audio/Announcers/MedBot/unused/animes.ogg new file mode 100644 index 00000000000..7615a744a66 Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/unused/animes.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/unused/intercept.ogg b/Resources/Audio/Announcers/MedBot/unused/intercept.ogg new file mode 100644 index 00000000000..c59d0455c1c Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/unused/intercept.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/unused/newai.ogg b/Resources/Audio/Announcers/MedBot/unused/newai.ogg new file mode 100644 index 00000000000..c40b0990206 Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/unused/newai.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/unused/outbreak7.ogg b/Resources/Audio/Announcers/MedBot/unused/outbreak7.ogg new file mode 100644 index 00000000000..1fc542534db Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/unused/outbreak7.ogg differ diff --git a/Resources/Audio/Announcers/MedBot/unused/radiation.ogg b/Resources/Audio/Announcers/MedBot/unused/radiation.ogg new file mode 100644 index 00000000000..5c48830b5f2 Binary files /dev/null and b/Resources/Audio/Announcers/MedBot/unused/radiation.ogg differ diff --git a/Resources/Audio/Announcers/Michael/alerts/blue.ogg b/Resources/Audio/Announcers/Michael/alerts/blue.ogg new file mode 100644 index 00000000000..f6458c00bb3 Binary files /dev/null and b/Resources/Audio/Announcers/Michael/alerts/blue.ogg differ diff --git a/Resources/Audio/Announcers/Michael/alerts/epsilon.ogg b/Resources/Audio/Announcers/Michael/alerts/epsilon.ogg new file mode 100644 index 00000000000..82815eda27d Binary files /dev/null and b/Resources/Audio/Announcers/Michael/alerts/epsilon.ogg differ diff --git a/Resources/Audio/Announcers/Michael/alerts/gamma.ogg b/Resources/Audio/Announcers/Michael/alerts/gamma.ogg new file mode 100644 index 00000000000..0a402b6d331 Binary files /dev/null and b/Resources/Audio/Announcers/Michael/alerts/gamma.ogg differ diff --git a/Resources/Audio/Announcers/Michael/alerts/green.ogg b/Resources/Audio/Announcers/Michael/alerts/green.ogg new file mode 100644 index 00000000000..d9d0f036fd4 Binary files /dev/null and b/Resources/Audio/Announcers/Michael/alerts/green.ogg differ diff --git a/Resources/Audio/Announcers/Michael/alerts/red.ogg b/Resources/Audio/Announcers/Michael/alerts/red.ogg new file mode 100644 index 00000000000..4a7f1e9472a Binary files /dev/null and b/Resources/Audio/Announcers/Michael/alerts/red.ogg differ diff --git a/Resources/Audio/Announcers/Michael/alerts/rename.js b/Resources/Audio/Announcers/Michael/alerts/rename.js new file mode 100644 index 00000000000..0c52f5fb327 --- /dev/null +++ b/Resources/Audio/Announcers/Michael/alerts/rename.js @@ -0,0 +1,191 @@ +// Set the method variable to one of these +const methods = [ + 'startsWith', // Replaces beginning with replace if matches searchfor OR cuts off the beginning if matches searchfor if replace is undefined + 'endsWith', // Replaces end with replace if matches searchfor OR cuts off the end if matches searchfor if replace is undefined + 'includes', // Replaces all instances of searchfor with replace OR removes all instances of searchfor if replace is undefined + + 'numberedSuffix', // Adds a numbered suffix to the end of the file name + 'numberedPrefix', // Adds a numbered prefix to the beginning of the file name + 'numbered', // Replaces the file name with a number +]; + +// User set variables +const dirname = './'; // Where to look for files ('./' is the current directory) +const includedirs = false; // Whether or not to include directories in the search +const searchfor = 'code_'; // What to search for +const replace = undefined; // What to replace with (undefined will remove the searchfor from the file name entirely) +const removeextraperiods = true; // Whether or not to remove extra periods in the file name (good for numbered methods) +const method = 'startsWith'; // Which method to use (see above) +const startingnumber = 0; // What number to start at (only used with numbered methods) +const debuglog = true; // Provides some extra info about what's going on +// End of user set variables + + +// Check if method is valid +if (typeof method !== 'string' || !methods.includes(method)) return console.log(`ERROR: Invalid method ${method}`); +else console.log('INFO: Using method: ' + method); + +// Check if dirname is valid +if (typeof dirname !== 'string') return console.log('ERROR: Invalid dirname'); +else console.log('INFO: Using directory: ' + dirname); + +// Warn of includedirs +if (includedirs) console.log('WARNING: Including directories in search'); +else console.log('INFO: Not including directories in search'); + +// Check if searchfor is valid +if (typeof searchfor !== 'string' && !method.includes('numbered')) return console.log('ERROR: Invalid searchfor'); +if (typeof searchfor !== 'string' && method.includes('numbered')) console.log('WARNING: Searchfor is undefined, this will replace the file name with a number'); +else console.log(`INFO: Searching for: ${searchfor}`); + +// Warn if replace is undefined +if (replace === undefined) console.log('WARNING: Replace is undefined, this will remove the searchfor from the file name entirely'); +else console.log(`INFO: Replacing with: ${replace}`); + +// Tell user if startingnumber is not 0 +if (startingnumber !== 0) console.log(`INFO: Starting number: ${startingnumber}`); + +// Check if debuglog is enabled +if (debuglog) console.log('INFO: Debug log is enabled'); +else console.log('INFO: Debug log is disabled'); + + +console.log('\n'); +console.log('INFO: Starting search...'); +console.log('\n'); + + +const fs = require('fs'); +const files = fs.readdirSync(dirname); +let number = 0 + startingnumber; + + +// Debug log +if (debuglog) { + console.log('DEBUG: Files:'); + console.log(files); + console.log(`DEBUG: Files type: ${typeof files}`); + console.log('\n'); +} + + +files.forEach((file) => { + // Split file name and extension + // If there is no extension, name[1] will be a blank string to avoid files getting fucked up + let name = file.includes('.') ? file.split('.') : [file, '']; + name.reverse(); + if (name[0].length > 0) name[0] = '.' + name[0]; + var extension = name[0]; + name.reverse(); + if (removeextraperiods) name = [name.join('').slice(0, -extension.length), extension]; + else name = [name.join('.').slice(0, -extension.length), extension]; + + + // Check if file is this script + if (process.argv[1].includes(name.join(''))) return; + + + // Debug log + if (debuglog) console.log(`DEBUG: File name: ${name[0]}`); + + + // Check if file is a directory + if (!includedirs) { + try { + if (fs.readdirSync(dirname + file).length) { + if (debuglog) console.log(`DEBUG: File is a directory, skipping: ${name[0]}`); + if (debuglog) console.log('\n'); + return; + } + } catch (e) { + if (debuglog) console.log(`DEBUG: File is a file: ${name[0]}`); + } + } else { + try { + if (fs.readdirSync(dirname + file).length) { + if (debuglog) console.log(`DEBUG: File is a directory: ${name[0]}`); + } + } catch (e) { + if (debuglog) console.log(`DEBUG: File is a file: ${name[0]}`); + } + } + + + if (method === 'startsWith') { + if (!name[0].startsWith(searchfor)) { + if (debuglog) console.log(`DEBUG: File name does not start with '${searchfor}': ${name[0]}`); + if (debuglog) console.log('\n'); + return; + } + + if (replace) { + fs.renameSync(dirname + '/' + file, dirname + '/' + replace + name[0].slice(searchfor.length) + name[1]); + } else { + fs.renameSync(dirname + '/' + file, dirname + '/' + name[0].slice(searchfor.length) + name[1]); + } + } else if (method === 'endsWith') { + if (!name[0].endsWith(searchfor)) { + if (debuglog) console.log(`DEBUG: File name does not end with '${searchfor}': ${name[0]}`); + if (debuglog) console.log('\n'); + return; + } + + if (replace) { + fs.renameSync(dirname + '/' + file, dirname + '/' + name[0].slice(0, -searchfor.length) + replace + name[1]); + } else { + fs.renameSync(dirname + '/' + file, dirname + '/' + name[0].slice(0, -searchfor.length) + name[1]); + } + } else if (method === 'includes') { + if (!name[0].includes(searchfor)) { + if (debuglog) console.log(`DEBUG: File name does not include '${searchfor}': ${name[0]}`); + if (debuglog) console.log('\n'); + return; + } + + const regex = new RegExp(searchfor, 'g'); + + if (replace) { + fs.renameSync(dirname + '/' + file, dirname + '/' + name[0].replace(regex, replace) + name[1]); + } else { + fs.renameSync(dirname + '/' + file, dirname + '/' + name[0].replace(regex, '') + name[1]); + } + } else if (method === 'numberedPrefix') { + if (!name[0].startsWith(searchfor)) { + if (debuglog) console.log(`DEBUG: File name does not start with '${searchfor}': ${name[0]}`); + if (debuglog) console.log('\n'); + return; + } + + fs.renameSync(dirname + '/' + file, dirname + '/' + number + name[0] + name[1]); + + number++; + } else if (method === 'numberedSuffix') { + if (!name[0].endsWith(searchfor)) { + if (debuglog) console.log(`DEBUG: File name does not end with '${searchfor}': ${name[0]}`); + if (debuglog) console.log('\n'); + return; + } + + fs.renameSync(dirname + '/' + file, dirname + '/' + name[0] + number + name[1]); + + number++; + } else if (method === 'numbered') { + if (typeof searchfor === 'string') { + if (!name[0].includes(searchfor)) { + if (debuglog) console.log(`DEBUG: File name does not include '${searchfor}': ${name[0]}`); + if (debuglog) console.log('\n'); + return; + } + + fs.renameSync(dirname + '/' + file, dirname + '/' + number + name[1]); + } else { + fs.renameSync(dirname + '/' + file, dirname + '/' + number + name[1]); + } + + number++; + } + + console.log('\n'); +}); + +console.log('INFO: Search complete'); diff --git a/Resources/Audio/Announcers/Michael/alerts/violet.ogg b/Resources/Audio/Announcers/Michael/alerts/violet.ogg new file mode 100644 index 00000000000..7a611d819eb Binary files /dev/null and b/Resources/Audio/Announcers/Michael/alerts/violet.ogg differ diff --git a/Resources/Audio/Announcers/Michael/alerts/white.ogg b/Resources/Audio/Announcers/Michael/alerts/white.ogg new file mode 100644 index 00000000000..1bb3afd354c Binary files /dev/null and b/Resources/Audio/Announcers/Michael/alerts/white.ogg differ diff --git a/Resources/Audio/Announcers/Michael/alerts/yellow.ogg b/Resources/Audio/Announcers/Michael/alerts/yellow.ogg new file mode 100644 index 00000000000..3a72eca3bbc Binary files /dev/null and b/Resources/Audio/Announcers/Michael/alerts/yellow.ogg differ diff --git a/Resources/Audio/Announcers/Michael/comms/attention.ogg b/Resources/Audio/Announcers/Michael/comms/attention.ogg new file mode 100644 index 00000000000..e25fdf2af7e Binary files /dev/null and b/Resources/Audio/Announcers/Michael/comms/attention.ogg differ diff --git a/Resources/Audio/Announcers/Michael/comms/welcome.ogg b/Resources/Audio/Announcers/Michael/comms/welcome.ogg new file mode 100644 index 00000000000..32d90041090 Binary files /dev/null and b/Resources/Audio/Announcers/Michael/comms/welcome.ogg differ diff --git a/Resources/Audio/Announcers/Michael/events/aliens.ogg b/Resources/Audio/Announcers/Michael/events/aliens.ogg new file mode 100644 index 00000000000..1293b3ced5b Binary files /dev/null and b/Resources/Audio/Announcers/Michael/events/aliens.ogg differ diff --git a/Resources/Audio/Announcers/Michael/events/bureaucratic_error.ogg b/Resources/Audio/Announcers/Michael/events/bureaucratic_error.ogg new file mode 100644 index 00000000000..2bb2710acf0 Binary files /dev/null and b/Resources/Audio/Announcers/Michael/events/bureaucratic_error.ogg differ diff --git a/Resources/Audio/Announcers/Michael/events/gas_leak.ogg b/Resources/Audio/Announcers/Michael/events/gas_leak.ogg new file mode 100644 index 00000000000..26912c859d1 Binary files /dev/null and b/Resources/Audio/Announcers/Michael/events/gas_leak.ogg differ diff --git a/Resources/Audio/Announcers/Michael/events/kudzu_growth.ogg b/Resources/Audio/Announcers/Michael/events/kudzu_growth.ogg new file mode 100644 index 00000000000..31eccef518b Binary files /dev/null and b/Resources/Audio/Announcers/Michael/events/kudzu_growth.ogg differ diff --git a/Resources/Audio/Announcers/Michael/events/meteors.ogg b/Resources/Audio/Announcers/Michael/events/meteors.ogg new file mode 100644 index 00000000000..62a21625712 Binary files /dev/null and b/Resources/Audio/Announcers/Michael/events/meteors.ogg differ diff --git a/Resources/Audio/Announcers/Michael/events/noospheric_storm.ogg b/Resources/Audio/Announcers/Michael/events/noospheric_storm.ogg new file mode 100644 index 00000000000..eedaaf5699b Binary files /dev/null and b/Resources/Audio/Announcers/Michael/events/noospheric_storm.ogg differ diff --git a/Resources/Audio/Announcers/Michael/events/power_grid_check-complete.ogg b/Resources/Audio/Announcers/Michael/events/power_grid_check-complete.ogg new file mode 100644 index 00000000000..de5fa1ff401 Binary files /dev/null and b/Resources/Audio/Announcers/Michael/events/power_grid_check-complete.ogg differ diff --git a/Resources/Audio/Announcers/Michael/events/power_grid_check.ogg b/Resources/Audio/Announcers/Michael/events/power_grid_check.ogg new file mode 100644 index 00000000000..07964d240e7 Binary files /dev/null and b/Resources/Audio/Announcers/Michael/events/power_grid_check.ogg differ diff --git a/Resources/Audio/Announcers/Michael/events/vent_clog.ogg b/Resources/Audio/Announcers/Michael/events/vent_clog.ogg new file mode 100644 index 00000000000..16fbf88e03f Binary files /dev/null and b/Resources/Audio/Announcers/Michael/events/vent_clog.ogg differ diff --git a/Resources/Audio/Announcers/Michael/fallback.ogg b/Resources/Audio/Announcers/Michael/fallback.ogg new file mode 100644 index 00000000000..e25fdf2af7e Binary files /dev/null and b/Resources/Audio/Announcers/Michael/fallback.ogg differ diff --git a/Resources/Audio/Announcers/Michael/shuttle/shuttle_called.ogg b/Resources/Audio/Announcers/Michael/shuttle/shuttle_called.ogg new file mode 100644 index 00000000000..261ecabf6e5 Binary files /dev/null and b/Resources/Audio/Announcers/Michael/shuttle/shuttle_called.ogg differ diff --git a/Resources/Audio/Announcers/Michael/shuttle/shuttle_dock.ogg b/Resources/Audio/Announcers/Michael/shuttle/shuttle_dock.ogg new file mode 100644 index 00000000000..a83b482f49a Binary files /dev/null and b/Resources/Audio/Announcers/Michael/shuttle/shuttle_dock.ogg differ diff --git a/Resources/Audio/Announcers/Michael/shuttle/shuttle_recalled.ogg b/Resources/Audio/Announcers/Michael/shuttle/shuttle_recalled.ogg new file mode 100644 index 00000000000..b104ea561a8 Binary files /dev/null and b/Resources/Audio/Announcers/Michael/shuttle/shuttle_recalled.ogg differ diff --git a/Resources/Audio/Announcers/Michael/unused/outbreak7.ogg b/Resources/Audio/Announcers/Michael/unused/outbreak7.ogg new file mode 100644 index 00000000000..fc584db60c4 Binary files /dev/null and b/Resources/Audio/Announcers/Michael/unused/outbreak7.ogg differ diff --git a/Resources/Audio/Announcers/Michael/unused/radiation.ogg b/Resources/Audio/Announcers/Michael/unused/radiation.ogg new file mode 100644 index 00000000000..7b7ca0c2b9e Binary files /dev/null and b/Resources/Audio/Announcers/Michael/unused/radiation.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/alerts/code_blue.ogg b/Resources/Audio/Announcers/NEIL/alerts/code_blue.ogg new file mode 100644 index 00000000000..48d9c6548b6 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/alerts/code_blue.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/alerts/code_delta.ogg b/Resources/Audio/Announcers/NEIL/alerts/code_delta.ogg new file mode 100644 index 00000000000..70826719156 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/alerts/code_delta.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/alerts/code_epsilon.ogg b/Resources/Audio/Announcers/NEIL/alerts/code_epsilon.ogg new file mode 100644 index 00000000000..a8db858b1af Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/alerts/code_epsilon.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/alerts/code_gamma.ogg b/Resources/Audio/Announcers/NEIL/alerts/code_gamma.ogg new file mode 100644 index 00000000000..3351acc2e0d Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/alerts/code_gamma.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/alerts/code_green.ogg b/Resources/Audio/Announcers/NEIL/alerts/code_green.ogg new file mode 100644 index 00000000000..3c0031833be Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/alerts/code_green.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/alerts/code_red.ogg b/Resources/Audio/Announcers/NEIL/alerts/code_red.ogg new file mode 100644 index 00000000000..209eb36ba11 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/alerts/code_red.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/alerts/code_violet.ogg b/Resources/Audio/Announcers/NEIL/alerts/code_violet.ogg new file mode 100644 index 00000000000..c1681c1edb8 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/alerts/code_violet.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/alerts/code_white.ogg b/Resources/Audio/Announcers/NEIL/alerts/code_white.ogg new file mode 100644 index 00000000000..24c942dfcb2 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/alerts/code_white.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/alerts/code_yellow.ogg b/Resources/Audio/Announcers/NEIL/alerts/code_yellow.ogg new file mode 100644 index 00000000000..9139446d2b6 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/alerts/code_yellow.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/comms/announce.ogg b/Resources/Audio/Announcers/NEIL/comms/announce.ogg new file mode 100644 index 00000000000..0ee0f36d56f Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/comms/announce.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/comms/attention.ogg b/Resources/Audio/Announcers/NEIL/comms/attention.ogg new file mode 100644 index 00000000000..310fad3ef63 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/comms/attention.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/comms/welcome.ogg b/Resources/Audio/Announcers/NEIL/comms/welcome.ogg new file mode 100644 index 00000000000..00c8991276c Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/comms/welcome.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/events/bureaucraticerror.ogg b/Resources/Audio/Announcers/NEIL/events/bureaucraticerror.ogg new file mode 100644 index 00000000000..0cbafcf6276 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/events/bureaucraticerror.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/events/gasleak.ogg b/Resources/Audio/Announcers/NEIL/events/gasleak.ogg new file mode 100644 index 00000000000..8efffd02c45 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/events/gasleak.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/events/kudzu.ogg b/Resources/Audio/Announcers/NEIL/events/kudzu.ogg new file mode 100644 index 00000000000..4ddd2f636bc Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/events/kudzu.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/events/meteors.ogg b/Resources/Audio/Announcers/NEIL/events/meteors.ogg new file mode 100644 index 00000000000..cf525441745 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/events/meteors.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/events/noosphericstorm.ogg b/Resources/Audio/Announcers/NEIL/events/noosphericstorm.ogg new file mode 100644 index 00000000000..47b52f7cb47 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/events/noosphericstorm.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/events/power_grid_check-complete.ogg b/Resources/Audio/Announcers/NEIL/events/power_grid_check-complete.ogg new file mode 100644 index 00000000000..961b50a5031 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/events/power_grid_check-complete.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/events/power_grid_check.ogg b/Resources/Audio/Announcers/NEIL/events/power_grid_check.ogg new file mode 100644 index 00000000000..7d7a43825fe Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/events/power_grid_check.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/events/ventclog.ogg b/Resources/Audio/Announcers/NEIL/events/ventclog.ogg new file mode 100644 index 00000000000..90350f0e359 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/events/ventclog.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/fallback.ogg b/Resources/Audio/Announcers/NEIL/fallback.ogg new file mode 100644 index 00000000000..310fad3ef63 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/fallback.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/shuttle/shuttlecalled.ogg b/Resources/Audio/Announcers/NEIL/shuttle/shuttlecalled.ogg new file mode 100644 index 00000000000..482bf133193 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/shuttle/shuttlecalled.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/shuttle/shuttledock.ogg b/Resources/Audio/Announcers/NEIL/shuttle/shuttledock.ogg new file mode 100644 index 00000000000..0615536555b Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/shuttle/shuttledock.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/shuttle/shuttlerecalled.ogg b/Resources/Audio/Announcers/NEIL/shuttle/shuttlerecalled.ogg new file mode 100644 index 00000000000..5a198ca5fe4 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/shuttle/shuttlerecalled.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/unused/outbreak7.ogg b/Resources/Audio/Announcers/NEIL/unused/outbreak7.ogg new file mode 100644 index 00000000000..91067960125 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/unused/outbreak7.ogg differ diff --git a/Resources/Audio/Announcers/NEIL/unused/radiation.ogg b/Resources/Audio/Announcers/NEIL/unused/radiation.ogg new file mode 100644 index 00000000000..3ea2928c1d9 Binary files /dev/null and b/Resources/Audio/Announcers/NEIL/unused/radiation.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/comms/announce.ogg b/Resources/Audio/Announcers/VoxFem/comms/announce.ogg new file mode 100644 index 00000000000..0ee0f36d56f Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/comms/announce.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/comms/attention.ogg b/Resources/Audio/Announcers/VoxFem/comms/attention.ogg new file mode 100644 index 00000000000..912be4425eb Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/comms/attention.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/comms/command_report.ogg b/Resources/Audio/Announcers/VoxFem/comms/command_report.ogg new file mode 100644 index 00000000000..82e4ca425de Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/comms/command_report.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/comms/welcome.ogg b/Resources/Audio/Announcers/VoxFem/comms/welcome.ogg new file mode 100644 index 00000000000..c7013dcbd5f Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/comms/welcome.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/events/aliens.ogg b/Resources/Audio/Announcers/VoxFem/events/aliens.ogg new file mode 100644 index 00000000000..f7d1746247f Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/events/aliens.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/events/anomaly.ogg b/Resources/Audio/Announcers/VoxFem/events/anomaly.ogg new file mode 100644 index 00000000000..7680726f153 Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/events/anomaly.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/events/ion_storm.ogg b/Resources/Audio/Announcers/VoxFem/events/ion_storm.ogg new file mode 100644 index 00000000000..9f39713de6e Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/events/ion_storm.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/events/meteors.ogg b/Resources/Audio/Announcers/VoxFem/events/meteors.ogg new file mode 100644 index 00000000000..8f1c3aeacbb Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/events/meteors.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/events/power_grid_check-complete.ogg b/Resources/Audio/Announcers/VoxFem/events/power_grid_check-complete.ogg new file mode 100644 index 00000000000..9d18797d6ea Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/events/power_grid_check-complete.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/events/power_grid_check.ogg b/Resources/Audio/Announcers/VoxFem/events/power_grid_check.ogg new file mode 100644 index 00000000000..1c6377c9d8d Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/events/power_grid_check.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/fallback.ogg b/Resources/Audio/Announcers/VoxFem/fallback.ogg new file mode 100644 index 00000000000..912be4425eb Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/fallback.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/shuttle/shuttlecalled.ogg b/Resources/Audio/Announcers/VoxFem/shuttle/shuttlecalled.ogg new file mode 100644 index 00000000000..716bf824654 Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/shuttle/shuttlecalled.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/shuttle/shuttledock.ogg b/Resources/Audio/Announcers/VoxFem/shuttle/shuttledock.ogg new file mode 100644 index 00000000000..0f70bebc751 Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/shuttle/shuttledock.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/shuttle/shuttlerecalled.ogg b/Resources/Audio/Announcers/VoxFem/shuttle/shuttlerecalled.ogg new file mode 100644 index 00000000000..5f6db404b87 Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/shuttle/shuttlerecalled.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/unused/newai.ogg b/Resources/Audio/Announcers/VoxFem/unused/newai.ogg new file mode 100644 index 00000000000..35aba34564f Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/unused/newai.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/unused/outbreak7.ogg b/Resources/Audio/Announcers/VoxFem/unused/outbreak7.ogg new file mode 100644 index 00000000000..f21d4fca443 Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/unused/outbreak7.ogg differ diff --git a/Resources/Audio/Announcers/VoxFem/unused/radiation.ogg b/Resources/Audio/Announcers/VoxFem/unused/radiation.ogg new file mode 100644 index 00000000000..ef395af3101 Binary files /dev/null and b/Resources/Audio/Announcers/VoxFem/unused/radiation.ogg differ diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 67e7f21a140..0c4ecd3f96a 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -4393,3 +4393,85 @@ Entries: Intercom, FireAlarm and Substation) id: 6150 time: '2024-07-09T19:37:03.0000000+00:00' +- author: CodedCrow + changes: + - type: Add + message: Added an Arachne 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' +- author: Mnemotechnician + changes: + - type: Fix + message: Foreigner traits now work correctly. + id: 6155 + time: '2024-07-12T17:46:37.0000000+00:00' +- author: WarMechanic + changes: + - type: Add + message: >- + Added the Heavyweight Drunk trait, which doubles your alcoholism + potential. + id: 6156 + time: '2024-07-12T19:02:15.0000000+00:00' +- author: DEATHB4DEFEAT + changes: + - type: Add + message: Added 4 new announcers that will randomly be selected every shift + id: 6157 + time: '2024-07-12T20:13:50.0000000+00:00' +- author: VMSolidus + changes: + - type: Add + message: 'Mass Contests have returned in a new reworked form. ' + - type: Add + message: >- + Weapon Recoil is now resisted by character mass. More massive characters + take less recoil, less massive characters take more recoil. + - type: Add + message: >- + Disarm and Shove actions are now modified by relative character mass. It + is easier to shove people around if you're bigger than them. + - type: Add + message: >- + Cablecuffs and Zipties are now easier to escape out of if you're + smaller. + id: 6158 + time: '2024-07-13T08:05:52.0000000+00:00' +- author: FoxxoTrystan + changes: + - type: Tweak + message: We got new ID Cards the old ones were soo tiny that they were lost. + id: 6159 + time: '2024-07-14T23:12:33.0000000+00:00' +- author: Mnemotechnician + changes: + - type: Tweak + message: >- + Shock collars can now be manufactured in the security techfab. They also + deal less damage and ignore insulated gloves. + id: 6160 + time: '2024-07-14T23:14:03.0000000+00:00' diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index c3be81504b8..0cf9c113634 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aexxie, africalimedrop, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, BasedUser, BGare, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bribrooo, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CaasGit, CakeQ, CaptainSqrBeard, Carbonhell, Carolyn3114, casperr04, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, clement-or, Clyybber, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, deepdarkdepths, Delete69, deltanedas, DeltaV-Bot, DerbyX, DoctorBeard, DogZeroX, dontbetank, Doru991, DoubleRiceEddiedd, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, FoxxoTrystan, freeman2651, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, Genkail, geraeumig, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, Guess-My-Name, gusxyz, h3half, Hanzdegloker, Hardly3D, harikattar, Hebiman, Henry12116, HerCoyote23, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Hyenh, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, IntegerTempest, Interrobang01, IProduceWidgets, ItsMeThom, j-giebel, Jackal298, Jackrost, jamessimo, janekvap, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, joelhed, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTether, JustinTrotter, KaiShibaa, kalane15, kalanosh, KEEYNy, Keikiru, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Kmc2000, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, leonardo-dabepis, LetterN, Level10Cybermancer, lever1209, LightVillet, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, LovelyLophi, LudwigVonChesterfield, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, M3739, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, Mnemotechnician, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, noverd, nuke-haus, NULL882, OCOtheOmega, OctoRocket, OldDanceJacket, onoira, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, PHCodes, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, SignalWalker, SimpleStation14, Simyon264, SirDragooon, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, Snowni, snowsignal, SonicHDC, SoulSloth, SpaceManiac, SpeltIncorrectyl, spoogemonster, ssdaniel24, Stealthbomber16, stellar-novas, StrawberryMoses, SweptWasTaken, Szunti, TadJohnson00, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, tom-leys, tomasalves8, Tomeno, Tornado-Technology, tosatur, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UKNOWH, UnicornOnLSD, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Verslebas, VigersRay, Visne, VMSolidus, volundr-, Voomra, Vordenburg, vulppine, waylon531, weaversam8, Willhelm53, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zumorica, Zymem +0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aexxie, africalimedrop, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, BasedUser, BGare, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bribrooo, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CaasGit, CakeQ, CaptainSqrBeard, Carbonhell, Carolyn3114, casperr04, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, clement-or, Clyybber, CodedCrow, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, deepdarkdepths, Delete69, deltanedas, DeltaV-Bot, DerbyX, DoctorBeard, DogZeroX, dontbetank, Doru991, DoubleRiceEddiedd, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, FoxxoTrystan, freeman2651, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, Genkail, geraeumig, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, Guess-My-Name, gusxyz, h3half, Hanzdegloker, Hardly3D, harikattar, Hebiman, Henry12116, HerCoyote23, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Hyenh, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, IntegerTempest, Interrobang01, IProduceWidgets, ItsMeThom, j-giebel, Jackal298, Jackrost, jamessimo, janekvap, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, joelhed, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTether, JustinTrotter, KaiShibaa, kalane15, kalanosh, KEEYNy, Keikiru, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Kmc2000, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, leonardo-dabepis, LetterN, Level10Cybermancer, lever1209, LightVillet, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, LovelyLophi, LudwigVonChesterfield, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, Mnemotechnician, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, notafet, notquitehadouken, noudoit, noverd, nuke-haus, NULL882, OCOtheOmega, OctoRocket, OldDanceJacket, onoira, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, PHCodes, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, SignalWalker, SimpleStation14, Simyon264, SirDragooon, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, Snowni, snowsignal, SonicHDC, SoulSloth, SpaceManiac, SpeltIncorrectyl, spoogemonster, ssdaniel24, Stealthbomber16, stellar-novas, StrawberryMoses, SweptWasTaken, Szunti, TadJohnson00, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, tom-leys, tomasalves8, Tomeno, Tornado-Technology, tosatur, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UKNOWH, UnicornOnLSD, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Verslebas, VigersRay, Visne, VMSolidus, volundr-, Voomra, Vordenburg, vulppine, WarMechanic, waylon531, weaversam8, Willhelm53, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zumorica, Zymem 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/announcements/announcers/announcers.ftl b/Resources/Locale/en-US/announcements/announcers/announcers.ftl new file mode 100644 index 00000000000..d18857a38e2 --- /dev/null +++ b/Resources/Locale/en-US/announcements/announcers/announcers.ftl @@ -0,0 +1,5 @@ +announcer-Intern-name=Central Command +announcer-MedBot-name=Automated +announcer-Michael-name=Michael +announcer-NEIL-name=N.E.I.L. +announcer-VoxFem-name=Automated diff --git a/Resources/Locale/en-US/deltav/station-events/events/xeno-vent.ftl b/Resources/Locale/en-US/deltav/station-events/events/xeno-vent.ftl index eebfe1610f9..eb59c8e0a63 100644 --- a/Resources/Locale/en-US/deltav/station-events/events/xeno-vent.ftl +++ b/Resources/Locale/en-US/deltav/station-events/events/xeno-vent.ftl @@ -1 +1 @@ -station-event-xeno-vent-start-announcement = Confirmed sightings of hostile alien wildlife on the station. Personnel are advised to arm themselves, barricade doors, and defend themselves if necessary. Security is advised to eradicate the threat as soon as possible. +station-event-xeno-vents-announcement = Confirmed sightings of hostile alien wildlife on the station. Personnel are advised to arm themselves, barricade doors, and defend themselves if necessary. Security is advised to eradicate the threat as soon as possible. diff --git a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl index f76d1c04784..e438512b553 100644 --- a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl +++ b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl @@ -30,6 +30,7 @@ ui-options-ambience-volume = Ambience volume: ui-options-lobby-volume = Lobby & Round-end volume: ui-options-interface-volume = Interface volume: ui-options-ambience-max-sounds = Ambience simultaneous sounds: +ui-options-announcer-volume = Announcer volume: ui-options-lobby-music = Lobby & Round-end Music ui-options-restart-sounds = Round Restart Sounds ui-options-event-music = Event Music 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/game-ticking/game-ticker.ftl b/Resources/Locale/en-US/game-ticking/game-ticker.ftl index 9527ecb57d9..a8253dd9f35 100644 --- a/Resources/Locale/en-US/game-ticking/game-ticker.ftl +++ b/Resources/Locale/en-US/game-ticking/game-ticker.ftl @@ -25,6 +25,7 @@ game-ticker-get-info-preround-text = Hi and welcome to [color=white]Space Statio >[color=yellow]{$desc}[/color] game-ticker-no-map-selected = [color=yellow]Map not yet selected![/color] game-ticker-player-no-jobs-available-when-joining = When attempting to join to the game, no jobs were available. +game-ticker-welcome-to-the-station = Welcome to the station crew, enjoy your stay! # Displayed in chat to admins when a player joins player-join-message = Player {$name} joined. @@ -39,4 +40,4 @@ latejoin-arrivals-direction = A shuttle transferring you to your station will ar latejoin-arrivals-direction-time = A shuttle transferring you to your station will arrive in {$time}. preset-not-enough-ready-players = Can't start {$presetName}. Requires {$minimumPlayers} players but we have {$readyPlayersCount}. -preset-no-one-ready = Can't start {$presetName}. No players are ready. \ No newline at end of file +preset-no-one-ready = Can't start {$presetName}. No players are ready. 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/station-events/events/anomaly-spawn.ftl b/Resources/Locale/en-US/station-events/events/anomaly-spawn.ftl index 0e4b5f6e39c..543bef949aa 100644 --- a/Resources/Locale/en-US/station-events/events/anomaly-spawn.ftl +++ b/Resources/Locale/en-US/station-events/events/anomaly-spawn.ftl @@ -1,7 +1,7 @@ -anomaly-spawn-event-announcement = Our readings have detected a dangerous interspacial anomaly. Please inform the research team of { $sighting }. +station-event-anomaly-spawn-announcement = Our readings have detected a dangerous interspacial anomaly. Please inform the research team of { $sighting }. anomaly-spawn-sighting-1 = low pulsating sounds heard throughout the station anomaly-spawn-sighting-2 = strange sources of light anomaly-spawn-sighting-3 = inexplicable shapes anomaly-spawn-sighting-4 = forms causing severe mental distress -anomaly-spawn-sighting-5 = strange effects on the local environment \ No newline at end of file +anomaly-spawn-sighting-5 = strange effects on the local environment diff --git a/Resources/Locale/en-US/station-events/events/bluespace-artifact.ftl b/Resources/Locale/en-US/station-events/events/bluespace-artifact.ftl index a2307d77b5d..59ea8a536b5 100644 --- a/Resources/Locale/en-US/station-events/events/bluespace-artifact.ftl +++ b/Resources/Locale/en-US/station-events/events/bluespace-artifact.ftl @@ -6,4 +6,4 @@ bluespace-artifact-sighting-3 = otherworldly structures bluespace-artifact-sighting-4 = incomprehensible alien objects bluespace-artifact-sighting-5 = unfamiliar objects in strange places bluespace-artifact-sighting-6 = unknown alien artifacts -bluespace-artifact-sighting-7 = explosions of light accompanied by weird sounds \ No newline at end of file +bluespace-artifact-sighting-7 = explosions of light accompanied by weird sounds diff --git a/Resources/Locale/en-US/station-events/events/cargo-gifts.ftl b/Resources/Locale/en-US/station-events/events/cargo-gifts.ftl index d669fbeff90..fd20459b5eb 100644 --- a/Resources/Locale/en-US/station-events/events/cargo-gifts.ftl +++ b/Resources/Locale/en-US/station-events/events/cargo-gifts.ftl @@ -10,14 +10,14 @@ cargo-gift-dest-janitor = Service Dept cargo-gift-dest-med = Medical Dept cargo-gift-dest-sec = Security Dept -cargo-gift-pizza-small = A small pizza party -cargo-gift-pizza-large = A large pizza party +cargo-gift-pizza-small = a small pizza party +cargo-gift-pizza-large = a large pizza party -cargo-gift-eng = Repair Materials -cargo-gift-vending = Vending machines refills -cargo-gift-cleaning = Cleaning equipment -cargo-gift-medical-supply = Medical supplies -cargo-gift-space-protection = Space Hazard Protection -cargo-gift-fire-protection = Fire Protection -cargo-gift-security-guns = Lethal Weapons -cargo-gift-security-riot = Riot Gear +cargo-gift-eng = repair Materials +cargo-gift-vending = vending machine refills +cargo-gift-cleaning = cleaning equipment +cargo-gift-medical-supply = medical supplies +cargo-gift-space-protection = space hazard protection +cargo-gift-fire-protection = fire protection +cargo-gift-security-guns = lethal weapons +cargo-gift-security-riot = riot gear diff --git a/Resources/Locale/en-US/station-events/events/gas-leak.ftl b/Resources/Locale/en-US/station-events/events/gas-leak.ftl index 18429fa58d8..230e886db47 100644 --- a/Resources/Locale/en-US/station-events/events/gas-leak.ftl +++ b/Resources/Locale/en-US/station-events/events/gas-leak.ftl @@ -1,2 +1,2 @@ -station-event-gas-leak-start-announcement = Attention crew, there is a gas leak on the station. We advise you to avoid the area and wear suit internals in the meantime. -station-event-gas-leak-end-announcement = The source of the gas leak has been fixed. Please be cautious around areas with gas remaining. +station-event-gas-leak-announcement = Attention crew, there is a gas leak on the station. We advise you to avoid the area and wear suit internals in the meantime. +station-event-gas-leak-complete-announcement = The source of the gas leak has been fixed. Please be cautious around areas with gas remaining. diff --git a/Resources/Locale/en-US/station-events/events/immovable-rod.ftl b/Resources/Locale/en-US/station-events/events/immovable-rod.ftl index 06abcc85c38..ff39923ecdd 100644 --- a/Resources/Locale/en-US/station-events/events/immovable-rod.ftl +++ b/Resources/Locale/en-US/station-events/events/immovable-rod.ftl @@ -1 +1 @@ -station-event-immovable-rod-start-announcement = High velocity unidentified object is on a collision course with the station. Impact imminent. +station-event-immovable-rod-announcement = High velocity unidentified object is on a collision course with the station. Impact imminent. diff --git a/Resources/Locale/en-US/station-events/events/ion-storm.ftl b/Resources/Locale/en-US/station-events/events/ion-storm.ftl index 28192d96637..10e8f190061 100644 --- a/Resources/Locale/en-US/station-events/events/ion-storm.ftl +++ b/Resources/Locale/en-US/station-events/events/ion-storm.ftl @@ -1,4 +1,4 @@ -station-event-ion-storm-start-announcement = Ion storm detected near the station. Please check all AI-controlled equipment for errors. +station-event-ion-storm-announcement = Ion storm detected near the station. Please check all AI-controlled equipment for errors. ion-storm-law-scrambled-number = [font="Monospace"][scramble rate=250 length={$length} chars="@@###$$&%!01"/][/font] diff --git a/Resources/Locale/en-US/station-events/events/kudzu-growth.ftl b/Resources/Locale/en-US/station-events/events/kudzu-growth.ftl index e0725deaf15..9ba4adb4a2d 100644 --- a/Resources/Locale/en-US/station-events/events/kudzu-growth.ftl +++ b/Resources/Locale/en-US/station-events/events/kudzu-growth.ftl @@ -1 +1 @@ -station-event-kudzu-growth-start-announcement = Attention crew, we have detected a Type 2 Biological Invader on-station, that poses potentially serious threat to crew productivity. We advise you to exterminate it. +station-event-kudzu-growth-announcement = Attention crew, we have detected a Type 2 Biological Invader on-station, that poses potentially serious threat to crew productivity. We advise you to exterminate it. diff --git a/Resources/Locale/en-US/station-events/events/meteor-swarm.ftl b/Resources/Locale/en-US/station-events/events/meteor-swarm.ftl index 6a96c560481..9066fb52121 100644 --- a/Resources/Locale/en-US/station-events/events/meteor-swarm.ftl +++ b/Resources/Locale/en-US/station-events/events/meteor-swarm.ftl @@ -1,2 +1,2 @@ -station-event-meteor-swarm-start-announcement = Meteors are on a collision course with the station. Brace for impact. -station-event-meteor-swarm-end-announcement = The meteor swarm has passed. Please return to your stations. +station-event-meteor-swarm-announcement = Meteors are on a collision course with the station. Brace for impact. +station-event-meteor-swarm-complete-announcement = The meteor swarm has passed. Please return to your stations. diff --git a/Resources/Locale/en-US/station-events/events/power-grid-check.ftl b/Resources/Locale/en-US/station-events/events/power-grid-check.ftl index b42dbd19db3..90ede6b9965 100644 --- a/Resources/Locale/en-US/station-events/events/power-grid-check.ftl +++ b/Resources/Locale/en-US/station-events/events/power-grid-check.ftl @@ -1,4 +1,4 @@ ## PowerGridCheck -station-event-power-grid-check-start-announcement = Abnormal activity detected in the station's powernet. As a precautionary measure, the station's power will be shut off for an indeterminate duration. -station-event-power-grid-check-end-announcement = Power has been restored to the station. We apologize for the inconvenience. \ No newline at end of file +station-event-power-grid-check-announcement = Abnormal activity detected in the station's powernet. As a precautionary measure, the station's power will be shut off for an indeterminate duration. +station-event-power-grid-check-complete-announcement = Power has been restored to the station. We apologize for the inconvenience. diff --git a/Resources/Locale/en-US/station-events/events/radiation-storm.ftl b/Resources/Locale/en-US/station-events/events/radiation-storm.ftl index c8f07c1b2ff..a3c5f48e84f 100644 --- a/Resources/Locale/en-US/station-events/events/radiation-storm.ftl +++ b/Resources/Locale/en-US/station-events/events/radiation-storm.ftl @@ -1,4 +1,4 @@ ## RadiationStorm -station-event-radiation-storm-start-announcement = High levels of radiation detected near the station. Evacuate any areas containing abnormal green energy fields. -station-event-radiation-storm-end-announcement = The radiation threat has passed. Please return to your workplaces. \ No newline at end of file +station-event-radiation-storm-announcement = High levels of radiation detected near the station. Evacuate any areas containing abnormal green energy fields. +station-event-radiation-storm-complete-announcement = The radiation threat has passed. Please return to your workplaces. diff --git a/Resources/Locale/en-US/station-events/events/solar-flare.ftl b/Resources/Locale/en-US/station-events/events/solar-flare.ftl index 5c88f82ded1..617b65739a8 100644 --- a/Resources/Locale/en-US/station-events/events/solar-flare.ftl +++ b/Resources/Locale/en-US/station-events/events/solar-flare.ftl @@ -1,2 +1,2 @@ -station-event-solar-flare-start-announcement = A solar flare has been detected near the station. Some communication channels may not function. -station-event-solar-flare-end-announcement = The solar flare ended. Communication channels no longer affected. +station-event-solar-flare-announcement = A solar flare has been detected near the station. Some communication channels may not function. +station-event-solar-flare-complete-announcement = The solar flare ended. Communication channels no longer affected. diff --git a/Resources/Locale/en-US/station-events/events/vent-clog.ftl b/Resources/Locale/en-US/station-events/events/vent-clog.ftl index f87f9e241b2..1d6ddd136b7 100644 --- a/Resources/Locale/en-US/station-events/events/vent-clog.ftl +++ b/Resources/Locale/en-US/station-events/events/vent-clog.ftl @@ -1 +1 @@ -station-event-vent-clog-start-announcement = The scrubbers network is experiencing a backpressure surge. Some ejection of contents may occur. +station-event-vent-clog-announcement = The scrubbers network is experiencing a backpressure surge. Some ejection of contents may occur. diff --git a/Resources/Locale/en-US/station-events/events/vent-critters.ftl b/Resources/Locale/en-US/station-events/events/vent-critters.ftl index 426f0c0ca1a..7c0103cd559 100644 --- a/Resources/Locale/en-US/station-events/events/vent-critters.ftl +++ b/Resources/Locale/en-US/station-events/events/vent-critters.ftl @@ -1 +1,4 @@ -station-event-vent-creatures-start-announcement = Attention. A large influx of unknown life forms have been detected residing within the station's ventilation systems. Please be rid of these creatures before it begins to affect productivity. +station-event-cockroach-migration-announcement = Attention. A large influx of unknown life forms have been detected residing within the station's ventilation systems. Please be rid of these creatures before it begins to affect productivity. +station-event-slimes-spawn-announcement = Attention. A large influx of unknown life forms have been detected residing within the station's ventilation systems. Please be rid of these creatures before it begins to affect productivity. +station-event-vent-critters-announcement = Attention. A large influx of unknown life forms have been detected residing within the station's ventilation systems. Please be rid of these creatures before it begins to affect productivity. +station-event-spider-spawn-announcement = Attention. A large influx of unknown life forms have been detected residing within the station's ventilation systems. Please be rid of these creatures before it begins to affect productivity. 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 2f59d322282..f8cef13ecd3 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -11,6 +11,9 @@ trait-description-Pacifist = You cannot attack or hurt any living beings. trait-name-LightweightDrunk = Lightweight Drunk trait-description-LightweightDrunk = Alcohol has a stronger effect on you +trait-name-HeavyweightDrunk = Heavyweight Drunk +trait-description-HeavyweightDrunk = Alcohols are afraid of you + trait-name-Muted = Muted trait-description-Muted = You can't speak @@ -42,3 +45,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/Announcers/!randomAnnouncers.yml b/Resources/Prototypes/Announcers/!randomAnnouncers.yml new file mode 100644 index 00000000000..a796094b28d --- /dev/null +++ b/Resources/Prototypes/Announcers/!randomAnnouncers.yml @@ -0,0 +1,8 @@ +- type: weightedRandom + id: RandomAnnouncers + weights: + Intern: 0.15 + MedBot: 0.5 + Michael: 0.9 + NEIL: 1 + VoxFem: 0.55 diff --git a/Resources/Prototypes/Announcers/intern.yml b/Resources/Prototypes/Announcers/intern.yml new file mode 100644 index 00000000000..0a1a4049b6d --- /dev/null +++ b/Resources/Prototypes/Announcers/intern.yml @@ -0,0 +1,134 @@ +- type: announcer + id: Intern + basePath: /Audio/Announcers/Intern + baseAudioParams: + volume: -7 + announcements: + # Communications + - id: announce # Communications console + path: comms/announce.ogg + # - id: attention # Generic alert sound # Should be different from fallback but it's very similar + # path: comms/attention.ogg + - id: commandReport # Station goal, Central Command messages, etc + collection: InternCommandReportAnnouncements + # - id: spawnAnnounceCaptain # Captain arrives on the station # TODO That system is annoyingly not modular + # path: comms/spawn_announce.ogg + # - id: war # Nuclear Operative declaration of war + # path: comms/war.ogg + # - id: nukeCodes # The station has been send nuclear activation codes + # path: comms/nuke_codes.ogg # Or command_report.ogg if you want + # - id: nukeArm # The nuke is active and ticking + # path: comms/nuke_arm.ogg + # - id: nukeDisarm # The nuke has been disarmed + # path: comms/nuke_disarm.ogg + - id: welcome # The shift has started + collection: InternWelcomeAnnouncements + + # Alert levels + # - id: alertGreen # Everything is fine + # path: alerts/green.ogg + # - id: alertBlue # Something is amiss + # path: alerts/blue.ogg + # - id: alertViolet # Viral infection or misc medical emergencies, listen to Medical + # path: alerts/violet.ogg + # - id: alertWhite # Glimmer is too high, listen to Epistemics + # path: alerts/white.ogg + # - id: alertYellow # The station is being largely damaged, listen to Engineering + # path: alerts/yellow.ogg + # - id: alertRed # Generic many things are bad, listen to Security + # path: alerts/red.ogg + # - id: alertGamma # There is a massive immediate threat to the station, listen to Central Command + # path: alerts/gamma.ogg + # - id: alertDelta # The station is being or about to be massively destroyed, run for your life + # path: alerts/delta.ogg + # - id: alertEpsilon # The station has been terminated, good luck survivors! + # path: alerts/epsilon.ogg + + # Events + ## Wizard's Den + ### Mid-Round Antagonists + # - id: ninjaHacking # A Ninja is hacking something + # path: comms/ninja_hacking.ogg + # - id: powerSinkExplosion # A power sink is about to overcharge and explode + # path: comms/powersink_explosion.ogg + ### Events + - id: anomalySpawn # An anomaly has spawned in a random place + path: events/anomaly.ogg + # - id: bluespaceArtifact # An artifact has spawned in a random place + # path: events/bluespace_artifact.ogg + # - id: bluespaceLocker # Two random lockers now share inventories + # path: events/bluespace_locker.ogg + # - id: breakerFlip # A few random APCs have been disabled, ask Engineering to fix them + # path: events/breaker_flip.ogg + # - id: bureaucraticError # Random jobs have been added, removed, or made infinite + # path: events/bureaucratic_error.ogg + # - id: clericalError # Random crew are removed from the manifest + # path: events/clerical_error.ogg + # - id: carpRift # A dragon's carp rift is active + # path: events/carp_rift.ogg + # - id: revenantSpawn # A revenant has spawned (by a prober?) + # path: events/revenant_spawn.ogg + # - id: gasLeak # A random gas is coming out of a random vent + # path: events/gas_leak.ogg + # - id: gasLeakComplete # Gas has stopped coming out of a vent + # path: events/gas_leak-complete.ogg + # - id: kudzuGrowth # Kudzu is growing in a random place + # path: events/kudzu_growth.ogg + - id: meteorSwarm # Meteors are flying at the station, stay away from windows + path: events/meteors.ogg + # - id: meteorSwarmComplete # Meteors have stopped flying at the station + # path: events/meteors-complete.ogg + # - id: mouseMigration # Several mice have appeared in a random place + # path: events/mouse_migration.ogg + # - id: cockroachMigration # Several cockroaches have appeared in a random place + # path: events/cockroach_migration.ogg + - id: powerGridCheck # The station's power is offline for some moments + path: events/power_grid_check.ogg + - id: powerGridCheckComplete # The station's power is online again + path: events/power_grid_check-complete.ogg + # - id: randomSentience # A random few animals have become sentient + # path: events/random_sentience.ogg + # - id: solarFlare # A solar flare is nearby, may mess with comms and electronics + # path: events/solar_flare.ogg + # - id: solarFlareComplete # The solar flare has passed + # path: events/solar_flare-complete.ogg + # - id: ventClog # A random reagent is coming out of a scrubber + # path: events/vent_clog.ogg + # - id: slimesSpawn # Some simple slimes are appearing in vents + # path: events/slimes_spawn.ogg + # - id: spiderSpawn # Some simple spiders are appearing in vents + # path: events/spider_spawn.ogg + # - id: immovableRodSpawn # The station is moving into an immovable rod, don't die or something, ask Engineering for help repairing it + # path: events/immovable_rod_spawn.ogg + - id: ionStorm # AI-controlled equipment are now weird, check their laws + path: events/ion_storm.ogg + ## Delta-V + - id: xenoVents # Xenomorphs are coming out of vents + path: events/aliens.ogg + ## NyanoTrasen + # - id: noosphericStorm # A large amount of glimmer has joined the station and made people psionic + # path: events/noospheric_storm.ogg + + # Shuttle + - id: shuttleCalled # The shuttle is on its way + path: shuttle/called.ogg + - id: shuttleRecalled # The shuttle is going back to Central Command + path: shuttle/recalled.ogg + - id: shuttleDock # The shuttle has arrived at the station + path: shuttle/dock.ogg + # - id: shuttleNearby # The shuttle couldn't dock, it's at a specified location + # path: shuttle/nearby.ogg + # - id: shuttleGoodLuck # The shuttle could not find its way to the station, good luck crew + # path: shuttle/good_luck.ogg + # - id: shuttleAuthAdded # One of few have added their acceptance to early launching + # path: shuttle/auth_added.ogg + # - id: shuttleAuthRevoked # One of few have revoked their acceptance to early launching + # path: shuttle/auth_revoked.ogg + # - id: shuttleAlmostLaunching # The shuttle will leave to FTL in 10 seconds + # path: shuttle/almost_launching.ogg + # - id: shuttleLeft # The shuttle has left the station + # path: shuttle/left.ogg + + # Fallback # REQUIRED + - id: fallback # Any announcement sent without a valid announcement on this announcer will use this + collection: InternAnnouncements diff --git a/Resources/Prototypes/Announcers/medbot.yml b/Resources/Prototypes/Announcers/medbot.yml new file mode 100644 index 00000000000..3b1fba49721 --- /dev/null +++ b/Resources/Prototypes/Announcers/medbot.yml @@ -0,0 +1,132 @@ +- type: announcer + id: MedBot + basePath: /Audio/Announcers/MedBot + announcements: + # Communications + - id: announce # Communications console + path: comms/announce.ogg + - id: attention # Generic alert sound # Should be different from fallback but it's very similar + path: comms/attention.ogg + - id: commandReport # Station goal, Central Command messages, etc + path: comms/command_report.ogg + # - id: spawnAnnounceCaptain # Captain arrives on the station # TODO That system is annoyingly not modular + # path: comms/spawn_announce.ogg + # - id: war # Nuclear Operative declaration of war + # path: comms/war.ogg + # - id: nukeCodes # The station has been send nuclear activation codes + # path: comms/nuke_codes.ogg # Or command_report.ogg if you want + # - id: nukeArm # The nuke is active and ticking + # path: comms/nuke_arm.ogg + # - id: nukeDisarm # The nuke has been disarmed + # path: comms/nuke_disarm.ogg + - id: welcome # The shift has started + path: comms/welcome.ogg + + # Alert levels + # - id: alertGreen # Everything is fine + # path: alerts/green.ogg + # - id: alertBlue # Something is amiss + # path: alerts/blue.ogg + # - id: alertViolet # Viral infection or misc medical emergencies, listen to Medical + # path: alerts/violet.ogg + # - id: alertWhite # Glimmer is too high, listen to Epistemics + # path: alerts/white.ogg + # - id: alertYellow # The station is being largely damaged, listen to Engineering + # path: alerts/yellow.ogg + # - id: alertRed # Generic many things are bad, listen to Security + # path: alerts/red.ogg + # - id: alertGamma # There is a massive immediate threat to the station, listen to Central Command + # path: alerts/gamma.ogg + # - id: alertDelta # The station is being or about to be massively destroyed, run for your life + # path: alerts/delta.ogg + # - id: alertEpsilon # The station has been terminated, good luck survivors! + # path: alerts/epsilon.ogg + + # Events + ## Wizard's Den + ### Mid-Round Antagonists + # - id: ninjaHacking # A Ninja is hacking something + # path: comms/ninja_hacking.ogg + # - id: powerSinkExplosion # A power sink is about to overcharge and explode + # path: comms/powersink_explosion.ogg + ### Events + - id: anomalySpawn # An anomaly has spawned in a random place + path: events/anomaly.ogg + # - id: bluespaceArtifact # An artifact has spawned in a random place + # path: events/bluespace_artifact.ogg + # - id: bluespaceLocker # Two random lockers now share inventories + # path: events/bluespace_locker.ogg + # - id: breakerFlip # A few random APCs have been disabled, ask Engineering to fix them + # path: events/breaker_flip.ogg + # - id: bureaucraticError # Random jobs have been added, removed, or made infinite + # path: events/bureaucratic_error.ogg + # - id: clericalError # Random crew are removed from the manifest + # path: events/clerical_error.ogg + # - id: carpRift # A dragon's carp rift is active + # path: events/carp_rift.ogg + # - id: revenantSpawn # A revenant has spawned (by a prober?) + # path: events/revenant_spawn.ogg + # - id: gasLeak # A random gas is coming out of a random vent + # path: events/gas_leak.ogg + # - id: gasLeakComplete # Gas has stopped coming out of a vent + # path: events/gas_leak-complete.ogg + # - id: kudzuGrowth # Kudzu is growing in a random place + # path: events/kudzu_growth.ogg + - id: meteorSwarm # Meteors are flying at the station, stay away from windows + path: events/meteors.ogg + # - id: meteorSwarmComplete # Meteors have stopped flying at the station + # path: events/meteors-complete.ogg + # - id: mouseMigration # Several mice have appeared in a random place + # path: events/mouse_migration.ogg + # - id: cockroachMigration # Several cockroaches have appeared in a random place + # path: events/cockroach_migration.ogg + - id: powerGridCheck # The station's power is offline for some moments + path: events/power_grid_check.ogg + - id: powerGridCheckComplete # The station's power is online again + path: events/power_grid_check-complete.ogg + # - id: randomSentience # A random few animals have become sentient + # path: events/random_sentience.ogg + # - id: solarFlare # A solar flare is nearby, may mess with comms and electronics + # path: events/solar_flare.ogg + # - id: solarFlareComplete # The solar flare has passed + # path: events/solar_flare-complete.ogg + # - id: ventClog # A random reagent is coming out of a scrubber + # path: events/vent_clog.ogg + # - id: slimesSpawn # Some simple slimes are appearing in vents + # path: events/slimes_spawn.ogg + # - id: spiderSpawn # Some simple spiders are appearing in vents + # path: events/spider_spawn.ogg + # - id: immovableRodSpawn # The station is moving into an immovable rod, don't die or something, ask Engineering for help repairing it + # path: events/immovable_rod_spawn.ogg + - id: ionStorm # AI-controlled equipment are now weird, check their laws + path: events/ion_storm.ogg + ## Delta-V + - id: xenoVents # Xenomorphs are coming out of vents + path: events/aliens.ogg + ## NyanoTrasen + # - id: noosphericStorm # A large amount of glimmer has joined the station and made people psionic + # path: events/noospheric_storm.ogg + + # Shuttle + - id: shuttleCalled # The shuttle is on its way + path: shuttle/called.ogg + - id: shuttleRecalled # The shuttle is going back to Central Command + path: shuttle/recalled.ogg + - id: shuttleDock # The shuttle has arrived at the station + path: shuttle/dock.ogg + # - id: shuttleNearby # The shuttle couldn't dock, it's at a specified location + # path: shuttle/nearby.ogg + # - id: shuttleGoodLuck # The shuttle could not find its way to the station, good luck crew + # path: shuttle/good_luck.ogg + # - id: shuttleAuthAdded # One of few have added their acceptance to early launching + # path: shuttle/auth_added.ogg + # - id: shuttleAuthRevoked # One of few have revoked their acceptance to early launching + # path: shuttle/auth_revoked.ogg + # - id: shuttleAlmostLaunching # The shuttle will leave to FTL in 10 seconds + # path: shuttle/almost_launching.ogg + # - id: shuttleLeft # The shuttle has left the station + # path: shuttle/left.ogg + + # Fallback # REQUIRED + - id: fallback # Any announcement sent without a valid announcement on this announcer will use this + path: fallback.ogg diff --git a/Resources/Prototypes/Announcers/michael.yml b/Resources/Prototypes/Announcers/michael.yml new file mode 100644 index 00000000000..f63b934fbc6 --- /dev/null +++ b/Resources/Prototypes/Announcers/michael.yml @@ -0,0 +1,134 @@ +- type: announcer + id: Michael + basePath: /Audio/Announcers/Michael + announcements: + # Communications + # - id: announce # Communications console + # path: comms/announce.ogg + - id: attention # Generic alert sound # Should be different from fallback but it's very similar + path: comms/attention.ogg + # - id: commandReport # Station goal, Central Command messages, etc + # path: comms/command_report.ogg + # - id: spawnAnnounceCaptain # Captain arrives on the station # TODO That system is annoyingly not modular + # path: comms/spawn_announce.ogg + # - id: war # Nuclear Operative declaration of war + # path: comms/war.ogg + # - id: nukeCodes # The station has been send nuclear activation codes + # path: comms/nuke_codes.ogg # Or command_report.ogg if you want + # - id: nukeArm # The nuke is active and ticking + # path: comms/nuke_arm.ogg + # - id: nukeDisarm # The nuke has been disarmed + # path: comms/nuke_disarm.ogg + - id: welcome # The shift has started + path: comms/welcome.ogg + + # Alert levels + - id: alertGreen # Everything is fine + path: alerts/green.ogg + - id: alertBlue # Something is amiss + path: alerts/blue.ogg + - id: alertViolet # Viral infection or misc medical emergencies, listen to Medical + path: alerts/violet.ogg + - id: alertWhite # Glimmer is too high, listen to Epistemics + path: alerts/white.ogg + - id: alertYellow # The station is being largely damaged, listen to Engineering + path: alerts/yellow.ogg + - id: alertRed # Generic many things are bad, listen to Security + path: alerts/red.ogg + - id: alertGamma # There is a massive immediate threat to the station, listen to Central Command + path: alerts/gamma.ogg + # - id: alertDelta # The station is being or about to be massively destroyed, run for your life + # path: alerts/delta.ogg + - id: alertEpsilon # The station has been terminated, good luck survivors! + path: alerts/epsilon.ogg + audioParams: + volume: -3 + + # Events + ## Wizard's Den + ### Mid-Round Antagonists + # - id: ninjaHacking # A Ninja is hacking something + # path: comms/ninja_hacking.ogg + # - id: powerSinkExplosion # A power sink is about to overcharge and explode + # path: comms/powersink_explosion.ogg + ### Events + # - id: anomalySpawn # An anomaly has spawned in a random place + # path: events/anomaly.ogg + # - id: bluespaceArtifact # An artifact has spawned in a random place + # path: events/bluespace_artifact.ogg + # - id: bluespaceLocker # Two random lockers now share inventories + # path: events/bluespace_locker.ogg + # - id: breakerFlip # A few random APCs have been disabled, ask Engineering to fix them + # path: events/breaker_flip.ogg + - id: bureaucraticError # Random jobs have been added, removed, or made infinite + path: events/bureaucratic_error.ogg + # - id: clericalError # Random crew are removed from the manifest + # path: events/clerical_error.ogg + # - id: carpRift # A dragon's carp rift is active + # path: events/carp_rift.ogg + # - id: revenantSpawn # A revenant has spawned (by a prober?) + # path: events/revenant_spawn.ogg + - id: gasLeak # A random gas is coming out of a random vent + path: events/gas_leak.ogg + # - id: gasLeakComplete # Gas has stopped coming out of a vent + # path: events/gas_leak-complete.ogg + - id: kudzuGrowth # Kudzu is growing in a random place + path: events/kudzu_growth.ogg + - id: meteorSwarm # Meteors are flying at the station, stay away from windows + path: events/meteors.ogg + # - id: meteorSwarmComplete # Meteors have stopped flying at the station + # path: events/meteors-complete.ogg + # - id: mouseMigration # Several mice have appeared in a random place + # path: events/mouse_migration.ogg + # - id: cockroachMigration # Several cockroaches have appeared in a random place + # path: events/cockroach_migration.ogg + - id: powerGridCheck # The station's power is offline for some moments + path: events/power_grid_check.ogg + - id: powerGridCheckComplete # The station's power is online again + path: events/power_grid_check-complete.ogg + # - id: randomSentience # A random few animals have become sentient + # path: events/random_sentience.ogg + # - id: solarFlare # A solar flare is nearby, may mess with comms and electronics + # path: events/solar_flare.ogg + # - id: solarFlareComplete # The solar flare has passed + # path: events/solar_flare-complete.ogg + - id: ventClog # A random reagent is coming out of a scrubber + path: events/vent_clog.ogg + # - id: slimesSpawn # Some simple slimes are appearing in vents + # path: events/slimes_spawn.ogg + # - id: spiderSpawn # Some simple spiders are appearing in vents + # path: events/spider_spawn.ogg + # - id: immovableRodSpawn # The station is moving into an immovable rod, don't die or something, ask Engineering for help repairing it + # path: events/immovable_rod_spawn.ogg + # - id: ionStorm # AI-controlled equipment are now weird, check their laws + # path: events/ion_storm.ogg + ## Delta-V + - id: xenoVents # Xenomorphs are coming out of vents + path: events/aliens.ogg + ## NyanoTrasen + - id: noosphericStorm # A large amount of glimmer has joined the station and made people psionic + path: events/noospheric_storm.ogg + + # Shuttle + - id: shuttleCalled # The shuttle is on its way + path: shuttle/called.ogg + - id: shuttleRecalled # The shuttle is going back to Central Command + path: shuttle/recalled.ogg + - id: shuttleDock # The shuttle has arrived at the station + path: shuttle/dock.ogg + # - id: shuttleNearby # The shuttle couldn't dock, it's at a specified location + # path: shuttle/nearby.ogg + # - id: shuttleGoodLuck # The shuttle could not find its way to the station, good luck crew + # path: shuttle/good_luck.ogg + # - id: shuttleAuthAdded # One of few have added their acceptance to early launching + # path: shuttle/auth_added.ogg + # - id: shuttleAuthRevoked # One of few have revoked their acceptance to early launching + # path: shuttle/auth_revoked.ogg + # - id: shuttleAlmostLaunching # The shuttle will leave to FTL in 10 seconds + # path: shuttle/almost_launching.ogg + # - id: shuttleLeft # The shuttle has left the station + # path: shuttle/left.ogg + + # Fallback # REQUIRED + - id: fallback # Any announcement sent without a valid announcement on this announcer will use this + path: fallback.ogg diff --git a/Resources/Prototypes/Announcers/neil.yml b/Resources/Prototypes/Announcers/neil.yml new file mode 100644 index 00000000000..1db1828ed11 --- /dev/null +++ b/Resources/Prototypes/Announcers/neil.yml @@ -0,0 +1,134 @@ +- type: announcer + id: NEIL + basePath: /Audio/Announcers/NEIL + baseAudioParams: + volume: -3 + announcements: + # Communications + - id: announce # Communications console + path: comms/announce.ogg + - id: attention # Generic alert sound # Should be different from fallback but it's very similar + path: comms/attention.ogg + # - id: commandReport # Station goal, Central Command messages, etc + # path: comms/command_report.ogg + # - id: spawnAnnounceCaptain # Captain arrives on the station # TODO That system is annoyingly not modular + # path: comms/spawn_announce.ogg + # - id: war # Nuclear Operative declaration of war + # path: comms/war.ogg + # - id: nukeCodes # The station has been send nuclear activation codes + # path: comms/nuke_codes.ogg # Or command_report.ogg if you want + # - id: nukeArm # The nuke is active and ticking + # path: comms/nuke_arm.ogg + # - id: nukeDisarm # The nuke has been disarmed + # path: comms/nuke_disarm.ogg + - id: welcome # The shift has started + path: comms/welcome.ogg + + # Alert levels + - id: alertGreen # Everything is fine + path: alerts/green.ogg + - id: alertBlue # Something is amiss + path: alerts/blue.ogg + - id: alertViolet # Viral infection or misc medical emergencies, listen to Medical + path: alerts/violet.ogg + - id: alertWhite # Glimmer is too high, listen to Epistemics + path: alerts/white.ogg + - id: alertYellow # The station is being largely damaged, listen to Engineering + path: alerts/yellow.ogg + - id: alertRed # Generic many things are bad, listen to Security + path: alerts/red.ogg + - id: alertGamma # There is a massive immediate threat to the station, listen to Central Command + path: alerts/gamma.ogg + - id: alertDelta # The station is being or about to be massively destroyed, run for your life + path: alerts/delta.ogg + - id: alertEpsilon # The station has been terminated, good luck survivors! + path: alerts/epsilon.ogg + + # Events + ## Wizard's Den + ### Mid-Round Antagonists + # - id: ninjaHacking # A Ninja is hacking something + # path: comms/ninja_hacking.ogg + # - id: powerSinkExplosion # A power sink is about to overcharge and explode + # path: comms/powersink_explosion.ogg + ### Events + - id: anomalySpawn # An anomaly has spawned in a random place + path: events/anomaly.ogg + # - id: bluespaceArtifact # An artifact has spawned in a random place + # path: events/bluespace_artifact.ogg + # - id: bluespaceLocker # Two random lockers now share inventories + # path: events/bluespace_locker.ogg + - id: breakerFlip # A few random APCs have been disabled, ask Engineering to fix them + path: events/breaker_flip.ogg + - id: bureaucraticError # Random jobs have been added, removed, or made infinite + path: events/bureaucratic_error.ogg + # - id: clericalError # Random crew are removed from the manifest + # path: events/clerical_error.ogg + # - id: carpRift # A dragon's carp rift is active + # path: events/carp_rift.ogg + # - id: revenantSpawn # A revenant has spawned (by a prober?) + # path: events/revenant_spawn.ogg + - id: gasLeak # A random gas is coming out of a random vent + path: events/gas_leak.ogg + # - id: gasLeakComplete # Gas has stopped coming out of a vent + # path: events/gas_leak-complete.ogg + - id: kudzuGrowth # Kudzu is growing in a random place + path: events/kudzu_growth.ogg + - id: meteorSwarm # Meteors are flying at the station, stay away from windows + path: events/meteors.ogg + # - id: meteorSwarmComplete # Meteors have stopped flying at the station + # path: events/meteors-complete.ogg + # - id: mouseMigration # Several mice have appeared in a random place + # path: events/mouse_migration.ogg + # - id: cockroachMigration # Several cockroaches have appeared in a random place + # path: events/cockroach_migration.ogg + - id: powerGridCheck # The station's power is offline for some moments + path: events/power_grid_check.ogg + - id: powerGridCheckComplete # The station's power is online again + path: events/power_grid_check-complete.ogg + # - id: randomSentience # A random few animals have become sentient + # path: events/random_sentience.ogg + # - id: solarFlare # A solar flare is nearby, may mess with comms and electronics + # path: events/solar_flare.ogg + # - id: solarFlareComplete # The solar flare has passed + # path: events/solar_flare-complete.ogg + - id: ventClog # A random reagent is coming out of a scrubber + path: events/vent_clog.ogg + # - id: slimesSpawn # Some simple slimes are appearing in vents + # path: events/slimes_spawn.ogg + # - id: spiderSpawn # Some simple spiders are appearing in vents + # path: events/spider_spawn.ogg + # - id: immovableRodSpawn # The station is moving into an immovable rod, don't die or something, ask Engineering for help repairing it + # path: events/immovable_rod_spawn.ogg + - id: ionStorm # AI-controlled equipment are now weird, check their laws + path: events/ion_storm.ogg + ## Delta-V + # - id: xenoVents # Xenomorphs are coming out of vents + # path: events/xeno_vents.ogg + ## NyanoTrasen + - id: noosphericStorm # A large amount of glimmer has joined the station and made people psionic + path: events/noospheric_storm.ogg + + # Shuttle + - id: shuttleCalled # The shuttle is on its way + path: shuttle/called.ogg + - id: shuttleRecalled # The shuttle is going back to Central Command + path: shuttle/recalled.ogg + - id: shuttleDock # The shuttle has arrived at the station + path: shuttle/dock.ogg + # - id: shuttleNearby # The shuttle couldn't dock, it's at a specified location + # path: shuttle/nearby.ogg + # - id: shuttleGoodLuck # The shuttle could not find its way to the station, good luck crew + # path: shuttle/good_luck.ogg + # - id: shuttleAuthAdded # One of few have added their acceptance to early launching + # path: shuttle/auth_added.ogg + # - id: shuttleAuthRevoked # One of few have revoked their acceptance to early launching + # path: shuttle/auth_revoked.ogg + # - id: shuttleAlmostLaunching # The shuttle will leave to FTL in 10 seconds + # path: shuttle/almost_launching.ogg + # - id: shuttleLeft # The shuttle has left the station + # path: shuttle/left.ogg + + # Fallback # REQUIRED + - id: fallback # Any announcement sent without a valid announcement on this announcer will use this + path: fallback.ogg diff --git a/Resources/Prototypes/Announcers/template b/Resources/Prototypes/Announcers/template new file mode 100644 index 00000000000..8cd007956de --- /dev/null +++ b/Resources/Prototypes/Announcers/template @@ -0,0 +1,158 @@ +# This should be getting auto detected as YML if you're using VSCode +# If it's not, you can probably manually set the language mode to YML somehow +# Or just change the filename to `template.yml` temporarily +# The game will complain if I name it template.yaml in an attempt to not load it +# +# This file contains instructions for everything you can do with the announcer system +# This also contains every "needed" announcement for any new announcers and descriptions for what they are +# +# Avoid renaming announcement audio files to keep consistency between announcers for workspace searching +# Keep comments on specific announcements and follow this same formatting for every announcer +# If you don't have an announcement audio, comment the announcemt type instead of deleting it + +- type: announcer + id: Announcer # Localized as "announcer--name" in chat + basePath: /Audio/Codebase/Announcements/Announcer # Where to start looking for audio files + baseAudioParams: # Default audio parameters for all announcements, all options explained in the template announcement + volume: -7 # If this announcer is really loud, lower it to match the others' volume #? Default is 3 + announcements: # List of all announcements this announcer has audio for #! Comment out unused announcements, don't remove them + # Template, delete this in real announcer files + - id: template # Lowercase of the event ID, add "Complete" to the end for post-event announcements (endings) + ignoreBasePath: false # If true, it will ignore the basePath and use the path as is + path: template.ogg # Path to the file relative to basePath/, named with snake_case except for "-complete" + collection: AnnouncerTemplateAnnouncements # Collection of audios to randomly use for this, will ignore path if set #! Ignores basePath automatically + message: announcer-announcement-template # Localization key for the announcement message to use instead of the default one # NOTE this does not pass through previous loc args yet + audioParams: # Overrides baseAudioParams entirely for this specific announcement, numbers are all floats + volume: 3 # We don't want individual announcement volumes to vary too much, normalize them with this #? Default is 3 + pitch: 1 #? Default is 1 + playOffsetSeconds: 0 # How many seconds into the audio to start from #? Default is 0 + variation: 0 # Probably wouldn't sound very good unless very low, 0.15 or less is normally used #? Default is 0 + + # Communications + - id: announce # Communications console + path: comms/announce.ogg + - id: attention # Generic alert sound # Should be different from fallback but it's very similar + path: comms/attention.ogg + - id: commandReport # Station goal, Central Command messages, etc + path: comms/command_report.ogg + - id: spawnAnnounce # Captain join # TODO That system is annoyingly not modular + path: comms/spawn_announce.ogg + - id: war # Nuclear Operative declaration of war + path: comms/war.ogg + - id: nukeCodes # The station has been send nuclear activation codes + path: comms/nuke_codes.ogg # Or command_report.ogg if you want + - id: nukeArm # The nuke is active and ticking + path: comms/nuke_arm.ogg + - id: nukeDisarm # The nuke has been disarmed + path: comms/nuke_disarm.ogg + - id: welcome # The shift has started + path: comms/welcome.ogg + + # Alert levels + - id: alertGreen # Everything is fine + path: alerts/green.ogg + - id: alertBlue # Something is amiss + path: alerts/blue.ogg + - id: alertViolet # Viral infection or misc medical emergencies, listen to Medical + path: alerts/violet.ogg + - id: alertWhite # Glimmer is too high, listen to Epistemics + path: alerts/white.ogg + - id: alertYellow # The station is being largely damaged, listen to Engineering + path: alerts/yellow.ogg + - id: alertRed # Generic many things are bad, listen to Security + path: alerts/red.ogg + - id: alertGamma # There is a massive immediate threat to the station, listen to Central Command + path: alerts/gamma.ogg + - id: alertDelta # The station is being or about to be massively destroyed, run for your life + path: alerts/delta.ogg + - id: alertEpsilon # The station has been terminated, good luck survivors! + path: alerts/epsilon.ogg + + # Events + ## Wizard's Den + ### Mid-Round Antagonists + - id: ninjaHacking # A Ninja is hacking something + path: comms/ninja_hacking.ogg + - id: powersinkExplosion # A power sink is about to overcharge and explode + path: comms/powersink_explosion.ogg + ### Events + - id: anomalySpawn # An anomaly has spawned in a random place + path: events/anomaly.ogg + - id: bluespaceArtifact # An artifact has spawned in a random place + path: events/bluespace_artifact.ogg + - id: bluespaceLocker # Two random lockers now share inventories + path: events/bluespace_locker.ogg + - id: breakerFlip # A few random APCs have been disabled, ask Engineering to fix them + path: events/breaker_flip.ogg + - id: bureaucraticError # Random jobs have been added, removed, or made infinite + path: events/bureaucratic_error.ogg + - id: clericalError # Random crew are removed from the manifest + path: events/clerical_error.ogg + - id: carpRift # A dragon's carp rift is active + path: events/carp_rift.ogg + - id: revenantSpawn # A revenant has spawned (by a prober?) + path: events/revenant_spawn.ogg + - id: gasLeak # A random gas is coming out of a random vent + path: events/gas_leak.ogg + - id: gasLeakComplete # Gas has stopped coming out of a vent + path: events/gas_leak-complete.ogg + - id: kudzuGrowth # Kudzu is growing in a random place + path: events/kudzu_growth.ogg + - id: meteorSwarm # Meteors are flying at the station, stay away from windows + path: events/meteors.ogg + - id: meteorSwarmComplete # Meteors have stopped flying at the station + path: events/meteors-complete.ogg + - id: mouseMigration # Several mice have appeared in a random place + path: events/mouse_migration.ogg + - id: cockroachMigration # Several cockroaches have appeared in a random place + path: events/cockroach_migration.ogg + - id: powerGridCheck # The station's power is offline for some moments + path: events/power_grid_check.ogg + - id: powerGridCheckComplete # The station's power is online again + path: events/power_grid_check-complete.ogg + - id: randomSentience # A random few animals have become sentient + path: events/random_sentience.ogg + - id: solarFlare # A solar flare is nearby, may mess with comms and electronics + path: events/solar_flare.ogg + - id: solarFlareComplete # The solar flare has passed + path: events/solar_flare-complete.ogg + - id: ventClog # A random reagent is coming out of a scrubber + path: events/vent_clog.ogg + - id: slimesSpawn # Some simple slimes are appearing in vents + path: events/slimes_spawn.ogg + - id: spiderSpawn # Some simple spiders are appearing in vents + path: events/spider_spawn.ogg + - id: immovableRodSpawn # The station is moving into an immovable rod, don't die or something, ask Engineering for help repairing it + path: events/immovable_rod_spawn.ogg + - id: ionStorm # AI-controlled equipment are now weird, check their laws + path: events/ion_storm.ogg + ## Delta-V + - id: xenoVents # Xenomorphs are coming out of vents + path: events/xeno_vents.ogg + ## NyanoTrasen + - id: noosphericStorm # A large amount of glimmer has joined the station and made people psionic + path: events/noospheric_storm.ogg + + # Shuttle + - id: shuttleCalled # The shuttle is on its way + path: shuttle/called.ogg + - id: shuttleRecalled # The shuttle is going back to Central Command + path: shuttle/recalled.ogg + - id: shuttleDock # The shuttle has arrived at the station + path: shuttle/dock.ogg + - id: shuttleNearby # The shuttle couldn't dock, it's at a specified location + path: shuttle/nearby.ogg + - id: shuttleGoodLuck # The shuttle could not find its way to the station, good luck crew + path: shuttle/good_luck.ogg + - id: shuttleAuthAdded # One of few have added their acceptance to early launching + path: shuttle/auth_added.ogg + - id: shuttleAuthRevoked # One of few have revoked their acceptance to early launching + path: shuttle/auth_revoked.ogg + - id: shuttleAlmostLaunching # The shuttle will leave to FTL in 10 seconds + path: shuttle/almost_launching.ogg + - id: shuttleLeft # The shuttle has left the station + path: shuttle/left.ogg + + # Fallback # REQUIRED + - id: fallback # Any announcement sent without a valid announcement on this announcer will use this + path: fallback.ogg diff --git a/Resources/Prototypes/Announcers/voxfem.yml b/Resources/Prototypes/Announcers/voxfem.yml new file mode 100644 index 00000000000..bcf518e0a2c --- /dev/null +++ b/Resources/Prototypes/Announcers/voxfem.yml @@ -0,0 +1,132 @@ +- type: announcer + id: VoxFem + basePath: /Audio/Announcers/VoxFem + announcements: + # Communications + - id: announce # Communications console + path: comms/announce.ogg + - id: attention # Generic alert sound # Should be different from fallback but it's very similar + path: comms/attention.ogg + - id: commandReport # Station goal, Central Command messages, etc + path: comms/command_report.ogg + # - id: spawnAnnounceCaptain # Captain arrives on the station # TODO That system is annoyingly not modular + # path: comms/spawn_announce.ogg + # - id: war # Nuclear Operative declaration of war + # path: comms/war.ogg + # - id: nukeCodes # The station has been send nuclear activation codes + # path: comms/nuke_codes.ogg # Or command_report.ogg if you want + # - id: nukeArm # The nuke is active and ticking + # path: comms/nuke_arm.ogg + # - id: nukeDisarm # The nuke has been disarmed + # path: comms/nuke_disarm.ogg + - id: welcome # The shift has started + path: comms/welcome.ogg + + # Alert levels + # - id: alertGreen # Everything is fine + # path: alerts/green.ogg + # - id: alertBlue # Something is amiss + # path: alerts/blue.ogg + # - id: alertViolet # Viral infection or misc medical emergencies, listen to Medical + # path: alerts/violet.ogg + # - id: alertWhite # Glimmer is too high, listen to Epistemics + # path: alerts/white.ogg + # - id: alertYellow # The station is being largely damaged, listen to Engineering + # path: alerts/yellow.ogg + # - id: alertRed # Generic many things are bad, listen to Security + # path: alerts/red.ogg + # - id: alertGamma # There is a massive immediate threat to the station, listen to Central Command + # path: alerts/gamma.ogg + # - id: alertDelta # The station is being or about to be massively destroyed, run for your life + # path: alerts/delta.ogg + # - id: alertEpsilon # The station has been terminated, good luck survivors! + # path: alerts/epsilon.ogg + + # Events + ## Wizard's Den + ### Mid-Round Antagonists + - id: ninjaHacking # A Ninja is hacking something + path: comms/ninja_hacking.ogg + - id: powerSinkExplosion # A power sink is about to overcharge and explode + path: comms/powersink_explosion.ogg + ### Events + # - id: anomalySpawn # An anomaly has spawned in a random place + # path: events/anomaly.ogg + # - id: bluespaceArtifact # An artifact has spawned in a random place + # path: events/bluespace_artifact.ogg + # - id: bluespaceLocker # Two random lockers now share inventories + # path: events/bluespace_locker.ogg + # - id: breakerFlip # A few random APCs have been disabled, ask Engineering to fix them + # path: events/breaker_flip.ogg + # - id: bureaucraticError # Random jobs have been added, removed, or made infinite + # path: events/bureaucratic_error.ogg + # - id: clericalError # Random crew are removed from the manifest + # path: events/clerical_error.ogg + # - id: carpRift # A dragon's carp rift is active + # path: events/carp_rift.ogg + # - id: revenantSpawn # A revenant has spawned (by a prober?) + # path: events/revenant_spawn.ogg + # - id: gasLeak # A random gas is coming out of a random vent + # path: events/gas_leak.ogg + # - id: gasLeakComplete # Gas has stopped coming out of a vent + # path: events/gas_leak-complete.ogg + # - id: kudzuGrowth # Kudzu is growing in a random place + # path: events/kudzu_growth.ogg + - id: meteorSwarm # Meteors are flying at the station, stay away from windows + path: events/meteors.ogg + # - id: meteorSwarmComplete # Meteors have stopped flying at the station + # path: events/meteors-complete.ogg + # - id: mouseMigration # Several mice have appeared in a random place + # path: events/mouse_migration.ogg + # - id: cockroachMigration # Several cockroaches have appeared in a random place + # path: events/cockroach_migration.ogg + - id: powerGridCheck # The station's power is offline for some moments + path: events/power_grid_check.ogg + - id: powerGridCheckComplete # The station's power is online again + path: events/power_grid_check-complete.ogg + # - id: randomSentience # A random few animals have become sentient + # path: events/random_sentience.ogg + # - id: solarFlare # A solar flare is nearby, may mess with comms and electronics + # path: events/solar_flare.ogg + # - id: solarFlareComplete # The solar flare has passed + # path: events/solar_flare-complete.ogg + # - id: ventClog # A random reagent is coming out of a scrubber + # path: events/vent_clog.ogg + # - id: slimesSpawn # Some simple slimes are appearing in vents + # path: events/slimes_spawn.ogg + # - id: spiderSpawn # Some simple spiders are appearing in vents + # path: events/spider_spawn.ogg + # - id: immovableRodSpawn # The station is moving into an immovable rod, don't die or something, ask Engineering for help repairing it + # path: events/immovable_rod_spawn.ogg + - id: ionStorm # AI-controlled equipment are now weird, check their laws + path: events/ion_storm.ogg + ## Delta-V + - id: xenoVents # Xenomorphs are coming out of vents + path: events/aliens.ogg + ## NyanoTrasen + # - id: noosphericStorm # A large amount of glimmer has joined the station and made people psionic + # path: events/noospheric_storm.ogg + + # Shuttle + - id: shuttleCalled # The shuttle is on its way + path: shuttle/called.ogg + - id: shuttleRecalled # The shuttle is going back to Central Command + path: shuttle/recalled.ogg + - id: shuttleDock # The shuttle has arrived at the station + path: shuttle/dock.ogg + # - id: shuttleNearby # The shuttle couldn't dock, it's at a specified location + # path: shuttle/nearby.ogg + # - id: shuttleGoodLuck # The shuttle could not find its way to the station, good luck crew + # path: shuttle/good_luck.ogg + # - id: shuttleAuthAdded # One of few have added their acceptance to early launching + # path: shuttle/auth_added.ogg + # - id: shuttleAuthRevoked # One of few have revoked their acceptance to early launching + # path: shuttle/auth_revoked.ogg + # - id: shuttleAlmostLaunching # The shuttle will leave to FTL in 10 seconds + # path: shuttle/almost_launching.ogg + # - id: shuttleLeft # The shuttle has left the station + # path: shuttle/left.ogg + + # Fallback # REQUIRED + - id: fallback # Any announcement sent without a valid announcement on this announcer will use this + path: fallback.ogg 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 0a3556ca36a..63a748e082d 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -1241,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/DeltaV/GameRules/events.yml b/Resources/Prototypes/DeltaV/GameRules/events.yml index 9391756492b..9a2b9a4569d 100644 --- a/Resources/Prototypes/DeltaV/GameRules/events.yml +++ b/Resources/Prototypes/DeltaV/GameRules/events.yml @@ -4,9 +4,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: station-event-xeno-vent-start-announcement - startAudio: - path: /Audio/Announcements/aliens.ogg + startAnnouncement: true earliestStart: 20 minimumPlayers: 15 weight: 1 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/Devices/translators.yml b/Resources/Prototypes/Entities/Objects/Devices/translators.yml index 664626ea4b4..2eb44264548 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] + 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: [ TranslatorUnpowered, 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 901b52b21b1..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 diff --git a/Resources/Prototypes/Entities/Objects/Misc/handcuffs.yml b/Resources/Prototypes/Entities/Objects/Misc/handcuffs.yml index 5f970da1840..527233920d5 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/handcuffs.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/handcuffs.yml @@ -52,6 +52,7 @@ path: /Audio/Items/Handcuffs/rope_breakout.ogg startBreakoutSound: path: /Audio/Items/Handcuffs/rope_takeoff.ogg + uncuffEasierWhenLarge: true - type: Construction graph: makeshifthandcuffs node: cuffscable @@ -93,6 +94,7 @@ path: /Audio/Items/Handcuffs/rope_breakout.ogg startBreakoutSound: path: /Audio/Items/Handcuffs/rope_takeoff.ogg + uncuffEasierWhenLarge: true - type: Sprite sprite: Objects/Misc/zipties.rsi state: cuff diff --git a/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml b/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml index 0b86677a8e5..f797ad5cead 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml @@ -35,7 +35,11 @@ - type: Sprite layers: - state: default - - state: idpassenger + - state: department + color: "#878787" + - state: subdepartment + color: "#878787" + - state: passenger - type: PresetIdCard job: Passenger @@ -49,7 +53,11 @@ - type: Sprite layers: - state: default - - state: idintern-tech + - state: department + color: "#F39F27" + - state: subdepartment + color: "#F39F27" + - state: assistant - type: entity parent: PassengerIDCard @@ -61,7 +69,11 @@ - type: Sprite layers: - state: default - - state: idintern-med + - state: department + color: "#5B97BC" + - state: subdepartment + color: "#5B97BC" + - state: assistant - type: entity parent: PassengerIDCard @@ -73,7 +85,11 @@ - type: Sprite layers: - state: default - - state: idintern-sci + - state: department + color: "#C96DBF" + - state: subdepartment + color: "#C96DBF" + - state: assistant - type: entity parent: PassengerIDCard @@ -85,7 +101,11 @@ - type: Sprite layers: - state: default - - state: idintern-cadet + - state: department + color: "#CB0000" + - state: subdepartment + color: "#CB0000" + - state: assistant - type: entity parent: PassengerIDCard @@ -97,7 +117,11 @@ - type: Sprite layers: - state: default - - state: idintern-service + - state: department + color: "#58C800" + - state: subdepartment + color: "#58C800" + - state: assistant - type: entity parent: IDCardStandard @@ -107,7 +131,11 @@ - type: Sprite layers: - state: gold - - state: idcaptain + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#1B67A5" + - state: captain - type: Item heldPrefix: gold - type: PresetIdCard @@ -128,7 +156,11 @@ - type: Sprite layers: - state: default - - state: idsecurityofficer + - state: department + color: "#CB0000" + - state: subdepartment + color: "#CB0000" + - state: securityofficer - type: PresetIdCard job: SecurityOfficer @@ -137,12 +169,16 @@ id: WardenIDCard name: warden ID card components: - - type: Sprite - layers: - - state: default - - state: idwarden - - type: PresetIdCard - job: Warden + - type: Sprite + layers: + - state: default + - state: department + color: "#CB0000" + - state: subdepartment + color: "#CB0000" + - state: warden + - type: PresetIdCard + job: Warden - type: entity parent: IDCardStandard @@ -152,7 +188,11 @@ - type: Sprite layers: - state: default - - state: idstationengineer + - state: department + color: "#F39F27" + - state: subdepartment + color: "#F39F27" + - state: stationengineer - type: PresetIdCard job: StationEngineer @@ -164,7 +204,11 @@ - type: Sprite layers: - state: default - - state: idmedicaldoctor + - state: department + color: "#5B97BC" + - state: subdepartment + color: "#5B97BC" + - state: medicaldoctor - type: PresetIdCard job: MedicalDoctor @@ -176,7 +220,11 @@ - type: Sprite layers: - state: default - - state: idparamedic + - state: department + color: "#5B97BC" + - state: subdepartment + color: "#5B97BC" + - state: paramedic - type: PresetIdCard job: Paramedic @@ -188,7 +236,11 @@ - type: Sprite layers: - state: default - - state: idchemist + - state: department + color: "#5B97BC" + - state: subdepartment + color: "#5B97BC" + - state: chemist - type: PresetIdCard job: Chemist @@ -200,7 +252,11 @@ - type: Sprite layers: - state: default - - state: idcargotechnician + - state: department + color: "#B18644" + - state: subdepartment + color: "#B18644" + - state: cargotechnician - type: PresetIdCard job: CargoTechnician @@ -212,7 +268,11 @@ - type: Sprite layers: - state: default - - state: idshaftminer + - state: department + color: "#B18644" + - state: subdepartment + color: "#C96DBF" + - state: shaftminer - type: PresetIdCard job: SalvageSpecialist @@ -221,14 +281,18 @@ id: QuartermasterIDCard name: logistics officer ID card # DeltaV - Logistics Department replacing Cargo components: - - type: Sprite - layers: - - state: silver - - state: idquartermaster - - type: Item - heldPrefix: silver - - type: PresetIdCard - job: Quartermaster + - type: Sprite + layers: + - state: silver + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#B18644" + - state: cargotechnician + - type: Item + heldPrefix: silver + - type: PresetIdCard + job: Quartermaster - type: entity parent: IDCardStandard @@ -238,7 +302,11 @@ - type: Sprite layers: - state: default - - state: idscientist + - state: department + color: "#C96DBF" + - state: subdepartment + color: "#C96DBF" + - state: scientist - type: PresetIdCard job: Scientist @@ -249,8 +317,12 @@ components: - type: Sprite layers: - - state: default - - state: idclown + - state: rainbow + - state: department + color: "#FF00FF" + - state: subdepartment + color: "#58C800" + - state: clown - type: PresetIdCard job: Clown @@ -262,7 +334,10 @@ - type: Sprite layers: - state: default - - state: idmime + - state: department + color: "#878787" + - state: subdepartment + - state: mime - type: PresetIdCard job: Mime @@ -271,11 +346,14 @@ id: ChaplainIDCard name: chaplain ID card components: - - type: Sprite - layers: - - state: default - - sprite: DeltaV/Objects/Misc/id_cards.rsi # DeltaV - Give Chaplain ID Epistemics colors - state: idchaplain + - type: Sprite + layers: + - state: default + - state: department + color: "#C96DBF" + - state: subdepartment + color: "#58C800" + - state: chaplain - type: PresetIdCard job: Chaplain @@ -287,7 +365,11 @@ - type: Sprite layers: - state: default - - state: idjanitor + - state: department + color: "#58C800" + - state: subdepartment + color: "#58C800" + - state: janitor - type: PresetIdCard job: Janitor @@ -299,22 +381,22 @@ - type: Sprite layers: - state: default - - state: idbartender + - state: department + color: "#58C800" + - state: subdepartment + color: "#58C800" + - state: bartender - type: PresetIdCard job: Bartender - type: entity - parent: IDCardStandard + parent: BartenderIDCard id: PunPunIDCard name: pun pun ID card components: - - type: Sprite - layers: - - state: default - - state: idbartender - - type: PresetIdCard - job: Bartender - name: Pun Pun + - type: PresetIdCard + job: Bartender + name: Pun Pun - type: entity parent: IDCardStandard @@ -324,7 +406,11 @@ - type: Sprite layers: - state: default - - state: idcook + - state: department + color: "#58C800" + - state: subdepartment + color: "#58C800" + - state: cook - type: PresetIdCard job: Chef @@ -333,36 +419,48 @@ id: BotanistIDCard name: botanist ID card components: - - type: Sprite - layers: - - state: default - - state: idbotanist - - type: PresetIdCard - job: Botanist + - type: Sprite + layers: + - state: default + - state: department + color: "#58C800" + - state: subdepartment + color: "#58C800" + - state: botanist + - type: PresetIdCard + job: Botanist - type: entity parent: IDCardStandard id: LibrarianIDCard name: librarian ID card components: - - type: Sprite - layers: - - state: default - - state: idcurator - - type: PresetIdCard - job: Librarian + - type: Sprite + layers: + - state: default + - state: department + color: "#58C800" + - state: subdepartment + color: "#58C800" + - state: curator + - type: PresetIdCard + job: Librarian - type: entity parent: IDCardStandard id: LawyerIDCard name: lawyer ID card components: - - type: Sprite - layers: - - state: default - - state: idlawyer - - type: PresetIdCard - job: Lawyer + - type: Sprite + layers: + - state: default + - state: department + color: "#878787" + - state: subdepartment + color: "#CB0000" + - state: lawyer + - type: PresetIdCard + job: Lawyer - type: entity parent: IDCardStandard @@ -372,7 +470,11 @@ - type: Sprite layers: - state: silver - - state: idheadofpersonnel + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#58C800" + - state: headofpersonnel - type: Item heldPrefix: silver - type: PresetIdCard @@ -386,7 +488,11 @@ - type: Sprite layers: - state: silver - - state: idchiefengineer + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#F39F27" + - state: stationengineer - type: Item heldPrefix: silver - type: PresetIdCard @@ -400,7 +506,11 @@ - type: Sprite layers: - state: silver - - state: idchiefmedicalofficer + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#5B97BC" + - state: medicaldoctor - type: Item heldPrefix: silver - type: PresetIdCard @@ -414,7 +524,11 @@ - type: Sprite layers: - state: silver - - state: idresearchdirector + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#C96DBF" + - state: scientist - type: Item heldPrefix: silver - type: PresetIdCard @@ -428,7 +542,11 @@ - type: Sprite layers: - state: silver - - state: idheadofsecurity + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#CB0000" + - state: securityofficer - type: Item heldPrefix: silver - type: PresetIdCard @@ -442,7 +560,11 @@ - type: Sprite layers: - state: default - - state: idbrigmedic + - state: department + color: "#CB0000" + - state: subdepartment + color: "#5B97BC" + - state: medicaldoctor - type: PresetIdCard # Delta V - Brigmedic, see Prototypes/DeltaV/Roles/Jobs/Security/brigmedic.yml job: Brigmedic @@ -454,7 +576,11 @@ - type: Sprite layers: - state: centcom - - state: idcentcom + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#1B67A5" + - state: cc - type: Item heldPrefix: blue - type: IdCard @@ -473,8 +599,12 @@ components: - type: Sprite layers: - - state: gold - - state: ert_commander + - state: centcom + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#1B67A5" + - state: captain - type: IdCard jobTitle: ERT Company Commander - type: Item @@ -487,8 +617,12 @@ components: - type: Sprite layers: - - state: gold - - state: ert_chaplain # we have the sprite for the id but dont have chaplain ERT equipment for now. + - state: centcom + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#C96DBF" + - state: chaplain - type: IdCard jobTitle: ERT Soul Officer - type: Item @@ -501,8 +635,12 @@ components: - type: Sprite layers: - - state: gold - - state: ert_engineer + - state: centcom + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#F39F27" + - state: stationengineer - type: IdCard jobTitle: ERT Field Engineer @@ -513,8 +651,12 @@ components: - type: Sprite layers: - - state: gold - - state: ert_janitor + - state: centcom + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#58C800" + - state: janitor - type: IdCard jobTitle: ERT Custodian @@ -525,8 +667,12 @@ components: - type: Sprite layers: - - state: gold - - state: ert_medic + - state: centcom + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#5B97BC" + - state: medicaldoctor - type: IdCard jobTitle: ERT Medical Doctor @@ -537,8 +683,12 @@ components: - type: Sprite layers: - - state: gold - - state: ert_security + - state: centcom + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#CB0000" + - state: securityofficer - type: IdCard jobTitle: ERT Field Officer @@ -551,7 +701,11 @@ - type: Sprite layers: - state: centcom - - state: idcentcom + - state: departmenthead + color: "#1B67A5" + - state: subdepartment + color: "#1B67A5" + - state: cc - type: Item heldPrefix: blue - type: IdCard @@ -565,12 +719,16 @@ id: MusicianIDCard name: musician ID card components: - - type: Sprite - layers: - - state: default - - state: idmusician - - type: PresetIdCard - job: Musician + - type: Sprite + layers: + - state: default + - state: department + color: "#878787" + - state: subdepartment + color: "#878787" + - state: musician + - type: PresetIdCard + job: Musician - type: entity parent: CentcomIDCard @@ -580,6 +738,11 @@ - type: Sprite layers: - state: centcom + - state: departmenthead + color: "#CB0000" + - state: subdepartment + color: "#CB0000" + - state: death - type: Item heldPrefix: blue - type: IdCard @@ -600,7 +763,11 @@ - type: Sprite layers: - state: default - - state: idpassenger + - state: department + color: "#878787" + - state: subdepartment + color: "#878787" + - state: passenger - type: AgentIDCard icons: # TODO figure out a better way of doing this. @@ -691,8 +858,12 @@ components: - type: Sprite layers: - - state: orange - - state: idatmospherictechnician + - state: default + - state: department + color: "#F39F27" + - state: subdepartment + color: "#F39F27" + - state: atmospherictechnician - type: PresetIdCard job: AtmosphericTechnician @@ -703,7 +874,12 @@ components: - type: Sprite layers: - - state: syndie + - state: black + - state: department + color: "#CB0000" + - state: subdepartment + color: "#CB0000" + - state: syndicate - type: Access tags: - NuclearOperative @@ -716,7 +892,12 @@ components: - type: Sprite layers: - - state: pirate + - state: black + - state: department + color: "#878787" + - state: subdepartment + color: "#878787" + - state: death - type: Access tags: - NuclearOperative @@ -730,7 +911,11 @@ - type: Sprite layers: - state: default - - state: idpsychologist + - state: department + color: "#5B97BC" + - state: subdepartment + color: "#5B97BC" + - state: psychologist - type: PresetIdCard job: Psychologist @@ -742,7 +927,11 @@ - type: Sprite layers: - state: default - - state: idreporter + - state: department + color: "#58C800" + - state: subdepartment + color: "#58C800" + - state: headofpersonnel - type: PresetIdCard job: Reporter @@ -754,7 +943,11 @@ - type: Sprite layers: - state: default - - state: idboxer + - state: department + color: "#878787" + - state: subdepartment + color: "#878787" + - state: boxer - type: PresetIdCard job: Boxer @@ -766,7 +959,11 @@ - type: Sprite layers: - state: default - - state: idzookeeper + - state: department + color: "#58C800" + - state: subdepartment + color: "#58C800" + - state: zookeeper - type: PresetIdCard job: Zookeeper @@ -778,7 +975,11 @@ - type: Sprite layers: - state: default - - state: iddetective + - state: department + color: "#CB0000" + - state: subdepartment + color: "#CB0000" + - state: detective - type: PresetIdCard job: Detective @@ -791,6 +992,11 @@ - type: Sprite layers: - state: centcom + - state: departmenthead + color: "#CB0000" + - state: subdepartment + color: "#CB0000" + - state: cc - type: Item heldPrefix: blue - type: IdCard @@ -804,8 +1010,13 @@ components: - type: Sprite layers: - - state: default - - state: idcluwne + - state: rainbow + - state: department + color: "#5DC574" + - state: subdepartment + color: "#5DC574" + - state: clown + color: "#7C0A0A" - type: IdCard jobTitle: Cluwne - type: Unremoveable @@ -818,7 +1029,11 @@ - type: Sprite layers: - state: default - - state: idseniorengineer + - state: department + color: "#F39F27" + - state: subdepartment + color: "#F39F27" + - state: senior - type: PresetIdCard job: SeniorEngineer @@ -830,7 +1045,11 @@ - type: Sprite layers: - state: default - - state: idseniorresearcher + - state: department + color: "#C96DBF" + - state: subdepartment + color: "#C96DBF" + - state: senior - type: PresetIdCard job: SeniorResearcher @@ -842,7 +1061,11 @@ - type: Sprite layers: - state: default - - state: idseniorphysician + - state: department + color: "#5B97BC" + - state: subdepartment + color: "#5B97BC" + - state: senior - type: PresetIdCard job: SeniorPhysician @@ -854,6 +1077,10 @@ - type: Sprite layers: - state: default - - state: idseniorofficer + - state: department + color: "#CB0000" + - state: subdepartment + color: "#CB0000" + - state: senior - type: PresetIdCard job: SeniorOfficer diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index a90664cbcf0..81128ba84f1 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 @@ -744,6 +745,7 @@ - ShellShotgunPractice - WeaponLaserCarbinePractice - WeaponDisablerPractice + - ShockCollar # DeltaV - .38 special ammo - Add various .38 special ammo to security techfab - MagazineBoxSpecial - MagazineBoxSpecialPractice 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/events.yml b/Resources/Prototypes/GameRules/events.yml index 00ad5f54ea9..f4936f51f50 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -49,7 +49,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: station-event-bureaucratic-error-announcement + startAnnouncement: true minimumPlayers: 25 weight: 5 duration: 1 @@ -61,7 +61,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: station-event-clerical-error-announcement + startAnnouncement: true minimumPlayers: 15 weight: 5 duration: 1 @@ -134,10 +134,8 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: station-event-gas-leak-start-announcement - startAudio: - path: /Audio/Announcements/gas_leak.ogg # DeltaV - custom announcer - endAnnouncement: station-event-gas-leak-end-announcement + startAnnouncement: true + endAnnouncement: true earliestStart: 10 minimumPlayers: 5 weight: 10 @@ -166,12 +164,8 @@ earliestStart: 30 weight: 7.5 minimumPlayers: 10 #Enough to hopefully have at least one engineering guy - startAnnouncement: station-event-meteor-swarm-start-announcement - endAnnouncement: station-event-meteor-swarm-end-announcement - startAudio: - path: /Audio/Announcements/meteors.ogg - params: - volume: -4 + startAnnouncement: true + endAnnouncement: true duration: null #ending is handled by MeteorSwarmRule startDelay: 30 - type: MeteorSwarmRule @@ -182,9 +176,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: station-event-vent-creatures-start-announcement - startAudio: - path: /Audio/Announcements/attention.ogg + startAnnouncement: true startDelay: 10 earliestStart: 30 minimumPlayers: 35 @@ -210,9 +202,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: station-event-vent-creatures-start-announcement - startAudio: - path: /Audio/Announcements/attention.ogg + startAnnouncement: true startDelay: 10 weight: 5 duration: 50 @@ -230,12 +220,8 @@ components: - type: StationEvent weight: 10 - startAnnouncement: station-event-power-grid-check-start-announcement - endAnnouncement: station-event-power-grid-check-end-announcement - startAudio: - path: /Audio/Announcements/power_off.ogg - params: - volume: -4 + startAnnouncement: true + endAnnouncement: true startDelay: 24 duration: 60 maxDuration: 120 @@ -249,8 +235,6 @@ # - type: StationEvent # weight: 10 # duration: 1 -# startAudio: -# path: /Audio/Announcements/attention.ogg # - type: RandomSentienceRule - type: entity @@ -260,10 +244,8 @@ components: - type: StationEvent weight: 10 - startAnnouncement: station-event-solar-flare-start-announcement - endAnnouncement: station-event-solar-flare-end-announcement - startAudio: - path: /Audio/Announcements/attention.ogg + startAnnouncement: true + endAnnouncement: true duration: 120 maxDuration: 240 - type: SolarFlareRule @@ -301,9 +283,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: station-event-vent-clog-start-announcement - startAudio: - path: /Audio/Announcements/ventclog.ogg # DeltaV - custom announcer + startAnnouncement: true earliestStart: 15 minimumPlayers: 15 weight: 5 @@ -317,9 +297,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: station-event-vent-creatures-start-announcement - startAudio: - path: /Audio/Announcements/attention.ogg + startAnnouncement: true startDelay: 10 earliestStart: 15 minimumPlayers: 15 @@ -340,9 +318,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: station-event-vent-creatures-start-announcement - startAudio: - path: /Audio/Announcements/attention.ogg + startAnnouncement: true startDelay: 10 earliestStart: 20 minimumPlayers: 15 @@ -363,9 +339,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: station-event-vent-creatures-start-announcement - startAudio: - path: /Audio/Announcements/attention.ogg + startAnnouncement: true startDelay: 10 earliestStart: 20 minimumPlayers: 15 @@ -382,9 +356,7 @@ # noSpawn: true # components: # - type: StationEvent -# startAnnouncement: station-event-vent-creatures-start-announcement -# startAudio: -# path: /Audio/Announcements/attention.ogg +# startAnnouncement: true # startDelay: 10 # earliestStart: 20 # minimumPlayers: 15 @@ -447,9 +419,7 @@ # noSpawn: true # components: # - type: StationEvent -# startAnnouncement: station-event-immovable-rod-start-announcement -# startAudio: -# path: /Audio/Announcements/attention.ogg +# startAnnouncement: true # weight: 5 # duration: 1 # earliestStart: 45 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/Nyanotrasen/Entities/Objects/Devices/Misc/identification_cards.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/Misc/identification_cards.yml index ff9b3f8f2a0..94efc40530c 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/Misc/identification_cards.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/Misc/identification_cards.yml @@ -5,9 +5,12 @@ components: - type: Sprite layers: - - state: orange - - sprite: DeltaV/Objects/Misc/id_cards.rsi - state: nyanoprisoner + - state: prisoner + - state: department + color: "#292929" + - state: subdepartment + color: "#A54900" + - state: warden - type: PresetIdCard job: Prisoner @@ -18,9 +21,12 @@ components: - type: Sprite layers: - - state: orange - - sprite: DeltaV/Objects/Misc/id_cards.rsi - state: nyanogladiator + - state: default + - state: department + color: "#878787" + - state: subdepartment + color: "#878787" + - state: gladiator - type: PresetIdCard job: Gladiator @@ -32,8 +38,11 @@ - type: Sprite layers: - state: default - - sprite: DeltaV/Objects/Misc/id_cards.rsi - state: nyanoprisonguard + - state: department + color: "#CB0000" + - state: subdepartment + color: "#CB0000" + - state: prisonguard - type: PresetIdCard job: PrisonGuard @@ -45,8 +54,11 @@ - type: Sprite layers: - state: default - - sprite: DeltaV/Objects/Misc/id_cards.rsi - state: nyanomailcarrier + - state: department + color: "#B18644" + - state: subdepartment + color: "#B18644" + - state: mailcarrier - type: PresetIdCard job: MailCarrier @@ -59,8 +71,11 @@ - type: Sprite layers: - state: default - - sprite: DeltaV/Objects/Misc/id_cards.rsi - state: nyanomartialartist + - state: department + color: "#878787" + - state: subdepartment + color: "#878787" + - state: martialartist - type: PresetIdCard job: MartialArtist @@ -69,10 +84,13 @@ id: ForensicMantisIDCard name: psionic mantis ID card # DeltaV - Rename Forensic Mantis to Psionic Mantis components: - - type: Sprite - layers: - - state: default - - sprite: DeltaV/Objects/Misc/id_cards.rsi - state: nyanomantis - - type: PresetIdCard - job: ForensicMantis + - type: Sprite + layers: + - state: default + - state: department + color: "#C96DBF" + - state: subdepartment + color: "#C96DBF" + - state: detective + - type: PresetIdCard + job: ForensicMantis diff --git a/Resources/Prototypes/Nyanotrasen/GameRules/events.yml b/Resources/Prototypes/Nyanotrasen/GameRules/events.yml index f5626a0406f..8646ff643f3 100644 --- a/Resources/Prototypes/Nyanotrasen/GameRules/events.yml +++ b/Resources/Prototypes/Nyanotrasen/GameRules/events.yml @@ -5,9 +5,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: station-event-noospheric-storm-announcement - startAudio: - path: /Audio/Announcements/noosphericstorm.ogg + startAnnouncement: true weight: 5 earliestStart: 15 - type: NoosphericStormRule diff --git a/Resources/Prototypes/Objectives/ninja.yml b/Resources/Prototypes/Objectives/ninja.yml index b6c3d553df0..0495be29355 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/Objectives/stealTargetGroups.yml b/Resources/Prototypes/Objectives/stealTargetGroups.yml index 006c061666b..11e503d7940 100644 --- a/Resources/Prototypes/Objectives/stealTargetGroups.yml +++ b/Resources/Prototypes/Objectives/stealTargetGroups.yml @@ -55,13 +55,13 @@ sprite: sprite: Objects/Consumable/Food/meat.rsi state: corgi - # + - type: stealTargetGroup id: CaptainIDCard name: captain ID card sprite: sprite: Objects/Misc/id_cards.rsi - state: ert_commander #no one will know the difference. + state: gold - type: stealTargetGroup id: JetpackCaptainFilled 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/SoundCollections/Announcers/intern.yml b/Resources/Prototypes/SoundCollections/Announcers/intern.yml new file mode 100644 index 00000000000..46172713dc8 --- /dev/null +++ b/Resources/Prototypes/SoundCollections/Announcers/intern.yml @@ -0,0 +1,34 @@ +- type: soundCollection + id: InternAnnouncements + files: + - /Audio/Announcers/Intern/comms/announce/1.ogg + - /Audio/Announcers/Intern/comms/announce/2.ogg + - /Audio/Announcers/Intern/comms/announce/3.ogg + - /Audio/Announcers/Intern/comms/announce/4.ogg + - /Audio/Announcers/Intern/comms/announce/5.ogg + - /Audio/Announcers/Intern/comms/announce/6.ogg + - /Audio/Announcers/Intern/comms/announce/7.ogg + - /Audio/Announcers/Intern/comms/announce/8.ogg + - /Audio/Announcers/Intern/comms/announce/9.ogg + - /Audio/Announcers/Intern/comms/announce/10.ogg + - /Audio/Announcers/Intern/comms/announce/11.ogg + - /Audio/Announcers/Intern/comms/announce/12.ogg + - /Audio/Announcers/Intern/comms/announce/13.ogg + - /Audio/Announcers/Intern/comms/announce/14.ogg + +- type: soundCollection + id: InternCommandReportAnnouncements + files: + - /Audio/Announcers/Intern/comms/commandReport/1.ogg + - /Audio/Announcers/Intern/comms/commandReport/2.ogg + - /Audio/Announcers/Intern/comms/commandReport/3.ogg + +- type: soundCollection + id: InternWelcomeAnnouncements + files: + - /Audio/Announcers/Intern/comms/welcome/1.ogg + - /Audio/Announcers/Intern/comms/welcome/2.ogg + - /Audio/Announcers/Intern/comms/welcome/3.ogg + - /Audio/Announcers/Intern/comms/welcome/4.ogg + - /Audio/Announcers/Intern/comms/welcome/5.ogg + - /Audio/Announcers/Intern/comms/welcome/6.ogg diff --git a/Resources/Prototypes/Traits/inconveniences.yml b/Resources/Prototypes/Traits/inconveniences.yml index dcf53d9ab7f..fee312593da 100644 --- a/Resources/Prototypes/Traits/inconveniences.yml +++ b/Resources/Prototypes/Traits/inconveniences.yml @@ -7,6 +7,10 @@ jobs: - Borg - MedicalBorg + - !type:CharacterTraitRequirement + inverted: true + traits: + - HeavyweightDrunk components: - type: LightweightDrunk boozeStrengthMultiplier: 2 @@ -26,3 +30,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/Traits/skills.yml b/Resources/Prototypes/Traits/skills.yml index 6175834c1fc..988307f58e4 100644 --- a/Resources/Prototypes/Traits/skills.yml +++ b/Resources/Prototypes/Traits/skills.yml @@ -1,3 +1,21 @@ +- type: trait + id: HeavyweightDrunk + category: Physical + points: -2 + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Borg + - MedicalBorg + - !type:CharacterTraitRequirement + inverted: true + traits: + - LightweightDrunk + components: + - type: LightweightDrunk + boozeStrengthMultiplier: 0.5 + - type: trait id: Thieving category: Physical 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/Textures/DeltaV/Objects/Misc/id_cards.rsi/idchaplain.png b/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/idchaplain.png deleted file mode 100644 index d27ec5bbfdb..00000000000 Binary files a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/idchaplain.png and /dev/null differ diff --git a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/meta.json b/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/meta.json deleted file mode 100644 index 521c6e58fa7..00000000000 --- a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/meta.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/d917f4c2a088419d5c3aec7656b7ff8cebd1822e | nyanoprisonguard, nyanogladiator, nyanomartialartist made by Floofers", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "idchaplain" - }, - { - "name": "nyanogladiator" - }, - { - "name": "nyanoprisonguard" - }, - { - "name": "nyanoprisoner" - }, - { - "name": "nyanomailcarrier" - }, - { - "name": "nyanomartialartist" - }, - { - "name": "nyanomantis" - } - ] -} diff --git a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanogladiator.png b/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanogladiator.png deleted file mode 100644 index 17f3ea2aae1..00000000000 Binary files a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanogladiator.png and /dev/null differ diff --git a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanomailcarrier.png b/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanomailcarrier.png deleted file mode 100644 index ba7a3d4dcdd..00000000000 Binary files a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanomailcarrier.png and /dev/null differ diff --git a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanomantis.png b/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanomantis.png deleted file mode 100644 index f80e175ec33..00000000000 Binary files a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanomantis.png and /dev/null differ diff --git a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanomartialartist.png b/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanomartialartist.png deleted file mode 100644 index 1f7862d0922..00000000000 Binary files a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanomartialartist.png and /dev/null differ diff --git a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanoprisoner.png b/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanoprisoner.png deleted file mode 100644 index 96c8f4d9825..00000000000 Binary files a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanoprisoner.png and /dev/null differ diff --git a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanoprisonguard.png b/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanoprisonguard.png deleted file mode 100644 index 0539dd50da6..00000000000 Binary files a/Resources/Textures/DeltaV/Objects/Misc/id_cards.rsi/nyanoprisonguard.png and /dev/null differ 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/Objects/Misc/id_cards.rsi/assistant.png b/Resources/Textures/Objects/Misc/id_cards.rsi/assistant.png new file mode 100644 index 00000000000..a3e83280f72 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/assistant.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/atmospherictechnician.png b/Resources/Textures/Objects/Misc/id_cards.rsi/atmospherictechnician.png new file mode 100644 index 00000000000..99e10c276d7 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/atmospherictechnician.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/bartender.png b/Resources/Textures/Objects/Misc/id_cards.rsi/bartender.png new file mode 100644 index 00000000000..24a1b62fe24 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/bartender.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/black.png b/Resources/Textures/Objects/Misc/id_cards.rsi/black.png new file mode 100644 index 00000000000..6958c6d4490 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/black.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/botanist.png b/Resources/Textures/Objects/Misc/id_cards.rsi/botanist.png new file mode 100644 index 00000000000..4d62c18a60a Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/botanist.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/boxer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/boxer.png new file mode 100644 index 00000000000..90b9165c049 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/boxer.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/captain.png b/Resources/Textures/Objects/Misc/id_cards.rsi/captain.png new file mode 100644 index 00000000000..72ec0038427 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/captain.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/cargotechnician.png b/Resources/Textures/Objects/Misc/id_cards.rsi/cargotechnician.png new file mode 100644 index 00000000000..7f5fba10169 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/cargotechnician.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/carp.png b/Resources/Textures/Objects/Misc/id_cards.rsi/carp.png new file mode 100644 index 00000000000..1ef99e1cef6 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/carp.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/cc.png b/Resources/Textures/Objects/Misc/id_cards.rsi/cc.png new file mode 100644 index 00000000000..9a28b7780c6 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/cc.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/centcom.png b/Resources/Textures/Objects/Misc/id_cards.rsi/centcom.png index ff1e293183c..3ed8b4045a7 100644 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/centcom.png and b/Resources/Textures/Objects/Misc/id_cards.rsi/centcom.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/chaplain.png b/Resources/Textures/Objects/Misc/id_cards.rsi/chaplain.png new file mode 100644 index 00000000000..1e134c3911b Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/chaplain.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/chemist.png b/Resources/Textures/Objects/Misc/id_cards.rsi/chemist.png new file mode 100644 index 00000000000..11a34b3f053 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/chemist.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/clown.png b/Resources/Textures/Objects/Misc/id_cards.rsi/clown.png new file mode 100644 index 00000000000..24dea8a6d00 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/clown.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/cook.png b/Resources/Textures/Objects/Misc/id_cards.rsi/cook.png new file mode 100644 index 00000000000..8a4e8c15d1e Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/cook.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/curator.png b/Resources/Textures/Objects/Misc/id_cards.rsi/curator.png new file mode 100644 index 00000000000..534150e6de3 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/curator.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/death.png b/Resources/Textures/Objects/Misc/id_cards.rsi/death.png new file mode 100644 index 00000000000..c2190596169 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/death.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/default.png b/Resources/Textures/Objects/Misc/id_cards.rsi/default.png index 95b3d54c270..a7142e353ea 100644 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/default.png and b/Resources/Textures/Objects/Misc/id_cards.rsi/default.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/department.png b/Resources/Textures/Objects/Misc/id_cards.rsi/department.png new file mode 100644 index 00000000000..001c8d75f5e Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/department.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/departmenthead.png b/Resources/Textures/Objects/Misc/id_cards.rsi/departmenthead.png new file mode 100644 index 00000000000..09f48fbb838 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/departmenthead.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/detective.png b/Resources/Textures/Objects/Misc/id_cards.rsi/detective.png new file mode 100644 index 00000000000..8e21d3539c4 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/detective.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/ert_chaplain.png b/Resources/Textures/Objects/Misc/id_cards.rsi/ert_chaplain.png deleted file mode 100644 index 624bb4864db..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/ert_chaplain.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/ert_commander.png b/Resources/Textures/Objects/Misc/id_cards.rsi/ert_commander.png deleted file mode 100644 index d9c8b6e261d..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/ert_commander.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/ert_engineer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/ert_engineer.png deleted file mode 100644 index a7c8b683556..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/ert_engineer.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/ert_janitor.png b/Resources/Textures/Objects/Misc/id_cards.rsi/ert_janitor.png deleted file mode 100644 index afbe56e5f0d..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/ert_janitor.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/ert_medic.png b/Resources/Textures/Objects/Misc/id_cards.rsi/ert_medic.png deleted file mode 100644 index 6fe2ce6698f..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/ert_medic.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/ert_security.png b/Resources/Textures/Objects/Misc/id_cards.rsi/ert_security.png deleted file mode 100644 index 6bb3134b557..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/ert_security.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/flame.png b/Resources/Textures/Objects/Misc/id_cards.rsi/flame.png new file mode 100644 index 00000000000..014c98322aa Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/flame.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/geneticist.png b/Resources/Textures/Objects/Misc/id_cards.rsi/geneticist.png new file mode 100644 index 00000000000..0980bb306c3 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/geneticist.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/gladiator.png b/Resources/Textures/Objects/Misc/id_cards.rsi/gladiator.png new file mode 100644 index 00000000000..407a927b6a4 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/gladiator.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/gold.png b/Resources/Textures/Objects/Misc/id_cards.rsi/gold.png index bb20387315c..859a9ec99cf 100644 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/gold.png and b/Resources/Textures/Objects/Misc/id_cards.rsi/gold.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/headofpersonnel.png b/Resources/Textures/Objects/Misc/id_cards.rsi/headofpersonnel.png new file mode 100644 index 00000000000..d0b4235c816 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/headofpersonnel.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idatmospherictechnician.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idatmospherictechnician.png deleted file mode 100644 index 3bd5446a3bc..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idatmospherictechnician.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idbartender.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idbartender.png deleted file mode 100644 index 435ea39ad6a..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idbartender.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idbotanist.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idbotanist.png deleted file mode 100644 index f5dfdb86629..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idbotanist.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idboxer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idboxer.png deleted file mode 100644 index 0c2b5298e5f..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idboxer.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idbrigmedic.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idbrigmedic.png deleted file mode 100644 index 1f64eb4845a..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idbrigmedic.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idcaptain.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idcaptain.png deleted file mode 100644 index e59e7c3d042..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idcaptain.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idcargotechnician.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idcargotechnician.png deleted file mode 100644 index cfa0a35dcc5..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idcargotechnician.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idcentcom.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idcentcom.png deleted file mode 100644 index 25b7017a708..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idcentcom.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idchaplain.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idchaplain.png deleted file mode 100644 index e10e3c82a6e..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idchaplain.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idchemist.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idchemist.png deleted file mode 100644 index 2dba29c2060..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idchemist.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idchiefengineer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idchiefengineer.png deleted file mode 100644 index f105b4f88ad..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idchiefengineer.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idchiefmedicalofficer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idchiefmedicalofficer.png deleted file mode 100644 index 82a608a9a1a..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idchiefmedicalofficer.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idclown.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idclown.png deleted file mode 100644 index e0372020448..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idclown.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idcluwne.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idcluwne.png deleted file mode 100644 index d57a7e3d416..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idcluwne.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idcook.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idcook.png deleted file mode 100644 index 09527688a46..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idcook.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idcurator.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idcurator.png deleted file mode 100644 index cd01dc77702..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idcurator.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/iddetective.png b/Resources/Textures/Objects/Misc/id_cards.rsi/iddetective.png deleted file mode 100644 index 3b749d582a5..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/iddetective.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idgeneticist.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idgeneticist.png deleted file mode 100644 index 1303135aa5a..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idgeneticist.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idheadofpersonnel.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idheadofpersonnel.png deleted file mode 100644 index be72e37e574..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idheadofpersonnel.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idheadofsecurity.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idheadofsecurity.png deleted file mode 100644 index bb03adc665c..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idheadofsecurity.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-cadet.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-cadet.png deleted file mode 100644 index cebf46fade2..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-cadet.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-med.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-med.png deleted file mode 100644 index 15c7cde5e86..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-med.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-sci.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-sci.png deleted file mode 100644 index de676944766..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-sci.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-service.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-service.png deleted file mode 100644 index 5fc1f43c05b..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-service.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-tech.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-tech.png deleted file mode 100644 index f7ed302b23a..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idintern-tech.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idjanitor.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idjanitor.png deleted file mode 100644 index 320c3885e7e..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idjanitor.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idlawyer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idlawyer.png deleted file mode 100644 index b86f437aee1..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idlawyer.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idmedicaldoctor.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idmedicaldoctor.png deleted file mode 100644 index 9e390bb606f..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idmedicaldoctor.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idmime.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idmime.png deleted file mode 100644 index b917612dae2..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idmime.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idmusician.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idmusician.png deleted file mode 100644 index b51dfaaab8e..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idmusician.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idparamedic.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idparamedic.png deleted file mode 100644 index 1881839e965..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idparamedic.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idpassenger.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idpassenger.png deleted file mode 100644 index 88866eb1693..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idpassenger.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idprisoner.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idprisoner.png deleted file mode 100644 index f9337a37c53..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idprisoner.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idpsychologist.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idpsychologist.png deleted file mode 100644 index 038a5321d4b..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idpsychologist.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idquartermaster.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idquartermaster.png deleted file mode 100644 index bfdd0678724..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idquartermaster.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idreporter.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idreporter.png deleted file mode 100644 index e4ea4f95882..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idreporter.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idresearchdirector.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idresearchdirector.png deleted file mode 100644 index fdb1fd2648b..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idresearchdirector.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idroboticist.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idroboticist.png deleted file mode 100644 index a9d6c5facd5..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idroboticist.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idscientist.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idscientist.png deleted file mode 100644 index 893346a9c90..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idscientist.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idsecurityofficer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idsecurityofficer.png deleted file mode 100644 index 6456d644396..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idsecurityofficer.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idseniorengineer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idseniorengineer.png deleted file mode 100644 index 73c8b6d00fe..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idseniorengineer.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idseniorofficer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idseniorofficer.png deleted file mode 100644 index f3a87d6fa4b..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idseniorofficer.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idseniorphysician.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idseniorphysician.png deleted file mode 100644 index 2d00cf7a164..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idseniorphysician.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idseniorresearcher.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idseniorresearcher.png deleted file mode 100644 index 47a2ccb7485..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idseniorresearcher.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idshaftminer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idshaftminer.png deleted file mode 100644 index c232dd439d4..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idshaftminer.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idstationengineer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idstationengineer.png deleted file mode 100644 index 2e046fbacdd..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idstationengineer.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idunknown.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idunknown.png deleted file mode 100644 index bc792fe1a17..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idunknown.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idvirologist.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idvirologist.png deleted file mode 100644 index 1d4b2f2d474..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idvirologist.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idwarden.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idwarden.png deleted file mode 100644 index 0b4086478ca..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idwarden.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/idzookeeper.png b/Resources/Textures/Objects/Misc/id_cards.rsi/idzookeeper.png deleted file mode 100644 index c24212aab8d..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/idzookeeper.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/janitor.png b/Resources/Textures/Objects/Misc/id_cards.rsi/janitor.png new file mode 100644 index 00000000000..19eb0d52482 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/janitor.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/lawyer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/lawyer.png new file mode 100644 index 00000000000..973bbf66e1e Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/lawyer.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/mailcarrier.png b/Resources/Textures/Objects/Misc/id_cards.rsi/mailcarrier.png new file mode 100644 index 00000000000..db33a5ead4c Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/mailcarrier.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/martialartist.png b/Resources/Textures/Objects/Misc/id_cards.rsi/martialartist.png new file mode 100644 index 00000000000..e49e4478429 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/martialartist.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/medicaldoctor.png b/Resources/Textures/Objects/Misc/id_cards.rsi/medicaldoctor.png new file mode 100644 index 00000000000..92832dcaad6 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/medicaldoctor.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/meta.json b/Resources/Textures/Objects/Misc/id_cards.rsi/meta.json index a84c76a46c9..eeed695ca3c 100644 --- a/Resources/Textures/Objects/Misc/id_cards.rsi/meta.json +++ b/Resources/Textures/Objects/Misc/id_cards.rsi/meta.json @@ -1,261 +1,204 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/d917f4c2a088419d5c3aec7656b7ff8cebd1822e idcluwne made by brainfood1183 (github) for ss14, idbrigmedic made by PuroSlavKing (Github), pirate made by brainfood1183 (github)", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "default" - }, - { - "name": "centcom" - }, - { - "name": "ert_chaplain" - }, - { - "name": "ert_commander" - }, - { - "name": "ert_engineer" - }, - { - "name": "ert_janitor" - }, - { - "name": "ert_medic" - }, - { - "name": "ert_security" - }, - { - "name": "gold" - }, - { - "name": "idpassenger" - }, - { - "name": "idatmospherictechnician" - }, - { - "name": "idbartender" - }, - { - "name": "idbotanist" - }, - { - "name": "idboxer" - }, - { - "name": "idcaptain" - }, - { - "name": "idcargotechnician" - }, - { - "name": "idcentcom" - }, - { - "name": "idchaplain" - }, - { - "name": "idchemist" - }, - { - "name": "idchiefengineer" - }, - { - "name": "idchiefmedicalofficer" - }, - { - "name": "idclown" - }, - { - "name": "idcook" - }, - { - "name": "idcurator" - }, - { - "name": "iddetective" - }, - { - "name": "idgeneticist" - }, - { - "name": "idheadofpersonnel" - }, - { - "name": "idheadofsecurity" - }, - { - "name": "idbrigmedic" - }, - { - "name": "idjanitor" - }, - { - "name": "idlawyer" - }, - { - "name": "idmedicaldoctor" - }, - { - "name": "idmime" - }, - { - "name": "idparamedic" - }, - { - "name": "idpsychologist" - }, - { - "name": "idreporter" - }, - { - "name": "idprisoner" - }, - { - "name": "idquartermaster" - }, - { - "name": "idresearchdirector" - }, - { - "name": "idroboticist" - }, - { - "name": "idscientist" - }, - { - "name": "idsecurityofficer" - }, - { - "name": "idshaftminer" - }, - { - "name": "idstationengineer" - }, - { - "name": "idunknown" - }, - { - "name": "idvirologist" - }, - { - "name": "idwarden" - }, - { - "name": "idmusician" - }, - { - "name": "idzookeeper" - }, - { - "name": "idintern-sci" - }, - { - "name": "idintern-cadet" - }, - { - "name": "idintern-med" - }, - { - "name": "idintern-service" - }, - { - "name": "idintern-tech" - }, - { - "name": "orange" - }, - { - "name": "pirate" - }, - { - "name": "prisoner_001" - }, - { - "name": "prisoner_002" - }, - { - "name": "prisoner_003" - }, - { - "name": "prisoner_004" - }, - { - "name": "prisoner_005" - }, - { - "name": "prisoner_006" - }, - { - "name": "prisoner_007" - }, - { - "name": "silver" - }, - { - "name": "syndie" - }, - { - "name": "idcluwne" - }, - { - "name": "idseniorengineer" - }, - { - "name": "idseniorresearcher" - }, - { - "name": "idseniorphysician" - }, - { - "name": "idseniorofficer" - }, - { - "name": "gold-inhand-left", - "directions": 4 - }, - { - "name": "gold-inhand-right", - "directions": 4 - }, - { - "name": "default-inhand-left", - "directions": 4 - }, - { - "name": "default-inhand-right", - "directions": 4 - }, - { - "name": "silver-inhand-left", - "directions": 4 - }, - { - "name": "silver-inhand-right", - "directions": 4 - }, - { - "name": "orange-inhand-left", - "directions": 4 - }, - { - "name": "orange-inhand-right", - "directions": 4 - }, - { - "name": "blue-inhand-left", - "directions": 4 - }, - { - "name": "blue-inhand-right", - "directions": 4 - } - ] + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/tgstation/tgstation // Icon Edit by FoxxoTrystan", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "default" + }, + { + "name": "centcom" + }, + { + "name": "gold" + }, + { + "name": "silver" + }, + { + "name": "rainbow" + }, + { + "name": "prisoner" + }, + { + "name": "flame" + }, + { + "name": "rpg" + }, + { + "name": "carp" + }, + { + "name": "black" + }, + { + "name": "department" + }, + { + "name": "departmenthead" + }, + { + "name": "subdepartment" + }, + { + "name": "captain" + }, + { + "name": "passenger" + }, + { + "name": "assistant" + }, + { + "name": "securityofficer" + }, + { + "name": "warden" + }, + { + "name": "stationengineer" + }, + { + "name": "medicaldoctor" + }, + { + "name": "paramedic" + }, + { + "name": "chemist" + }, + { + "name": "cargotechnician" + }, + { + "name": "shaftminer" + }, + { + "name": "scientist" + }, + { + "name": "clown" + }, + { + "name": "mime" + }, + { + "name": "chaplain" + }, + { + "name": "janitor" + }, + { + "name": "bartender" + }, + { + "name": "cook" + }, + { + "name": "botanist" + }, + { + "name": "curator" + }, + { + "name": "lawyer" + }, + { + "name": "headofpersonnel" + }, + { + "name": "cc" + }, + { + "name": "musician" + }, + { + "name": "death" + }, + { + "name": "roboticist" + }, + { + "name": "atmospherictechnician" + }, + { + "name": "syndicate" + }, + { + "name": "geneticist" + }, + { + "name": "zookeeper" + }, + { + "name": "detective" + }, + { + "name": "senior" + }, + { + "name": "mailcarrier" + }, + { + "name": "prisonguard" + }, + { + "name": "psychologist" + }, + { + "name": "boxer" + }, + { + "name": "gladiator" + }, + { + "name": "martialartist" + }, + { + "name": "gold-inhand-left", + "directions": 4 + }, + { + "name": "gold-inhand-right", + "directions": 4 + }, + { + "name": "default-inhand-left", + "directions": 4 + }, + { + "name": "default-inhand-right", + "directions": 4 + }, + { + "name": "silver-inhand-left", + "directions": 4 + }, + { + "name": "silver-inhand-right", + "directions": 4 + }, + { + "name": "orange-inhand-left", + "directions": 4 + }, + { + "name": "orange-inhand-right", + "directions": 4 + }, + { + "name": "blue-inhand-left", + "directions": 4 + }, + { + "name": "blue-inhand-right", + "directions": 4 + } + ] } diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/mime.png b/Resources/Textures/Objects/Misc/id_cards.rsi/mime.png new file mode 100644 index 00000000000..8368027151f Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/mime.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/musician.png b/Resources/Textures/Objects/Misc/id_cards.rsi/musician.png new file mode 100644 index 00000000000..1f1df6370ce Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/musician.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/orange.png b/Resources/Textures/Objects/Misc/id_cards.rsi/orange.png deleted file mode 100644 index a44da608ec0..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/orange.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/paramedic.png b/Resources/Textures/Objects/Misc/id_cards.rsi/paramedic.png new file mode 100644 index 00000000000..d1f48a111d3 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/paramedic.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/passenger.png b/Resources/Textures/Objects/Misc/id_cards.rsi/passenger.png new file mode 100644 index 00000000000..9f04507a9c5 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/passenger.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/pirate.png b/Resources/Textures/Objects/Misc/id_cards.rsi/pirate.png deleted file mode 100644 index d5670a71aa1..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/pirate.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner.png b/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner.png new file mode 100644 index 00000000000..ad8052cc01b Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_001.png b/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_001.png deleted file mode 100644 index 67dd3e88622..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_001.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_002.png b/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_002.png deleted file mode 100644 index 004677ba4ab..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_002.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_003.png b/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_003.png deleted file mode 100644 index 23c998f1507..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_003.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_004.png b/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_004.png deleted file mode 100644 index c6e31412cc7..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_004.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_005.png b/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_005.png deleted file mode 100644 index d1d29bb0558..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_005.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_006.png b/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_006.png deleted file mode 100644 index 8b2305d7587..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_006.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_007.png b/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_007.png deleted file mode 100644 index a5041faa88b..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/prisoner_007.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/prisonguard.png b/Resources/Textures/Objects/Misc/id_cards.rsi/prisonguard.png new file mode 100644 index 00000000000..f8570b2595c Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/prisonguard.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/psychologist.png b/Resources/Textures/Objects/Misc/id_cards.rsi/psychologist.png new file mode 100644 index 00000000000..d7d24d69470 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/psychologist.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/rainbow.png b/Resources/Textures/Objects/Misc/id_cards.rsi/rainbow.png new file mode 100644 index 00000000000..a0ccc1758c8 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/rainbow.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/roboticist.png b/Resources/Textures/Objects/Misc/id_cards.rsi/roboticist.png new file mode 100644 index 00000000000..ea7bda6c41e Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/roboticist.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/rpg.png b/Resources/Textures/Objects/Misc/id_cards.rsi/rpg.png new file mode 100644 index 00000000000..f6aa05e158b Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/rpg.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/scientist.png b/Resources/Textures/Objects/Misc/id_cards.rsi/scientist.png new file mode 100644 index 00000000000..7942e128b03 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/scientist.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/securityofficer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/securityofficer.png new file mode 100644 index 00000000000..fffc7d2a065 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/securityofficer.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/senior.png b/Resources/Textures/Objects/Misc/id_cards.rsi/senior.png new file mode 100644 index 00000000000..28729bd5626 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/senior.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/shaftminer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/shaftminer.png new file mode 100644 index 00000000000..4b42f5bbb67 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/shaftminer.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/silver.png b/Resources/Textures/Objects/Misc/id_cards.rsi/silver.png index b13153a246e..81d9626a727 100644 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/silver.png and b/Resources/Textures/Objects/Misc/id_cards.rsi/silver.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/stationengineer.png b/Resources/Textures/Objects/Misc/id_cards.rsi/stationengineer.png new file mode 100644 index 00000000000..c04b4761ea8 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/stationengineer.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/subdepartment.png b/Resources/Textures/Objects/Misc/id_cards.rsi/subdepartment.png new file mode 100644 index 00000000000..cc1547cbf73 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/subdepartment.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/syndicate.png b/Resources/Textures/Objects/Misc/id_cards.rsi/syndicate.png new file mode 100644 index 00000000000..5ed1c8ffe54 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/syndicate.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/syndie.png b/Resources/Textures/Objects/Misc/id_cards.rsi/syndie.png deleted file mode 100644 index 3d5cc6e384f..00000000000 Binary files a/Resources/Textures/Objects/Misc/id_cards.rsi/syndie.png and /dev/null differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/warden.png b/Resources/Textures/Objects/Misc/id_cards.rsi/warden.png new file mode 100644 index 00000000000..754ac67a883 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/warden.png differ diff --git a/Resources/Textures/Objects/Misc/id_cards.rsi/zookeeper.png b/Resources/Textures/Objects/Misc/id_cards.rsi/zookeeper.png new file mode 100644 index 00000000000..29b8dbca067 Binary files /dev/null and b/Resources/Textures/Objects/Misc/id_cards.rsi/zookeeper.png differ diff --git a/Resources/Textures/Objects/Tools/emag.rsi/icon.png b/Resources/Textures/Objects/Tools/emag.rsi/icon.png index 61c283cfd01..912fd6be821 100644 Binary files a/Resources/Textures/Objects/Tools/emag.rsi/icon.png and b/Resources/Textures/Objects/Tools/emag.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Tools/emag.rsi/meta.json b/Resources/Textures/Objects/Tools/emag.rsi/meta.json index 66fc62d663d..d90e37eb584 100644 --- a/Resources/Textures/Objects/Tools/emag.rsi/meta.json +++ b/Resources/Textures/Objects/Tools/emag.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation wiki at https://tgstation13.org/wiki/File:Emag.png, inhand sprites modified from tgstation at commit https://github.com/tgstation/tgstation/commit/d917f4c2a088419d5c3aec7656b7ff8cebd1822e", + "copyright": "Taken from https://github.com/tgstation/tgstation", "size": { "x": 32, "y": 32 @@ -16,7 +16,17 @@ "directions": 4 }, { - "name": "icon" + "name": "icon", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] } ] } diff --git a/RobustToolbox b/RobustToolbox index 8607ba1f16c..eb638099999 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 8607ba1f16ce676a849b59a41efd389a6e467f5c +Subproject commit eb638099999dce3a43d90772ca976ae010d649c0