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