diff --git a/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs b/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs index 9864dbcb2a..7d7d77f51a 100644 --- a/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs +++ b/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs @@ -20,7 +20,6 @@ public sealed partial class ContentAudioSystem { [Dependency] private readonly IBaseClient _client = default!; [Dependency] private readonly ClientGameTicker _gameTicker = default!; - [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly IResourceCache _resourceCache = default!; private readonly AudioParams _lobbySoundtrackParams = new(-5f, 1, 0, 0, 0, false, 0f); @@ -71,7 +70,7 @@ public sealed partial class ContentAudioSystem Subs.CVar(_configManager, CCVars.LobbyMusicEnabled, LobbyMusicCVarChanged); Subs.CVar(_configManager, CCVars.LobbyMusicVolume, LobbyMusicVolumeCVarChanged); - _stateManager.OnStateChanged += StateManagerOnStateChanged; + _state.OnStateChanged += StateManagerOnStateChanged; _client.PlayerLeaveServer += OnLeave; @@ -115,7 +114,7 @@ public sealed partial class ContentAudioSystem private void LobbyMusicCVarChanged(bool musicEnabled) { - if (musicEnabled && _stateManager.CurrentState is LobbyState) + if (musicEnabled && _state.CurrentState is LobbyState) { StartLobbyMusic(); } @@ -234,7 +233,7 @@ public sealed partial class ContentAudioSystem private void ShutdownLobbyMusic() { - _stateManager.OnStateChanged -= StateManagerOnStateChanged; + _state.OnStateChanged -= StateManagerOnStateChanged; _client.PlayerLeaveServer -= OnLeave; diff --git a/Content.Client/Cargo/Systems/ClientPriceGunSystem.cs b/Content.Client/Cargo/Systems/ClientPriceGunSystem.cs new file mode 100644 index 0000000000..f173932478 --- /dev/null +++ b/Content.Client/Cargo/Systems/ClientPriceGunSystem.cs @@ -0,0 +1,21 @@ +using Content.Shared.Timing; +using Content.Shared.Cargo.Systems; + +namespace Content.Client.Cargo.Systems; + +/// +/// This handles... +/// +public sealed class ClientPriceGunSystem : SharedPriceGunSystem +{ + [Dependency] private readonly UseDelaySystem _useDelay = default!; + + protected override bool GetPriceOrBounty(EntityUid priceGunUid, EntityUid target, EntityUid user) + { + if (!TryComp(priceGunUid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((priceGunUid, useDelay))) + return false; + + // It feels worse if the cooldown is predicted but the popup isn't! So only do the cooldown reset on the server. + return true; + } +} diff --git a/Content.Client/Chat/Managers/ChatManager.cs b/Content.Client/Chat/Managers/ChatManager.cs index e428d30f20..68707e021c 100644 --- a/Content.Client/Chat/Managers/ChatManager.cs +++ b/Content.Client/Chat/Managers/ChatManager.cs @@ -21,6 +21,16 @@ internal sealed class ChatManager : IChatManager _sawmill.Level = LogLevel.Info; } + public void SendAdminAlert(string message) + { + // See server-side manager. This just exists for shared code. + } + + public void SendAdminAlert(EntityUid player, string message) + { + // See server-side manager. This just exists for shared code. + } + public void SendMessage(string text, ChatSelectChannel channel) { var str = text.ToString(); diff --git a/Content.Client/Chat/Managers/IChatManager.cs b/Content.Client/Chat/Managers/IChatManager.cs index 6464ca1019..62a97c6bd8 100644 --- a/Content.Client/Chat/Managers/IChatManager.cs +++ b/Content.Client/Chat/Managers/IChatManager.cs @@ -2,10 +2,8 @@ using Content.Shared.Chat; namespace Content.Client.Chat.Managers { - public interface IChatManager + public interface IChatManager : ISharedChatManager { - void Initialize(); - public void SendMessage(string text, ChatSelectChannel channel); } } diff --git a/Content.Client/Clothing/ClientClothingSystem.cs b/Content.Client/Clothing/ClientClothingSystem.cs index 51eae82839..85d1aea321 100644 --- a/Content.Client/Clothing/ClientClothingSystem.cs +++ b/Content.Client/Clothing/ClientClothingSystem.cs @@ -327,7 +327,8 @@ public sealed class ClientClothingSystem : ClothingSystem if (layerData.State is not null && inventory.SpeciesId is not null && layerData.State.EndsWith(inventory.SpeciesId)) continue; - _displacement.TryAddDisplacement(displacementData, sprite, index, key, revealedLayers); + if (_displacement.TryAddDisplacement(displacementData, sprite, index, key, revealedLayers)) + index++; } } diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index a7793d2657..3b52022407 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -70,7 +70,6 @@ namespace Content.Client.Entry [Dependency] private readonly IResourceManager _resourceManager = default!; [Dependency] private readonly IReplayLoadManager _replayLoad = default!; [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly ContentReplayPlaybackManager _replayMan = default!; [Dependency] private readonly DebugMonitorManager _debugMonitorManager = default!; public override void Init() @@ -192,7 +191,7 @@ namespace Content.Client.Entry _resourceManager, ReplayConstants.ReplayZipFolder.ToRootedPath()); - _replayMan.LastLoad = (null, ReplayConstants.ReplayZipFolder.ToRootedPath()); + _playbackMan.LastLoad = (null, ReplayConstants.ReplayZipFolder.ToRootedPath()); _replayLoad.LoadAndStartReplay(reader); } else if (_gameController.LaunchState.FromLauncher) diff --git a/Content.Client/Inventory/StrippableBoundUserInterface.cs b/Content.Client/Inventory/StrippableBoundUserInterface.cs index 97172f8de8..2ce07758c9 100644 --- a/Content.Client/Inventory/StrippableBoundUserInterface.cs +++ b/Content.Client/Inventory/StrippableBoundUserInterface.cs @@ -98,7 +98,7 @@ namespace Content.Client.Inventory } } - if (EntMan.TryGetComponent(Owner, out var handsComp)) + if (EntMan.TryGetComponent(Owner, out var handsComp) && handsComp.CanBeStripped) { // good ol hands shit code. there is a GuiHands comparer that does the same thing... but these are hands // and not gui hands... which are different... diff --git a/Content.Client/IoC/ClientContentIoC.cs b/Content.Client/IoC/ClientContentIoC.cs index 1fd237cf3e..e643552f70 100644 --- a/Content.Client/IoC/ClientContentIoC.cs +++ b/Content.Client/IoC/ClientContentIoC.cs @@ -18,8 +18,11 @@ using Content.Client.Viewport; using Content.Client.Voting; using Content.Shared.Administration.Logs; using Content.Client.Lobby; +using Content.Client.Players.RateLimiting; using Content.Shared.Administration.Managers; +using Content.Shared.Chat; using Content.Shared.Players.PlayTimeTracking; +using Content.Shared.Players.RateLimiting; namespace Content.Client.IoC { @@ -31,6 +34,7 @@ namespace Content.Client.IoC collection.Register(); collection.Register(); + collection.Register(); collection.Register(); collection.Register(); collection.Register(); @@ -47,10 +51,12 @@ namespace Content.Client.IoC collection.Register(); collection.Register(); collection.Register(); - collection.Register(); + collection.Register(); collection.Register(); collection.Register(); collection.Register(); + collection.Register(); + collection.Register(); } } } diff --git a/Content.Client/Paper/UI/PaperWindow.xaml.cs b/Content.Client/Paper/UI/PaperWindow.xaml.cs index 02c4ed64c3..3522aabc66 100644 --- a/Content.Client/Paper/UI/PaperWindow.xaml.cs +++ b/Content.Client/Paper/UI/PaperWindow.xaml.cs @@ -319,6 +319,8 @@ namespace Content.Client.Paper.UI private void RunOnSaved() { + // Prevent further saving while text processing still in + SaveButton.Disabled = true; OnSaved?.Invoke(Rope.Collapse(Input.TextRope)); } diff --git a/Content.Client/Players/RateLimiting/PlayerRateLimitManager.cs b/Content.Client/Players/RateLimiting/PlayerRateLimitManager.cs new file mode 100644 index 0000000000..e79eadd92b --- /dev/null +++ b/Content.Client/Players/RateLimiting/PlayerRateLimitManager.cs @@ -0,0 +1,23 @@ +using Content.Shared.Players.RateLimiting; +using Robust.Shared.Player; + +namespace Content.Client.Players.RateLimiting; + +public sealed class PlayerRateLimitManager : SharedPlayerRateLimitManager +{ + public override RateLimitStatus CountAction(ICommonSession player, string key) + { + // TODO Rate-Limit + // Add support for rate limit prediction + // I.e., dont mis-predict just because somebody is clicking too quickly. + return RateLimitStatus.Allowed; + } + + public override void Register(string key, RateLimitRegistration registration) + { + } + + public override void Initialize() + { + } +} diff --git a/Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs b/Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs index bf6b65a969..d5bc764b34 100644 --- a/Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs +++ b/Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs @@ -1,4 +1,5 @@ using Content.Shared.Doors.Components; +using Content.Shared.Electrocution; using Content.Shared.Silicons.StationAi; using Robust.Shared.Utility; @@ -6,25 +7,69 @@ namespace Content.Client.Silicons.StationAi; public sealed partial class StationAiSystem { + private readonly ResPath _aiActionsRsi = new ResPath("/Textures/Interface/Actions/actions_ai.rsi"); + private void InitializeAirlock() { SubscribeLocalEvent(OnDoorBoltGetRadial); + SubscribeLocalEvent(OnEmergencyAccessGetRadial); + SubscribeLocalEvent(OnDoorElectrifiedGetRadial); } private void OnDoorBoltGetRadial(Entity ent, ref GetStationAiRadialEvent args) { - args.Actions.Add(new StationAiRadial() - { - Sprite = ent.Comp.BoltsDown ? - new SpriteSpecifier.Rsi( - new ResPath("/Textures/Structures/Doors/Airlocks/Standard/basic.rsi"), "open") : - new SpriteSpecifier.Rsi( - new ResPath("/Textures/Structures/Doors/Airlocks/Standard/basic.rsi"), "closed"), - Tooltip = ent.Comp.BoltsDown ? Loc.GetString("bolt-open") : Loc.GetString("bolt-close"), - Event = new StationAiBoltEvent() + args.Actions.Add( + new StationAiRadial { - Bolted = !ent.Comp.BoltsDown, + Sprite = ent.Comp.BoltsDown + ? new SpriteSpecifier.Rsi(_aiActionsRsi, "unbolt_door") + : new SpriteSpecifier.Rsi(_aiActionsRsi, "bolt_door"), + Tooltip = ent.Comp.BoltsDown + ? Loc.GetString("bolt-open") + : Loc.GetString("bolt-close"), + Event = new StationAiBoltEvent + { + Bolted = !ent.Comp.BoltsDown, + } } - }); + ); + } + + private void OnEmergencyAccessGetRadial(Entity ent, ref GetStationAiRadialEvent args) + { + args.Actions.Add( + new StationAiRadial + { + Sprite = ent.Comp.EmergencyAccess + ? new SpriteSpecifier.Rsi(_aiActionsRsi, "emergency_off") + : new SpriteSpecifier.Rsi(_aiActionsRsi, "emergency_on"), + Tooltip = ent.Comp.EmergencyAccess + ? Loc.GetString("emergency-access-off") + : Loc.GetString("emergency-access-on"), + Event = new StationAiEmergencyAccessEvent + { + EmergencyAccess = !ent.Comp.EmergencyAccess, + } + } + ); + } + + private void OnDoorElectrifiedGetRadial(Entity ent, ref GetStationAiRadialEvent args) + { + args.Actions.Add( + new StationAiRadial + { + Sprite = ent.Comp.Enabled + ? new SpriteSpecifier.Rsi(_aiActionsRsi, "door_overcharge_off") + : new SpriteSpecifier.Rsi(_aiActionsRsi, "door_overcharge_on"), + Tooltip = ent.Comp.Enabled + ? Loc.GetString("electrify-door-off") + : Loc.GetString("electrify-door-on"), + Event = new StationAiElectrifiedEvent + { + Electrified = !ent.Comp.Enabled, + } + } + ); } } diff --git a/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs b/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs index 1dffeb8d2d..a6c1cfc94f 100644 --- a/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs +++ b/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs @@ -398,10 +398,6 @@ public sealed class ActionUIController : UIController, IOnStateChanged (ActionButton) GetChild(index); } - private void BuildActionButtons(int count) + public void SetActionData(ActionsSystem system, params EntityUid?[] actionTypes) { + var uniqueCount = Math.Min(system.GetClientActions().Count(), actionTypes.Length + 1); var keys = ContentKeyFunctions.GetHotbarBoundKeys(); - Children.Clear(); - for (var index = 0; index < count; index++) + for (var i = 0; i < uniqueCount; i++) { - Children.Add(MakeButton(index)); + if (i >= ChildCount) + { + AddChild(MakeButton(i)); + } + + if (!actionTypes.TryGetValue(i, out var action)) + action = null; + ((ActionButton) GetChild(i)).UpdateData(action, system); + } + + for (var i = ChildCount - 1; i >= uniqueCount; i--) + { + RemoveChild(GetChild(i)); } ActionButton MakeButton(int index) @@ -55,20 +67,6 @@ public class ActionButtonContainer : GridContainer } } - public void SetActionData(ActionsSystem system, params EntityUid?[] actionTypes) - { - var uniqueCount = Math.Min(system.GetClientActions().Count(), actionTypes.Length + 1); - if (ChildCount != uniqueCount) - BuildActionButtons(uniqueCount); - - for (var i = 0; i < uniqueCount; i++) - { - if (!actionTypes.TryGetValue(i, out var action)) - action = null; - ((ActionButton) GetChild(i)).UpdateData(action, system); - } - } - public void ClearActionData() { foreach (var button in Children) diff --git a/Content.IntegrationTests/Pair/TestPair.Helpers.cs b/Content.IntegrationTests/Pair/TestPair.Helpers.cs index 588cf0d80e..4604cd8296 100644 --- a/Content.IntegrationTests/Pair/TestPair.Helpers.cs +++ b/Content.IntegrationTests/Pair/TestPair.Helpers.cs @@ -35,9 +35,9 @@ public sealed partial class TestPair mapData.GridCoords = new EntityCoordinates(mapData.Grid, 0, 0); var plating = tileDefinitionManager[tile]; var platingTile = new Tile(plating.TileId); - mapData.Grid.Comp.SetTile(mapData.GridCoords, platingTile); + Server.System().SetTile(mapData.Grid.Owner, mapData.Grid.Comp, mapData.GridCoords, platingTile); mapData.MapCoords = new MapCoordinates(0, 0, mapData.MapId); - mapData.Tile = mapData.Grid.Comp.GetAllTiles().First(); + mapData.Tile = Server.System().GetAllTiles(mapData.Grid.Owner, mapData.Grid.Comp).First(); }); TestMap = mapData; diff --git a/Content.IntegrationTests/PoolManager.Cvars.cs b/Content.IntegrationTests/PoolManager.Cvars.cs index bcd48f8238..23f0ded7df 100644 --- a/Content.IntegrationTests/PoolManager.Cvars.cs +++ b/Content.IntegrationTests/PoolManager.Cvars.cs @@ -36,7 +36,9 @@ public static partial class PoolManager (CCVars.ConfigPresetDevelopment.Name, "false"), (CCVars.AdminLogsEnabled.Name, "false"), (CCVars.AutosaveEnabled.Name, "false"), - (CVars.NetBufferSize.Name, "0") + (CVars.NetBufferSize.Name, "0"), + (CCVars.InteractionRateLimitCount.Name, "9999999"), + (CCVars.InteractionRateLimitPeriod.Name, "0.1"), }; public static async Task SetupCVars(RobustIntegrationTest.IntegrationInstance instance, PoolSettings settings) diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs index fef8a031d9..70fcfccc4e 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs @@ -52,7 +52,6 @@ public sealed partial class AdminVerbSystem [Dependency] private readonly StationJobsSystem _stationJobsSystem = default!; [Dependency] private readonly JointSystem _jointSystem = default!; [Dependency] private readonly BatterySystem _batterySystem = default!; - [Dependency] private readonly SharedTransformSystem _xformSystem = default!; [Dependency] private readonly MetaDataSystem _metaSystem = default!; [Dependency] private readonly GunSystem _gun = default!; @@ -90,22 +89,22 @@ public sealed partial class AdminVerbSystem args.Verbs.Add(bolt); } - if (TryComp(args.Target, out var airlock)) + if (TryComp(args.Target, out var airlockComp)) { Verb emergencyAccess = new() { - Text = airlock.EmergencyAccess ? "Emergency Access Off" : "Emergency Access On", + Text = airlockComp.EmergencyAccess ? "Emergency Access Off" : "Emergency Access On", Category = VerbCategory.Tricks, Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/emergency_access.png")), Act = () => { - _airlockSystem.ToggleEmergencyAccess(args.Target, airlock); + _airlockSystem.SetEmergencyAccess((args.Target, airlockComp), !airlockComp.EmergencyAccess); }, Impact = LogImpact.Medium, - Message = Loc.GetString(airlock.EmergencyAccess + Message = Loc.GetString(airlockComp.EmergencyAccess ? "admin-trick-emergency-access-off-description" : "admin-trick-emergency-access-on-description"), - Priority = (int) (airlock.EmergencyAccess ? TricksVerbPriorities.EmergencyAccessOff : TricksVerbPriorities.EmergencyAccessOn), + Priority = (int) (airlockComp.EmergencyAccess ? TricksVerbPriorities.EmergencyAccessOff : TricksVerbPriorities.EmergencyAccessOn), }; args.Verbs.Add(emergencyAccess); } @@ -327,7 +326,7 @@ public sealed partial class AdminVerbSystem Act = () => { var (mapUid, gridUid) = _adminTestArenaSystem.AssertArenaLoaded(player); - _xformSystem.SetCoordinates(args.Target, new EntityCoordinates(gridUid ?? mapUid, Vector2.One)); + _transformSystem.SetCoordinates(args.Target, new EntityCoordinates(gridUid ?? mapUid, Vector2.One)); }, Impact = LogImpact.Medium, Message = Loc.GetString("admin-trick-send-to-test-arena-description"), @@ -533,7 +532,7 @@ public sealed partial class AdminVerbSystem if (shuttle is null) return; - _xformSystem.SetCoordinates(args.User, new EntityCoordinates(shuttle.Value, Vector2.Zero)); + _transformSystem.SetCoordinates(args.User, new EntityCoordinates(shuttle.Value, Vector2.Zero)); }, Impact = LogImpact.Low, Message = Loc.GetString("admin-trick-locate-cargo-shuttle-description"), diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.cs b/Content.Server/Administration/Systems/AdminVerbSystem.cs index 5aa05ce28b..2ab27e4388 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.cs @@ -35,10 +35,8 @@ using Robust.Shared.Toolshed; using Robust.Shared.Utility; using System.Linq; using Content.Server.Silicons.Laws; -using Content.Shared.Silicons.Laws; using Content.Shared.Silicons.Laws.Components; using Robust.Server.Player; -using Content.Shared.Mind; using Robust.Shared.Physics.Components; using static Content.Shared.Configurable.ConfigurationComponent; @@ -63,7 +61,6 @@ namespace Content.Server.Administration.Systems [Dependency] private readonly ArtifactSystem _artifactSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly PrayerSystem _prayerSystem = default!; - [Dependency] private readonly EuiManager _eui = default!; [Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly ToolshedManager _toolshed = default!; [Dependency] private readonly RejuvenateSystem _rejuvenate = default!; @@ -294,7 +291,7 @@ namespace Content.Server.Administration.Systems Act = () => { var ui = new AdminLogsEui(); - _eui.OpenEui(ui, player); + _euiManager.OpenEui(ui, player); ui.SetLogFilter(search:args.Target.Id.ToString()); }, Impact = LogImpact.Low diff --git a/Content.Server/Administration/Systems/BwoinkSystem.cs b/Content.Server/Administration/Systems/BwoinkSystem.cs index 893de4aba5..1efc0a9d56 100644 --- a/Content.Server/Administration/Systems/BwoinkSystem.cs +++ b/Content.Server/Administration/Systems/BwoinkSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Administration; using Content.Shared.CCVar; using Content.Shared.GameTicking; using Content.Shared.Mind; +using Content.Shared.Players.RateLimiting; using JetBrains.Annotations; using Robust.Server.Player; using Robust.Shared; @@ -104,12 +105,10 @@ namespace Content.Server.Administration.Systems _rateLimit.Register( RateLimitKey, - new RateLimitRegistration - { - CVarLimitPeriodLength = CCVars.AhelpRateLimitPeriod, - CVarLimitCount = CCVars.AhelpRateLimitCount, - PlayerLimitedAction = PlayerRateLimitedAction - }); + new RateLimitRegistration(CCVars.AhelpRateLimitPeriod, + CCVars.AhelpRateLimitCount, + PlayerRateLimitedAction) + ); } private void PlayerRateLimitedAction(ICommonSession obj) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs index 70c4639e48..f38ec3fef6 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -281,6 +281,17 @@ namespace Content.Server.Atmos.EntitySystems return true; } + /// + /// Compares two TileAtmospheres to see if they are within acceptable ranges for group processing to be enabled. + /// + public GasCompareResult CompareExchange(TileAtmosphere sample, TileAtmosphere otherSample) + { + if (sample.AirArchived == null || otherSample.AirArchived == null) + return GasCompareResult.NoExchange; + + return CompareExchange(sample.AirArchived, otherSample.AirArchived); + } + /// /// Compares two gas mixtures to see if they are within acceptable ranges for group processing to be enabled. /// diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs index 3983234dea..b11eb5dc3e 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs @@ -270,7 +270,7 @@ public sealed partial class AtmosphereSystem { DebugTools.AssertNotNull(tile.Air); DebugTools.Assert(tile.Air?.Immutable == false); - Array.Clear(tile.MolesArchived); + tile.AirArchived = null; tile.ArchivedCycle = 0; var count = 0; diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs index b96dd0ac48..0b43c92414 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs @@ -210,7 +210,7 @@ namespace Content.Server.Atmos.EntitySystems MovedByPressureComponent.ProbabilityOffset); // Can we yeet the thing (due to probability, strength, etc.) - if (moveProb > MovedByPressureComponent.ProbabilityOffset && _robustRandom.Prob(MathF.Min(moveProb / 100f, 1f)) + if (moveProb > MovedByPressureComponent.ProbabilityOffset && _random.Prob(MathF.Min(moveProb / 100f, 1f)) && !float.IsPositiveInfinity(component.MoveResist) && (physics.BodyType != BodyType.Static && (maxForce >= (component.MoveResist * MovedByPressureComponent.MoveForcePushRatio))) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs index fb2375899d..55b38924c0 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs @@ -54,7 +54,7 @@ namespace Content.Server.Atmos.EntitySystems } shouldShareAir = true; - } else if (CompareExchange(tile.Air, enemyTile.Air) != GasCompareResult.NoExchange) + } else if (CompareExchange(tile, enemyTile) != GasCompareResult.NoExchange) { AddActiveTile(gridAtmosphere, enemyTile); if (ExcitedGroups) @@ -117,9 +117,8 @@ namespace Content.Server.Atmos.EntitySystems private void Archive(TileAtmosphere tile, int fireCount) { if (tile.Air != null) - tile.Air.Moles.AsSpan().CopyTo(tile.MolesArchived.AsSpan()); + tile.AirArchived = new GasMixture(tile.Air); - tile.TemperatureArchived = tile.Temperature; tile.ArchivedCycle = fireCount; } @@ -184,10 +183,10 @@ namespace Content.Server.Atmos.EntitySystems /// public float GetHeatCapacityArchived(TileAtmosphere tile) { - if (tile.Air == null) + if (tile.AirArchived == null) return tile.HeatCapacity; - return GetHeatCapacityCalculation(tile.MolesArchived!, tile.Space); + return GetHeatCapacity(tile.AirArchived); } /// @@ -195,10 +194,11 @@ namespace Content.Server.Atmos.EntitySystems /// public float Share(TileAtmosphere tileReceiver, TileAtmosphere tileSharer, int atmosAdjacentTurfs) { - if (tileReceiver.Air is not {} receiver || tileSharer.Air is not {} sharer) + if (tileReceiver.Air is not {} receiver || tileSharer.Air is not {} sharer || + tileReceiver.AirArchived == null || tileSharer.AirArchived == null) return 0f; - var temperatureDelta = tileReceiver.TemperatureArchived - tileSharer.TemperatureArchived; + var temperatureDelta = tileReceiver.AirArchived.Temperature - tileSharer.AirArchived.Temperature; var absTemperatureDelta = Math.Abs(temperatureDelta); var oldHeatCapacity = 0f; var oldSharerHeatCapacity = 0f; @@ -249,12 +249,12 @@ namespace Content.Server.Atmos.EntitySystems // Transfer of thermal energy (via changed heat capacity) between self and sharer. if (!receiver.Immutable && newHeatCapacity > Atmospherics.MinimumHeatCapacity) { - receiver.Temperature = ((oldHeatCapacity * receiver.Temperature) - (heatCapacityToSharer * tileReceiver.TemperatureArchived) + (heatCapacitySharerToThis * tileSharer.TemperatureArchived)) / newHeatCapacity; + receiver.Temperature = ((oldHeatCapacity * receiver.Temperature) - (heatCapacityToSharer * tileReceiver.AirArchived.Temperature) + (heatCapacitySharerToThis * tileSharer.AirArchived.Temperature)) / newHeatCapacity; } if (!sharer.Immutable && newSharerHeatCapacity > Atmospherics.MinimumHeatCapacity) { - sharer.Temperature = ((oldSharerHeatCapacity * sharer.Temperature) - (heatCapacitySharerToThis * tileSharer.TemperatureArchived) + (heatCapacityToSharer * tileReceiver.TemperatureArchived)) / newSharerHeatCapacity; + sharer.Temperature = ((oldSharerHeatCapacity * sharer.Temperature) - (heatCapacitySharerToThis * tileSharer.AirArchived.Temperature) + (heatCapacityToSharer * tileReceiver.AirArchived.Temperature)) / newSharerHeatCapacity; } // Thermal energy of the system (self and sharer) is unchanged. @@ -273,7 +273,7 @@ namespace Content.Server.Atmos.EntitySystems var moles = receiver.TotalMoles; var theirMoles = sharer.TotalMoles; - return (tileReceiver.TemperatureArchived * (moles + movedMoles)) - (tileSharer.TemperatureArchived * (theirMoles - movedMoles)) * Atmospherics.R / receiver.Volume; + return (tileReceiver.AirArchived.Temperature * (moles + movedMoles)) - (tileSharer.AirArchived.Temperature * (theirMoles - movedMoles)) * Atmospherics.R / receiver.Volume; } /// @@ -281,10 +281,11 @@ namespace Content.Server.Atmos.EntitySystems /// public float TemperatureShare(TileAtmosphere tileReceiver, TileAtmosphere tileSharer, float conductionCoefficient) { - if (tileReceiver.Air is not { } receiver || tileSharer.Air is not { } sharer) + if (tileReceiver.Air is not { } receiver || tileSharer.Air is not { } sharer || + tileReceiver.AirArchived == null || tileSharer.AirArchived == null) return 0f; - var temperatureDelta = tileReceiver.TemperatureArchived - tileSharer.TemperatureArchived; + var temperatureDelta = tileReceiver.AirArchived.Temperature - tileSharer.AirArchived.Temperature; if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider) { var heatCapacity = GetHeatCapacityArchived(tileReceiver); @@ -310,10 +311,10 @@ namespace Content.Server.Atmos.EntitySystems /// public float TemperatureShare(TileAtmosphere tileReceiver, float conductionCoefficient, float sharerTemperature, float sharerHeatCapacity) { - if (tileReceiver.Air is not {} receiver) + if (tileReceiver.Air is not {} receiver || tileReceiver.AirArchived == null) return 0; - var temperatureDelta = tileReceiver.TemperatureArchived - sharerTemperature; + var temperatureDelta = tileReceiver.AirArchived.Temperature - sharerTemperature; if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider) { var heatCapacity = GetHeatCapacityArchived(tileReceiver); diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs index 08193027d6..e6466c8157 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs @@ -355,7 +355,7 @@ namespace Content.Server.Atmos.EntitySystems continue; DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite())); - if (otherTile2.Air != null && CompareExchange(otherTile2.Air, tile.Air) == GasCompareResult.NoExchange) + if (otherTile2.Air != null && CompareExchange(otherTile2, tile) == GasCompareResult.NoExchange) continue; AddActiveTile(gridAtmosphere, otherTile2); @@ -689,7 +689,7 @@ namespace Content.Server.Atmos.EntitySystems var chance = MathHelper.Clamp(0.01f + (sum / SpacingMaxWind) * 0.3f, 0.003f, 0.3f); - if (sum > 20 && _robustRandom.Prob(chance)) + if (sum > 20 && _random.Prob(chance)) PryTile(mapGrid, tile.GridIndices); } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs index 85b1a93e20..59320ba67b 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs @@ -231,7 +231,7 @@ namespace Content.Server.Atmos.EntitySystems tile.MapAtmosphere = false; atmos.MapTiles.Remove(tile); tile.Air = null; - Array.Clear(tile.MolesArchived); + tile.AirArchived = null; tile.ArchivedCycle = 0; tile.LastShare = 0f; tile.Space = false; @@ -261,7 +261,7 @@ namespace Content.Server.Atmos.EntitySystems return; tile.Air = null; - Array.Clear(tile.MolesArchived); + tile.AirArchived = null; tile.ArchivedCycle = 0; tile.LastShare = 0f; tile.Hotspot = new Hotspot(); diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs index 8ed92a9d0e..46a554054b 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs @@ -131,7 +131,10 @@ namespace Content.Server.Atmos.EntitySystems private void TemperatureShareMutualSolid(TileAtmosphere tile, TileAtmosphere other, float conductionCoefficient) { - var deltaTemperature = (tile.TemperatureArchived - other.TemperatureArchived); + if (tile.AirArchived == null || other.AirArchived == null) + return; + + var deltaTemperature = (tile.AirArchived.Temperature - other.AirArchived.Temperature); if (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider && tile.HeatCapacity != 0f && other.HeatCapacity != 0f) { @@ -145,11 +148,14 @@ namespace Content.Server.Atmos.EntitySystems public void RadiateToSpace(TileAtmosphere tile) { + if (tile.AirArchived == null) + return; + // Considering 0ºC as the break even point for radiation in and out. if (tile.Temperature > Atmospherics.T0C) { // Hardcoded space temperature. - var deltaTemperature = (tile.TemperatureArchived - Atmospherics.TCMB); + var deltaTemperature = (tile.AirArchived.Temperature - Atmospherics.TCMB); if ((tile.HeatCapacity > 0) && (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider)) { var heat = tile.ThermalConductivity * deltaTemperature * (tile.HeatCapacity * diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs index 13d8f73dc5..f27f7411bf 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs @@ -14,7 +14,6 @@ using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Physics.Systems; using Robust.Shared.Prototypes; -using Robust.Shared.Random; using System.Linq; namespace Content.Server.Atmos.EntitySystems; @@ -27,7 +26,6 @@ public sealed partial class AtmosphereSystem : SharedAtmosphereSystem { [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly IAdminLogManager _adminLog = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly InternalsSystem _internals = default!; @@ -39,7 +37,6 @@ public sealed partial class AtmosphereSystem : SharedAtmosphereSystem [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly TileSystem _tile = default!; [Dependency] private readonly MapSystem _map = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] public readonly PuddleSystem Puddle = default!; private const float ExposedUpdateDelay = 1f; @@ -124,6 +121,6 @@ public sealed partial class AtmosphereSystem : SharedAtmosphereSystem private void CacheDecals() { - _burntDecals = _prototypeManager.EnumeratePrototypes().Where(x => x.Tags.Contains("burnt")).Select(x => x.ID).ToArray(); + _burntDecals = _protoMan.EnumeratePrototypes().Where(x => x.Tags.Contains("burnt")).Select(x => x.ID).ToArray(); } } diff --git a/Content.Server/Atmos/TileAtmosphere.cs b/Content.Server/Atmos/TileAtmosphere.cs index 0026dbbf4f..46a85990fa 100644 --- a/Content.Server/Atmos/TileAtmosphere.cs +++ b/Content.Server/Atmos/TileAtmosphere.cs @@ -22,9 +22,6 @@ namespace Content.Server.Atmos [ViewVariables] public float Temperature { get; set; } = Atmospherics.T20C; - [ViewVariables] - public float TemperatureArchived { get; set; } = Atmospherics.T20C; - [ViewVariables] public TileAtmosphere? PressureSpecificTarget { get; set; } @@ -93,12 +90,16 @@ namespace Content.Server.Atmos [Access(typeof(AtmosphereSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends public GasMixture? Air { get; set; } + /// + /// Like Air, but a copy stored each atmos tick before tile processing takes place. This lets us update Air + /// in-place without affecting the results based on update order. + /// + [ViewVariables] + public GasMixture? AirArchived; + [DataField("lastShare")] public float LastShare; - [ViewVariables] - public readonly float[] MolesArchived = new float[Atmospherics.AdjustedNumberOfGases]; - GasMixture IGasMixtureHolder.Air { get => Air ?? new GasMixture(Atmospherics.CellVolume){ Temperature = Temperature }; @@ -139,6 +140,7 @@ namespace Content.Server.Atmos GridIndex = gridIndex; GridIndices = gridIndices; Air = mixture; + AirArchived = Air != null ? Air.Clone() : null; Space = space; if(immutable) @@ -153,7 +155,7 @@ namespace Content.Server.Atmos NoGridTile = other.NoGridTile; MapAtmosphere = other.MapAtmosphere; Air = other.Air?.Clone(); - Array.Copy(other.MolesArchived, MolesArchived, MolesArchived.Length); + AirArchived = Air != null ? Air.Clone() : null; } public TileAtmosphere() diff --git a/Content.Server/Body/Systems/LungSystem.cs b/Content.Server/Body/Systems/LungSystem.cs index 901d61f8fc..859618ae1a 100644 --- a/Content.Server/Body/Systems/LungSystem.cs +++ b/Content.Server/Body/Systems/LungSystem.cs @@ -14,7 +14,6 @@ public sealed class LungSystem : EntitySystem [Dependency] private readonly AtmosphereSystem _atmos = default!; [Dependency] private readonly InternalsSystem _internals = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; - [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; public static string LungSolutionName = "Lung"; @@ -29,7 +28,7 @@ public sealed class LungSystem : EntitySystem private void OnGotUnequipped(Entity ent, ref GotUnequippedEvent args) { - _atmosphereSystem.DisconnectInternals(ent); + _atmos.DisconnectInternals(ent); } private void OnGotEquipped(Entity ent, ref GotEquippedEvent args) @@ -93,7 +92,7 @@ public sealed class LungSystem : EntitySystem if (moles <= 0) continue; - var reagent = _atmosphereSystem.GasReagents[i]; + var reagent = _atmos.GasReagents[i]; if (reagent is null) continue; diff --git a/Content.Server/Botany/Systems/MutationSystem.cs b/Content.Server/Botany/Systems/MutationSystem.cs index 07a24d19f6..37b68d9475 100644 --- a/Content.Server/Botany/Systems/MutationSystem.cs +++ b/Content.Server/Botany/Systems/MutationSystem.cs @@ -27,7 +27,7 @@ public sealed class MutationSystem : EntitySystem { foreach (var mutation in _randomMutations.mutations) { - if (Random(mutation.BaseOdds * severity)) + if (Random(Math.Min(mutation.BaseOdds * severity, 1.0f))) { if (mutation.AppliesToPlant) { diff --git a/Content.Server/Cargo/Components/PriceGunComponent.cs b/Content.Server/Cargo/Components/PriceGunComponent.cs deleted file mode 100644 index 7207beae99..0000000000 --- a/Content.Server/Cargo/Components/PriceGunComponent.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Content.Server.Cargo.Components; - -/// -/// This is used for the price gun, which calculates the price of any object it appraises. -/// -[RegisterComponent] -public sealed partial class PriceGunComponent : Component -{ - -} diff --git a/Content.Server/Cargo/Systems/PriceGunSystem.cs b/Content.Server/Cargo/Systems/PriceGunSystem.cs index 19fe07bd25..94f9f9cba9 100644 --- a/Content.Server/Cargo/Systems/PriceGunSystem.cs +++ b/Content.Server/Cargo/Systems/PriceGunSystem.cs @@ -1,73 +1,34 @@ -using Content.Server.Cargo.Components; using Content.Server.Popups; using Content.Shared.IdentityManagement; -using Content.Shared.Interaction; using Content.Shared.Timing; -using Content.Shared.Verbs; +using Content.Shared.Cargo.Systems; namespace Content.Server.Cargo.Systems; -/// -/// This handles... -/// -public sealed class PriceGunSystem : EntitySystem +public sealed class PriceGunSystem : SharedPriceGunSystem { [Dependency] private readonly UseDelaySystem _useDelay = default!; [Dependency] private readonly PricingSystem _pricingSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly CargoSystem _bountySystem = default!; - /// - public override void Initialize() + protected override bool GetPriceOrBounty(EntityUid priceGunUid, EntityUid target, EntityUid user) { - SubscribeLocalEvent(OnAfterInteract); - SubscribeLocalEvent>(OnUtilityVerb); - } - - private void OnUtilityVerb(EntityUid uid, PriceGunComponent component, GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract || args.Using == null) - return; - - if (!TryComp(uid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((uid, useDelay))) - return; - - var price = _pricingSystem.GetPrice(args.Target); - - var verb = new UtilityVerb() - { - Act = () => - { - _popupSystem.PopupEntity(Loc.GetString("price-gun-pricing-result", ("object", Identity.Entity(args.Target, EntityManager)), ("price", $"{price:F2}")), args.User, args.User); - _useDelay.TryResetDelay((uid, useDelay)); - }, - Text = Loc.GetString("price-gun-verb-text"), - Message = Loc.GetString("price-gun-verb-message", ("object", Identity.Entity(args.Target, EntityManager))) - }; - - args.Verbs.Add(verb); - } - - private void OnAfterInteract(EntityUid uid, PriceGunComponent component, AfterInteractEvent args) - { - if (!args.CanReach || args.Target == null || args.Handled) - return; - - if (!TryComp(uid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((uid, useDelay))) - return; + if (!TryComp(priceGunUid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((priceGunUid, useDelay))) + return false; // Check if we're scanning a bounty crate - if (_bountySystem.IsBountyComplete(args.Target.Value, out _)) + if (_bountySystem.IsBountyComplete(target, out _)) { - _popupSystem.PopupEntity(Loc.GetString("price-gun-bounty-complete"), args.User, args.User); + _popupSystem.PopupEntity(Loc.GetString("price-gun-bounty-complete"), user, user); } else // Otherwise appraise the price { - double price = _pricingSystem.GetPrice(args.Target.Value); - _popupSystem.PopupEntity(Loc.GetString("price-gun-pricing-result", ("object", Identity.Entity(args.Target.Value, EntityManager)), ("price", $"{price:F2}")), args.User, args.User); + var price = _pricingSystem.GetPrice(target); + _popupSystem.PopupEntity(Loc.GetString("price-gun-pricing-result", ("object", Identity.Entity(target, EntityManager)), ("price", $"{price:F2}")), user, user); } - _useDelay.TryResetDelay((uid, useDelay)); - args.Handled = true; + _useDelay.TryResetDelay((priceGunUid, useDelay)); + return true; } } diff --git a/Content.Server/CartridgeLoader/Cartridges/MedTekCartridgeComponent.cs b/Content.Server/CartridgeLoader/Cartridges/MedTekCartridgeComponent.cs new file mode 100644 index 0000000000..1a49b4d1f7 --- /dev/null +++ b/Content.Server/CartridgeLoader/Cartridges/MedTekCartridgeComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.CartridgeLoader.Cartridges; + +[RegisterComponent] +public sealed partial class MedTekCartridgeComponent : Component +{ +} diff --git a/Content.Server/CartridgeLoader/Cartridges/MedTekCartridgeSystem.cs b/Content.Server/CartridgeLoader/Cartridges/MedTekCartridgeSystem.cs new file mode 100644 index 0000000000..4d1b71dad0 --- /dev/null +++ b/Content.Server/CartridgeLoader/Cartridges/MedTekCartridgeSystem.cs @@ -0,0 +1,31 @@ +using Content.Server.Medical.Components; +using Content.Shared.CartridgeLoader; + +namespace Content.Server.CartridgeLoader.Cartridges; + +public sealed class MedTekCartridgeSystem : EntitySystem +{ + [Dependency] private readonly CartridgeLoaderSystem _cartridgeLoaderSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnCartridgeAdded); + SubscribeLocalEvent(OnCartridgeRemoved); + } + + private void OnCartridgeAdded(Entity ent, ref CartridgeAddedEvent args) + { + var healthAnalyzer = EnsureComp(args.Loader); + } + + private void OnCartridgeRemoved(Entity ent, ref CartridgeRemovedEvent args) + { + // only remove when the program itself is removed + if (!_cartridgeLoaderSystem.HasProgram(args.Loader)) + { + RemComp(args.Loader); + } + } +} diff --git a/Content.Server/Chat/Managers/ChatManager.RateLimit.cs b/Content.Server/Chat/Managers/ChatManager.RateLimit.cs index 45e7d2e20d..ccb38166a6 100644 --- a/Content.Server/Chat/Managers/ChatManager.RateLimit.cs +++ b/Content.Server/Chat/Managers/ChatManager.RateLimit.cs @@ -1,6 +1,6 @@ -using Content.Server.Players.RateLimiting; using Content.Shared.CCVar; using Content.Shared.Database; +using Content.Shared.Players.RateLimiting; using Robust.Shared.Player; namespace Content.Server.Chat.Managers; @@ -12,15 +12,13 @@ internal sealed partial class ChatManager private void RegisterRateLimits() { _rateLimitManager.Register(RateLimitKey, - new RateLimitRegistration - { - CVarLimitPeriodLength = CCVars.ChatRateLimitPeriod, - CVarLimitCount = CCVars.ChatRateLimitCount, - CVarAdminAnnounceDelay = CCVars.ChatRateLimitAnnounceAdminsDelay, - PlayerLimitedAction = RateLimitPlayerLimited, - AdminAnnounceAction = RateLimitAlertAdmins, - AdminLogType = LogType.ChatRateLimited, - }); + new RateLimitRegistration(CCVars.ChatRateLimitPeriod, + CCVars.ChatRateLimitCount, + RateLimitPlayerLimited, + CCVars.ChatRateLimitAnnounceAdminsDelay, + RateLimitAlertAdmins, + LogType.ChatRateLimited) + ); } private void RateLimitPlayerLimited(ICommonSession player) @@ -30,8 +28,7 @@ internal sealed partial class ChatManager private void RateLimitAlertAdmins(ICommonSession player) { - if (_configurationManager.GetCVar(CCVars.ChatRateLimitAnnounceAdmins)) - SendAdminAlert(Loc.GetString("chat-manager-rate-limit-admin-announcement", ("player", player.Name))); + SendAdminAlert(Loc.GetString("chat-manager-rate-limit-admin-announcement", ("player", player.Name))); } public RateLimitStatus HandleRateLimit(ICommonSession player) diff --git a/Content.Server/Chat/Managers/ChatManager.cs b/Content.Server/Chat/Managers/ChatManager.cs index 02f718daef..75c46abe37 100644 --- a/Content.Server/Chat/Managers/ChatManager.cs +++ b/Content.Server/Chat/Managers/ChatManager.cs @@ -12,6 +12,7 @@ using Content.Shared.CCVar; using Content.Shared.Chat; using Content.Shared.Database; using Content.Shared.Mind; +using Content.Shared.Players.RateLimiting; using Robust.Shared.Configuration; using Robust.Shared.Network; using Robust.Shared.Player; diff --git a/Content.Server/Chat/Managers/IChatManager.cs b/Content.Server/Chat/Managers/IChatManager.cs index 76fa91d847..23211c28fa 100644 --- a/Content.Server/Chat/Managers/IChatManager.cs +++ b/Content.Server/Chat/Managers/IChatManager.cs @@ -1,17 +1,14 @@ using System.Diagnostics.CodeAnalysis; -using Content.Server.Players; -using Content.Server.Players.RateLimiting; using Content.Shared.Administration; using Content.Shared.Chat; +using Content.Shared.Players.RateLimiting; using Robust.Shared.Network; using Robust.Shared.Player; namespace Content.Server.Chat.Managers { - public interface IChatManager + public interface IChatManager : ISharedChatManager { - void Initialize(); - /// /// Dispatch a server announcement to every connected player. /// @@ -26,8 +23,6 @@ namespace Content.Server.Chat.Managers void SendHookOOC(string sender, string message); void SendAdminAnnouncement(string message, AdminFlags? flagBlacklist = null, AdminFlags? flagWhitelist = null); void SendAdminAnnouncementMessage(ICommonSession player, string message, bool suppressLog = true); - void SendAdminAlert(string message); - void SendAdminAlert(EntityUid player, string message); void ChatMessageToOne(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0, NetUserId? author = null); diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index 24937ea4b9..624c18130b 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -20,6 +20,7 @@ using Content.Shared.Ghost; using Content.Shared.IdentityManagement; using Content.Shared.Mobs.Systems; using Content.Shared.Players; +using Content.Shared.Players.RateLimiting; using Content.Shared.Radio; using Content.Shared.Whitelist; using Robust.Server.Player; diff --git a/Content.Server/Connection/ConnectionManager.cs b/Content.Server/Connection/ConnectionManager.cs index 1b142830f1..2c1f9fb36f 100644 --- a/Content.Server/Connection/ConnectionManager.cs +++ b/Content.Server/Connection/ConnectionManager.cs @@ -46,7 +46,6 @@ namespace Content.Server.Connection /// public sealed partial class ConnectionManager : IConnectionManager { - [Dependency] private readonly IServerDbManager _dbManager = default!; [Dependency] private readonly IPlayerManager _plyMgr = default!; [Dependency] private readonly IServerNetManager _netMgr = default!; [Dependency] private readonly IServerDbManager _db = default!; @@ -205,7 +204,7 @@ namespace Content.Server.Connection return null; } - var adminData = await _dbManager.GetAdminDataForAsync(e.UserId); + var adminData = await _db.GetAdminDataForAsync(e.UserId); if (_cfg.GetCVar(CCVars.PanicBunkerEnabled) && adminData == null) { @@ -213,7 +212,7 @@ namespace Content.Server.Connection var customReason = _cfg.GetCVar(CCVars.PanicBunkerCustomReason); var minMinutesAge = _cfg.GetCVar(CCVars.PanicBunkerMinAccountAge); - var record = await _dbManager.GetPlayerRecordByUserId(userId); + var record = await _db.GetPlayerRecordByUserId(userId); var validAccountAge = record != null && record.FirstSeenTime.CompareTo(DateTimeOffset.UtcNow - TimeSpan.FromMinutes(minMinutesAge)) <= 0; var bypassAllowed = _cfg.GetCVar(CCVars.BypassBunkerWhitelist) && await _db.GetWhitelistStatusAsync(userId); @@ -317,7 +316,7 @@ namespace Content.Server.Connection var maxPlaytimeMinutes = _cfg.GetCVar(CCVars.BabyJailMaxOverallMinutes); // Wait some time to lookup data - var record = await _dbManager.GetPlayerRecordByUserId(userId); + var record = await _db.GetPlayerRecordByUserId(userId); // No player record = new account or the DB is having a skill issue if (record == null) diff --git a/Content.Server/Construction/Completions/AttemptElectrocute.cs b/Content.Server/Construction/Completions/AttemptElectrocute.cs index 05f0977b66..5c97d5e90f 100644 --- a/Content.Server/Construction/Completions/AttemptElectrocute.cs +++ b/Content.Server/Construction/Completions/AttemptElectrocute.cs @@ -1,4 +1,5 @@ using Content.Server.Electrocution; +using Content.Shared.Electrocution; using Content.Shared.Construction; namespace Content.Server.Construction.Completions; diff --git a/Content.Server/Decals/DecalSystem.cs b/Content.Server/Decals/DecalSystem.cs index 519f7a133e..718475754f 100644 --- a/Content.Server/Decals/DecalSystem.cs +++ b/Content.Server/Decals/DecalSystem.cs @@ -35,7 +35,7 @@ namespace Content.Server.Decals [Dependency] private readonly IConfigurationManager _conf = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly MapSystem _mapSystem = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; private readonly Dictionary> _dirtyChunks = new(); private readonly Dictionary>> _previousSentChunks = new(); @@ -106,7 +106,7 @@ namespace Content.Server.Decals return; // Transfer decals over to the new grid. - var enumerator = Comp(ev.Grid).GetAllTilesEnumerator(); + var enumerator = _mapSystem.GetAllTilesEnumerator(ev.Grid, Comp(ev.Grid)); var oldChunkCollection = oldComp.ChunkCollection.ChunkCollection; var chunkCollection = newComp.ChunkCollection.ChunkCollection; diff --git a/Content.Server/Discord/WebhookMessages/VoteWebhooks.cs b/Content.Server/Discord/WebhookMessages/VoteWebhooks.cs new file mode 100644 index 0000000000..74953d10fa --- /dev/null +++ b/Content.Server/Discord/WebhookMessages/VoteWebhooks.cs @@ -0,0 +1,183 @@ +using Content.Server.GameTicking; +using Content.Server.Voting; +using Robust.Server; +using Robust.Shared.Configuration; +using Robust.Shared.Utility; +using System.Diagnostics; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace Content.Server.Discord.WebhookMessages; + +public sealed class VoteWebhooks : IPostInjectInit +{ + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly IEntitySystemManager _entSys = default!; + [Dependency] private readonly DiscordWebhook _discord = default!; + [Dependency] private readonly IBaseServer _baseServer = default!; + + private ISawmill _sawmill = default!; + + public WebhookState? CreateWebhookIfConfigured(VoteOptions voteOptions, string? webhookUrl = null, string? customVoteName = null, string? customVoteMessage = null) + { + // All this webhook code is complete garbage. + // I tried to clean it up somewhat, at least to fix the glaring bugs in it. + // Jesus christ man what is with our code review process. + + if (string.IsNullOrEmpty(webhookUrl)) + return null; + + // Set up the webhook payload + var serverName = _baseServer.ServerName; + + var fields = new List(); + + foreach (var voteOption in voteOptions.Options) + { + var newVote = new WebhookEmbedField + { + Name = voteOption.text, + Value = Loc.GetString("custom-vote-webhook-option-pending") + }; + fields.Add(newVote); + } + + var gameTicker = _entSys.GetEntitySystemOrNull(); + _sawmill = Logger.GetSawmill("discord"); + + var runLevel = gameTicker != null ? Loc.GetString($"game-run-level-{gameTicker.RunLevel}") : ""; + var runId = gameTicker != null ? gameTicker.RoundId : 0; + + var voteName = customVoteName ?? Loc.GetString("custom-vote-webhook-name"); + var description = customVoteMessage ?? voteOptions.Title; + + var payload = new WebhookPayload() + { + Username = voteName, + Embeds = new List + { + new() + { + Title = voteOptions.InitiatorText, + Color = 13438992, // #CD1010 + Description = description, + Footer = new WebhookEmbedFooter + { + Text = Loc.GetString( + "custom-vote-webhook-footer", + ("serverName", serverName), + ("roundId", runId), + ("runLevel", runLevel)), + }, + + Fields = fields, + }, + }, + }; + + var state = new WebhookState + { + WebhookUrl = webhookUrl, + Payload = payload, + }; + + CreateWebhookMessage(state, payload); + + return state; + } + + public void UpdateWebhookIfConfigured(WebhookState? state, VoteFinishedEventArgs finished) + { + if (state == null) + return; + + var embed = state.Payload.Embeds![0]; + embed.Color = 2353993; // #23EB49 + + for (var i = 0; i < finished.Votes.Count; i++) + { + var oldName = embed.Fields[i].Name; + var newValue = finished.Votes[i].ToString(); + embed.Fields[i] = new WebhookEmbedField { Name = oldName, Value = newValue, Inline = true }; + } + + state.Payload.Embeds[0] = embed; + + UpdateWebhookMessage(state, state.Payload, state.MessageId); + } + + public void UpdateCancelledWebhookIfConfigured(WebhookState? state, string? customCancelReason = null) + { + if (state == null) + return; + + var embed = state.Payload.Embeds![0]; + embed.Color = 13356304; // #CBCD10 + if (customCancelReason == null) + embed.Description += "\n\n" + Loc.GetString("custom-vote-webhook-cancelled"); + else + embed.Description += "\n\n" + customCancelReason; + + for (var i = 0; i < embed.Fields.Count; i++) + { + var oldName = embed.Fields[i].Name; + embed.Fields[i] = new WebhookEmbedField { Name = oldName, Value = Loc.GetString("custom-vote-webhook-option-cancelled"), Inline = true }; + } + + state.Payload.Embeds[0] = embed; + + UpdateWebhookMessage(state, state.Payload, state.MessageId); + } + + // Sends the payload's message. + public async void CreateWebhookMessage(WebhookState state, WebhookPayload payload) + { + try + { + if (await _discord.GetWebhook(state.WebhookUrl) is not { } identifier) + return; + + state.Identifier = identifier.ToIdentifier(); + _sawmill.Debug(JsonSerializer.Serialize(payload)); + + var request = await _discord.CreateMessage(identifier.ToIdentifier(), payload); + var content = await request.Content.ReadAsStringAsync(); + state.MessageId = ulong.Parse(JsonNode.Parse(content)?["id"]!.GetValue()!); + } + catch (Exception e) + { + _sawmill.Error($"Error while sending vote webhook to Discord: {e}"); + } + } + + // Edits a pre-existing payload message, given an ID + public async void UpdateWebhookMessage(WebhookState state, WebhookPayload payload, ulong id) + { + if (state.MessageId == 0) + { + _sawmill.Warning("Failed to deliver update to custom vote webhook: message ID was zero. This likely indicates a previous connection error sending the original message."); + return; + } + + DebugTools.Assert(state.Identifier != default); + + try + { + await _discord.EditMessage(state.Identifier, id, payload); + } + catch (Exception e) + { + _sawmill.Error($"Error while updating vote webhook on Discord: {e}"); + } + } + + public sealed class WebhookState + { + public required string WebhookUrl; + public required WebhookPayload Payload; + public WebhookIdentifier Identifier; + public ulong MessageId; + } + + void IPostInjectInit.PostInject() { } +} diff --git a/Content.Server/Doors/WireActions/DoorBoltWireAction.cs b/Content.Server/Doors/WireActions/DoorBoltWireAction.cs index fc1cf50cd8..80555f68f9 100644 --- a/Content.Server/Doors/WireActions/DoorBoltWireAction.cs +++ b/Content.Server/Doors/WireActions/DoorBoltWireAction.cs @@ -2,7 +2,6 @@ using Content.Server.Doors.Systems; using Content.Server.Wires; using Content.Shared.Doors; using Content.Shared.Doors.Components; -using Content.Shared.Doors.Systems; using Content.Shared.Wires; namespace Content.Server.Doors; diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index 67e60c9de4..88404c4aa9 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -488,4 +488,15 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem } _audio.PlayPvs(electrified.ShockNoises, targetUid, AudioParams.Default.WithVolume(electrified.ShockVolume)); } + + public void SetElectrifiedWireCut(Entity ent, bool value) + { + if (ent.Comp.IsWireCut == value) + { + return; + } + + ent.Comp.IsWireCut = value; + Dirty(ent); + } } diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs index 29477c16b2..b08b66474b 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs @@ -29,7 +29,7 @@ public sealed partial class ExplosionSystem Dictionary edges = new(); _gridEdges[ev.EntityUid] = edges; - foreach (var tileRef in grid.GetAllTiles()) + foreach (var tileRef in _map.GetAllTiles(ev.EntityUid, grid)) { if (IsEdge(grid, tileRef.GridIndices, out var dir)) edges.Add(tileRef.GridIndices, dir); diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs index 97d52e436a..5f14858f1f 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs @@ -666,6 +666,7 @@ sealed class Explosion private readonly IEntityManager _entMan; private readonly ExplosionSystem _system; + private readonly SharedMapSystem _mapSystem; public readonly EntityUid VisualEnt; @@ -688,11 +689,13 @@ sealed class Explosion IEntityManager entMan, IMapManager mapMan, EntityUid visualEnt, - EntityUid? cause) + EntityUid? cause, + SharedMapSystem mapSystem) { VisualEnt = visualEnt; Cause = cause; _system = system; + _mapSystem = mapSystem; ExplosionType = explosionType; _tileSetIntensity = tileSetIntensity; Epicenter = epicenter; @@ -899,7 +902,7 @@ sealed class Explosion { if (list.Count > 0 && _entMan.EntityExists(grid.Owner)) { - grid.SetTiles(list); + _mapSystem.SetTiles(grid.Owner, grid, list); } } _tileUpdateDict.Clear(); diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index 818953ed4b..42b66b5479 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -389,7 +389,8 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem EntityManager, _mapManager, visualEnt, - queued.Cause); + queued.Cause, + _map); } private void CameraShake(float range, MapCoordinates epicenter, float totalIntensity) diff --git a/Content.Server/Fluids/EntitySystems/DrainSystem.cs b/Content.Server/Fluids/EntitySystems/DrainSystem.cs index 091f384986..69d15b8d00 100644 --- a/Content.Server/Fluids/EntitySystems/DrainSystem.cs +++ b/Content.Server/Fluids/EntitySystems/DrainSystem.cs @@ -1,5 +1,4 @@ using Content.Server.DoAfter; -using Content.Server.Fluids.Components; using Content.Server.Popups; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Audio; @@ -13,9 +12,7 @@ using Content.Shared.Fluids.Components; using Content.Shared.Interaction; using Content.Shared.Tag; using Content.Shared.Verbs; -using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; -using Robust.Shared.Collections; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; @@ -32,19 +29,27 @@ public sealed class DrainSystem : SharedDrainSystem [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly PuddleSystem _puddleSystem = default!; - [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + private readonly HashSet> _puddles = new(); + public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnDrainMapInit); SubscribeLocalEvent>(AddEmptyVerb); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnInteract); SubscribeLocalEvent(OnDoAfter); } + private void OnDrainMapInit(Entity ent, ref MapInitEvent args) + { + // Randomise puddle drains so roundstart ones don't all dump at the same time. + ent.Comp.Accumulator = _random.NextFloat(ent.Comp.DrainFrequency); + } + private void AddEmptyVerb(Entity entity, ref GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract || args.Using == null) @@ -118,9 +123,6 @@ public sealed class DrainSystem : SharedDrainSystem { base.Update(frameTime); var managerQuery = GetEntityQuery(); - var xformQuery = GetEntityQuery(); - var puddleQuery = GetEntityQuery(); - var puddles = new ValueList<(Entity Entity, string Solution)>(); var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var drain)) @@ -158,22 +160,10 @@ public sealed class DrainSystem : SharedDrainSystem // This will ensure that UnitsPerSecond is per second... var amount = drain.UnitsPerSecond * drain.DrainFrequency; - if (!xformQuery.TryGetComponent(uid, out var xform)) - continue; + _puddles.Clear(); + _lookup.GetEntitiesInRange(Transform(uid).Coordinates, drain.Range, _puddles); - puddles.Clear(); - - foreach (var entity in _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(uid, xform), drain.Range)) - { - // No InRangeUnobstructed because there's no collision group that fits right now - // and these are placed by mappers and not buildable/movable so shouldnt really be a problem... - if (puddleQuery.TryGetComponent(entity, out var puddle)) - { - puddles.Add(((entity, puddle), puddle.SolutionName)); - } - } - - if (puddles.Count == 0) + if (_puddles.Count == 0) { _ambientSoundSystem.SetAmbience(uid, false); continue; @@ -181,13 +171,13 @@ public sealed class DrainSystem : SharedDrainSystem _ambientSoundSystem.SetAmbience(uid, true); - amount /= puddles.Count; + amount /= _puddles.Count; - foreach (var (puddle, solution) in puddles) + foreach (var puddle in _puddles) { // Queue the solution deletion if it's empty. EvaporationSystem might also do this // but queuedelete should be pretty safe. - if (!_solutionContainerSystem.ResolveSolution(puddle.Owner, solution, ref puddle.Comp.Solution, out var puddleSolution)) + if (!_solutionContainerSystem.ResolveSolution(puddle.Owner, puddle.Comp.SolutionName, ref puddle.Comp.Solution, out var puddleSolution)) { EntityManager.QueueDeleteEntity(puddle); continue; diff --git a/Content.Server/Fluids/EntitySystems/SpraySystem.cs b/Content.Server/Fluids/EntitySystems/SpraySystem.cs index fe179be402..a1f195bf43 100644 --- a/Content.Server/Fluids/EntitySystems/SpraySystem.cs +++ b/Content.Server/Fluids/EntitySystems/SpraySystem.cs @@ -14,6 +14,7 @@ using Robust.Shared.Audio.Systems; using Robust.Shared.Physics.Components; using Robust.Shared.Prototypes; using System.Numerics; +using Robust.Shared.Map; namespace Content.Server.Fluids.EntitySystems; @@ -35,6 +36,19 @@ public sealed class SpraySystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnActivateInWorld); + } + + private void OnActivateInWorld(Entity entity, ref UserActivateInWorldEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + + var targetMapPos = _transform.GetMapCoordinates(GetEntityQuery().GetComponent(args.Target)); + + Spray(entity, args.User, targetMapPos); } private void OnAfterInteract(Entity entity, ref AfterInteractEvent args) @@ -44,29 +58,36 @@ public sealed class SpraySystem : EntitySystem args.Handled = true; + var clickPos = _transform.ToMapCoordinates(args.ClickLocation); + + Spray(entity, args.User, clickPos); + } + + public void Spray(Entity entity, EntityUid user, MapCoordinates mapcoord) + { if (!_solutionContainer.TryGetSolution(entity.Owner, SprayComponent.SolutionName, out var soln, out var solution)) return; - var ev = new SprayAttemptEvent(args.User); + var ev = new SprayAttemptEvent(user); RaiseLocalEvent(entity, ref ev); if (ev.Cancelled) return; - if (!TryComp(entity, out var useDelay) - || _useDelay.IsDelayed((entity, useDelay))) + if (TryComp(entity, out var useDelay) + && _useDelay.IsDelayed((entity, useDelay))) return; if (solution.Volume <= 0) { - _popupSystem.PopupEntity(Loc.GetString("spray-component-is-empty-message"), entity.Owner, args.User); + _popupSystem.PopupEntity(Loc.GetString("spray-component-is-empty-message"), entity.Owner, user); return; } var xformQuery = GetEntityQuery(); - var userXform = xformQuery.GetComponent(args.User); + var userXform = xformQuery.GetComponent(user); var userMapPos = _transform.GetMapCoordinates(userXform); - var clickMapPos = args.ClickLocation.ToMap(EntityManager, _transform); + var clickMapPos = mapcoord; var diffPos = clickMapPos.Position - userMapPos.Position; if (diffPos == Vector2.Zero || diffPos == Vector2Helpers.NaN) @@ -88,8 +109,6 @@ public sealed class SpraySystem : EntitySystem var amount = Math.Max(Math.Min((solution.Volume / entity.Comp.TransferAmount).Int(), entity.Comp.VaporAmount), 1); var spread = entity.Comp.VaporSpread / amount; - // TODO: Just use usedelay homie. - var cooldownTime = 0f; for (var i = 0; i < amount; i++) { @@ -131,20 +150,19 @@ public sealed class SpraySystem : EntitySystem // impulse direction is defined in world-coordinates, not local coordinates var impulseDirection = rotation.ToVec(); var time = diffLength / entity.Comp.SprayVelocity; - cooldownTime = MathF.Max(time, cooldownTime); - _vapor.Start(ent, vaporXform, impulseDirection * diffLength, entity.Comp.SprayVelocity, target, time, args.User); + _vapor.Start(ent, vaporXform, impulseDirection * diffLength, entity.Comp.SprayVelocity, target, time, user); - if (TryComp(args.User, out var body)) + if (TryComp(user, out var body)) { - if (_gravity.IsWeightless(args.User, body)) - _physics.ApplyLinearImpulse(args.User, -impulseDirection.Normalized() * entity.Comp.PushbackAmount, body: body); + if (_gravity.IsWeightless(user, body)) + _physics.ApplyLinearImpulse(user, -impulseDirection.Normalized() * entity.Comp.PushbackAmount, body: body); } } _audio.PlayPvs(entity.Comp.SpraySound, entity, entity.Comp.SpraySound.Params.WithVariation(0.125f)); - _useDelay.SetLength(entity.Owner, TimeSpan.FromSeconds(cooldownTime)); - _useDelay.TryResetDelay((entity, useDelay)); + if (useDelay != null) + _useDelay.TryResetDelay((entity, useDelay)); } } diff --git a/Content.Server/GameTicking/GameTicker.CVars.cs b/Content.Server/GameTicking/GameTicker.CVars.cs index 60ffa660f5..bb68459740 100644 --- a/Content.Server/GameTicking/GameTicker.CVars.cs +++ b/Content.Server/GameTicking/GameTicker.CVars.cs @@ -36,7 +36,7 @@ namespace Content.Server.GameTicking private void InitializeCVars() { - Subs.CVar(_configurationManager, CCVars.GameLobbyEnabled, value => + Subs.CVar(_cfg, CCVars.GameLobbyEnabled, value => { LobbyEnabled = value; foreach (var (userId, status) in _playerGameStatuses) @@ -47,23 +47,23 @@ namespace Content.Server.GameTicking LobbyEnabled ? PlayerGameStatus.NotReadyToPlay : PlayerGameStatus.ReadyToPlay; } }, true); - Subs.CVar(_configurationManager, CCVars.GameDummyTicker, value => DummyTicker = value, true); - Subs.CVar(_configurationManager, CCVars.GameLobbyDuration, value => LobbyDuration = TimeSpan.FromSeconds(value), true); - Subs.CVar(_configurationManager, CCVars.GameDisallowLateJoins, + Subs.CVar(_cfg, CCVars.GameDummyTicker, value => DummyTicker = value, true); + Subs.CVar(_cfg, CCVars.GameLobbyDuration, value => LobbyDuration = TimeSpan.FromSeconds(value), true); + Subs.CVar(_cfg, CCVars.GameDisallowLateJoins, value => { DisallowLateJoin = value; UpdateLateJoinStatus(); }, true); - Subs.CVar(_configurationManager, CCVars.AdminLogsServerName, value => + Subs.CVar(_cfg, CCVars.AdminLogsServerName, value => { // TODO why tf is the server name on admin logs ServerName = value; }, true); - Subs.CVar(_configurationManager, CCVars.DiscordRoundUpdateWebhook, value => + Subs.CVar(_cfg, CCVars.DiscordRoundUpdateWebhook, value => { if (!string.IsNullOrWhiteSpace(value)) { _discord.GetWebhook(value, data => _webhookIdentifier = data.ToIdentifier()); } }, true); - Subs.CVar(_configurationManager, CCVars.DiscordRoundEndRoleWebhook, value => + Subs.CVar(_cfg, CCVars.DiscordRoundEndRoleWebhook, value => { DiscordRoundEndRole = value; @@ -72,9 +72,9 @@ namespace Content.Server.GameTicking DiscordRoundEndRole = null; } }, true); - Subs.CVar(_configurationManager, CCVars.RoundEndSoundCollection, value => RoundEndSoundCollection = value, true); + Subs.CVar(_cfg, CCVars.RoundEndSoundCollection, value => RoundEndSoundCollection = value, true); #if EXCEPTION_TOLERANCE - Subs.CVar(_configurationManager, CCVars.RoundStartFailShutdownCount, value => RoundStartFailShutdownCount = value, true); + Subs.CVar(_cfg, CCVars.RoundStartFailShutdownCount, value => RoundStartFailShutdownCount = value, true); #endif } } diff --git a/Content.Server/GameTicking/GameTicker.GamePreset.cs b/Content.Server/GameTicking/GameTicker.GamePreset.cs index 5642e84f90..d1a8b062c4 100644 --- a/Content.Server/GameTicking/GameTicker.GamePreset.cs +++ b/Content.Server/GameTicking/GameTicker.GamePreset.cs @@ -41,9 +41,9 @@ namespace Content.Server.GameTicking DelayStart(TimeSpan.FromSeconds(PresetFailedCooldownIncrease)); } - if (_configurationManager.GetCVar(CCVars.GameLobbyFallbackEnabled)) + if (_cfg.GetCVar(CCVars.GameLobbyFallbackEnabled)) { - var fallbackPresets = _configurationManager.GetCVar(CCVars.GameLobbyFallbackPreset).Split(","); + var fallbackPresets = _cfg.GetCVar(CCVars.GameLobbyFallbackPreset).Split(","); var startFailed = true; foreach (var preset in fallbackPresets) @@ -86,7 +86,7 @@ namespace Content.Server.GameTicking private void InitializeGamePreset() { - SetGamePreset(LobbyEnabled ? _configurationManager.GetCVar(CCVars.GameLobbyDefaultPreset) : "sandbox"); + SetGamePreset(LobbyEnabled ? _cfg.GetCVar(CCVars.GameLobbyDefaultPreset) : "sandbox"); } public void SetGamePreset(GamePresetPrototype? preset, bool force = false) @@ -190,7 +190,7 @@ namespace Content.Server.GameTicking private void IncrementRoundNumber() { var playerIds = _playerGameStatuses.Keys.Select(player => player.UserId).ToArray(); - var serverName = _configurationManager.GetCVar(CCVars.AdminLogsServerName); + var serverName = _cfg.GetCVar(CCVars.AdminLogsServerName); // TODO FIXME AAAAAAAAAAAAAAAAAAAH THIS IS BROKEN // Task.Run as a terrible dirty workaround to avoid synchronization context deadlock from .Result here. diff --git a/Content.Server/GameTicking/GameTicker.Player.cs b/Content.Server/GameTicking/GameTicker.Player.cs index 61cdf6f855..f376408130 100644 --- a/Content.Server/GameTicking/GameTicker.Player.cs +++ b/Content.Server/GameTicking/GameTicker.Player.cs @@ -1,5 +1,3 @@ -using System.Linq; -using Content.Server.Database; using Content.Shared.Administration; using Content.Shared.CCVar; using Content.Shared.GameTicking; @@ -9,7 +7,6 @@ using Content.Shared.Preferences; using JetBrains.Annotations; using Robust.Server.Player; using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; using Robust.Shared.Enums; using Robust.Shared.Player; using Robust.Shared.Timing; @@ -21,8 +18,6 @@ namespace Content.Server.GameTicking public sealed partial class GameTicker { [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly IServerDbManager _dbManager = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; private void InitializePlayer() { @@ -64,7 +59,7 @@ namespace Content.Server.GameTicking // timer time must be > tick length Timer.Spawn(0, () => _playerManager.JoinGame(args.Session)); - var record = await _dbManager.GetPlayerRecordByUserId(args.Session.UserId); + var record = await _db.GetPlayerRecordByUserId(args.Session.UserId); var firstConnection = record != null && Math.Abs((record.FirstSeenTime - record.LastSeenTime).TotalMinutes) < 1; @@ -74,8 +69,8 @@ namespace Content.Server.GameTicking RaiseNetworkEvent(GetConnectionStatusMsg(), session.Channel); - if (firstConnection && _configurationManager.GetCVar(CCVars.AdminNewPlayerJoinSound)) - _audioSystem.PlayGlobal(new SoundPathSpecifier("/Audio/Effects/newplayerping.ogg"), + if (firstConnection && _cfg.GetCVar(CCVars.AdminNewPlayerJoinSound)) + _audio.PlayGlobal(new SoundPathSpecifier("/Audio/Effects/newplayerping.ogg"), Filter.Empty().AddPlayers(_adminManager.ActiveAdmins), false, audioParams: new AudioParams { Volume = -5f }); diff --git a/Content.Server/GameTicking/GameTicker.Replays.cs b/Content.Server/GameTicking/GameTicker.Replays.cs index 9109fbf96a..7efa52bd00 100644 --- a/Content.Server/GameTicking/GameTicker.Replays.cs +++ b/Content.Server/GameTicking/GameTicker.Replays.cs @@ -127,8 +127,8 @@ public sealed partial class GameTicker metadata["gamemode"] = new ValueDataNode(CurrentPreset != null ? Loc.GetString(CurrentPreset.ModeTitle) : string.Empty); metadata["roundEndPlayers"] = _serialman.WriteValue(_replayRoundPlayerInfo); metadata["roundEndText"] = new ValueDataNode(_replayRoundText); - metadata["server_id"] = new ValueDataNode(_configurationManager.GetCVar(CCVars.ServerId)); - metadata["server_name"] = new ValueDataNode(_configurationManager.GetCVar(CCVars.AdminLogsServerName)); + metadata["server_id"] = new ValueDataNode(_cfg.GetCVar(CCVars.ServerId)); + metadata["server_name"] = new ValueDataNode(_cfg.GetCVar(CCVars.AdminLogsServerName)); metadata["roundId"] = new ValueDataNode(RoundId.ToString()); } diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index ca087c46ed..0c7668d354 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -364,7 +364,7 @@ namespace Content.Server.GameTicking var listOfPlayerInfo = new List(); // Grab the great big book of all the Minds, we'll need them for this. var allMinds = EntityQueryEnumerator(); - var pvsOverride = _configurationManager.GetCVar(CCVars.RoundEndPVSOverrides); + var pvsOverride = _cfg.GetCVar(CCVars.RoundEndPVSOverrides); while (allMinds.MoveNext(out var mindId, out var mind)) { // TODO don't list redundant observer roles? diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index fa1e01c7a8..f8ba7e1d7d 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -8,19 +8,15 @@ using Content.Server.Maps; using Content.Server.Players.PlayTimeTracking; using Content.Server.Preferences.Managers; using Content.Server.ServerUpdates; -using Content.Server.Shuttles.Systems; using Content.Server.Station.Systems; using Content.Shared.Chat; -using Content.Shared.Damage; using Content.Shared.GameTicking; using Content.Shared.Mind; -using Content.Shared.Mobs.Systems; using Content.Shared.Roles; using Robust.Server; using Robust.Server.GameObjects; using Robust.Server.GameStates; using Robust.Shared.Audio.Systems; -using Robust.Shared.Configuration; using Robust.Shared.Console; using Robust.Shared.Map; using Robust.Shared.Prototypes; @@ -39,7 +35,6 @@ namespace Content.Server.GameTicking [Dependency] private readonly IBanManager _banManager = default!; [Dependency] private readonly IBaseServer _baseServer = default!; [Dependency] private readonly IChatManager _chatManager = default!; - [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly IConsoleHost _consoleHost = default!; [Dependency] private readonly IGameMapManager _gameMapManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 945b0ff998..85fec0d7d1 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -46,11 +46,9 @@ namespace Content.Server.Ghost [Dependency] private readonly JobSystem _jobs = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly MindSystem _minds = default!; - [Dependency] private readonly SharedMindSystem _mindSystem = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly GameTicker _ticker = default!; [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly VisibilitySystem _visibilitySystem = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; @@ -170,7 +168,7 @@ namespace Content.Server.Ghost // Allow this entity to be seen by other ghosts. var visibility = EnsureComp(uid); - if (_ticker.RunLevel != GameRunLevel.PostRound) + if (_gameTicker.RunLevel != GameRunLevel.PostRound) { _visibilitySystem.AddLayer((uid, visibility), (int) VisibilityFlags.Ghost, false); _visibilitySystem.RemoveLayer((uid, visibility), (int) VisibilityFlags.Normal, false); @@ -277,7 +275,7 @@ namespace Content.Server.Ghost return; } - _mindSystem.UnVisit(actor.PlayerSession); + _mind.UnVisit(actor.PlayerSession); } #region Warp @@ -455,7 +453,7 @@ namespace Content.Server.Ghost spawnPosition = null; // If it's bad, look for a valid point to spawn - spawnPosition ??= _ticker.GetObserverSpawnPoint(); + spawnPosition ??= _gameTicker.GetObserverSpawnPoint(); // Make sure the new point is valid too if (!IsValidSpawnPosition(spawnPosition)) diff --git a/Content.Server/ImmovableRod/ImmovableRodSystem.cs b/Content.Server/ImmovableRod/ImmovableRodSystem.cs index f9873b0d6a..0d3d69c1bc 100644 --- a/Content.Server/ImmovableRod/ImmovableRodSystem.cs +++ b/Content.Server/ImmovableRod/ImmovableRodSystem.cs @@ -25,6 +25,7 @@ public sealed class ImmovableRodSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedMapSystem _map = default!; public override void Update(float frameTime) { @@ -39,7 +40,7 @@ public sealed class ImmovableRodSystem : EntitySystem if (!TryComp(trans.GridUid, out var grid)) continue; - grid.SetTile(trans.Coordinates, Tile.Empty); + _map.SetTile(trans.GridUid.Value, grid, trans.Coordinates, Tile.Empty); } } diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index 7c69ec8ea5..6e277dd29f 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -74,14 +74,12 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem return; // same as store code, but message is only shown to yourself - args.Handled = _store.TryAddCurrency(_store.GetCurrencyValue(args.Used, currency), uid, store); - - if (!args.Handled) + if (!_store.TryAddCurrency((args.Used, currency), (uid, store))) return; + args.Handled = true; var msg = Loc.GetString("store-currency-inserted-implant", ("used", args.Used)); _popup.PopupEntity(msg, args.User, args.User); - QueueDel(args.Used); } private void OnFreedomImplant(EntityUid uid, SubdermalImplantComponent component, UseFreedomImplantEvent args) diff --git a/Content.Server/IoC/ServerContentIoC.cs b/Content.Server/IoC/ServerContentIoC.cs index 3851f145c4..902e16d531 100644 --- a/Content.Server/IoC/ServerContentIoC.cs +++ b/Content.Server/IoC/ServerContentIoC.cs @@ -7,6 +7,7 @@ using Content.Server.Chat.Managers; using Content.Server.Connection; using Content.Server.Database; using Content.Server.Discord; +using Content.Server.Discord.WebhookMessages; using Content.Server.EUI; using Content.Server.GhostKick; using Content.Server.Info; @@ -14,8 +15,6 @@ using Content.Server.Mapping; using Content.Server.Maps; using Content.Server.MoMMI; using Content.Server.NodeContainer.NodeGroups; -using Content.Server.Objectives; -using Content.Server.Players; using Content.Server.Players.JobWhitelist; using Content.Server.Players.PlayTimeTracking; using Content.Server.Players.RateLimiting; @@ -26,8 +25,10 @@ using Content.Server.Voting.Managers; using Content.Server.Worldgen.Tools; using Content.Shared.Administration.Logs; using Content.Shared.Administration.Managers; +using Content.Shared.Chat; using Content.Shared.Kitchen; using Content.Shared.Players.PlayTimeTracking; +using Content.Shared.Players.RateLimiting; namespace Content.Server.IoC { @@ -36,6 +37,7 @@ namespace Content.Server.IoC public static void Register() { IoCManager.Register(); + IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); @@ -63,11 +65,13 @@ namespace Content.Server.IoC IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); IoCManager.Register(); } } diff --git a/Content.Server/MassMedia/Systems/NewsSystem.cs b/Content.Server/MassMedia/Systems/NewsSystem.cs index 3ac4ecc37b..641f2074b8 100644 --- a/Content.Server/MassMedia/Systems/NewsSystem.cs +++ b/Content.Server/MassMedia/Systems/NewsSystem.cs @@ -4,7 +4,6 @@ using Content.Server.CartridgeLoader; using Content.Server.CartridgeLoader.Cartridges; using Content.Server.Chat.Managers; using Content.Server.GameTicking; -using Content.Server.Interaction; using Content.Server.MassMedia.Components; using Content.Server.Popups; using Content.Server.Station.Systems; @@ -27,7 +26,6 @@ public sealed class NewsSystem : SharedNewsSystem { [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly InteractionSystem _interaction = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly CartridgeLoaderSystem _cartridgeLoaderSystem = default!; @@ -35,7 +33,6 @@ public sealed class NewsSystem : SharedNewsSystem [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly GameTicker _ticker = default!; - [Dependency] private readonly AccessReaderSystem _accessReader = default!; [Dependency] private readonly IChatManager _chatManager = default!; public override void Initialize() diff --git a/Content.Server/Medical/Components/HealthAnalyzerComponent.cs b/Content.Server/Medical/Components/HealthAnalyzerComponent.cs index 85956b6029..34b7af0212 100644 --- a/Content.Server/Medical/Components/HealthAnalyzerComponent.cs +++ b/Content.Server/Medical/Components/HealthAnalyzerComponent.cs @@ -54,7 +54,7 @@ public sealed partial class HealthAnalyzerComponent : Component /// Sound played on scanning end /// [DataField] - public SoundSpecifier? ScanningEndSound; + public SoundSpecifier ScanningEndSound = new SoundPathSpecifier("/Audio/Items/Medical/healthscanner.ogg"); /// /// Whether to show up the popup diff --git a/Content.Server/Medical/CryoPodSystem.cs b/Content.Server/Medical/CryoPodSystem.cs index a1c1f1f9ce..fc9ab081d2 100644 --- a/Content.Server/Medical/CryoPodSystem.cs +++ b/Content.Server/Medical/CryoPodSystem.cs @@ -1,16 +1,13 @@ using Content.Server.Administration.Logs; -using Content.Server.Atmos; using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.Piping.Components; using Content.Server.Atmos.Piping.Unary.EntitySystems; using Content.Server.Body.Components; using Content.Server.Body.Systems; using Content.Server.Medical.Components; -using Content.Server.NodeContainer; using Content.Server.NodeContainer.EntitySystems; using Content.Server.NodeContainer.NodeGroups; using Content.Server.NodeContainer.Nodes; -using Content.Server.Power.Components; using Content.Server.Temperature.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Atmos; @@ -18,7 +15,6 @@ using Content.Shared.UserInterface; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; -using Content.Shared.Chemistry.Reagent; using Content.Shared.Climbing.Systems; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; @@ -46,7 +42,6 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; - [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly SharedToolSystem _toolSystem = default!; @@ -196,7 +191,7 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem } // TODO: This should be a state my dude - _userInterfaceSystem.ServerSendUiMessage( + _uiSystem.ServerSendUiMessage( entity.Owner, HealthAnalyzerUiKey.Key, new HealthAnalyzerScannedUserMessage(GetNetEntity(entity.Comp.BodyContainer.ContainedEntity), diff --git a/Content.Server/Medical/HealthAnalyzerSystem.cs b/Content.Server/Medical/HealthAnalyzerSystem.cs index 82f8075902..60a492a755 100644 --- a/Content.Server/Medical/HealthAnalyzerSystem.cs +++ b/Content.Server/Medical/HealthAnalyzerSystem.cs @@ -13,11 +13,9 @@ using Content.Shared.Item.ItemToggle.Components; using Content.Shared.MedicalScanner; using Content.Shared.Mobs.Components; using Content.Shared.Popups; -using Content.Shared.PowerCell; using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; -using Robust.Shared.Player; using Robust.Shared.Timing; namespace Content.Server.Medical; @@ -104,7 +102,8 @@ public sealed class HealthAnalyzerSystem : EntitySystem if (args.Handled || args.Cancelled || args.Target == null || !_cell.HasDrawCharge(uid, user: args.User)) return; - _audio.PlayPvs(uid.Comp.ScanningEndSound, uid); + if (!uid.Comp.Silent) + _audio.PlayPvs(uid.Comp.ScanningEndSound, uid); OpenUserInterface(args.User, uid); BeginAnalyzingEntity(uid, args.Target.Value); diff --git a/Content.Server/NPC/Queries/Considerations/TargetOnFireCon.cs b/Content.Server/NPC/Queries/Considerations/TargetOnFireCon.cs new file mode 100644 index 0000000000..d86dbc06ec --- /dev/null +++ b/Content.Server/NPC/Queries/Considerations/TargetOnFireCon.cs @@ -0,0 +1,9 @@ +namespace Content.Server.NPC.Queries.Considerations; + +/// +/// Returns 1f if the target is on fire or 0f if not. +/// +public sealed partial class TargetOnFireCon : UtilityConsideration +{ + +} diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index 8dff93648b..60bc5cdfd8 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -1,3 +1,4 @@ +using Content.Server.Atmos.Components; using Content.Server.Fluids.EntitySystems; using Content.Server.NPC.Queries; using Content.Server.NPC.Queries.Considerations; @@ -351,6 +352,12 @@ public sealed class NPCUtilitySystem : EntitySystem return 0f; } + case TargetOnFireCon: + { + if (TryComp(targetUid, out FlammableComponent? fire) && fire.OnFire) + return 1f; + return 0f; + } default: throw new NotImplementedException(); } diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs index f7650f599b..c3d41cead6 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs @@ -27,7 +27,6 @@ namespace Content.Server.Nutrition.EntitySystems [Dependency] private readonly FoodSystem _foodSystem = default!; [Dependency] private readonly ExplosionSystem _explosionSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; private void InitializeVapes() { @@ -127,7 +126,7 @@ namespace Content.Server.Nutrition.EntitySystems || args.Args.Target == null) return; - var environment = _atmosphereSystem.GetContainingMixture(args.Args.Target.Value, true, true); + var environment = _atmos.GetContainingMixture(args.Args.Target.Value, true, true); if (environment == null) { return; @@ -139,7 +138,7 @@ namespace Content.Server.Nutrition.EntitySystems var merger = new GasMixture(1) { Temperature = args.Solution.Temperature }; merger.SetMoles(entity.Comp.GasType, args.Solution.Volume.Value / entity.Comp.ReductionFactor); - _atmosphereSystem.Merge(environment, merger); + _atmos.Merge(environment, merger); args.Solution.RemoveAllSolution(); diff --git a/Content.Server/Parallax/BiomeSystem.cs b/Content.Server/Parallax/BiomeSystem.cs index 1d7ea2a440..22b531eb7c 100644 --- a/Content.Server/Parallax/BiomeSystem.cs +++ b/Content.Server/Parallax/BiomeSystem.cs @@ -962,7 +962,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem } } - grid.SetTiles(tiles); + _mapSystem.SetTiles(gridUid, grid, tiles); tiles.Clear(); component.LoadedChunks.Remove(chunk); diff --git a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.ControlBox.cs b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.ControlBox.cs index 404eb00fef..90a5cb2ea0 100644 --- a/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.ControlBox.cs +++ b/Content.Server/ParticleAccelerator/EntitySystems/ParticleAcceleratorSystem.ControlBox.cs @@ -9,7 +9,6 @@ using Content.Shared.CCVar; using Content.Shared.Power; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; -using Robust.Shared.Timing; using Robust.Shared.Player; namespace Content.Server.ParticleAccelerator.EntitySystems; @@ -18,7 +17,6 @@ public sealed partial class ParticleAcceleratorSystem { [Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly IGameTiming _timing = default!; private void InitializeControlBoxSystem() { @@ -175,7 +173,7 @@ public sealed partial class ParticleAcceleratorSystem if (strength >= alertMinPowerState) { var pos = Transform(uid); - if (_timing.CurTime > comp.EffectCooldown) + if (_gameTiming.CurTime > comp.EffectCooldown) { _chat.SendAdminAlert(player, Loc.GetString("particle-accelerator-admin-power-strength-warning", @@ -186,7 +184,7 @@ public sealed partial class ParticleAcceleratorSystem Filter.Empty().AddPlayers(_adminManager.ActiveAdmins), false, AudioParams.Default.WithVolume(-8f)); - comp.EffectCooldown = _timing.CurTime + comp.CooldownDuration; + comp.EffectCooldown = _gameTiming.CurTime + comp.CooldownDuration; } } } diff --git a/Content.Server/Physics/Controllers/ChaoticJumpSystem.cs b/Content.Server/Physics/Controllers/ChaoticJumpSystem.cs index ee4b776cf0..3f2493b43f 100644 --- a/Content.Server/Physics/Controllers/ChaoticJumpSystem.cs +++ b/Content.Server/Physics/Controllers/ChaoticJumpSystem.cs @@ -18,7 +18,6 @@ public sealed class ChaoticJumpSystem : VirtualController [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly SharedTransformSystem _xform = default!; public override void Initialize() { @@ -73,6 +72,6 @@ public sealed class ChaoticJumpSystem : VirtualController Spawn(component.Effect, transform.Coordinates); - _xform.SetWorldPosition(uid, targetPos); + _transform.SetWorldPosition(uid, targetPos); } } diff --git a/Content.Server/Players/RateLimiting/PlayerRateLimitManager.cs b/Content.Server/Players/RateLimiting/PlayerRateLimitManager.cs index 59f086f9c3..a3b4d4a536 100644 --- a/Content.Server/Players/RateLimiting/PlayerRateLimitManager.cs +++ b/Content.Server/Players/RateLimiting/PlayerRateLimitManager.cs @@ -1,6 +1,7 @@ using System.Runtime.InteropServices; using Content.Server.Administration.Logs; using Content.Shared.Database; +using Content.Shared.Players.RateLimiting; using Robust.Server.Player; using Robust.Shared.Configuration; using Robust.Shared.Enums; @@ -10,26 +11,7 @@ using Robust.Shared.Utility; namespace Content.Server.Players.RateLimiting; -/// -/// General-purpose system to rate limit actions taken by clients, such as chat messages. -/// -/// -/// -/// Different categories of rate limits must be registered ahead of time by calling . -/// Once registered, you can simply call to count a rate-limited action for a player. -/// -/// -/// This system is intended for rate limiting player actions over short periods, -/// to ward against spam that can cause technical issues such as admin client load. -/// It should not be used for in-game actions or similar. -/// -/// -/// Rate limits are reset when a client reconnects. -/// This should not be an issue for the reasonably short rate limit periods this system is intended for. -/// -/// -/// -public sealed class PlayerRateLimitManager +public sealed class PlayerRateLimitManager : SharedPlayerRateLimitManager { [Dependency] private readonly IAdminLogManager _adminLog = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; @@ -39,18 +21,7 @@ public sealed class PlayerRateLimitManager private readonly Dictionary _registrations = new(); private readonly Dictionary> _rateLimitData = new(); - /// - /// Count and validate an action performed by a player against rate limits. - /// - /// The player performing the action. - /// The key string that was previously used to register a rate limit category. - /// Whether the action counted should be blocked due to surpassing rate limits or not. - /// - /// is not a connected player - /// OR is not a registered rate limit category. - /// - /// - public RateLimitStatus CountAction(ICommonSession player, string key) + public override RateLimitStatus CountAction(ICommonSession player, string key) { if (player.Status == SessionStatus.Disconnected) throw new ArgumentException("Player is not connected"); @@ -74,7 +45,8 @@ public sealed class PlayerRateLimitManager return RateLimitStatus.Allowed; // Breached rate limits, inform admins if configured. - if (registration.AdminAnnounceDelay is { } cvarAnnounceDelay) + // Negative delays can be used to disable admin announcements. + if (registration.AdminAnnounceDelay is {TotalSeconds: >= 0} cvarAnnounceDelay) { if (datum.NextAdminAnnounce < time) { @@ -85,7 +57,7 @@ public sealed class PlayerRateLimitManager if (!datum.Announced) { - registration.Registration.PlayerLimitedAction(player); + registration.Registration.PlayerLimitedAction?.Invoke(player); _adminLog.Add( registration.Registration.AdminLogType, LogImpact.Medium, @@ -97,17 +69,7 @@ public sealed class PlayerRateLimitManager return RateLimitStatus.Blocked; } - /// - /// Register a new rate limit category. - /// - /// - /// The key string that will be referred to later with . - /// Must be unique and should probably just be a constant somewhere. - /// - /// The data specifying the rate limit's parameters. - /// has already been registered. - /// is invalid. - public void Register(string key, RateLimitRegistration registration) + public override void Register(string key, RateLimitRegistration registration) { if (_registrations.ContainsKey(key)) throw new InvalidOperationException($"Key already registered: {key}"); @@ -135,7 +97,7 @@ public sealed class PlayerRateLimitManager if (registration.CVarAdminAnnounceDelay != null) { _cfg.OnValueChanged( - registration.CVarLimitCount, + registration.CVarAdminAnnounceDelay, i => data.AdminAnnounceDelay = TimeSpan.FromSeconds(i), invokeImmediately: true); } @@ -143,10 +105,7 @@ public sealed class PlayerRateLimitManager _registrations.Add(key, data); } - /// - /// Initialize the manager's functionality at game startup. - /// - public void Initialize() + public override void Initialize() { _playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged; } @@ -189,66 +148,3 @@ public sealed class PlayerRateLimitManager public TimeSpan NextAdminAnnounce; } } - -/// -/// Contains all data necessary to register a rate limit with . -/// -public sealed class RateLimitRegistration -{ - /// - /// CVar that controls the period over which the rate limit is counted, measured in seconds. - /// - public required CVarDef CVarLimitPeriodLength { get; init; } - - /// - /// CVar that controls how many actions are allowed in a single rate limit period. - /// - public required CVarDef CVarLimitCount { get; init; } - - /// - /// An action that gets invoked when this rate limit has been breached by a player. - /// - /// - /// This can be used for informing players or taking administrative action. - /// - public required Action PlayerLimitedAction { get; init; } - - /// - /// CVar that controls the minimum delay between admin notifications, measured in seconds. - /// This can be omitted to have no admin notification system. - /// - /// - /// If set, must be set too. - /// - public CVarDef? CVarAdminAnnounceDelay { get; init; } - - /// - /// An action that gets invoked when a rate limit was breached and admins should be notified. - /// - /// - /// If set, must be set too. - /// - public Action? AdminAnnounceAction { get; init; } - - /// - /// Log type used to log rate limit violations to the admin logs system. - /// - public LogType AdminLogType { get; init; } = LogType.RateLimited; -} - -/// -/// Result of a rate-limited operation. -/// -/// -public enum RateLimitStatus : byte -{ - /// - /// The action was not blocked by the rate limit. - /// - Allowed, - - /// - /// The action was blocked by the rate limit. - /// - Blocked, -} diff --git a/Content.Server/Pointing/EntitySystems/PointingSystem.cs b/Content.Server/Pointing/EntitySystems/PointingSystem.cs index d5ad1fb123..23c70d78f4 100644 --- a/Content.Server/Pointing/EntitySystems/PointingSystem.cs +++ b/Content.Server/Pointing/EntitySystems/PointingSystem.cs @@ -40,6 +40,7 @@ namespace Content.Server.Pointing.EntitySystems [Dependency] private readonly VisibilitySystem _visibilitySystem = default!; [Dependency] private readonly SharedMindSystem _minds = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly ExamineSystemShared _examine = default!; @@ -73,8 +74,13 @@ namespace Content.Server.Pointing.EntitySystems } // TODO: FOV - private void SendMessage(EntityUid source, IEnumerable viewers, EntityUid pointed, string selfMessage, - string viewerMessage, string? viewerPointedAtMessage = null) + private void SendMessage( + EntityUid source, + IEnumerable viewers, + EntityUid pointed, + string selfMessage, + string viewerMessage, + string? viewerPointedAtMessage = null) { var netSource = GetNetEntity(source); @@ -226,8 +232,8 @@ namespace Content.Server.Pointing.EntitySystems if (_mapManager.TryFindGridAt(mapCoordsPointed, out var gridUid, out var grid)) { - position = $"EntId={gridUid} {grid.WorldToTile(mapCoordsPointed.Position)}"; - tileRef = grid.GetTileRef(grid.WorldToTile(mapCoordsPointed.Position)); + position = $"EntId={gridUid} {_map.WorldToTile(gridUid, grid, mapCoordsPointed.Position)}"; + tileRef = _map.GetTileRef(gridUid, grid, _map.WorldToTile(gridUid, grid, mapCoordsPointed.Position)); } var tileDef = _tileDefinitionManager[tileRef?.Tile.TypeId ?? 0]; diff --git a/Content.Server/Power/EntitySystems/CableSystem.Placer.cs b/Content.Server/Power/EntitySystems/CableSystem.Placer.cs index 1160f15e18..05f092b59a 100644 --- a/Content.Server/Power/EntitySystems/CableSystem.Placer.cs +++ b/Content.Server/Power/EntitySystems/CableSystem.Placer.cs @@ -11,6 +11,8 @@ namespace Content.Server.Power.EntitySystems; public sealed partial class CableSystem { [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedMapSystem _map = default!; private void InitializeCablePlacer() { @@ -26,11 +28,12 @@ public sealed partial class CableSystem if (component.CablePrototypeId == null) return; - if(!TryComp(args.ClickLocation.GetGridUid(EntityManager), out var grid)) + if(!TryComp(_transform.GetGrid(args.ClickLocation), out var grid)) return; + var gridUid = _transform.GetGrid(args.ClickLocation)!.Value; var snapPos = grid.TileIndicesFor(args.ClickLocation); - var tileDef = (ContentTileDefinition) _tileManager[grid.GetTileRef(snapPos).Tile.TypeId]; + var tileDef = (ContentTileDefinition) _tileManager[_map.GetTileRef(gridUid, grid,snapPos).Tile.TypeId]; if ((!tileDef.IsSubFloor || !tileDef.Sturdy) && placer.Comp.CP14OnlySubfloor) //CP14 if only subfloor return; diff --git a/Content.Server/Power/EntitySystems/CableSystem.cs b/Content.Server/Power/EntitySystems/CableSystem.cs index 62eb08d7cb..d0f45b54fc 100644 --- a/Content.Server/Power/EntitySystems/CableSystem.cs +++ b/Content.Server/Power/EntitySystems/CableSystem.cs @@ -17,7 +17,6 @@ public sealed partial class CableSystem : EntitySystem [Dependency] private readonly SharedToolSystem _toolSystem = default!; [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly ElectrocutionSystem _electrocutionSystem = default!; - [Dependency] private readonly IAdminLogManager _adminLogs = default!; public override void Initialize() { @@ -51,7 +50,7 @@ public sealed partial class CableSystem : EntitySystem if (_electrocutionSystem.TryDoElectrifiedAct(uid, args.User)) return; - _adminLogs.Add(LogType.CableCut, LogImpact.Medium, $"The {ToPrettyString(uid)} at {xform.Coordinates} was cut by {ToPrettyString(args.User)}."); + _adminLogger.Add(LogType.CableCut, LogImpact.Medium, $"The {ToPrettyString(uid)} at {xform.Coordinates} was cut by {ToPrettyString(args.User)}."); Spawn(cable.CableDroppedOnCutPrototype, xform.Coordinates); QueueDel(uid); diff --git a/Content.Server/Power/PowerWireAction.cs b/Content.Server/Power/PowerWireAction.cs index ac34966036..cebb7de8ec 100644 --- a/Content.Server/Power/PowerWireAction.cs +++ b/Content.Server/Power/PowerWireAction.cs @@ -1,4 +1,5 @@ using Content.Server.Electrocution; +using Content.Shared.Electrocution; using Content.Server.Power.Components; using Content.Server.Wires; using Content.Shared.Power; @@ -104,6 +105,7 @@ public sealed partial class PowerWireAction : BaseWireAction && !EntityManager.TryGetComponent(used, out electrified)) return; + _electrocutionSystem.SetElectrifiedWireCut((used, electrified), setting); electrified.Enabled = setting; } diff --git a/Content.Server/Procedural/DungeonSystem.Commands.cs b/Content.Server/Procedural/DungeonSystem.Commands.cs index 51a6a57bbe..09e881686e 100644 --- a/Content.Server/Procedural/DungeonSystem.Commands.cs +++ b/Content.Server/Procedural/DungeonSystem.Commands.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Content.Server.Administration; using Content.Shared.Administration; using Content.Shared.Procedural; -using Content.Shared.Procedural.DungeonGenerators; using Robust.Shared.Console; using Robust.Shared.Map; using Robust.Shared.Map.Components; @@ -44,7 +42,7 @@ public sealed partial class DungeonSystem } var position = new Vector2i(posX, posY); - var dungeonUid = _mapManager.GetMapEntityId(mapId); + var dungeonUid = _maps.GetMapOrInvalid(mapId); if (!TryComp(dungeonUid, out var dungeonGrid)) { @@ -118,7 +116,7 @@ public sealed partial class DungeonSystem } var mapId = new MapId(mapInt); - var mapUid = _mapManager.GetMapEntityId(mapId); + var mapUid = _maps.GetMapOrInvalid(mapId); if (!_prototype.TryIndex(args[1], out var pack)) { @@ -156,7 +154,7 @@ public sealed partial class DungeonSystem } } - grid.SetTiles(tiles); + _maps.SetTiles(mapUid, grid, tiles); shell.WriteLine(Loc.GetString("cmd-dungen_pack_vis")); } @@ -174,7 +172,7 @@ public sealed partial class DungeonSystem } var mapId = new MapId(mapInt); - var mapUid = _mapManager.GetMapEntityId(mapId); + var mapUid =_maps.GetMapOrInvalid(mapId); if (!_prototype.TryIndex(args[1], out var preset)) { @@ -197,7 +195,7 @@ public sealed partial class DungeonSystem } } - grid.SetTiles(tiles); + _maps.SetTiles(mapUid, grid, tiles); shell.WriteLine(Loc.GetString("cmd-dungen_pack_vis")); } diff --git a/Content.Server/Remotes/DoorRemoteSystem.cs b/Content.Server/Remotes/DoorRemoteSystem.cs index 6716065087..de327bd084 100644 --- a/Content.Server/Remotes/DoorRemoteSystem.cs +++ b/Content.Server/Remotes/DoorRemoteSystem.cs @@ -74,7 +74,7 @@ namespace Content.Shared.Remotes case OperatingMode.ToggleEmergencyAccess: if (airlockComp != null) { - _airlock.ToggleEmergencyAccess(args.Target.Value, airlockComp); + _airlock.SetEmergencyAccess((args.Target.Value, airlockComp), !airlockComp.EmergencyAccess); _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)} to set emergency access {(airlockComp.EmergencyAccess ? "on" : "off")}"); } diff --git a/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs b/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs index 3bf820535f..bf5a45549c 100644 --- a/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs @@ -32,6 +32,7 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedTransformSystem _xformSystem = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; [Dependency] private readonly TagSystem _tagSystem = default!; #endregion Dependencies @@ -254,7 +255,7 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem var ev = new TilesConsumedByEventHorizonEvent(tiles, gridId, grid, hungry, eventHorizon); RaiseLocalEvent(hungry, ref ev); - grid.SetTiles(tiles); + _mapSystem.SetTiles(gridId, grid, tiles); } /// diff --git a/Content.Server/Stack/StackSystem.cs b/Content.Server/Stack/StackSystem.cs index b9553a6b84..bc1800ffd1 100644 --- a/Content.Server/Stack/StackSystem.cs +++ b/Content.Server/Stack/StackSystem.cs @@ -100,6 +100,13 @@ namespace Content.Server.Stack /// public List SpawnMultiple(string entityPrototype, int amount, EntityCoordinates spawnPosition) { + if (amount <= 0) + { + Log.Error( + $"Attempted to spawn an invalid stack: {entityPrototype}, {amount}. Trace: {Environment.StackTrace}"); + return new(); + } + var spawns = CalculateSpawns(entityPrototype, amount); var spawnedEnts = new List(); @@ -116,6 +123,13 @@ namespace Content.Server.Stack /// public List SpawnMultiple(string entityPrototype, int amount, EntityUid target) { + if (amount <= 0) + { + Log.Error( + $"Attempted to spawn an invalid stack: {entityPrototype}, {amount}. Trace: {Environment.StackTrace}"); + return new(); + } + var spawns = CalculateSpawns(entityPrototype, amount); var spawnedEnts = new List(); diff --git a/Content.Server/Store/Components/CurrencyComponent.cs b/Content.Server/Store/Components/CurrencyComponent.cs index cfe9b76c8b..eb4ca1c0e3 100644 --- a/Content.Server/Store/Components/CurrencyComponent.cs +++ b/Content.Server/Store/Components/CurrencyComponent.cs @@ -8,6 +8,11 @@ namespace Content.Server.Store.Components; /// Identifies a component that can be inserted into a store /// to increase its balance. /// +/// +/// Note that if this entity is a stack of items, then this is meant to represent the value per stack item, not +/// the whole stack. This also means that in general, the actual value should not be modified from the initial +/// prototype value because otherwise stack merging/splitting may modify the total value. +/// [RegisterComponent] public sealed partial class CurrencyComponent : Component { @@ -16,6 +21,12 @@ public sealed partial class CurrencyComponent : Component /// The string is the currency type that will be added. /// The FixedPoint2 is the value of each individual currency entity. /// + /// + /// Note that if this entity is a stack of items, then this is meant to represent the value per stack item, not + /// the whole stack. This also means that in general, the actual value should not be modified from the initial + /// prototype value + /// because otherwise stack merging/splitting may modify the total value. + /// [ViewVariables(VVAccess.ReadWrite)] [DataField("price", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] public Dictionary Price = new(); diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index 247055c2a7..f1c0cb1e90 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -30,7 +30,6 @@ public sealed partial class StoreSystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; private void InitializeUi() { @@ -260,7 +259,7 @@ public sealed partial class StoreSystem //log dat shit. _admin.Add(LogType.StorePurchase, LogImpact.Low, - $"{ToPrettyString(buyer):player} purchased listing \"{ListingLocalisationHelpers.GetLocalisedNameOrEntityName(listing, _prototypeManager)}\" from {ToPrettyString(uid)}"); + $"{ToPrettyString(buyer):player} purchased listing \"{ListingLocalisationHelpers.GetLocalisedNameOrEntityName(listing, _proto)}\" from {ToPrettyString(uid)}"); listing.PurchaseAmount++; //track how many times something has been purchased _audio.PlayEntity(component.BuySuccessSound, msg.Actor, uid); //cha-ching! @@ -284,6 +283,9 @@ public sealed partial class StoreSystem /// private void OnRequestWithdraw(EntityUid uid, StoreComponent component, StoreRequestWithdrawMessage msg) { + if (msg.Amount <= 0) + return; + //make sure we have enough cash in the bank and we actually support this currency if (!component.Balance.TryGetValue(msg.Currency, out var currentAmount) || currentAmount < msg.Amount) return; @@ -307,7 +309,8 @@ public sealed partial class StoreSystem var cashId = proto.Cash[value]; var amountToSpawn = (int) MathF.Floor((float) (amountRemaining / value)); var ents = _stack.SpawnMultiple(cashId, amountToSpawn, coordinates); - _hands.PickupOrDrop(buyer, ents.First()); + if (ents.FirstOrDefault() is {} ent) + _hands.PickupOrDrop(buyer, ent); amountRemaining -= value * amountToSpawn; } diff --git a/Content.Server/Store/Systems/StoreSystem.cs b/Content.Server/Store/Systems/StoreSystem.cs index c13a9583be..7bdf550301 100644 --- a/Content.Server/Store/Systems/StoreSystem.cs +++ b/Content.Server/Store/Systems/StoreSystem.cs @@ -92,14 +92,12 @@ public sealed partial class StoreSystem : EntitySystem if (ev.Cancelled) return; - args.Handled = TryAddCurrency(GetCurrencyValue(uid, component), args.Target.Value, store); + if (!TryAddCurrency((uid, component), (args.Target.Value, store))) + return; - if (args.Handled) - { - var msg = Loc.GetString("store-currency-inserted", ("used", args.Used), ("target", args.Target)); - _popup.PopupEntity(msg, args.Target.Value, args.User); - QueueDel(args.Used); - } + args.Handled = true; + var msg = Loc.GetString("store-currency-inserted", ("used", args.Used), ("target", args.Target)); + _popup.PopupEntity(msg, args.Target.Value, args.User); } private void OnImplantActivate(EntityUid uid, StoreComponent component, OpenUplinkImplantEvent args) @@ -111,6 +109,10 @@ public sealed partial class StoreSystem : EntitySystem /// Gets the value from an entity's currency component. /// Scales with stacks. /// + /// + /// If this result is intended to be used with , + /// consider using instead to ensure that the currency is consumed in the process. + /// /// /// /// The value of the currency @@ -121,19 +123,34 @@ public sealed partial class StoreSystem : EntitySystem } /// - /// Tries to add a currency to a store's balance. + /// Tries to add a currency to a store's balance. Note that if successful, this will consume the currency in the process. /// - /// - /// - /// The currency to add - /// The store to add it to - /// Whether or not the currency was succesfully added - [PublicAPI] - public bool TryAddCurrency(EntityUid currencyEnt, EntityUid storeEnt, StoreComponent? store = null, CurrencyComponent? currency = null) + public bool TryAddCurrency(Entity currency, Entity store) { - if (!Resolve(currencyEnt, ref currency) || !Resolve(storeEnt, ref store)) + if (!Resolve(currency.Owner, ref currency.Comp)) return false; - return TryAddCurrency(GetCurrencyValue(currencyEnt, currency), storeEnt, store); + + if (!Resolve(store.Owner, ref store.Comp)) + return false; + + var value = currency.Comp.Price; + if (TryComp(currency.Owner, out StackComponent? stack) && stack.Count != 1) + { + value = currency.Comp.Price + .ToDictionary(v => v.Key, p => p.Value * stack.Count); + } + + if (!TryAddCurrency(value, store, store.Comp)) + return false; + + // Avoid having the currency accidentally be re-used. E.g., if multiple clients try to use the currency in the + // same tick + currency.Comp.Price.Clear(); + if (stack != null) + _stack.SetCount(currency.Owner, 0, stack); + + QueueDel(currency); + return true; } /// diff --git a/Content.Server/SubFloor/SubFloorHideSystem.cs b/Content.Server/SubFloor/SubFloorHideSystem.cs index 2767f500f9..497d7788d7 100644 --- a/Content.Server/SubFloor/SubFloorHideSystem.cs +++ b/Content.Server/SubFloor/SubFloorHideSystem.cs @@ -19,7 +19,7 @@ public sealed class SubFloorHideSystem : SharedSubFloorHideSystem var xform = Transform(uid); if (TryComp(xform.GridUid, out var grid) - && HasFloorCover(grid, grid.TileIndicesFor(xform.Coordinates))) + && HasFloorCover(xform.GridUid.Value, grid, Map.TileIndicesFor(xform.GridUid.Value, grid, xform.Coordinates))) { args.Cancel(); } diff --git a/Content.Server/Voting/Managers/VoteManager.DefaultVotes.cs b/Content.Server/Voting/Managers/VoteManager.DefaultVotes.cs index 0f7d238518..736ff48817 100644 --- a/Content.Server/Voting/Managers/VoteManager.DefaultVotes.cs +++ b/Content.Server/Voting/Managers/VoteManager.DefaultVotes.cs @@ -2,6 +2,7 @@ using System.Linq; using Content.Server.Administration; using Content.Server.Administration.Managers; using Content.Server.Database; +using Content.Server.Discord.WebhookMessages; using Content.Server.GameTicking; using Content.Server.GameTicking.Presets; using Content.Server.Maps; @@ -27,6 +28,7 @@ namespace Content.Server.Voting.Managers [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly IBanManager _bans = default!; [Dependency] private readonly IServerDbManager _dbManager = default!; + [Dependency] private readonly VoteWebhooks _voteWebhooks = default!; private VotingSystem? _votingSystem; private RoleSystem? _roleSystem; @@ -449,10 +451,13 @@ namespace Content.Server.Voting.Managers var vote = CreateVote(options); _adminLogger.Add(LogType.Vote, LogImpact.Extreme, $"Votekick for {located.Username} ({targetEntityName}) due to {reason} started, initiated by {initiator}."); + // Create Discord webhook + var webhookState = _voteWebhooks.CreateWebhookIfConfigured(options, _cfg.GetCVar(CCVars.DiscordVotekickWebhook), Loc.GetString("votekick-webhook-name"), options.Title + "\n" + Loc.GetString("votekick-webhook-description", ("initiator", initiatorName), ("target", targetSession))); + // Time out the vote now that we know it will happen TimeoutStandardVote(StandardVoteType.Votekick); - vote.OnFinished += (_, _) => + vote.OnFinished += (_, eventArgs) => { var votesYes = vote.VotesPerOption["yes"]; @@ -487,6 +492,7 @@ namespace Content.Server.Voting.Managers { _adminLogger.Add(LogType.Vote, LogImpact.Extreme, $"Votekick for {located.Username} attempted to pass, but an admin was online. Yes: {votesYes} / No: {votesNo}. Yes: {yesVotersString} / No: {noVotersString}"); AnnounceCancelledVotekickForVoters(targetEntityName); + _voteWebhooks.UpdateCancelledWebhookIfConfigured(webhookState, Loc.GetString("votekick-webhook-cancelled-admin-online")); return; } // Check if the target is an antag and the vote reason is raiding (this is to prevent false positives) @@ -494,6 +500,7 @@ namespace Content.Server.Voting.Managers { _adminLogger.Add(LogType.Vote, LogImpact.Extreme, $"Votekick for {located.Username} due to {reason} finished, created by {initiator}, but was cancelled due to the target being an antagonist."); AnnounceCancelledVotekickForVoters(targetEntityName); + _voteWebhooks.UpdateCancelledWebhookIfConfigured(webhookState, Loc.GetString("votekick-webhook-cancelled-antag-target")); return; } // Check if the target is an admin/de-admined admin @@ -501,6 +508,7 @@ namespace Content.Server.Voting.Managers { _adminLogger.Add(LogType.Vote, LogImpact.Extreme, $"Votekick for {located.Username} due to {reason} finished, created by {initiator}, but was cancelled due to the target being a de-admined admin."); AnnounceCancelledVotekickForVoters(targetEntityName); + _voteWebhooks.UpdateCancelledWebhookIfConfigured(webhookState, Loc.GetString("votekick-webhook-cancelled-admin-target")); return; } else @@ -515,6 +523,9 @@ namespace Content.Server.Voting.Managers severity = NoteSeverity.High; } + // Discord webhook, success + _voteWebhooks.UpdateWebhookIfConfigured(webhookState, eventArgs); + uint minutes = (uint)_cfg.GetCVar(CCVars.VotekickBanDuration); _bans.CreateServerBan(targetUid, target, null, null, targetHWid, minutes, severity, reason); @@ -522,6 +533,10 @@ namespace Content.Server.Voting.Managers } else { + + // Discord webhook, failure + _voteWebhooks.UpdateWebhookIfConfigured(webhookState, eventArgs); + _adminLogger.Add(LogType.Vote, LogImpact.Extreme, $"Votekick failed: Yes: {votesYes} / No: {votesNo}. Yes: {yesVotersString} / No: {noVotersString}"); _chatManager.DispatchServerAnnouncement(Loc.GetString("ui-vote-votekick-failure", ("target", targetEntityName), ("reason", reason))); } diff --git a/Content.Server/Voting/VoteCommands.cs b/Content.Server/Voting/VoteCommands.cs index 7dbb41b50f..d4c236f394 100644 --- a/Content.Server/Voting/VoteCommands.cs +++ b/Content.Server/Voting/VoteCommands.cs @@ -1,11 +1,8 @@ using System.Linq; -using System.Text.Json; -using System.Text.Json.Nodes; using Content.Server.Administration; using Content.Server.Administration.Logs; using Content.Server.Chat.Managers; -using Content.Server.Discord; -using Content.Server.GameTicking; +using Content.Server.Discord.WebhookMessages; using Content.Server.Voting.Managers; using Content.Shared.Administration; using Content.Shared.CCVar; @@ -76,11 +73,9 @@ namespace Content.Server.Voting { [Dependency] private readonly IVoteManager _voteManager = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly IConfigurationManager _cfg = default!; - [Dependency] private readonly DiscordWebhook _discord = default!; - [Dependency] private readonly GameTicker _gameTicker = default!; - [Dependency] private readonly IBaseServer _baseServer = default!; [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly VoteWebhooks _voteWebhooks = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; private ISawmill _sawmill = default!; @@ -120,7 +115,7 @@ namespace Content.Server.Voting var vote = _voteManager.CreateVote(options); - var webhookState = CreateWebhookIfConfigured(options); + var webhookState = _voteWebhooks.CreateWebhookIfConfigured(options, _cfg.GetCVar(CCVars.DiscordVoteWebhook)); vote.OnFinished += (_, eventArgs) => { @@ -136,12 +131,12 @@ namespace Content.Server.Voting _chatManager.DispatchServerAnnouncement(Loc.GetString("cmd-customvote-on-finished-win", ("winner", args[(int) eventArgs.Winner]))); } - UpdateWebhookIfConfigured(webhookState, eventArgs); + _voteWebhooks.UpdateWebhookIfConfigured(webhookState, eventArgs); }; vote.OnCancelled += _ => { - UpdateCancelledWebhookIfConfigured(webhookState); + _voteWebhooks.UpdateCancelledWebhookIfConfigured(webhookState); }; } @@ -156,159 +151,6 @@ namespace Content.Server.Voting var n = args.Length - 1; return CompletionResult.FromHint(Loc.GetString("cmd-customvote-arg-option-n", ("n", n))); } - - private WebhookState? CreateWebhookIfConfigured(VoteOptions voteOptions) - { - // All this webhook code is complete garbage. - // I tried to clean it up somewhat, at least to fix the glaring bugs in it. - // Jesus christ man what is with our code review process. - - var webhookUrl = _cfg.GetCVar(CCVars.DiscordVoteWebhook); - if (string.IsNullOrEmpty(webhookUrl)) - return null; - - // Set up the webhook payload - var serverName = _baseServer.ServerName; - - var fields = new List(); - - foreach (var voteOption in voteOptions.Options) - { - var newVote = new WebhookEmbedField - { - Name = voteOption.text, - Value = Loc.GetString("custom-vote-webhook-option-pending") - }; - fields.Add(newVote); - } - - var runLevel = Loc.GetString($"game-run-level-{_gameTicker.RunLevel}"); - - var payload = new WebhookPayload() - { - Username = Loc.GetString("custom-vote-webhook-name"), - Embeds = new List - { - new() - { - Title = voteOptions.InitiatorText, - Color = 13438992, // #CD1010 - Description = voteOptions.Title, - Footer = new WebhookEmbedFooter - { - Text = Loc.GetString( - "custom-vote-webhook-footer", - ("serverName", serverName), - ("roundId", _gameTicker.RoundId), - ("runLevel", runLevel)), - }, - - Fields = fields, - }, - }, - }; - - var state = new WebhookState - { - WebhookUrl = webhookUrl, - Payload = payload, - }; - - CreateWebhookMessage(state, payload); - - return state; - } - - private void UpdateWebhookIfConfigured(WebhookState? state, VoteFinishedEventArgs finished) - { - if (state == null) - return; - - var embed = state.Payload.Embeds![0]; - embed.Color = 2353993; // #23EB49 - - for (var i = 0; i < finished.Votes.Count; i++) - { - var oldName = embed.Fields[i].Name; - var newValue = finished.Votes[i].ToString(); - embed.Fields[i] = new WebhookEmbedField { Name = oldName, Value = newValue, Inline = true}; - } - - state.Payload.Embeds[0] = embed; - - UpdateWebhookMessage(state, state.Payload, state.MessageId); - } - - private void UpdateCancelledWebhookIfConfigured(WebhookState? state) - { - if (state == null) - return; - - var embed = state.Payload.Embeds![0]; - embed.Color = 13356304; // #CBCD10 - embed.Description += "\n\n" + Loc.GetString("custom-vote-webhook-cancelled"); - - for (var i = 0; i < embed.Fields.Count; i++) - { - var oldName = embed.Fields[i].Name; - embed.Fields[i] = new WebhookEmbedField { Name = oldName, Value = Loc.GetString("custom-vote-webhook-option-cancelled"), Inline = true}; - } - - state.Payload.Embeds[0] = embed; - - UpdateWebhookMessage(state, state.Payload, state.MessageId); - } - - // Sends the payload's message. - private async void CreateWebhookMessage(WebhookState state, WebhookPayload payload) - { - try - { - if (await _discord.GetWebhook(state.WebhookUrl) is not { } identifier) - return; - - state.Identifier = identifier.ToIdentifier(); - - _sawmill.Debug(JsonSerializer.Serialize(payload)); - - var request = await _discord.CreateMessage(identifier.ToIdentifier(), payload); - var content = await request.Content.ReadAsStringAsync(); - state.MessageId = ulong.Parse(JsonNode.Parse(content)?["id"]!.GetValue()!); - } - catch (Exception e) - { - _sawmill.Error($"Error while sending vote webhook to Discord: {e}"); - } - } - - // Edits a pre-existing payload message, given an ID - private async void UpdateWebhookMessage(WebhookState state, WebhookPayload payload, ulong id) - { - if (state.MessageId == 0) - { - _sawmill.Warning("Failed to deliver update to custom vote webhook: message ID was zero. This likely indicates a previous connection error sending the original message."); - return; - } - - DebugTools.Assert(state.Identifier != default); - - try - { - await _discord.EditMessage(state.Identifier, id, payload); - } - catch (Exception e) - { - _sawmill.Error($"Error while updating vote webhook on Discord: {e}"); - } - } - - private sealed class WebhookState - { - public required string WebhookUrl; - public required WebhookPayload Payload; - public WebhookIdentifier Identifier; - public ulong MessageId; - } } [AnyCommand] diff --git a/Content.Server/Worldgen/Systems/Debris/BlobFloorPlanBuilderSystem.cs b/Content.Server/Worldgen/Systems/Debris/BlobFloorPlanBuilderSystem.cs index a09416e593..ba0a3a7132 100644 --- a/Content.Server/Worldgen/Systems/Debris/BlobFloorPlanBuilderSystem.cs +++ b/Content.Server/Worldgen/Systems/Debris/BlobFloorPlanBuilderSystem.cs @@ -15,6 +15,7 @@ public sealed class BlobFloorPlanBuilderSystem : BaseWorldSystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinition = default!; [Dependency] private readonly TileSystem _tiles = default!; + [Dependency] private readonly SharedMapSystem _map = default!; /// public override void Initialize() @@ -25,10 +26,10 @@ public sealed class BlobFloorPlanBuilderSystem : BaseWorldSystem private void OnBlobFloorPlanBuilderStartup(EntityUid uid, BlobFloorPlanBuilderComponent component, ComponentStartup args) { - PlaceFloorplanTiles(component, Comp(uid)); + PlaceFloorplanTiles(uid, component, Comp(uid)); } - private void PlaceFloorplanTiles(BlobFloorPlanBuilderComponent comp, MapGridComponent grid) + private void PlaceFloorplanTiles(EntityUid gridUid, BlobFloorPlanBuilderComponent comp, MapGridComponent grid) { // NO MORE THAN TWO ALLOCATIONS THANK YOU VERY MUCH. // TODO: Just put these on a field instead then? @@ -82,7 +83,7 @@ public sealed class BlobFloorPlanBuilderSystem : BaseWorldSystem } } - grid.SetTiles(taken.Select(x => (x.Key, x.Value)).ToList()); + _map.SetTiles(gridUid, grid, taken.Select(x => (x.Key, x.Value)).ToList()); } } diff --git a/Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs b/Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs index ae1c6b5c00..a575e055ef 100644 --- a/Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs +++ b/Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs @@ -13,6 +13,7 @@ public sealed class SimpleFloorPlanPopulatorSystem : BaseWorldSystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinition = default!; + [Dependency] private readonly SharedMapSystem _map = default!; /// public override void Initialize() @@ -25,7 +26,7 @@ public sealed class SimpleFloorPlanPopulatorSystem : BaseWorldSystem { var placeables = new List(4); var grid = Comp(uid); - var enumerator = grid.GetAllTilesEnumerator(); + var enumerator = _map.GetAllTilesEnumerator(uid, grid); while (enumerator.MoveNext(out var tile)) { var coords = grid.GridTileToLocal(tile.Value.GridIndices); diff --git a/Content.Shared/Anomaly/SharedAnomalySystem.cs b/Content.Shared/Anomaly/SharedAnomalySystem.cs index 07beb1444d..00d97c1a46 100644 --- a/Content.Shared/Anomaly/SharedAnomalySystem.cs +++ b/Content.Shared/Anomaly/SharedAnomalySystem.cs @@ -33,7 +33,6 @@ public abstract class SharedAnomalySystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; - [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; public override void Initialize() @@ -372,7 +371,7 @@ public abstract class SharedAnomalySystem : EntitySystem if (tilerefs.Count == 0) break; - var tileref = _random.Pick(tilerefs); + var tileref = Random.Pick(tilerefs); var distance = MathF.Sqrt(MathF.Pow(tileref.X - xform.LocalPosition.X, 2) + MathF.Pow(tileref.Y - xform.LocalPosition.Y, 2)); //cut outer & inner circle diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 10179e58f4..f75ea5c43e 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -478,6 +478,12 @@ namespace Content.Shared.CCVar public static readonly CVarDef DiscordVoteWebhook = CVarDef.Create("discord.vote_webhook", string.Empty, CVar.SERVERONLY); + /// + /// URL of the Discord webhook which will relay all votekick votes. If left empty, disables the webhook. + /// + public static readonly CVarDef DiscordVotekickWebhook = + CVarDef.Create("discord.votekick_webhook", string.Empty, CVar.SERVERONLY); + /// URL of the Discord webhook which will relay round restart messages. /// public static readonly CVarDef DiscordRoundUpdateWebhook = @@ -922,8 +928,8 @@ namespace Content.Shared.CCVar /// After the period has passed, the count resets. /// /// - public static readonly CVarDef AhelpRateLimitPeriod = - CVarDef.Create("ahelp.rate_limit_period", 2, CVar.SERVERONLY); + public static readonly CVarDef AhelpRateLimitPeriod = + CVarDef.Create("ahelp.rate_limit_period", 2f, CVar.SERVERONLY); /// /// How many ahelp messages are allowed in a single rate limit period. @@ -1856,8 +1862,8 @@ namespace Content.Shared.CCVar /// After the period has passed, the count resets. /// /// - public static readonly CVarDef ChatRateLimitPeriod = - CVarDef.Create("chat.rate_limit_period", 2, CVar.SERVERONLY); + public static readonly CVarDef ChatRateLimitPeriod = + CVarDef.Create("chat.rate_limit_period", 2f, CVar.SERVERONLY); /// /// How many chat messages are allowed in a single rate limit period. @@ -1867,19 +1873,12 @@ namespace Content.Shared.CCVar /// divided by . /// /// - /// public static readonly CVarDef ChatRateLimitCount = CVarDef.Create("chat.rate_limit_count", 10, CVar.SERVERONLY); /// - /// If true, announce when a player breached chat rate limit to game administrators. - /// - /// - public static readonly CVarDef ChatRateLimitAnnounceAdmins = - CVarDef.Create("chat.rate_limit_announce_admins", true, CVar.SERVERONLY); - - /// - /// Minimum delay (in seconds) between announcements from . + /// Minimum delay (in seconds) between notifying admins about chat message rate limit violations. + /// A negative value disables admin announcements. /// public static readonly CVarDef ChatRateLimitAnnounceAdminsDelay = CVarDef.Create("chat.rate_limit_announce_admins_delay", 15, CVar.SERVERONLY); @@ -2075,6 +2074,34 @@ namespace Content.Shared.CCVar public static readonly CVarDef ToggleWalk = CVarDef.Create("control.toggle_walk", false, CVar.CLIENTONLY | CVar.ARCHIVE); + /* + * Interactions + */ + + // The rationale behind the default limit is simply that I can easily get to 7 interactions per second by just + // trying to spam toggle a light switch or lever (though the UseDelay component limits the actual effect of the + // interaction). I don't want to accidentally spam admins with alerts just because somebody is spamming a + // key manually, nor do we want to alert them just because the player is having network issues and the server + // receives multiple interactions at once. But we also want to try catch people with modified clients that spam + // many interactions on the same tick. Hence, a very short period, with a relatively high count. + + /// + /// Maximum number of interactions that a player can perform within seconds + /// + public static readonly CVarDef InteractionRateLimitCount = + CVarDef.Create("interaction.rate_limit_count", 5, CVar.SERVER | CVar.REPLICATED); + + /// + public static readonly CVarDef InteractionRateLimitPeriod = + CVarDef.Create("interaction.rate_limit_period", 0.5f, CVar.SERVER | CVar.REPLICATED); + + /// + /// Minimum delay (in seconds) between notifying admins about interaction rate limit violations. A negative + /// value disables admin announcements. + /// + public static readonly CVarDef InteractionRateLimitAnnounceAdminsDelay = + CVarDef.Create("interaction.rate_limit_announce_admins_delay", 120, CVar.SERVERONLY); + /* * STORAGE */ diff --git a/Content.Shared/Cargo/Components/PriceGunComponent.cs b/Content.Shared/Cargo/Components/PriceGunComponent.cs new file mode 100644 index 0000000000..7024f1195a --- /dev/null +++ b/Content.Shared/Cargo/Components/PriceGunComponent.cs @@ -0,0 +1,11 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Cargo.Components; + +/// +/// This is used for the price gun, which calculates the price of any object it appraises. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class PriceGunComponent : Component +{ +} diff --git a/Content.Shared/Cargo/SharedPriceGunSystem.cs b/Content.Shared/Cargo/SharedPriceGunSystem.cs new file mode 100644 index 0000000000..779d55055a --- /dev/null +++ b/Content.Shared/Cargo/SharedPriceGunSystem.cs @@ -0,0 +1,55 @@ +using Content.Shared.Cargo.Components; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Verbs; + +namespace Content.Shared.Cargo.Systems; + +/// +/// The price gun system! If this component is on an entity, you can scan objects (Click or use verb) to see their price. +/// +public abstract class SharedPriceGunSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent>(OnUtilityVerb); + SubscribeLocalEvent(OnAfterInteract); + } + + private void OnUtilityVerb(EntityUid uid, PriceGunComponent component, GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract || args.Using == null) + return; + + var verb = new UtilityVerb() + { + Act = () => + { + GetPriceOrBounty(uid, args.Target, args.User); + }, + Text = Loc.GetString("price-gun-verb-text"), + Message = Loc.GetString("price-gun-verb-message", ("object", Identity.Entity(args.Target, EntityManager))) + }; + + args.Verbs.Add(verb); + } + + private void OnAfterInteract(Entity entity, ref AfterInteractEvent args) + { + if (!args.CanReach || args.Target == null || args.Handled) + return; + + args.Handled |= GetPriceOrBounty(entity, args.Target.Value, args.User); + } + + /// + /// Find the price or confirm if the item is a bounty. Will give a popup of the result to the passed user. + /// + /// + /// + /// This is abstract for prediction. When the bounty system / cargo systems that are necessary are moved to shared, + /// combine all the server, client, and shared stuff into one non abstract file. + /// + protected abstract bool GetPriceOrBounty(EntityUid priceGunUid, EntityUid target, EntityUid user); +} diff --git a/Content.Shared/Chat/ISharedChatManager.cs b/Content.Shared/Chat/ISharedChatManager.cs new file mode 100644 index 0000000000..39c1d85dd2 --- /dev/null +++ b/Content.Shared/Chat/ISharedChatManager.cs @@ -0,0 +1,8 @@ +namespace Content.Shared.Chat; + +public interface ISharedChatManager +{ + void Initialize(); + void SendAdminAlert(string message); + void SendAdminAlert(EntityUid player, string message); +} diff --git a/Content.Shared/Doors/Components/AirlockComponent.cs b/Content.Shared/Doors/Components/AirlockComponent.cs index 6577b1942a..6b3fcfad7e 100644 --- a/Content.Shared/Doors/Components/AirlockComponent.cs +++ b/Content.Shared/Doors/Components/AirlockComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.DeviceLinking; using Content.Shared.Doors.Systems; +using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -23,6 +24,18 @@ public sealed partial class AirlockComponent : Component [ViewVariables(VVAccess.ReadWrite)] [DataField, AutoNetworkedField] public bool EmergencyAccess = false; + + /// + /// Sound to play when the airlock emergency access is turned on. + /// + [DataField] + public SoundSpecifier EmergencyOnSound = new SoundPathSpecifier("/Audio/Machines/airlock_emergencyon.ogg"); + + /// + /// Sound to play when the airlock emergency access is turned off. + /// + [DataField] + public SoundSpecifier EmergencyOffSound = new SoundPathSpecifier("/Audio/Machines/airlock_emergencyoff.ogg"); /// /// Pry modifier for a powered airlock. diff --git a/Content.Shared/Doors/Systems/SharedAirlockSystem.cs b/Content.Shared/Doors/Systems/SharedAirlockSystem.cs index 5a9cde74ee..e404a91bdd 100644 --- a/Content.Shared/Doors/Systems/SharedAirlockSystem.cs +++ b/Content.Shared/Doors/Systems/SharedAirlockSystem.cs @@ -1,4 +1,5 @@ using Content.Shared.Doors.Components; +using Robust.Shared.Audio.Systems; using Content.Shared.Popups; using Content.Shared.Prying.Components; using Content.Shared.Wires; @@ -10,7 +11,9 @@ public abstract class SharedAirlockSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; + [Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] protected readonly SharedDoorSystem DoorSystem = default!; + [Dependency] protected readonly SharedPopupSystem Popup = default!; [Dependency] private readonly SharedWiresSystem _wiresSystem = default!; public override void Initialize() @@ -131,11 +134,23 @@ public abstract class SharedAirlockSystem : EntitySystem Appearance.SetData(uid, DoorVisuals.EmergencyLights, component.EmergencyAccess); } - public void ToggleEmergencyAccess(EntityUid uid, AirlockComponent component) + public void SetEmergencyAccess(Entity ent, bool value, EntityUid? user = null, bool predicted = false) { - component.EmergencyAccess = !component.EmergencyAccess; - Dirty(uid, component); // This only runs on the server apparently so we need this. - UpdateEmergencyLightStatus(uid, component); + if(!ent.Comp.Powered) + return; + + if (ent.Comp.EmergencyAccess == value) + return; + + ent.Comp.EmergencyAccess = value; + Dirty(ent, ent.Comp); // This only runs on the server apparently so we need this. + UpdateEmergencyLightStatus(ent, ent.Comp); + + var sound = ent.Comp.EmergencyAccess ? ent.Comp.EmergencyOnSound : ent.Comp.EmergencyOffSound; + if (predicted) + Audio.PlayPredicted(sound, ent, user: user); + else + Audio.PlayPvs(sound, ent); } public void SetAutoCloseDelayModifier(AirlockComponent component, float value) diff --git a/Content.Shared/Doors/Systems/SharedDoorSystem.Bolts.cs b/Content.Shared/Doors/Systems/SharedDoorSystem.Bolts.cs index 35681bfd82..13050616e1 100644 --- a/Content.Shared/Doors/Systems/SharedDoorSystem.Bolts.cs +++ b/Content.Shared/Doors/Systems/SharedDoorSystem.Bolts.cs @@ -77,8 +77,20 @@ public abstract partial class SharedDoorSystem public void SetBoltsDown(Entity ent, bool value, EntityUid? user = null, bool predicted = false) { + TrySetBoltDown(ent, value, user, predicted); + } + + public bool TrySetBoltDown( + Entity ent, + bool value, + EntityUid? user = null, + bool predicted = false + ) + { + if (!_powerReceiver.IsPowered(ent.Owner)) + return false; if (ent.Comp.BoltsDown == value) - return; + return false; ent.Comp.BoltsDown = value; Dirty(ent, ent.Comp); @@ -89,6 +101,7 @@ public abstract partial class SharedDoorSystem Audio.PlayPredicted(sound, ent, user: user); else Audio.PlayPvs(sound, ent); + return true; } private void OnStateChanged(Entity entity, ref DoorStateChangedEvent args) diff --git a/Content.Shared/Doors/Systems/SharedDoorSystem.cs b/Content.Shared/Doors/Systems/SharedDoorSystem.cs index 2319d5e916..80e7ff9669 100644 --- a/Content.Shared/Doors/Systems/SharedDoorSystem.cs +++ b/Content.Shared/Doors/Systems/SharedDoorSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Emag.Systems; using Content.Shared.Interaction; using Content.Shared.Physics; using Content.Shared.Popups; +using Content.Shared.Power.EntitySystems; using Content.Shared.Prying.Components; using Content.Shared.Prying.Systems; using Content.Shared.Stunnable; @@ -22,6 +23,7 @@ using Robust.Shared.Timing; using Robust.Shared.Audio.Systems; using Robust.Shared.Network; using Robust.Shared.Map.Components; +using Robust.Shared.Physics; namespace Content.Shared.Doors.Systems; @@ -42,26 +44,19 @@ public abstract partial class SharedDoorSystem : EntitySystem [Dependency] private readonly PryingSystem _pryingSystem = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; [Dependency] private readonly SharedMapSystem _mapSystem = default!; + [Dependency] private readonly SharedPowerReceiverSystem _powerReceiver = default!; [ValidatePrototypeId] public const string DoorBumpTag = "DoorBumpOpener"; - /// - /// A body must have an intersection percentage larger than this in order to be considered as colliding with a - /// door. Used for safety close-blocking and crushing. - /// - /// - /// The intersection percentage relies on WORLD AABBs. So if this is too small, and the grid is rotated 45 - /// degrees, then an entity outside of the airlock may be crushed. - /// - public const float IntersectPercentage = 0.2f; - /// /// A set of doors that are currently opening, closing, or just queued to open/close after some delay. /// private readonly HashSet> _activeDoors = new(); + private readonly HashSet> _doorIntersecting = new(); + public override void Initialize() { base.Initialize(); @@ -161,7 +156,6 @@ public abstract partial class SharedDoorSystem : EntitySystem _activeDoors.Add(ent); RaiseLocalEvent(ent, new DoorStateChangedEvent(door.State)); - AppearanceSystem.SetData(ent, DoorVisuals.State, door.State); } protected bool SetState(EntityUid uid, DoorState state, DoorComponent? door = null) @@ -209,6 +203,7 @@ public abstract partial class SharedDoorSystem : EntitySystem door.State = state; Dirty(uid, door); RaiseLocalEvent(uid, new DoorStateChangedEvent(state)); + AppearanceSystem.SetData(uid, DoorVisuals.State, door.State); return true; } @@ -555,20 +550,24 @@ public abstract partial class SharedDoorSystem : EntitySystem if (!TryComp(xform.GridUid, out var mapGridComp)) yield break; var tileRef = _mapSystem.GetTileRef(xform.GridUid.Value, mapGridComp, xform.Coordinates); - var doorWorldBounds = _entityLookup.GetWorldBounds(tileRef); + + _doorIntersecting.Clear(); + _entityLookup.GetLocalEntitiesIntersecting(xform.GridUid.Value, tileRef.GridIndices, _doorIntersecting, gridComp: mapGridComp, flags: (LookupFlags.All & ~LookupFlags.Sensors)); // TODO SLOTH fix electro's code. // ReSharper disable once InconsistentNaming - var doorAABB = _entityLookup.GetWorldAABB(uid); - foreach (var otherPhysics in PhysicsSystem.GetCollidingEntities(Transform(uid).MapID, doorWorldBounds)) + foreach (var otherPhysics in _doorIntersecting) { if (otherPhysics.Comp == physics) continue; + if (!otherPhysics.Comp.CanCollide) + continue; + //TODO: Make only shutters ignore these objects upon colliding instead of all airlocks // Excludes Glasslayer for windows, GlassAirlockLayer for windoors, TableLayer for tables - if (!otherPhysics.Comp.CanCollide || otherPhysics.Comp.CollisionLayer == (int) CollisionGroup.GlassLayer || otherPhysics.Comp.CollisionLayer == (int) CollisionGroup.GlassAirlockLayer || otherPhysics.Comp.CollisionLayer == (int) CollisionGroup.TableLayer) + if (otherPhysics.Comp.CollisionLayer == (int) CollisionGroup.GlassLayer || otherPhysics.Comp.CollisionLayer == (int) CollisionGroup.GlassAirlockLayer || otherPhysics.Comp.CollisionLayer == (int) CollisionGroup.TableLayer) continue; //If the colliding entity is a slippable item ignore it by the airlock @@ -582,9 +581,6 @@ public abstract partial class SharedDoorSystem : EntitySystem if ((physics.CollisionMask & otherPhysics.Comp.CollisionLayer) == 0 && (otherPhysics.Comp.CollisionMask & physics.CollisionLayer) == 0) continue; - if (_entityLookup.GetWorldAABB(otherPhysics.Owner).IntersectPercentage(doorAABB) < IntersectPercentage) - continue; - yield return otherPhysics.Owner; } } @@ -612,7 +608,7 @@ public abstract partial class SharedDoorSystem : EntitySystem var otherUid = args.OtherEntity; if (Tags.HasTag(otherUid, DoorBumpTag)) - TryOpen(uid, door, otherUid, quiet: door.State == DoorState.Denying); + TryOpen(uid, door, otherUid, quiet: door.State == DoorState.Denying, predicted: true); } #endregion @@ -712,7 +708,7 @@ public abstract partial class SharedDoorSystem : EntitySystem var (uid, door, physics) = ent; if (door.BumpOpen) { - foreach (var other in PhysicsSystem.GetContactingEntities(uid, physics, approximate: true)) + foreach (var other in PhysicsSystem.GetContactingEntities(uid, physics)) { if (Tags.HasTag(other, DoorBumpTag) && TryOpen(uid, door, other, quiet: true)) break; diff --git a/Content.Server/Electrocution/Components/ElectrifiedComponent.cs b/Content.Shared/Electrocution/Components/ElectrifiedComponent.cs similarity index 65% rename from Content.Server/Electrocution/Components/ElectrifiedComponent.cs rename to Content.Shared/Electrocution/Components/ElectrifiedComponent.cs index 5755e98091..52eb76ca54 100644 --- a/Content.Server/Electrocution/Components/ElectrifiedComponent.cs +++ b/Content.Shared/Electrocution/Components/ElectrifiedComponent.cs @@ -1,121 +1,131 @@ +using Robust.Shared.GameStates; using Robust.Shared.Audio; -namespace Content.Server.Electrocution; +namespace Content.Shared.Electrocution; /// /// Component for things that shock users on touch. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class ElectrifiedComponent : Component { - [DataField("enabled")] + [DataField, AutoNetworkedField] public bool Enabled = true; /// /// Should player get damage on collide /// - [DataField("onBump")] + [DataField, AutoNetworkedField] public bool OnBump = true; /// /// Should player get damage on attack /// - [DataField("onAttacked")] + [DataField, AutoNetworkedField] public bool OnAttacked = true; /// /// When true - disables power if a window is present in the same tile /// - [DataField("noWindowInTile")] + [DataField, AutoNetworkedField] public bool NoWindowInTile = false; /// /// Should player get damage on interact with empty hand /// - [DataField("onHandInteract")] + [DataField, AutoNetworkedField] public bool OnHandInteract = true; /// /// Should player get damage on interact while holding an object in their hand /// - [DataField("onInteractUsing")] + [DataField, AutoNetworkedField] public bool OnInteractUsing = true; /// /// Indicates if the entity requires power to function /// - [DataField("requirePower")] + [DataField, AutoNetworkedField] public bool RequirePower = true; /// /// Indicates if the entity uses APC power /// - [DataField("usesApcPower")] + [DataField, AutoNetworkedField] public bool UsesApcPower = false; /// /// Identifier for the high voltage node. /// - [DataField("highVoltageNode")] + [DataField, AutoNetworkedField] public string? HighVoltageNode; /// /// Identifier for the medium voltage node. /// - [DataField("mediumVoltageNode")] + [DataField, AutoNetworkedField] public string? MediumVoltageNode; /// /// Identifier for the low voltage node. /// - [DataField("lowVoltageNode")] + [DataField, AutoNetworkedField] public string? LowVoltageNode; /// /// Damage multiplier for HV electrocution /// - [DataField] + [DataField, AutoNetworkedField] public float HighVoltageDamageMultiplier = 3f; /// /// Shock time multiplier for HV electrocution /// - [DataField] + [DataField, AutoNetworkedField] public float HighVoltageTimeMultiplier = 1.5f; /// /// Damage multiplier for MV electrocution /// - [DataField] + [DataField, AutoNetworkedField] public float MediumVoltageDamageMultiplier = 2f; /// /// Shock time multiplier for MV electrocution /// - [DataField] + [DataField, AutoNetworkedField] public float MediumVoltageTimeMultiplier = 1.25f; - [DataField("shockDamage")] + [DataField, AutoNetworkedField] public float ShockDamage = 7.5f; /// /// Shock time, in seconds. /// - [DataField("shockTime")] + [DataField, AutoNetworkedField] public float ShockTime = 8f; - [DataField("siemensCoefficient")] + [DataField, AutoNetworkedField] public float SiemensCoefficient = 1f; - [DataField("shockNoises")] + [DataField, AutoNetworkedField] public SoundSpecifier ShockNoises = new SoundCollectionSpecifier("sparks"); - [DataField("playSoundOnShock")] + [DataField, AutoNetworkedField] + public SoundPathSpecifier AirlockElectrifyDisabled = new("/Audio/Machines/airlock_electrify_on.ogg"); + + [DataField, AutoNetworkedField] + public SoundPathSpecifier AirlockElectrifyEnabled = new("/Audio/Machines/airlock_electrify_off.ogg"); + + [DataField, AutoNetworkedField] public bool PlaySoundOnShock = true; - [DataField("shockVolume")] + [DataField, AutoNetworkedField] public float ShockVolume = 20; - [DataField] + [DataField, AutoNetworkedField] public float Probability = 1f; + + [DataField, AutoNetworkedField] + public bool IsWireCut = false; } diff --git a/Content.Shared/Electrocution/SharedElectrocutionSystem.cs b/Content.Shared/Electrocution/SharedElectrocutionSystem.cs index b228a987af..e36e4a804b 100644 --- a/Content.Shared/Electrocution/SharedElectrocutionSystem.cs +++ b/Content.Shared/Electrocution/SharedElectrocutionSystem.cs @@ -23,6 +23,20 @@ namespace Content.Shared.Electrocution Dirty(uid, insulated); } + /// + /// Sets electrified value of component and marks dirty if required. + /// + public void SetElectrified(Entity ent, bool value) + { + if (ent.Comp.Enabled == value) + { + return; + } + + ent.Comp.Enabled = value; + Dirty(ent, ent.Comp); + } + /// Entity being electrocuted. /// Source entity of the electrocution. /// How much shock damage the entity takes. diff --git a/Content.Shared/Execution/SharedExecutionSystem.cs b/Content.Shared/Execution/SharedExecutionSystem.cs index c34cecc3da..b6d52cf352 100644 --- a/Content.Shared/Execution/SharedExecutionSystem.cs +++ b/Content.Shared/Execution/SharedExecutionSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.CombatMode; using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.DoAfter; +using Content.Shared.IdentityManagement; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Popups; @@ -155,7 +156,7 @@ public sealed class SharedExecutionSystem : EntitySystem if (predict) { _popup.PopupClient( - Loc.GetString(locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)), + Loc.GetString(locString, ("attacker", Identity.Entity(attacker, EntityManager)), ("victim", Identity.Entity(victim, EntityManager)), ("weapon", weapon)), attacker, attacker, PopupType.MediumCaution @@ -164,7 +165,7 @@ public sealed class SharedExecutionSystem : EntitySystem else { _popup.PopupEntity( - Loc.GetString(locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)), + Loc.GetString(locString, ("attacker", Identity.Entity(attacker, EntityManager)), ("victim", Identity.Entity(victim, EntityManager)), ("weapon", weapon)), attacker, attacker, PopupType.MediumCaution @@ -175,7 +176,7 @@ public sealed class SharedExecutionSystem : EntitySystem private void ShowExecutionExternalPopup(string locString, EntityUid attacker, EntityUid victim, EntityUid weapon) { _popup.PopupEntity( - Loc.GetString(locString, ("attacker", attacker), ("victim", victim), ("weapon", weapon)), + Loc.GetString(locString, ("attacker", Identity.Entity(attacker, EntityManager)), ("victim", Identity.Entity(victim, EntityManager)), ("weapon", weapon)), attacker, Filter.PvsExcept(attacker), true, diff --git a/Content.Shared/Fluids/Components/DrainComponent.cs b/Content.Shared/Fluids/Components/DrainComponent.cs index 50cb5f5195..3064721bf3 100644 --- a/Content.Shared/Fluids/Components/DrainComponent.cs +++ b/Content.Shared/Fluids/Components/DrainComponent.cs @@ -52,7 +52,7 @@ public sealed partial class DrainComponent : Component /// drain puddles from. /// [DataField] - public float Range = 2f; + public float Range = 2.5f; /// /// How often in seconds the drain checks for puddles around it. diff --git a/Content.Shared/Friction/TileFrictionController.cs b/Content.Shared/Friction/TileFrictionController.cs index 930de07dab..eb109caa42 100644 --- a/Content.Shared/Friction/TileFrictionController.cs +++ b/Content.Shared/Friction/TileFrictionController.cs @@ -22,6 +22,7 @@ namespace Content.Shared.Friction [Dependency] private readonly SharedGravitySystem _gravity = default!; [Dependency] private readonly SharedMoverController _mover = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedMapSystem _map = default!; private EntityQuery _frictionQuery; private EntityQuery _xformQuery; @@ -185,7 +186,7 @@ namespace Content.Shared.Friction : DefaultFriction; } - var tile = grid.GetTileRef(xform.Coordinates); + var tile = _map.GetTileRef(xform.GridUid.Value, grid, xform.Coordinates); // If it's a map but on an empty tile then just assume it has gravity. if (tile.Tile.IsEmpty && diff --git a/Content.Shared/Hands/Components/HandsComponent.cs b/Content.Shared/Hands/Components/HandsComponent.cs index f218455c0b..b3cb51ae35 100644 --- a/Content.Shared/Hands/Components/HandsComponent.cs +++ b/Content.Shared/Hands/Components/HandsComponent.cs @@ -80,6 +80,12 @@ public sealed partial class HandsComponent : Component [DataField] public DisplacementData? HandDisplacement; + + /// + /// If false, hands cannot be stripped, and they do not show up in the stripping menu. + /// + [DataField] + public bool CanBeStripped = true; } [Serializable, NetSerializable] diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 8539b9d282..43dd97762c 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -2,6 +2,8 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.ActionBlocker; using Content.Shared.Administration.Logs; +using Content.Shared.CCVar; +using Content.Shared.Chat; using Content.Shared.CombatMode; using Content.Shared.Database; using Content.Shared.Ghost; @@ -16,8 +18,8 @@ using Content.Shared.Item; using Content.Shared.Movement.Components; using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Physics; +using Content.Shared.Players.RateLimiting; using Content.Shared.Popups; -using Content.Shared.Silicons.StationAi; using Content.Shared.Storage; using Content.Shared.Tag; using Content.Shared.Timing; @@ -25,6 +27,7 @@ using Content.Shared.UserInterface; using Content.Shared.Verbs; using Content.Shared.Wall; using JetBrains.Annotations; +using Robust.Shared.Configuration; using Robust.Shared.Containers; using Robust.Shared.Input; using Robust.Shared.Input.Binding; @@ -64,6 +67,9 @@ namespace Content.Shared.Interaction [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; + [Dependency] private readonly SharedPlayerRateLimitManager _rateLimit = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly ISharedChatManager _chat = default!; private EntityQuery _ignoreUiRangeQuery; private EntityQuery _fixtureQuery; @@ -80,8 +86,8 @@ namespace Content.Shared.Interaction public const float InteractionRange = 1.5f; public const float InteractionRangeSquared = InteractionRange * InteractionRange; - public const float MaxRaycastRange = 100f; + public const string RateLimitKey = "Interaction"; public delegate bool Ignored(EntityUid entity); @@ -119,9 +125,22 @@ namespace Content.Shared.Interaction new PointerInputCmdHandler(HandleTryPullObject)) .Register(); + _rateLimit.Register(RateLimitKey, + new RateLimitRegistration(CCVars.InteractionRateLimitPeriod, + CCVars.InteractionRateLimitCount, + null, + CCVars.InteractionRateLimitAnnounceAdminsDelay, + RateLimitAlertAdmins) + ); + InitializeBlocking(); } + private void RateLimitAlertAdmins(ICommonSession session) + { + _chat.SendAdminAlert(Loc.GetString("interaction-rate-limit-admin-announcement", ("player", session.Name))); + } + public override void Shutdown() { CommandBinds.Unregister(); @@ -1250,8 +1269,11 @@ namespace Content.Shared.Interaction return InRangeUnobstructed(user, wearer) && _containerSystem.IsInSameOrParentContainer(user, wearer); } - protected bool ValidateClientInput(ICommonSession? session, EntityCoordinates coords, - EntityUid uid, [NotNullWhen(true)] out EntityUid? userEntity) + protected bool ValidateClientInput( + ICommonSession? session, + EntityCoordinates coords, + EntityUid uid, + [NotNullWhen(true)] out EntityUid? userEntity) { userEntity = null; @@ -1281,7 +1303,7 @@ namespace Content.Shared.Interaction return false; } - return true; + return _rateLimit.CountAction(session!, RateLimitKey) == RateLimitStatus.Allowed; } /// diff --git a/Content.Shared/Interaction/SmartEquipSystem.cs b/Content.Shared/Interaction/SmartEquipSystem.cs index 4feb0445f8..746bc994ee 100644 --- a/Content.Shared/Interaction/SmartEquipSystem.cs +++ b/Content.Shared/Interaction/SmartEquipSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Hands.EntitySystems; using Content.Shared.Input; using Content.Shared.Inventory; using Content.Shared.Popups; +using Content.Shared.Stacks; using Content.Shared.Storage; using Content.Shared.Storage.EntitySystems; using Content.Shared.Whitelist; @@ -151,8 +152,13 @@ public sealed class SmartEquipSystem : EntitySystem _hands.TryDrop(uid, hands.ActiveHand, handsComp: hands); _storage.Insert(slotItem, handItem.Value, out var stacked, out _); - if (stacked != null) - _hands.TryPickup(uid, stacked.Value, handsComp: hands); + // if the hand item stacked with the things in inventory, but there's no more space left for the rest + // of the stack, place the stack back in hand rather than dropping it on the floor + if (stacked != null && !_storage.CanInsert(slotItem, handItem.Value, out _)) + { + if (TryComp(handItem.Value, out var handStack) && handStack.Count > 0) + _hands.TryPickup(uid, handItem.Value, handsComp: hands); + } return; } diff --git a/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs index 98029f97d5..d5bbaac12c 100644 --- a/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs +++ b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs @@ -71,7 +71,7 @@ public sealed class ItemToggleSystem : EntitySystem private void OnActivateVerb(Entity ent, ref GetVerbsEvent args) { - if (!args.CanAccess || !args.CanInteract) + if (!args.CanAccess || !args.CanInteract || !ent.Comp.OnActivate) return; var user = args.User; diff --git a/Content.Shared/Materials/SharedMaterialStorageSystem.cs b/Content.Shared/Materials/SharedMaterialStorageSystem.cs index dc4858fd74..01539936b9 100644 --- a/Content.Shared/Materials/SharedMaterialStorageSystem.cs +++ b/Content.Shared/Materials/SharedMaterialStorageSystem.cs @@ -8,6 +8,7 @@ using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Timing; using Robust.Shared.Utility; +using Content.Shared.Research.Components; namespace Content.Shared.Materials; @@ -34,6 +35,7 @@ public abstract class SharedMaterialStorageSystem : EntitySystem SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnDatabaseModified); } public override void Update(float frameTime) @@ -312,6 +314,11 @@ public abstract class SharedMaterialStorageSystem : EntitySystem args.Handled = TryInsertMaterialEntity(args.User, args.Used, uid, component); } + private void OnDatabaseModified(Entity ent, ref TechnologyDatabaseModifiedEvent args) + { + UpdateMaterialWhitelist(ent); + } + public int GetSheetVolume(MaterialPrototype material) { if (material.StackEntity == null) diff --git a/Content.Shared/Players/RateLimiting/RateLimitRegistration.cs b/Content.Shared/Players/RateLimiting/RateLimitRegistration.cs new file mode 100644 index 0000000000..6bcf15d30b --- /dev/null +++ b/Content.Shared/Players/RateLimiting/RateLimitRegistration.cs @@ -0,0 +1,76 @@ +using Content.Shared.Database; +using Robust.Shared.Configuration; +using Robust.Shared.Player; + +namespace Content.Shared.Players.RateLimiting; + +/// +/// Contains all data necessary to register a rate limit with . +/// +public sealed class RateLimitRegistration( + CVarDef cVarLimitPeriodLength, + CVarDef cVarLimitCount, + Action? playerLimitedAction, + CVarDef? cVarAdminAnnounceDelay = null, + Action? adminAnnounceAction = null, + LogType adminLogType = LogType.RateLimited) +{ + /// + /// CVar that controls the period over which the rate limit is counted, measured in seconds. + /// + public readonly CVarDef CVarLimitPeriodLength = cVarLimitPeriodLength; + + /// + /// CVar that controls how many actions are allowed in a single rate limit period. + /// + public readonly CVarDef CVarLimitCount = cVarLimitCount; + + /// + /// An action that gets invoked when this rate limit has been breached by a player. + /// + /// + /// This can be used for informing players or taking administrative action. + /// + public readonly Action? PlayerLimitedAction = playerLimitedAction; + + /// + /// CVar that controls the minimum delay between admin notifications, measured in seconds. + /// This can be omitted to have no admin notification system. + /// If the cvar is set to 0, there every breach will be reported. + /// If the cvar is set to a negative number, admin announcements are disabled. + /// + /// + /// If set, must be set too. + /// + public readonly CVarDef? CVarAdminAnnounceDelay = cVarAdminAnnounceDelay; + + /// + /// An action that gets invoked when a rate limit was breached and admins should be notified. + /// + /// + /// If set, must be set too. + /// + public readonly Action? AdminAnnounceAction = adminAnnounceAction; + + /// + /// Log type used to log rate limit violations to the admin logs system. + /// + public readonly LogType AdminLogType = adminLogType; +} + +/// +/// Result of a rate-limited operation. +/// +/// +public enum RateLimitStatus : byte +{ + /// + /// The action was not blocked by the rate limit. + /// + Allowed, + + /// + /// The action was blocked by the rate limit. + /// + Blocked, +} diff --git a/Content.Shared/Players/RateLimiting/SharedPlayerRateLimitManager.cs b/Content.Shared/Players/RateLimiting/SharedPlayerRateLimitManager.cs new file mode 100644 index 0000000000..addb1dee37 --- /dev/null +++ b/Content.Shared/Players/RateLimiting/SharedPlayerRateLimitManager.cs @@ -0,0 +1,55 @@ +using Robust.Shared.Player; + +namespace Content.Shared.Players.RateLimiting; + +/// +/// General-purpose system to rate limit actions taken by clients, such as chat messages. +/// +/// +/// +/// Different categories of rate limits must be registered ahead of time by calling . +/// Once registered, you can simply call to count a rate-limited action for a player. +/// +/// +/// This system is intended for rate limiting player actions over short periods, +/// to ward against spam that can cause technical issues such as admin client load. +/// It should not be used for in-game actions or similar. +/// +/// +/// Rate limits are reset when a client reconnects. +/// This should not be an issue for the reasonably short rate limit periods this system is intended for. +/// +/// +/// +public abstract class SharedPlayerRateLimitManager +{ + /// + /// Count and validate an action performed by a player against rate limits. + /// + /// The player performing the action. + /// The key string that was previously used to register a rate limit category. + /// Whether the action counted should be blocked due to surpassing rate limits or not. + /// + /// is not a connected player + /// OR is not a registered rate limit category. + /// + /// + public abstract RateLimitStatus CountAction(ICommonSession player, string key); + + /// + /// Register a new rate limit category. + /// + /// + /// The key string that will be referred to later with . + /// Must be unique and should probably just be a constant somewhere. + /// + /// The data specifying the rate limit's parameters. + /// has already been registered. + /// is invalid. + public abstract void Register(string key, RateLimitRegistration registration); + + /// + /// Initialize the manager's functionality at game startup. + /// + public abstract void Initialize(); +} diff --git a/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs index 2bc2af7831..b7ba2a31c5 100644 --- a/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs +++ b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs @@ -1,5 +1,4 @@ using System.Diagnostics.CodeAnalysis; -using Content.Shared.Examine; using Content.Shared.Power.Components; namespace Content.Shared.Power.EntitySystems; @@ -8,6 +7,9 @@ public abstract class SharedPowerReceiverSystem : EntitySystem { public abstract bool ResolveApc(EntityUid entity, [NotNullWhen(true)] ref SharedApcPowerReceiverComponent? component); + /// + /// Checks if entity is APC-powered device, and if it have power. + /// public bool IsPowered(Entity entity) { if (!ResolveApc(entity.Owner, ref entity.Comp)) diff --git a/Content.Shared/Roles/Jobs/SharedJobSystem.cs b/Content.Shared/Roles/Jobs/SharedJobSystem.cs index ce4428d9fe..939a57baad 100644 --- a/Content.Shared/Roles/Jobs/SharedJobSystem.cs +++ b/Content.Shared/Roles/Jobs/SharedJobSystem.cs @@ -15,8 +15,6 @@ public abstract class SharedJobSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototypes = default!; [Dependency] private readonly SharedPlayerSystem _playerSystem = default!; - - [Dependency] private readonly IPrototypeManager _protoManager = default!; private readonly Dictionary _inverseTrackerLookup = new(); public override void Initialize() @@ -37,7 +35,7 @@ public abstract class SharedJobSystem : EntitySystem _inverseTrackerLookup.Clear(); // This breaks if you have N trackers to 1 JobId but future concern. - foreach (var job in _protoManager.EnumeratePrototypes()) + foreach (var job in _prototypes.EnumeratePrototypes()) { _inverseTrackerLookup.Add(job.PlayTimeTracker, job.ID); } @@ -50,7 +48,7 @@ public abstract class SharedJobSystem : EntitySystem /// public string GetJobPrototype(string trackerProto) { - DebugTools.Assert(_protoManager.HasIndex(trackerProto)); + DebugTools.Assert(_prototypes.HasIndex(trackerProto)); return _inverseTrackerLookup[trackerProto]; } @@ -60,7 +58,7 @@ public abstract class SharedJobSystem : EntitySystem public bool TryGetDepartment(string jobProto, [NotNullWhen(true)] out DepartmentPrototype? departmentPrototype) { // Not that many departments so we can just eat the cost instead of storing the inverse lookup. - var departmentProtos = _protoManager.EnumeratePrototypes().ToList(); + var departmentProtos = _prototypes.EnumeratePrototypes().ToList(); departmentProtos.Sort((x, y) => string.Compare(x.ID, y.ID, StringComparison.Ordinal)); foreach (var department in departmentProtos) @@ -85,7 +83,7 @@ public abstract class SharedJobSystem : EntitySystem { // not sorting it since there should only be 1 primary department for a job. // this is enforced by the job tests. - var departmentProtos = _protoManager.EnumeratePrototypes(); + var departmentProtos = _prototypes.EnumeratePrototypes(); foreach (var department in departmentProtos) { diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs index ff6fc1ece0..37e5cd6e6a 100644 --- a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs @@ -1,5 +1,6 @@ using Content.Shared.Doors.Components; using Robust.Shared.Serialization; +using Content.Shared.Electrocution; namespace Content.Shared.Silicons.StationAi; @@ -10,16 +11,84 @@ public abstract partial class SharedStationAiSystem private void InitializeAirlock() { SubscribeLocalEvent(OnAirlockBolt); + SubscribeLocalEvent(OnAirlockEmergencyAccess); + SubscribeLocalEvent(OnElectrified); } + /// + /// Attempts to bolt door. If wire was cut (AI or for bolts) or its not powered - notifies AI and does nothing. + /// private void OnAirlockBolt(EntityUid ent, DoorBoltComponent component, StationAiBoltEvent args) { - _doors.SetBoltsDown((ent, component), args.Bolted, args.User, predicted: true); + if (component.BoltWireCut) + { + ShowDeviceNotRespondingPopup(args.User); + return; + } + + var setResult = _doors.TrySetBoltDown((ent, component), args.Bolted, args.User, predicted: true); + if (!setResult) + { + ShowDeviceNotRespondingPopup(args.User); + } + } + + /// + /// Attempts to bolt door. If wire was cut (AI) or its not powered - notifies AI and does nothing. + /// + private void OnAirlockEmergencyAccess(EntityUid ent, AirlockComponent component, StationAiEmergencyAccessEvent args) + { + if (!PowerReceiver.IsPowered(ent)) + { + ShowDeviceNotRespondingPopup(args.User); + return; + } + + _airlocks.SetEmergencyAccess((ent, component), args.EmergencyAccess, args.User, predicted: true); + } + + /// + /// Attempts to bolt door. If wire was cut (AI or for one of power-wires) or its not powered - notifies AI and does nothing. + /// + private void OnElectrified(EntityUid ent, ElectrifiedComponent component, StationAiElectrifiedEvent args) + { + if ( + component.IsWireCut + || !PowerReceiver.IsPowered(ent) + ) + { + ShowDeviceNotRespondingPopup(args.User); + return; + } + + _electrify.SetElectrified((ent, component), args.Electrified); + var soundToPlay = component.Enabled + ? component.AirlockElectrifyDisabled + : component.AirlockElectrifyEnabled; + _audio.PlayLocal(soundToPlay, ent, args.User); } } +/// Event for StationAI attempt at bolting/unbolting door. [Serializable, NetSerializable] public sealed class StationAiBoltEvent : BaseStationAiAction { + /// Marker, should be door bolted or unbolted. public bool Bolted; } + +/// Event for StationAI attempt at setting emergency access for door on/off. +[Serializable, NetSerializable] +public sealed class StationAiEmergencyAccessEvent : BaseStationAiAction +{ + /// Marker, should door have emergency access on or off. + public bool EmergencyAccess; +} + +/// Event for StationAI attempt at electrifying/de-electrifying door. +[Serializable, NetSerializable] +public sealed class StationAiElectrifiedEvent : BaseStationAiAction +{ + /// Marker, should door be electrified or no. + public bool Electrified; +} diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs index c59c472307..e067cf3efa 100644 --- a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.Actions.Events; using Content.Shared.IdentityManagement; using Content.Shared.Interaction.Events; +using Content.Shared.Popups; using Content.Shared.Verbs; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -13,9 +14,9 @@ public abstract partial class SharedStationAiSystem /* * Added when an entity is inserted into a StationAiCore. */ - - //TODO: Fix this, please - private const string JobNameLocId = "job-name-station-ai"; + + //TODO: Fix this, please + private const string JobNameLocId = "job-name-station-ai"; private void InitializeHeld() { @@ -26,10 +27,10 @@ public abstract partial class SharedStationAiSystem SubscribeLocalEvent(OnHeldInteraction); SubscribeLocalEvent(OnHeldRelay); SubscribeLocalEvent(OnCoreJump); - SubscribeLocalEvent(OnTryGetIdentityShortInfo); + SubscribeLocalEvent(OnTryGetIdentityShortInfo); } - - private void OnTryGetIdentityShortInfo(TryGetIdentityShortInfoEvent args) + + private void OnTryGetIdentityShortInfo(TryGetIdentityShortInfoEvent args) { if (args.Handled) { @@ -40,7 +41,7 @@ public abstract partial class SharedStationAiSystem { return; } - args.Title = $"{Name(args.ForActor)} ({Loc.GetString(JobNameLocId)})"; + args.Title = $"{Name(args.ForActor)} ({Loc.GetString(JobNameLocId)})"; args.Handled = true; } @@ -108,41 +109,56 @@ public abstract partial class SharedStationAiSystem return; if (TryComp(ev.Actor, out StationAiHeldComponent? aiComp) && - (!ValidateAi((ev.Actor, aiComp)) || - !HasComp(ev.Target))) + (!TryComp(ev.Target, out StationAiWhitelistComponent? whitelistComponent) || + !ValidateAi((ev.Actor, aiComp)))) { + if (whitelistComponent is { Enabled: false }) + { + ShowDeviceNotRespondingPopup(ev.Actor); + } ev.Cancel(); } } private void OnHeldInteraction(Entity ent, ref InteractionAttemptEvent args) { - // Cancel if it's not us or something with a whitelist. - args.Cancelled = ent.Owner != args.Target && - args.Target != null && - (!TryComp(args.Target, out StationAiWhitelistComponent? whitelist) || !whitelist.Enabled); + // Cancel if it's not us or something with a whitelist, or whitelist is disabled. + args.Cancelled = (!TryComp(args.Target, out StationAiWhitelistComponent? whitelistComponent) + || !whitelistComponent.Enabled) + && ent.Owner != args.Target + && args.Target != null; + if (whitelistComponent is { Enabled: false }) + { + ShowDeviceNotRespondingPopup(ent.Owner); + } } private void OnTargetVerbs(Entity ent, ref GetVerbsEvent args) { - if (!args.CanComplexInteract || - !ent.Comp.Enabled || - !HasComp(args.User) || - !HasComp(args.Target)) + if (!args.CanComplexInteract + || !HasComp(args.User)) { return; } var user = args.User; + var target = args.Target; var isOpen = _uiSystem.IsUiOpen(target, AiUi.Key, user); - args.Verbs.Add(new AlternativeVerb() + var verb = new AlternativeVerb { Text = isOpen ? Loc.GetString("ai-close") : Loc.GetString("ai-open"), - Act = () => + Act = () => { + // no need to show menu if device is not powered. + if (!PowerReceiver.IsPowered(ent.Owner)) + { + ShowDeviceNotRespondingPopup(user); + return; + } + if (isOpen) { _uiSystem.CloseUi(ent.Owner, AiUi.Key, user); @@ -152,7 +168,13 @@ public abstract partial class SharedStationAiSystem _uiSystem.OpenUi(ent.Owner, AiUi.Key, user); } } - }); + }; + args.Verbs.Add(verb); + } + + private void ShowDeviceNotRespondingPopup(EntityUid toEntity) + { + _popup.PopupClient(Loc.GetString("ai-device-not-responding"), toEntity, PopupType.MediumCaution); } } diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs index 17c592879c..baef62c3da 100644 --- a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs @@ -4,14 +4,18 @@ using Content.Shared.Administration.Managers; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; using Content.Shared.Doors.Systems; +using Content.Shared.Electrocution; using Content.Shared.Interaction; using Content.Shared.Item.ItemToggle; using Content.Shared.Mind; using Content.Shared.Movement.Components; using Content.Shared.Movement.Systems; +using Content.Shared.Popups; using Content.Shared.Power; +using Content.Shared.Power.EntitySystems; using Content.Shared.StationAi; using Content.Shared.Verbs; +using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.Map.Components; using Robust.Shared.Network; @@ -24,23 +28,28 @@ namespace Content.Shared.Silicons.StationAi; public abstract partial class SharedStationAiSystem : EntitySystem { - [Dependency] private readonly ISharedAdminManager _admin = default!; - [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly INetManager _net = default!; - [Dependency] private readonly ItemSlotsSystem _slots = default!; - [Dependency] private readonly ItemToggleSystem _toggles = default!; - [Dependency] private readonly ActionBlockerSystem _blocker = default!; - [Dependency] private readonly MetaDataSystem _metadata = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedContainerSystem _containers = default!; - [Dependency] private readonly SharedDoorSystem _doors = default!; - [Dependency] private readonly SharedEyeSystem _eye = default!; + [Dependency] private readonly ISharedAdminManager _admin = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly ItemSlotsSystem _slots = default!; + [Dependency] private readonly ItemToggleSystem _toggles = default!; + [Dependency] private readonly ActionBlockerSystem _blocker = default!; + [Dependency] private readonly MetaDataSystem _metadata = default!; + [Dependency] private readonly SharedAirlockSystem _airlocks = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedContainerSystem _containers = default!; + [Dependency] private readonly SharedDoorSystem _doors = default!; + [Dependency] private readonly SharedElectrocutionSystem _electrify = default!; + [Dependency] private readonly SharedEyeSystem _eye = default!; [Dependency] protected readonly SharedMapSystem Maps = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; - [Dependency] private readonly SharedMoverController _mover = default!; - [Dependency] private readonly SharedTransformSystem _xforms = default!; - [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; - [Dependency] private readonly StationAiVisionSystem _vision = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly SharedMoverController _mover = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedPowerReceiverSystem PowerReceiver = default!; + [Dependency] private readonly SharedTransformSystem _xforms = default!; + [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly StationAiVisionSystem _vision = default!; // StationAiHeld is added to anything inside of an AI core. // StationAiHolder indicates it can hold an AI positronic brain (e.g. holocard / core). diff --git a/Content.Shared/Sound/SharedEmitSoundSystem.cs b/Content.Shared/Sound/SharedEmitSoundSystem.cs index 8733edf485..8040910dc3 100644 --- a/Content.Shared/Sound/SharedEmitSoundSystem.cs +++ b/Content.Shared/Sound/SharedEmitSoundSystem.cs @@ -35,6 +35,7 @@ public abstract class SharedEmitSoundSystem : EntitySystem [Dependency] private readonly SharedAmbientSoundSystem _ambient = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; + [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; public override void Initialize() @@ -86,7 +87,7 @@ public abstract class SharedEmitSoundSystem : EntitySystem return; } - var tile = grid.GetTileRef(xform.Coordinates); + var tile = _map.GetTileRef(xform.GridUid.Value, grid, xform.Coordinates); // Handle maps being grids (we'll still emit the sound). if (xform.GridUid != xform.MapUid && tile.IsSpace(_tileDefMan)) diff --git a/Content.Shared/Strip/SharedStrippableSystem.cs b/Content.Shared/Strip/SharedStrippableSystem.cs index a68bf755d4..e1c3d8ef0d 100644 --- a/Content.Shared/Strip/SharedStrippableSystem.cs +++ b/Content.Shared/Strip/SharedStrippableSystem.cs @@ -118,6 +118,9 @@ public abstract class SharedStrippableSystem : EntitySystem !Resolve(target, ref targetStrippable)) return; + if (!target.Comp.CanBeStripped) + return; + if (!_handsSystem.TryGetHand(target.Owner, handId, out var handSlot)) return; @@ -349,6 +352,9 @@ public abstract class SharedStrippableSystem : EntitySystem !Resolve(target, ref target.Comp)) return false; + if (!target.Comp.CanBeStripped) + return false; + if (user.Comp.ActiveHand == null) return false; @@ -449,6 +455,9 @@ public abstract class SharedStrippableSystem : EntitySystem if (!Resolve(target, ref target.Comp)) return false; + if (!target.Comp.CanBeStripped) + return false; + if (!_handsSystem.TryGetHand(target, handName, out var handSlot, target.Comp)) { _popupSystem.PopupCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Identity.Name(target, EntityManager, user)))); diff --git a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs index cebc84ecb9..c90a28a513 100644 --- a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs +++ b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs @@ -17,6 +17,7 @@ namespace Content.Shared.SubFloor { [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; + [Dependency] protected readonly SharedMapSystem Map = default!; [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; public override void Initialize() @@ -92,7 +93,7 @@ namespace Content.Shared.SubFloor if (args.NewTile.Tile.IsEmpty) return; // Anything that was here will be unanchored anyways. - UpdateTile(Comp(args.NewTile.GridUid), args.NewTile.GridIndices); + UpdateTile(args.NewTile.GridUid, Comp(args.NewTile.GridUid), args.NewTile.GridIndices); } /// @@ -104,25 +105,25 @@ namespace Content.Shared.SubFloor return; if (xform.Anchored && TryComp(xform.GridUid, out var grid)) - component.IsUnderCover = HasFloorCover(grid, grid.TileIndicesFor(xform.Coordinates)); + component.IsUnderCover = HasFloorCover(xform.GridUid.Value, grid, Map.TileIndicesFor(xform.GridUid.Value, grid, xform.Coordinates)); else component.IsUnderCover = false; UpdateAppearance(uid, component); } - public bool HasFloorCover(MapGridComponent grid, Vector2i position) + public bool HasFloorCover(EntityUid gridUid, MapGridComponent grid, Vector2i position) { // TODO Redo this function. Currently wires on an asteroid are always "below the floor" - var tileDef = (ContentTileDefinition) _tileDefinitionManager[grid.GetTileRef(position).Tile.TypeId]; + var tileDef = (ContentTileDefinition) _tileDefinitionManager[Map.GetTileRef(gridUid, grid, position).Tile.TypeId]; return !tileDef.IsSubFloor; } - private void UpdateTile(MapGridComponent grid, Vector2i position) + private void UpdateTile(EntityUid gridUid, MapGridComponent grid, Vector2i position) { - var covered = HasFloorCover(grid, position); + var covered = HasFloorCover(gridUid, grid, position); - foreach (var uid in grid.GetAnchoredEntities(position)) + foreach (var uid in Map.GetAnchoredEntities(gridUid, grid, position)) { if (!TryComp(uid, out SubFloorHideComponent? hideComp)) continue; diff --git a/Content.Shared/Tiles/FloorTileSystem.cs b/Content.Shared/Tiles/FloorTileSystem.cs index f031292f23..3acd5051c9 100644 --- a/Content.Shared/Tiles/FloorTileSystem.cs +++ b/Content.Shared/Tiles/FloorTileSystem.cs @@ -36,6 +36,7 @@ public sealed class FloorTileSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly TileSystem _tile = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedMapSystem _map = default!; private static readonly Vector2 CheckRange = new(1f, 1f); @@ -132,7 +133,7 @@ public sealed class FloorTileSystem : EntitySystem return; } - var tile = mapGrid.GetTileRef(location); + var tile = _map.GetTileRef(gridUid, mapGrid, location); var baseTurf = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId]; if (HasBaseTurf(currentTileDefinition, baseTurf.ID)) @@ -176,7 +177,7 @@ public sealed class FloorTileSystem : EntitySystem var random = new System.Random((int) _timing.CurTick.Value); var variant = _tile.PickVariant((ContentTileDefinition) _tileDefinitionManager[tileId], random); - mapGrid.SetTile(location.Offset(new Vector2(offset, offset)), new Tile(tileId, 0, variant)); + _map.SetTile(gridUid, mapGrid,location.Offset(new Vector2(offset, offset)), new Tile(tileId, 0, variant)); _audio.PlayPredicted(placeSound, location, user); } diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs b/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs index 60eafce474..a6c3c4779d 100644 --- a/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs +++ b/Content.Shared/Tools/Systems/SharedToolSystem.Welder.cs @@ -55,7 +55,7 @@ public abstract partial class SharedToolSystem if (!Resolve(uid, ref welder, ref solutionContainer)) return default; - if (!SolutionContainer.TryGetSolution( + if (!SolutionContainerSystem.TryGetSolution( (uid, solutionContainer), welder.FuelSolutionName, out _, diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.cs b/Content.Shared/Tools/Systems/SharedToolSystem.cs index 86b91dcda4..4c7383a38c 100644 --- a/Content.Shared/Tools/Systems/SharedToolSystem.cs +++ b/Content.Shared/Tools/Systems/SharedToolSystem.cs @@ -31,7 +31,6 @@ public abstract partial class SharedToolSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly TileSystem _tiles = default!; [Dependency] private readonly TurfSystem _turfs = default!; - [Dependency] protected readonly SharedSolutionContainerSystem SolutionContainer = default!; public const string CutQuality = "Cutting"; public const string PulseQuality = "Pulsing"; diff --git a/Resources/Audio/Machines/airlock_electrify_off.ogg b/Resources/Audio/Machines/airlock_electrify_off.ogg new file mode 100644 index 0000000000..c88d00299e Binary files /dev/null and b/Resources/Audio/Machines/airlock_electrify_off.ogg differ diff --git a/Resources/Audio/Machines/airlock_electrify_on.ogg b/Resources/Audio/Machines/airlock_electrify_on.ogg new file mode 100644 index 0000000000..c87b7d3851 Binary files /dev/null and b/Resources/Audio/Machines/airlock_electrify_on.ogg differ diff --git a/Resources/Audio/Machines/airlock_emergencyoff.ogg b/Resources/Audio/Machines/airlock_emergencyoff.ogg new file mode 100644 index 0000000000..5046a75ec2 Binary files /dev/null and b/Resources/Audio/Machines/airlock_emergencyoff.ogg differ diff --git a/Resources/Audio/Machines/airlock_emergencyon.ogg b/Resources/Audio/Machines/airlock_emergencyon.ogg new file mode 100644 index 0000000000..6c8b54a284 Binary files /dev/null and b/Resources/Audio/Machines/airlock_emergencyon.ogg differ diff --git a/Resources/Audio/Machines/attributions.yml b/Resources/Audio/Machines/attributions.yml index b1f9924546..1b4ea74741 100644 --- a/Resources/Audio/Machines/attributions.yml +++ b/Resources/Audio/Machines/attributions.yml @@ -171,3 +171,12 @@ license: "CC0-1.0" copyright: "by Ko4erga" source: "https://github.com/space-wizards/space-station-14/pull/30431" + +- files: + - airlock_emergencyoff.ogg + - airlock_emergencyon.ogg + - airlock_electrify_off.ogg + - airlock_electrify_on.ogg + license: "CC0-1.0" + copyright: "by ScarKy0" + source: "https://github.com/space-wizards/space-station-14/pull/32012" diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 5ccd9dd6e1..1c6155ae0a 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,285 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Fix being able to throw items while your cursor is off-screen. - type: Fix - id: 6947 - time: '2024-07-21T06:13:28.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30164 -- author: metalgearsloth - changes: - - message: Reset the scroll bar in the ghost warp menu whenever you search for a - role. Previously it remained at your previous position and you would have to - scroll up to see the first entry. - type: Tweak - id: 6948 - time: '2024-07-21T06:38:45.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30159 -- author: Blackern5000 - changes: - - message: The syndicate agent's cyborg weapons module now uses syndicate weaponry - rather than NT weaponry. - type: Tweak - id: 6949 - time: '2024-07-21T07:04:33.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26947 -- author: Winkarst-cpu - changes: - - message: Added ambience to the camera routers and telecommunication servers. - type: Add - - message: Now server's and router's ambience stops once they are unpowered. - type: Fix - id: 6950 - time: '2024-07-21T07:22:02.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30091 -- author: Scott Dimeling - changes: - - message: Reduced the number of botanists in some stations, for optimal _workflow_ - type: Tweak - id: 6951 - time: '2024-07-21T07:23:28.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29581 -- author: metalgearsloth - changes: - - message: Escape pods won't show up on shuttle map anymore. - type: Tweak - id: 6952 - time: '2024-07-21T07:23:44.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29758 -- author: IProduceWidgets - changes: - - message: an arabian lamp! - type: Add - id: 6953 - time: '2024-07-21T07:24:28.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27270 -- author: osjarw - changes: - - message: NPCs no longer get stuck trying to pick up anchored pipes. - type: Fix - id: 6954 - time: '2024-07-21T07:28:37.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30061 -- author: The Hands Leader - JoJo cat - changes: - - message: Train map is back into rotation - type: Tweak - id: 6955 - time: '2024-07-21T07:44:18.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30145 -- author: Plykiya - changes: - - message: Syndicate traitor reinforcements are now specialized to be medics, spies, - or thieves. - type: Add - - message: Reinforcement radios with options now have a radial menu, similar to - RCDs. - type: Tweak - id: 6956 - time: '2024-07-21T10:32:25.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29853 -- author: Cojoke-dot - changes: - - message: Dead Space Dragons no long despawn - type: Fix - id: 6957 - time: '2024-07-21T10:46:33.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29842 -- author: slarticodefast - changes: - - message: Fixed microwave construction. - type: Fix - id: 6958 - time: '2024-07-21T16:20:09.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30232 -- author: Sphiral&Kezu - changes: - - message: 'Added a variety of new wall based storages: Shelfs! Build some today!' - type: Add - id: 6959 - time: '2024-07-21T17:16:58.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27858 -- author: valquaint, slarticodefast - changes: - - message: Fixed borgs being unable to state laws with an activated flashlight. - type: Fix - id: 6960 - time: '2024-07-22T03:55:35.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30183 -- author: Lank - changes: - - message: Darts can now pop balloons. Keep them away from any monkeys. - type: Add - id: 6961 - time: '2024-07-22T05:38:56.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30088 -- author: Plykiya - changes: - - message: You can now eat or drink and swap hands without it being interrupted. - type: Tweak - id: 6962 - time: '2024-07-22T09:17:57.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30060 -- author: IProduceWidgets - changes: - - message: Zookeepers can now possess Nonlethal shotguns according to spacelaw. - type: Tweak - id: 6963 - time: '2024-07-22T09:33:03.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30237 -- author: Plykiya - changes: - - message: Bag sounds can now only be heard from half the distance and is quieter - in general. - type: Tweak - id: 6964 - time: '2024-07-22T09:54:15.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30225 -- author: osjarw - changes: - - message: Syringes are now 0.5 seconds faster. - type: Tweak - id: 6965 - time: '2024-07-22T10:20:36.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29825 -- author: Errant - changes: - - message: Replay observers now always spawn on the station. - type: Fix - id: 6966 - time: '2024-07-22T19:32:30.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30252 -- author: Cojoke-dot - changes: - - message: You can now read the volume of a gas tank in its examine text - type: Tweak - id: 6967 - time: '2024-07-22T21:41:42.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29771 -- author: Cojoke-dot - changes: - - message: Throwing a jetpack mid-flight will no longer freeze your character - type: Fix - id: 6968 - time: '2024-07-22T22:24:26.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30223 -- author: Flareguy - changes: - - message: Added vox sprites for a few headwear items, including radiation suits - and the paramedic helmet. - type: Add - id: 6969 - time: '2024-07-23T02:18:33.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30150 -- author: Cojoke-dot - changes: - - message: You can no longer use telescreens and televisions while blind or asleep. - type: Fix - id: 6970 - time: '2024-07-23T02:33:41.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30260 -- author: Cojoke-dot - changes: - - message: Fix one of the QSI popups - type: Fix - id: 6971 - time: '2024-07-23T03:23:04.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30265 -- author: Errant - changes: - - message: Players are now notified when trying to insert an incompatible magazine - into a gun. - type: Add - id: 6972 - time: '2024-07-23T06:36:06.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29046 -- author: TheKittehJesus - changes: - - message: The Syndicate Assault Borg can now wield their double esword - type: Fix - id: 6973 - time: '2024-07-23T08:13:18.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30229 -- author: Scribbles0 - changes: - - message: Handless mobs can no longer wipe devices like positronic brains or pAIs. - type: Fix - id: 6974 - time: '2024-07-23T17:47:08.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30149 -- author: Quantus - changes: - - message: Reagent grinders can no longer auto-grind when unpowered. - type: Fix - id: 6975 - time: '2024-07-23T21:02:07.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30267 -- author: BombasterDS - changes: - - message: Fixed items disappearing after shelfs and mannequin disassembling - type: Fix - id: 6976 - time: '2024-07-24T08:57:03.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30313 -- author: Cojoke-dot - changes: - - message: Fix infinite QSI linking range - type: Fix - id: 6977 - time: '2024-07-24T20:57:45.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30332 -- author: deltanedas - changes: - - message: Borgs can no longer unlock the robotics console or other borgs. - type: Tweak - id: 6978 - time: '2024-07-25T03:54:52.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27888 -- author: themias - changes: - - message: Fixed the ripley control panel not loading - type: Fix - id: 6979 - time: '2024-07-25T05:23:53.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30325 -- author: Timur2011 - changes: - - message: Space adders are now butcherable. - type: Add - - message: Snakes now drop snake meat when butchered. - type: Fix - - message: Snakes now appear lying when in critical state. - type: Tweak - id: 6980 - time: '2024-07-25T10:52:18.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29629 -- author: Plykiya - changes: - - message: You can now build atmos gas pipes through things like walls. - type: Tweak - id: 6981 - time: '2024-07-25T23:26:06.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/28707 -- author: Ilya246 - changes: - - message: Nuclear operative reinforcements now come with full nuclear operative - gear (and a toy carp) at no additional cost. - type: Tweak - - message: Nuclear operative reinforcements now get nuclear operative names. - type: Tweak - id: 6982 - time: '2024-07-25T23:37:54.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30173 -- author: Cojoke-dot - changes: - - message: Engineering goggles and other similar-looking eyewear now help block - identity. - type: Tweak - - message: Radiation suit's hood now blocks identity. - type: Fix - id: 6983 - time: '2024-07-26T05:26:05.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30305 - author: Moomoobeef changes: - message: Some radio channel colors have been tweaked in order to be more easily @@ -3937,3 +3656,284 @@ id: 7446 time: '2024-09-27T07:12:10.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/32478 +- author: Fildrance, ScarKy0 + changes: + - message: AI now can electrify doors and set them to emergency access. Setting + door to emergency access will now play sound effect for everyone around, while + electrifying door will play sound effect only for AI + type: Add + - message: AI will now be notified when trying to interact with door without power + or when respective wires were messed up with + type: Fix + id: 7447 + time: '2024-09-27T07:22:17.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32012 +- author: LittleNorthStar + changes: + - message: Noir trenchcoat to detective loadout + type: Add + id: 7448 + time: '2024-09-27T10:03:55.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32380 +- author: deltanedas + changes: + - message: Removed the thief figurines objective. + type: Remove + id: 7449 + time: '2024-09-27T19:04:22.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32413 +- author: Beck Thompson + changes: + - message: The appraisal tool verb is now predicted! + type: Fix + id: 7450 + time: '2024-09-28T04:40:24.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32496 +- author: Pyvik + changes: + - message: Added 2 new hairstyles. + type: Add + id: 7451 + time: '2024-09-28T06:07:51.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/31010 +- author: metalgearsloth + changes: + - message: Fix a lot of jankiness around airlocks. + type: Fix + id: 7452 + time: '2024-09-28T09:02:43.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32483 +- author: ElectroJr + changes: + - message: Fixed a store currency duplication bug/exploit. + type: Fix + id: 7453 + time: '2024-09-29T12:13:22.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32524 +- author: ElectroJr + changes: + - message: There is now a rate limit for most interactions. It should not be noticeable + most of the time, but may lead to mispredicts when spam-clicking. + type: Tweak + id: 7454 + time: '2024-09-29T12:19:00.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32527 +- author: ArtisticRoomba + changes: + - message: HoS's energy shotgun is now correctly marked as grand theft contraband. + type: Tweak + id: 7455 + time: '2024-09-29T12:22:57.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32521 +- author: Ilya246 + changes: + - message: Steel cost of conveyor belt assemblies halved. + type: Tweak + id: 7456 + time: '2024-09-29T15:18:09.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32444 +- author: CuteMoonGod + changes: + - message: Fixed execution system showing character name instead of their identity + type: Fix + id: 7457 + time: '2024-09-29T22:36:47.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32536 +- author: drakewill-CRL + changes: + - message: Fix error in gas exchange processing order. + type: Fix + id: 7458 + time: '2024-09-30T05:14:07.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32088 +- author: qwerltaz + changes: + - message: AI can now use fire alarms. + type: Add + id: 7459 + time: '2024-09-30T09:56:05.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32467 +- author: BramvanZijp + changes: + - message: The Engineering Cyborg's Advanced Tool Module now has jaws of life and + a power drill instead of an omnitool, with a multitool replacing the network + configurator. + type: Tweak + id: 7460 + time: '2024-09-30T17:38:22.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32487 +- author: pofitlo-Git + changes: + - message: Added a camera bug in the syndicate uplink that cost 4 TC and allow you + watch all cameras on station. + type: Add + id: 7461 + time: '2024-09-30T22:24:37.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/30250 +- author: kosticia + changes: + - message: The maximum number of ID cards required to complete a thief's objective + has been changed from 15 to 10. + type: Tweak + id: 7462 + time: '2024-09-30T22:40:31.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32411 +- author: Golinth + changes: + - message: Firebots can now be constructed by scientists - the bots wander the station + looking for fires to put out with their built-in extinguisher. + type: Add + id: 7463 + time: '2024-10-01T03:13:16.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32482 +- author: ScarKy0 + changes: + - message: Binary encryption key now uses an AI icon + type: Tweak + id: 7464 + time: '2024-10-01T04:03:37.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32327 +- author: BramvanZijp + changes: + - message: Fixed borgs being able to be briefly disabled by others. + type: Fix + id: 7465 + time: '2024-10-01T15:13:59.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32485 +- author: BackeTako + changes: + - message: Gay Pin + type: Add + id: 7466 + time: '2024-10-01T21:07:48.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32584 +- author: BackeTako + changes: + - message: Reinforced walls sprite see throu top + type: Fix + id: 7467 + time: '2024-10-01T21:44:47.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32578 +- author: Zylo + changes: + - message: Seismic charges being uncraftable + type: Fix + id: 7468 + time: '2024-10-02T02:56:49.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32459 +- author: slarticodefast + changes: + - message: Fixed the chameleon settings menu not showing up for the voice mask. + type: Fix + id: 7469 + time: '2024-10-02T03:22:09.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32546 +- author: metalgearsloth + changes: + - message: Fix physics sensors (e.g. proximity triggers) being able to block doors. + type: Fix + id: 7470 + time: '2024-10-02T05:00:48.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32591 +- author: Plykiya + changes: + - message: You can now quick-swap uneven stacks of items into your inventory without + it getting "stuck" in your hands. + type: Fix + id: 7471 + time: '2024-10-02T05:27:01.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32560 +- author: BackeTako + changes: + - message: New hydroponics doors + type: Add + id: 7472 + time: '2024-10-02T05:33:35.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32575 +- author: BackeTako + changes: + - message: Red circuit tile + type: Add + id: 7473 + time: '2024-10-02T05:35:48.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32557 +- author: ArchRBX + changes: + - message: The MedTek PDA cartridge has now been added, providing health analyzer + functionality to PDA's + type: Add + - message: The MedTek cartridge has been added to the CMO locker, and comes preinstalled + on medical PDA's, replacing the built-in analyzer functionality on these PDA's + type: Add + id: 7474 + time: '2024-10-02T06:17:57.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32450 +- author: Toly65 + changes: + - message: fixed slippery and bioluminescent effects persisting on planters after + the plant has been harvested + type: Fix + - message: removed the slippery and bioluminescent visuals from planters temporararily + type: Remove + id: 7475 + time: '2024-10-02T09:37:49.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32576 +- author: lzk228 + changes: + - message: Star sticker can be used in chameleon menu. + type: Fix + id: 7476 + time: '2024-10-02T10:53:19.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32594 +- author: deltanedas + changes: + - message: Fixed the Instigator shuttle event never happening. + type: Fix + id: 7477 + time: '2024-10-02T11:44:12.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32597 +- author: FluffMe + changes: + - message: Fixed accidental erase of paper contents by spamming save action. + type: Fix + id: 7478 + time: '2024-10-02T12:00:31.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32598 +- author: PJB3005 + changes: + - message: Removed bioluminescence plant mutations due to it breaking the rendering + engine. + type: Remove + id: 7479 + time: '2024-10-02T13:52:14.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32561 +- author: PJB3005 + changes: + - message: Fixed borg "hands" showing up in their stripping menu. + type: Fix + id: 7480 + time: '2024-10-03T00:11:56.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32606 +- author: ArtisticRoomba + changes: + - message: X-ray cannons and stinger grenades are now security restricted. HoS's + armored trench coat and its variants are security and command restricted. + type: Fix + id: 7481 + time: '2024-10-03T10:01:58.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32614 +- author: Myra + changes: + - message: Medibots and Janibots can no longer become sentient via the sentience + event. + type: Remove + id: 7482 + time: '2024-10-03T10:32:11.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32383 +- author: eoineoineoin + changes: + - message: Action bar can be reconfigured again + type: Fix + id: 7483 + time: '2024-10-03T14:01:01.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32552 diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index 21d0f430bd..b18c4646ed 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0x6273, 12rabbits, 13spacemen, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 3nderall, 4310v343k, 4dplanner, 612git, 778b, Ablankmann, abregado, Absolute-Potato, achookh, Acruid, actioninja, actually-reb, ada-please, adamsong, Adeinitas, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aerocrux, Aeshus, Aexolott, Aexxie, africalimedrop, Afrokada, Agoichi, Ahion, aiden, AJCM-git, AjexRose, Alekshhh, alexkar598, AlexMorgan3817, alexumandxgabriel08x, Alithsko, ALMv1, Alpha-Two, AlphaQwerty, Altoids1, amylizzle, ancientpower, Andre19926, AndrewEyeke, AndreyCamper, Anzarot121, Appiah, ar4ill, ArchPigeon, ArchRBX, areitpog, Arendian, arimah, Arkanic, ArkiveDev, armoks, Arteben, ArthurMousatov, ArtisticRoomba, artur, AruMoon, ArZarLordOfMango, as334, AsikKEsel, AsnDen, asperger-sind, aspiringLich, astriloqua, august-sun, AutoOtter, avghdev, Awlod, AzzyIsNotHere, BackeTako, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, bellwetherlogic, benev0, benjamin-burges, BGare, bhenrich, bhespiritu, bibbly, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, BlitzTheSquishy, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, BombasterDS, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, BriBrooo, Bright0, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, bvelliquette, byondfuckery, c0rigin, c4llv07e, CaasGit, Caconym27, Calecute, Callmore, capnsockless, CaptainSqrBeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, CatTheSystem, Centronias, chairbender, Charlese2, ChaseFlorom, chavonadelal, Cheackraze, cheesePizza2, cheeseplated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, civilCornball, Clement-O, clyf, Clyybber, CMDR-Piboy314, cohanna, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, CookieMasterT, coolboy911, coolmankid12345, Coolsurf6, corentt, CormosLemming, crazybrain23, creadth, CrigCrag, croilbird, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DakotaGay, DamianX, DangerRevolution, daniel-cr, DanSAussieITS, Daracke, Darkenson, DawBla, Daxxi3, dch-GH, de0rix, Deahaka, dean, DEATHB4DEFEAT, DeathCamel58, Deatherd, deathride58, DebugOk, Decappi, Decortex, Deeeeja, deepdarkdepths, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, dexlerxd, dffdff2423, DieselMohawk, digitalic, Dimastra, DinoWattz, DisposableCrewmember42, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DoctorBeard, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, Dynexust, Easypoller, echo, eclips_e, eden077, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, Emisse, emmafornash, EmoGarbage404, Endecc, eoineoineoin, eris, erohrs2, ERORR404V1, Errant-4, ertanic, esguard, estacaoespacialpirata, eugene, exincore, exp111, f0x-n3rd, FacePluslll, Fahasor, FairlySadPanda, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, FillerVK, FinnishPaladin, FirinMaLazors, Fishfish458, FL-OZ, Flareguy, flashgnash, FluffiestFloof, FluidRock, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, Fouin, foxhorn, freeman2651, freeze2222, Froffy025, Fromoriss, froozigiusz, FrostMando, FungiFellow, FunTust, Futuristic-OK, GalacticChimp, Gaxeer, gbasood, Geekyhobo, genderGeometries, GeneralGaws, Genkail, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, gituhabu, GlassEclipse, GNF54, godisdeadLOL, goet, Goldminermac, Golinth, GoodWheatley, Gorox221, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, Hardly3D, harikattar, he1acdvv, Hebi, Henry, HerCoyote23, hitomishirichan, hiucko, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, Hreno, hubismal, Hugal31, Huxellberger, Hyenh, i-justuser-i, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, imrenq, imweax, indeano, Injazz, Insineer, IntegerTempest, Interrobang01, IProduceWidgets, ItsMeThom, Itzbenz, iztokbajcar, Jackal298, Jackrost, jacksonzck, Jackw2As, jacob, jamessimo, janekvap, Jark255, Jaskanbe, JasperJRoth, jerryimmouse, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, JoeHammad1844, joelsgp, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, KaiShibaa, kalane15, kalanosh, Kanashi-Panda, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, Kimpes, KingFroozy, kira-er, Kirillcas, Kirus59, Kistras, Kit0vras, KittenColony, klaypexx, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, koteq, KrasnoshchekovPavel, Krunklehorn, Kukutis96513, Kupie, kxvvv, kyupolaris, kzhanik, lajolico, Lamrr, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, leander-0, leonardo-dabepis, leonsfriedrich, LeoSantich, LetterN, lettern, Level10Cybermancer, LEVELcat, lever1209, Lgibb18, lgruthes, LightVillet, liltenhead, LinkUyx, LittleBuilderJane, lizelive, localcc, lokachop, Lomcastar, LordCarve, LordEclipse, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luizwritescode, Lukasz825700516, luminight, lunarcomets, luringens, lvvova1, Lyndomen, lyroth001, lzimann, lzk228, M3739, mac6na6na, MACMAN2003, Macoron, Magicalus, magmodius, MagnusCrowe, malchanceux, MaloTV, ManelNavola, Mangohydra, marboww, Markek1, Matz05, max, MaxNox7, maylokana, MehimoNemo, MeltedPixel, MemeProof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, MilenVolf, milon, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MissKay1994, MisterMecky, Mith-randalf, MjrLandWhale, mkanke-real, MLGTASTICa, moderatelyaware, modern-nm, mokiros, Moneyl, Moomoobeef, moony, Morb0, mr-bo-jangles, Mr0maks, MrFippik, mrrobdemo, MureixloI, musicmanvr, MWKane, Myakot, Myctai, N3X15, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, NazrinNya, neutrino-laser, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, NIXC, NkoKirkto, nmajask, noctyrnal, nok-ko, NonchalantNoob, NoobyLegion, Nopey, not-gavnaed, notafet, notquitehadouken, NotSoDana, noudoit, noverd, NuclearWinter, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, och-och, OctoRocket, OldDanceJacket, OliverOtter, onoira, OnyxTheBrave, OrangeMoronage9622, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, paigemaeforrest, pali6, Pangogie, panzer-iv1, partyaddict, patrikturi, PaulRitter, peccneck, Peptide90, peptron1, PeterFuto, PetMudstone, pewter-wiz, Pgriha, Phantom-Lily, Phill101, phunnyguy, pigeonpeas, PilgrimViis, Pill-U, Pireax, Pissachu, pissdemon, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, pok27, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, ProfanedBane, ProPandaBear, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykzz, PuceTint, PuroSlavKing, PursuitInAshes, Putnam3145, quatre, QueerNB, QuietlyWhisper, qwerltaz, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, RiceMar1244, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, RobbyTheFish, Rockdtben, Rohesie, rok-povsic, rolfero, RomanNovo, rosieposieeee, Roudenn, router, RumiTiger, S1rFl0, S1ss3l, Saakra, saga3152, saintmuntzer, Salex08, sam, samgithubaccount, SaphireLattice, SapphicOverload, Sarahon, sativaleanne, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, scrato, Scribbles0, scruq445, scuffedjays, ScumbagDog, Segonist, sephtasm, Serkket, sewerpig, sh18rw, ShadeAware, ShadowCommander, Shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SignalWalker, siigiil, Simyon264, sirdragooon, Sirionaut, Sk1tch, SkaldetSkaeg, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, Slyfox333, snebl, snicket, sniperchance, Snowni, snowsignal, SolidusSnek, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, SpaceManiac, SpaceyLady, spanky-spanky, spartak, SpartanKadence, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, Stealthbomber16, stellar-novas, stopbreaking, stopka-html, StrawberryMoses, Stray-Pyramid, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, superjj18, Supernorn, SweptWasTaken, Sybil, SYNCHRONIC, Szunti, Tainakov, takemysoult, TaralGit, Taran, taurie, Tayrtahn, tday93, TekuNut, telyonok, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, TGODiamond, TGRCdev, tgrkzus, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, theashtronaut, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, TheIntoxicatedCat, thekilk, themias, Theomund, theOperand, TherapyGoth, TheShuEd, thetolbean, thevinter, TheWaffleJesus, Thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, Titian3, tk-a369, tkdrg, tmtmtl30, TokenStyle, Tollhouse, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, Tornado-Technology, tosatur, TotallyLemon, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, TyAshley, Tyler-IN, Tyzemol, UbaserB, ubis1, UBlueberry, UKNOWH, UltimateJester, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, Unkn0wnGh0st333, unusualcrow, Uriende, UristMcDorf, user424242420, Vaaankas, valentfingerov, Varen, VasilisThePikachu, veliebm, VelonacepsCalyxEggs, veprolet, veritable-calamity, Veritius, Vermidia, vero5123, Verslebas, VigersRay, violet754, Visne, VMSolidus, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, vulppine, wafehling, Warentan, WarMechanic, Watermelon914, waylon531, weaversam8, wertanchik, whateverusername0, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, wrexbe, wtcwr68, xkreksx, xprospero, xRiriq, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, Yousifb26, youtissoum, YuriyKiss, zach-hill, Zadeon, zamp, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zero, ZeroDiamond, zerorulez, ZeWaka, zionnBE, ZNixian, ZoldorfTheWizard, Zonespace27, Zumorica, Zymem, zzylex +0x6273, 12rabbits, 13spacemen, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 3nderall, 4310v343k, 4dplanner, 612git, 778b, Ablankmann, abregado, Absolute-Potato, achookh, Acruid, actioninja, actually-reb, ada-please, adamsong, Adeinitas, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aerocrux, Aeshus, Aexolott, Aexxie, africalimedrop, Afrokada, Agoichi, Ahion, aiden, AJCM-git, AjexRose, Alekshhh, alexkar598, AlexMorgan3817, alexumandxgabriel08x, Alithsko, ALMv1, Alpha-Two, AlphaQwerty, Altoids1, amylizzle, ancientpower, Andre19926, AndrewEyeke, AndreyCamper, Anzarot121, Appiah, ar4ill, ArchPigeon, ArchRBX, areitpog, Arendian, arimah, Arkanic, ArkiveDev, armoks, Arteben, ArthurMousatov, ArtisticRoomba, artur, AruMoon, ArZarLordOfMango, as334, AsikKEsel, AsnDen, asperger-sind, aspiringLich, astriloqua, august-sun, AutoOtter, avghdev, Awlod, AzzyIsNotHere, BackeTako, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, bellwetherlogic, benev0, benjamin-burges, BGare, bhenrich, bhespiritu, bibbly, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, BlitzTheSquishy, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, BombasterDS, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, BriBrooo, Bright0, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, bvelliquette, byondfuckery, c0rigin, c4llv07e, CaasGit, Caconym27, Calecute, Callmore, capnsockless, CaptainSqrBeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, CatTheSystem, Centronias, chairbender, Charlese2, ChaseFlorom, chavonadelal, Cheackraze, cheesePizza2, cheeseplated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, civilCornball, Clement-O, clyf, Clyybber, CMDR-Piboy314, cohanna, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, CookieMasterT, coolboy911, coolmankid12345, Coolsurf6, corentt, CormosLemming, crazybrain23, creadth, CrigCrag, croilbird, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DakotaGay, DamianX, DangerRevolution, daniel-cr, DanSAussieITS, Daracke, Darkenson, DawBla, Daxxi3, dch-GH, de0rix, Deahaka, dean, DEATHB4DEFEAT, DeathCamel58, Deatherd, deathride58, DebugOk, Decappi, Decortex, Deeeeja, deepdarkdepths, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, dexlerxd, dffdff2423, DieselMohawk, digitalic, Dimastra, DinoWattz, DisposableCrewmember42, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DoctorBeard, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, Dynexust, Easypoller, echo, eclips_e, eden077, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, Emisse, emmafornash, EmoGarbage404, Endecc, eoineoineoin, eris, erohrs2, ERORR404V1, Errant-4, ertanic, esguard, estacaoespacialpirata, eugene, exincore, exp111, f0x-n3rd, FacePluslll, Fahasor, FairlySadPanda, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, FillerVK, FinnishPaladin, FirinMaLazors, Fishfish458, FL-OZ, Flareguy, flashgnash, FluffiestFloof, FluidRock, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, Fouin, foxhorn, freeman2651, freeze2222, Froffy025, Fromoriss, froozigiusz, FrostMando, FungiFellow, FunTust, Futuristic-OK, GalacticChimp, Gaxeer, gbasood, Geekyhobo, genderGeometries, GeneralGaws, Genkail, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, gituhabu, GlassEclipse, GNF54, godisdeadLOL, goet, Goldminermac, Golinth, GoodWheatley, Gorox221, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, Hardly3D, harikattar, he1acdvv, Hebi, Henry, HerCoyote23, hitomishirichan, hiucko, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, Hreno, hubismal, Hugal31, Huxellberger, Hyenh, i-justuser-i, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, imrenq, imweax, indeano, Injazz, Insineer, IntegerTempest, Interrobang01, IProduceWidgets, ItsMeThom, Itzbenz, iztokbajcar, Jackal298, Jackrost, jacksonzck, Jackw2As, jacob, jamessimo, janekvap, Jark255, Jaskanbe, JasperJRoth, jerryimmouse, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, JoeHammad1844, joelsgp, JohnGinnane, johnku1, Jophire, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, KaiShibaa, kalane15, kalanosh, Kanashi-Panda, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, Kimpes, KingFroozy, kira-er, Kirillcas, Kirus59, Kistras, Kit0vras, KittenColony, klaypexx, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, koteq, KrasnoshchekovPavel, Krunklehorn, Kukutis96513, Kupie, kxvvv, kyupolaris, kzhanik, lajolico, Lamrr, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, leander-0, leonardo-dabepis, leonsfriedrich, LeoSantich, LetterN, lettern, Level10Cybermancer, LEVELcat, lever1209, Lgibb18, lgruthes, LightVillet, liltenhead, LinkUyx, LittleBuilderJane, LittleNorthStar, lizelive, localcc, lokachop, Lomcastar, LordCarve, LordEclipse, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luizwritescode, Lukasz825700516, luminight, lunarcomets, luringens, lvvova1, Lyndomen, lyroth001, lzimann, lzk228, M3739, mac6na6na, MACMAN2003, Macoron, Magicalus, magmodius, MagnusCrowe, malchanceux, MaloTV, ManelNavola, Mangohydra, marboww, Markek1, Matz05, max, MaxNox7, maylokana, MehimoNemo, MeltedPixel, MemeProof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, MilenVolf, MilonPL, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MissKay1994, MisterMecky, Mith-randalf, MjrLandWhale, mkanke-real, MLGTASTICa, moderatelyaware, modern-nm, mokiros, Moneyl, Moomoobeef, moony, Morb0, mr-bo-jangles, Mr0maks, MrFippik, mrrobdemo, MureixloI, musicmanvr, MWKane, Myakot, Myctai, N3X15, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, NazrinNya, neutrino-laser, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, NIXC, NkoKirkto, nmajask, noctyrnal, nok-ko, NonchalantNoob, NoobyLegion, Nopey, not-gavnaed, notafet, notquitehadouken, NotSoDana, noudoit, noverd, NuclearWinter, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, och-och, OctoRocket, OldDanceJacket, OliverOtter, onoira, OnyxTheBrave, OrangeMoronage9622, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, paigemaeforrest, pali6, Pangogie, panzer-iv1, partyaddict, patrikturi, PaulRitter, peccneck, Peptide90, peptron1, PeterFuto, PetMudstone, pewter-wiz, Pgriha, Phantom-Lily, Phill101, phunnyguy, pigeonpeas, PilgrimViis, Pill-U, Pireax, Pissachu, pissdemon, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, pok27, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, ProfanedBane, ProPandaBear, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykzz, PuceTint, PuroSlavKing, PursuitInAshes, Putnam3145, quatre, QueerNB, QuietlyWhisper, qwerltaz, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, RiceMar1244, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, RobbyTheFish, Rockdtben, Rohesie, rok-povsic, rolfero, RomanNovo, rosieposieeee, Roudenn, router, RumiTiger, S1rFl0, S1ss3l, Saakra, saga3152, saintmuntzer, Salex08, sam, samgithubaccount, SaphireLattice, SapphicOverload, Sarahon, sativaleanne, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, scrato, Scribbles0, scruq445, scuffedjays, ScumbagDog, Segonist, sephtasm, Serkket, sewerpig, sh18rw, ShadeAware, ShadowCommander, Shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SignalWalker, siigiil, Simyon264, sirdragooon, Sirionaut, Sk1tch, SkaldetSkaeg, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, Slyfox333, snebl, snicket, sniperchance, Snowni, snowsignal, SolidusSnek, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, Soydium, SpaceManiac, SpaceyLady, spanky-spanky, spartak, SpartanKadence, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, Stealthbomber16, stellar-novas, stopbreaking, stopka-html, StrawberryMoses, Stray-Pyramid, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, superjj18, Supernorn, SweptWasTaken, Sybil, SYNCHRONIC, Szunti, Tainakov, takemysoult, TaralGit, Taran, taurie, Tayrtahn, tday93, TekuNut, telyonok, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, TGODiamond, TGRCdev, tgrkzus, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, theashtronaut, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, TheIntoxicatedCat, thekilk, themias, Theomund, theOperand, TherapyGoth, TheShuEd, thetolbean, thevinter, TheWaffleJesus, Thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, Titian3, tk-a369, tkdrg, tmtmtl30, TokenStyle, Tollhouse, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, Tornado-Technology, tosatur, TotallyLemon, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, TyAshley, Tyler-IN, Tyzemol, UbaserB, ubis1, UBlueberry, UKNOWH, UltimateJester, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, Unkn0wnGh0st333, unusualcrow, Uriende, UristMcDorf, user424242420, Vaaankas, valentfingerov, Varen, VasilisThePikachu, veliebm, VelonacepsCalyxEggs, veprolet, veritable-calamity, Veritius, Vermidia, vero5123, Verslebas, VigersRay, violet754, Visne, VMSolidus, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, vulppine, wafehling, Warentan, WarMechanic, Watermelon914, waylon531, weaversam8, wertanchik, whateverusername0, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, wrexbe, wtcwr68, xkreksx, xprospero, xRiriq, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, Yousifb26, youtissoum, YuriyKiss, zach-hill, Zadeon, zamp, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zero, ZeroDiamond, zerorulez, ZeWaka, zionnBE, ZNixian, ZoldorfTheWizard, Zonespace27, Zumorica, Zylofan, Zymem, zzylex diff --git a/Resources/Locale/en-US/accessories/human-hair.ftl b/Resources/Locale/en-US/accessories/human-hair.ftl index 9575cd5c2f..58314f9bb2 100644 --- a/Resources/Locale/en-US/accessories/human-hair.ftl +++ b/Resources/Locale/en-US/accessories/human-hair.ftl @@ -97,6 +97,7 @@ marking-HumanHairJensen = Jensen Hair marking-HumanHairJoestar = Joestar marking-HumanHairKeanu = Keanu Hair marking-HumanHairKusanagi = Kusanagi Hair +marking-HumanHairLongBow = Long Bow marking-HumanHairLong = Long Hair 1 marking-HumanHairLong2 = Long Hair 2 marking-HumanHairLong3 = Long Hair 3 @@ -147,6 +148,7 @@ marking-HumanHairSpikyponytail = Ponytail (Spiky) marking-HumanHairPoofy = Poofy marking-HumanHairQuiff = Quiff marking-HumanHairRonin = Ronin +marking-HumanHairShaped = Shaped marking-HumanHairShaved = Shaved marking-HumanHairShavedpart = Shaved Part marking-HumanHairShortbangs = Short Bangs diff --git a/Resources/Locale/en-US/administration/commands/custom-vote-command.ftl b/Resources/Locale/en-US/administration/commands/custom-vote-command.ftl deleted file mode 100644 index 221f0629a5..0000000000 --- a/Resources/Locale/en-US/administration/commands/custom-vote-command.ftl +++ /dev/null @@ -1,5 +0,0 @@ -custom-vote-webhook-name = Custom Vote Held -custom-vote-webhook-footer = server: { $serverName }, round: { $roundId } { $runLevel } -custom-vote-webhook-cancelled = **Vote cancelled** -custom-vote-webhook-option-pending = TBD -custom-vote-webhook-option-cancelled = N/A diff --git a/Resources/Locale/en-US/cartridge-loader/cartridges.ftl b/Resources/Locale/en-US/cartridge-loader/cartridges.ftl index 804da0992f..ceeac1be3c 100644 --- a/Resources/Locale/en-US/cartridge-loader/cartridges.ftl +++ b/Resources/Locale/en-US/cartridge-loader/cartridges.ftl @@ -22,6 +22,8 @@ log-probe-label-number = # astro-nav-program-name = AstroNav +med-tek-program-name = MedTek + # Wanted list cartridge wanted-list-program-name = Wanted list wanted-list-label-no-records = It's all right, cowboy diff --git a/Resources/Locale/en-US/discord/vote-notifications.ftl b/Resources/Locale/en-US/discord/vote-notifications.ftl new file mode 100644 index 0000000000..f6779cac83 --- /dev/null +++ b/Resources/Locale/en-US/discord/vote-notifications.ftl @@ -0,0 +1,11 @@ +custom-vote-webhook-name = Custom Vote Held +custom-vote-webhook-footer = server: { $serverName }, round: { $roundId } { $runLevel } +custom-vote-webhook-cancelled = **Vote cancelled** +custom-vote-webhook-option-pending = TBD +custom-vote-webhook-option-cancelled = N/A + +votekick-webhook-name = Votekick Held +votekick-webhook-description = Initiator: { $initiator }; Target: { $target } +votekick-webhook-cancelled-admin-online = **Vote cancelled due to admins online** +votekick-webhook-cancelled-admin-target = **Vote cancelled due to target being admin** +votekick-webhook-cancelled-antag-target = **Vote cancelled due to target being antag** diff --git a/Resources/Locale/en-US/execution/execution.ftl b/Resources/Locale/en-US/execution/execution.ftl index 08f9a06dd4..1eb914a147 100644 --- a/Resources/Locale/en-US/execution/execution.ftl +++ b/Resources/Locale/en-US/execution/execution.ftl @@ -6,12 +6,12 @@ execution-verb-message = Use your weapon to execute someone. # victim (the person being executed) # weapon (the weapon used for the execution) -execution-popup-melee-initial-internal = You ready {THE($weapon)} against {$victim}'s throat. -execution-popup-melee-initial-external = {$attacker} readies {POSS-ADJ($attacker)} {$weapon} against the throat of {$victim}. -execution-popup-melee-complete-internal = You slit the throat of {$victim}! -execution-popup-melee-complete-external = {$attacker} slits the throat of {$victim}! +execution-popup-melee-initial-internal = You ready {THE($weapon)} against {THE($victim)}'s throat. +execution-popup-melee-initial-external = { CAPITALIZE(THE($attacker)) } readies {POSS-ADJ($attacker)} {$weapon} against the throat of {THE($victim)}. +execution-popup-melee-complete-internal = You slit the throat of {THE($victim)}! +execution-popup-melee-complete-external = { CAPITALIZE(THE($attacker)) } slits the throat of {THE($victim)}! execution-popup-self-initial-internal = You ready {THE($weapon)} against your own throat. -execution-popup-self-initial-external = {$attacker} readies {POSS-ADJ($attacker)} {$weapon} against their own throat. +execution-popup-self-initial-external = { CAPITALIZE(THE($attacker)) } readies {POSS-ADJ($attacker)} {$weapon} against their own throat. execution-popup-self-complete-internal = You slit your own throat! -execution-popup-self-complete-external = {$attacker} slits their own throat! \ No newline at end of file +execution-popup-self-complete-external = { CAPITALIZE(THE($attacker)) } slits their own throat! diff --git a/Resources/Locale/en-US/interaction/interaction-popup-component.ftl b/Resources/Locale/en-US/interaction/interaction-popup-component.ftl index 10773d6de8..a180b698fa 100644 --- a/Resources/Locale/en-US/interaction/interaction-popup-component.ftl +++ b/Resources/Locale/en-US/interaction/interaction-popup-component.ftl @@ -60,6 +60,7 @@ petting-success-honkbot = You pet {THE($target)} on {POSS-ADJ($target)} slippery petting-success-mimebot = You pet {THE($target)} on {POSS-ADJ($target)} cold metal head. petting-success-cleanbot = You pet {THE($target)} on {POSS-ADJ($target)} damp metal head. petting-success-medibot = You pet {THE($target)} on {POSS-ADJ($target)} sterile metal head. +petting-success-firebot = You pet {THE($target)} on {POSS-ADJ($target)} warm metal head. petting-success-generic-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} metal head. petting-success-salvage-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} dirty metal head. petting-success-engineer-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} reflective metal head. @@ -73,6 +74,7 @@ petting-failure-honkbot = You reach out to pet {THE($target)}, but {SUBJECT($tar petting-failure-cleanbot = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy mopping! petting-failure-mimebot = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy miming! petting-failure-medibot = You reach out to pet {THE($target)}, but {POSS-ADJ($target)} syringe nearly stabs your hand! +petting-failure-firebot = You reach out to pet {THE($target)}, but {SUBJECT($target)} sprays you in the face before you can get close! petting-failure-generic-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy stating laws! petting-failure-salvage-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy drilling! petting-failure-engineer-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy repairing! diff --git a/Resources/Locale/en-US/interaction/interaction-system.ftl b/Resources/Locale/en-US/interaction/interaction-system.ftl index a4c380abca..3c0c3ae8b4 100644 --- a/Resources/Locale/en-US/interaction/interaction-system.ftl +++ b/Resources/Locale/en-US/interaction/interaction-system.ftl @@ -1,2 +1,3 @@ shared-interaction-system-in-range-unobstructed-cannot-reach = You can't reach there! -interaction-system-user-interaction-cannot-reach = You can't reach there! \ No newline at end of file +interaction-system-user-interaction-cannot-reach = You can't reach there! +interaction-rate-limit-admin-announcement = Player { $player } breached interaction rate limits. They may be using macros, auto-clickers, or a modified client. Though they may just be spamming buttons or having network issues. diff --git a/Resources/Locale/en-US/npc/firebot.ftl b/Resources/Locale/en-US/npc/firebot.ftl new file mode 100644 index 0000000000..758874ceaa --- /dev/null +++ b/Resources/Locale/en-US/npc/firebot.ftl @@ -0,0 +1 @@ +firebot-fire-detected = Fire detected! diff --git a/Resources/Locale/en-US/paper/syndicate-business-card.ftl b/Resources/Locale/en-US/paper/syndicate-business-card.ftl new file mode 100644 index 0000000000..b4c8c8c43c --- /dev/null +++ b/Resources/Locale/en-US/paper/syndicate-business-card.ftl @@ -0,0 +1,2 @@ +syndicate-business-card-base = {" "} It's nothing personal, it's just business + diff --git a/Resources/Locale/en-US/silicons/station-ai.ftl b/Resources/Locale/en-US/silicons/station-ai.ftl index d51a99ebb0..7d9db3f6dc 100644 --- a/Resources/Locale/en-US/silicons/station-ai.ftl +++ b/Resources/Locale/en-US/silicons/station-ai.ftl @@ -11,4 +11,12 @@ ai-close = Close actions bolt-close = Close bolt bolt-open = Open bolt +emergency-access-on = Enable emergency access +emergency-access-off = Disable emergency access + +electrify-door-on = Enable overcharge +electrify-door-off = Disable overcharge + toggle-light = Toggle light + +ai-device-not-responding = Device is not responding diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 45d7ad2816..e0864de41b 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -443,5 +443,11 @@ uplink-barber-scissors-desc = A good tool to give your fellow agent a nice hairc uplink-backpack-syndicate-name = Syndicate backpack uplink-backpack-syndicate-desc = A lightweight explosion-proof backpack for holding various traitor goods +uplink-cameraBug-name = Camera bug +uplink-cameraBug-desc = A portable device that allows you to view the station's cameras. + uplink-combat-bakery-name = Combat Bakery Kit uplink-combat-bakery-desc = A kit of clandestine baked weapons. Contains a baguette sword, a pair of throwing croissants, and a syndicate microwave board for making more. Once the job is done, eat the evidence. + +uplink-business-card-name = Syndicate business card. +uplink-business-card-desc = A business card that you can give to someone to demonstrate your involvement in the syndicate or leave at the crime scene in order to make fun of the detective. You can buy no more than three of them. diff --git a/Resources/Locale/en-US/tiles/tiles.ftl b/Resources/Locale/en-US/tiles/tiles.ftl index 35cea19f78..b520235614 100644 --- a/Resources/Locale/en-US/tiles/tiles.ftl +++ b/Resources/Locale/en-US/tiles/tiles.ftl @@ -90,6 +90,7 @@ tiles-reinforced-glass-floor = reinforced glass floor tiles-metal-foam = metal foam floor tiles-green-circuit-floor = green circuit floor tiles-blue-circuit-floor = blue circuit floor +tiles-red-circuit-floor = red circuit floor tiles-snow = snow tiles-snow-plating = snowed plating tiles-snow-dug = dug snow diff --git a/Resources/Maps/cog.yml b/Resources/Maps/cog.yml index 562b27f87f..7bc328d798 100644 --- a/Resources/Maps/cog.yml +++ b/Resources/Maps/cog.yml @@ -805,8 +805,6 @@ entities: 5555: -28,58 5779: 31,-8 5786: 31,-2 - 5798: 0,-4 - 5799: -2,-4 6198: 3,-46 6200: 3,-40 6258: 58,-27 @@ -13373,6 +13371,29 @@ entities: - 4266 - 32067 - 32069 + - uid: 32231 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -4.5,-1.5 + parent: 12 + - type: DeviceList + devices: + - 32227 + - 32226 + - 32234 + - 32225 + - 32224 + - 32235 + - uid: 32232 + components: + - type: Transform + pos: -3.5,-9.5 + parent: 12 + - type: DeviceList + devices: + - 32229 + - 10069 - proto: AirAlarmVox entities: - uid: 7822 @@ -13412,16 +13433,6 @@ entities: - type: Transform pos: 38.5,-32.5 parent: 12 - - uid: 7195 - components: - - type: Transform - anchored: True - pos: 0.5,-3.5 - parent: 12 - - type: Physics - bodyType: Static - - type: Lock - locked: True - uid: 8863 components: - type: Transform @@ -18395,6 +18406,12 @@ entities: - type: Transform pos: -9.5,-16.5 parent: 12 + - uid: 145 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-5.5 + parent: 12 - uid: 247 components: - type: Transform @@ -24190,17 +24207,12 @@ entities: - uid: 2217 components: - type: Transform - pos: -0.5,-10.5 + pos: -0.5,0.5 parent: 12 - uid: 2218 components: - type: Transform - pos: -0.5,-9.5 - parent: 12 - - uid: 2219 - components: - - type: Transform - pos: -0.5,-8.5 + pos: -0.5,-0.5 parent: 12 - uid: 2220 components: @@ -24215,7 +24227,7 @@ entities: - uid: 2222 components: - type: Transform - pos: -0.5,-4.5 + pos: -1.5,-1.5 parent: 12 - uid: 2223 components: @@ -24265,18 +24277,13 @@ entities: - uid: 2232 components: - type: Transform - pos: -0.5,-3.5 + pos: 0.5,-1.5 parent: 12 - uid: 2233 components: - type: Transform pos: -0.5,-2.5 parent: 12 - - uid: 2234 - components: - - type: Transform - pos: -0.5,-1.5 - parent: 12 - uid: 2262 components: - type: Transform @@ -36062,6 +36069,11 @@ entities: - type: Transform pos: -20.5,-62.5 parent: 12 + - uid: 20074 + components: + - type: Transform + pos: -0.5,-1.5 + parent: 12 - uid: 20257 components: - type: Transform @@ -43702,6 +43714,11 @@ entities: - type: Transform pos: -63.5,-53.5 parent: 12 + - uid: 32230 + components: + - type: Transform + pos: 1.5,-5.5 + parent: 12 - proto: CableApcStack entities: - uid: 16561 @@ -56175,6 +56192,11 @@ entities: - type: Transform pos: -2.5,-9.5 parent: 12 + - uid: 2234 + components: + - type: Transform + pos: 1.5,-5.5 + parent: 12 - uid: 2246 components: - type: Transform @@ -62365,11 +62387,6 @@ entities: - type: Transform pos: -47.5,17.5 parent: 12 - - uid: 20074 - components: - - type: Transform - pos: 2.5,-5.5 - parent: 12 - uid: 20343 components: - type: Transform @@ -103019,6 +103036,13 @@ entities: rot: 3.141592653589793 rad pos: 0.5,67.5 parent: 12 + - uid: 32228 + components: + - type: Transform + pos: -4.5,-4.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' - proto: GasPipeBend entities: - uid: 928 @@ -103223,12 +103247,6 @@ entities: parent: 12 - type: AtmosPipeColor color: '#990000FF' - - uid: 2124 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -2.5,-1.5 - parent: 12 - uid: 2168 components: - type: Transform @@ -103907,23 +103925,22 @@ entities: rot: -1.5707963267948966 rad pos: 37.5,-32.5 parent: 12 - - uid: 7188 - components: - - type: Transform - pos: -1.5,-1.5 - parent: 12 - uid: 7189 components: - type: Transform rot: -1.5707963267948966 rad - pos: 1.5,-1.5 + pos: 0.5,-3.5 parent: 12 - - uid: 7190 + - type: AtmosPipeColor + color: '#0055CCFF' + - uid: 7194 components: - type: Transform rot: 1.5707963267948966 rad - pos: 0.5,-1.5 + pos: -0.5,-3.5 parent: 12 + - type: AtmosPipeColor + color: '#0055CCFF' - uid: 7205 components: - type: Transform @@ -106210,6 +106227,14 @@ entities: rot: 1.5707963267948966 rad pos: 0.5,68.5 parent: 12 + - uid: 32223 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -4.5,-5.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' - proto: GasPipeFourway entities: - uid: 2577 @@ -106259,6 +106284,20 @@ entities: parent: 12 - type: AtmosPipeColor color: '#0055CCFF' + - uid: 7188 + components: + - type: Transform + pos: -1.5,-5.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' + - uid: 7190 + components: + - type: Transform + pos: -0.5,-6.5 + parent: 12 + - type: AtmosPipeColor + color: '#0055CCFF' - uid: 10894 components: - type: Transform @@ -107744,6 +107783,13 @@ entities: parent: 12 - type: AtmosPipeColor color: '#0055CCFF' + - uid: 2124 + components: + - type: Transform + pos: -1.5,-2.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' - uid: 2128 components: - type: Transform @@ -112564,6 +112610,41 @@ entities: parent: 12 - type: AtmosPipeColor color: '#990000FF' + - uid: 7185 + components: + - type: Transform + pos: -0.5,-4.5 + parent: 12 + - type: AtmosPipeColor + color: '#0055CCFF' + - uid: 7186 + components: + - type: Transform + pos: 0.5,-2.5 + parent: 12 + - type: AtmosPipeColor + color: '#0055CCFF' + - uid: 7192 + components: + - type: Transform + pos: -1.5,-3.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' + - uid: 7193 + components: + - type: Transform + pos: 0.5,-0.5 + parent: 12 + - type: AtmosPipeColor + color: '#0055CCFF' + - uid: 7196 + components: + - type: Transform + pos: -0.5,-5.5 + parent: 12 + - type: AtmosPipeColor + color: '#0055CCFF' - uid: 7204 components: - type: Transform @@ -114582,6 +114663,13 @@ entities: parent: 12 - type: AtmosPipeColor color: '#0055CCFF' + - uid: 10033 + components: + - type: Transform + pos: -1.5,-0.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' - uid: 10034 components: - type: Transform @@ -114766,13 +114854,6 @@ entities: parent: 12 - type: AtmosPipeColor color: '#0055CCFF' - - uid: 10066 - components: - - type: Transform - pos: -0.5,-7.5 - parent: 12 - - type: AtmosPipeColor - color: '#0055CCFF' - uid: 10067 components: - type: Transform @@ -129244,6 +129325,65 @@ entities: parent: 12 - type: AtmosPipeColor color: '#0055CCFF' + - uid: 32216 + components: + - type: Transform + pos: -1.5,-4.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' + - uid: 32217 + components: + - type: Transform + pos: -1.5,-6.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' + - uid: 32218 + components: + - type: Transform + pos: -1.5,-7.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' + - uid: 32219 + components: + - type: Transform + pos: -1.5,-8.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' + - uid: 32220 + components: + - type: Transform + pos: -1.5,-9.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' + - uid: 32221 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-5.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' + - uid: 32222 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -3.5,-5.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' + - uid: 32233 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -0.5,-5.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' - proto: GasPipeTJunction entities: - uid: 103 @@ -130207,6 +130347,22 @@ entities: parent: 12 - type: AtmosPipeColor color: '#0055CCFF' + - uid: 7191 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 0.5,-1.5 + parent: 12 + - type: AtmosPipeColor + color: '#0055CCFF' + - uid: 7195 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-1.5 + parent: 12 + - type: AtmosPipeColor + color: '#990000FF' - uid: 7206 components: - type: Transform @@ -130611,13 +130767,6 @@ entities: parent: 12 - type: AtmosPipeColor color: '#0055CCFF' - - uid: 10033 - components: - - type: Transform - pos: -0.5,-6.5 - parent: 12 - - type: AtmosPipeColor - color: '#0055CCFF' - uid: 10064 components: - type: Transform @@ -130634,6 +130783,14 @@ entities: parent: 12 - type: AtmosPipeColor color: '#0055CCFF' + - uid: 10066 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -0.5,-7.5 + parent: 12 + - type: AtmosPipeColor + color: '#0055CCFF' - uid: 10070 components: - type: Transform @@ -132652,18 +132809,6 @@ entities: rot: -1.5707963267948966 rad pos: 36.5,-32.5 parent: 12 - - uid: 7191 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-3.5 - parent: 12 - - uid: 7192 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-3.5 - parent: 12 - uid: 9038 components: - type: Transform @@ -132994,19 +133139,6 @@ entities: targetPressure: 4500 - type: AtmosPipeColor color: '#0055CCFF' - - uid: 7193 - components: - - type: Transform - pos: -1.5,-2.5 - parent: 12 - - type: GasPressurePump - targetPressure: 4500 - - uid: 7194 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-2.5 - parent: 12 - uid: 7212 components: - type: Transform @@ -133638,11 +133770,6 @@ entities: - 32066 - type: AtmosPipeColor color: '#0055CCFF' - - uid: 7186 - components: - - type: Transform - pos: 1.5,-0.5 - parent: 12 - uid: 7327 components: - type: Transform @@ -133819,6 +133946,9 @@ entities: rot: 3.141592653589793 rad pos: -0.5,-10.5 parent: 12 + - type: DeviceNetwork + deviceLists: + - 32232 - type: AtmosPipeColor color: '#0055CCFF' - uid: 10073 @@ -135226,6 +135356,38 @@ entities: - 32066 - type: AtmosPipeColor color: '#0055CCFF' + - uid: 32224 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-1.5 + parent: 12 + - type: DeviceNetwork + deviceLists: + - 32231 + - type: AtmosPipeColor + color: '#0055CCFF' + - uid: 32225 + components: + - type: Transform + pos: 0.5,0.5 + parent: 12 + - type: DeviceNetwork + deviceLists: + - 32231 + - type: AtmosPipeColor + color: '#0055CCFF' + - uid: 32235 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 0.5,-7.5 + parent: 12 + - type: DeviceNetwork + deviceLists: + - 32231 + - type: AtmosPipeColor + color: '#0055CCFF' - proto: GasVentPumpVox entities: - uid: 6688 @@ -135634,11 +135796,6 @@ entities: - 28365 - type: AtmosPipeColor color: '#990000FF' - - uid: 7185 - components: - - type: Transform - pos: -2.5,-0.5 - parent: 12 - uid: 7519 components: - type: Transform @@ -136864,6 +137021,49 @@ entities: parent: 12 - type: AtmosPipeColor color: '#990000FF' + - uid: 32226 + components: + - type: Transform + pos: -1.5,0.5 + parent: 12 + - type: DeviceNetwork + deviceLists: + - 32231 + - type: AtmosPipeColor + color: '#990000FF' + - uid: 32227 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -2.5,-1.5 + parent: 12 + - type: DeviceNetwork + deviceLists: + - 32231 + - type: AtmosPipeColor + color: '#990000FF' + - uid: 32229 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -1.5,-10.5 + parent: 12 + - type: DeviceNetwork + deviceLists: + - 32232 + - type: AtmosPipeColor + color: '#990000FF' + - uid: 32234 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 0.5,-5.5 + parent: 12 + - type: DeviceNetwork + deviceLists: + - 32231 + - type: AtmosPipeColor + color: '#990000FF' - proto: GasVentScrubberVox entities: - uid: 7844 @@ -147563,7 +147763,7 @@ entities: - type: Transform pos: -0.5,-8.5 parent: 12 - - uid: 145 + - uid: 2219 components: - type: Transform pos: -0.5,-4.5 @@ -159531,7 +159731,7 @@ entities: - uid: 24354 components: - type: Transform - pos: 39.5,45.5 + pos: 39.5,46.5 parent: 12 - uid: 24355 components: @@ -173430,16 +173630,6 @@ entities: - type: Transform pos: 36.5,-32.5 parent: 12 - - uid: 7196 - components: - - type: Transform - anchored: True - pos: -1.5,-3.5 - parent: 12 - - type: Physics - bodyType: Static - - type: Lock - locked: True - uid: 20776 components: - type: Transform @@ -174058,7 +174248,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: AI core entrance + id: AI core entrance hall - uid: 32075 components: - type: Transform @@ -174070,6 +174260,20 @@ entities: - SurveillanceCameraCommand nameSet: True id: Gravity Gen + - uid: 32214 + components: + - type: Transform + pos: -10.5,-6.5 + parent: 12 + - type: SurveillanceCamera + id: AI core entrance + - uid: 32215 + components: + - type: Transform + pos: 3.5,-7.5 + parent: 12 + - type: SurveillanceCamera + id: AI core power room - proto: SurveillanceCameraEngineering entities: - uid: 4167 @@ -181447,6 +181651,13 @@ entities: - type: Transform pos: -0.5,-44.5 parent: 12 +- proto: VendingMachineHappyHonk + entities: + - uid: 32236 + components: + - type: Transform + pos: 39.5,45.5 + parent: 12 - proto: VendingMachineHydrobe entities: - uid: 10333 diff --git a/Resources/Maps/marathon.yml b/Resources/Maps/marathon.yml index 7a23a84c70..fa6faec647 100644 --- a/Resources/Maps/marathon.yml +++ b/Resources/Maps/marathon.yml @@ -30953,6 +30953,31 @@ entities: - type: Transform pos: 19.5,-39.5 parent: 30 + - uid: 22544 + components: + - type: Transform + pos: -11.5,-44.5 + parent: 30 + - uid: 22547 + components: + - type: Transform + pos: -10.5,-44.5 + parent: 30 + - uid: 22550 + components: + - type: Transform + pos: -9.5,-44.5 + parent: 30 + - uid: 22551 + components: + - type: Transform + pos: -8.5,-44.5 + parent: 30 + - uid: 22552 + components: + - type: Transform + pos: -7.5,-44.5 + parent: 30 - proto: CableApcStack entities: - uid: 1637 diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml index 2ca2b0f740..82660f8f13 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml @@ -219,6 +219,7 @@ - id: MedicalTechFabCircuitboard - id: MedkitFilled - id: RubberStampCMO + - id: MedTekCartridge # Hardsuit table, used for suit storage as well - type: entityTable diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index c84e88d077..a91aab53c8 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -773,7 +773,7 @@ id: UplinkBinaryTranslatorKey name: uplink-binary-translator-key-name description: uplink-binary-translator-key-desc - icon: { sprite: /Textures/Objects/Devices/encryption_keys.rsi, state: borg_label } + icon: { sprite: /Textures/Objects/Devices/encryption_keys.rsi, state: ai_label } productEntity: EncryptionKeyBinarySyndicate cost: Telecrystal: 1 @@ -1085,6 +1085,16 @@ categories: - UplinkDisruption +- type: listing + id: UplinkCameraBug + name: uplink-cameraBug-name + description: uplink-cameraBug-desc + productEntity: CameraBug + cost: + Telecrystal: 4 + categories: + - UplinkDisruption + # Allies - type: listing @@ -1805,6 +1815,17 @@ categories: - UplinkPointless +- type: listing + id: UplinkSyndicateBusinessCard + name: uplink-business-card-name + description: uplink-business-card-desc + productEntity: SyndicateBusinessCard + categories: + - UplinkPointless + conditions: + - !type:ListingLimitedStockCondition + stock: 3 + # Job Specific - type: listing diff --git a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml index 625ef7a056..11643c076d 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml @@ -249,6 +249,7 @@ - type: Tag tags: - WhitelistChameleon + - FireHelmet - type: HideLayerClothing slots: - Hair @@ -281,6 +282,7 @@ - type: Tag tags: - WhitelistChameleon + - FireHelmet - type: HideLayerClothing slots: - Hair diff --git a/Resources/Prototypes/Entities/Clothing/Masks/specific.yml b/Resources/Prototypes/Entities/Clothing/Masks/specific.yml index 90c648c9d8..d8da80611c 100644 --- a/Resources/Prototypes/Entities/Clothing/Masks/specific.yml +++ b/Resources/Prototypes/Entities/Clothing/Masks/specific.yml @@ -35,6 +35,8 @@ - Snout - type: UserInterface interfaces: + enum.ChameleonUiKey.Key: + type: ChameleonBoundUserInterface enum.VoiceMaskUIKey.Key: type: VoiceMaskBoundUserInterface diff --git a/Resources/Prototypes/Entities/Clothing/Neck/misc.yml b/Resources/Prototypes/Entities/Clothing/Neck/misc.yml index 8dfc709bc4..af2af7a420 100644 --- a/Resources/Prototypes/Entities/Clothing/Neck/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Neck/misc.yml @@ -78,3 +78,19 @@ event: !type:StethoscopeActionEvent checkCanInteract: false priority: -1 + +- type: entity + parent: ClothingNeckBase + id: Dinkystar + name: star sticker + description: A dinky lil star for only the hardest working security officers! It's not even sticky anymore. + components: + - type: Sprite + sprite: Clothing/Neck/Misc/dinkystar.rsi + state: icon + - type: Item + size: Tiny + - type: Tag + tags: + - Trash + - WhitelistChameleon diff --git a/Resources/Prototypes/Entities/Clothing/Neck/pins.yml b/Resources/Prototypes/Entities/Clothing/Neck/pins.yml index eb948d299c..a7dcf03f28 100644 --- a/Resources/Prototypes/Entities/Clothing/Neck/pins.yml +++ b/Resources/Prototypes/Entities/Clothing/Neck/pins.yml @@ -7,6 +7,10 @@ components: - type: Item size: Tiny + - type: Sprite + sprite: Clothing/Neck/Misc/pins.rsi + - type: Clothing + sprite: Clothing/Neck/Misc/pins.rsi - type: entity parent: ClothingNeckPinBase @@ -15,14 +19,9 @@ description: Be gay do crime. components: - type: Sprite - sprite: Clothing/Neck/Misc/pins.rsi - layers: - - state: lgbt + state: lgbt - type: Clothing - sprite: Clothing/Neck/Misc/pins.rsi - clothingVisuals: - neck: - - state: lgbt-equipped + equippedPrefix: lgbt - type: entity parent: ClothingNeckPinBase @@ -31,14 +30,9 @@ description: Be aro do crime. components: - type: Sprite - sprite: Clothing/Neck/Misc/pins.rsi - layers: - - state: aro + state: aro - type: Clothing - sprite: Clothing/Neck/Misc/pins.rsi - clothingVisuals: - neck: - - state: aro-equipped + equippedPrefix: aro - type: entity parent: ClothingNeckPinBase @@ -47,14 +41,9 @@ description: Be ace do crime. components: - type: Sprite - sprite: Clothing/Neck/Misc/pins.rsi - layers: - - state: asex + state: asex - type: Clothing - sprite: Clothing/Neck/Misc/pins.rsi - clothingVisuals: - neck: - - state: asex-equipped + equippedPrefix: asex - type: entity parent: ClothingNeckPinBase @@ -63,14 +52,20 @@ description: Be bi do crime. components: - type: Sprite - sprite: Clothing/Neck/Misc/pins.rsi - layers: - - state: bi + state: bi - type: Clothing - sprite: Clothing/Neck/Misc/pins.rsi - clothingVisuals: - neck: - - state: bi-equipped + equippedPrefix: bi + +- type: entity + parent: ClothingNeckPinBase + id: ClothingNeckGayPin + name: gay pin + description: Be gay~ do crime. + components: + - type: Sprite + state: gay + - type: Clothing + equippedPrefix: gay - type: entity parent: ClothingNeckPinBase @@ -79,14 +74,9 @@ description: Be intersex do crime. components: - type: Sprite - sprite: Clothing/Neck/Misc/pins.rsi - layers: - - state: inter + state: inter - type: Clothing - sprite: Clothing/Neck/Misc/pins.rsi - clothingVisuals: - neck: - - state: inter-equipped + equippedPrefix: inter - type: entity parent: ClothingNeckPinBase @@ -95,14 +85,9 @@ description: Be lesbian do crime. components: - type: Sprite - sprite: Clothing/Neck/Misc/pins.rsi - layers: - - state: les + state: les - type: Clothing - sprite: Clothing/Neck/Misc/pins.rsi - clothingVisuals: - neck: - - state: les-equipped + equippedPrefix: les - type: entity parent: ClothingNeckPinBase @@ -111,14 +96,9 @@ description: "01100010 01100101 00100000 01100101 01101110 01100010 01111001 00100000 01100100 01101111 00100000 01100011 01110010 01101001 01101101 01100101" components: - type: Sprite - sprite: Clothing/Neck/Misc/pins.rsi - layers: - - state: non + state: non - type: Clothing - sprite: Clothing/Neck/Misc/pins.rsi - clothingVisuals: - neck: - - state: non-equipped + equippedPrefix: non - type: entity parent: ClothingNeckPinBase @@ -127,14 +107,9 @@ description: Be pan do crime. components: - type: Sprite - sprite: Clothing/Neck/Misc/pins.rsi - layers: - - state: pan + state: pan - type: Clothing - sprite: Clothing/Neck/Misc/pins.rsi - clothingVisuals: - neck: - - state: pan-equipped + equippedPrefix: pan - type: entity parent: ClothingNeckPinBase @@ -143,14 +118,9 @@ description: Be trans do crime. components: - type: Sprite - sprite: Clothing/Neck/Misc/pins.rsi - layers: - - state: trans + state: trans - type: Clothing - sprite: Clothing/Neck/Misc/pins.rsi - clothingVisuals: - neck: - - state: trans-equipped + equippedPrefix: trans - type: entity parent: ClothingNeckPinBase @@ -159,14 +129,9 @@ description: Be autism do crime. components: - type: Sprite - sprite: Clothing/Neck/Misc/autismpin.rsi - layers: - - state: autism + state: autism - type: Clothing - sprite: Clothing/Neck/Misc/autismpin.rsi - clothingVisuals: - neck: - - state: autism-equipped + equippedPrefix: autism - type: entity parent: ClothingNeckPinBase @@ -175,11 +140,6 @@ description: Be autism do warcrime. components: - type: Sprite - sprite: Clothing/Neck/Misc/goldautismpin.rsi - layers: - - state: goldautism + state: goldautism - type: Clothing - sprite: Clothing/Neck/Misc/goldautismpin.rsi - clothingVisuals: - neck: - - state: goldautism-equipped + equippedPrefix: goldautism diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml index 973af52221..21c91faadc 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml @@ -90,7 +90,7 @@ damageCoefficient: 0.9 - type: entity - parent: [ClothingOuterArmorHoS, ClothingOuterStorageBase] + parent: [ClothingOuterArmorHoS, ClothingOuterStorageBase, BaseSecurityCommandContraband] id: ClothingOuterCoatHoSTrench name: head of security's armored trenchcoat description: A greatcoat enhanced with a special alloy for some extra protection and style for those with a commanding presence. @@ -376,7 +376,7 @@ sprite: Clothing/OuterClothing/Coats/windbreaker_paramedic.rsi - type: entity - parent: ClothingOuterStorageBase + parent: [ClothingOuterStorageBase, BaseSyndicateContraband] id: ClothingOuterCoatSyndieCap name: syndicate's coat description: The syndicate's coat is made of durable fabric, with gilded patterns. @@ -387,7 +387,7 @@ sprite: Clothing/OuterClothing/Coats/syndicate/coatsyndiecap.rsi - type: entity - parent: ClothingOuterCoatHoSTrench + parent: [BaseSyndicateContraband, ClothingOuterCoatHoSTrench] # BaseSyndicateContraband as first parent so contraband system takes that as priority, yeah I know id: ClothingOuterCoatSyndieCapArmored name: syndicate's armored coat description: The syndicate's armored coat is made of durable fabric, with gilded patterns. diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/wintercoats.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/wintercoats.yml index e7f5a59472..4bf1ec87fd 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/wintercoats.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/wintercoats.yml @@ -359,7 +359,7 @@ ########################################################## - type: entity - parent: [ClothingOuterArmorHoS, ClothingOuterWinterCoatToggleable, BaseCommandContraband] + parent: [ClothingOuterArmorHoS, ClothingOuterWinterCoatToggleable, BaseSecurityCommandContraband] id: ClothingOuterWinterHoS name: head of security's armored winter coat description: A sturdy, utilitarian winter coat designed to protect a head of security from any brig-bound threats and hypothermic events. @@ -373,7 +373,7 @@ ########################################################## - type: entity - parent: [ClothingOuterWinterCoatToggleable, BaseCommandContraband] + parent: [ClothingOuterWinterCoatToggleable, BaseSecurityCommandContraband] id: ClothingOuterWinterHoSUnarmored name: head of security's winter coat description: A sturdy coat, a warm coat, but not an armored coat. diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml index 0aaaaf99b5..3f735cf98b 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml @@ -144,6 +144,7 @@ - id: ClothingNeckAromanticPin - id: ClothingNeckAsexualPin - id: ClothingNeckBisexualPin + - id: ClothingNeckGayPin - id: ClothingNeckIntersexPin - id: ClothingNeckLesbianPin - id: ClothingNeckNonBinaryPin diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index 955ddfd2e3..c65db8fe19 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -85,6 +85,7 @@ - type: Hands showInHands: false disableExplosionRecursion: true + canBeStripped: false - type: ComplexInteraction - type: IntrinsicRadioReceiver - type: IntrinsicRadioTransmitter @@ -121,6 +122,7 @@ cellSlotId: cell_slot fitsInCharger: true - type: ItemToggle + onActivate: false # You should not be able to turn off a borg temporarily. activated: false # gets activated when a mind is added onUse: false # no item-borg toggling sorry - type: ItemTogglePointLight diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml index d5b4366e2b..612e49baec 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml @@ -108,6 +108,53 @@ - type: NoSlip - type: Insulated +- type: entity + parent: MobSiliconBase + id: MobFireBot + name: firebot + description: A little fire extinguishing bot. He looks rather anxious. + components: + - type: Sprite + sprite: Mobs/Silicon/Bots/firebot.rsi + state: firebot + - type: Construction + graph: FireBot + node: bot + - type: SentienceTarget + flavorKind: station-event-random-sentience-flavor-mechanical + - type: HTN + rootTask: + task: FirebotCompound + - type: SolutionContainerManager + solutions: + spray: + maxVol: 10 + reagents: + - ReagentId: Water + Quantity: 10 + - type: SolutionRegeneration + solution: spray + generated: + reagents: + - ReagentId: Water + Quantity: 10 + - type: Spray + transferAmount: 10 + pushbackAmount: 60 + spraySound: + path: /Audio/Effects/extinguish.ogg + sprayedPrototype: ExtinguisherSpray + vaporAmount: 1 + vaporSpread: 90 + sprayVelocity: 3.0 + - type: UseDelay + delay: 4 + - type: InteractionPopup + interactSuccessString: petting-success-firebot + interactFailureString: petting-failure-firebot + interactSuccessSound: + path: /Audio/Ambience/Objects/periodic_beep.ogg + - type: entity parent: MobSiliconBase id: MobHonkBot @@ -211,8 +258,6 @@ - type: Construction graph: CleanBot node: bot - - type: SentienceTarget - flavorKind: station-event-random-sentience-flavor-mechanical - type: Absorbent pickupAmount: 10 - type: UseDelay @@ -284,8 +329,6 @@ - type: Construction graph: MediBot node: bot - - type: SentienceTarget - flavorKind: station-event-random-sentience-flavor-mechanical - type: Anchorable - type: InteractionPopup interactSuccessString: petting-success-medibot diff --git a/Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/camera_bug.yml b/Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/camera_bug.yml new file mode 100644 index 0000000000..6e10d5274a --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/camera_bug.yml @@ -0,0 +1,25 @@ +- type: entity + id: CameraBug + parent: [ BaseItem, BaseSyndicateContraband ] + name: camera bug + description: An illegal syndicate device that allows you to hack into the station's camera network. + components: + - type: Sprite + sprite: Objects/Devices/camera_bug.rsi + layers: + - state: camera_bug + - type: Item + - type: ActivatableUI + requireActiveHand: false + inHandsOnly: true + key: enum.SurveillanceCameraMonitorUiKey.Key + - type: UserInterface + interfaces: + enum.SurveillanceCameraMonitorUiKey.Key: + type: SurveillanceCameraMonitorBoundUserInterface + - type: DeviceNetwork + deviceNetId: Wired + receiveFrequencyId: SurveillanceCamera + transmitFrequencyId: SurveillanceCamera + - type: WiredNetworkConnection + - type: SurveillanceCameraMonitor \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml b/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml index aee26b0776..0ab0b687dc 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/cartridges.yml @@ -117,6 +117,25 @@ - type: StealTarget stealGroup: WantedListCartridge +- type: entity + parent: BaseItem + id: MedTekCartridge + name: MedTek cartridge + description: A program that provides medical diagnostic tools. + components: + - type: Sprite + sprite: Objects/Devices/cartridge.rsi + state: cart-med + - type: Icon + sprite: Objects/Devices/cartridge.rsi + state: cart-med + - type: Cartridge + programName: med-tek-program-name + icon: + sprite: Objects/Specific/Medical/healthanalyzer.rsi + state: icon + - type: MedTekCartridge + - type: entity parent: BaseItem id: AstroNavCartridge diff --git a/Resources/Prototypes/Entities/Objects/Devices/encryption_keys.yml b/Resources/Prototypes/Entities/Objects/Devices/encryption_keys.yml index 25d81a6f83..7393532654 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/encryption_keys.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/encryption_keys.yml @@ -227,7 +227,7 @@ - type: Sprite layers: - state: crypt_silver - - state: borg_label + - state: ai_label - type: entity parent: [ EncryptionKey, BaseSyndicateContraband ] @@ -242,7 +242,7 @@ - type: Sprite layers: - state: crypt_red - - state: borg_label + - state: ai_label - type: entity parent: EncryptionKey diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index c6952fdd6a..dfc1c6fa5b 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -131,12 +131,13 @@ id: BaseMedicalPDA abstract: true components: - - type: ItemToggle - onUse: false - - type: HealthAnalyzer - scanDelay: 1 - scanningEndSound: - path: "/Audio/Items/Medical/healthscanner.ogg" + - type: CartridgeLoader + uiKey: enum.PdaUiKey.Key + preinstalled: + - CrewManifestCartridge + - NotekeeperCartridge + - NewsReaderCartridge + - MedTekCartridge - type: entity parent: BasePDA @@ -169,7 +170,7 @@ parent: BaseMedicalPDA id: MedicalInternPDA name: medical intern PDA - description: Why isn't it white? Has a built-in health analyzer. + description: Why isn't it white? components: - type: Pda id: MedicalInternIDCard @@ -569,7 +570,7 @@ parent: BaseMedicalPDA id: CMOPDA name: chief medical officer PDA - description: Extraordinarily shiny and sterile. Has a built-in health analyzer. + description: Extraordinarily shiny and sterile. components: - type: Pda id: CMOIDCard @@ -585,7 +586,7 @@ parent: BaseMedicalPDA id: MedicalPDA name: medical PDA - description: Shiny and sterile. Has a built-in health analyzer. + description: Shiny and sterile. components: - type: Pda id: MedicalIDCard @@ -612,7 +613,7 @@ parent: BaseMedicalPDA id: ParamedicPDA name: paramedic PDA - description: Shiny and sterile. Has a built-in rapid health analyzer. + description: Shiny and sterile. components: - type: Pda id: ParamedicIDCard @@ -905,14 +906,18 @@ id: ERTMedicPDA name: ERT Medic PDA suffix: Medic - description: Red for firepower, it's shiny and sterile. Has a built-in rapid health analyzer. + description: Red for firepower, it's shiny and sterile. components: - type: Pda id: ERTMedicIDCard - - type: HealthAnalyzer - scanDelay: 1 - scanningEndSound: - path: "/Audio/Items/Medical/healthscanner.ogg" + - type: CartridgeLoader + uiKey: enum.PdaUiKey.Key + preinstalled: + - CrewManifestCartridge + - NotekeeperCartridge + - NewsReaderCartridge + - MedTekCartridge + - WantedListCartridge - type: entity parent: ERTLeaderPDA @@ -1019,7 +1024,7 @@ parent: BaseMedicalPDA id: BrigmedicPDA name: brigmedic PDA - description: I wonder whose pulse is on the screen? I hope he doesnt stop... PDA has a built-in health analyzer. + description: I wonder whose pulse is on the screen? I hope it doesn't stop... components: - type: Pda id: BrigmedicIDCard @@ -1089,7 +1094,7 @@ parent: BaseMedicalPDA id: SeniorPhysicianPDA name: senior physician PDA - description: Smells faintly like iron and chemicals. Has a built-in health analyzer. + description: Smells faintly like iron and chemicals. components: - type: Pda id: SeniorPhysicianIDCard @@ -1145,6 +1150,7 @@ uiKey: enum.PdaUiKey.Key preinstalled: - NotekeeperCartridge + - MedTekCartridge cartridgeSlot: priority: -1 name: Cartridge diff --git a/Resources/Prototypes/Entities/Objects/Fun/figurines.yml b/Resources/Prototypes/Entities/Objects/Fun/figurines.yml index e48ac9d84a..5f962b90c8 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/figurines.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/figurines.yml @@ -17,8 +17,6 @@ - type: Tag tags: - Figurine - - type: StealTarget - stealGroup: Figurines - type: UseDelay delay: 10 - type: Speech diff --git a/Resources/Prototypes/Entities/Objects/Misc/business_card.yml b/Resources/Prototypes/Entities/Objects/Misc/business_card.yml new file mode 100644 index 0000000000..a04351693a --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Misc/business_card.yml @@ -0,0 +1,26 @@ +- type: entity + id: SyndicateBusinessCard + name: syndicate business card + parent: Paper + description: A black card with the syndicate's logo. There's something written on the back. + components: + - type: Sprite + sprite: Objects/Misc/bureaucracy.rsi + layers: + - state: syndicate_card + - state: syndicate_card + map: ["enum.PaperVisualLayers.Writing"] + visible: false + - state: paper_stamp-generic + map: ["enum.PaperVisualLayers.Stamp"] + visible: false + - type: Paper + content: syndicate-business-card-base + - type: PaperVisuals + headerImagePath: "/Textures/Interface/Paper/paper_heading_syndicate_logo.svg.96dpi.png" + headerMargin: 0.0, 0.0, 0.0, 2.0 + backgroundImagePath: "/Textures/Interface/Paper/paper_background_black.svg.96dpi.png" + backgroundPatchMargin: 16.0, 16.0, 16.0, 16.0 + contentMargin: 4.0, 4.0, 4.0, 4.0 + maxWritableArea: 400.0, 256.0 + diff --git a/Resources/Prototypes/Entities/Objects/Misc/dinkystar.yml b/Resources/Prototypes/Entities/Objects/Misc/dinkystar.yml deleted file mode 100644 index 7703cf2eea..0000000000 --- a/Resources/Prototypes/Entities/Objects/Misc/dinkystar.yml +++ /dev/null @@ -1,19 +0,0 @@ -- type: entity - parent: ClothingNeckBase - id: Dinkystar - name: star sticker - description: A dinky lil star for only the hardest working security officers! It's not even sticky anymore. - components: - - type: Sprite - sprite: Clothing/Neck/Misc/dinkystar.rsi - state: icon - - type: Clothing - sprite: Clothing/Neck/Misc/dinkystar.rsi - clothingVisuals: - neck: - - state: star-equipped - - type: Item - size: Tiny - - type: Tag - tags: - - Trash diff --git a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml index 0389db27ea..b306da6442 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml @@ -62,6 +62,9 @@ - Rolling speedModifier: 0.5 # its very big, awkward to use - type: Appearance + - type: Tag + tags: + - FireExtinguisher - type: GenericVisualizer visuals: enum.ToggleVisuals.Toggled: diff --git a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml index 1e5f2573fd..9032268958 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml @@ -1286,6 +1286,22 @@ - type: Stack stackType: FloorTileBCircuit +- type: entity + name: red circuit floor + parent: FloorTileItemBase + id: FloorTileItemRCircuit + components: + - type: Sprite + state: rcircuit + - type: Item + heldPrefix: rcircuit + - type: FloorTile + outputs: + - Plating + - FloorRedCircuit + - type: Stack + stackType: FloorTileRCircuit + # Circuits stacks - type: entity @@ -1304,6 +1320,14 @@ - type: Stack count: 4 +- type: entity + parent: FloorTileItemRCircuit + id: FloorTileItemRCircuit4 + suffix: 4 + components: + - type: Stack + count: 4 + # Terrain - type: entity name: grass tile diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml index 3b3a539664..17775b7e25 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml @@ -237,9 +237,10 @@ - state: icon-tools-adv - type: ItemBorgModule items: - - Omnitool + - JawsOfLife + - PowerDrill + - Multitool - WelderExperimental - - NetworkConfigurator - RemoteSignaller - GasAnalyzer - GeigerCounter diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml index 7db085eb0a..92c952671f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -380,7 +380,7 @@ - type: entity name: x-ray cannon - parent: [BaseWeaponBattery, BaseGunWieldable] + parent: [BaseWeaponBattery, BaseGunWieldable, BaseSecurityContraband] id: WeaponXrayCannon description: An experimental weapon that uses concentrated x-ray energy against its target. components: @@ -720,7 +720,7 @@ - type: entity name: energy shotgun - parent: [BaseWeaponBattery, BaseGunWieldable] + parent: [BaseWeaponBattery, BaseGunWieldable, BaseGrandTheftContraband] id: WeaponEnergyShotgun description: A one-of-a-kind prototype energy weapon that uses various shotgun configurations. It offers the possibility of both lethal and non-lethal shots, making it a versatile weapon. components: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/clusterbang.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/clusterbang.yml index b4f540ae53..b041349d26 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/clusterbang.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/clusterbang.yml @@ -116,7 +116,7 @@ cluster-payload: !type:Container - type: entity - parent: [GrenadeBase, BaseSyndicateContraband] + parent: [GrenadeBase, BaseSecurityContraband] id: GrenadeStinger name: stinger grenade description: Nothing to see here, please disperse. diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml index c7951993f1..71feb1d4e6 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml @@ -65,7 +65,7 @@ board: [ DoorElectronicsBar ] - type: entity - parent: AirlockServiceLocked + parent: AirlockHydroponics id: AirlockHydroponicsLocked suffix: Hydroponics, Locked components: @@ -531,7 +531,7 @@ board: [ DoorElectronicsJanitor ] - type: entity - parent: AirlockServiceGlassLocked + parent: AirlockHydroponicsGlass id: AirlockHydroGlassLocked suffix: Hydroponics, Locked components: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml index 3de6555be1..cf6d5a89df 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml @@ -40,6 +40,16 @@ - type: Wires layoutId: AirlockCargo +- type: entity + parent: Airlock + id: AirlockHydroponics + suffix: Hydroponics + components: + - type: Sprite + sprite: Structures/Doors/Airlocks/Standard/hydroponics.rsi + - type: Wires + layoutId: AirlockService + - type: entity parent: Airlock id: AirlockMedical @@ -197,6 +207,16 @@ - type: Wires layoutId: AirlockCargo +- type: entity + parent: AirlockGlass + id: AirlockHydroponicsGlass + suffix: Hydroponics + components: + - type: Sprite + sprite: Structures/Doors/Airlocks/Glass/hydroponics.rsi + - type: Wires + layoutId: AirlockService + - type: entity parent: AirlockGlass id: AirlockMedicalGlass diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/assembly.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/assembly.yml index c464c70a15..98508b21bc 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/assembly.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/assembly.yml @@ -138,6 +138,25 @@ sprite: Structures/Doors/Airlocks/Standard/freezer.rsi state: "assembly" +#Hydroponics +- type: entity + parent: AirlockAssembly + id: AirlockAssemblyHydroponics + suffix: Hydroponics + components: + - type: Sprite + sprite: Structures/Doors/Airlocks/Standard/hydroponics.rsi + state: "assembly" + +- type: entity + parent: AirlockAssembly + id: AirlockAssemblyHydroponicsGlass + suffix: Hydroponics, Glass + components: + - type: Sprite + sprite: Structures/Doors/Airlocks/Glass/hydroponics.rsi + state: "assembly" + #Maintenance - type: entity parent: AirlockAssembly diff --git a/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml b/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml index e8f000514a..9beedb5e49 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml @@ -9,6 +9,7 @@ command: Structures/Doors/Airlocks/Standard/command.rsi engineering: Structures/Doors/Airlocks/Standard/engineering.rsi freezer: Structures/Doors/Airlocks/Standard/freezer.rsi + hydroponics: Structures/Doors/Airlocks/Standard/hydroponics.rsi maintenance: Structures/Doors/Airlocks/Standard/maint.rsi medical: Structures/Doors/Airlocks/Standard/medical.rsi science: Structures/Doors/Airlocks/Standard/science.rsi @@ -27,6 +28,7 @@ science: Structures/Doors/Airlocks/Glass/science.rsi engineering: Structures/Doors/Airlocks/Glass/engineering.rsi glass: Structures/Doors/Airlocks/Glass/glass.rsi + hydroponics: Structures/Doors/Airlocks/Glass/hydroponics.rsi maintenance: Structures/Doors/Airlocks/Glass/maint.rsi medical: Structures/Doors/Airlocks/Glass/medical.rsi security: Structures/Doors/Airlocks/Glass/security.rsi @@ -74,6 +76,7 @@ engineering: Engineering freezer: Civilian glass: Civilian + hydroponics: Civilian maintenance: Civilian medical: Medical science: Science diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 666550cd8a..846441cb39 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -442,6 +442,7 @@ - UniformPrinterMachineCircuitboard - FloorGreenCircuit - FloorBlueCircuit + - FloorRedCircuit - MicrowaveMachineCircuitboard - ReagentGrinderMachineCircuitboard - ElectricGrillMachineCircuitboard diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml index 917a94ddc9..03bc3e4a8d 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml @@ -104,6 +104,7 @@ collection: MetalGlassBreak params: volume: -4 + - type: StationAiWhitelist placement: mode: SnapgridCenter snap: diff --git a/Resources/Prototypes/GameRules/unknown_shuttles.yml b/Resources/Prototypes/GameRules/unknown_shuttles.yml index 7e40e3a1f2..afbd552af3 100644 --- a/Resources/Prototypes/GameRules/unknown_shuttles.yml +++ b/Resources/Prototypes/GameRules/unknown_shuttles.yml @@ -20,6 +20,7 @@ - id: UnknownShuttleMeatZone - id: UnknownShuttleMicroshuttle - id: UnknownShuttleSpacebus + - id: UnknownShuttleInstigator - type: entityTable id: UnknownShuttlesFreelanceTable diff --git a/Resources/Prototypes/Hydroponics/randomMutations.yml b/Resources/Prototypes/Hydroponics/randomMutations.yml index 8f409a5eaf..77ae2288f3 100644 --- a/Resources/Prototypes/Hydroponics/randomMutations.yml +++ b/Resources/Prototypes/Hydroponics/randomMutations.yml @@ -1,9 +1,11 @@ - type: RandomPlantMutationList id: RandomPlantMutations - mutations: - - name: Bioluminescent - baseOdds: 0.036 - effect: !type:Glow + mutations: + # disabled until 50 morbillion point lights don't cause the renderer to die + #- name: Bioluminescent + # baseOdds: 0.036 + # appliesToPlant: false + # effect: !type:Glow - name: Sentient baseOdds: 0.0072 appliesToProduce: false @@ -11,6 +13,7 @@ effect: !type:MakeSentient # existing effect. - name: Slippery baseOdds: 0.036 + appliesToPlant: false effect: !type:Slipify - name: ChangeSpecies baseOdds: 0.036 @@ -176,4 +179,4 @@ - name: ChangeHarvest baseOdds: 0.036 persists: false - effect: !type:PlantMutateHarvest \ No newline at end of file + effect: !type:PlantMutateHarvest diff --git a/Resources/Prototypes/Loadouts/Jobs/Security/detective.yml b/Resources/Prototypes/Loadouts/Jobs/Security/detective.yml index e8afa5a203..15fdd073b5 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Security/detective.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Security/detective.yml @@ -46,3 +46,8 @@ id: DetectiveCoat equipment: outerClothing: ClothingOuterCoatDetectiveLoadout + +- type: loadout + id: DetectiveCoatGrey + equipment: + outerClothing: ClothingOuterCoatDetectiveLoadoutGrey diff --git a/Resources/Prototypes/Loadouts/Miscellaneous/trinkets.yml b/Resources/Prototypes/Loadouts/Miscellaneous/trinkets.yml index c91108124f..ea70828ce4 100644 --- a/Resources/Prototypes/Loadouts/Miscellaneous/trinkets.yml +++ b/Resources/Prototypes/Loadouts/Miscellaneous/trinkets.yml @@ -95,6 +95,12 @@ back: - ClothingNeckBisexualPin +- type: loadout + id: ClothingNeckGayPin + storage: + back: + - ClothingNeckGayPin + - type: loadout id: ClothingNeckIntersexPin storage: diff --git a/Resources/Prototypes/Loadouts/loadout_groups.yml b/Resources/Prototypes/Loadouts/loadout_groups.yml index fe17ea0fff..1483fc3c11 100644 --- a/Resources/Prototypes/Loadouts/loadout_groups.yml +++ b/Resources/Prototypes/Loadouts/loadout_groups.yml @@ -18,6 +18,7 @@ - ClothingNeckAromanticPin - ClothingNeckAsexualPin - ClothingNeckBisexualPin + - ClothingNeckGayPin - ClothingNeckIntersexPin - ClothingNeckLesbianPin - ClothingNeckNonBinaryPin @@ -1054,6 +1055,7 @@ loadouts: - DetectiveArmorVest - DetectiveCoat + - DetectiveCoatGrey - type: loadoutGroup id: SecurityCadetJumpsuit diff --git a/Resources/Prototypes/NPCs/firebot.yml b/Resources/Prototypes/NPCs/firebot.yml new file mode 100644 index 0000000000..acea6498bd --- /dev/null +++ b/Resources/Prototypes/NPCs/firebot.yml @@ -0,0 +1,44 @@ +- type: htnCompound + id: FirebotCompound + branches: + - tasks: + - !type:HTNCompoundTask + task: DouseFireTargetCompound + - tasks: + - !type:HTNCompoundTask + task: IdleCompound + +- type: htnCompound + id: DouseFireTargetCompound + branches: + - tasks: + - !type:HTNPrimitiveTask + operator: !type:UtilityOperator + proto: NearbyOnFire + + - !type:HTNPrimitiveTask + operator: !type:SpeakOperator + speech: firebot-fire-detected + hidden: true + + - !type:HTNPrimitiveTask + operator: !type:MoveToOperator + pathfindInPlanning: true + removeKeyOnFinish: false + targetKey: TargetCoordinates + pathfindKey: TargetPathfind + rangeKey: InteractRange + + - !type:HTNPrimitiveTask + preconditions: + - !type:TargetInRangePrecondition + targetKey: Target + rangeKey: InteractRange + operator: !type:InteractWithOperator + targetKey: Target + services: + - !type:UtilityService + id: FireService + proto: NearbyOnFire + key: Target + diff --git a/Resources/Prototypes/NPCs/utility_queries.yml b/Resources/Prototypes/NPCs/utility_queries.yml index 06bc0a9a9e..e10a0ed30c 100644 --- a/Resources/Prototypes/NPCs/utility_queries.yml +++ b/Resources/Prototypes/NPCs/utility_queries.yml @@ -144,6 +144,27 @@ - !type:TargetAccessibleCon curve: !type:BoolCurve +- type: utilityQuery + id: NearbyOnFire + query: + - !type:ComponentQuery + components: + - type: Flammable + # why does Flammable even have a required damage + damage: + types: + burn: 0 + considerations: + - !type:TargetDistanceCon + curve: !type:PresetCurve + preset: TargetDistance + - !type:TargetAccessibleCon + curve: !type:BoolCurve + - !type:TargetInLOSOrCurrentCon + curve: !type:BoolCurve + - !type:TargetOnFireCon + curve: !type:BoolCurve + - type: utilityQuery id: NearbyPuddles query: diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml index e72de0d94a..3c59ab9a18 100644 --- a/Resources/Prototypes/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/Objectives/objectiveGroups.yml @@ -63,7 +63,6 @@ StampStealCollectionObjective: 1 DoorRemoteStealCollectionObjective: 1 TechnologyDiskStealCollectionObjective: 1 #rnd - FigurineStealCollectionObjective: 0.3 #service IDCardsStealCollectionObjective: 1 LAMPStealCollectionObjective: 2 #only for moth diff --git a/Resources/Prototypes/Objectives/stealTargetGroups.yml b/Resources/Prototypes/Objectives/stealTargetGroups.yml index 09619bf986..b21e0dc2de 100644 --- a/Resources/Prototypes/Objectives/stealTargetGroups.yml +++ b/Resources/Prototypes/Objectives/stealTargetGroups.yml @@ -86,13 +86,6 @@ # Thief Collection -- type: stealTargetGroup - id: Figurines - name: steal-target-groups-figurines - sprite: - sprite: Objects/Fun/figurines.rsi - state: figurine_spawner - - type: stealTargetGroup id: HeadCloak name: steal-target-groups-heads-cloaks diff --git a/Resources/Prototypes/Objectives/thief.yml b/Resources/Prototypes/Objectives/thief.yml index 7a46d0f5e9..2dbb2cc339 100644 --- a/Resources/Prototypes/Objectives/thief.yml +++ b/Resources/Prototypes/Objectives/thief.yml @@ -53,17 +53,6 @@ # Collections -- type: entity - parent: BaseThiefStealCollectionObjective - id: FigurineStealCollectionObjective - components: - - type: StealCondition - stealGroup: Figurines - minCollectionSize: 10 - maxCollectionSize: 18 #will be limited to the number of figures on the station anyway. - - type: Objective - difficulty: 0.25 - - type: entity parent: BaseThiefStealCollectionObjective id: HeadCloakStealCollectionObjective @@ -129,7 +118,7 @@ - type: StealCondition stealGroup: IDCard minCollectionSize: 5 - maxCollectionSize: 15 + maxCollectionSize: 10 verifyMapExistence: false - type: Objective difficulty: 0.7 diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/bots/firebot.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/bots/firebot.yml new file mode 100644 index 0000000000..977ffd4093 --- /dev/null +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/bots/firebot.yml @@ -0,0 +1,33 @@ +- type: constructionGraph + id: FireBot + start: start + graph: + - node: start + edges: + - to: bot + steps: + - tag: FireExtinguisher + icon: + sprite: Objects/Misc/fire_extinguisher.rsi + state: fire_extinguisher_open + name: fire extinguisher + - tag: FireHelmet + icon: + sprite: Clothing/Head/Helmets/firehelmet.rsi + state: icon + name: fire helmet + doAfter: 2 + - tag: ProximitySensor + icon: + sprite: Objects/Misc/proximity_sensor.rsi + state: icon + name: proximity sensor + doAfter: 2 + - tag: BorgArm + icon: + sprite: Mobs/Silicon/drone.rsi + state: l_hand + name: borg arm + doAfter: 2 + - node: bot + entity: MobFireBot diff --git a/Resources/Prototypes/Recipes/Crafting/bots.yml b/Resources/Prototypes/Recipes/Crafting/bots.yml index 3031f4a780..21b524a060 100644 --- a/Resources/Prototypes/Recipes/Crafting/bots.yml +++ b/Resources/Prototypes/Recipes/Crafting/bots.yml @@ -11,6 +11,19 @@ sprite: Mobs/Silicon/Bots/cleanbot.rsi state: cleanbot +- type: construction + name: firebot + id: firebot + graph: FireBot + startNode: start + targetNode: bot + category: construction-category-utilities + objectType: Item + description: This bot puts out fires wherever it goes. + icon: + sprite: Mobs/Silicon/Bots/firebot.rsi + state: firebot + - type: construction name: honkbot id: honkbot diff --git a/Resources/Prototypes/Recipes/Lathes/cargo.yml b/Resources/Prototypes/Recipes/Lathes/cargo.yml index 630dc1d062..82050985b8 100644 --- a/Resources/Prototypes/Recipes/Lathes/cargo.yml +++ b/Resources/Prototypes/Recipes/Lathes/cargo.yml @@ -3,7 +3,7 @@ result: ConveyorBeltAssembly completetime: 4 materials: - Steel: 500 + Steel: 250 Plastic: 50 - type: latheRecipe diff --git a/Resources/Prototypes/Recipes/Lathes/misc.yml b/Resources/Prototypes/Recipes/Lathes/misc.yml index 0f043df9f8..a0e74fc34e 100644 --- a/Resources/Prototypes/Recipes/Lathes/misc.yml +++ b/Resources/Prototypes/Recipes/Lathes/misc.yml @@ -199,6 +199,13 @@ materials: Steel: 100 +- type: latheRecipe + id: FloorRedCircuit + result: FloorTileItemRCircuit4 + completetime: 2 + materials: + Steel: 100 + - type: latheRecipe id: HandheldStationMap result: HandheldStationMapEmpty diff --git a/Resources/Prototypes/Stacks/floor_tile_stacks.yml b/Resources/Prototypes/Stacks/floor_tile_stacks.yml index de03fcba19..65fd672e1a 100644 --- a/Resources/Prototypes/Stacks/floor_tile_stacks.yml +++ b/Resources/Prototypes/Stacks/floor_tile_stacks.yml @@ -538,6 +538,12 @@ spawn: FloorTileItemBCircuit maxCount: 30 +- type: stack + id: FloorTileRCircuit + name: red-circuit floor + spawn: FloorTileItemRCircuit + maxCount: 30 + - type: stack id: FloorTileGrass name: grass floor tile diff --git a/Resources/Prototypes/Tiles/floors.yml b/Resources/Prototypes/Tiles/floors.yml index 2d552cc33e..c98ee6d582 100644 --- a/Resources/Prototypes/Tiles/floors.yml +++ b/Resources/Prototypes/Tiles/floors.yml @@ -1422,6 +1422,18 @@ itemDrop: FloorTileItemBCircuit heatCapacity: 10000 +- type: tile + id: FloorRedCircuit + name: tiles-red-circuit-floor + sprite: /Textures/Tiles/red_circuit.png + baseTurf: Plating + isSubfloor: false + deconstructTools: [ Prying ] + footstepSounds: + collection: FootstepHull + itemDrop: FloorTileItemRCircuit + heatCapacity: 10000 + # Terrain - type: tile id: FloorAsphalt diff --git a/Resources/Prototypes/Wires/layouts.yml b/Resources/Prototypes/Wires/layouts.yml index 955bae0104..cb211cf843 100644 --- a/Resources/Prototypes/Wires/layouts.yml +++ b/Resources/Prototypes/Wires/layouts.yml @@ -90,6 +90,7 @@ - type: wireLayout id: FireAlarm wires: + - !type:AiInteractWireAction - !type:PowerWireAction - !type:AtmosMonitorDeviceNetWire alarmOnPulse: true diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 2a07d061d3..86ade97d4e 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -617,6 +617,12 @@ - type: Tag id: FirelockElectronics +- type: Tag + id: FireExtinguisher + +- type: Tag + id: FireHelmet + - type: Tag id: Flare diff --git a/Resources/Textures/Clothing/Head/Bandanas/blue.rsi/equipped-HELMET-vox.png b/Resources/Textures/Clothing/Head/Bandanas/blue.rsi/equipped-HELMET-vox.png index 7266432372..6bc5266367 100644 Binary files a/Resources/Textures/Clothing/Head/Bandanas/blue.rsi/equipped-HELMET-vox.png and b/Resources/Textures/Clothing/Head/Bandanas/blue.rsi/equipped-HELMET-vox.png differ diff --git a/Resources/Textures/Clothing/Head/Bandanas/blue.rsi/equipped-MASK-vox.png b/Resources/Textures/Clothing/Head/Bandanas/blue.rsi/equipped-MASK-vox.png index 6bc5266367..7266432372 100644 Binary files a/Resources/Textures/Clothing/Head/Bandanas/blue.rsi/equipped-MASK-vox.png and b/Resources/Textures/Clothing/Head/Bandanas/blue.rsi/equipped-MASK-vox.png differ diff --git a/Resources/Textures/Clothing/Neck/Misc/autismpin.rsi/meta.json b/Resources/Textures/Clothing/Neck/Misc/autismpin.rsi/meta.json deleted file mode 100644 index e82672f071..0000000000 --- a/Resources/Textures/Clothing/Neck/Misc/autismpin.rsi/meta.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-NC-4.0", - "copyright": "Terraspark's work", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "autism" - }, - { - "name": "autism-equipped", - "directions": 4 - } - ] -} diff --git a/Resources/Textures/Clothing/Neck/Misc/dinkystar.rsi/star-equipped.png b/Resources/Textures/Clothing/Neck/Misc/dinkystar.rsi/equipped-NECK.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/dinkystar.rsi/star-equipped.png rename to Resources/Textures/Clothing/Neck/Misc/dinkystar.rsi/equipped-NECK.png diff --git a/Resources/Textures/Clothing/Neck/Misc/dinkystar.rsi/meta.json b/Resources/Textures/Clothing/Neck/Misc/dinkystar.rsi/meta.json index ae8a2141db..0ed35562ca 100644 --- a/Resources/Textures/Clothing/Neck/Misc/dinkystar.rsi/meta.json +++ b/Resources/Textures/Clothing/Neck/Misc/dinkystar.rsi/meta.json @@ -11,7 +11,7 @@ "name": "icon" }, { - "name": "star-equipped", + "name": "equipped-NECK", "directions": 4 } ] diff --git a/Resources/Textures/Clothing/Neck/Misc/goldautismpin.rsi/meta.json b/Resources/Textures/Clothing/Neck/Misc/goldautismpin.rsi/meta.json deleted file mode 100644 index 6848744ab8..0000000000 --- a/Resources/Textures/Clothing/Neck/Misc/goldautismpin.rsi/meta.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-NC-4.0", - "copyright": "Terraspark's work", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "goldautism" - }, - { - "name": "goldautism-equipped", - "directions": 4 - } - ] -} diff --git a/Resources/Textures/Clothing/Neck/Misc/pins.rsi/aro-equipped.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/aro-equipped-NECK.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/pins.rsi/aro-equipped.png rename to Resources/Textures/Clothing/Neck/Misc/pins.rsi/aro-equipped-NECK.png diff --git a/Resources/Textures/Clothing/Neck/Misc/pins.rsi/asex-equipped.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/asex-equipped-NECK.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/pins.rsi/asex-equipped.png rename to Resources/Textures/Clothing/Neck/Misc/pins.rsi/asex-equipped-NECK.png diff --git a/Resources/Textures/Clothing/Neck/Misc/autismpin.rsi/autism-equipped.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/autism-equipped-NECK.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/autismpin.rsi/autism-equipped.png rename to Resources/Textures/Clothing/Neck/Misc/pins.rsi/autism-equipped-NECK.png diff --git a/Resources/Textures/Clothing/Neck/Misc/autismpin.rsi/autism.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/autism.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/autismpin.rsi/autism.png rename to Resources/Textures/Clothing/Neck/Misc/pins.rsi/autism.png diff --git a/Resources/Textures/Clothing/Neck/Misc/pins.rsi/bi-equipped.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/bi-equipped-NECK.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/pins.rsi/bi-equipped.png rename to Resources/Textures/Clothing/Neck/Misc/pins.rsi/bi-equipped-NECK.png diff --git a/Resources/Textures/Clothing/Neck/Misc/pins.rsi/gay-equipped-NECK.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/gay-equipped-NECK.png new file mode 100644 index 0000000000..d5127a0218 Binary files /dev/null and b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/gay-equipped-NECK.png differ diff --git a/Resources/Textures/Clothing/Neck/Misc/pins.rsi/gay.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/gay.png new file mode 100644 index 0000000000..580d390fce Binary files /dev/null and b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/gay.png differ diff --git a/Resources/Textures/Clothing/Neck/Misc/goldautismpin.rsi/goldautism-equipped.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/goldautism-equipped-NECK.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/goldautismpin.rsi/goldautism-equipped.png rename to Resources/Textures/Clothing/Neck/Misc/pins.rsi/goldautism-equipped-NECK.png diff --git a/Resources/Textures/Clothing/Neck/Misc/goldautismpin.rsi/goldautism.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/goldautism.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/goldautismpin.rsi/goldautism.png rename to Resources/Textures/Clothing/Neck/Misc/pins.rsi/goldautism.png diff --git a/Resources/Textures/Clothing/Neck/Misc/pins.rsi/inter-equipped.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/inter-equipped-NECK.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/pins.rsi/inter-equipped.png rename to Resources/Textures/Clothing/Neck/Misc/pins.rsi/inter-equipped-NECK.png diff --git a/Resources/Textures/Clothing/Neck/Misc/pins.rsi/les-equipped.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/les-equipped-NECK.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/pins.rsi/les-equipped.png rename to Resources/Textures/Clothing/Neck/Misc/pins.rsi/les-equipped-NECK.png diff --git a/Resources/Textures/Clothing/Neck/Misc/pins.rsi/lgbt-equipped.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/lgbt-equipped-NECK.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/pins.rsi/lgbt-equipped.png rename to Resources/Textures/Clothing/Neck/Misc/pins.rsi/lgbt-equipped-NECK.png diff --git a/Resources/Textures/Clothing/Neck/Misc/pins.rsi/meta.json b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/meta.json index 46b0496399..0619f962df 100644 --- a/Resources/Textures/Clothing/Neck/Misc/pins.rsi/meta.json +++ b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "PixelTK leaves his mark on upstream", + "copyright": "PixelTK leaves his mark on upstream, BackeTako made the gay, autism pins by Terraspark", "size": { "x": 32, "y": 32 @@ -11,63 +11,84 @@ "name": "aro" }, { - "name": "aro-equipped", + "name": "aro-equipped-NECK", "directions": 4 }, { "name": "asex" }, { - "name": "asex-equipped", + "name": "asex-equipped-NECK", + "directions": 4 + }, + { + "name": "autism" + }, + { + "name": "autism-equipped-NECK", "directions": 4 }, { "name": "bi" }, { - "name": "bi-equipped", + "name": "bi-equipped-NECK", + "directions": 4 + }, + { + "name": "gay" + }, + { + "name": "gay-equipped-NECK", + "directions": 4 + }, + { + "name": "goldautism" + }, + { + "name": "goldautism-equipped-NECK", "directions": 4 }, { "name": "inter" }, { - "name": "inter-equipped", + "name": "inter-equipped-NECK", "directions": 4 }, { "name": "les" }, { - "name": "les-equipped", + "name": "les-equipped-NECK", "directions": 4 }, { "name": "lgbt" }, { - "name": "lgbt-equipped", + "name": "lgbt-equipped-NECK", "directions": 4 }, { "name": "non" }, { - "name": "non-equipped", + "name": "non-equipped-NECK", "directions": 4 }, { "name": "pan" }, { - "name": "pan-equipped", + "name": "pan-equipped-NECK", "directions": 4 }, { "name": "trans" }, { - "name": "trans-equipped", + "name": "trans-equipped-NECK", "directions": 4 } ] diff --git a/Resources/Textures/Clothing/Neck/Misc/pins.rsi/non-equipped.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/non-equipped-NECK.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/pins.rsi/non-equipped.png rename to Resources/Textures/Clothing/Neck/Misc/pins.rsi/non-equipped-NECK.png diff --git a/Resources/Textures/Clothing/Neck/Misc/pins.rsi/pan-equipped.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/pan-equipped-NECK.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/pins.rsi/pan-equipped.png rename to Resources/Textures/Clothing/Neck/Misc/pins.rsi/pan-equipped-NECK.png diff --git a/Resources/Textures/Clothing/Neck/Misc/pins.rsi/trans-equipped.png b/Resources/Textures/Clothing/Neck/Misc/pins.rsi/trans-equipped-NECK.png similarity index 100% rename from Resources/Textures/Clothing/Neck/Misc/pins.rsi/trans-equipped.png rename to Resources/Textures/Clothing/Neck/Misc/pins.rsi/trans-equipped-NECK.png diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/bolt_door.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/bolt_door.png new file mode 100644 index 0000000000..f794248980 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/bolt_door.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/door_overcharge_off.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/door_overcharge_off.png new file mode 100644 index 0000000000..d5301ccba0 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/door_overcharge_off.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/door_overcharge_on.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/door_overcharge_on.png new file mode 100644 index 0000000000..ea654d8634 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/door_overcharge_on.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/emergency_off.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/emergency_off.png new file mode 100644 index 0000000000..86328da46b Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/emergency_off.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/emergency_on.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/emergency_on.png new file mode 100644 index 0000000000..14034429f4 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/emergency_on.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json b/Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json index 6b974d8521..1efee13f3a 100644 --- a/Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json +++ b/Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json @@ -1,27 +1,27 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/blob/c473a8bcc28fbd80827dfca5660d81ca6e833e2c/icons/hud/screen_ai.dmi", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "ai_core" - }, - { - "name": "camera_light" - }, - { - "name": "crew_monitor" - }, - { - "name": "manifest" - }, - { - "name": "state_laws" - }, + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/blob/c473a8bcc28fbd80827dfca5660d81ca6e833e2c/icons/hud/screen_ai.dmi , some sprites updated by ScarKy0(Discord), door actions by ScarKy0(Discord) and @Max_tanuki(Twitter)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "ai_core" + }, + { + "name": "camera_light" + }, + { + "name": "crew_monitor" + }, + { + "name": "manifest" + }, + { + "name": "state_laws" + }, { "name": "station_records" }, @@ -33,6 +33,24 @@ }, { "name": "comms_console" + }, + { + "name": "emergency_off" + }, + { + "name": "emergency_on" + }, + { + "name": "bolt_door" + }, + { + "name": "unbolt_door" + }, + { + "name": "door_overcharge_on" + }, + { + "name": "door_overcharge_off" } - ] + ] } diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/unbolt_door.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/unbolt_door.png new file mode 100644 index 0000000000..dfbb102f97 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/unbolt_door.png differ diff --git a/Resources/Textures/Interface/Paper/paper_background_black.svg b/Resources/Textures/Interface/Paper/paper_background_black.svg new file mode 100644 index 0000000000..7c208901f4 --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_background_black.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + diff --git a/Resources/Textures/Interface/Paper/paper_background_black.svg.96dpi.png b/Resources/Textures/Interface/Paper/paper_background_black.svg.96dpi.png new file mode 100644 index 0000000000..47e74fc873 Binary files /dev/null and b/Resources/Textures/Interface/Paper/paper_background_black.svg.96dpi.png differ diff --git a/Resources/Textures/Interface/Paper/paper_background_black.svg.96dpi.png.yml b/Resources/Textures/Interface/Paper/paper_background_black.svg.96dpi.png.yml new file mode 100644 index 0000000000..5c43e23305 --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_background_black.svg.96dpi.png.yml @@ -0,0 +1,2 @@ +sample: + filter: true diff --git a/Resources/Textures/Interface/Paper/paper_heading_syndicate_logo.svg b/Resources/Textures/Interface/Paper/paper_heading_syndicate_logo.svg new file mode 100644 index 0000000000..e7ba27f8cd --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_heading_syndicate_logo.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/Textures/Interface/Paper/paper_heading_syndicate_logo.svg.96dpi.png b/Resources/Textures/Interface/Paper/paper_heading_syndicate_logo.svg.96dpi.png new file mode 100644 index 0000000000..21e85b65d8 Binary files /dev/null and b/Resources/Textures/Interface/Paper/paper_heading_syndicate_logo.svg.96dpi.png differ diff --git a/Resources/Textures/Interface/Paper/paper_heading_syndicate_logo.svg.96dpi.png.yml b/Resources/Textures/Interface/Paper/paper_heading_syndicate_logo.svg.96dpi.png.yml new file mode 100644 index 0000000000..5c43e23305 --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_heading_syndicate_logo.svg.96dpi.png.yml @@ -0,0 +1,2 @@ +sample: + filter: true diff --git a/Resources/Textures/Mobs/Customization/human_hair.rsi/longbow.png b/Resources/Textures/Mobs/Customization/human_hair.rsi/longbow.png new file mode 100644 index 0000000000..40705175a4 Binary files /dev/null and b/Resources/Textures/Mobs/Customization/human_hair.rsi/longbow.png differ diff --git a/Resources/Textures/Mobs/Customization/human_hair.rsi/meta.json b/Resources/Textures/Mobs/Customization/human_hair.rsi/meta.json index d6cd1e11da..ff556dbc78 100644 --- a/Resources/Textures/Mobs/Customization/human_hair.rsi/meta.json +++ b/Resources/Textures/Mobs/Customization/human_hair.rsi/meta.json @@ -790,6 +790,14 @@ { "name": "classiclong3", "directions": 4 + }, + { + "name": "shaped", + "directions": 4 + }, + { + "name": "longbow", + "directions": 4 } ] } diff --git a/Resources/Textures/Mobs/Customization/human_hair.rsi/shaped.png b/Resources/Textures/Mobs/Customization/human_hair.rsi/shaped.png new file mode 100644 index 0000000000..f65d7cf444 Binary files /dev/null and b/Resources/Textures/Mobs/Customization/human_hair.rsi/shaped.png differ diff --git a/Resources/Textures/Mobs/Silicon/Bots/firebot.rsi/firebot.png b/Resources/Textures/Mobs/Silicon/Bots/firebot.rsi/firebot.png new file mode 100644 index 0000000000..70ee9313d2 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/Bots/firebot.rsi/firebot.png differ diff --git a/Resources/Textures/Mobs/Silicon/Bots/firebot.rsi/meta.json b/Resources/Textures/Mobs/Silicon/Bots/firebot.rsi/meta.json new file mode 100644 index 0000000000..e13b42deee --- /dev/null +++ b/Resources/Textures/Mobs/Silicon/Bots/firebot.rsi/meta.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/tgstation/tgstation/commit/eba0d62005e7754dd8b1c88e45cd949c360774d5", + "states": [ + { + "name": "firebot", + "delays": [ + [ + 0.5, + 0.2 + ] + ] + } + ] +} diff --git a/Resources/Textures/Objects/Devices/camera_bug.rsi/camera_bug.png b/Resources/Textures/Objects/Devices/camera_bug.rsi/camera_bug.png new file mode 100644 index 0000000000..ba8255fc14 Binary files /dev/null and b/Resources/Textures/Objects/Devices/camera_bug.rsi/camera_bug.png differ diff --git a/Resources/Textures/Objects/Devices/camera_bug.rsi/meta.json b/Resources/Textures/Objects/Devices/camera_bug.rsi/meta.json new file mode 100644 index 0000000000..c4298b6ec8 --- /dev/null +++ b/Resources/Textures/Objects/Devices/camera_bug.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "created by pofitlo", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "camera_bug" + } + ] +} diff --git a/Resources/Textures/Objects/Devices/cartridge.rsi/cart-med.png b/Resources/Textures/Objects/Devices/cartridge.rsi/cart-med.png new file mode 100644 index 0000000000..69be9eb42e Binary files /dev/null and b/Resources/Textures/Objects/Devices/cartridge.rsi/cart-med.png differ diff --git a/Resources/Textures/Objects/Devices/cartridge.rsi/meta.json b/Resources/Textures/Objects/Devices/cartridge.rsi/meta.json index e7415fe1c2..b79f46d08d 100644 --- a/Resources/Textures/Objects/Devices/cartridge.rsi/meta.json +++ b/Resources/Textures/Objects/Devices/cartridge.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from vgstation at https://github.com/vgstation-coders/vgstation13/commit/1cdfb0230cc96d0ba751fa002d04f8aa2f25ad7d and tgstation at tgstation at https://github.com/tgstation/tgstation/commit/0c15d9dbcf0f2beb230eba5d9d889ef2d1945bb8, cart-log made by Skarletto (github), cart-sec made by dieselmohawk (discord), cart-nav made by ArchRBX (github)", + "copyright": "Taken from vgstation at https://github.com/vgstation-coders/vgstation13/commit/1cdfb0230cc96d0ba751fa002d04f8aa2f25ad7d and tgstation at tgstation at https://github.com/tgstation/tgstation/commit/0c15d9dbcf0f2beb230eba5d9d889ef2d1945bb8, cart-log made by Skarletto (github), cart-sec made by dieselmohawk (discord), cart-nav, cart-med made by ArchRBX (github)", "size": { "x": 32, "y": 32 @@ -55,6 +55,9 @@ { "name": "cart-m" }, + { + "name": "cart-med" + }, { "name": "cart-mi" }, diff --git a/Resources/Textures/Objects/Devices/encryption_keys.rsi/ai_label.png b/Resources/Textures/Objects/Devices/encryption_keys.rsi/ai_label.png new file mode 100644 index 0000000000..02409127a3 Binary files /dev/null and b/Resources/Textures/Objects/Devices/encryption_keys.rsi/ai_label.png differ diff --git a/Resources/Textures/Objects/Devices/encryption_keys.rsi/meta.json b/Resources/Textures/Objects/Devices/encryption_keys.rsi/meta.json index 6ae7bca9dd..a6222e988f 100644 --- a/Resources/Textures/Objects/Devices/encryption_keys.rsi/meta.json +++ b/Resources/Textures/Objects/Devices/encryption_keys.rsi/meta.json @@ -35,6 +35,7 @@ {"name": "sec_label"}, {"name": "service_label"}, {"name": "synd_label"}, - {"name": "borg_label"} + {"name": "borg_label"}, + {"name": "ai_label"} ] } diff --git a/Resources/Textures/Objects/Misc/bureaucracy.rsi/meta.json b/Resources/Textures/Objects/Misc/bureaucracy.rsi/meta.json index 9005034d3f..bc9ba80adf 100644 --- a/Resources/Textures/Objects/Misc/bureaucracy.rsi/meta.json +++ b/Resources/Textures/Objects/Misc/bureaucracy.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. paper_stamp-syndicate by Veritius. paper_receipt, paper_receipt_horizontal by eoineoineoin. paper_stamp-greytide by ubaser. paper_stamp-psychologist by clinux", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. paper_stamp-syndicate by Veritius. paper_receipt, paper_receipt_horizontal by eoineoineoin. paper_stamp-greytide by ubaser. paper_stamp-psychologist by clinux. syndicate_card by Aserovich", "size": { "x": 32, "y": 32 @@ -228,6 +228,9 @@ { "name": "paper_stamp-greytide" }, + { + "name": "syndicate_card" + }, { "name": "paper_stamp-psychologist" } diff --git a/Resources/Textures/Objects/Misc/bureaucracy.rsi/syndicate_card.png b/Resources/Textures/Objects/Misc/bureaucracy.rsi/syndicate_card.png new file mode 100644 index 0000000000..e015c78484 Binary files /dev/null and b/Resources/Textures/Objects/Misc/bureaucracy.rsi/syndicate_card.png differ diff --git a/Resources/Textures/Objects/Tiles/tile.rsi/meta.json b/Resources/Textures/Objects/Tiles/tile.rsi/meta.json index 7db50200ed..7562d0f9b1 100644 --- a/Resources/Textures/Objects/Tiles/tile.rsi/meta.json +++ b/Resources/Textures/Objects/Tiles/tile.rsi/meta.json @@ -159,6 +159,9 @@ { "name": "bcircuit" }, + { + "name": "rcircuit" + }, { "name": "carpet-black" }, @@ -472,6 +475,14 @@ "name": "gold-inhand-left", "directions": 4 }, + { + "name": "rcircuit-inhand-right", + "directions": 4 + }, + { + "name": "rcircuit-inhand-left", + "directions": 4 + }, { "name": "reinforced-inhand-right", "directions": 4 diff --git a/Resources/Textures/Objects/Tiles/tile.rsi/rcircuit-inhand-left.png b/Resources/Textures/Objects/Tiles/tile.rsi/rcircuit-inhand-left.png new file mode 100644 index 0000000000..06a279199c Binary files /dev/null and b/Resources/Textures/Objects/Tiles/tile.rsi/rcircuit-inhand-left.png differ diff --git a/Resources/Textures/Objects/Tiles/tile.rsi/rcircuit-inhand-right.png b/Resources/Textures/Objects/Tiles/tile.rsi/rcircuit-inhand-right.png new file mode 100644 index 0000000000..44703f72f7 Binary files /dev/null and b/Resources/Textures/Objects/Tiles/tile.rsi/rcircuit-inhand-right.png differ diff --git a/Resources/Textures/Objects/Tiles/tile.rsi/rcircuit.png b/Resources/Textures/Objects/Tiles/tile.rsi/rcircuit.png new file mode 100644 index 0000000000..be9dc0c20f Binary files /dev/null and b/Resources/Textures/Objects/Tiles/tile.rsi/rcircuit.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/assembly.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/assembly.png new file mode 100644 index 0000000000..f84c6150fd Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/assembly.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/bolted_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/bolted_unlit.png new file mode 100644 index 0000000000..6857f2a241 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/bolted_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/closed.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/closed.png new file mode 100644 index 0000000000..65ea6c4f57 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/closed.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/closed_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/closed_unlit.png new file mode 100644 index 0000000000..c78d01c42d Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/closed_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/closing.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/closing.png new file mode 100644 index 0000000000..567e59aaef Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/closing.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/closing_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/closing_unlit.png new file mode 100644 index 0000000000..2a71f76d5d Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/closing_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/deny_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/deny_unlit.png new file mode 100644 index 0000000000..7c56263f83 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/deny_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/emergency_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/emergency_unlit.png new file mode 100644 index 0000000000..817f2fb3f9 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/emergency_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/meta.json b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/meta.json new file mode 100644 index 0000000000..ed871d3b7e --- /dev/null +++ b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/meta.json @@ -0,0 +1,195 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c6e3401f2e7e1e55c57060cdf956a98ef1fefc24, hydroponics version made by BackeTako (github) for ss14", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "assembly" + }, + { + "name": "bolted_unlit" + }, + { + "name": "closed" + }, + { + "name": "closed_unlit" + }, + { + "name": "closing", + "delays": [ + [ + 0.1, + 0.1, + 0.07, + 0.07, + 0.07, + 0.2 + ] + ] + }, + { + "name": "closing_unlit", + "delays": [ + [ + 0.1, + 0.1, + 0.07, + 0.07, + 0.07, + 0.2 + ] + ] + }, + { + "name": "deny_unlit", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "open", + "delays": [ + [ + 1 + ] + ] + }, + { + "name": "opening", + "delays": [ + [ + 0.1, + 0.1, + 0.07, + 0.07, + 0.07, + 0.2 + ] + ] + }, + { + "name": "opening_unlit", + "delays": [ + [ + 0.1, + 0.1, + 0.07, + 0.07, + 0.07, + 0.2 + ] + ] + }, + { + "name": "panel_closing", + "delays": [ + [ + 0.1, + 0.1, + 0.07, + 0.07, + 0.07, + 0.2 + ] + ] + }, + { + "name": "panel_open", + "delays": [ + [ + 1 + ] + ] + }, + { + "name": "panel_opening", + "delays": [ + [ + 0.1, + 0.1, + 0.07, + 0.07, + 0.07, + 0.2 + ] + ] + }, + { + "name": "sparks", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "sparks_broken", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "sparks_damaged", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 1.7 + ] + ] + }, + { + "name": "sparks_open", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "welded" + }, + { + "name": "emergency_unlit", + "delays": [ + [ + 0.4, + 0.4 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/open.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/open.png new file mode 100644 index 0000000000..667b6bcf00 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/open.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/opening.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/opening.png new file mode 100644 index 0000000000..084547837b Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/opening.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/opening_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/opening_unlit.png new file mode 100644 index 0000000000..84933bd5ed Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/opening_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/panel_closing.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/panel_closing.png new file mode 100644 index 0000000000..db7be0bc4a Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/panel_closing.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/panel_open.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/panel_open.png new file mode 100644 index 0000000000..24eb2aedc2 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/panel_open.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/panel_opening.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/panel_opening.png new file mode 100644 index 0000000000..fc90acd637 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/panel_opening.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/sparks.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/sparks.png new file mode 100644 index 0000000000..dd67e88a31 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/sparks.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/sparks_broken.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/sparks_broken.png new file mode 100644 index 0000000000..fb5d774588 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/sparks_broken.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/sparks_damaged.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/sparks_damaged.png new file mode 100644 index 0000000000..f16a028dee Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/sparks_damaged.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/sparks_open.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/sparks_open.png new file mode 100644 index 0000000000..630eabb976 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/sparks_open.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/welded.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/welded.png new file mode 100644 index 0000000000..a0040dfdc7 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Glass/hydroponics.rsi/welded.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/assembly.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/assembly.png new file mode 100644 index 0000000000..c0d8c9c7d5 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/assembly.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/bolted_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/bolted_unlit.png new file mode 100644 index 0000000000..6857f2a241 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/bolted_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/closed.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/closed.png new file mode 100644 index 0000000000..bba4b80308 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/closed.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/closed_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/closed_unlit.png new file mode 100644 index 0000000000..c78d01c42d Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/closed_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/closing.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/closing.png new file mode 100644 index 0000000000..9963ddf614 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/closing.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/closing_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/closing_unlit.png new file mode 100644 index 0000000000..2a71f76d5d Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/closing_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/deny_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/deny_unlit.png new file mode 100644 index 0000000000..7c56263f83 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/deny_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/emergency_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/emergency_unlit.png new file mode 100644 index 0000000000..817f2fb3f9 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/emergency_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/meta.json b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/meta.json new file mode 100644 index 0000000000..def0b429a2 --- /dev/null +++ b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/meta.json @@ -0,0 +1,195 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c6e3401f2e7e1e55c57060cdf956a98ef1fefc24, hydroponics version made by BackeTako (github) for ss14", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "assembly" + }, + { + "name": "bolted_unlit" + }, + { + "name": "closed" + }, + { + "name": "closed_unlit" + }, + { + "name": "closing", + "delays": [ + [ + 0.1, + 0.1, + 0.07, + 0.07, + 0.07, + 0.2 + ] + ] + }, + { + "name": "closing_unlit", + "delays": [ + [ + 0.1, + 0.1, + 0.07, + 0.07, + 0.07, + 0.2 + ] + ] + }, + { + "name": "deny_unlit", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "open", + "delays": [ + [ + 1 + ] + ] + }, + { + "name": "opening", + "delays": [ + [ + 0.1, + 0.1, + 0.07, + 0.07, + 0.07, + 0.2 + ] + ] + }, + { + "name": "opening_unlit", + "delays": [ + [ + 0.1, + 0.1, + 0.07, + 0.07, + 0.07, + 0.2 + ] + ] + }, + { + "name": "panel_closing", + "delays": [ + [ + 0.1, + 0.1, + 0.07, + 0.07, + 0.07, + 0.2 + ] + ] + }, + { + "name": "panel_open", + "delays": [ + [ + 1 + ] + ] + }, + { + "name": "panel_opening", + "delays": [ + [ + 0.1, + 0.1, + 0.07, + 0.07, + 0.07, + 0.2 + ] + ] + }, + { + "name": "sparks", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "sparks_broken", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "sparks_damaged", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 1.7 + ] + ] + }, + { + "name": "sparks_open", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "welded" + }, + { + "name": "emergency_unlit", + "delays": [ + [ + 0.4, + 0.4 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/open.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/open.png new file mode 100644 index 0000000000..667b6bcf00 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/open.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/opening.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/opening.png new file mode 100644 index 0000000000..81aa75f7a5 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/opening.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/opening_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/opening_unlit.png new file mode 100644 index 0000000000..84933bd5ed Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/opening_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/panel_closing.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/panel_closing.png new file mode 100644 index 0000000000..db7be0bc4a Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/panel_closing.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/panel_open.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/panel_open.png new file mode 100644 index 0000000000..24eb2aedc2 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/panel_open.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/panel_opening.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/panel_opening.png new file mode 100644 index 0000000000..fc90acd637 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/panel_opening.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/sparks.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/sparks.png new file mode 100644 index 0000000000..dd67e88a31 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/sparks.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/sparks_broken.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/sparks_broken.png new file mode 100644 index 0000000000..fb5d774588 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/sparks_broken.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/sparks_damaged.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/sparks_damaged.png new file mode 100644 index 0000000000..f16a028dee Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/sparks_damaged.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/sparks_open.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/sparks_open.png new file mode 100644 index 0000000000..630eabb976 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/sparks_open.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/welded.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/welded.png new file mode 100644 index 0000000000..a0040dfdc7 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/hydroponics.rsi/welded.png differ diff --git a/Resources/Textures/Structures/Walls/solid.rsi/reinf_over0.png b/Resources/Textures/Structures/Walls/solid.rsi/reinf_over0.png index eb81655efa..338d7c8de0 100644 Binary files a/Resources/Textures/Structures/Walls/solid.rsi/reinf_over0.png and b/Resources/Textures/Structures/Walls/solid.rsi/reinf_over0.png differ diff --git a/Resources/Textures/Tiles/attributions.yml b/Resources/Textures/Tiles/attributions.yml index 6a6f545d1e..652947cb53 100644 --- a/Resources/Textures/Tiles/attributions.yml +++ b/Resources/Textures/Tiles/attributions.yml @@ -26,7 +26,7 @@ copyright: "Taken from /tg/station at commit 6665eec76c98a4f3f89bebcd10b34b47dcc0b8ae." source: "https://github.com/tgstation/tgstation/" -- files: ["blue_circuit.png", "cropped_parallax.png", "eighties.png", "gold.png", "grass.png", "ironsand1.png", "ironsand2.png", "ironsand3.png", "ironsand4.png", "junglegrass.png", "lattice.png", "reinforced.png", "silver.png", "snow.png", "wood.png"] +- files: ["blue_circuit.png", "cropped_parallax.png", "eighties.png", "gold.png", "grass.png", "ironsand1.png", "ironsand2.png", "ironsand3.png", "ironsand4.png", "junglegrass.png", "lattice.png", "red_circuit.png", "reinforced.png", "silver.png", "snow.png", "wood.png"] license: "CC-BY-SA-3.0" copyright: "tgstation commit 8abb19545828230d92ba18827feeb42a67a55d49, cropped_parallax modified from parallax." source: "https://github.com/tgstation/tgstation/" diff --git a/Resources/Textures/Tiles/blue_circuit.png b/Resources/Textures/Tiles/blue_circuit.png index 021c5363d6..0a95b49aa5 100644 Binary files a/Resources/Textures/Tiles/blue_circuit.png and b/Resources/Textures/Tiles/blue_circuit.png differ diff --git a/Resources/Textures/Tiles/green_circuit.png b/Resources/Textures/Tiles/green_circuit.png index 1628c20ae7..0638d7bde5 100644 Binary files a/Resources/Textures/Tiles/green_circuit.png and b/Resources/Textures/Tiles/green_circuit.png differ diff --git a/Resources/Textures/Tiles/red_circuit.png b/Resources/Textures/Tiles/red_circuit.png new file mode 100644 index 0000000000..44e33bdee3 Binary files /dev/null and b/Resources/Textures/Tiles/red_circuit.png differ diff --git a/Resources/Textures/Tiles/steel.png b/Resources/Textures/Tiles/steel.png index b7792ef138..e41e7a1804 100644 Binary files a/Resources/Textures/Tiles/steel.png and b/Resources/Textures/Tiles/steel.png differ diff --git a/RobustToolbox b/RobustToolbox index c86cb0b795..1c3ea968e4 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit c86cb0b7951cc4a662c7292138c5f45d868c5f58 +Subproject commit 1c3ea968e4ab66d57ffe014f6ee207e4d0d2c403