From 24993c3ffef9aaa48174a4ab3b59f02ed2d3cb3a Mon Sep 17 00:00:00 2001
From: Samuka <47865393+Samuka-C@users.noreply.github.com>
Date: Tue, 2 Dec 2025 11:28:33 -0300
Subject: [PATCH 001/360] Fix xenoborg evac calling announcment (#41437)
* no longer calls evac if evac is called or if the round is over
* can't recall shuttle
* some commentary
* can recall next round
* cancel evac recalling
* add this back
* only call once
* admins can recall anyway now
* 1 bool is better than 2 i guess
* some cleanup
---------
Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
---
.../Commands/ShuttleCommands.cs | 2 +-
.../Components/XenoborgsRuleComponent.cs | 7 +++
.../GameTicking/Rules/XenoborgsRuleSystem.cs | 15 +++---
Content.Server/RoundEnd/RoundEndSystem.cs | 46 +++++++++++++++----
4 files changed, 55 insertions(+), 15 deletions(-)
diff --git a/Content.Server/Administration/Commands/ShuttleCommands.cs b/Content.Server/Administration/Commands/ShuttleCommands.cs
index 677c38ac4e..6dffe1f52c 100644
--- a/Content.Server/Administration/Commands/ShuttleCommands.cs
+++ b/Content.Server/Administration/Commands/ShuttleCommands.cs
@@ -35,7 +35,7 @@ namespace Content.Server.Administration.Commands
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
- _roundEndSystem.CancelRoundEndCountdown(shell.Player?.AttachedEntity, false);
+ _roundEndSystem.CancelRoundEndCountdown(shell.Player?.AttachedEntity, forceRecall: true);
}
}
}
diff --git a/Content.Server/GameTicking/Rules/Components/XenoborgsRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/XenoborgsRuleComponent.cs
index c7496bb0fb..4a018d7e6c 100644
--- a/Content.Server/GameTicking/Rules/Components/XenoborgsRuleComponent.cs
+++ b/Content.Server/GameTicking/Rules/Components/XenoborgsRuleComponent.cs
@@ -36,4 +36,11 @@ public sealed partial class XenoborgsRuleComponent : Component
///
[DataField]
public bool MothershipCoreDeathAnnouncmentSent = false;
+
+ ///
+ /// If the emergency shuttle trigged by was already called.
+ /// Will only call once. if a admin recalls it. it won't call again unless this is set to false by a admin
+ ///
+ [DataField]
+ public bool XenoborgShuttleCalled = false;
}
diff --git a/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs b/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs
index 97a6efb471..cda1bce258 100644
--- a/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs
+++ b/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs
@@ -100,14 +100,17 @@ public sealed class XenoborgsRuleSystem : GameRuleSystem
xenoborgsRuleComponent.MaxNumberXenoborgs = Math.Max(xenoborgsRuleComponent.MaxNumberXenoborgs, numXenoborgs);
- if ((float)numXenoborgs / (numHumans + numXenoborgs) > xenoborgsRuleComponent.XenoborgShuttleCallPercentage)
+ if (xenoborgsRuleComponent.XenoborgShuttleCalled
+ || (float)numXenoborgs / (numHumans + numXenoborgs) <= xenoborgsRuleComponent.XenoborgShuttleCallPercentage
+ || _roundEnd.IsRoundEndRequested())
+ return;
+
+ foreach (var station in _station.GetStations())
{
- foreach (var station in _station.GetStations())
- {
- _chatSystem.DispatchStationAnnouncement(station, Loc.GetString("xenoborg-shuttle-call"), colorOverride: Color.BlueViolet);
- }
- _roundEnd.RequestRoundEnd(null, false);
+ _chatSystem.DispatchStationAnnouncement(station, Loc.GetString("xenoborg-shuttle-call"), colorOverride: Color.BlueViolet);
}
+ _roundEnd.RequestRoundEnd(null, false, cantRecall: true);
+ xenoborgsRuleComponent.XenoborgShuttleCalled = true;
}
protected override void Started(EntityUid uid, XenoborgsRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
diff --git a/Content.Server/RoundEnd/RoundEndSystem.cs b/Content.Server/RoundEnd/RoundEndSystem.cs
index f59203c50c..8e728a962f 100644
--- a/Content.Server/RoundEnd/RoundEndSystem.cs
+++ b/Content.Server/RoundEnd/RoundEndSystem.cs
@@ -56,6 +56,11 @@ namespace Content.Server.RoundEnd
public TimeSpan? ExpectedShuttleLength => ExpectedCountdownEnd - LastCountdownStart;
public TimeSpan? ShuttleTimeLeft => ExpectedCountdownEnd - _gameTiming.CurTime;
+ ///
+ /// If the shuttle can't be recalled. if set to true, the station wont be able to recall
+ ///
+ public bool CantRecall = false;
+
public TimeSpan AutoCallStartTime;
private bool _autoCalledBefore = false;
@@ -85,6 +90,8 @@ namespace Content.Server.RoundEnd
_cooldownTokenSource = null;
}
+ CantRecall = false;
+
LastCountdownStart = null;
ExpectedCountdownEnd = null;
SetAutoCallTime();
@@ -116,7 +123,7 @@ namespace Content.Server.RoundEnd
public bool CanCallOrRecall()
{
- return _cooldownTokenSource == null;
+ return _cooldownTokenSource == null && !CantRecall;
}
public bool IsRoundEndRequested()
@@ -124,7 +131,15 @@ namespace Content.Server.RoundEnd
return _countdownTokenSource != null;
}
- public void RequestRoundEnd(EntityUid? requester = null, bool checkCooldown = true, string text = "round-end-system-shuttle-called-announcement", string name = "round-end-system-shuttle-sender-announcement")
+ ///
+ /// Starts the process of ending the round by calling evac
+ ///
+ ///
+ ///
+ /// text in the announcement of shuttle calling
+ /// name in the announcement of shuttle calling
+ /// if the station shouldn't be able to recall the shuttle
+ public void RequestRoundEnd(EntityUid? requester = null, bool checkCooldown = true, string text = "round-end-system-shuttle-called-announcement", string name = "round-end-system-shuttle-sender-announcement", bool cantRecall = false)
{
var duration = DefaultCountdownDuration;
@@ -139,10 +154,19 @@ namespace Content.Server.RoundEnd
}
}
- RequestRoundEnd(duration, requester, checkCooldown, text, name);
+ RequestRoundEnd(duration, requester, checkCooldown, text, name, cantRecall);
}
- public void RequestRoundEnd(TimeSpan countdownTime, EntityUid? requester = null, bool checkCooldown = true, string text = "round-end-system-shuttle-called-announcement", string name = "round-end-system-shuttle-sender-announcement")
+ ///
+ /// Starts the process of ending the round by calling evac
+ ///
+ /// time for evac to arrive
+ ///
+ ///
+ /// text in the announcement of shuttle calling
+ /// name in the announcement of shuttle calling
+ /// if the station shouldn't be able to recall the shuttle
+ public void RequestRoundEnd(TimeSpan countdownTime, EntityUid? requester = null, bool checkCooldown = true, string text = "round-end-system-shuttle-called-announcement", string name = "round-end-system-shuttle-sender-announcement", bool cantRecall = false)
{
if (_gameTicker.RunLevel != GameRunLevel.InRound)
return;
@@ -154,6 +178,7 @@ namespace Content.Server.RoundEnd
return;
_countdownTokenSource = new();
+ CantRecall = cantRecall;
if (requester != null)
{
@@ -214,12 +239,17 @@ namespace Content.Server.RoundEnd
}
}
- public void CancelRoundEndCountdown(EntityUid? requester = null, bool checkCooldown = true)
+ public void CancelRoundEndCountdown(EntityUid? requester = null, bool forceRecall = false)
{
- if (_gameTicker.RunLevel != GameRunLevel.InRound) return;
- if (checkCooldown && _cooldownTokenSource != null) return;
+ if (_gameTicker.RunLevel != GameRunLevel.InRound)
+ return;
+
+ if (!forceRecall && (CantRecall || _cooldownTokenSource != null))
+ return;
+
+ if (_countdownTokenSource == null)
+ return;
- if (_countdownTokenSource == null) return;
_countdownTokenSource.Cancel();
_countdownTokenSource = null;
From 6486b81397fded692195f407ddbc64170bbd34a5 Mon Sep 17 00:00:00 2001
From: BarryNorfolk
Date: Thu, 22 Jan 2026 19:54:42 +0100
Subject: [PATCH 002/360] Downstream fix for xenoborg evac calls
---
Content.Server/_DV/CosmicCult/CosmicCultSystem.Finale.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Content.Server/_DV/CosmicCult/CosmicCultSystem.Finale.cs b/Content.Server/_DV/CosmicCult/CosmicCultSystem.Finale.cs
index bb6ae0ad07..c0968ead8a 100644
--- a/Content.Server/_DV/CosmicCult/CosmicCultSystem.Finale.cs
+++ b/Content.Server/_DV/CosmicCult/CosmicCultSystem.Finale.cs
@@ -104,7 +104,7 @@ public sealed partial class CosmicCultSystem : SharedCosmicCultSystem
Dirty(uid, monument);
_ui.SetUiState(uid.Owner, MonumentKey.Key, new MonumentBuiState(monument));
- if (!_evac.EmergencyShuttleArrived && _roundEnd.IsRoundEndRequested()) _roundEnd.CancelRoundEndCountdown(checkCooldown: false);
+ if (!_evac.EmergencyShuttleArrived && _roundEnd.IsRoundEndRequested()) _roundEnd.CancelRoundEndCountdown();
var query = EntityQueryEnumerator();
while (query.MoveNext(out var cultist, out var cultComp))
{
From d531f34b9c41cacd3b1847ba63e655d4d4ed1d82 Mon Sep 17 00:00:00 2001
From: Princess Cheeseballs
<66055347+Princess-Cheeseballs@users.noreply.github.com>
Date: Tue, 2 Dec 2025 06:51:59 -0800
Subject: [PATCH 003/360] Fix Mothership cannot use their tools and BorgSystem
cleanup. (#41673)
Fixe
Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
---
.../Silicons/Borgs/SharedBorgSystem.API.cs | 13 ++++++-------
.../Silicons/Borgs/SharedBorgSystem.cs | 17 +++++------------
2 files changed, 11 insertions(+), 19 deletions(-)
diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.API.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.API.cs
index 821ef35d8d..3f86abb568 100644
--- a/Content.Shared/Silicons/Borgs/SharedBorgSystem.API.cs
+++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.API.cs
@@ -24,7 +24,7 @@ public abstract partial class SharedBorgSystem
if (!_mind.TryGetMind(chassis.Owner, out _, out _))
return false;
- if (!_mobState.IsAlive(chassis.Owner))
+ if (_mobState.IsIncapacitated(chassis.Owner))
return false;
return true;
@@ -39,13 +39,12 @@ public abstract partial class SharedBorgSystem
if (chassis.Comp.Active)
return false; // Already active.
- if (CanActivate(chassis))
- {
- SetActive(chassis, true, user);
- return true;
- }
+ if (!CanActivate(chassis))
+ return false;
+
+ SetActive(chassis, true, user);
+ return true;
- return false;
}
///
diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs
index d965a362ff..71818ca211 100644
--- a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs
+++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs
@@ -197,8 +197,8 @@ public abstract partial class SharedBorgSystem : EntitySystem
// Unpredicted because the event is raised on the server.
_popup.PopupEntity(Loc.GetString("borg-mind-added", ("name", Identity.Name(chassis.Owner, EntityManager))), chassis.Owner);
- if (CanActivate(chassis))
- SetActive(chassis, true);
+ TryActivate(chassis);
+
_appearance.SetData(chassis.Owner, BorgVisuals.HasPlayer, true);
}
@@ -285,14 +285,9 @@ public abstract partial class SharedBorgSystem : EntitySystem
private void OnMobStateChanged(Entity chassis, ref MobStateChangedEvent args)
{
if (args.NewMobState == MobState.Alive)
- {
- if (CanActivate(chassis))
- SetActive(chassis, true, user: args.Origin);
- }
+ TryActivate(chassis, args.Origin);
else
- {
SetActive(chassis, false, user: args.Origin);
- }
}
private void OnBeingGibbed(Entity chassis, ref BeingGibbedEvent args)
@@ -357,8 +352,7 @@ public abstract partial class SharedBorgSystem : EntitySystem
// Raised when a power cell is inserted.
private void OnPowerCellChanged(Entity chassis, ref PowerCellChangedEvent args)
{
- if (CanActivate(chassis))
- SetActive(chassis, true);
+ TryActivate(chassis);
}
public override void Update(float frameTime)
@@ -374,8 +368,7 @@ public abstract partial class SharedBorgSystem : EntitySystem
Dirty(uid, borgChassis);
// If we aren't drawing and suddenly get enough power to draw again, reenable.
- if (CanActivate((uid, borgChassis)))
- SetActive((uid, borgChassis), true);
+ TryActivate((uid, borgChassis));
}
}
}
From 6e7df0e06b2fcc384049fd2784a6b9277953865d Mon Sep 17 00:00:00 2001
From: Hitlinemoss <209321380+Hitlinemoss@users.noreply.github.com>
Date: Tue, 2 Dec 2025 11:43:43 -0500
Subject: [PATCH 004/360] Minor cleanup of crowbars.yml (#41672)
* Minor cleanup of crowbars.yml
* Or, not and
* I think the CrowbarRed tag actually is used for belt sprite stuff? Removing a code comment calling it a legacy tag.
---
.../Prototypes/Entities/Objects/Tools/crowbars.yml | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/Resources/Prototypes/Entities/Objects/Tools/crowbars.yml b/Resources/Prototypes/Entities/Objects/Tools/crowbars.yml
index 31a6b8ef06..50ba2b9f7f 100644
--- a/Resources/Prototypes/Entities/Objects/Tools/crowbars.yml
+++ b/Resources/Prototypes/Entities/Objects/Tools/crowbars.yml
@@ -1,8 +1,9 @@
- type: entity
- name: crowbar
+ abstract: true
parent: BaseItem
id: BaseCrowbar
- abstract: true
+ name: crowbar
+ description: A multipurpose tool used for many tasks, such as prying doors or bludgeoning interdimensional invaders.
components:
- type: EmitSoundOnLand
sound:
@@ -50,7 +51,6 @@
- type: entity
parent: BaseCrowbar
id: Crowbar
- description: A multipurpose tool to pry open doors and fight interdimensional invaders.
components:
- type: Sprite
state: icon
@@ -61,10 +61,8 @@
# Emergency (red) Crowbar
- type: entity
- name: emergency crowbar
parent: BaseCrowbar
id: CrowbarRed
- description: An emergency crowbar designed to pry open doors and firelocks during power outages.
components:
- type: Tag
tags:
@@ -99,7 +97,6 @@
- type: entity
parent: BaseCrowbar
id: CrowbarGreen
- description: A multipurpose tool to pry open doors and fight interdimensional invaders, printed from an autolathe.
components:
- type: Sprite
layers:
@@ -130,7 +127,6 @@
- type: entity
parent: BaseCrowbar
id: CrowbarOrange
- description: A multipurpose tool to pry open doors and fight interdimensional invaders, found in toolboxes.
components:
- type: Sprite
layers:
@@ -161,7 +157,6 @@
- type: entity
parent: BaseCrowbar
id: CrowbarYellow
- description: A multipurpose tool to pry open doors and fight interdimensional invaders, dispensed from Engineering.
components:
- type: Sprite
layers:
From 22c086c901847b417b76c7544fd8bcae7e73f627 Mon Sep 17 00:00:00 2001
From: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
Date: Tue, 2 Dec 2025 19:33:40 +0100
Subject: [PATCH 005/360] cleanup EntityStorageSystem (#40163)
---
.../Storage/Systems/EntityStorageSystem.cs | 42 +-----
.../EntitySystems/EntityStorageSystem.cs | 88 ++----------
.../Components/EntityStorageComponent.cs | 93 ++++++-------
.../InsideEntityStorageComponent.cs | 12 +-
.../SharedEntityStorageSystem.cs | 125 ++++++++++--------
5 files changed, 133 insertions(+), 227 deletions(-)
diff --git a/Content.Client/Storage/Systems/EntityStorageSystem.cs b/Content.Client/Storage/Systems/EntityStorageSystem.cs
index ca2b986667..96dc353c48 100644
--- a/Content.Client/Storage/Systems/EntityStorageSystem.cs
+++ b/Content.Client/Storage/Systems/EntityStorageSystem.cs
@@ -1,43 +1,5 @@
-using System.Diagnostics.CodeAnalysis;
-using Content.Client.Storage.Components;
-using Content.Shared.Destructible;
-using Content.Shared.Foldable;
-using Content.Shared.Interaction;
-using Content.Shared.Lock;
-using Content.Shared.Movement.Events;
-using Content.Shared.Storage.Components;
-using Content.Shared.Storage.EntitySystems;
-using Content.Shared.Verbs;
-using Robust.Shared.GameStates;
+using Content.Shared.Storage.EntitySystems;
namespace Content.Client.Storage.Systems;
-public sealed class EntityStorageSystem : SharedEntityStorageSystem
-{
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent(OnEntityUnpausedEvent);
- SubscribeLocalEvent(OnComponentInit);
- SubscribeLocalEvent(OnComponentStartup);
- SubscribeLocalEvent(OnInteract, after: new[] { typeof(LockSystem) });
- SubscribeLocalEvent(OnLockToggleAttempt);
- SubscribeLocalEvent(OnDestruction);
- SubscribeLocalEvent>(AddToggleOpenVerb);
- SubscribeLocalEvent(OnRelayMovement);
- SubscribeLocalEvent(OnFoldAttempt);
-
- SubscribeLocalEvent(OnGetState);
- SubscribeLocalEvent(OnHandleState);
- }
-
- public override bool ResolveStorage(EntityUid uid, [NotNullWhen(true)] ref EntityStorageComponent? component)
- {
- if (component != null)
- return true;
-
- TryComp(uid, out var storage);
- component = storage;
- return component != null;
- }
-}
+public sealed class EntityStorageSystem : SharedEntityStorageSystem;
diff --git a/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs b/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs
index c38a3e9b17..e22a4f5d7e 100644
--- a/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs
+++ b/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs
@@ -1,22 +1,10 @@
-using System.Diagnostics.CodeAnalysis;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Systems;
using Content.Server.Construction;
using Content.Server.Construction.Components;
-using Content.Server.Storage.Components;
-using Content.Shared.Destructible;
-using Content.Shared.Explosion;
-using Content.Shared.Foldable;
-using Content.Shared.Interaction;
-using Content.Shared.Lock;
-using Content.Shared.Movement.Events;
using Content.Shared.Storage.Components;
using Content.Shared.Storage.EntitySystems;
-using Content.Shared.Tools.Systems;
-using Content.Shared.Verbs;
using Robust.Server.GameObjects;
-using Robust.Shared.Containers;
-using Robust.Shared.GameStates;
using Robust.Shared.Map;
namespace Content.Server.Storage.EntitySystems;
@@ -32,30 +20,11 @@ public sealed class EntityStorageSystem : SharedEntityStorageSystem
{
base.Initialize();
- /* CompRef things */
- SubscribeLocalEvent(OnEntityUnpausedEvent);
- SubscribeLocalEvent(OnComponentInit);
- SubscribeLocalEvent(OnComponentStartup);
- SubscribeLocalEvent(OnInteract, after: new[] { typeof(LockSystem) });
- SubscribeLocalEvent(OnLockToggleAttempt);
- SubscribeLocalEvent(OnDestruction);
- SubscribeLocalEvent>(AddToggleOpenVerb);
- SubscribeLocalEvent(OnRelayMovement);
- SubscribeLocalEvent(OnFoldAttempt);
-
- SubscribeLocalEvent(OnGetState);
- SubscribeLocalEvent(OnHandleState);
- /* CompRef things */
-
SubscribeLocalEvent(OnMapInit);
- SubscribeLocalEvent(OnWeldableAttempt);
- SubscribeLocalEvent(OnExploded);
SubscribeLocalEvent(OnInsideInhale);
SubscribeLocalEvent(OnInsideExhale);
SubscribeLocalEvent(OnInsideExposed);
-
- SubscribeLocalEvent(OnRemoved);
}
private void OnMapInit(EntityUid uid, EntityStorageComponent component, MapInitEvent args)
@@ -76,64 +45,30 @@ public sealed class EntityStorageSystem : SharedEntityStorageSystem
_construction.AddContainer(uid, ContainerName, construction);
}
- public override bool ResolveStorage(EntityUid uid, [NotNullWhen(true)] ref EntityStorageComponent? component)
- {
- if (component != null)
- return true;
-
- TryComp(uid, out var storage);
- component = storage;
- return component != null;
- }
-
- private void OnWeldableAttempt(EntityUid uid, EntityStorageComponent component, WeldableAttemptEvent args)
- {
- if (component.Open)
- {
- args.Cancel();
- return;
- }
-
- if (component.Contents.Contains(args.User))
- {
- var msg = Loc.GetString("entity-storage-component-already-contains-user-message");
- Popup.PopupEntity(msg, args.User, args.User);
- args.Cancel();
- }
- }
-
- private void OnExploded(Entity ent, ref BeforeExplodeEvent args)
- {
- args.Contents.AddRange(ent.Comp.Contents.ContainedEntities);
- }
-
protected override void TakeGas(EntityUid uid, EntityStorageComponent component)
{
if (!component.Airtight)
return;
- var serverComp = (EntityStorageComponent) component;
- var tile = GetOffsetTileRef(uid, serverComp);
+ var tile = GetOffsetTileRef(uid, component);
- if (tile != null && _atmos.GetTileMixture(tile.Value.GridUid, null, tile.Value.GridIndices, true) is {} environment)
+ if (tile != null && _atmos.GetTileMixture(tile.Value.GridUid, null, tile.Value.GridIndices, true) is { } environment)
{
- _atmos.Merge(serverComp.Air, environment.RemoveVolume(serverComp.Air.Volume));
+ _atmos.Merge(component.Air, environment.RemoveVolume(component.Air.Volume));
}
}
public override void ReleaseGas(EntityUid uid, EntityStorageComponent component)
{
- var serverComp = (EntityStorageComponent) component;
-
- if (!serverComp.Airtight)
+ if (!component.Airtight)
return;
- var tile = GetOffsetTileRef(uid, serverComp);
+ var tile = GetOffsetTileRef(uid, component);
- if (tile != null && _atmos.GetTileMixture(tile.Value.GridUid, null, tile.Value.GridIndices, true) is {} environment)
+ if (tile != null && _atmos.GetTileMixture(tile.Value.GridUid, null, tile.Value.GridIndices, true) is { } environment)
{
- _atmos.Merge(environment, serverComp.Air);
- serverComp.Air.Clear();
+ _atmos.Merge(environment, component.Air);
+ component.Air.Clear();
}
}
@@ -149,13 +84,6 @@ public sealed class EntityStorageSystem : SharedEntityStorageSystem
return null;
}
- private void OnRemoved(EntityUid uid, InsideEntityStorageComponent component, EntGotRemovedFromContainerMessage args)
- {
- if (args.Container.Owner != component.Storage)
- return;
- RemComp(uid, component);
- }
-
#region Gas mix event handlers
private void OnInsideInhale(EntityUid uid, InsideEntityStorageComponent component, InhaleLocationEvent args)
diff --git a/Content.Shared/Storage/Components/EntityStorageComponent.cs b/Content.Shared/Storage/Components/EntityStorageComponent.cs
index 009083b2d8..ecfcccc45b 100644
--- a/Content.Shared/Storage/Components/EntityStorageComponent.cs
+++ b/Content.Shared/Storage/Components/EntityStorageComponent.cs
@@ -5,42 +5,61 @@ using Content.Shared.Whitelist;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
-using Robust.Shared.Serialization;
namespace Content.Shared.Storage.Components;
+///
+/// A storage component that stores nearby entities in a container when this object is opened or closed.
+/// This does not have an UI like grid storage, but just makes them disappear inside.
+/// Used for lockers, crates etc.
+///
[RegisterComponent, NetworkedComponent]
+[AutoGenerateComponentState, AutoGenerateComponentPause] // TODO: Field deltas
public sealed partial class EntityStorageComponent : Component, IGasMixtureHolder
{
- public readonly float MaxSize = 1.0f; // maximum width or height of an entity allowed inside the storage.
+ ///
+ /// Maximum width or height of an entity allowed inside the storage.
+ ///
+ [DataField, AutoNetworkedField]
+ public float MaxSize = 1.0f;
- public static readonly TimeSpan InternalOpenAttemptDelay = TimeSpan.FromSeconds(0.5);
+ ///
+ /// The delay between opening attempts when stuck inside an entity storage.
+ ///
+ [DataField, AutoNetworkedField]
+ public TimeSpan InternalOpenAttemptDelay = TimeSpan.FromSeconds(0.5);
+
+ ///
+ /// The next time a player stuck inside the entity storage can attempt to open it from inside.
+ ///
+ [DataField, AutoNetworkedField, AutoPausedField]
public TimeSpan NextInternalOpenAttempt;
///
- /// Collision masks that get removed when the storage gets opened.
+ /// Collision masks that get removed when the storage gets opened.
///
- public readonly int MasksToRemove = (int)(
+ [DataField]
+ public int MasksToRemove = (int)(
CollisionGroup.MidImpassable |
CollisionGroup.HighImpassable |
CollisionGroup.LowImpassable);
///
- /// Collision masks that were removed from ANY layer when the storage was opened;
+ /// Collision masks that were removed from ANY layer when the storage was opened;
///
[DataField]
public int RemovedMasks;
///
- /// The total amount of items that can fit in one entitystorage
+ /// The total amount of items that can fit in one entitystorage.
///
- [DataField]
+ [DataField, AutoNetworkedField]
public int Capacity = 30;
///
- /// Whether or not the entity still has collision when open
+ /// Whether or not the entity still has collision when open.
///
- [DataField]
+ [DataField, AutoNetworkedField]
public bool IsCollidableWhenOpen;
///
@@ -48,7 +67,7 @@ public sealed partial class EntityStorageComponent : Component, IGasMixtureHolde
/// If false, it prevents the storage from opening when the entity inside of it moves.
/// This is for objects that you want the player to move while inside, like large cardboard boxes, without opening the storage.
///
- [DataField]
+ [DataField, AutoNetworkedField]
public bool OpenOnMove = true;
//The offset for where items are emptied/vacuumed for the EntityStorage.
@@ -60,62 +79,62 @@ public sealed partial class EntityStorageComponent : Component, IGasMixtureHolde
public CollisionGroup EnteringOffsetCollisionFlags = CollisionGroup.Impassable | CollisionGroup.MidImpassable;
///
- /// How close you have to be to the "entering" spot to be able to enter
+ /// How close you have to be to the "entering" spot to be able to enter.
///
- [DataField]
+ [DataField, AutoNetworkedField]
public float EnteringRange = 0.18f;
///
- /// Whether or not to show the contents when the storage is closed
+ /// Whether or not to show the contents when the storage is closed.
///
[DataField]
public bool ShowContents;
///
- /// Whether or not light is occluded by the storage
+ /// Whether or not light is occluded by the storage.
///
[DataField]
public bool OccludesLight = true;
///
- /// Whether or not all the contents stored should be deleted with the entitystorage
+ /// Whether or not all the contents stored should be deleted with the entitystorage.
///
[DataField]
public bool DeleteContentsOnDestruction;
///
- /// Whether or not the container is sealed and traps air inside of it
+ /// Whether or not the container is sealed and traps air inside of it.
///
[DataField]
public bool Airtight = true;
///
- /// Whether or not the entitystorage is open or closed
+ /// Whether or not the entitystorage is open or closed.
///
- [DataField]
+ [DataField, AutoNetworkedField]
public bool Open;
///
- /// The sound made when closed
+ /// The sound made when closed.
///
[DataField]
public SoundSpecifier CloseSound = new SoundPathSpecifier("/Audio/Effects/closetclose.ogg");
///
- /// The sound made when open
+ /// The sound made when opened.
///
[DataField]
public SoundSpecifier OpenSound = new SoundPathSpecifier("/Audio/Effects/closetopen.ogg");
///
- /// Whitelist for what entities are allowed to be inserted into this container. If this is not null, the
- /// standard requirement that the entity must be an item or mob is waived.
+ /// Whitelist for what entities are allowed to be inserted into this container. If this is not null, the
+ /// standard requirement that the entity must be an item or mob is waived.
///
[DataField]
public EntityWhitelist? Whitelist;
///
- /// The contents of the storage
+ /// The contents of the storage.
///
[ViewVariables]
public Container Contents = default!;
@@ -128,32 +147,6 @@ public sealed partial class EntityStorageComponent : Component, IGasMixtureHolde
public GasMixture Air { get; set; } = new(200);
}
-[Serializable, NetSerializable]
-public sealed class EntityStorageComponentState : ComponentState
-{
- public bool Open;
-
- public int Capacity;
-
- public bool IsCollidableWhenOpen;
-
- public bool OpenOnMove;
-
- public float EnteringRange;
-
- public TimeSpan NextInternalOpenAttempt;
-
- public EntityStorageComponentState(bool open, int capacity, bool isCollidableWhenOpen, bool openOnMove, float enteringRange, TimeSpan nextInternalOpenAttempt)
- {
- Open = open;
- Capacity = capacity;
- IsCollidableWhenOpen = isCollidableWhenOpen;
- OpenOnMove = openOnMove;
- EnteringRange = enteringRange;
- NextInternalOpenAttempt = nextInternalOpenAttempt;
- }
-}
-
///
/// Raised on the entity being inserted whenever checking if an entity can be inserted into an entity storage.
///
diff --git a/Content.Shared/Storage/Components/InsideEntityStorageComponent.cs b/Content.Shared/Storage/Components/InsideEntityStorageComponent.cs
index 258ea07ec6..8d0b45a4a2 100644
--- a/Content.Shared/Storage/Components/InsideEntityStorageComponent.cs
+++ b/Content.Shared/Storage/Components/InsideEntityStorageComponent.cs
@@ -1,10 +1,16 @@
-namespace Content.Shared.Storage.Components;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Storage.Components;
///
-/// Added to entities contained within entity storage, for directed event purposes.
+/// Added to entities contained within entity storage, for directed event purposes.
///
-[RegisterComponent]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class InsideEntityStorageComponent : Component
{
+ ///
+ /// The entity storage this entity is inside.
+ ///
+ [DataField, AutoNetworkedField]
public EntityUid Storage;
}
diff --git a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs
index dd6f1a223e..93a534843a 100644
--- a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs
+++ b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs
@@ -1,10 +1,9 @@
-using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
-using Content.Shared.Body.Components;
using Content.Shared.Destructible;
using Content.Shared.Foldable;
using Content.Shared.Hands.Components;
+using Content.Shared.Explosion;
using Content.Shared.Interaction;
using Content.Shared.Item;
using Content.Shared.Lock;
@@ -19,7 +18,6 @@ using Content.Shared.ActionBlocker;
using Content.Shared.Mobs.Components;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
-using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Physics;
@@ -48,31 +46,23 @@ public abstract class SharedEntityStorageSystem : EntitySystem
public const string ContainerName = "entity_storage";
- protected void OnEntityUnpausedEvent(EntityUid uid, EntityStorageComponent component, EntityUnpausedEvent args)
+ public override void Initialize()
{
- component.NextInternalOpenAttempt += args.PausedTime;
- }
+ base.Initialize();
- protected void OnGetState(EntityUid uid, EntityStorageComponent component, ref ComponentGetState args)
- {
- args.State = new EntityStorageComponentState(component.Open,
- component.Capacity,
- component.IsCollidableWhenOpen,
- component.OpenOnMove,
- component.EnteringRange,
- component.NextInternalOpenAttempt);
- }
+ SubscribeLocalEvent(OnComponentInit);
+ SubscribeLocalEvent(OnComponentStartup);
+ SubscribeLocalEvent(OnInteract, after: new[] { typeof(LockSystem) });
+ SubscribeLocalEvent(OnLockToggleAttempt);
+ SubscribeLocalEvent(OnDestruction);
+ SubscribeLocalEvent>(AddToggleOpenVerb);
+ SubscribeLocalEvent(OnRelayMovement);
+ SubscribeLocalEvent(OnFoldAttempt);
- protected void OnHandleState(EntityUid uid, EntityStorageComponent component, ref ComponentHandleState args)
- {
- if (args.Current is not EntityStorageComponentState state)
- return;
- component.Open = state.Open;
- component.Capacity = state.Capacity;
- component.IsCollidableWhenOpen = state.IsCollidableWhenOpen;
- component.OpenOnMove = state.OpenOnMove;
- component.EnteringRange = state.EnteringRange;
- component.NextInternalOpenAttempt = state.NextInternalOpenAttempt;
+ SubscribeLocalEvent(OnWeldableAttempt);
+ SubscribeLocalEvent(OnExploded);
+
+ SubscribeLocalEvent(OnRemoved);
}
protected virtual void OnComponentInit(EntityUid uid, EntityStorageComponent component, ComponentInit args)
@@ -82,12 +72,12 @@ public abstract class SharedEntityStorageSystem : EntitySystem
component.Contents.OccludesLight = component.OccludesLight;
}
- protected virtual void OnComponentStartup(EntityUid uid, EntityStorageComponent component, ComponentStartup args)
+ private void OnComponentStartup(EntityUid uid, EntityStorageComponent component, ComponentStartup args)
{
_appearance.SetData(uid, StorageVisuals.Open, component.Open);
}
- protected void OnInteract(EntityUid uid, EntityStorageComponent component, ActivateInWorldEvent args)
+ private void OnInteract(EntityUid uid, EntityStorageComponent component, ActivateInWorldEvent args)
{
if (args.Handled || !args.Complex)
return;
@@ -96,9 +86,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
ToggleOpen(args.User, uid, component);
}
- public abstract bool ResolveStorage(EntityUid uid, [NotNullWhen(true)] ref EntityStorageComponent? component);
-
- protected void OnLockToggleAttempt(EntityUid uid, EntityStorageComponent target, ref LockToggleAttemptEvent args)
+ private void OnLockToggleAttempt(EntityUid uid, EntityStorageComponent target, ref LockToggleAttemptEvent args)
{
// Cannot (un)lock open lockers.
if (target.Open)
@@ -109,7 +97,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
args.Cancelled = true;
}
- protected void OnDestruction(EntityUid uid, EntityStorageComponent component, DestructionEventArgs args)
+ private void OnDestruction(EntityUid uid, EntityStorageComponent component, DestructionEventArgs args)
{
component.Open = true;
Dirty(uid, component);
@@ -125,7 +113,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
}
}
- protected void OnRelayMovement(EntityUid uid, EntityStorageComponent component, ref ContainerRelayMovementEntityEvent args)
+ private void OnRelayMovement(EntityUid uid, EntityStorageComponent component, ref ContainerRelayMovementEntityEvent args)
{
if (!HasComp(args.Entity))
return;
@@ -136,21 +124,54 @@ public abstract class SharedEntityStorageSystem : EntitySystem
if (_timing.CurTime < component.NextInternalOpenAttempt)
return;
- component.NextInternalOpenAttempt = _timing.CurTime + EntityStorageComponent.InternalOpenAttemptDelay;
+ component.NextInternalOpenAttempt = _timing.CurTime + component.InternalOpenAttemptDelay;
Dirty(uid, component);
if (component.OpenOnMove)
TryOpenStorage(args.Entity, uid);
}
- protected void OnFoldAttempt(EntityUid uid, EntityStorageComponent component, ref FoldAttemptEvent args)
+ private void OnFoldAttempt(EntityUid uid, EntityStorageComponent component, ref FoldAttemptEvent args)
{
if (args.Cancelled)
return;
+
args.Cancelled = component.Open || component.Contents.ContainedEntities.Count != 0;
}
- protected void AddToggleOpenVerb(EntityUid uid, EntityStorageComponent component, GetVerbsEvent args)
+ private void OnWeldableAttempt(EntityUid uid, EntityStorageComponent component, WeldableAttemptEvent args)
+ {
+ if (component.Open)
+ {
+ args.Cancel();
+ return;
+ }
+
+ if (component.Contents.Contains(args.User))
+ {
+ var msg = Loc.GetString("entity-storage-component-already-contains-user-message");
+ Popup.PopupEntity(msg, args.User, args.User);
+ args.Cancel();
+ }
+ }
+
+ private void OnExploded(Entity ent, ref BeforeExplodeEvent args)
+ {
+ args.Contents.AddRange(ent.Comp.Contents.ContainedEntities);
+ }
+
+ private void OnRemoved(EntityUid uid, InsideEntityStorageComponent component, EntGotRemovedFromContainerMessage args)
+ {
+ if (_timing.ApplyingState)
+ return; // the component removal is already networked on its own
+
+ if (args.Container.Owner != component.Storage)
+ return;
+
+ RemComp(uid, component);
+ }
+
+ private void AddToggleOpenVerb(EntityUid uid, EntityStorageComponent component, GetVerbsEvent args)
{
if (!args.CanAccess || !args.CanInteract)
return;
@@ -177,7 +198,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
public void ToggleOpen(EntityUid user, EntityUid target, EntityStorageComponent? component = null)
{
- if (!ResolveStorage(target, ref component))
+ if (!Resolve(target, ref component))
return;
if (component.Open)
@@ -192,7 +213,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
public void EmptyContents(EntityUid uid, EntityStorageComponent? component = null)
{
- if (!ResolveStorage(uid, ref component))
+ if (!Resolve(uid, ref component))
return;
var uidXform = Transform(uid);
@@ -205,7 +226,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
public void OpenStorage(EntityUid uid, EntityStorageComponent? component = null)
{
- if (!ResolveStorage(uid, ref component))
+ if (!Resolve(uid, ref component))
return;
if (component.Open)
@@ -226,7 +247,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
public void CloseStorage(EntityUid uid, EntityStorageComponent? component = null)
{
- if (!ResolveStorage(uid, ref component))
+ if (!Resolve(uid, ref component))
return;
if (!component.Open)
@@ -277,7 +298,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
public bool Insert(EntityUid toInsert, EntityUid container, EntityStorageComponent? component = null)
{
- if (!ResolveStorage(container, ref component))
+ if (!Resolve(container, ref component))
return false;
if (component.Open)
@@ -286,12 +307,14 @@ public abstract class SharedEntityStorageSystem : EntitySystem
return true;
}
+ // TODO: This should be done automatically for all containers
_joints.RecursiveClearJoints(toInsert);
if (!_container.Insert(toInsert, component.Contents))
return false;
var inside = EnsureComp(toInsert);
inside.Storage = container;
+ Dirty(toInsert, inside);
return true;
}
@@ -300,7 +323,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
if (!Resolve(container, ref xform, false))
return false;
- if (!ResolveStorage(container, ref component))
+ if (!Resolve(container, ref component))
return false;
_container.Remove(toRemove, component.Contents);
@@ -327,7 +350,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
public bool CanInsert(EntityUid toInsert, EntityUid container, EntityStorageComponent? component = null)
{
- if (!ResolveStorage(container, ref component))
+ if (!Resolve(container, ref component))
return false;
if (component.Open)
@@ -384,7 +407,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
public bool IsOpen(EntityUid target, EntityStorageComponent? component = null)
{
- if (!ResolveStorage(target, ref component))
+ if (!Resolve(target, ref component))
return false;
return component.Open;
@@ -392,7 +415,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
public bool CanOpen(EntityUid user, EntityUid target, bool silent = false, EntityStorageComponent? component = null)
{
- if (!ResolveStorage(target, ref component))
+ if (!Resolve(target, ref component))
return false;
if (!HasComp(user))
@@ -434,7 +457,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
public bool AddToContents(EntityUid toAdd, EntityUid container, EntityStorageComponent? component = null)
{
- if (!ResolveStorage(container, ref component))
+ if (!Resolve(container, ref component))
return false;
if (toAdd == container)
@@ -445,7 +468,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
private void ModifyComponents(EntityUid uid, EntityStorageComponent? component = null)
{
- if (!ResolveStorage(uid, ref component))
+ if (!Resolve(uid, ref component))
return;
if (!component.IsCollidableWhenOpen && TryComp(uid, out var fixtures) &&
@@ -475,13 +498,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
_appearance.SetData(uid, StorageVisuals.HasContents, component.Contents.ContainedEntities.Count > 0);
}
- protected virtual void TakeGas(EntityUid uid, EntityStorageComponent component)
- {
+ protected virtual void TakeGas(EntityUid uid, EntityStorageComponent component) { }
- }
-
- public virtual void ReleaseGas(EntityUid uid, EntityStorageComponent component)
- {
-
- }
+ public virtual void ReleaseGas(EntityUid uid, EntityStorageComponent component) { }
}
From 4b3e6e73a7980a3d1630ae320600f78e5b498484 Mon Sep 17 00:00:00 2001
From: BarryNorfolk
Date: Thu, 22 Jan 2026 19:50:34 +0100
Subject: [PATCH 006/360] Downstream fix for EntityStorageSystem changes
---
Content.Server/_DV/Autoclave/AutoclaveSystem.cs | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/Content.Server/_DV/Autoclave/AutoclaveSystem.cs b/Content.Server/_DV/Autoclave/AutoclaveSystem.cs
index b594990b7e..8468ed7827 100644
--- a/Content.Server/_DV/Autoclave/AutoclaveSystem.cs
+++ b/Content.Server/_DV/Autoclave/AutoclaveSystem.cs
@@ -2,6 +2,7 @@ using Content.Server.Power.EntitySystems;
using Content.Shared._DV.Autoclave;
using Content.Shared._DV.Surgery;
using Content.Shared.Power;
+using Content.Shared.Storage;
using Content.Shared.Storage.Components;
using Content.Shared.Storage.EntitySystems;
using Robust.Shared.GameObjects;
@@ -46,8 +47,7 @@ public sealed class AutoclaveSystem : EntitySystem
if (!(isPowered && isClosed))
continue;
- EntityStorageComponent? storageComponent = null;
- if (!_entityStorage.ResolveStorage(uid, ref storageComponent))
+ if (!TryComp(uid, out var storageComponent))
continue;
foreach (var containedEntity in storageComponent.Contents.ContainedEntities)
@@ -61,9 +61,8 @@ public sealed class AutoclaveSystem : EntitySystem
private void UpdateVisuals(EntityUid ent, bool isPowered, bool isClosed)
{
- EntityStorageComponent? storageComponent = null;
- bool hasDirtyContents =
- _entityStorage.ResolveStorage(ent, ref storageComponent)
+ var hasDirtyContents =
+ TryComp(ent, out var storageComponent)
&& storageComponent.Contents.ContainedEntities.Any(contained => _surgeryClean.RequiresCleaning(contained));
var (greenLight, redLight) = (isPowered, isClosed, hasDirtyContents) switch
From cfb0ac862df3cf0b0ff298b723d910d217f88fe1 Mon Sep 17 00:00:00 2001
From: Pok <113675512+Pok27@users.noreply.github.com>
Date: Tue, 2 Dec 2025 21:39:21 +0200
Subject: [PATCH 007/360] Move some admin components to shared (#41677)
* AdminSystem-move-to-shared
* review
---
.../Components/KillSignComponent.cs | 7 -------
.../Administration/Systems/BufferingSystem.cs | 7 +++++++
.../Administration/Systems/KillSignSystem.cs | 2 +-
.../Tests/PostMapInitTest.cs | 15 +++++++--------
.../Components/AdminMinigunComponent.cs | 7 -------
.../Components/KillSignComponent.cs | 7 -------
.../Components/SuperBonkComponent.cs | 3 ++-
.../Systems/AdminVerbSystem.Smites.cs | 8 ++++----
.../Systems/AdminVerbSystem.Tools.cs | 5 ++---
.../Administration/Systems/BufferingSystem.cs | 5 +++--
.../Components/AdminMinigunComponent.cs | 6 ++++++
.../Components/BufferingComponent.cs | 19 +++++++++++++------
.../Components/DisarmProneComponent.cs | 5 +----
...tandComponent.cs => HeadstandComponent.cs} | 0
.../Components/KillSignComponent.cs | 6 ++++++
.../Components/SharedKillSignComponent.cs | 9 ---------
.../StationInfiniteBatteryTargetComponent.cs | 11 +++++------
.../Administration/Systems/AdminGunSystem.cs | 4 ++--
.../Systems/SharedBufferingSystem.cs | 5 +++++
19 files changed, 64 insertions(+), 67 deletions(-)
delete mode 100644 Content.Client/Administration/Components/KillSignComponent.cs
create mode 100644 Content.Client/Administration/Systems/BufferingSystem.cs
delete mode 100644 Content.Server/Administration/Components/AdminMinigunComponent.cs
delete mode 100644 Content.Server/Administration/Components/KillSignComponent.cs
create mode 100644 Content.Shared/Administration/Components/AdminMinigunComponent.cs
rename {Content.Server => Content.Shared}/Administration/Components/BufferingComponent.cs (67%)
rename Content.Shared/Administration/Components/{SharedHeadstandComponent.cs => HeadstandComponent.cs} (100%)
create mode 100644 Content.Shared/Administration/Components/KillSignComponent.cs
delete mode 100644 Content.Shared/Administration/Components/SharedKillSignComponent.cs
rename {Content.Server => Content.Shared}/Administration/Components/StationInfiniteBatteryTargetComponent.cs (55%)
rename {Content.Server => Content.Shared}/Administration/Systems/AdminGunSystem.cs (80%)
create mode 100644 Content.Shared/Administration/Systems/SharedBufferingSystem.cs
diff --git a/Content.Client/Administration/Components/KillSignComponent.cs b/Content.Client/Administration/Components/KillSignComponent.cs
deleted file mode 100644
index 91c44ef3f2..0000000000
--- a/Content.Client/Administration/Components/KillSignComponent.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-using Content.Shared.Administration.Components;
-using Robust.Shared.GameStates;
-
-namespace Content.Client.Administration.Components;
-
-[RegisterComponent]
-public sealed partial class KillSignComponent : SharedKillSignComponent;
diff --git a/Content.Client/Administration/Systems/BufferingSystem.cs b/Content.Client/Administration/Systems/BufferingSystem.cs
new file mode 100644
index 0000000000..e511bbff36
--- /dev/null
+++ b/Content.Client/Administration/Systems/BufferingSystem.cs
@@ -0,0 +1,7 @@
+using Content.Shared.Administration.Systems;
+
+namespace Content.Client.Administration.Systems;
+
+public sealed class BufferingSystem : SharedBufferingSystem
+{
+}
diff --git a/Content.Client/Administration/Systems/KillSignSystem.cs b/Content.Client/Administration/Systems/KillSignSystem.cs
index c12f65f1f0..f15cfe6442 100644
--- a/Content.Client/Administration/Systems/KillSignSystem.cs
+++ b/Content.Client/Administration/Systems/KillSignSystem.cs
@@ -1,5 +1,5 @@
using System.Numerics;
-using Content.Client.Administration.Components;
+using Content.Shared.Administration.Components;
using Robust.Client.GameObjects;
using Robust.Shared.Utility;
diff --git a/Content.IntegrationTests/Tests/PostMapInitTest.cs b/Content.IntegrationTests/Tests/PostMapInitTest.cs
index 95860b1720..1459214bba 100644
--- a/Content.IntegrationTests/Tests/PostMapInitTest.cs
+++ b/Content.IntegrationTests/Tests/PostMapInitTest.cs
@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
+using YamlDotNet.RepresentationModel;
using Content.Server.Administration.Systems;
using Content.Server.GameTicking;
using Content.Server.Maps;
@@ -11,20 +12,18 @@ using Content.Server.Spawners.Components;
using Content.Server.Station.Components;
using Content.Shared.CCVar;
using Content.Shared.Roles;
+using Content.Shared.Station.Components;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
-using Robust.Shared.GameObjects;
-using Robust.Shared.Map;
-using Robust.Shared.Map.Components;
-using Robust.Shared.Prototypes;
-using Content.Shared.Station.Components;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Systems;
+using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
-using Robust.Shared.Utility;
-using YamlDotNet.RepresentationModel;
+using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
using Robust.Shared.Map.Events;
-
+using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
namespace Content.IntegrationTests.Tests
{
[TestFixture]
diff --git a/Content.Server/Administration/Components/AdminMinigunComponent.cs b/Content.Server/Administration/Components/AdminMinigunComponent.cs
deleted file mode 100644
index 368d3d3ba7..0000000000
--- a/Content.Server/Administration/Components/AdminMinigunComponent.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Content.Server.Administration.Components;
-
-[RegisterComponent]
-public sealed partial class AdminMinigunComponent : Component
-{
-
-}
diff --git a/Content.Server/Administration/Components/KillSignComponent.cs b/Content.Server/Administration/Components/KillSignComponent.cs
deleted file mode 100644
index 11479c32fc..0000000000
--- a/Content.Server/Administration/Components/KillSignComponent.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-using Content.Shared.Administration.Components;
-using Robust.Shared.GameStates;
-
-namespace Content.Server.Administration.Components;
-
-[RegisterComponent]
-public sealed partial class KillSignComponent : SharedKillSignComponent;
diff --git a/Content.Server/Administration/Components/SuperBonkComponent.cs b/Content.Server/Administration/Components/SuperBonkComponent.cs
index 0ceeccd136..9c20c4d88a 100644
--- a/Content.Server/Administration/Components/SuperBonkComponent.cs
+++ b/Content.Server/Administration/Components/SuperBonkComponent.cs
@@ -6,7 +6,8 @@ namespace Content.Server.Administration.Components;
///
/// Component to track the timer for the SuperBonk smite.
///
-[RegisterComponent, AutoGenerateComponentPause, Access(typeof(SuperBonkSystem))]
+[RegisterComponent, AutoGenerateComponentPause]
+[Access(typeof(SuperBonkSystem))]
public sealed partial class SuperBonkComponent : Component
{
///
diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs
index 36f7399d87..2ad927aac9 100644
--- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs
+++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs
@@ -1,4 +1,5 @@
-using Content.Server.Administration.Components;
+using System.Numerics;
+using System.Threading;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Systems;
using Content.Server.Electrocution;
@@ -17,12 +18,14 @@ using Content.Server.Tabletop.Components;
using Content.Shared.Actions;
using Content.Shared.Administration;
using Content.Shared.Administration.Components;
+using Content.Shared.Administration.Systems;
using Content.Shared.Atmos.Components;
using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Content.Shared.Clothing.Components;
using Content.Shared.Clumsy;
using Content.Shared.Cluwne;
+using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Database;
using Content.Shared.Electrocution;
@@ -54,9 +57,6 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Spawners;
using Robust.Shared.Utility;
-using System.Numerics;
-using System.Threading;
-using Content.Shared.Damage.Components;
using Timer = Robust.Shared.Timing.Timer;
namespace Content.Server.Administration.Systems;
diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs
index 8c7d3f33d1..f510ed52e9 100644
--- a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs
+++ b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs
@@ -1,11 +1,9 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
-using Content.Server.Administration.Components;
using Content.Server.Cargo.Components;
using Content.Server.Doors.Systems;
using Content.Server.Hands.Systems;
-using Content.Server.Power.Components;
using Content.Server.Revenant.Components; // Imp
using Content.Server.Revenant.EntitySystems; // Imp
using Content.Server.Stack;
@@ -15,10 +13,11 @@ using Content.Shared.Access;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Administration;
+using Content.Shared.Administration.Components;
+using Content.Shared.Administration.Systems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Construction.Components;
-using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Database;
using Content.Shared.Doors.Components;
diff --git a/Content.Server/Administration/Systems/BufferingSystem.cs b/Content.Server/Administration/Systems/BufferingSystem.cs
index f3df34e7d2..71e5c4d5c6 100644
--- a/Content.Server/Administration/Systems/BufferingSystem.cs
+++ b/Content.Server/Administration/Systems/BufferingSystem.cs
@@ -1,12 +1,13 @@
using System.Numerics;
-using Content.Server.Administration.Components;
using Content.Shared.Administration;
+using Content.Shared.Administration.Components;
+using Content.Shared.Administration.Systems;
using Robust.Shared.Map;
using Robust.Shared.Random;
namespace Content.Server.Administration.Systems;
-public sealed class BufferingSystem : EntitySystem
+public sealed class BufferingSystem : SharedBufferingSystem
{
[Dependency] private readonly IRobustRandom _random = default!;
diff --git a/Content.Shared/Administration/Components/AdminMinigunComponent.cs b/Content.Shared/Administration/Components/AdminMinigunComponent.cs
new file mode 100644
index 0000000000..04a7aacd69
--- /dev/null
+++ b/Content.Shared/Administration/Components/AdminMinigunComponent.cs
@@ -0,0 +1,6 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Administration.Components;
+
+[RegisterComponent, NetworkedComponent]
+public sealed partial class AdminMinigunComponent : Component;
diff --git a/Content.Server/Administration/Components/BufferingComponent.cs b/Content.Shared/Administration/Components/BufferingComponent.cs
similarity index 67%
rename from Content.Server/Administration/Components/BufferingComponent.cs
rename to Content.Shared/Administration/Components/BufferingComponent.cs
index 1f80376596..3d365d00aa 100644
--- a/Content.Server/Administration/Components/BufferingComponent.cs
+++ b/Content.Shared/Administration/Components/BufferingComponent.cs
@@ -1,22 +1,29 @@
-using Content.Server.Administration.Systems;
+using Content.Shared.Administration.Systems;
-namespace Content.Server.Administration.Components;
+namespace Content.Shared.Administration.Components;
-[RegisterComponent, Access(typeof(BufferingSystem))]
+[RegisterComponent]
+[Access(typeof(SharedBufferingSystem))]
public sealed partial class BufferingComponent : Component
{
[DataField("minBufferTime")]
public float MinimumBufferTime = 0.5f;
+
[DataField("maxBufferTime")]
public float MaximumBufferTime = 1.5f;
+
[DataField("minTimeTilNextBuffer")]
public float MinimumTimeTilNextBuffer = 10.0f;
+
[DataField("maxTimeTilNextBuffer")]
public float MaximumTimeTilNextBuffer = 120.0f;
- [DataField("timeTilNextBuffer")]
+
+ [DataField]
public float TimeTilNextBuffer = 15.0f;
- [DataField("bufferingIcon")]
+
+ [DataField]
public EntityUid? BufferingIcon = null;
- [DataField("bufferingTimer")]
+
+ [DataField]
public float BufferingTimer = 0.0f;
}
diff --git a/Content.Shared/Administration/Components/DisarmProneComponent.cs b/Content.Shared/Administration/Components/DisarmProneComponent.cs
index 4ba11df8e2..86addab4f6 100644
--- a/Content.Shared/Administration/Components/DisarmProneComponent.cs
+++ b/Content.Shared/Administration/Components/DisarmProneComponent.cs
@@ -8,7 +8,4 @@ namespace Content.Shared.Administration.Components;
///
[RegisterComponent, NetworkedComponent]
[Access(typeof(SharedMeleeWeaponSystem))]
-public sealed partial class DisarmProneComponent : Component
-{
-
-}
+public sealed partial class DisarmProneComponent : Component;
diff --git a/Content.Shared/Administration/Components/SharedHeadstandComponent.cs b/Content.Shared/Administration/Components/HeadstandComponent.cs
similarity index 100%
rename from Content.Shared/Administration/Components/SharedHeadstandComponent.cs
rename to Content.Shared/Administration/Components/HeadstandComponent.cs
diff --git a/Content.Shared/Administration/Components/KillSignComponent.cs b/Content.Shared/Administration/Components/KillSignComponent.cs
new file mode 100644
index 0000000000..34c36759cc
--- /dev/null
+++ b/Content.Shared/Administration/Components/KillSignComponent.cs
@@ -0,0 +1,6 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Administration.Components;
+
+[RegisterComponent, NetworkedComponent]
+public sealed partial class KillSignComponent : Component;
diff --git a/Content.Shared/Administration/Components/SharedKillSignComponent.cs b/Content.Shared/Administration/Components/SharedKillSignComponent.cs
deleted file mode 100644
index 9a95454f72..0000000000
--- a/Content.Shared/Administration/Components/SharedKillSignComponent.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Administration.Components;
-
-[NetworkedComponent]
-public abstract partial class SharedKillSignComponent : Component
-{
-
-}
diff --git a/Content.Server/Administration/Components/StationInfiniteBatteryTargetComponent.cs b/Content.Shared/Administration/Components/StationInfiniteBatteryTargetComponent.cs
similarity index 55%
rename from Content.Server/Administration/Components/StationInfiniteBatteryTargetComponent.cs
rename to Content.Shared/Administration/Components/StationInfiniteBatteryTargetComponent.cs
index 5543d5ed8c..d26527a472 100644
--- a/Content.Server/Administration/Components/StationInfiniteBatteryTargetComponent.cs
+++ b/Content.Shared/Administration/Components/StationInfiniteBatteryTargetComponent.cs
@@ -1,10 +1,9 @@
-namespace Content.Server.Administration.Components;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Administration.Components;
///
/// This is used for the admin map-wide/station-wide/grid-wide infinite power trick.
///
-[RegisterComponent]
-public sealed partial class StationInfiniteBatteryTargetComponent : Component
-{
-
-}
+[RegisterComponent, NetworkedComponent]
+public sealed partial class StationInfiniteBatteryTargetComponent : Component;
diff --git a/Content.Server/Administration/Systems/AdminGunSystem.cs b/Content.Shared/Administration/Systems/AdminGunSystem.cs
similarity index 80%
rename from Content.Server/Administration/Systems/AdminGunSystem.cs
rename to Content.Shared/Administration/Systems/AdminGunSystem.cs
index 6270481a3c..28ff142709 100644
--- a/Content.Server/Administration/Systems/AdminGunSystem.cs
+++ b/Content.Shared/Administration/Systems/AdminGunSystem.cs
@@ -1,7 +1,7 @@
-using Content.Server.Administration.Components;
+using Content.Shared.Administration.Components;
using Content.Shared.Weapons.Ranged.Events;
-namespace Content.Server.Administration.Systems;
+namespace Content.Shared.Administration.Systems;
public sealed class AdminGunSystem : EntitySystem
{
diff --git a/Content.Shared/Administration/Systems/SharedBufferingSystem.cs b/Content.Shared/Administration/Systems/SharedBufferingSystem.cs
new file mode 100644
index 0000000000..a1f375ebfd
--- /dev/null
+++ b/Content.Shared/Administration/Systems/SharedBufferingSystem.cs
@@ -0,0 +1,5 @@
+namespace Content.Shared.Administration.Systems;
+
+public abstract class SharedBufferingSystem : EntitySystem
+{
+}
From bede43b560670b7184b34d5d54cf0704021b9747 Mon Sep 17 00:00:00 2001
From: imatsoup <93290208+imatsoup@users.noreply.github.com>
Date: Wed, 3 Dec 2025 03:25:03 +0000
Subject: [PATCH 008/360] Change default Rat King order from 'Loose' to
'Follow' (#41680)
Update RatKingComponent.cs
---
Content.Shared/RatKing/RatKingComponent.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Content.Shared/RatKing/RatKingComponent.cs b/Content.Shared/RatKing/RatKingComponent.cs
index 712d4ae3a1..f3830cf4f7 100644
--- a/Content.Shared/RatKing/RatKingComponent.cs
+++ b/Content.Shared/RatKing/RatKingComponent.cs
@@ -56,7 +56,7 @@ public sealed partial class RatKingComponent : Component
///
[DataField("currentOrders"), ViewVariables(VVAccess.ReadWrite)]
[AutoNetworkedField]
- public RatKingOrderType CurrentOrder = RatKingOrderType.Loose;
+ public RatKingOrderType CurrentOrder = RatKingOrderType.Follow;
///
/// The servants that the rat king is currently controlling
From 42e7e8fe88339bd684636e71c7d4f2381d814c72 Mon Sep 17 00:00:00 2001
From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com>
Date: Wed, 3 Dec 2025 12:55:29 +0100
Subject: [PATCH 009/360] Add status effect support to Traits, change
PainNumbness to be a status effect (#41646)
* Initial commit
* Review comments
* Jobify
* Prototype(effect)
---
.../DamageOverlayUiController.cs | 4 ++-
Content.Server/Cloning/CloningSystem.cs | 35 +++++++++++++++++++
.../Jobs/ApplyStatusEffectSpecial.cs | 27 ++++++++++++++
Content.Server/Traits/TraitSystem.cs | 11 +++++-
.../Cloning/CloningSettingsPrototype.cs | 6 ++++
Content.Shared/Roles/JobSpecial.cs | 4 ++-
.../StatusEffectNew/StatusEffectSystem.API.cs | 1 +
.../StatusEffectSystem.Relay.cs | 5 +++
.../Traits/Assorted/PainNumbnessComponent.cs | 16 ---------
.../PainNumbnessStatusEffectComponent.cs | 20 +++++++++++
.../Traits/Assorted/PainNumbnessSystem.cs | 32 +++++++++--------
Content.Shared/Traits/TraitPrototype.cs | 9 +++++
.../Prototypes/Entities/Mobs/Player/clone.yml | 1 -
.../Entities/StatusEffects/body.yml | 25 +++++++++----
Resources/Prototypes/Traits/disabilities.yml | 6 ++--
15 files changed, 158 insertions(+), 44 deletions(-)
create mode 100644 Content.Server/Jobs/ApplyStatusEffectSpecial.cs
delete mode 100644 Content.Shared/Traits/Assorted/PainNumbnessComponent.cs
create mode 100644 Content.Shared/Traits/Assorted/PainNumbnessStatusEffectComponent.cs
diff --git a/Content.Client/UserInterface/Systems/DamageOverlays/DamageOverlayUiController.cs b/Content.Client/UserInterface/Systems/DamageOverlays/DamageOverlayUiController.cs
index 20db76554d..f709df4b77 100644
--- a/Content.Client/UserInterface/Systems/DamageOverlays/DamageOverlayUiController.cs
+++ b/Content.Client/UserInterface/Systems/DamageOverlays/DamageOverlayUiController.cs
@@ -3,6 +3,7 @@ using Content.Shared.FixedPoint;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
+using Content.Shared.StatusEffectNew;
using Content.Shared.Traits.Assorted;
using JetBrains.Annotations;
using Robust.Client.Graphics;
@@ -20,6 +21,7 @@ public sealed class DamageOverlayUiController : UIController
[Dependency] private readonly IPlayerManager _playerManager = default!;
[UISystemDependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
+ [UISystemDependency] private readonly StatusEffectsSystem _statusEffects = default!;
private Overlays.DamageOverlay _overlay = default!;
public override void Initialize()
@@ -98,7 +100,7 @@ public sealed class DamageOverlayUiController : UIController
FixedPoint2 painLevel = 0;
_overlay.PainLevel = 0;
- if (!EntityManager.HasComponent(entity))
+ if (!_statusEffects.TryEffectsWithComp(entity, out _))
{
foreach (var painDamageType in damageable.PainDamageGroups)
{
diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs
index 654a2de07e..bd53e32567 100644
--- a/Content.Server/Cloning/CloningSystem.cs
+++ b/Content.Server/Cloning/CloningSystem.cs
@@ -9,6 +9,7 @@ using Content.Shared.Implants;
using Content.Shared.Implants.Components;
using Content.Shared.NameModifier.EntitySystems;
using Content.Shared.StatusEffect;
+using Content.Shared.StatusEffectNew.Components;
using Content.Shared.Storage;
using Content.Shared.Storage.EntitySystems;
using Content.Shared.Whitelist;
@@ -37,6 +38,7 @@ public sealed partial class CloningSystem : SharedCloningSystem
[Dependency] private readonly SharedStorageSystem _storage = default!;
[Dependency] private readonly SharedSubdermalImplantSystem _subdermalImplant = default!;
[Dependency] private readonly NameModifierSystem _nameMod = default!;
+ [Dependency] private readonly Shared.StatusEffectNew.StatusEffectsSystem _statusEffects = default!; //TODO: This system has to support both the old and new status effect systems, until the old is able to be fully removed.
///
/// Spawns a clone of the given humanoid mob at the specified location or in nullspace.
@@ -76,6 +78,10 @@ public sealed partial class CloningSystem : SharedCloningSystem
if (settings.CopyImplants)
CopyImplants(original, clone.Value, settings.CopyInternalStorage, settings.Whitelist, settings.Blacklist);
+ // Copy permanent status effects
+ if (settings.CopyStatusEffects)
+ CopyStatusEffects(original, clone.Value);
+
var originalName = _nameMod.GetBaseName(original);
// Set the clone's name. The raised events will also adjust their PDA and ID card names.
@@ -268,4 +274,33 @@ public sealed partial class CloningSystem : SharedCloningSystem
}
}
+
+ ///
+ /// Scans all permanent status effects applied to the original entity and transfers them to the clone.
+ ///
+ public void CopyStatusEffects(Entity original, Entity target)
+ {
+ if (!Resolve(original, ref original.Comp, false))
+ return;
+
+ if (original.Comp.ActiveStatusEffects is null)
+ return;
+
+ foreach (var effect in original.Comp.ActiveStatusEffects.ContainedEntities)
+ {
+ if (!TryComp(effect, out var effectComp))
+ continue;
+
+ //We are not interested in temporary effects, only permanent ones.
+ if (effectComp.EndEffectTime is not null)
+ continue;
+
+ var effectProto = Prototype(effect);
+
+ if (effectProto is null)
+ continue;
+
+ _statusEffects.TrySetStatusEffectDuration(target, effectProto);
+ }
+ }
}
diff --git a/Content.Server/Jobs/ApplyStatusEffectSpecial.cs b/Content.Server/Jobs/ApplyStatusEffectSpecial.cs
new file mode 100644
index 0000000000..b06ce114ba
--- /dev/null
+++ b/Content.Server/Jobs/ApplyStatusEffectSpecial.cs
@@ -0,0 +1,27 @@
+using Content.Shared.Roles;
+using Content.Shared.StatusEffectNew;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Jobs;
+
+///
+/// Adds permanent status effects to the entity.
+/// TODO: Move this, and other JobSpecials, from Server to Shared.
+///
+[UsedImplicitly]
+public sealed partial class ApplyStatusEffectSpecial : JobSpecial
+{
+ [DataField(required: true)]
+ public HashSet StatusEffects { get; private set; } = new();
+
+ public override void AfterEquip(EntityUid mob)
+ {
+ var entMan = IoCManager.Resolve();
+ var statusSystem = entMan.System();
+ foreach (var effect in StatusEffects)
+ {
+ statusSystem.TrySetStatusEffectDuration(mob, effect);
+ }
+ }
+}
diff --git a/Content.Server/Traits/TraitSystem.cs b/Content.Server/Traits/TraitSystem.cs
index 5e7675b76b..8e062785e2 100644
--- a/Content.Server/Traits/TraitSystem.cs
+++ b/Content.Server/Traits/TraitSystem.cs
@@ -2,6 +2,7 @@
// using Content.Shared.Hands.Components;
// using Content.Shared.Hands.EntitySystems;
// using Content.Shared.Roles;
+// using Content.Shared.StatusEffectNew;
// using Content.Shared.Traits;
// using Content.Shared.Whitelist;
// using Robust.Shared.Prototypes;
@@ -13,6 +14,7 @@
// [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
// [Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!;
// [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
+// [Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
//
// public override void Initialize()
// {
@@ -45,7 +47,14 @@
// continue;
//
// // Add all components required by the prototype
-// EntityManager.AddComponents(args.Mob, traitPrototype.Components, false);
+// if (traitPrototype.Components.Count > 0)
+// EntityManager.AddComponents(args.Mob, traitPrototype.Components, false);
+//
+// // Add all JobSpecials required by the prototype
+// foreach (var special in traitPrototype.Specials)
+// {
+// special.AfterEquip(args.Mob);
+// }
//
// // Begin DeltaV - Add overridden components
// if(traitPrototype.OverriddenComponents != null)
diff --git a/Content.Shared/Cloning/CloningSettingsPrototype.cs b/Content.Shared/Cloning/CloningSettingsPrototype.cs
index b422f7188b..0b531561ca 100644
--- a/Content.Shared/Cloning/CloningSettingsPrototype.cs
+++ b/Content.Shared/Cloning/CloningSettingsPrototype.cs
@@ -50,6 +50,12 @@ public sealed partial class CloningSettingsPrototype : IPrototype, IInheritingPr
[DataField]
public bool CopyImplants = true;
+ ///
+ /// Should infinite status effects applied to an entity be copied or not?
+ ///
+ [DataField]
+ public bool CopyStatusEffects = true;
+
///
/// Whitelist for the equipment allowed to be copied.
///
diff --git a/Content.Shared/Roles/JobSpecial.cs b/Content.Shared/Roles/JobSpecial.cs
index 468e939836..8ebeb69a6d 100644
--- a/Content.Shared/Roles/JobSpecial.cs
+++ b/Content.Shared/Roles/JobSpecial.cs
@@ -1,7 +1,9 @@
namespace Content.Shared.Roles
{
///
- /// Provides special hooks for when jobs get spawned in/equipped.
+ /// Provides special hooks for when jobs get spawned in/equipped.
+ /// TODO: This is being/should be utilized by more than jobs, and is really just a way to assign components/implants/status effects upon spawning. Rename this class and its derivatives in the future!
+ /// TODO: Move derivatives from Server to Shared, probably.
///
[ImplicitDataDefinitionForInheritors]
public abstract partial class JobSpecial
diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs
index ab6362746c..a65d4fe063 100644
--- a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs
+++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs
@@ -353,6 +353,7 @@ public sealed partial class StatusEffectsSystem
///
/// Returns all status effects that have the specified component.
///
+ /// Returns true if any entity with the specified component is found.
public bool TryEffectsWithComp(EntityUid? target, [NotNullWhen(true)] out HashSet>? effects) where T : IComponent
{
effects = null;
diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs
index 3644bed45e..9b16aadff0 100644
--- a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs
+++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs
@@ -1,3 +1,5 @@
+using Content.Shared.Damage.Events;
+using Content.Shared.Mobs.Events;
using Content.Shared.Movement.Events;
using Content.Shared.Movement.Systems;
using Content.Shared.Rejuvenate;
@@ -25,6 +27,9 @@ public sealed partial class StatusEffectsSystem
SubscribeLocalEvent(RefRelayStatusEffectEvent);
SubscribeLocalEvent(RefRelayStatusEffectEvent);
+ SubscribeLocalEvent(RelayStatusEffectEvent);
+ SubscribeLocalEvent(RelayStatusEffectEvent);
+
SubscribeLocalEvent(RelayStatusEffectEvent);
}
diff --git a/Content.Shared/Traits/Assorted/PainNumbnessComponent.cs b/Content.Shared/Traits/Assorted/PainNumbnessComponent.cs
deleted file mode 100644
index 9ae72c6286..0000000000
--- a/Content.Shared/Traits/Assorted/PainNumbnessComponent.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using Content.Shared.Dataset;
-using Robust.Shared.GameStates;
-using Robust.Shared.Prototypes;
-
-namespace Content.Shared.Traits.Assorted;
-
-[RegisterComponent, NetworkedComponent]
-public sealed partial class PainNumbnessComponent : Component
-{
- ///
- /// The fluent string prefix to use when picking a random suffix
- /// This is only active for those who have the pain numbness component
- ///
- [DataField]
- public ProtoId ForceSayNumbDataset = "ForceSayNumbDataset";
-}
diff --git a/Content.Shared/Traits/Assorted/PainNumbnessStatusEffectComponent.cs b/Content.Shared/Traits/Assorted/PainNumbnessStatusEffectComponent.cs
new file mode 100644
index 0000000000..c5c340fd10
--- /dev/null
+++ b/Content.Shared/Traits/Assorted/PainNumbnessStatusEffectComponent.cs
@@ -0,0 +1,20 @@
+using Content.Shared.Dataset;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Traits.Assorted;
+
+///
+/// Hides the damage overlay and displays the health alert for the client controlling the entity as full.
+/// Has to be applied as a status effect.
+///
+[RegisterComponent, NetworkedComponent]
+public sealed partial class PainNumbnessStatusEffectComponent : Component
+{
+ ///
+ /// The fluent string prefix to use when picking a random suffix upon taking damage.
+ /// This is only active for those who have the pain numbness status effect. Set to null to prevent changing.
+ ///
+ [DataField]
+ public ProtoId? ForceSayNumbDataset = "ForceSayNumbDataset";
+}
diff --git a/Content.Shared/Traits/Assorted/PainNumbnessSystem.cs b/Content.Shared/Traits/Assorted/PainNumbnessSystem.cs
index 3ded13300d..688354c161 100644
--- a/Content.Shared/Traits/Assorted/PainNumbnessSystem.cs
+++ b/Content.Shared/Traits/Assorted/PainNumbnessSystem.cs
@@ -2,6 +2,7 @@ using Content.Shared.Damage.Events;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Events;
using Content.Shared.Mobs.Systems;
+using Content.Shared.StatusEffectNew;
namespace Content.Shared.Traits.Assorted;
@@ -11,36 +12,37 @@ public sealed class PainNumbnessSystem : EntitySystem
public override void Initialize()
{
- SubscribeLocalEvent(OnComponentInit);
- SubscribeLocalEvent(OnComponentRemove);
- SubscribeLocalEvent(OnChangeForceSay);
- SubscribeLocalEvent(OnAlertSeverityCheck);
+ SubscribeLocalEvent(OnEffectApplied);
+ SubscribeLocalEvent(OnEffectRemoved);
+ SubscribeLocalEvent>(OnChangeForceSay);
+ SubscribeLocalEvent>(OnAlertSeverityCheck);
}
- private void OnComponentRemove(EntityUid uid, PainNumbnessComponent component, ComponentRemove args)
+ private void OnEffectApplied(Entity ent, ref StatusEffectAppliedEvent args)
{
- if (!HasComp(uid))
+ if (!HasComp(args.Target))
return;
- _mobThresholdSystem.VerifyThresholds(uid);
+ _mobThresholdSystem.VerifyThresholds(args.Target);
}
- private void OnComponentInit(EntityUid uid, PainNumbnessComponent component, ComponentInit args)
+ private void OnEffectRemoved(Entity ent, ref StatusEffectRemovedEvent args)
{
- if (!HasComp(uid))
+ if (!HasComp(args.Target))
return;
- _mobThresholdSystem.VerifyThresholds(uid);
+ _mobThresholdSystem.VerifyThresholds(args.Target);
}
- private void OnChangeForceSay(Entity ent, ref BeforeForceSayEvent args)
+ private void OnChangeForceSay(Entity ent, ref StatusEffectRelayedEvent args)
{
- args.Prefix = ent.Comp.ForceSayNumbDataset;
+ if (ent.Comp.ForceSayNumbDataset != null)
+ args.Args.Prefix = ent.Comp.ForceSayNumbDataset.Value;
}
- private void OnAlertSeverityCheck(Entity ent, ref BeforeAlertSeverityCheckEvent args)
+ private void OnAlertSeverityCheck(Entity ent, ref StatusEffectRelayedEvent args)
{
- if (args.CurrentAlert == "HumanHealth")
- args.CancelUpdate = true;
+ if (args.Args.CurrentAlert == "HumanHealth")
+ args.Args.CancelUpdate = true;
}
}
diff --git a/Content.Shared/Traits/TraitPrototype.cs b/Content.Shared/Traits/TraitPrototype.cs
index caacaaff4a..91fcfccc8a 100644
--- a/Content.Shared/Traits/TraitPrototype.cs
+++ b/Content.Shared/Traits/TraitPrototype.cs
@@ -1,3 +1,4 @@
+// using Content.Shared.Roles;
// using Content.Shared.Whitelist; // DeltaV - Traits rework
// using Robust.Shared.Prototypes;
// using Content.Shared.Humanoid.Prototypes; // DeltaV - Trait species hiding
@@ -40,11 +41,19 @@
//
// ///
// /// The components that get added to the player, when they pick this trait.
+// /// NOTE: When implementing a new trait, it's preferable to add it as a status effect instead if possible.
// ///
// [DataField]
+// [Obsolete("Use JobSpecial instead.")]
// public ComponentRegistry Components { get; private set; } = default!;
//
// ///
+// /// Special effects applied to the player who takes this Trait.
+// ///
+// [DataField(serverOnly: true)]
+// public List Specials { get; private set; } = new();
+//
+// ///
// /// DeltaV - Components that get added to the player, overriding any existing instances of the component if they exist.
// ///
// [DataField]
diff --git a/Resources/Prototypes/Entities/Mobs/Player/clone.yml b/Resources/Prototypes/Entities/Mobs/Player/clone.yml
index 7a3f9d99cf..6b0af7e86b 100644
--- a/Resources/Prototypes/Entities/Mobs/Player/clone.yml
+++ b/Resources/Prototypes/Entities/Mobs/Player/clone.yml
@@ -23,7 +23,6 @@
- Muted
- Narcolepsy
- Pacified
- - PainNumbness
- Paracusia
- PermanentBlindness
- Snoring
diff --git a/Resources/Prototypes/Entities/StatusEffects/body.yml b/Resources/Prototypes/Entities/StatusEffects/body.yml
index 3765ebefd4..4c94804884 100644
--- a/Resources/Prototypes/Entities/StatusEffects/body.yml
+++ b/Resources/Prototypes/Entities/StatusEffects/body.yml
@@ -3,16 +3,27 @@
id: BloodstreamStatusEffectBase
abstract: true
components:
- - type: StatusEffect
- whitelist:
- components:
- - Bloodstream
+ - type: StatusEffect
+ whitelist:
+ components:
+ - Bloodstream
- type: entity
parent: [ BloodstreamStatusEffectBase ]
id: StatusEffectBloodloss
name: bloodloss
components:
- - type: StutteringAccent
- - type: DrunkStatusEffect
- - type: RejuvenateRemovedStatusEffect
+ - type: StutteringAccent
+ - type: DrunkStatusEffect
+ - type: RejuvenateRemovedStatusEffect
+
+- type: entity
+ parent: MobStatusEffectBase
+ id: PainNumbnessTraitStatusEffect
+ components:
+ - type: StatusEffect
+ whitelist:
+ components:
+ - MobState
+ - MobThresholds
+ - type: PainNumbnessStatusEffect
diff --git a/Resources/Prototypes/Traits/disabilities.yml b/Resources/Prototypes/Traits/disabilities.yml
index 488fdd7d8c..b9a8e1fd54 100644
--- a/Resources/Prototypes/Traits/disabilities.yml
+++ b/Resources/Prototypes/Traits/disabilities.yml
@@ -87,8 +87,10 @@
# name: trait-painnumbness-name
# description: trait-painnumbness-desc
# category: Disabilities
-# components:
-# - type: PainNumbness
+# specials:
+# - !type:ApplyStatusEffectSpecial
+# statusEffects:
+# - PainNumbnessTraitStatusEffect
#
#- type: trait
# id: ImpairedMobility
From 7f2b7500d2234e04904f22a47aaf120217c006c9 Mon Sep 17 00:00:00 2001
From: BarryNorfolk
Date: Thu, 22 Jan 2026 19:57:30 +0100
Subject: [PATCH 010/360] Downstream fix for painnumbness status effect
---
Content.Server/_Shitmed/Medical/Surgery/SurgerySystem.cs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Content.Server/_Shitmed/Medical/Surgery/SurgerySystem.cs b/Content.Server/_Shitmed/Medical/Surgery/SurgerySystem.cs
index e44aedb99c..2bce1959c4 100644
--- a/Content.Server/_Shitmed/Medical/Surgery/SurgerySystem.cs
+++ b/Content.Server/_Shitmed/Medical/Surgery/SurgerySystem.cs
@@ -21,6 +21,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using Content.Shared.Verbs;
using Content.Shared._Goobstation.CCVar;
+using Content.Shared.StatusEffectNew;
namespace Content.Server._Shitmed.Medical.Surgery;
@@ -35,6 +36,7 @@ public sealed class SurgerySystem : SharedSurgerySystem
[Dependency] private readonly SurgeryCleanSystem _clean = default!; // DeltaV
[Dependency] private readonly UserInterfaceSystem _ui = default!;
[Dependency] private readonly InventorySystem _inventory = default!; // DeltaV - surgery cross contamination
+ [Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
private readonly HashSet _dirtyDnas = new(); // DeltaV
@@ -251,7 +253,7 @@ public sealed class SurgerySystem : SharedSurgerySystem
{
if (HasComp(args.Body)) // DeltaV
return;
- if (HasComp(args.Body)) // DeltaV
+ if (_statusEffects.HasEffectComp(ent))
return;
_chat.TryEmoteWithChat(args.Body, ent.Comp.Emote);
From b24ec257b7e551027bc49a7df8bbe56a703557f0 Mon Sep 17 00:00:00 2001
From: BarryNorfolk
Date: Fri, 23 Jan 2026 08:38:17 +0100
Subject: [PATCH 011/360] Extend DV trait system to handle status effects
---
.../Tests/_DV/TraitSystemTest.cs | 3 +++
Content.Server/_DV/Traits/TraitSystem.cs | 4 ++++
.../Traits/Conditions/BaseTraitCondition.cs | 2 ++
.../_DV/Traits/Effects/ApplyStatusEffect.cs | 23 +++++++++++++++++++
.../_DV/Traits/Effects/BaseTraitEffect.cs | 2 ++
.../Prototypes/_DV/Traits/disabilities.yml | 6 ++---
6 files changed, 37 insertions(+), 3 deletions(-)
create mode 100644 Content.Shared/_DV/Traits/Effects/ApplyStatusEffect.cs
diff --git a/Content.IntegrationTests/Tests/_DV/TraitSystemTest.cs b/Content.IntegrationTests/Tests/_DV/TraitSystemTest.cs
index 5daf838b25..670b443de2 100644
--- a/Content.IntegrationTests/Tests/_DV/TraitSystemTest.cs
+++ b/Content.IntegrationTests/Tests/_DV/TraitSystemTest.cs
@@ -7,6 +7,7 @@ using Content.Shared._DV.Traits.Effects;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Nutrition.Components;
+using Content.Shared.StatusEffectNew;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
@@ -773,6 +774,7 @@ public sealed partial class TraitSystemTest
LogMan = IoCManager.Resolve(),
JobId = jobId,
SpeciesId = speciesId,
+ StatusEffects = IoCManager.Resolve(),
};
}
@@ -790,6 +792,7 @@ public sealed partial class TraitSystemTest
CompFactory = factory,
LogMan = IoCManager.Resolve(),
Transform = entMan.GetComponent(player),
+ StatusEffects = IoCManager.Resolve(),
};
}
diff --git a/Content.Server/_DV/Traits/TraitSystem.cs b/Content.Server/_DV/Traits/TraitSystem.cs
index f3d67e2571..d43cfe1f2e 100644
--- a/Content.Server/_DV/Traits/TraitSystem.cs
+++ b/Content.Server/_DV/Traits/TraitSystem.cs
@@ -9,6 +9,7 @@ using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Preferences;
using Content.Shared.Roles;
+using Content.Shared.StatusEffectNew;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
@@ -25,6 +26,7 @@ public sealed class TraitSystem : EntitySystem
[Dependency] private readonly ILogManager _log = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
+ [Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
private int _maxTraitCount;
private int _maxTraitPoints;
@@ -104,6 +106,7 @@ public sealed class TraitSystem : EntitySystem
JobId = jobId,
SpeciesId = speciesId,
Profile = profile,
+ StatusEffects = _statusEffects
};
foreach (var traitId in selectedTraits)
@@ -272,6 +275,7 @@ public sealed class TraitSystem : EntitySystem
CompFactory = _factory,
LogMan = _log,
Transform = transform,
+ StatusEffects = _statusEffects,
};
foreach (var effect in trait.Effects)
diff --git a/Content.Shared/_DV/Traits/Conditions/BaseTraitCondition.cs b/Content.Shared/_DV/Traits/Conditions/BaseTraitCondition.cs
index 293dceeb7e..68781af5d2 100644
--- a/Content.Shared/_DV/Traits/Conditions/BaseTraitCondition.cs
+++ b/Content.Shared/_DV/Traits/Conditions/BaseTraitCondition.cs
@@ -1,4 +1,5 @@
using Content.Shared.Preferences;
+using Content.Shared.StatusEffectNew;
using JetBrains.Annotations;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
@@ -48,6 +49,7 @@ public sealed class TraitConditionContext
public required IPrototypeManager Proto { get; init; }
public required IComponentFactory CompFactory { get; init; }
public required ILogManager LogMan { get; init; }
+ public required StatusEffectsSystem StatusEffects { get; init; }
///
/// The job ID of the player, if available.
diff --git a/Content.Shared/_DV/Traits/Effects/ApplyStatusEffect.cs b/Content.Shared/_DV/Traits/Effects/ApplyStatusEffect.cs
new file mode 100644
index 0000000000..712d628825
--- /dev/null
+++ b/Content.Shared/_DV/Traits/Effects/ApplyStatusEffect.cs
@@ -0,0 +1,23 @@
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._DV.Traits.Effects;
+
+///
+/// Effect that adds status effects to the player entity.
+///
+public sealed partial class ApplyStatusEffect : BaseTraitEffect
+{
+ ///
+ /// The status effects to add to the entity.
+ ///
+ [DataField(required: true)]
+ public HashSet StatusEffects = new();
+
+ public override void Apply(TraitEffectContext ctx)
+ {
+ foreach (var effect in StatusEffects)
+ {
+ ctx.StatusEffects.TrySetStatusEffectDuration(ctx.Player, effect);
+ }
+ }
+}
diff --git a/Content.Shared/_DV/Traits/Effects/BaseTraitEffect.cs b/Content.Shared/_DV/Traits/Effects/BaseTraitEffect.cs
index a901e626d1..8e3118258e 100644
--- a/Content.Shared/_DV/Traits/Effects/BaseTraitEffect.cs
+++ b/Content.Shared/_DV/Traits/Effects/BaseTraitEffect.cs
@@ -1,3 +1,4 @@
+using Content.Shared.StatusEffectNew;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
@@ -28,4 +29,5 @@ public sealed class TraitEffectContext
public required IComponentFactory CompFactory { get; init; }
public required ILogManager LogMan { get; init; }
public required TransformComponent Transform { get; init; }
+ public required StatusEffectsSystem StatusEffects { get; init; }
}
diff --git a/Resources/Prototypes/_DV/Traits/disabilities.yml b/Resources/Prototypes/_DV/Traits/disabilities.yml
index a8f6635fc1..b021d5c9ca 100644
--- a/Resources/Prototypes/_DV/Traits/disabilities.yml
+++ b/Resources/Prototypes/_DV/Traits/disabilities.yml
@@ -117,9 +117,9 @@
description: trait-painnumbness-desc
category: Disabilities
effects:
- - !type:AddCompsEffect
- components:
- - type: PainNumbness
+ - !type:ApplyStatusEffect
+ statusEffects:
+ - PainNumbnessTraitStatusEffect
- type: trait
id: Hemophilia
From 538a1b18394c49bfbe0d652c4ddce40a5f1d46b3 Mon Sep 17 00:00:00 2001
From: BarryNorfolk
Date: Fri, 23 Jan 2026 09:14:02 +0100
Subject: [PATCH 012/360] Downstream devil fix to Painnumbness status effect
---
.../Devil/Contract/DevilContractSystem.cs | 14 ++++++++++++++
.../Devil/Contract/DevilClausePrototype.cs | 3 +++
.../Prototypes/_Goobstation/Devil/clauses.yml | 5 ++++-
3 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/Content.Server/_Goobstation/Devil/Contract/DevilContractSystem.cs b/Content.Server/_Goobstation/Devil/Contract/DevilContractSystem.cs
index 702a93205a..305d5ff864 100644
--- a/Content.Server/_Goobstation/Devil/Contract/DevilContractSystem.cs
+++ b/Content.Server/_Goobstation/Devil/Contract/DevilContractSystem.cs
@@ -35,6 +35,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility;
using Content.Shared._EE.Silicon.Components;
+using Content.Shared.StatusEffectNew; // DeltaV - Clause status effects
namespace Content.Server._Goobstation.Devil.Contract;
@@ -51,6 +52,7 @@ public sealed partial class DevilContractSystem : EntitySystem
[Dependency] private readonly PolymorphSystem _polymorph = null!;
[Dependency] private readonly ExplosionSystem _explosion = null!;
[Dependency] private readonly MindSystem _mind = null!;
+ [Dependency] private readonly StatusEffectsSystem _statusEffects = null!; // DeltaV - Clause status effects
public override void Initialize()
{
@@ -363,6 +365,8 @@ public sealed partial class DevilContractSystem : EntitySystem
OverrideComponents(target, clause); // DeltaV - Fix component modifications
+ ApplyStatusEffect(target, clause); // DeltaV - Clause status effects
+
ChangeDamageModifier(target, clause);
AddImplants(target, clause);
@@ -414,6 +418,16 @@ public sealed partial class DevilContractSystem : EntitySystem
}
// End DeltaV Addition
+ // Begin DeltaV Addition - Devil clause status effects
+ private void ApplyStatusEffect(EntityUid target, DevilClausePrototype clause)
+ {
+ foreach (var effect in clause.StatusEffects)
+ {
+ _statusEffects.TrySetStatusEffectDuration(target, effect);
+ }
+ }
+ // End DeltaV Addition - Devil clause status effects
+
private void SpawnItems(EntityUid target, DevilClausePrototype clause)
{
if (clause.SpawnedItems == null)
diff --git a/Content.Shared/_Goobstation/Devil/Contract/DevilClausePrototype.cs b/Content.Shared/_Goobstation/Devil/Contract/DevilClausePrototype.cs
index 6f72df57f0..5b0d28cc15 100644
--- a/Content.Shared/_Goobstation/Devil/Contract/DevilClausePrototype.cs
+++ b/Content.Shared/_Goobstation/Devil/Contract/DevilClausePrototype.cs
@@ -27,6 +27,9 @@ public sealed class DevilClausePrototype : IPrototype
[DataField]
public ComponentRegistry? OverriddenComponents; // DeltaV - Added overridden components
+ [DataField]
+ public HashSet StatusEffects = new(); // DeltaV - Add status effects to clauses
+
[DataField]
public string? DamageModifierSet;
diff --git a/Resources/Prototypes/_Goobstation/Devil/clauses.yml b/Resources/Prototypes/_Goobstation/Devil/clauses.yml
index f03a562f04..fe9db9627c 100644
--- a/Resources/Prototypes/_Goobstation/Devil/clauses.yml
+++ b/Resources/Prototypes/_Goobstation/Devil/clauses.yml
@@ -117,11 +117,14 @@
id: pain
clauseWeight: -5
addedComponents:
- - type: PainNumbness
- type: SlowOnDamage
speedModifierThresholds:
60: 1
80: 1
+ # Begin DeltaV Additions - Clause status effects
+ statusEffects:
+ - PainNumbnessTraitStatusEffect
+ # End DeltaV Additions - Clause status effects
- type: clause
id: chance
From 9dd9f5cd921cc233b7c0c27ea4b40d93ecf69fe5 Mon Sep 17 00:00:00 2001
From: Pieter-Jan Briers
Date: Wed, 3 Dec 2025 16:52:25 +0100
Subject: [PATCH 013/360] Reduce explosion airtight cache memory usage (#40912)
* Reduce explosion airtight cache memory usage
This means you can happily add explosion prototypes again
New approach has the tolerance value data in a shared storage with reference counting.
* Oops fix index removal
* Remove debug code and fix merge conflicts
* Also address my other review
* Oh it's in two places lmao
---------
Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
---
.../ExplosionAirtightGridComponent.cs | 100 +++++++++
.../EntitySystems/ExplosionGridTileFlood.cs | 19 +-
.../EntitySystems/ExplosionSystem.Airtight.cs | 209 +++++++++++++-----
.../EntitySystems/ExplosionSystem.GridMap.cs | 2 +-
.../EntitySystems/ExplosionSystem.TileFill.cs | 19 +-
.../EntitySystems/ExplosionSystem.cs | 3 +
Resources/Prototypes/explosion.yml | 12 -
7 files changed, 282 insertions(+), 82 deletions(-)
create mode 100644 Content.Server/Explosion/Components/ExplosionAirtightGridComponent.cs
diff --git a/Content.Server/Explosion/Components/ExplosionAirtightGridComponent.cs b/Content.Server/Explosion/Components/ExplosionAirtightGridComponent.cs
new file mode 100644
index 0000000000..68f576dcb9
--- /dev/null
+++ b/Content.Server/Explosion/Components/ExplosionAirtightGridComponent.cs
@@ -0,0 +1,100 @@
+using Content.Server.Explosion.EntitySystems;
+using Content.Shared.Atmos;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Map.Components;
+using Robust.Shared.Utility;
+
+namespace Content.Server.Explosion.Components;
+
+///
+/// Stores data for airtight explosion traversal on a entity.
+///
+///
+[RegisterComponent]
+[Access(typeof(ExplosionSystem), Other = AccessPermissions.None)]
+public sealed partial class ExplosionAirtightGridComponent : Component
+{
+ ///
+ /// Data for every tile on the current grid.
+ ///
+ ///
+ /// Intentionally not saved.
+ ///
+ [ViewVariables]
+ public readonly Dictionary Tiles = new();
+
+ ///
+ /// Data struct that describes the explosion-blocking airtight entities on a tile.
+ ///
+ public struct TileData
+ {
+ ///
+ /// Which index into the tolerance cache of this tile is using.
+ ///
+ public required int ToleranceCacheIndex;
+
+ ///
+ /// Which directions this tile is blocking explosions in. Bitflag field.
+ ///
+ public required AtmosDirection BlockedDirections;
+ }
+
+ ///
+ /// A set of tolerance values
+ ///
+ public struct ToleranceValues : IEquatable
+ {
+ ///
+ /// Special value that indicates the entity is "invulnerable" against a specific explosion type.
+ ///
+ ///
+ /// Here to deal with the limited range of over typical floats.
+ ///
+ public static readonly FixedPoint2 Invulnerable = FixedPoint2.MaxValue;
+
+ ///
+ /// The intensities at which explosions of each type can instantly break through an entity.
+ ///
+ ///
+ ///
+ /// This is an array, with the index of each value corresponding to the "explosion type ID" cached by
+ /// .
+ ///
+ ///
+ /// Values are stored as to avoid possible precision issues resulting in
+ /// different-but-almost-identical tolerance values wasting memory.
+ ///
+ ///
+ /// If a value is , that indicates the tile is invulnerable.
+ ///
+ ///
+ public required FixedPoint2[] Values;
+
+ public bool Equals(ToleranceValues other)
+ {
+ return Values.AsSpan().SequenceEqual(other.Values);
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return obj is ToleranceValues other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ var hc = new HashCode();
+ hc.AddArray(Values);
+ return hc.ToHashCode();
+ }
+
+ public static bool operator ==(ToleranceValues left, ToleranceValues right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(ToleranceValues left, ToleranceValues right)
+ {
+ return !left.Equals(right);
+ }
+ }
+}
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs b/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs
index da3ce635af..0274979c55 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionGridTileFlood.cs
@@ -1,7 +1,8 @@
using System.Numerics;
using Content.Shared.Atmos;
-using Robust.Shared.Map;
+using Content.Shared.FixedPoint;
using Robust.Shared.Map.Components;
+using static Content.Server.Explosion.Components.ExplosionAirtightGridComponent;
using static Content.Server.Explosion.EntitySystems.ExplosionSystem;
namespace Content.Server.Explosion.EntitySystems;
@@ -11,6 +12,8 @@ namespace Content.Server.Explosion.EntitySystems;
///
public sealed class ExplosionGridTileFlood : ExplosionTileFlood
{
+ private readonly ExplosionSystem _explosionSystem;
+
public Entity Grid;
private bool _needToTransform = false;
@@ -45,7 +48,8 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood
Dictionary edgeTiles,
EntityUid? referenceGrid,
Matrix3x2 spaceMatrix,
- Angle spaceAngle)
+ Angle spaceAngle,
+ ExplosionSystem explosionSystem)
{
Grid = grid;
_airtightMap = airtightMap;
@@ -53,6 +57,7 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood
_intensityStepSize = intensityStepSize;
_typeIndex = typeIndex;
_edgeTiles = edgeTiles;
+ _explosionSystem = explosionSystem;
// initialise SpaceTiles
foreach (var (tile, spaceNeighbors) in _edgeTiles)
@@ -193,11 +198,11 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood
NewBlockedTiles.Add(tile);
// At what explosion iteration would this blocker be destroyed?
- var required = tileData.ExplosionTolerance[_typeIndex];
+ var required = _explosionSystem.GetToleranceValues(tileData.ToleranceCacheIndex).Values[_typeIndex];
if (required > _maxIntensity)
return; // blocker is never destroyed.
- var clearIteration = iteration + (int) MathF.Ceiling(required / _intensityStepSize);
+ var clearIteration = iteration + (int) MathF.Ceiling((float)required / _intensityStepSize);
if (FreedTileLists.TryGetValue(clearIteration, out var list))
list.Add(tile);
else
@@ -261,13 +266,13 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood
foreach (var tile in tiles)
{
var blockedDirections = AtmosDirection.Invalid;
- float sealIntegrity = 0;
+ FixedPoint2 sealIntegrity = 0;
// Note that if (grid, tile) is not a valid key, then airtight.BlockedDirections will default to 0 (no blocked directions)
if (_airtightMap.TryGetValue(tile, out var tileData))
{
blockedDirections = tileData.BlockedDirections;
- sealIntegrity = tileData.ExplosionTolerance[_typeIndex];
+ sealIntegrity = _explosionSystem.GetToleranceValues(tileData.ToleranceCacheIndex).Values[_typeIndex];
}
// First, yield any neighboring tiles that are not blocked by airtight entities on this tile
@@ -290,7 +295,7 @@ public sealed class ExplosionGridTileFlood : ExplosionTileFlood
continue;
// At what explosion iteration would this blocker be destroyed?
- var clearIteration = iteration + (int) MathF.Ceiling(sealIntegrity / _intensityStepSize);
+ var clearIteration = iteration + (int) MathF.Ceiling((float) sealIntegrity / _intensityStepSize);
// Get the delayed neighbours list
if (!_delayedNeighbors.TryGetValue(clearIteration, out var list))
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs
index 303c4e8cab..da2a571900 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs
@@ -1,44 +1,59 @@
+using System.Linq;
+using System.Runtime.InteropServices;
using Content.Server.Atmos.Components;
+using Content.Server.Explosion.Components;
using Content.Shared.Atmos;
using Content.Shared.Damage.Systems;
using Content.Shared.Explosion;
using Content.Shared.FixedPoint;
+using Robust.Shared.Collections;
using Robust.Shared.Map.Components;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
+using static Content.Server.Explosion.Components.ExplosionAirtightGridComponent;
namespace Content.Server.Explosion.EntitySystems;
public sealed partial class ExplosionSystem
{
- private readonly Dictionary _explosionTypes = new();
+ // We keep track of which tiles are airtight, and how much damage from explosions those airtight blockers can take.
+ // This is quite complicated, as the data effectively needs to be tracked *per tile*, *per explosion type*.
+ // To avoid wasting significant memory, we calculate the values and share the actual backing storage of it.
+ // Stored values are reference counted so they can be evicted when no longer needed.
+ // At the time of writing, this compacts the storage for Box Station from ~5500 tolerance value sets to 13,
+ // at round start.
+
+ // Use integers instead of prototype IDs for storage of explosion data.
+ // This allows us to replace a Dictionary with just a FixedPoint2[].
+ private readonly Dictionary, int> _explosionTypes = new();
+ // Index to look up if we already have an existing set of tolerance values stored, so the data can be shared.
+ private readonly Dictionary _toleranceIndex = new();
+ // Storage for tolerance values. Entries form a free linked list when not occupied by a set of real values.
+ private ValueList _toleranceData;
+ // First free position in _toleranceData.
+ // -1 indicates there are no free slots left and the storage must be expanded.
+ private int _freeListHead = -1;
private void InitAirtightMap()
{
- // Currently explosion prototype hot-reload isn't supported, as it would involve completely re-computing the
- // airtight map. Could be done, just not yet implemented.
+ _explosionTypes.Clear();
- // for storing airtight entity damage thresholds for all anchored airtight entities, we will use integers in
- // place of id-strings. This initializes the string <--> id association.
- // This allows us to replace a Dictionary with just a float[].
int index = 0;
foreach (var prototype in _prototypeManager.EnumeratePrototypes())
{
- // TODO EXPLOSION
- // just make this a field on the prototype
_explosionTypes.Add(prototype.ID, index);
index++;
}
}
- // The explosion intensity required to break an entity depends on the explosion type. So it is stored in a
- // Dictionary
- //
- // Hence, each tile has a tuple (Dictionary, AtmosDirection). This specifies what directions are
- // blocked, and how intense a given explosion type needs to be in order to destroy ALL airtight entities on that
- // tile. This is the TileData struct.
- //
- // We then need this data for every tile on a grid. So this mess of a variable maps the Grid ID and Vector2i grid
- // indices to this tile-data struct.
- private Dictionary> _airtightMap = new();
+ private void ReloadExplosionPrototypes(PrototypesReloadedEventArgs prototypesReloadedEventArgs)
+ {
+ if (!prototypesReloadedEventArgs.Modified.Contains(typeof(ExplosionPrototype)))
+ return;
+
+ InitAirtightMap();
+ ReloadMap();
+ }
public void UpdateAirtightMap(EntityUid gridId, Vector2i tile, MapGridComponent? grid = null)
{
@@ -46,6 +61,12 @@ public sealed partial class ExplosionSystem
UpdateAirtightMap(gridId, grid, tile);
}
+ [Access(typeof(ExplosionGridTileFlood))]
+ public ToleranceValues GetToleranceValues(int idx)
+ {
+ return _toleranceData[idx].Values;
+ }
+
///
/// Update the map of explosion blockers.
///
@@ -58,11 +79,12 @@ public sealed partial class ExplosionSystem
///
public void UpdateAirtightMap(EntityUid gridId, MapGridComponent grid, Vector2i tile)
{
- var tolerance = new float[_explosionTypes.Count];
- var blockedDirections = AtmosDirection.Invalid;
+ var airtightGrid = EnsureComp(gridId);
- if (!_airtightMap.ContainsKey(gridId))
- _airtightMap[gridId] = new();
+ // Calculate tile new airtight state.
+
+ var tolerance = new FixedPoint2[_explosionTypes.Count];
+ var blockedDirections = AtmosDirection.Invalid;
var anchoredEnumerator = _map.GetAnchoredEntitiesEnumerator(gridId, grid, tile);
@@ -72,17 +94,97 @@ public sealed partial class ExplosionSystem
continue;
blockedDirections |= airtight.AirBlockedDirection;
- var entityTolerances = GetExplosionTolerance(uid.Value);
- for (var i = 0; i < tolerance.Length; i++)
- {
- tolerance[i] = Math.Max(tolerance[i], entityTolerances[i]);
- }
+ GetExplosionTolerance(uid.Value, tolerance);
}
- if (blockedDirections != AtmosDirection.Invalid)
- _airtightMap[gridId][tile] = new(tolerance, blockedDirections);
+ // Log.Info($"UPDATE {gridId}/{tile}: {blockedDirections}");
+
+ if (blockedDirections == AtmosDirection.Invalid)
+ {
+ // No longer airtight
+
+ if (!airtightGrid.Tiles.Remove(tile, out var tileData))
+ {
+ // Did not have this tile before and after, nothing to do.
+ return;
+ }
+
+ // Removing tile data.
+ DecrementRefCount(tileData.ToleranceCacheIndex);
+ return;
+ }
+
+ ref var tileEntry = ref CollectionsMarshal.GetValueRefOrAddDefault(airtightGrid.Tiles, tile, out var existed);
+ var cacheKey = new ToleranceValues { Values = tolerance };
+
+ // Remove previous tolerance reference if necessary.
+ if (existed)
+ {
+ ref var prevEntry = ref _toleranceData[tileEntry.ToleranceCacheIndex];
+ if (prevEntry.Values == cacheKey)
+ {
+ // No change.
+ return;
+ }
+
+ DecrementRefCount(tileEntry.ToleranceCacheIndex);
+ }
+
+ ref var newCacheIndex = ref CollectionsMarshal.GetValueRefOrAddDefault(_toleranceIndex, cacheKey, out existed);
+ if (existed)
+ {
+ _toleranceData[newCacheIndex].RefCount += 1;
+ }
else
- _airtightMap[gridId].Remove(tile);
+ {
+ if (_freeListHead < 0)
+ ExpandCache();
+
+ newCacheIndex = _freeListHead;
+ ref var newCacheEntry = ref _toleranceData[newCacheIndex];
+ _freeListHead = newCacheEntry.RefCount;
+
+ newCacheEntry.Values = cacheKey;
+ newCacheEntry.RefCount = 1;
+ }
+
+ tileEntry = new TileData
+ {
+ BlockedDirections = blockedDirections,
+ ToleranceCacheIndex = newCacheIndex,
+ };
+ }
+
+ private void ExpandCache()
+ {
+ var newCacheSize = Math.Max(8, _toleranceData.Count * 2);
+ var curSize = _toleranceData.Count;
+
+ _toleranceData.EnsureLength(newCacheSize);
+ for (var i = curSize; i < newCacheSize; i++)
+ {
+ _toleranceData[i].RefCount = _freeListHead;
+ _freeListHead = i;
+ }
+ }
+
+ private void DecrementRefCount(int index)
+ {
+ ref var cacheEntry = ref _toleranceData[index];
+
+ DebugTools.Assert(cacheEntry.RefCount > 0);
+ cacheEntry.RefCount -= 1;
+
+ if (cacheEntry.RefCount == 0)
+ {
+ var prevValue = cacheEntry.Values;
+ cacheEntry.Values = default;
+ cacheEntry.RefCount = _freeListHead;
+ _freeListHead = index;
+
+ var result = _toleranceIndex.Remove(prevValue);
+ DebugTools.Assert(result, "Failed to removed 0 refcounted index!");
+ }
}
///
@@ -106,7 +208,7 @@ public sealed partial class ExplosionSystem
///
/// Return a dictionary that specifies how intense a given explosion type needs to be in order to destroy an entity.
///
- public float[] GetExplosionTolerance(EntityUid uid)
+ private void GetExplosionTolerance(EntityUid uid, Span explosionTolerance)
{
// How much total damage is needed to destroy this entity? This also includes "break" behaviors. This ASSUMES
// that this will result in a non-airtight entity.Entities that ONLY break via construction graph node changes
@@ -117,14 +219,14 @@ public sealed partial class ExplosionSystem
totalDamageTarget = _destructibleSystem.DestroyedAt(uid, destructible);
}
- var explosionTolerance = new float[_explosionTypes.Count];
if (totalDamageTarget == FixedPoint2.MaxValue || !_damageableQuery.TryGetComponent(uid, out var damageable))
{
for (var i = 0; i < explosionTolerance.Length; i++)
{
- explosionTolerance[i] = float.MaxValue;
+ explosionTolerance[i] = ToleranceValues.Invulnerable;
}
- return explosionTolerance;
+
+ return;
}
// What multiple of each explosion type damage set will result in the damage exceeding the required amount? This
@@ -157,38 +259,43 @@ public sealed partial class ExplosionSystem
damagePerIntensity += value * mod * Math.Max(0, ev.DamageCoefficient);
}
- explosionTolerance[index] = damagePerIntensity > 0
+ var toleranceValue = damagePerIntensity > 0
? (float) ((totalDamageTarget - damageable.TotalDamage) / damagePerIntensity)
- : float.MaxValue;
- }
+ : ToleranceValues.Invulnerable;
- return explosionTolerance;
+ explosionTolerance[index] = toleranceValue;
+ }
}
- ///
- /// Data struct that describes the explosion-blocking airtight entities on a tile.
- ///
- public struct TileData
+ private void OnAirtightGridRemoved(EntityUid entity)
{
- public TileData(float[] explosionTolerance, AtmosDirection blockedDirections)
+ if (!TryComp(entity, out ExplosionAirtightGridComponent? airtightGrid))
+ return;
+
+ foreach (var tile in airtightGrid.Tiles.Values)
{
- ExplosionTolerance = explosionTolerance;
- BlockedDirections = blockedDirections;
+ DecrementRefCount(tile.ToleranceCacheIndex);
}
- public float[] ExplosionTolerance;
- public AtmosDirection BlockedDirections = AtmosDirection.Invalid;
+ RemComp(entity);
}
public override void ReloadMap()
{
- foreach (var(grid, dict) in _airtightMap)
+ var enumerator = EntityQueryEnumerator();
+ while (enumerator.MoveNext(out var uid, out var airtightComp, out var mapGrid))
{
- var comp = Comp(grid);
- foreach (var index in dict.Keys)
+ foreach (var pos in airtightComp.Tiles.Keys)
{
- UpdateAirtightMap(grid, comp, index);
+ UpdateAirtightMap(uid, pos, mapGrid);
}
}
}
+
+ private struct CacheEntry
+ {
+ public ToleranceValues Values;
+ public int RefCount; // Doubles as freelist chain
+ }
+
}
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs
index 5c032d5c82..3767d0c238 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs
@@ -38,7 +38,7 @@ public sealed partial class ExplosionSystem
private void OnGridRemoved(GridRemovalEvent ev)
{
- _airtightMap.Remove(ev.EntityUid);
+ OnAirtightGridRemoved(ev.EntityUid);
_gridEdges.Remove(ev.EntityUid);
// this should be a small enough set that iterating all of them is fine
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs
index ac539da213..a274fa8660 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.TileFill.cs
@@ -1,5 +1,6 @@
using System.Linq;
using System.Numerics;
+using Content.Server.Explosion.Components;
using Content.Shared.Administration;
using Content.Shared.Explosion.Components;
using Robust.Shared.Map;
@@ -40,11 +41,7 @@ public sealed partial class ExplosionSystem
if (totalIntensity <= 0 || slope <= 0)
return null;
- if (!_explosionTypes.TryGetValue(typeID, out var typeIndex))
- {
- Log.Error("Attempted to spawn explosion using a prototype that was not defined during initialization. Explosion prototype hot-reload is not currently supported.");
- return null;
- }
+ var typeIndex = _explosionTypes[typeID];
Vector2i initialTile;
EntityUid? epicentreGrid = null;
@@ -103,8 +100,7 @@ public sealed partial class ExplosionSystem
// set up the initial `gridData` instance
encounteredGrids.Add(epicentreGrid.Value);
- if (!_airtightMap.TryGetValue(epicentreGrid.Value, out var airtightMap))
- airtightMap = new();
+ var airtightMap = CompOrNull(epicentreGrid)?.Tiles ?? new();
var initialGridData = new ExplosionGridTileFlood(
(epicentreGrid.Value, Comp(epicentreGrid.Value)),
@@ -115,7 +111,8 @@ public sealed partial class ExplosionSystem
_gridEdges[epicentreGrid.Value],
referenceGrid,
spaceMatrix,
- spaceAngle);
+ spaceAngle,
+ this);
gridData[epicentreGrid.Value] = initialGridData;
@@ -192,8 +189,7 @@ public sealed partial class ExplosionSystem
// is this a new grid, for which we must create a new explosion data set
if (!gridData.TryGetValue(grid, out var data))
{
- if (!_airtightMap.TryGetValue(grid, out var airtightMap))
- airtightMap = new();
+ var airtightMap = CompOrNull(grid)?.Tiles ?? new();
data = new ExplosionGridTileFlood(
(grid, Comp(grid)),
@@ -204,7 +200,8 @@ public sealed partial class ExplosionSystem
_gridEdges[grid],
referenceGrid,
spaceMatrix,
- spaceAngle);
+ spaceAngle,
+ this);
gridData[grid] = data;
}
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
index 934e4b40c6..223d1dbd01 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
@@ -106,6 +106,8 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
_destructibleQuery = GetEntityQuery();
_damageableQuery = GetEntityQuery();
_airtightQuery = GetEntityQuery();
+
+ _prototypeManager.PrototypesReloaded += ReloadExplosionPrototypes;
}
private void OnReset(RoundRestartCleanupEvent ev)
@@ -124,6 +126,7 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
base.Shutdown();
_nodeGroupSystem.PauseUpdating = false;
_pathfindingSystem.PauseUpdating = false;
+ _prototypeManager.PrototypesReloaded -= ReloadExplosionPrototypes;
}
private void RelayedResistance(EntityUid uid, ExplosionResistanceComponent component,
diff --git a/Resources/Prototypes/explosion.yml b/Resources/Prototypes/explosion.yml
index ad00333892..4d3febeda7 100644
--- a/Resources/Prototypes/explosion.yml
+++ b/Resources/Prototypes/explosion.yml
@@ -1,11 +1,3 @@
-# Does not currently support prototype hot-reloading. See comments in c# file.
-
-# Note that for every explosion type you define, explosions & nukes will start performing worse
-# You should only define a new explopsion type if you really need to
-#
-# If you just want to modify properties other than `damagePerIntensity`, it'd be better to
-# split off explosion damage & explosion visuals/effects into their own separate prototypes.
-
- type: explosion
id: Default
damagePerIntensity:
@@ -135,7 +127,3 @@
texturePath: /Textures/Effects/fire.rsi
fireStates: 3
fireStacks: 2
-
-# STOP
-# BEFORE YOU ADD MORE EXPLOSION TYPES CONSIDER IF AN EXISTING ONE IS SUITABLE
-# ADDING NEW ONES IS PROHIBITIVELY EXPENSIVE
From 2ac36dcbffd7b0ffd04719f7989b590edcb6319d Mon Sep 17 00:00:00 2001
From: Hitlinemoss <209321380+Hitlinemoss@users.noreply.github.com>
Date: Wed, 3 Dec 2025 12:39:18 -0500
Subject: [PATCH 014/360] Minor cleanup of hypospray.yml + clearer medipen
descriptions (#41682)
* Minor cleanup of hypospray.yml
* SyndiHypo is only used by corpsmen (it's not in the uplink) so the description should reflect that
* Small and large shots
---
.../Objects/Specific/Medical/hypospray.yml | 61 +++++++++----------
1 file changed, 30 insertions(+), 31 deletions(-)
diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml
index 1cdcee68b3..fc6f4dc8e3 100644
--- a/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml
+++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml
@@ -60,7 +60,7 @@
parent: [BaseHypospray, BaseSyndicateContraband]
id: SyndiHypo
name: gorlex hypospray
- description: Using reverse engineered designs from NT, Cybersun produced these in limited quantities for Gorlex Marauder operatives.
+ description: A sterile injector for rapid administration of drugs. Reverse-engineered from Nanotrasen designs, Cybersun produces these in limited quantities for Gorlex Marauders' corpsmen.
components:
- type: Sprite
sprite: Objects/Specific/Medical/syndihypo.rsi
@@ -85,7 +85,7 @@
parent: BaseHypospray
id: BorgHypo
name: borghypo
- description: A sterile injector for rapid administration of drugs to patients. A cheaper and more specialised version for medical borgs.
+ description: A sterile injector for rapid administration of drugs to patients. This integrated model is specialized for use by medical borgs.
components:
- type: Sprite
sprite: Objects/Specific/Medical/hypospray.rsi
@@ -137,7 +137,7 @@
- JetInjectorDynamicMode
- JetInjectorInjectMode
- type: Tag
- tags:
+ tags:
- JetInjector
- type: entity
@@ -186,7 +186,7 @@
parent: BaseHypospray
id: ChemicalMedipen
name: chemical medipen
- description: A sterile injector for rapid administration of drugs to patients. This one can't be refilled.
+ description: A single-dose, non-refillable medipen.
components:
- type: Sprite
sprite: Objects/Specific/Medical/medipen.rsi
@@ -241,10 +241,10 @@
- ChemicalMedipen
- type: entity
- name: emergency medipen
parent: ChemicalMedipen
id: EmergencyMedipen
- description: A rapid and safe way to stabilize patients in critical condition for personnel without advanced medical knowledge. Beware, as it's easy to overdose on epinephrine and tranexamic acid.
+ name: emergency medipen
+ description: A single-dose, non-refillable medipen containing epinephrine and tranexamic acid, used to temporarily stabilize patients in critical condition and buy time for them to recieve proper medical treatment. Beware of overdose.
components:
- type: Sprite
sprite: Objects/Specific/Medical/medipen.rsi
@@ -266,10 +266,10 @@
Quantity: 3
- type: entity
- name: poison auto-injector
parent: ChemicalMedipen
id: AntiPoisonMedipen
- description: A rapid dose of anti-poison. Contains ultravasculine and epinephrine.
+ name: poison auto-injector
+ description: A single-dose, non-refillable medipen containing ultravasculine and epinephrine, used to quickly and easily treat toxin poisoning.
components:
- type: Item
inhandVisuals:
@@ -303,10 +303,10 @@
Quantity: 5
- type: entity
- name: brute auto-injector
parent: ChemicalMedipen
id: BruteAutoInjector
- description: A rapid dose of bicaridine and tranexamic acid, intended for combat applications.
+ name: brute auto-injector
+ description: A single-dose, non-refillable medipen containing bicaridine and tranexamic acid, used to quickly and easily treat simple physical injuries. Commonly used as a combat medicine.
components:
- type: Item
inhandVisuals:
@@ -340,10 +340,10 @@
Quantity: 5
- type: entity
- name: burn auto-injector
parent: ChemicalMedipen
id: BurnAutoInjector
- description: A rapid dose of dermaline and leporazine, intended for combat applications.
+ name: burn auto-injector
+ description: A single-dose, non-refillable medipen containing dermaline and leporazine, used to quickly and easily treat non-chemical burns. Commonly used as a combat medicine.
components:
- type: Item
inhandVisuals:
@@ -377,10 +377,10 @@
Quantity: 10
- type: entity
- name: rad auto-injector
parent: ChemicalMedipen
id: RadAutoInjector
- description: A rapid dose of anti-radiation. Contains arithrazine and bicaridine.
+ name: rad auto-injector
+ description: A single-dose, non-refillable medipen containing arithrazine and bicaridine, used to quickly and easily treat radiation poisoning.
components:
- type: Item
inhandVisuals:
@@ -414,10 +414,10 @@
Quantity: 5
- type: entity
- name: puncturase auto-injector
parent: ChemicalMedipen
id: PunctAutoInjector
- description: A rapid dose of puncturase and tranexamic acid, intended for combat applications.
+ name: puncturase auto-injector
+ description: A single-dose, non-refillable medipen containing puncturase and tranexamic acid, used to quickly and easily treat puncture wounds. Commonly used as a combat medicine.
components:
- type: Item
inhandVisuals:
@@ -451,10 +451,10 @@
Quantity: 5
- type: entity
- name: pyrazine auto-injector
parent: ChemicalMedipen
id: PyraAutoInjector
- description: A rapid dose of pyrazine and dermaline, intended for combat applications.
+ name: pyrazine auto-injector
+ description: A single-dose, non-refillable medipen containing pyrazine and dermaline, used to quickly and easily treat thermal burns. Commonly used as a combat medicine.
components:
- type: Item
inhandVisuals:
@@ -488,10 +488,9 @@
Quantity: 10
- type: entity
- name: airloss auto-injector
parent: ChemicalMedipen
id: AirlossAutoInjector
- description: A rapid dose of saline and salbutamol, intended to get someone up quickly. # Funkychem - dex+ -> salbutamol
+ description: A single-dose, non-refillable medipen containing saline and dexalin plus, used to quickly and easily treat blood loss and asphyxiation.
components:
- type: Item
inhandVisuals:
@@ -527,10 +526,10 @@
tags: []
- type: entity
- name: space medipen
parent: ChemicalMedipen
id: SpaceMedipen
- description: Contains a mix of chemicals that protect you from the deadly effects of space.
+ name: space medipen
+ description: A single-dose, non-refillable medipen containing barozine and leporazine, used to protect exposed flesh from the deadly effects of space.
components:
- type: Item
inhandVisuals:
@@ -565,10 +564,10 @@
Quantity: 20
- type: entity
- name: hyperzine injector
parent: [ChemicalMedipen, BaseSyndicateContraband]
id: Stimpack
- description: Contains enough hyperzine for you to have the chemical's effect for 30 seconds. Use it when you're sure you're ready to throw down.
+ name: hyperzine injector
+ description: A chemical injector containing a large shot of pure hyperzine. For when it's time to throw down. Effects last for about 30 seconds.
components:
- type: Item
inhandVisuals:
@@ -604,10 +603,10 @@
price: 1500
- type: entity
- name: hyperzine microinjector
parent: [ChemicalMedipen, BaseSyndicateContraband]
id: StimpackMini
- description: A microinjector of hyperzine that give you about 15 seconds of the chemical's effects.
+ name: hyperzine microinjector
+ description: A chemical microinjector containing a small shot of pure hyperzine. Effects last for about 15 seconds.
components:
- type: Item
inhandVisuals:
@@ -643,10 +642,10 @@
price: 583 # 3500 for 6 as a freaky fraction
- type: entity
- name: combat medipen
parent: [ChemicalMedipen, BaseSyndicateContraband]
id: CombatMedipen
- description: A single-use medipen containing chemicals that regenerate most types of damage.
+ name: combat medipen
+ description: A single-dose, non-refillable medipen containing a chemical cocktail that treats most forms of damage.
components:
- type: Item
inhandVisuals:
@@ -682,9 +681,9 @@
price: 1500
- type: entity
- suffix: Hypopen
parent: Pen # It is just like normal pen, isn't it?
id: Hypopen
+ suffix: Hypopen
components:
- type: SolutionContainerManager
solutions:
@@ -729,10 +728,10 @@
path: /Audio/Effects/unwrap.ogg
- type: entity
- name: weh auto-injector
parent: ChemicalMedipen
id: WehMedipen
- description: A rapid dose of Weh. Contains juice that makes you Weh.
+ name: weh auto-injector
+ description: A non-refillable medipen containing multiple doses of weh.
components:
- type: Sprite
sprite: Objects/Specific/Medical/medipen.rsi
From 1052426b3ec1a0de34837b0945d25d6cdd3e67de Mon Sep 17 00:00:00 2001
From: BarryNorfolk
Date: Fri, 23 Jan 2026 08:27:45 +0100
Subject: [PATCH 015/360] Downstream fix for hypospray description updates
---
.../prototypes/entities/objects/specific/medical/hypospray.ftl | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 Resources/Locale/en-US/_DV/prototypes/entities/objects/specific/medical/hypospray.ftl
diff --git a/Resources/Locale/en-US/_DV/prototypes/entities/objects/specific/medical/hypospray.ftl b/Resources/Locale/en-US/_DV/prototypes/entities/objects/specific/medical/hypospray.ftl
new file mode 100644
index 0000000000..d8ddcf0d04
--- /dev/null
+++ b/Resources/Locale/en-US/_DV/prototypes/entities/objects/specific/medical/hypospray.ftl
@@ -0,0 +1,3 @@
+# Funkychem - dex+ -> salbutamol
+ent-AirlossAutoInjector = airloss auto injector
+ .desc = A single-dose, non-refillable medipen containing saline and salbutamol, used to quickly and easily treat blood loss and asphyxiation.
From e21a29ddf822c715f34a51b1ba801614fbd4dbab Mon Sep 17 00:00:00 2001
From: Pok <113675512+Pok27@users.noreply.github.com>
Date: Wed, 3 Dec 2025 21:44:17 +0200
Subject: [PATCH 016/360] Predict BedSystem (#41686)
* BedSystem-move-to-shared
* dependency
* dirty!!!
---
Content.Client/Bed/BedSystem.cs | 8 ---
Content.Server/Bed/BedSystem.cs | 57 -------------------
.../Bed/{SharedBedSystem.cs => BedSystem.cs} | 54 ++++++++++++++++--
.../Components/StasisBedBuckledComponent.cs | 2 +-
.../Bed/Components/StasisBedComponent.cs | 2 +-
5 files changed, 50 insertions(+), 73 deletions(-)
delete mode 100644 Content.Client/Bed/BedSystem.cs
delete mode 100644 Content.Server/Bed/BedSystem.cs
rename Content.Shared/Bed/{SharedBedSystem.cs => BedSystem.cs} (70%)
diff --git a/Content.Client/Bed/BedSystem.cs b/Content.Client/Bed/BedSystem.cs
deleted file mode 100644
index 9c6f28290f..0000000000
--- a/Content.Client/Bed/BedSystem.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using Content.Shared.Bed;
-
-namespace Content.Client.Bed;
-
-public sealed class BedSystem : SharedBedSystem
-{
-
-}
diff --git a/Content.Server/Bed/BedSystem.cs b/Content.Server/Bed/BedSystem.cs
deleted file mode 100644
index c56838c032..0000000000
--- a/Content.Server/Bed/BedSystem.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using Content.Shared.Bed;
-using Content.Shared.Bed.Components;
-using Content.Shared.Bed.Sleep;
-using Content.Shared.Buckle.Components;
-using Content.Shared.Damage.Systems;
-using Content.Shared.Mobs.Systems;
-using Content.Shared.Power;
-using Content.Shared._EE.Silicon.Components; // Goobstation
-
-namespace Content.Server.Bed
-{
- public sealed class BedSystem : SharedBedSystem
- {
- [Dependency] private readonly DamageableSystem _damageableSystem = default!;
- [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
-
- private EntityQuery _sleepingQuery;
-
- public override void Initialize()
- {
- base.Initialize();
-
- _sleepingQuery = GetEntityQuery();
- }
-
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
-
- var query = EntityQueryEnumerator();
- while (query.MoveNext(out var uid, out _, out var bedComponent, out var strapComponent))
- {
- if (Timing.CurTime < bedComponent.NextHealTime)
- continue;
-
- bedComponent.NextHealTime += TimeSpan.FromSeconds(bedComponent.HealTime);
-
- if (strapComponent.BuckledEntities.Count == 0)
- continue;
-
- foreach (var healedEntity in strapComponent.BuckledEntities)
- {
- if (_mobStateSystem.IsDead(healedEntity)
- || HasComp(healedEntity)) // Goobstation
- continue;
-
- var damage = bedComponent.Damage;
-
- if (_sleepingQuery.HasComp(healedEntity))
- damage *= bedComponent.SleepMultiplier;
-
- _damageableSystem.TryChangeDamage(healedEntity, damage, true, origin: uid);
- }
- }
- }
- }
-}
diff --git a/Content.Shared/Bed/SharedBedSystem.cs b/Content.Shared/Bed/BedSystem.cs
similarity index 70%
rename from Content.Shared/Bed/SharedBedSystem.cs
rename to Content.Shared/Bed/BedSystem.cs
index 0518f05fdf..c1b149c218 100644
--- a/Content.Shared/Bed/SharedBedSystem.cs
+++ b/Content.Shared/Bed/BedSystem.cs
@@ -1,10 +1,13 @@
+using Content.Shared._EE.Silicon.Components; // Goobstation
using Content.Shared.Actions;
using Content.Shared.Bed.Components;
using Content.Shared.Bed.Sleep;
using Content.Shared.Body.Events;
using Content.Shared.Body.Systems;
using Content.Shared.Buckle.Components;
+using Content.Shared.Damage.Systems;
using Content.Shared.Emag.Systems;
+using Content.Shared.Mobs.Systems;
using Content.Shared.Power;
using Content.Shared.Power.EntitySystems;
using Robust.Shared.Timing;
@@ -12,16 +15,20 @@ using Robust.Shared.Utility;
namespace Content.Shared.Bed;
-public abstract class SharedBedSystem : EntitySystem
+public sealed class BedSystem : EntitySystem
{
- [Dependency] protected readonly IGameTiming Timing = default!;
[Dependency] private readonly ActionContainerSystem _actConts = default!;
- [Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
+ [Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly EmagSystem _emag = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
+ [Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly SharedMetabolizerSystem _metabolizer = default!;
[Dependency] private readonly SharedPowerReceiverSystem _powerReceiver = default!;
[Dependency] private readonly SleepingSystem _sleepingSystem = default!;
+ private EntityQuery _sleepingQuery;
+
public override void Initialize()
{
base.Initialize();
@@ -35,6 +42,8 @@ public abstract class SharedBedSystem : EntitySystem
SubscribeLocalEvent(OnStasisEmagged);
SubscribeLocalEvent(OnPowerChanged);
SubscribeLocalEvent(OnStasisGetMetabolicMultiplier);
+
+ _sleepingQuery = GetEntityQuery();
}
private void OnHealMapInit(Entity ent, ref MapInitEvent args)
@@ -46,7 +55,7 @@ public abstract class SharedBedSystem : EntitySystem
private void OnStrapped(Entity bed, ref StrappedEvent args)
{
EnsureComp(bed);
- bed.Comp.NextHealTime = Timing.CurTime + TimeSpan.FromSeconds(bed.Comp.HealTime);
+ bed.Comp.NextHealTime = _timing.CurTime + TimeSpan.FromSeconds(bed.Comp.HealTime);
_actionsSystem.AddAction(args.Buckle, ref bed.Comp.SleepAction, SleepingSystem.SleepActionId, bed);
Dirty(bed);
@@ -62,7 +71,7 @@ public abstract class SharedBedSystem : EntitySystem
_actionsSystem.RemoveAction(args.Buckle.Owner, bed.Comp.SleepAction);
_sleepingSystem.TryWaking(args.Buckle.Owner);
}
-
+
RemComp(bed);
}
@@ -112,7 +121,7 @@ public abstract class SharedBedSystem : EntitySystem
args.Multiplier *= stasis.Multiplier;
}
- protected void UpdateMetabolisms(Entity ent)
+ private void UpdateMetabolisms(Entity ent)
{
if (!Resolve(ent, ref ent.Comp, false))
return;
@@ -122,4 +131,37 @@ public abstract class SharedBedSystem : EntitySystem
_metabolizer.UpdateMetabolicMultiplier(buckledEntity);
}
}
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out _, out var bedComponent, out var strapComponent))
+ {
+ if (_timing.CurTime < bedComponent.NextHealTime)
+ continue;
+
+ bedComponent.NextHealTime += TimeSpan.FromSeconds(bedComponent.HealTime);
+
+ Dirty(uid, bedComponent);
+
+ if (strapComponent.BuckledEntities.Count == 0)
+ continue;
+
+ foreach (var healedEntity in strapComponent.BuckledEntities)
+ {
+ if (_mobStateSystem.IsDead(healedEntity)
+ || HasComp(healedEntity)) // Goobstation)
+ continue;
+
+ var damage = bedComponent.Damage;
+
+ if (_sleepingQuery.HasComp(healedEntity))
+ damage *= bedComponent.SleepMultiplier;
+
+ _damageableSystem.TryChangeDamage(healedEntity, damage, true, origin: uid);
+ }
+ }
+ }
}
diff --git a/Content.Shared/Bed/Components/StasisBedBuckledComponent.cs b/Content.Shared/Bed/Components/StasisBedBuckledComponent.cs
index 3a1c991b1e..09759f71ef 100644
--- a/Content.Shared/Bed/Components/StasisBedBuckledComponent.cs
+++ b/Content.Shared/Bed/Components/StasisBedBuckledComponent.cs
@@ -6,5 +6,5 @@ namespace Content.Shared.Bed.Components;
/// Tracking component added to entities buckled to stasis beds.
///
[RegisterComponent, NetworkedComponent]
-[Access(typeof(SharedBedSystem))]
+[Access(typeof(BedSystem))]
public sealed partial class StasisBedBuckledComponent : Component;
diff --git a/Content.Shared/Bed/Components/StasisBedComponent.cs b/Content.Shared/Bed/Components/StasisBedComponent.cs
index 97936261d6..fdc720c34f 100644
--- a/Content.Shared/Bed/Components/StasisBedComponent.cs
+++ b/Content.Shared/Bed/Components/StasisBedComponent.cs
@@ -7,7 +7,7 @@ namespace Content.Shared.Bed.Components;
/// A that modifies a strapped entity's metabolic rate by the given multiplier
///
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
-[Access(typeof(SharedBedSystem))]
+[Access(typeof(BedSystem))]
public sealed partial class StasisBedComponent : Component
{
///
From 1104f5d06610028d3bfe4a513d69917ebc9ee51d Mon Sep 17 00:00:00 2001
From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com>
Date: Wed, 3 Dec 2025 22:55:04 +0100
Subject: [PATCH 017/360] Change stamina slowdown to use a percentage-based
threshold (#41691)
Change stamina to use a percentage based slowdown
---
Content.Shared/Damage/Components/StaminaComponent.cs | 4 ++--
Content.Shared/Damage/Systems/SharedStaminaSystem.cs | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Content.Shared/Damage/Components/StaminaComponent.cs b/Content.Shared/Damage/Components/StaminaComponent.cs
index b343b24403..9875d31349 100644
--- a/Content.Shared/Damage/Components/StaminaComponent.cs
+++ b/Content.Shared/Damage/Components/StaminaComponent.cs
@@ -85,10 +85,10 @@ public sealed partial class StaminaComponent : Component
public SoundSpecifier ForceStandSuccessSound = new SoundPathSpecifier("/Audio/Effects/thudswoosh.ogg");
///
- /// Thresholds that determine an entity's slowdown as a function of stamina damage.
+ /// Thresholds that determine an entity's slowdown as a function of stamina damage, in percentages.
///
[DataField]
- public Dictionary StunModifierThresholds = new() { {0, 1f }, { 60, 0.7f }, { 80, 0.5f } };
+ public Dictionary StunModifierThresholds = new() { {0, 1f }, { 0.6, 0.7f }, { 0.8, 0.5f } };
#region Animation Data
diff --git a/Content.Shared/Damage/Systems/SharedStaminaSystem.cs b/Content.Shared/Damage/Systems/SharedStaminaSystem.cs
index be89c12470..79028962ed 100644
--- a/Content.Shared/Damage/Systems/SharedStaminaSystem.cs
+++ b/Content.Shared/Damage/Systems/SharedStaminaSystem.cs
@@ -465,7 +465,7 @@ public abstract partial class SharedStaminaSystem : EntitySystem
{
var key = thres.Key.Float();
- if (ent.Comp.StaminaDamage >= key && key > closest && closest < ent.Comp.CritThreshold)
+ if ((ent.Comp.StaminaDamage / ent.Comp.CritThreshold) >= key && key > closest && closest < 1f)
closest = thres.Key;
}
From 0150cb5ed1faf8574fb4a24793377ff7e06a9f2c Mon Sep 17 00:00:00 2001
From: JackspajfMain <105893899+JackspajfMain@users.noreply.github.com>
Date: Wed, 3 Dec 2025 20:06:54 -0500
Subject: [PATCH 018/360] Change to fix wording of Pun Pun's jacket (#41695)
Slightly fixes wording on Pun Pun's jacket
---
Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml
index 2cab388fc5..4cef2dbec5 100644
--- a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml
+++ b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml
@@ -34,7 +34,7 @@
- type: entity
parent: ClothingUniformBase
id: ClothingUniformJumpsuitJacketMonkey
- name: bartender's jacket monkey
+ name: bartender's monkey jacket
description: A decent jacket, for a decent monkey.
components:
- type: Sprite
From a4c5cff209652790fb7d54101db2ad4a32946494 Mon Sep 17 00:00:00 2001
From: Hitlinemoss <209321380+Hitlinemoss@users.noreply.github.com>
Date: Tue, 3 Jun 2025 12:01:07 -0400
Subject: [PATCH 019/360] Cargo food crate adjustments (#38006)
* Adjusted cargo food prices; removed large pizza/softdrinks crates
* Adjusted salvage_rewards.yml and cargo_gifts.yml around the removal of large pizza/soda crates
---
Resources/Migrations/migration.yml | 8 ++--
.../Prototypes/Catalog/Cargo/cargo_food.yml | 32 +++------------
.../Prototypes/Catalog/Fills/Crates/food.yml | 39 -------------------
.../Prototypes/GameRules/cargo_gifts.yml | 4 +-
.../Prototypes/Procedural/salvage_rewards.yml | 2 +-
5 files changed, 13 insertions(+), 72 deletions(-)
diff --git a/Resources/Migrations/migration.yml b/Resources/Migrations/migration.yml
index 9cdbac19ca..a7804f0be7 100644
--- a/Resources/Migrations/migration.yml
+++ b/Resources/Migrations/migration.yml
@@ -618,9 +618,9 @@ WeaponEnergyTurretStationMachineCircuitboard: WeaponEnergyTurretSecurityMachineC
# 2025-05-30
SpawnHonkBot: SpawnMobHonkBot
-# 2025-06-01 - DeltaV: https://github.com/space-wizards/space-station-14/pull/38006 was never merged
-#CrateFoodPizzaLarge: CrateFoodPizza
-#CrateFoodSoftdrinksLarge: CrateFoodSoftdrinks
+# 2025-06-01
+CrateFoodPizzaLarge: CrateFoodPizza
+CrateFoodSoftdrinksLarge: CrateFoodSoftdrinks
# 2025-06-03
FloorTileItemGCircuit4: FloorTileItemGCircuit
@@ -691,4 +691,4 @@ ToyOwlman: ToyFigurineOwlman
ToySkeleton: ToyFigurineSkeleton
# 2025-10-28
-DrinkKiraSpecial: DrinkOrangeLimeSodaGlass
\ No newline at end of file
+DrinkKiraSpecial: DrinkOrangeLimeSodaGlass
diff --git a/Resources/Prototypes/Catalog/Cargo/cargo_food.yml b/Resources/Prototypes/Catalog/Cargo/cargo_food.yml
index e5847179c9..eec2516a8e 100644
--- a/Resources/Prototypes/Catalog/Cargo/cargo_food.yml
+++ b/Resources/Prototypes/Catalog/Cargo/cargo_food.yml
@@ -4,27 +4,17 @@
sprite: Objects/Consumable/Food/Baked/pizza.rsi
state: margherita-slice
product: CrateFoodPizza
- cost: 1000 # DeltaV - raised from 450
+ cost: 1000 # DeltaV - Lowered from 1600
category: cargoproduct-category-name-food
group: market
-#- type: cargoProduct # DeltaV - Removed to not invalidate chef
-# id: FoodPizzaLarge
-# icon:
-# sprite: Objects/Consumable/Food/Baked/pizza.rsi
-# state: margherita
-# product: CrateFoodPizzaLarge
-# cost: 1800
-# category: cargoproduct-category-name-food
-# group: market
-
- type: cargoProduct
id: FoodMRE
icon:
sprite: Objects/Consumable/Food/snacks.rsi
state: nutribrick
product: CrateFoodMRE
- cost: 1000
+ cost: 1400
category: cargoproduct-category-name-food
group: market
@@ -64,17 +54,7 @@
sprite: Objects/Consumable/Drinks/cola.rsi
state: icon
product: CrateFoodSoftdrinks
- cost: 1200
- category: cargoproduct-category-name-food
- group: market
-
-- type: cargoProduct
- id: FoodSoftdrinksLarge
- icon:
- sprite: Objects/Consumable/Drinks/colabottle.rsi
- state: icon
- product: CrateFoodSoftdrinksLarge
- cost: 2400
+ cost: 1500
category: cargoproduct-category-name-food
group: market
@@ -94,7 +74,7 @@
sprite: Objects/Consumable/Food/burger.rsi
state: bigbite
product: CrateFoodHappyHonkBigBite
- cost: 1500
+ cost: 2800
category: cargoproduct-category-name-food
group: market
@@ -104,7 +84,7 @@
sprite: Objects/Consumable/Food/frozen.rsi
state: sandwich
product: CrateFoodIceCream
- cost: 1200
+ cost: 2000
category: cargoproduct-category-name-food
group: market
@@ -114,6 +94,6 @@
sprite: Objects/Consumable/Food/frozen.rsi
state: cone
product: CrateFoodSnowcone
- cost: 1100
+ cost: 2000
category: cargoproduct-category-name-food
group: market
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/food.yml b/Resources/Prototypes/Catalog/Fills/Crates/food.yml
index 150d70e508..cb263ee735 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/food.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/food.yml
@@ -14,20 +14,6 @@
- id: LidSalami
prob: 0.01
-- type: entity
- id: CrateFoodPizzaLarge
- parent: CratePlastic
- name: disaster pizza delivery
- description: In the ultimate event that all else has failed, Find comfort in that more pizza solves everything. Includes 16 pizzas.
- components:
- - type: StorageFill
- contents:
- - id: FoodBoxPizzaFilled
- amount: 15
- - id: FoodBoxPizzaCotton
- - id: LidSalami
- prob: 0.04
-
- type: entity
id: CrateFoodMRE
parent: CratePlastic
@@ -155,31 +141,6 @@
- id: DrinkFourteenLokoCan
amount: 2
-- type: entity
- id: CrateFoodSoftdrinksLarge
- parent: CrateFreezer
- name: softdrinks bulk crate
- description: Lots of sodas taken straight out of Centcomm's own vending machines, because you just can't leave your department. Includes 32 sodas.
- components:
- - type: EntityStorage
- capacity: 32 # Slightly over-sized CrateFreezer to accomodate over 30 drink cans at once.
- - type: StorageFill
- contents:
- - id: DrinkColaCan
- amount: 8
- - id: DrinkGrapeCan
- amount: 4
- - id: DrinkRootBeerCan
- amount: 4
- - id: DrinkIcedTeaCan
- amount: 4
- - id: DrinkLemonLimeCan
- amount: 4
- - id: DrinkLemonLimeCranberryCan
- amount: 4
- - id: DrinkFourteenLokoCan
- amount: 4
-
- type: entity
id: CrateFoodGetMore
parent: CrateFreezer
diff --git a/Resources/Prototypes/GameRules/cargo_gifts.yml b/Resources/Prototypes/GameRules/cargo_gifts.yml
index 486280a1af..a881bc29b6 100644
--- a/Resources/Prototypes/GameRules/cargo_gifts.yml
+++ b/Resources/Prototypes/GameRules/cargo_gifts.yml
@@ -65,9 +65,9 @@
description: cargo-gift-pizza-large
dest: cargo-gift-dest-bar
gifts:
- FoodPizza: 2 # DeltaV - Changed to 2 small pizza crates, 8 pizzas total
+ FoodPizza: 2 # DeltaV - Changed to 2 small pizza crates, 8 pizzas total (Was 16)
FoodBarSupply: 1
- FoodSoftdrinksLarge: 1
+ FoodSoftdrinks: 2 # 32 sodas
- type: entity
id: GiftsEngineering
diff --git a/Resources/Prototypes/Procedural/salvage_rewards.yml b/Resources/Prototypes/Procedural/salvage_rewards.yml
index 2f24a32d1d..a54ac8fbb0 100644
--- a/Resources/Prototypes/Procedural/salvage_rewards.yml
+++ b/Resources/Prototypes/Procedural/salvage_rewards.yml
@@ -8,7 +8,7 @@
CrateMaterialSteel: 1.0
# things the station might want
CrateEngineeringAMEJar: 0.25
- CrateFoodPizzaLarge: 0.25
+ CrateFoodPizza: 0.25
CrateFoodSoftdrinks: 0.25
CrateFunInstrumentsVariety: 0.25
CrateSalvageEquipment: 0.25
From 514d6b5b395087eff9b3bd1da408ddc1e656f394 Mon Sep 17 00:00:00 2001
From: Hitlinemoss <209321380+Hitlinemoss@users.noreply.github.com>
Date: Thu, 4 Dec 2025 02:55:35 -0500
Subject: [PATCH 020/360] Cleanup of prototypes in
Resources/Prototypes/Catalog/Fills/Crates/ + fixed light crate descriptions
(#41697)
* Fix prototype string order
* Fix replacement lights crate description
* Clearer light descriptions in general
* Suffix fix
* Updated internals crate descriptions
---
.../Prototypes/Catalog/Fills/Crates/antag.yml | 4 +-
.../Catalog/Fills/Crates/armory.yml | 14 ++--
.../Catalog/Fills/Crates/botany.yml | 10 +--
.../Prototypes/Catalog/Fills/Crates/cargo.yml | 6 +-
.../Catalog/Fills/Crates/chemistry.yml | 8 +--
.../Catalog/Fills/Crates/emergency.yml | 22 +++---
.../Catalog/Fills/Crates/engineering.yml | 40 +++++------
.../Catalog/Fills/Crates/engines.yml | 30 ++++----
.../Prototypes/Catalog/Fills/Crates/food.yml | 20 +++---
.../Prototypes/Catalog/Fills/Crates/fun.yml | 55 ++++++++-------
.../Catalog/Fills/Crates/materials.yml | 34 +++++-----
.../Catalog/Fills/Crates/medical.yml | 28 ++++----
.../Prototypes/Catalog/Fills/Crates/npc.yml | 48 ++++++-------
.../Catalog/Fills/Crates/permaescape.yml | 56 +++++++--------
.../Catalog/Fills/Crates/salvage.yml | 10 +--
.../Catalog/Fills/Crates/science.yml | 6 +-
.../Catalog/Fills/Crates/security.yml | 14 ++--
.../Catalog/Fills/Crates/service.yml | 68 +++++++++----------
.../Catalog/Fills/Crates/syndicate.yml | 8 +--
.../Catalog/Fills/Crates/vending.yml | 53 +++++++--------
20 files changed, 266 insertions(+), 268 deletions(-)
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/antag.yml b/Resources/Prototypes/Catalog/Fills/Crates/antag.yml
index 352b33a3d0..8b397bd31c 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/antag.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/antag.yml
@@ -1,8 +1,8 @@
- type: entity
+ parent: CratePirate
id: CratePirateChestCaptain
name: captains pirate chest
suffix: Filled
- parent: CratePirate
components:
- type: EntityTableContainerFill
containers:
@@ -13,10 +13,10 @@
- id: MicroBombImplanter
- type: entity
+ parent: CratePirate
id: CratePirateChest
name: crews pirate chest
suffix: Filled
- parent: CratePirate
components:
- type: EntityTableContainerFill
containers:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/armory.yml b/Resources/Prototypes/Catalog/Fills/Crates/armory.yml
index 6a172a8a6f..9e01feb50e 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/armory.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/armory.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateArmorySMG
parent: [ CrateWeaponSecure, BaseSecurityContraband ]
+ id: CrateArmorySMG
name: SMG crate
description: Contains two SMGs with four mags. Requires Armory access to open.
components:
@@ -14,8 +14,8 @@
amount: 4
- type: entity
- id: CrateArmoryShotgun
parent: [ CrateWeaponSecure, BaseSecurityContraband ]
+ id: CrateArmoryShotgun
name: shotgun crate
description: For when the enemy absolutely needs to be replaced with lead. Contains two Enforcer Combat Shotguns, and some standard shotgun shells. Requires Armory access to open.
components:
@@ -29,8 +29,8 @@
amount: 4
- type: entity
- id: CrateTrackingImplants
parent: [ CrateWeaponSecure, BaseSecurityContraband ]
+ id: CrateTrackingImplants
name: tracking implants
description: Contains a handful of tracking implanters. Good for prisoners you'd like to release but still keep track of.
components:
@@ -53,8 +53,8 @@
amount: 3
- type: entity
- id: CrateArmoryLaser
parent: [ CrateWeaponSecure, BaseSecurityContraband ]
+ id: CrateArmoryLaser
name: lasers crate
description: Contains three standard-issue laser rifles. Requires Armory access to open.
components:
@@ -65,8 +65,8 @@
amount: 3
- type: entity
- id: CrateArmoryPistols
parent: [ CrateWeaponSecure, BaseSecurityContraband ]
+ id: CrateArmoryPistols
name: pistols crate
description: Contains two standard NT pistols with four mags. Requires Armory access to open.
components:
@@ -80,8 +80,8 @@
amount: 4
- type: entity
- id: CrateSecurityRiot
parent: [ CrateWeaponSecure, BaseSecurityContraband ]
+ id: CrateSecurityRiot
name: swat crate
description: Contains two sets of riot armor, helmets, shields, and enforcers loaded with beanbags. Extra ammo is included. Requires Armory access to open.
components:
@@ -101,8 +101,8 @@
amount: 2
- type: entity
- id: CrateArmoryRifle
parent: [ CrateWeaponSecure, BaseSecurityContraband ]
+ id: CrateArmoryRifle
name: rifle crate
description: Contains two high-powered assault rifles with four mags. Requires Armory access to open.
components:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/botany.yml b/Resources/Prototypes/Catalog/Fills/Crates/botany.yml
index 7f95f57d73..99702bc018 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/botany.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/botany.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateHydroponicsSeedsExotic
parent: CrateHydroSecure
+ id: CrateHydroponicsSeedsExotic
name: exotic seeds crate
description: Any entrepreneuring botanist's dream. Contains many different exotic seeds. Requires Hydroponics access to open.
components:
@@ -24,8 +24,8 @@
amount: 2
- type: entity
- id: CrateHydroponicsSeedsMedicinal
parent: CrateHydroSecure
+ id: CrateHydroponicsSeedsMedicinal
name: medicinal seeds crate
description: The wannabe chemist's dream. The power of medicine is at your fingertips! Requires Hydroponics access to open.
components:
@@ -45,8 +45,8 @@
amount: 3
- type: entity
- id: CrateHydroponicsTools
parent: CrateHydroponics
+ id: CrateHydroponicsTools
name: hydroponics equipment crate
description: Supplies for growing a great garden! Contains some spray bottles of plant chemicals, a hatchet, a mini-hoe, scythe, as well as a pair of leather gloves and a botanist's apron.
components:
@@ -68,8 +68,8 @@
amount: 2
- type: entity
- id: CrateHydroponicsSeeds
parent: CrateHydroponics
+ id: CrateHydroponicsSeeds
name: seeds crate
description: Big things have small beginnings. Contains twenty-four different seeds.
components:
@@ -103,8 +103,8 @@
- id: CottonSeeds
- type: entity
- id: CrateHydroponicsTray
parent: CrateHydroponics
+ id: CrateHydroponicsTray
name: hydroponics tray crate
description: Contains a hydroponics tray flatpack.
components:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/cargo.yml b/Resources/Prototypes/Catalog/Fills/Crates/cargo.yml
index 6cc4b70b7a..35e1c426c8 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/cargo.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/cargo.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateCargoLuxuryHardsuit
parent: CratePirate
+ id: CrateCargoLuxuryHardsuit
name: luxury mining hardsuit crate
# DeltaV - Logistics Department - Replace Quartermaster with Logistics Officer
description: Finally, a hardsuit the Logistics Officer could call their own. Centcomm has heard you, now stop asking.
@@ -12,8 +12,8 @@
id: ClothingOuterHardsuitLuxury
- type: entity
- id: CrateCargoParcelWrap
parent: CrateGenericSteel
+ id: CrateCargoParcelWrap
name: parcel wrap crate
description: All your parcel wrapping needs in one crate, containing three rolls of parcel wrap.
components:
@@ -24,10 +24,10 @@
amount: 3
- type: entity
+ parent: CratePrivateSecure
id: CrateCargoGambling
name: the grand lottery $$$
description: A box containing treasure beyond your greatest imaginations!
- parent: CratePrivateSecure
components:
- type: EntityTableContainerFill
containers:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/chemistry.yml b/Resources/Prototypes/Catalog/Fills/Crates/chemistry.yml
index 45fb81f1b5..01d05cd359 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/chemistry.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/chemistry.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateChemistryP
parent: CrateChemistrySecure
+ id: CrateChemistryP
name: chemicals crate (P)
description: Contains chemicals from the P-Block of elements. Requires Chemistry access to open.
components:
@@ -20,8 +20,8 @@
- id: JugNitrogen
- type: entity
- id: CrateChemistryS
parent: CrateChemistrySecure
+ id: CrateChemistryS
name: chemicals crate (S)
description: Contains chemicals from the S-Block of elements. Requires Chemistry access to open.
components:
@@ -36,8 +36,8 @@
- id: JugRadium
- type: entity
- id: CrateChemistryD
parent: CrateChemistrySecure
+ id: CrateChemistryD
name: chemicals crate (D)
description: Contains chemicals from the D-Block of elements. Requires Chemistry access to open.
components:
@@ -52,8 +52,8 @@
- id: JugSilver
- type: entity
- id: CratePlantBGone
parent: CrateGenericSteel
+ id: CratePlantBGone
name: bulk Plant-B-Gone crate
description: From Monstano. "Unwanted Weeds, Meet Your Celestial Roundup!"
components:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/emergency.yml b/Resources/Prototypes/Catalog/Fills/Crates/emergency.yml
index bf47d3f271..dd82c2208f 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/emergency.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/emergency.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateEmergencyExplosive
parent: CrateSecgear
+ id: CrateEmergencyExplosive
name: bomb suit crate
description: Science gone bonkers? Beeping behind the airlock? Buy now and be the hero the station des... I mean needs! (time not included)
components:
@@ -16,8 +16,8 @@
- id: ClothingOuterSuitBomb
- type: entity
- id: CrateEmergencyFire
parent: CrateGenericSteel
+ id: CrateEmergencyFire
name: firefighting crate
description: Only you can prevent station fires. Partner up with two firefighter suits, gas masks, flashlights, large oxygen tanks, extinguishers, and hardhats!
components:
@@ -39,10 +39,10 @@
amount: 2
- type: entity
+ parent: CrateInternals
id: CrateEmergencyInternals
- parent: CrateInternals
name: internals crate
- description: Master your life energy and control your breathing with 3 breath masks, emergency suits and large air tanks.
+ description: Master your life energy and control your breathing with three breath masks, three gas masks, three emergency EVA suits, three oxygen tanks, and three nitrogen tanks.
components:
- type: EntityTableContainerFill
containers:
@@ -60,10 +60,10 @@
amount: 3
- type: entity
+ parent: CrateInternals
id: CrateEmergencyInternalsLarge
- parent: CrateInternals
name: internals crate (large)
- description: Master your life energy and control your breathing with 6 breath masks, emergency suits and large air tanks.
+ description: Master your life energy and control your breathing with six breath masks, six gas masks, six emergency EVA suits, six oxygen tanks, and six nitrogen tanks.
components:
- type: EntityTableContainerFill
containers:
@@ -81,10 +81,10 @@
amount: 6
- type: entity
- id: CrateNitrogenInternals
parent: CrateInternals
+ id: CrateNitrogenInternals
name: internals crate (nitrogen)
- description: Contains four breath masks and four large nitrogen tanks. Intended for Slimepeople and Vox.
+ description: Contains two breath masks, two gas masks, and four nitrogen tanks. Intended for nitrogen-breathing species.
components:
- type: EntityTableContainerFill
containers:
@@ -98,8 +98,8 @@
amount: 4
- type: entity
- id: CrateEmergencyRadiation
parent: CrateRadiation
+ id: CrateEmergencyRadiation
name: radiation protection crate
description: Survive the Nuclear Apocalypse and Supermatter Engine alike with two sets of Radiation suits. Each set contains a helmet, suit, and Geiger counter. We'll even throw in a bottle of vodka and some glasses too, considering the life-expectancy of people who order this.
components:
@@ -116,8 +116,8 @@
amount: 2
- type: entity
- id: CrateEmergencyInflatablewall
parent: CratePlastic
+ id: CrateEmergencyInflatablewall
name: inflatable wall crate
description: Three stacks of inflatable walls for when the stations metal walls don't want to hold atmosphere anymore.
components:
@@ -127,8 +127,8 @@
id: BoxInflatable
- type: entity
- id: CrateGenericBiosuit
parent: CratePlastic
+ id: CrateGenericBiosuit
name: emergency bio suit crate
description: Contains 2 biohazard suits to ensure that no disease will distract you from what you're doing there.
components:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml b/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml
index bcf73a2c4d..27ade67868 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/engineering.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateEngineeringGear
parent: CrateEngineering
+ id: CrateEngineeringGear
name: engineering gear crate
description: Various engineering gear parts.
components:
@@ -22,8 +22,8 @@
amount: 2
- type: entity
- id: CrateEngineeringToolbox
parent: CrateEngineering
+ id: CrateEngineeringToolbox
name: toolbox crate
description: Two mechanical and two electrical toolboxes.
components:
@@ -37,8 +37,8 @@
amount: 2
#- type: entity
-# id: CrateEngineeringPowercell
# parent: CrateElectrical
+# id: CrateEngineeringPowercell
# name: AME crate
# description: Three microcreactor powercells.
# components:
@@ -49,8 +49,8 @@
# amount: 3
- type: entity
- id: CrateEngineeringCableLV
parent: CrateElectrical
+ id: CrateEngineeringCableLV
name: LV cable crate
description: 3 coils of LV cables.
components:
@@ -61,8 +61,8 @@
amount: 3
- type: entity
- id: CrateEngineeringCableMV
parent: CrateElectrical
+ id: CrateEngineeringCableMV
name: MV cable crate
description: 3 coils of MV cables.
components:
@@ -73,8 +73,8 @@
amount: 3
- type: entity
- id: CrateEngineeringCableHV
parent: CrateElectrical
+ id: CrateEngineeringCableHV
name: HV cable crate
description: 3 coils of HV cables.
components:
@@ -85,8 +85,8 @@
amount: 3
- type: entity
- id: CrateEngineeringFoamGrenade
parent: CrateEngineeringSecure
+ id: CrateEngineeringFoamGrenade
name: sealant grenade crate
description: 5 metal foam sealant grenades.
components:
@@ -97,8 +97,8 @@
amount: 5
- type: entity
- id: CrateEngineeringCableBulk
parent: CrateElectrical
+ id: CrateEngineeringCableBulk
name: bulk cable crate
description: 2 coils each for every cable type.
components:
@@ -114,8 +114,8 @@
amount: 2
- type: entity
- id: CrateEngineeringElectricalSupplies
parent: CrateElectrical
+ id: CrateEngineeringElectricalSupplies
name: electrical supplies crate
description: NT is not responsible for any workplace infighting relating to the insulated gloves included within these crates.
components:
@@ -129,8 +129,8 @@
amount: 2
- type: entity
- id: CrateEngineeringStationBeaconBundle
parent: CratePlastic
+ id: CrateEngineeringStationBeaconBundle
name: station beacon bundle
description: A crate containing 5 station beacon assemblies for modifying the station map.
components:
@@ -141,8 +141,8 @@
amount: 5
- type: entity
- id: CrateEngineeringJetpack
parent: CrateGenericSteel
+ id: CrateEngineeringJetpack
name: jetpack crate
description: Two jetpacks for those who don't know how to use fire extinguishers.
components:
@@ -153,8 +153,8 @@
amount: 2
- type: entity
- id: CrateEngineeringMiniJetpack
parent: CrateGenericSteel
+ id: CrateEngineeringMiniJetpack
name: mini jetpack crate
description: Two mini jetpacks for those who want an extra challenge.
components:
@@ -165,8 +165,8 @@
amount: 2
- type: entity
- id: CrateAirlockKit
parent: CrateEngineering
+ id: CrateAirlockKit
name: airlock kit
description: A kit for building 6 airlocks, doesn't include tools.
components:
@@ -180,8 +180,8 @@
amount: 6
- type: entity
- id: CrateEvaKit
parent: CrateCommandSecure
+ id: CrateEvaKit
name: EVA kit
description: A set consisting of two prestigious EVA suits and helmets.
components:
@@ -195,8 +195,8 @@
amount: 2
- type: entity
- id: CrateRCDAmmo
parent: CrateEngineering
+ id: CrateRCDAmmo
name: compressed matter crate
description: Contains three compressed matter cartridges.
components:
@@ -207,8 +207,8 @@
amount: 3
- type: entity
- id: CrateRCD
parent: CrateEngineeringSecure
+ id: CrateRCD
name: RCD crate
description: A crate containing a single rapid construction device.
components:
@@ -218,8 +218,8 @@
id: RCD
- type: entity
- id: CrateParticleDecelerators
parent: CrateEngineeringSecure
+ id: CrateParticleDecelerators
name: particle decelerators crate
description: A crate containing 3 Particle Decelerators.
components:
@@ -230,8 +230,8 @@
amount: 3
- type: entity
- id: CrateEngineeringSpaceHeater
parent: CrateEngineering
+ id: CrateEngineeringSpaceHeater
name: space heater crate
description: Contains a space heater for climate control.
components:
@@ -258,8 +258,8 @@
- id: BlockGameArcadeComputerCircuitboard
- type: entity
- id: CrateTechBoardRandom
parent: ToteBase
+ id: CrateTechBoardRandom
name: surplus boards
description: Surplus boards from somewhere.
components:
@@ -271,8 +271,8 @@
range: 6, 8
- type: entity
- id: CrateAirGrenade
parent: CrateEngineering
+ id: CrateAirGrenade
name: air grenade crate
description: A crate containing 3 air grenades.
components:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/engines.yml b/Resources/Prototypes/Catalog/Fills/Crates/engines.yml
index e81b6135ec..92a3eabe08 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/engines.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/engines.yml
@@ -1,8 +1,8 @@
# AME
- type: entity
- id: CrateEngineeringAMEShielding
parent: CrateEngineeringSecure
+ id: CrateEngineeringAMEShielding
name: packaged antimatter reactor crate
description: 9 parts for the main body of an antimatter reactor, or for expanding an existing one.
components:
@@ -13,8 +13,8 @@
amount: 9
- type: entity
- id: CrateEngineeringAMEJar
parent: CrateEngineeringSecure
+ id: CrateEngineeringAMEJar
name: antimatter containment jar crate
description: 3 antimatter jars, for fuelling an antimatter reactor.
components:
@@ -25,8 +25,8 @@
amount: 3
- type: entity
- id: CrateEngineeringAMEControl
parent: CrateEngineeringSecure
+ id: CrateEngineeringAMEControl
name: antimatter control unit crate
description: The control unit of an antimatter reactor.
components:
@@ -38,8 +38,8 @@
# Singularity
- type: entity
- id: CrateEngineeringSingularityEmitter
parent: CrateEngineeringSecure
+ id: CrateEngineeringSingularityEmitter
name: emitter crate
description: An emitter, best used for singularity engines.
components:
@@ -49,8 +49,8 @@
id: EmitterFlatpack
- type: entity
- id: CrateEngineeringSingularityCollector
parent: CrateEngineeringSecure
+ id: CrateEngineeringSingularityCollector
name: radiation collector crate
description: A radiation collector, best used for singularity engines. Plasma is included.
components:
@@ -62,8 +62,8 @@
- id: PlasmaTankFilled
- type: entity
- id: CrateEngineeringSingularityContainment
parent: CrateEngineeringSecure
+ id: CrateEngineeringSingularityContainment
name: containment field generator crate
description: A containment field generator, keeps the singulo in submission.
components:
@@ -73,8 +73,8 @@
id: ContainmentFieldGeneratorFlatpack
- type: entity
- id: CrateEngineeringSingularityGenerator
parent: CrateEngineeringSecure
+ id: CrateEngineeringSingularityGenerator
name: singularity generator crate
description: A singularity generator, the mother of the beast.
components:
@@ -86,8 +86,8 @@
# Particle Accelerator
- type: entity
- id: CrateEngineeringParticleAccelerator
parent: CrateEngineeringSecure
+ id: CrateEngineeringParticleAccelerator
name: PA board crate
description: Complex to setup, but rewarding as fuck. Contains boards for all particle accelerator components.
components:
@@ -106,10 +106,10 @@
# Non-functional for some reason
#- type: entity
+# parent: CrateEngineeringSecure
# id: CrateEngineeringSingularity
# name: singularity crate
# description: "Prank the station!"
-# parent: CrateEngineeringSecure
# components:
# - type: EntityTableContainerFill
# containers:
@@ -117,8 +117,8 @@
# id: Singularity
- type: entity
- id: CrateEngineeringGenerator
parent: CrateEngineering
+ id: CrateEngineeringGenerator
name: generator crate
suffix: DEBUG
components:
@@ -128,8 +128,8 @@
id: DebugGenerator # TODO change to flatpack
- type: entity
- id: CrateEngineeringSolar
parent: CrateEngineering
+ id: CrateEngineeringSolar
name: solar assembly crate
description: A kit with solar flatpacks and glass to construct ten solar panels.
components:
@@ -143,8 +143,8 @@
amount: 2
- type: entity
- id: CrateEngineeringShuttle
parent: CrateEngineeringSecure
+ id: CrateEngineeringShuttle
name: shuttle powering crate
description: A crate containing all needs for shuttle powering.
components:
@@ -158,8 +158,8 @@
- id: InflatableDoorStack1
- type: entity
- id: CrateEngineeringTeslaGenerator
parent: CrateEngineeringSecure
+ id: CrateEngineeringTeslaGenerator
name: tesla generator crate
description: A tesla generator. God save you.
components:
@@ -169,8 +169,8 @@
id: TeslaGeneratorFlatpack
- type: entity
- id: CrateEngineeringTeslaCoil
parent: CrateEngineeringSecure
+ id: CrateEngineeringTeslaCoil
name: tesla coil crate
description: Tesla coil. Attracts lightning and generates energy from it.
components:
@@ -180,8 +180,8 @@
id: TeslaCoilFlatpack
- type: entity
- id: CrateEngineeringTeslaGroundingRod
parent: CrateEngineeringSecure
+ id: CrateEngineeringTeslaGroundingRod
name: tesla grounding rod crate
description: Grounding rod, best for lightning protection.
components:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/food.yml b/Resources/Prototypes/Catalog/Fills/Crates/food.yml
index cb263ee735..29203f32d0 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/food.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/food.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateFoodPizza
parent: CratePlastic
+ id: CrateFoodPizza
name: emergency pizza delivery
description: Help do your part to end station hunger by distributing pizza to underfunded departments! Includes 4 pizzas.
components:
@@ -15,8 +15,8 @@
prob: 0.01
- type: entity
- id: CrateFoodMRE
parent: CratePlastic
+ id: CrateFoodMRE
name: MRE crate
description: A military style meal fit to feed a whole department.
components:
@@ -27,8 +27,8 @@
amount: 6
- type: entity
- id: CrateFoodCooking
parent: CrateFreezer
+ id: CrateFoodCooking
name: kitchen supplies crate
description: Extra kitchen supplies, in case the botanists are absent.
components:
@@ -61,8 +61,8 @@
amount: 2
- type: entity
- id: CrateFoodDinnerware
parent: CratePlastic
+ id: CrateFoodDinnerware
name: kitchen dinnerware crate
description: Extra kitchen supplies, in case the clown was allowed in the cafeteria unsupervised.
components:
@@ -88,8 +88,8 @@
- id: BoxUtensil
- type: entity
- id: CrateFoodBarSupply
parent: CrateFreezer
+ id: CrateFoodBarSupply
name: bartending supplies crate
description: Extra Bar supplies, in case the clown was allowed in the bar unsupervised.
components:
@@ -117,8 +117,8 @@
amount: 2
- type: entity
- id: CrateFoodSoftdrinks
parent: CrateFreezer
+ id: CrateFoodSoftdrinks
name: softdrinks crate
description: A variety of sodas to complement a small party, without having to empty the soda machines. Includes 14 sodas.
components:
@@ -142,8 +142,8 @@
amount: 2
- type: entity
- id: CrateFoodGetMore
parent: CrateFreezer
+ id: CrateFoodGetMore
name: Getmore Bakemore crate
description: Getmore branded snacks and baking supplies for the creative chef, all without the need of emptying your station's Getmore machines!
components:
@@ -170,8 +170,8 @@
- id: FoodBoxCloth
- type: entity
- id: CrateFoodIceCream
parent: CrateFreezer
+ id: CrateFoodIceCream
name: ice cream delivery
description: An assortment of ice cream delights for any occasion! Includes 16 frozen treats.
components:
@@ -195,8 +195,8 @@
amount: 2
- type: entity
- id: CrateFoodSnowcone
parent: CrateFreezer
+ id: CrateFoodSnowcone
name: snowcone delivery
description: A freezer packed with refreshing snowcones for a hard working crew, or even a lazy one! Includes 16 snowcones.
components:
@@ -218,8 +218,8 @@
amount: 2
- type: entity
- id: CrateFoodHappyHonkBigBite
parent: CratePlastic
+ id: CrateFoodHappyHonkBigBite
name: Happy Honk meal delivery
description: Two fully loaded Happy Honk Big Bite burger meals, complete with cheesy fries, a bottle of Space Cola, a slice of apple pie and a toy!
components:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/fun.yml b/Resources/Prototypes/Catalog/Fills/Crates/fun.yml
index a32fae9fef..cb40b508f7 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/fun.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/fun.yml
@@ -156,8 +156,8 @@
- id: PottedPlant26
- type: entity
- id: CratePlants
parent: CrateGenericSteel
+ id: CratePlants
name: plant crate
description: A variety pack of potted plants to spruce up your station!
components:
@@ -169,8 +169,8 @@
value: 5
- type: entity
- id: CrateFunPlushie
parent: CrateGenericSteel
+ id: CrateFunPlushie
name: plushie crate
description: A buncha soft plushies. Throw them around and then wonder how you're gonna explain this purchase to NT.
components:
@@ -182,8 +182,8 @@
value: 10
- type: entity
- id: CrateFunLizardPlushieBulk
parent: CrateGenericSteel
+ id: CrateFunLizardPlushieBulk
name: bulk lizard plushie crate
description: A buncha soft lizard plushies. Throw them around and then wonder how you're gonna explain this purchase to NT.
components:
@@ -195,8 +195,8 @@
value: 6
- type: entity
- id: CrateFunSharkPlushieBulk
parent: CrateGenericSteel
+ id: CrateFunSharkPlushieBulk
name: bulk soft toy shark crate
description: A crate filled with a variety of everyone's favorite finned friend. Rawr!
components:
@@ -215,8 +215,8 @@
value: 2
- type: entity
- id: CrateFunInstrumentsVariety
parent: CrateGenericSteel
+ id: CrateFunInstrumentsVariety
name: variety instrument collection
description: Get your sad station movin' and groovin' with this catch-all variety pack! Contains seven different instruments.
components:
@@ -233,8 +233,8 @@
- id: GlockenspielInstrument
- type: entity
- id: CrateFunInstrumentsBrass
parent: CrateGenericSteel
+ id: CrateFunInstrumentsBrass
name: brass instrument ensemble crate
description: Bring some jazz to the station with the brass ensemble. Contains a variety of brass instruments for the whole station to play.
components:
@@ -252,8 +252,8 @@
- id: TubaInstrument
- type: entity
- id: CrateFunInstrumentsString
parent: CrateGenericSteel
+ id: CrateFunInstrumentsString
name: string instrument ensemble crate
description: Pluck or pick, slap or shred! Play a smooth melody or melt peoples' faces with this package of stringed instruments.
components:
@@ -272,8 +272,8 @@
- id: HarpInstrument
- type: entity
- id: CrateFunInstrumentsWoodwind
parent: CrateGenericSteel
+ id: CrateFunInstrumentsWoodwind
name: woodwind instrument ensemble crate
description: If atmos is good at their job, use air to play music with these woodwind instruments! Real wood not guaranteed with every item.
components:
@@ -294,8 +294,8 @@
- id: PanFluteInstrument
- type: entity
- id: CrateFunInstrumentsKeyedPercussion
parent: CrateGenericSteel
+ id: CrateFunInstrumentsKeyedPercussion
name: keyed/percussion instrument ensemble crate
description: Hit some keys with some sticks or your hands, with this Keyed and Percussion instrument ensemble crate.
components:
@@ -315,8 +315,8 @@
- id: VibraphoneInstrument
- type: entity
- id: CrateFunInstrumentsSpecial
parent: CrateGenericSteel
+ id: CrateFunInstrumentsSpecial
name: special instrument collector's crate
description: Create some noise with this special collection of arguably-instruments! Centcomm is not responsible for any trauma caused by the contents.
components:
@@ -336,8 +336,8 @@
- id: ReverseCymbalsInstrument
- type: entity
- id: CrateFunInstrumentsRandom
parent: CrateGenericSteel
+ id: CrateFunInstrumentsRandom
name: random instrument collection
description: A box containing several randomly curated instruments, hand picked by Centcomm's top musicians!
components:
@@ -360,8 +360,8 @@
rolls: 2
- type: entity
- id: CrateFunArtSupplies
parent: CrateGenericSteel
+ id: CrateFunArtSupplies
name: art supplies
description: Make some happy little accidents with lots of crayons!
components:
@@ -371,8 +371,8 @@
id: CrayonBox
- type: entity
- id: CrateFunBoardGames
parent: CrateGenericSteel
+ id: CrateFunBoardGames
name: board game crate
description: Game nights have been proven to either decrease boredom or increase murderous rage depending on the game.
components:
@@ -395,8 +395,8 @@
amount: 6
- type: entity
- id: CrateFunSadTromboneImplants
parent: CrateGenericSteel
+ id: CrateFunSadTromboneImplants
name: sad trombone implants
description: Death's never been so fun before! Implant these to make dying a bit more happy.
components:
@@ -407,8 +407,8 @@
amount: 3
- type: entity
- id: CrateFunLightImplants
parent: CrateGenericSteel
+ id: CrateFunLightImplants
name: light implants
description: Light up your skin with these implants!
components:
@@ -419,8 +419,8 @@
amount: 3
- type: entity
- id: CrateFunParty
parent: CrateGenericSteel
+ id: CrateFunParty
name: party crate
description: An entire party just waiting for you to open it. Includes party favors, party beverages, and even a cake.
components:
@@ -452,8 +452,8 @@
amount: 2
- type: entity
- id: CrateFunWaterGuns
parent: CratePlastic
+ id: CrateFunWaterGuns
name: water gun crate
description: A summer special with a variety of brightly colored water guns. Water not included.
components:
@@ -467,8 +467,8 @@
amount: 4
- type: entity
- id: CrateFunFoamGuns
parent: CratePlastic
+ id: CrateFunFoamGuns
name: Foam Force crate
description: Contains four Foam Force rifles, some grenades, and extra ammo. It's [REDACTED] or nothing!
components:
@@ -484,8 +484,8 @@
amount: 4
- type: entity
- id: CrateFunBoxing
parent: CrateGenericSteel
+ id: CrateFunBoxing
name: boxing crate
description: Want to set up an underground fight club or host a tournament amongst station crew? This crate is for you!
components:
@@ -503,8 +503,8 @@
amount: 3
- type: entity
- id: CrateFunPirate
parent: CratePirate
+ id: CrateFunPirate
suffix: Filled
components:
- type: EntityTableContainerFill
@@ -522,8 +522,8 @@
amount: 2
- type: entity
- id: CrateFunToyBox
parent: CrateToyBox
+ id: CrateFunToyBox
suffix: Filled
components:
- type: EntityTableContainerFill
@@ -551,8 +551,8 @@
- id: RubberChicken
- type: entity
- id: CrateFunBikeHornImplants
parent: CrateGenericSteel
+ id: CrateFunBikeHornImplants
name: bike horn implants
description: A thousand honks a day keeps security officers away!
components:
@@ -563,8 +563,8 @@
amount: 3
- type: entity
- id: CrateFunMysteryFigurines
parent: CratePlastic
+ id: CrateFunMysteryFigurines
name: mystery figure crate
description: A collection of 10 Mystery Figurine boxes. Duplicates non refundable.
components:
@@ -579,10 +579,10 @@
prob: 0.05
- type: entity
+ parent: CratePlastic
+ id: CrateFunDartsSet
name: dartboard box set
description: A box with everything you need for a fun game of darts.
- id: CrateFunDartsSet
- parent: CratePlastic
components:
- type: EntityTableContainerFill
containers:
@@ -595,10 +595,10 @@
prob: 0.05
- type: entity
+ parent: CrateLivestock
+ id: CrateFunScurret
name: hydrated scurret
description: Wait, what?
- id: CrateFunScurret
- parent: CrateLivestock
components:
- type: EntityTableContainerFill
containers:
@@ -614,4 +614,3 @@
- id: ClothingHeadHatHardhatYellow
- id: ClothingNeckMantleQM
- id: ClothingHeadsetCargo
-
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/materials.yml b/Resources/Prototypes/Catalog/Fills/Crates/materials.yml
index cb8a690f2b..ebf1a4a584 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/materials.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/materials.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateMaterialGlass
parent: CrateGenericSteel
+ id: CrateMaterialGlass
name: glass sheet crate
description: 90 sheets of glass, packed with care.
components:
@@ -11,8 +11,8 @@
amount: 3
- type: entity
- id: CrateMaterialSteel
parent: CrateGenericSteel
+ id: CrateMaterialSteel
name: steel sheet crate
description: 90 sheets of steel.
components:
@@ -23,8 +23,8 @@
amount: 3
- type: entity
- id: CrateMaterialTextiles
parent: CrateGenericSteel
+ id: CrateMaterialTextiles
name: textiles crate
description: 60 pieces of cloth and 30 pieces of durathread.
components:
@@ -37,8 +37,8 @@
- id: MaterialDurathread
- type: entity
- id: CrateMaterialPlastic
parent: CrateGenericSteel
+ id: CrateMaterialPlastic
name: plastic sheet crate
description: 90 sheets of plastic.
components:
@@ -49,8 +49,8 @@
amount: 3
- type: entity
- id: CrateMaterialWood
parent: CrateGenericSteel
+ id: CrateMaterialWood
name: wood crate
description: Bunch of wood planks.
components:
@@ -60,8 +60,8 @@
id: MaterialWoodPlank
- type: entity
- id: CrateMaterialBrass
parent: CrateGenericSteel
+ id: CrateMaterialBrass
name: brass crate
description: 90 sheets of brass.
components:
@@ -72,8 +72,8 @@
amount: 3
- type: entity
- id: CrateMaterialPlasteel
parent: CrateGenericSteel
+ id: CrateMaterialPlasteel
name: plasteel crate
description: 30 sheets of plasteel.
components:
@@ -83,8 +83,8 @@
id: SheetPlasteel
- type: entity
- id: CrateMaterialPlasma
parent: CratePlasma
+ id: CrateMaterialPlasma
name: solid plasma crate
description: 30 sheets of plasma.
components:
@@ -94,8 +94,8 @@
id: SheetPlasma
- type: entity
- id: CrateMaterialGold
parent: CrateGenericSteel
+ id: CrateMaterialGold
name: gold crate
description: 30 bars of gold.
components:
@@ -105,8 +105,8 @@
id: IngotGold
- type: entity
- id: CrateMaterialSilver
parent: CrateGenericSteel
+ id: CrateMaterialSilver
name: silver crate
description: 30 bars of silver.
components:
@@ -116,8 +116,8 @@
id: IngotSilver
- type: entity
- id: CrateMaterialCardboard
parent: CrateGenericSteel
+ id: CrateMaterialCardboard
name: cardboard crate
description: 90 pieces of cardboard.
components:
@@ -128,8 +128,8 @@
amount: 3
- type: entity
- id: CrateMaterialPaper
parent: CrateGenericSteel
+ id: CrateMaterialPaper
name: paper crate
description: 90 sheets of paper.
components:
@@ -171,8 +171,8 @@
- id: IngotSilver1
- type: entity
- id: CrateMaterialRandom
parent: CrateGenericSteel
+ id: CrateMaterialRandom
name: surplus materials
description: Surplus materials from somewhere.
components:
@@ -184,8 +184,8 @@
range: 2, 4
- type: entity
- id: CrateMaterialSilo
parent: CrateGenericSteel
+ id: CrateMaterialSilo
name: material silo crate
description: A package including all the materials to create a material silo.
components:
@@ -202,8 +202,8 @@
amount: 2
- type: entity
- id: CrateMaterialBasicResource
parent: CrateGenericSteel
+ id: CrateMaterialBasicResource
name: basic sheet crate
description: 30 sheets of steel, glass, and plastic.
components:
@@ -216,9 +216,9 @@
- id: SheetPlastic
#- type: entity
+# parent: CrateGenericSteel
# id: CrateMaterialHFuelTank
# name: fueltank crate
-# parent: CrateGenericSteel
# components:
# - type: EntityTableContainerFill
# containers:
@@ -226,9 +226,9 @@
# id: WeldingFuelTank
#- type: entity
+# parent: CrateGenericSteel
# id: CrateMaterialHWaterTank
# name: watertank crate
-# parent: CrateGenericSteel
# components:
# - type: EntityTableContainerFill
# containers:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/medical.yml b/Resources/Prototypes/Catalog/Fills/Crates/medical.yml
index f14dffc6c5..7c904ff325 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/medical.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/medical.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateMedicalSupplies
parent: CrateMedical
+ id: CrateMedicalSupplies
name: medical supplies crate
description: Basic medical supplies.
components:
@@ -19,8 +19,8 @@
- id: BoxMouthSwab
- type: entity
- id: CrateChemistrySupplies
parent: CrateMedical
+ id: CrateChemistrySupplies
name: chemistry supplies crate
description: Basic chemistry supplies.
components:
@@ -39,8 +39,8 @@
amount: 2
- type: entity
- id: CrateChemistryVials
parent: CrateMedical
+ id: CrateChemistryVials
name: vial supply crate
description: Crate filled with a box of vials.
components:
@@ -50,8 +50,8 @@
id: BoxVial
- type: entity
- id: CrateMindShieldImplants
parent: CrateMedical
+ id: CrateMindShieldImplants
name: MindShield implant crate
description: Crate filled with 3 MindShield implants.
components:
@@ -62,8 +62,8 @@
amount: 3
- type: entity
- id: CrateMedicalSurgery
parent: CrateSurgery
+ id: CrateMedicalSurgery
name: surgical supplies crate
description: Surgical instruments.
components:
@@ -84,8 +84,8 @@
- id: BoxSterileMask
- type: entity
- id: CrateMedicalScrubs
parent: CrateMedical
+ id: CrateMedicalScrubs
name: medical scrubs crate
description: Medical clothings.
components:
@@ -116,8 +116,8 @@
amount: 1
- type: entity
- id: CrateEmergencyBurnKit
parent: CrateMedical
+ id: CrateEmergencyBurnKit
name: emergency burn kit
description: Crate filled with a burn treatment kit.
components:
@@ -127,8 +127,8 @@
id: MedkitBurnFilled
- type: entity
- id: CrateEmergencyToxinKit
parent: CrateMedical
+ id: CrateEmergencyToxinKit
name: emergency toxin kit
description: Crate filled with a toxin treatment kit.
components:
@@ -138,8 +138,8 @@
id: MedkitToxinFilled
- type: entity
- id: CrateEmergencyO2Kit
parent: CrateMedical
+ id: CrateEmergencyO2Kit
name: emergency O2 kit
description: Crate filled with an O2 treatment kit.
components:
@@ -149,8 +149,8 @@
id: MedkitOxygenFilled
- type: entity
- id: CrateEmergencyBruteKit
parent: CrateMedical
+ id: CrateEmergencyBruteKit
name: emergency brute kit
description: Crate filled with a brute treatment kit.
components:
@@ -160,8 +160,8 @@
id: MedkitBruteFilled
- type: entity
- id: CrateEmergencyAdvancedKit
parent: CrateMedical
+ id: CrateEmergencyAdvancedKit
name: emergency advanced kit
description: Crate filled with an advanced treatment kit.
components:
@@ -171,8 +171,8 @@
id: MedkitAdvancedFilled
- type: entity
- id: CrateEmergencyRadiationKit
parent: CrateMedical
+ id: CrateEmergencyRadiationKit
name: emergency radiation kit
description: Crate filled with a radiation treatment kit.
components:
@@ -182,8 +182,8 @@
id: MedkitRadiationFilled
- type: entity
- id: CrateBodyBags
parent: CrateMedical
+ id: CrateBodyBags
name: body bags crate
description: Contains ten body bags.
components:
@@ -194,8 +194,8 @@
amount: 2
- type: entity
- id: CrateVirologyBiosuit
parent: CrateMedicalSecure
+ id: CrateVirologyBiosuit
name: virology bio suit crate
description: Contains 2 biohazard suits to ensure that no disease will distract you from treating the crew. Requires Medical access to open.
components:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/npc.yml b/Resources/Prototypes/Catalog/Fills/Crates/npc.yml
index 625a890d8b..84a58e67ce 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/npc.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/npc.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateNPCBee
parent: CrateLivestock
+ id: CrateNPCBee
name: crate of bees
description: A crate containing a swarm of eight bees.
components:
@@ -11,8 +11,8 @@
amount: 8
- type: entity
- id: CrateNPCButterflies
parent: CrateLivestock
+ id: CrateNPCButterflies
name: crate of butterflies
description: A crate containing five butterflies.
components:
@@ -23,8 +23,8 @@
amount: 5
- type: entity
- id: CrateNPCCat
parent: CrateLivestock
+ id: CrateNPCCat
name: cat crate
description: A crate containing a single cat.
components:
@@ -42,8 +42,8 @@
weight: 0.005
- type: entity
- id: CrateNPCChicken
parent: CrateLivestock
+ id: CrateNPCChicken
name: chicken crate
description: A crate containing four fully grown chickens.
components:
@@ -54,8 +54,8 @@
amount: 4
- type: entity
- id: CrateNPCCrab
parent: CrateLivestock
+ id: CrateNPCCrab
name: crab crate
description: A crate containing three huge crabs.
components:
@@ -66,8 +66,8 @@
amount: 3
- type: entity
- id: CrateNPCDuck
parent: CrateLivestock
+ id: CrateNPCDuck
name: duck crate
description: A crate containing six fully grown ducks.
components:
@@ -81,8 +81,8 @@
amount: 2
- type: entity
- id: CrateNPCCorgi
parent: CrateLivestock
+ id: CrateNPCCorgi
name: corgi crate
description: A crate containing a single corgi.
components:
@@ -92,8 +92,8 @@
id: MobCorgi
- type: entity
- id: CrateNPCPuppyCorgi
parent: CrateLivestock
+ id: CrateNPCPuppyCorgi
name: puppy corgi crate
description: A crate containing a single puppy corgi. Awww.
components:
@@ -103,8 +103,8 @@
id: MobCorgiPuppy
- type: entity
- id: CrateNPCCow
parent: CrateLivestock
+ id: CrateNPCCow
name: cow crate
description: A crate containing a single cow.
components:
@@ -114,8 +114,8 @@
id: MobCow
- type: entity
- id: CrateNPCGoat
parent: CrateLivestock
+ id: CrateNPCGoat
name: goat crate
description: A crate containing a single goat.
components:
@@ -125,8 +125,8 @@
id: MobGoat
- type: entity
- id: CrateNPCGoose
parent: CrateLivestock
+ id: CrateNPCGoose
name: goose crate
description: A crate containing two geese.
components:
@@ -137,8 +137,8 @@
amount: 2
- type: entity
- id: CrateNPCGorilla
parent: CrateLivestock
+ id: CrateNPCGorilla
name: gorilla crate
description: A crate containing a single gorilla.
components:
@@ -148,8 +148,8 @@
id: MobGorilla
- type: entity
- id: CrateNPCMonkeyCube
parent: CrateGenericSteel
+ id: CrateNPCMonkeyCube
name: monkey cube crate
description: A crate containing single box of monkey cubes.
components:
@@ -159,8 +159,8 @@
id: MonkeyCubeBox
- type: entity
- id: CrateNPCKoboldCube
parent: CrateGenericSteel
+ id: CrateNPCKoboldCube
name: kobold cube crate
description: A crate containing single box of kobold cubes.
components:
@@ -170,8 +170,8 @@
id: KoboldCubeBox
- type: entity
- id: CrateNPCMouse
parent: CrateLivestock
+ id: CrateNPCMouse
name: mice crate
description: A crate containing five mice.
components:
@@ -182,8 +182,8 @@
amount: 5
- type: entity
- id: CrateNPCParrot
parent: CrateLivestock
+ id: CrateNPCParrot
name: parrot crate
description: A crate containing three parrots.
components:
@@ -194,8 +194,8 @@
amount: 3
- type: entity
- id: CrateNPCPenguin
parent: CrateLivestock
+ id: CrateNPCPenguin
name: penguin crate
description: A crate containing two penguins.
components:
@@ -206,8 +206,8 @@
amount: 2
- type: entity
- id: CrateNPCPig
parent: CrateLivestock
+ id: CrateNPCPig
name: pig crate
description: A crate containing a single pig.
components:
@@ -217,8 +217,8 @@
id: MobPig
- type: entity
- id: CrateNPCSnake
parent: CrateLivestock
+ id: CrateNPCSnake
name: snake crate
description: A crate containing three snakes.
components:
@@ -229,8 +229,8 @@
amount: 3
- type: entity
- id: CrateNPCHamster
parent: CrateRodentCage
+ id: CrateNPCHamster
suffix: Filled
components:
- type: EntityTableContainerFill
@@ -239,8 +239,8 @@
id: MobHamster
- type: entity
- id: CrateNPCHamlet
parent: CrateRodentCage
+ id: CrateNPCHamlet
suffix: Hamlet
components:
- type: EntityTableContainerFill
@@ -252,8 +252,8 @@
weight: 0.001
- type: entity
- id: CrateNPCLizard
parent: CrateLivestock
+ id: CrateNPCLizard
name: lizard crate
description: A crate containing a lizard.
components:
@@ -263,8 +263,8 @@
id: MobLizard
- type: entity
- id: CrateNPCKangaroo
parent: CrateLivestock
+ id: CrateNPCKangaroo
name: kangaroo crate
description: A crate containing a kangaroo.
components:
@@ -274,8 +274,8 @@
id: MobKangaroo
- type: entity
- id: CrateNPCMothroach
parent: CrateLivestock
+ id: CrateNPCMothroach
name: crate of mothroaches
description: A crate containing four mothroaches.
components:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/permaescape.yml b/Resources/Prototypes/Catalog/Fills/Crates/permaescape.yml
index e81d340bb0..3cf7e72eb0 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/permaescape.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/permaescape.yml
@@ -1,31 +1,31 @@
- type: entity
- name: Perma Escape Crate Spawner
- id: CratePermaEscapeSpawner
parent: CrateEmptySpawner
+ id: CratePermaEscapeSpawner
+ name: Perma Escape Crate Spawner
components:
- type: RandomSpawner
prototypes:
# Please note any duplicates & alphabetize <3
+ - ClosetMaintenanceFilledRandom # x3
+ - ClosetMaintenanceFilledRandom
+ - ClosetMaintenanceFilledRandom
- CrateEngineeringMiniJetpack
- CratePermaEscapeBureaucracy
- - CratePermaEscapeEVA
- - CratePermaEscapeGiftsFromSyndicate
- - CratePermaEscapeGun
- - CratePermaEscapeLights
- - CratePermaEscapeMerc
- - CrateServiceCustomSmokable
- - CrateTrashCartFilled
- CratePermaEscapeComs # x2
- CratePermaEscapeComs
- CratePermaEscapeDigging # x2
- CratePermaEscapeDigging
+ - CratePermaEscapeEVA
+ - CratePermaEscapeGiftsFromSyndicate
+ - CratePermaEscapeGun
+ - CratePermaEscapeLights
- CratePermaEscapeMats #x2
- CratePermaEscapeMats
+ - CratePermaEscapeMerc
- CratePermaEscapeTowercap # x2
- CratePermaEscapeTowercap
- - ClosetMaintenanceFilledRandom # x3
- - ClosetMaintenanceFilledRandom
- - ClosetMaintenanceFilledRandom
+ - CrateTrashCartFilled
+ - CrateServiceCustomSmokable
rarePrototypes:
- MobTick # These need to be killable by one dude with a shovel.
rareChance: .30
@@ -33,8 +33,8 @@
offset: 0.0
- type: entity
- id: CratePermaEscapeDigging
parent: CrateGenericSteel
+ id: CratePermaEscapeDigging
suffix: Digging
components:
- type: EntityTableContainerFill
@@ -58,9 +58,9 @@
prob: 0.05
- type: entity
- id: CratePermaEscapeEVA
parent: CrateGenericSteel
- suffix: EVAs
+ id: CratePermaEscapeEVA
+ suffix: EVA
components:
- type: EntityTableContainerFill
containers:
@@ -88,8 +88,8 @@
prob: 0.05
- type: entity
- id: CratePermaEscapeGun
parent: CrateGenericSteel
+ id: CratePermaEscapeGun
suffix: Gun
components:
- type: EntityTableContainerFill
@@ -112,9 +112,9 @@
weight: 2
- type: entity
- id: CratePermaEscapeBureaucracy
parent: CrateGenericSteel
- suffix: Writing
+ id: CratePermaEscapeBureaucracy
+ suffix: Bureaucracy
components:
- type: EntityTableContainerFill
containers:
@@ -145,8 +145,8 @@
- id: PersonalAI
- type: entity
- id: CratePermaEscapeLights
parent: CrateGenericSteel
+ id: CratePermaEscapeLights
suffix: Glowsticks
components:
- type: EntityTableContainerFill
@@ -185,9 +185,9 @@
prob: 0.05
- type: entity
- id: CratePermaEscapeMats
parent: CrateGenericSteel
- suffix: Mats
+ id: CratePermaEscapeMats
+ suffix: Materials
components:
- type: EntityTableContainerFill
containers:
@@ -198,9 +198,9 @@
- id: PartRodMetal
- type: entity
- id: CratePermaEscapeGiftsFromSyndicate
parent: CrateGenericSteel
- suffix: Syndi Gifts
+ id: CratePermaEscapeGiftsFromSyndicate
+ suffix: Syndicate gifts
components:
- type: EntityTableContainerFill
containers:
@@ -240,9 +240,9 @@
prob: 0.005
- type: entity
- id: CratePermaEscapeMerc
parent: CrateGenericSteel
- suffix: Merc
+ id: CratePermaEscapeMerc
+ suffix: Mercenary
components:
- type: EntityTableContainerFill
containers:
@@ -273,9 +273,9 @@
prob: 0.05
- type: entity
- id: CratePermaEscapeComs
parent: CrateGenericSteel
- suffix: Coms
+ id: CratePermaEscapeComs
+ suffix: Comms
components:
- type: EntityTableContainerFill
containers:
@@ -305,8 +305,8 @@
prob: 0.01
- type: entity
- id: CratePermaEscapeTowercap
parent: CrateGenericSteel
+ id: CratePermaEscapeTowercap
suffix: Towercap
components:
- type: EntityTableContainerFill
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/salvage.yml b/Resources/Prototypes/Catalog/Fills/Crates/salvage.yml
index 8c8e0e02cb..0580d1f46f 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/salvage.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/salvage.yml
@@ -1,9 +1,9 @@
- type: entity
+ parent: CrateGenericSteel
id: CrateSalvageEquipment
name: "salvage equipment crate"
- description: For the daring.
suffix: Filled
- parent: CrateGenericSteel
+ description: For the daring.
components:
- type: EntityTableContainerFill
containers:
@@ -28,10 +28,10 @@
prob: 0.8
- type: entity
- id: CrateSalvageAssortedGoodies
- suffix: Filled, Salvage Random
- categories: [ HideSpawnMenu ] # You should use SalvageMaterialCrateSpawner instead
parent: CrateGenericSteel
+ id: CrateSalvageAssortedGoodies
+ categories: [ HideSpawnMenu ] # You should use SalvageMaterialCrateSpawner instead
+ suffix: Filled, Salvage Random
components:
- type: EntityTableContainerFill
containers:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/science.yml b/Resources/Prototypes/Catalog/Fills/Crates/science.yml
index 1a5bd028ad..ab8cdc90e8 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/science.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/science.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateScienceBiosuit
parent: CrateScienceSecure
+ id: CrateScienceBiosuit
name: scientist bio suit crate
description: Contains 2 biohazard suits to ensure that no disease will distract you from doing science. Requires Science access to open.
components:
@@ -15,8 +15,8 @@
amount: 2
- type: entity
- id: CrateCrewMonitoring
parent: CrateScienceSecure
+ id: CrateCrewMonitoring
name: crew monitoring crate
description: Contains a flatpack of a crew monitoring server and a few crew monitoring computers. Requires Science access to open.
components:
@@ -29,8 +29,8 @@
amount: 3
- type: entity
- id: CrateStationAiCore
parent: CrateScienceSecure
+ id: CrateStationAiCore
name: station AI core crate
description: Contains the components for constructing a station AI core. Positronic brain not included. Requires Science access to open.
components:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/security.yml b/Resources/Prototypes/Catalog/Fills/Crates/security.yml
index 2727cd8799..9c3b695a5d 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/security.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/security.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateSecurityArmor
parent: CrateSecgear
+ id: CrateSecurityArmor
name: armor crate
description: Contains three bulletproof vests. Requires Security access to open.
components:
@@ -11,8 +11,8 @@
amount: 3
- type: entity
- id: CrateSecurityHelmet
parent: CrateSecgear
+ id: CrateSecurityHelmet
name: helmet crate
description: Contains three standard-issue brain buckets. Requires Security access to open.
components:
@@ -23,8 +23,8 @@
amount: 3
- type: entity
- id: CrateSecurityNonlethal
parent: CrateSecgear
+ id: CrateSecurityNonlethal
name: nonlethals crate
description: Contains a mix of disablers, stun batons, and flashes. Requires Security access to open.
components:
@@ -43,8 +43,8 @@
# - GrenadeTeargas
- type: entity
- id: CrateSecuritySupplies
parent: CrateSecgear
+ id: CrateSecuritySupplies
name: security supplies crate
description: Contains various supplies for the station's Security team. Requires Security access to open.
components:
@@ -59,8 +59,8 @@
# - SpacelawBook
- type: entity
- id: CrateRestraints
parent: CrateSecgear
+ id: CrateRestraints
name: restraints crate
description: Contains two boxes each of handcuffs and zipties. Requires Security access to open.
components:
@@ -74,8 +74,8 @@
amount: 2
- type: entity
- id: CrateSecurityBiosuit
parent: CrateSecgear
+ id: CrateSecurityBiosuit
name: security bio suit crate
description: Contains 2 biohazard suits to ensure that no disease will distract you from your duties. Requires Security access to open.
components:
@@ -90,10 +90,10 @@
amount: 2
- type: entity
+ parent: CrateSecgear
id: CrateSecurityTrackingMindshieldImplants
name: implanter crate
description: Contains 4 MindShield implants and 4 tracking implant. Requires Security access to open.
- parent: CrateSecgear
components:
- type: EntityTableContainerFill
containers:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/service.yml b/Resources/Prototypes/Catalog/Fills/Crates/service.yml
index 8a4f04894b..528db232e9 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/service.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/service.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateServiceJanitorialSupplies
parent: CratePlastic
+ id: CrateServiceJanitorialSupplies
name: janitorial supplies crate
description: Fight back against dirt and grime with Nanotrasen's Janitorial Essentials(tm)! Contains three buckets, caution signs, and cleaner grenades. Also has a single mop, broom, spray cleaner, wire brush, rag, and trash bag.
components:
@@ -26,8 +26,8 @@
amount: 2
- type: entity
- id: CrateServiceCleanerGrenades
parent: CratePlastic
+ id: CrateServiceCleanerGrenades
name: bulk cleanades crate
description: Contains two boxes of cleaner grenades, for those deeply-entrenched stains.
components:
@@ -38,10 +38,10 @@
amount: 2
- type: entity
- id: CrateServiceReplacementLights
parent: CrateGenericSteel
+ id: CrateServiceReplacementLights
name: replacement lights crate
- description: May the light of Aether shine upon this station! Or at least, the light of forty two light tubes and twenty one light bulbs.
+ description: May the light of Aether shine upon this station! Or at least, the light of twelve fluorescent light tubes and twelve incandescent light bulbs.
components:
- type: EntityTableContainerFill
containers:
@@ -51,10 +51,23 @@
- id: BoxLightbulb
- type: entity
- id: CrateServiceHolidayLights
parent: CrateGenericSteel
+ id: CrateServiceColorfulLights
+ name: colorful lights crate
+ description: It's not a party until it's hard to see, a little disorienting, and your ears hurt. Contains twelve light tubes and twelve light bulbs in a variety of colors.
+ components:
+ - type: EntityTableContainerFill
+ containers:
+ entity_storage: !type:AllSelector
+ children:
+ - id: BoxLightbulbColorfulMixed
+ - id: BoxLighttubeColorfulMixed
+
+- type: entity
+ parent: CrateGenericSteel
+ id: CrateServiceHolidayLights
name: holiday lights crate
- description: Deck the halls with these festive holiday lights!
+ description: Deck the halls with these festive holiday lights! Contains twelve red light tubes and twelve green light tubes.
components:
- type: EntityTableContainerFill
containers:
@@ -63,8 +76,8 @@
amount: 2
- type: entity
- id: CrateMousetrapBoxes
parent: CrateGenericSteel
+ id: CrateMousetrapBoxes
name: mousetraps crate
description: Mousetraps, for when all of service is being haunted by an entire horde of rats. Use sparingly... or not.
components:
@@ -74,8 +87,8 @@
id: BoxMousetrap
- type: entity
- id: CrateServiceSmokeables
parent: CrateGenericSteel
+ id: CrateServiceSmokeables
name: smokeables crate
description: Tired of a quick death on the station? Order this crate and chain-smoke your way to a coughy demise!
components:
@@ -109,8 +122,8 @@
amount: 2
- type: entity
- id: CrateServiceTheatre
parent: CrateGenericSteel
+ id: CrateServiceTheatre
name: theatrical performances crate
description: Contains a moth cloak, barber scissors, maid uniform, clown and mime attributes, and other performance charms.
components:
@@ -138,8 +151,8 @@
- id: ClothingBeltSuspendersBlack
- type: entity
- id: CrateServiceCustomSmokable
parent: CrateGenericSteel
+ id: CrateServiceCustomSmokable
name: DIY smokeables crate
description: Want to get a little creative with what you use to destroy your lungs? Then this crate is for you! Has everything you need to roll your own cigarettes.
components:
@@ -156,8 +169,8 @@
- id: Matchbox
- type: entity
- id: CrateServiceBureaucracy
parent: CrateGenericSteel
+ id: CrateServiceBureaucracy
name: bureaucracy crate
description: Several stacks of paper, a few pens and an office toy. What more could you ask for?
components:
@@ -207,8 +220,8 @@
# End DeltaV - More folders & colored paper reams in the bureaucracy crate + denied/approved stamp, low chance of paperwork hater stamp
- type: entity
- id: CrateServiceFaxMachine
parent: CrateGenericSteel
+ id: CrateServiceFaxMachine
name: fax machine crate
description: A fax machine and a screwdriver to set the name with.
components:
@@ -220,8 +233,8 @@
- id: FaxMachineFlatpack
- type: entity
- id: CrateServicePersonnel
parent: CrateCommandSecure
+ id: CrateServicePersonnel
name: personnel crate
description: Contains a box of blank ID cards and PDAs.
components:
@@ -233,8 +246,8 @@
- id: BoxID
- type: entity
- id: CrateServiceBooks
parent: CrateGenericSteel
+ id: CrateServiceBooks
name: books crate
description: Contains 10 empty books of random appearance.
components:
@@ -245,8 +258,8 @@
amount: 10
- type: entity
- id: CrateServiceGuidebooks
parent: CrateGenericSteel
+ id: CrateServiceGuidebooks
name: guidebooks crate
description: Contains guidebooks.
components:
@@ -270,8 +283,8 @@
- id: BookSpaceLaw
- type: entity
- id: CrateServiceSodaDispenser
parent: CrateFreezer
+ id: CrateServiceSodaDispenser
name: soda dispenser refill crate
description: Contains refills for soda dispensers.
components:
@@ -299,8 +312,8 @@
- id: DrinkWaterMelonJuiceJug
- type: entity
- id: CrateServiceBoozeDispenser
parent: CrateFreezer
+ id: CrateServiceBoozeDispenser
name: booze dispenser refill crate
description: Contains refills for booze dispensers.
components:
@@ -323,8 +336,8 @@
- id: DrinkJaegermisterBottleFull # Den - Jaegerminster
- type: entity
- id: CrateServiceBox
parent: CratePlastic
+ id: CrateServiceBox
name: boxes crate
description: Contains 6 empty multipurpose boxes.
components:
@@ -335,8 +348,8 @@
amount: 6
- type: entity
- id: CrateJanitorBiosuit
parent: CratePlastic
+ id: CrateJanitorBiosuit
name: janitor bio suit crate
description: Contains 2 biohazard suits to ensure that no disease will distract you from cleaning.
components:
@@ -351,9 +364,9 @@
amount: 2
- type: entity
+ parent: CrateTrashCart
id: CrateTrashCartFilled
suffix: Filled
- parent: CrateTrashCart
components:
- type: EntityTableContainerFill
containers:
@@ -428,8 +441,8 @@
prob: 0.1
- type: entity
- id: CrateCandles
parent: CrateGenericSteel
+ id: CrateCandles
name: candles crate
description: Contains 4 boxes of candles, 2 large and 2 small. For atmosphere or something.
components:
@@ -441,16 +454,3 @@
amount: 2
- id: BoxCandleSmall
amount: 2
-
-- type: entity
- parent: CrateGenericSteel
- id: CrateServiceColorfulLights
- name: colorful lights crate
- description: It's not a party until it's hard to see, a little disorienting, and your ears hurt.
- components:
- - type: EntityTableContainerFill
- containers:
- entity_storage: !type:AllSelector
- children:
- - id: BoxLightbulbColorfulMixed
- - id: BoxLighttubeColorfulMixed
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/syndicate.yml b/Resources/Prototypes/Catalog/Fills/Crates/syndicate.yml
index b51db18fc4..6e4418778a 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/syndicate.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/syndicate.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateSyndicateSurplusBundle
parent: [ CrateSyndicate, StorePresetUplink, BaseSyndicateContraband ]
+ id: CrateSyndicateSurplusBundle
name: Syndicate surplus crate
description: Contains 50 telecrystals worth of completely random Syndicate items. It can be useless junk or really good.
components:
@@ -11,10 +11,10 @@
- Syndicate
- type: entity
- id: CrateCybersunJuggernautBundle
- suffix: Filled
parent: CrateSyndicate
+ id: CrateCybersunJuggernautBundle
name: Cybersun juggernaut bundle
+ suffix: Filled
description: Contains everything except a big gun to go postal.
components:
- type: EntityTableContainerFill
@@ -29,8 +29,8 @@
- Syndicate
- type: entity
- id: CrateSyndicateSuperSurplusBundle
parent: [ CrateSyndicate, StorePresetUplink, BaseSyndicateContraband ]
+ id: CrateSyndicateSuperSurplusBundle
name: Syndicate super surplus crate
description: Contains 125 telecrystals worth of completely random Syndicate items.
components:
diff --git a/Resources/Prototypes/Catalog/Fills/Crates/vending.yml b/Resources/Prototypes/Catalog/Fills/Crates/vending.yml
index 0a68481bbd..047ae5c36f 100644
--- a/Resources/Prototypes/Catalog/Fills/Crates/vending.yml
+++ b/Resources/Prototypes/Catalog/Fills/Crates/vending.yml
@@ -1,6 +1,6 @@
- type: entity
- id: CrateVendingMachineRestockBoozeFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockBoozeFilled
name: Booze-O-Mat restock crate
description: Contains a restock box for the Booze-O-Mat.
components:
@@ -10,8 +10,8 @@
id: VendingMachineRestockBooze
- type: entity
- id: CrateVendingMachineRestockChefvendFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockChefvendFilled
name: ChefVend restock crate
description: Contains a restock box for the ChefVend.
components:
@@ -21,8 +21,8 @@
id: VendingMachineRestockChefvend
- type: entity
- id: CrateVendingMachineRestockClothesFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockClothesFilled
name: clothing restock crate
description: Contains a restock box for the clothes vending machines.
components:
@@ -32,8 +32,8 @@
id: VendingMachineRestockClothes
- type: entity
- id: CrateVendingMachineRestockAutoDrobeFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockAutoDrobeFilled
name: AutoDrobe restock crate
description: Contains a restock box for the AutoDrobe.
components:
@@ -43,8 +43,8 @@
id: VendingMachineRestockCostumes
- type: entity
- id: CrateVendingMachineRestockCondimentStationFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockCondimentStationFilled
name: condiment station restock crate
description: Contains a restock box for the condiment station.
components:
@@ -54,8 +54,8 @@
id: VendingMachineRestockCondimentStation
- type: entity
- id: CrateVendingMachineRestockDinnerwareFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockDinnerwareFilled
name: Plasteel Chef restock crate
description: Contains a restock box for the Plasteel Chef vending machine.
components:
@@ -65,8 +65,8 @@
id: VendingMachineRestockDinnerware
- type: entity
- id: CrateVendingMachineRestockEngineeringFilled
parent: CrateEngineeringSecure
+ id: CrateVendingMachineRestockEngineeringFilled
name: EngiVend restock crate
description: Contains a restock box for the EngiVend. Also supports the YouTool.
components:
@@ -76,8 +76,8 @@
id: VendingMachineRestockEngineering
- type: entity
- id: CrateVendingMachineRestockGamesFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockGamesFilled
name: Good Clean Fun restock crate
description: Contains a restock box for the Good Clean Fun vending machine.
components:
@@ -87,8 +87,8 @@
id: VendingMachineRestockGames
- type: entity
- id: CrateVendingMachineRestockHotDrinksFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockHotDrinksFilled
name: Solar's Best restock crate
description: Contains two restock boxes for Solar's Best Hot Drinks vending machine.
components:
@@ -99,8 +99,8 @@
amount: 2
- type: entity
- id: CrateVendingMachineRestockMedicalFilled
parent: CrateMedicalSecure
+ id: CrateVendingMachineRestockMedicalFilled
name: NanoMed restock crate
description: Contains a restock box, compatible with the NanoMed and NanoMedPlus.
components:
@@ -110,8 +110,8 @@
id: VendingMachineRestockMedical
- type: entity
- id: CrateVendingMachineRestockChemVendFilled
parent: CrateMedicalSecure
+ id: CrateVendingMachineRestockChemVendFilled
name: ChemVend restock crate
description: Contains a restock box for the ChemVend.
components:
@@ -121,8 +121,8 @@
id: VendingMachineRestockChemVend
- type: entity
- id: CrateVendingMachineRestockNutriMaxFilled
parent: CrateHydroSecure
+ id: CrateVendingMachineRestockNutriMaxFilled
name: NutriMax restock crate
description: Contains a restock box for the NutriMax vending machine.
components:
@@ -132,8 +132,8 @@
id: VendingMachineRestockNutriMax
- type: entity
- id: CrateVendingMachineRestockPTechFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockPTechFilled
name: PTech restock crate
description: Contains a restock box for the PTech bureaucracy dispenser.
components:
@@ -143,8 +143,8 @@
id: VendingMachineRestockPTech
- type: entity
- id: CrateVendingMachineRestockRobustSoftdrinksFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockRobustSoftdrinksFilled
name: beverage vendor restock crate
description: Contains restock boxes for beverage vending machines.
components:
@@ -154,11 +154,10 @@
id: VendingMachineRestockRobustSoftdrinks
amount: 2
-
#- type: entity # DeltaV: Salvage vendor doesn't have stock anymore
#- type: entity
-# id: CrateVendingMachineRestockSalvageEquipmentFilled
# parent: CrateGenericSteel
+# id: CrateVendingMachineRestockSalvageEquipmentFilled
# name: Salvage restock crate
# description: Contains a restock box for the salvage vendor.
# components:
@@ -168,8 +167,8 @@
# id: VendingMachineRestockSalvageEquipment
- type: entity
- id: CrateVendingMachineRestockSecTechFilled
parent: CrateSecgear
+ id: CrateVendingMachineRestockSecTechFilled
name: SecTech restock crate
description: Contains a restock box for the SecTech vending machine.
components:
@@ -179,8 +178,8 @@
id: VendingMachineRestockSecTech
- type: entity
- id: CrateVendingMachineRestockSeedsFilled
parent: CrateHydroSecure
+ id: CrateVendingMachineRestockSeedsFilled
name: MegaSeed restock crate
description: Contains a restock box for the MegaSeed vending machine.
components:
@@ -190,8 +189,8 @@
id: VendingMachineRestockSeeds
- type: entity
- id: CrateVendingMachineRestockSmokesFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockSmokesFilled
name: ShadyCigs restock crate
description: Contains two restock boxes for the ShadyCigs vending machine.
components:
@@ -202,8 +201,8 @@
amount: 2
- type: entity
- id: CrateVendingMachineRestockVendomatFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockVendomatFilled
name: Vendomat restock crate
description: Contains a restock box for a Vendomat vending machine.
components:
@@ -213,8 +212,8 @@
id: VendingMachineRestockVendomat
- type: entity
- id: CrateVendingMachineRestockRoboticsFilled
parent: CrateScienceSecure
+ id: CrateVendingMachineRestockRoboticsFilled
name: Robotech Deluxe restock crate
description: Contains a restock box for a Robotech Deluxe vending machine.
components:
@@ -224,8 +223,8 @@
id: VendingMachineRestockRobotics
- type: entity
- id: CrateVendingMachineRestockTankDispenserFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockTankDispenserFilled
name: tank dispenser restock crate
description: Contains a restock box for an Engineering or Atmospherics tank dispenser.
components:
@@ -235,8 +234,8 @@
id: VendingMachineRestockTankDispenser
- type: entity
- id: CrateVendingMachineRestockHappyHonkFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockHappyHonkFilled
name: Happy Honk restock crate
description: Contains a restock box for a happy honk dispenser.
components:
@@ -247,8 +246,8 @@
amount: 2
- type: entity
- id: CrateVendingMachineRestockGetmoreChocolateCorpFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockGetmoreChocolateCorpFilled
name: Getmore Chocolate Corp restock crate
description: Contains a restock box for a Getmore Chocolate Corp dispenser.
components:
@@ -259,8 +258,8 @@
amount: 2
- type: entity
- id: CrateVendingMachineRestockChangFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockChangFilled
name: Chang restock crate
description: Contains a restock box for a Mr. Chang dispenser.
components:
@@ -271,8 +270,8 @@
amount: 2
- type: entity
- id: CrateVendingMachineRestockDiscountDansFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockDiscountDansFilled
name: Discount Dans restock crate
description: Contains a restock box for a Discount Dan's dispenser.
components:
@@ -283,8 +282,8 @@
amount: 2
- type: entity
- id: CrateVendingMachineRestockDonutFilled
parent: CratePlastic
+ id: CrateVendingMachineRestockDonutFilled
name: Donut restock crate
description: Contains a restock box for a Monkin' Donuts dispenser.
components:
From 5bbfd796fd3f4401dc37e89ea118a493ab3aebd0 Mon Sep 17 00:00:00 2001
From: Hitlinemoss <209321380+Hitlinemoss@users.noreply.github.com>
Date: Thu, 4 Dec 2025 03:06:27 -0500
Subject: [PATCH 021/360] figurines.ftl is now sorted by department (#41701)
* figurines.ftl is now sorted by department
* Nukie order fix
* Greytider order fix
* Tweaks to existing lines
* still tweaking
---
Resources/Locale/en-US/datasets/figurines.ftl | 494 +++++++++---------
1 file changed, 259 insertions(+), 235 deletions(-)
diff --git a/Resources/Locale/en-US/datasets/figurines.ftl b/Resources/Locale/en-US/datasets/figurines.ftl
index 2c5d073864..b6428c0560 100644
--- a/Resources/Locale/en-US/datasets/figurines.ftl
+++ b/Resources/Locale/en-US/datasets/figurines.ftl
@@ -1,59 +1,4 @@
-figurines-hop-1 = Papers, please.
-figurines-hop-2 = You are fired.
-figurines-hop-3 = BRB.
-figurines-hop-4 = You can get AA if you fill out the form.
-figurines-hop-5 = I was gone for two seconds...
-
-figurines-passenger-1 = Insuls please.
-figurines-passenger-2 = Call evac.
-figurines-passenger-3 = HELP MAINTS!!
-figurines-passenger-4 = I'm no tider.
-figurines-passenger-5 = How much for a toolbelt?
-
-figurines-greytider-1 = Man, this party stinks. I fucking hate these people.
-figurines-greytider-2 = Uh-oh, who's lost their stunbaton?
-figurines-greytider-3 = Robust.
-figurines-greytider-4 = I'm not me without a toolbox.
-figurines-greytider-5 = Grey tide station wide!
-figurines-greytider-6 = Viva la revolution.
-
-figurines-clown-1 = Honk!
-figurines-clown-2 = Banana!
-figurines-clown-3 = Soap!
-figurines-clown-4 = HoP has one clown, HoS has the whole department.
-figurines-clown-5 = Do I annoy you?
-figurines-clown-6 = Can I have AA? Please?
-figurines-clown-7 = I'm a clown, but you're the whole circus!
-
-figurines-holoclown-1 = I'm helping my older brother.
-figurines-holoclown-2 = Hello, officer!
-figurines-holoclown-3 = Who are you calling blue?
-figurines-holoclown-4 = Bleeding on the ground is a good look for you.
-figurines-holoclown-5 = Pathetic.
-figurines-holoclown-6 = It's not them you need to worry about; it's me.
-figurines-holoclown-7 = What's so funny?
-
-figurines-mime-1 = ...
-figurines-mime-2 = ...
-figurines-mime-3 = ....
-figurines-mime-4 = .......
-figurines-mime-5 = ................
-figurines-mime-6 = ...........?
-figurines-mime-7 = !!!
-figurines-mime-8 = ....!
-figurines-mime-9 = ???
-
-figurines-musician-1 = Never gonna give you up!
-figurines-musician-2 = Never gonna let you down!
-figurines-musician-3 = Music is an art.
-figurines-musician-4 = Thank you, I'll be here all night.
-figurines-musician-5 = I'm a one man band.
-
-figurines-boxer-1 = The first rule of Fight Club is...
-figurines-boxer-2 = We settle this in the ring, alright?
-figurines-boxer-3 = I. AM. THE. CHAMPION!!
-figurines-boxer-4 = Don't look at me; he was shot, not punched.
-figurines-boxer-5 = 1v1 me, captain.
+# Command
figurines-captain-1 = Glory to NT!
figurines-captain-2 = How did I get hired? Yes.
@@ -64,19 +9,108 @@ figurines-captain-6 = The disk was in my bag last I checked.
figurines-captain-7 = The chain of command starts and ends with me.
figurines-captain-8 = It's hard being at the top.
+# Cargo
+
+figurines-qm-1 = Who stole the shuttle?
+figurines-qm-2 = Wait, where did my digi-board go?
+figurines-qm-3 = I didn't approve that shipment of guns!
+figurines-qm-4 = One toy box for my fellow clown!
+figurines-qm-5 = Time to gamble!
+figurines-qm-6 = Viva la Cargonia!
+figurines-qm-7 = Fill out the form.
+figurines-qm-8 = Where'd all our money go?
+figurines-qm-9 = 99% of gamblers quit right before they hit it big!
+
+figurines-cargotech-1 = DRAGON ON ATS!
+figurines-cargotech-2 = I sold the station!
+figurines-cargotech-3 = Brain bounty? I don't have a brain.
+figurines-cargotech-4 = You're worth 3000 spesos. Congrats.
+figurines-cargotech-5 = Vegetable bounty? Nobody eats those anyways.
+figurines-cargotech-6 = WE ARE SECEDING!! ALL HAIL CARGONIA!!
+
+figurines-salvage-1 = Megafauna? It was mega easy.
+figurines-salvage-2 = We're lost. Anyone bring a GPS?
+figurines-salvage-3 = Anyone have oxygen?
+figurines-salvage-4 = I found a blood-red and e-sword!
+figurines-salvage-5 = There's bears in space?
+figurines-salvage-6 = Crusher? I barely know her!
+
+# Engineering
+
+figurines-ce-1 = Everyone to the briefing!
+figurines-ce-2 = Wire the solars!
+figurines-ce-3 = How to setup the TEG?
+figurines-ce-4 = SINGULOOSE!
+figurines-ce-5 = TESLOOSE!
+figurines-ce-6 = Power's out again.
+
+figurines-atmostech-1 = I put plasma in distro.
+figurines-atmostech-2 = I will burn you in a burn chamber.
+figurines-atmostech-3 = Frezon...
+figurines-atmostech-4 = Tritium...
+figurines-atmostech-5 = Glory to Atmosia!
+figurines-atmostech-6 = Distro? That's short for disposal.
+figurines-atmostech-7 = TEG: Thermal Energy? Gone!
+
+figurines-engineer-1 = SINGULOOSE!
+figurines-engineer-2 = TESLOOSE!
+figurines-engineer-3 = Did anyone remember to turn the AME on?
+figurines-engineer-4 = Free insuls at Engineering!
+figurines-engineer-5 = Where'd the power go?
+figurines-engineer-6 = Someone bombed the medbay... again...
+figurines-engineer-7 = Well, why don't you come and fix it?
+
+# Medical
+
+figurines-cmo-1 = Suit sensors!
+figurines-cmo-2 = Why do we have meth?
+figurines-cmo-3 = Who drank all the chems?
+figurines-cmo-4 = Desoxyephedrine? Sounds healthy.
+figurines-cmo-5 = No, you're not getting my hypospray.
+
+figurines-chemist-1 = Get your pills!
+figurines-chemist-2 = We need to cook.
+figurines-chemist-3 = I am the one who knocks!
+figurines-chemist-4 = Say my name.
+figurines-chemist-5 = 99.8% purity.
+figurines-chemist-6 = Epinephrine? Didn't you say methamphetamine?
+
+figurines-doctor-1 = The patient is already dead!
+figurines-doctor-2 = CLEAR!
+figurines-doctor-3 = Saw makes BRRR.
+figurines-doctor-4 = Just a week away...
+figurines-doctor-5 = I knew it...
+
+figurines-paramedic-1 = Insuls and tools!
+figurines-paramedic-2 = I need AA for saving people!
+figurines-paramedic-3 = SUIT SENSORS!!
+figurines-paramedic-4 = I need the hypospray for saving people!
+figurines-paramedic-5 = 14 dead in the clown's room.
+
+# Science
+
+figurines-rd-1 = Blowing up all of the borgs!
+figurines-rd-2 = Tier 3 Arsenal? No way.
+figurines-rd-3 = Now where did I leave my hardsuit...?
+figurines-rd-4 = Now you're thinking with portals!
+figurines-rd-5 = The cake is a lie!
+figurines-rd-6 = The trait I look for in a scientist is expendability.
+
+figurines-scientist-1 = Someone else must have made those bombs!
+figurines-scientist-2 = He asked to be borged!
+figurines-scientist-3 = Carp at sci!
+figurines-scientist-4 = Explosion at sci!
+figurines-scientist-5 = Anyone seen an anomaly?
+figurines-scientist-6 = The anomaly exploded!
+
+# Security
+
figurines-hos-1 = Space law? What?
figurines-hos-2 = Shoot the clown.
figurines-hos-3 = Yes, I shot the clown. No, I don't regret it.
figurines-hos-4 = Clown is now KOS.
figurines-hos-5 = Armory is now open to the public!
-figurines-warden-1 = Execute him for breaking in!
-figurines-warden-2 = Perma the fucker for insulting me!
-figurines-warden-3 = We totally treat everyone fairly and do NOT mistreat our prisoners.
-figurines-warden-4 = Brig is my home. My home is brig. My brig is home. Stop, what?
-figurines-warden-5 = Soap is now contraband.
-figurines-warden-6 = You're going away for a long time, buddy.
-
figurines-detective-1 = The butler did it.
figurines-detective-2 = I need some whiskey after this.
figurines-detective-3 = Chameleon fibers? How did a chameleon get in here?
@@ -94,98 +128,86 @@ figurines-security-8 = I love donuts.
figurines-security-9 = Greytide this, motherfucker.
figurines-security-10 = Do not resist.
+figurines-warden-1 = Execute him for breaking in!
+figurines-warden-2 = Perma the fucker for insulting me!
+figurines-warden-3 = We totally treat everyone fairly and do NOT mistreat our prisoners.
+figurines-warden-4 = Brig is my home. My home is brig. My brig is home. Stop, what?
+figurines-warden-5 = Soap is now contraband.
+figurines-warden-6 = You're going away for a long time, buddy.
+
+# Service
+
+figurines-hop-1 = Papers, please.
+figurines-hop-2 = You are fired.
+figurines-hop-3 = BRB.
+figurines-hop-4 = You can get AA if you fill out the form.
+figurines-hop-5 = I was gone for two seconds...
+
+figurines-bartender-1 = Where's my monkey?
+figurines-bartender-2 = Sec won't drink.
+figurines-bartender-3 = I mixed a little something in there...
+figurines-bartender-4 = The recipe? Plasma and vomit. Why?
+figurines-bartender-5 = I need those toxins for my drinks, officer!
+figurines-bartender-6 = Read the room.
+figurines-bartender-7 = I've got a shotgun.
+
+figurines-botanist-1 = I don't have any weed, officer!
+figurines-botanist-2 = Dude, I see colors...
+figurines-botanist-3 = Is it just me, or is that weed glowing?
+figurines-botanist-4 = 50 more units of mutagen. That should be enough.
+figurines-botanist-5 = More bananas for my favorite clown!
+
+figurines-boxer-1 = The first rule of Fight Club is...
+figurines-boxer-2 = We settle this in the ring, alright?
+figurines-boxer-3 = I. AM. THE. CHAMPION!!
+figurines-boxer-4 = Don't look at me; he was shot, not punched.
+figurines-boxer-5 = 1v1 me, captain.
+
+figurines-chaplain-1 = Would you like to join my cul- I mean religion.
+figurines-chaplain-2 = Gods, please make me a killing machine!
+figurines-chaplain-3 = God exists!
+figurines-chaplain-4 = Those aren't blood runes, I drew them in crayon.
+figurines-chaplain-5 = Anyone want to be sacrificed?
+figurines-chaplain-6 = Vampires aren't real.
+
+figurines-chef-1 = I swear it's not human meat.
+figurines-chef-2 = More banana cream pies?
+figurines-chef-3 = How does rotary sushi sound?
+figurines-chef-4 = That'll be 1,000 spesos.
+figurines-chef-5 = For here or to go?
+figurines-chef-6 = Where'd Pun Pun go? No idea...
+
+figurines-clown-1 = Honk!
+figurines-clown-2 = Banana!
+figurines-clown-3 = Soap!
+figurines-clown-4 = HoP has one clown, HoS has the whole department.
+figurines-clown-5 = Do I annoy you?
+figurines-clown-6 = Can I have AA? Please?
+figurines-clown-7 = I'm a clown, but you're the whole circus!
+
+figurines-greytider-1 = Man, this party stinks. I fucking hate these people.
+figurines-greytider-2 = Uh-oh, who's lost their stunbaton?
+figurines-greytider-3 = Robust.
+figurines-greytider-4 = I'm not me without a toolbox.
+figurines-greytider-5 = Grey tide station wide!
+figurines-greytider-6 = Viva la revolution.
+
+figurines-janitor-1 = Clown stole my soap. Again.
+figurines-janitor-2 = Look at the signs, you idiot.
+figurines-janitor-3 = I've never seen this much lube in my life.
+figurines-janitor-4 = Another day, another spill.
+figurines-janitor-5 = I'm not even paid for this.
+figurines-janitor-6 = This blood wasn't evidence, right?
+figurines-janitor-7 = My only friend is my mop.
+figurines-janitor-8 = That better not be what I think it is...
+figurines-janitor-9 = Another day, another body.
+
figurines-lawyer-1 = Better Call Saul!
figurines-lawyer-2 = Objection!
figurines-lawyer-3 = Did you know that you have rights?
figurines-lawyer-4 = Space law says!
figurines-lawyer-5 = Sign the contract first.
-figurines-cargotech-1 = DRAGON ON ATS!
-figurines-cargotech-2 = I sold the station!
-figurines-cargotech-3 = Brain bounty? I don't have a brain.
-figurines-cargotech-4 = You're worth 3000 spesos. Congrats.
-figurines-cargotech-5 = Vegetable bounty? Nobody eats those anyways.
-figurines-cargotech-6 = WE ARE SECEDING!! ALL HAIL CARGONIA!!
-
-figurines-salvage-1 = Megafauna? It was mega easy.
-figurines-salvage-2 = We're lost. Anyone bring a GPS?
-figurines-salvage-3 = Anyone have oxygen?
-figurines-salvage-4 = I found a blood-red and e-sword!
-figurines-salvage-5 = There's bears in space?
-figurines-salvage-6 = Crusher? I barely know her!
-
-figurines-qm-1 = Who stole the shuttle?
-figurines-qm-2 = I won't approve the guns.
-figurines-qm-3 = I didn't buy those guns!
-figurines-qm-4 = One toys crate for ma fellow clown!
-figurines-qm-5 = Time to spent all money on gambling.
-figurines-qm-6 = Viva La Cargonia!
-figurines-qm-7 = Fill the form.
-figurines-qm-8 = Where'd all our money go?
-figurines-qm-9 = 99% of gamblers quit right before they hit it big!
-
-figurines-ce-1 = Everyone to the briefing!
-figurines-ce-2 = Wire the solars!
-figurines-ce-3 = How to setup the TEG?
-figurines-ce-4 = SINGULOOSE!
-figurines-ce-5 = TESLOOSE!
-figurines-ce-6 = Power's out again.
-
-figurines-engineer-1 = SINGULOOSE!
-figurines-engineer-2 = TESLOOSE!
-figurines-engineer-3 = What is AME?
-figurines-engineer-4 = Free insuls at Engineering!
-figurines-engineer-5 = Where'd the power go?
-figurines-engineer-6 = Someone bombed the medbay... again...
-figurines-engineer-7 = Well, why don't you come and fix it?
-
-figurines-atmostech-1 = I put plasma in distro.
-figurines-atmostech-2 = I will burn you in a burn chamber.
-figurines-atmostech-3 = Frezon...
-figurines-atmostech-4 = Tritium...
-figurines-atmostech-5 = Glory to Atmosia!
-figurines-atmostech-6 = Distro? That's short for disposal.
-figurines-atmostech-7 = TEG: Thermal Energy? Gone!
-
-figurines-rd-1 = Blowing up all of the borgs!
-figurines-rd-2 = Tier 3 Arsenal? No way.
-figurines-rd-3 = Now where did I leave my hardsuit...?
-figurines-rd-4 = Now you're thinking with portals!
-figurines-rd-5 = The cake is a lie!
-figurines-rd-6 = The trait I look for in a scientist is expendability.
-
-figurines-scientist-1 = Someone else must have made those bombs!
-figurines-scientist-2 = He asked to be borged!
-figurines-scientist-3 = Carp at sci!
-figurines-scientist-4 = Explosion at sci!
-figurines-scientist-5 = Anyone seen an anomaly?
-figurines-scientist-6 = The anomaly exploded!
-
-figurines-cmo-1 = Suit sensors!
-figurines-cmo-2 = Why do we have meth?
-figurines-cmo-3 = Who drank all the chems?
-figurines-cmo-4 = Desoxyephedrine? Sounds healthy.
-figurines-cmo-5 = No, you're not getting my hypospray.
-
-figurines-chemist-1 = Get your pills!
-figurines-chemist-2 = We need to cook.
-figurines-chemist-3 = I am the one who knocks!
-figurines-chemist-4 = Say my name.
-figurines-chemist-5 = 99.8% purity.
-figurines-chemist-6 = Epinephrine? Didn't you say methamphetamine?
-
-figurines-paramedic-1 = Insuls and tools!
-figurines-paramedic-2 = I need AA for saving people!
-figurines-paramedic-3 = SUIT SENSORS!!
-figurines-paramedic-4 = I need the hypospray for saving people!
-figurines-paramedic-5 = 14 dead in the clown's room.
-
-figurines-doctor-1 = The patient is already dead!
-figurines-doctor-2 = CLEAR!
-figurines-doctor-3 = Saw makes BRRR.
-figurines-doctor-4 = Just a week away...
-figurines-doctor-5 = I knew it...
-
figurines-librarian-1 = Silence!
figurines-librarian-2 = One day while...
figurines-librarian-3 = Once upon a time...
@@ -201,62 +223,33 @@ figurines-librarian-12 = Gather round...
figurines-librarian-13 = ...It's a tale as old as time...
figurines-librarian-14 = ...That's all she wrote.
-figurines-chaplain-1 = Would you like to join my cul- I mean religion.
-figurines-chaplain-2 = Gods make me a killing machine please!
-figurines-chaplain-3 = God exists!
-figurines-chaplain-4 = Those aren't blood runes, I drew them in crayon.
-figurines-chaplain-5 = Anyone want to be sacrificed?
-figurines-chaplain-6 = Vampires aren't real.
+figurines-mime-1 = ...
+figurines-mime-2 = ...
+figurines-mime-3 = ....
+figurines-mime-4 = .......
+figurines-mime-5 = ................
+figurines-mime-6 = ...........?
+figurines-mime-7 = !!!
+figurines-mime-8 = ....!
+figurines-mime-9 = ???
-figurines-chef-1 = I swear it's not human meat.
-figurines-chef-2 = More banana cream pies?
-figurines-chef-3 = How does rotary sushi sound?
-figurines-chef-4 = That'll be 1000 spesos
-figurines-chef-5 = For here or to go?
-figurines-chef-6 = Where'd Pun Pun go? No idea...
+figurines-musician-1 = Never gonna give you up!
+figurines-musician-2 = Never gonna let you down!
+figurines-musician-3 = Music is an art.
+figurines-musician-4 = Thank you, I'll be here all night.
+figurines-musician-5 = I'm a one man band.
-figurines-bartender-1 = Where's my monkey?
-figurines-bartender-2 = Sec won't drink.
-figurines-bartender-3 = I mixed a little something in there...
-figurines-bartender-4 = The recipe? Plasma and vomit. Why?
-figurines-bartender-5 = I need those toxins for my drinks, officer!
-figurines-bartender-6 = Read the room.
-figurines-bartender-7 = I've got a shotgun.
+figurines-passenger-1 = Insuls please.
+figurines-passenger-2 = Call evac.
+figurines-passenger-3 = HELP MAINTS!!
+figurines-passenger-4 = I'm no tider.
+figurines-passenger-5 = How much for a toolbelt?
-figurines-botanist-1 = I don't have any weed, officer!
-figurines-botanist-2 = Dude, I see colors...
-figurines-botanist-3 = Is it just me, or is that weed glowing?
-figurines-botanist-4 = 50 more units of mutagen. That should be enough.
-figurines-botanist-5 = More bananas for my favorite clown!
+# Silicon
-figurines-janitor-1 = Clown stole my soap. Again.
-figurines-janitor-2 = Look at the signs, you idiot.
-figurines-janitor-3 = I've never seen this much lube in my life.
-figurines-janitor-4 = Another day, another spill.
-figurines-janitor-5 = I'm not even paid for this.
-figurines-janitor-6 = This blood wasn't evidence, right?
-figurines-janitor-7 = My only friend is my mop.
-figurines-janitor-8 = That better not be what I think it is...
-figurines-janitor-9 = Another day, another body.
+# ...no voiced silicon figures at the moment.
-figurines-nukie-1 = I got the disk!
-figurines-nukie-2 = Whiskey, Echo, Whiskey.
-figurines-nukie-3 = The nuke makes boom.
-figurines-nukie-4 = What's the code?
-figurines-nukie-5 = Commander...? ...That's a balloon...
-
-figurines-nukie-elite-1 = Not a word in Nanotrasen.
-figurines-nukie-elite-2 = THAT'S A KEG!
-figurines-nukie-elite-3 = Guys, are you alive?
-figurines-nukie-elite-4 = Breach and clear!
-figurines-nukie-elite-5 = Leave no survivors.
-figurines-nukie-elite-6 = Good work, team.
-
-figurines-nukie-commander-1 = GET DAT FUKKEN DISK!
-figurines-nukie-commander-2 = Fuckin' flukies.
-figurines-nukie-commander-3 = The Syndicate sends its regards.
-figurines-nukie-commander-4 = Failure is not an option.
-figurines-nukie-commander-5 = Whoops.
+# Antagonists
figurines-footsoldier-1 = I'm an evil boy. Less boy every day, more evil every day.
figurines-footsoldier-2 = Who will you choose? Them or us? Us or them?
@@ -264,16 +257,32 @@ figurines-footsoldier-3 = Glory to the Syndicate!
figurines-footsoldier-4 = Down with Nanotrasen!
figurines-footsoldier-5 = I'd rather die than join Nanotrasen.
-figurines-wizard-1 = Ei Nath!!
-figurines-wizard-2 = Real wizards support trans rights.
-figurines-wizard-3 = Skidaddle skadoodle!
-figurines-wizard-4 = FIREBALL!
+figurines-holoclown-1 = I'm helping my older brother.
+figurines-holoclown-2 = Hello, officer!
+figurines-holoclown-3 = Who are you calling blue?
+figurines-holoclown-4 = Bleeding on the ground is a good look for you.
+figurines-holoclown-5 = Pathetic.
+figurines-holoclown-6 = It's not them you need to worry about; it's me.
+figurines-holoclown-7 = What's so funny?
-figurines-space-dragon-1 = Fish will consume the station.
-figurines-space-dragon-2 = Dragon de- Actually, nevermind.
-figurines-space-dragon-3 = Crew is delicious.
-figurines-space-dragon-4 = Don't you dare make sushi.
-figurines-space-dragon-5 = This station ain't big enough for the two of us.
+figurines-nukie-1 = I got the disk!
+figurines-nukie-2 = Whiskey, Echo, Whiskey.
+figurines-nukie-3 = The nuke makes boom.
+figurines-nukie-4 = What's the code?
+figurines-nukie-5 = Commander...? ...That's a balloon...
+
+figurines-nukie-commander-1 = GET DAT FUKKEN DISK!
+figurines-nukie-commander-2 = Fuckin' flukies.
+figurines-nukie-commander-3 = The Syndicate sends its regards.
+figurines-nukie-commander-4 = Failure is not an option.
+figurines-nukie-commander-5 = Whoops.
+
+figurines-nukie-elite-1 = Not a word in Nanotrasen.
+figurines-nukie-elite-2 = THAT'S A KEG!
+figurines-nukie-elite-3 = Guys, are you alive?
+figurines-nukie-elite-4 = Breach and clear!
+figurines-nukie-elite-5 = Leave no survivors.
+figurines-nukie-elite-6 = Good work, team.
figurines-queen-1 = Our domain must grow.
figurines-queen-2 = The hive hungers.
@@ -295,6 +304,40 @@ figurines-rat-servant-3 = The boss wants a word with youse.
figurines-rat-servant-4 = Ay, I'm walkin' here!
figurines-rat-servant-5 = You get the chedda', then we talk.
+figurines-space-dragon-1 = Fish will consume the station.
+figurines-space-dragon-2 = Dragon de- Actually, nevermind.
+figurines-space-dragon-3 = Crew is delicious.
+figurines-space-dragon-4 = Don't you dare make sushi.
+figurines-space-dragon-5 = This station ain't big enough for the two of us.
+
+figurines-skeleton-1 = ACK ACK!
+figurines-skeleton-2 = Ugh, that locker was cramped!
+figurines-skeleton-3 = You're going to have a bad time.
+figurines-skeleton-4 = Got any milk?
+figurines-skeleton-5 = I have a bone to pick with you!
+
+figurines-thief-1 = You don't have a warrant!
+figurines-thief-2 = This is just a normal beacon!
+figurines-thief-3 = Theres nothing suspicious about this satchel at all, officer.
+figurines-thief-4 = I have NO idea where your pet is...
+figurines-thief-5 = Huh, I didn't know that wall could open up...
+
+figurines-wizard-1 = Ei Nath!!
+figurines-wizard-2 = Real wizards support trans rights.
+figurines-wizard-3 = Skidaddle skadoodle!
+figurines-wizard-4 = FIREBALL!
+
+# Animals
+
+figurines-hamlet-1 = Piep!
+figurines-hamlet-2 = Squeak!
+figurines-hamlet-3 = Chuu!
+figurines-hamlet-4 = Eeee!
+figurines-hamlet-5 = Pip!
+figurines-hamlet-6 = Fwiep!
+figurines-hamlet-7 = Heep!
+figurines-hamlet-8 = NOT THE MICROWAVE!
+
figurines-mouse-1 = Piep!
figurines-mouse-2 = Squeak!
figurines-mouse-3 = Chuu!
@@ -308,35 +351,16 @@ figurines-slime-2 = Blimpuf?
figurines-slime-3 = Blump!
figurines-slime-4 = Squish!
-figurines-hamlet-1 = Piep!
-figurines-hamlet-2 = Squeak!
-figurines-hamlet-3 = Chuu!
-figurines-hamlet-4 = Eeee!
-figurines-hamlet-5 = Pip!
-figurines-hamlet-6 = Fwiep!
-figurines-hamlet-7 = Heep!
-figurines-hamlet-8 = NOT THE MICROWAVE!
-
-figurines-thief-1 = You don't have a warrant!
-figurines-thief-2 = This is just a normal beacon!
-figurines-thief-3 = Theres nothing suspicious about this satchel at all, officer.
-figurines-thief-4 = I have NO idea where your pet is...
-figurines-thief-5 = Huh, I didn't know that wall could open up...
-
-figurines-skeleton-1 = ACK ACK!
-figurines-skeleton-2 = Ugh, that locker was cramped!
-figurines-skeleton-3 = You're going to have a bad time.
-figurines-skeleton-4 = Got any milk?
-figurines-skeleton-5 = I have a bone to pick with you!
-
-figurines-owlman-1 = No need to fear, Owlman is here!
-figurines-owlman-2 = Owl be seeing you later!
-figurines-owlman-3 = HOOT!!
-figurines-owlman-4 = What do you call an owl magician, HOO-DINI!
-figurines-owlman-5 = Don't worry citizen, I'll save the day!
+# Other
figurines-griffin-1 = MUHAHAHAHA, I am so evil!!
figurines-griffin-2 = The second I see Owlman, they are so dead!!
figurines-griffin-3 = How do us Griffins deal with stress? We wing it!
figurines-griffin-4 = My name isn't Gilda!!
figurines-griffin-5 = Being a criminal mastermind, isn't easy.
+
+figurines-owlman-1 = No need to fear, Owlman is here!
+figurines-owlman-2 = Owl be seeing you later!
+figurines-owlman-3 = HOOT!!
+figurines-owlman-4 = What do you call an owl magician, HOO-DINI!
+figurines-owlman-5 = Don't worry citizen, I'll save the day!
From 0c4c101cb512ff59138ac2063265feac2f6f75a2 Mon Sep 17 00:00:00 2001
From: Minerva <218184747+mnva0@users.noreply.github.com>
Date: Thu, 4 Dec 2025 11:55:03 -0500
Subject: [PATCH 022/360] Allows spesos to fit in envelopes (#41700)
Co-authored-by: ScarKy0
---
Resources/Prototypes/Entities/Objects/Misc/paper.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Resources/Prototypes/Entities/Objects/Misc/paper.yml b/Resources/Prototypes/Entities/Objects/Misc/paper.yml
index 89deedf812..890642ad0d 100644
--- a/Resources/Prototypes/Entities/Objects/Misc/paper.yml
+++ b/Resources/Prototypes/Entities/Objects/Misc/paper.yml
@@ -421,6 +421,8 @@
insertSound: /Audio/Effects/packetrip.ogg
ejectSound: /Audio/Effects/packetrip.ogg
whitelist:
+ components:
+ - Cash
tags:
- Paper
- type: ActivatableUI
From 5d496b92d92d86b0e29c988025244de10ef0a123 Mon Sep 17 00:00:00 2001
From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com>
Date: Thu, 4 Dec 2025 18:52:56 +0100
Subject: [PATCH 023/360] Change Ephedrine, Desoxyephedrine and Hyperzine
properties (#41693)
* merff (no numbness)
* I can't feel a thing
* Update self-damage and OD values
* Missed a 5
---
.../Locale/en-US/store/uplink-catalog.ftl | 4 +-
.../Entities/StatusEffects/body.yml | 4 +
Resources/Prototypes/Reagents/narcotics.yml | 172 ++++++++++--------
3 files changed, 106 insertions(+), 74 deletions(-)
diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl
index a3a68132f2..736e1e21f3 100644
--- a/Resources/Locale/en-US/store/uplink-catalog.ftl
+++ b/Resources/Locale/en-US/store/uplink-catalog.ftl
@@ -405,10 +405,10 @@ uplink-nocturine-chemistry-bottle-name = Nocturine Bottle
uplink-nocturine-chemistry-bottle-desc = A chemical that puts your target straight to sleep.
uplink-stimpack-name = Hyperzine Injector
-uplink-stimpack-desc = The legendary chemical produced by Donk Co. for the Syndicate. Injecting yourself with this will increase your run speed and let you recover from stuns faster for 30 seconds.
+uplink-stimpack-desc = The legendary chemical produced by Donk Co. for the Syndicate. Injecting yourself with this will increase your run speed and let you recover from stuns faster for 60 seconds.
uplink-stimkit-name = Hyperzine Injector Kit
-uplink-stimkit-desc = A medkit containing 6 hyperzine microinjectors, which each inject you with enough hyperzine to last for 15 seconds.
+uplink-stimkit-desc = A medkit containing 6 hyperzine microinjectors, which each inject you with enough hyperzine to last for 30 seconds.
uplink-syndicate-segway-crate-name = Syndicate Segway
uplink-syndicate-segway-crate-desc = Be an enemy of the corporation, in style!
diff --git a/Resources/Prototypes/Entities/StatusEffects/body.yml b/Resources/Prototypes/Entities/StatusEffects/body.yml
index 4c94804884..6b6f704a2c 100644
--- a/Resources/Prototypes/Entities/StatusEffects/body.yml
+++ b/Resources/Prototypes/Entities/StatusEffects/body.yml
@@ -27,3 +27,7 @@
- MobState
- MobThresholds
- type: PainNumbnessStatusEffect
+
+- type: entity
+ parent: PainNumbnessTraitStatusEffect
+ id: StatusEffectPainNumbness
diff --git a/Resources/Prototypes/Reagents/narcotics.yml b/Resources/Prototypes/Reagents/narcotics.yml
index c290ac0a24..bb025f4c24 100644
--- a/Resources/Prototypes/Reagents/narcotics.yml
+++ b/Resources/Prototypes/Reagents/narcotics.yml
@@ -4,44 +4,54 @@
group: Narcotics
desc: reagent-desc-desoxyephedrine
physicalDesc: reagent-physical-desc-translucent
- contrabandSeverity: Major
+ contrabandSeverity: Minor
flavor: bitter
color: "#FAFAFA"
boilingPoint: 212.0 # Dexosyephedrine vape when?
meltingPoint: 170.0
metabolisms:
Poison:
+ # Main effects
effects:
- !type:HealthChange
damage:
types:
- Poison: 0.75
+ Poison: 0.55
+ Blunt: 0.5
Cellular: 1 # DeltaV - meth rots you just like in real life :D
+ # OD
- !type:HealthChange
conditions:
- !type:ReagentCondition
reagent: Desoxyephedrine
- min: 30
+ min: 16
damage:
types:
- Poison: 2 # this is added to the base damage of the meth.
+ Poison: 3 # this is added to the base damage of the meth.
Asphyxiation: 2
Narcotic:
effects:
+ # Main effects
- !type:MovementSpeedModifier
- walkSpeedModifier: 1.35
- sprintSpeedModifier: 1.35
+ walkSpeedModifier: 1.20
+ sprintSpeedModifier: 1.20
+ - !type:GenericStatusEffect
+ key: StaminaModifier # You are on meth. You keep going.
+ component: StaminaModifier
+ time: 3
+ - !type:GenericStatusEffect
+ key: Adrenaline
+ component: IgnoreSlowOnDamage
+ time: 3
+ # Side effects
+ - !type:Jitter
- !type:GenericStatusEffect
key: Stutter
component: StutteringAccent
- - !type:Jitter
- !type:ModifyStatusEffect
- effectProto: StatusEffectStunned
- time: 3
- type: Remove
- - !type:ModifyKnockdown
- time: 3
- type: Remove
+ effectProto: StatusEffectPainNumbness
+ time: 2
+ # Interactions
- !type:ModifyStatusEffect
conditions:
- !type:ReagentCondition
@@ -59,11 +69,12 @@
- !type:SuppressAddiction
Medicine:
effects:
+ # Side effects
- !type:ResetNarcolepsy
conditions:
- !type:ReagentCondition
reagent: Desoxyephedrine
- min: 20
+ min: 5
- type: reagent
id: Ephedrine
@@ -71,7 +82,6 @@
group: Narcotics
desc: reagent-desc-ephedrine
physicalDesc: reagent-physical-desc-powdery
- contrabandSeverity: Minor
flavor: bitter
color: "#D2FFFA"
boilingPoint: 255.0
@@ -79,19 +89,10 @@
metabolisms:
Narcotic:
effects:
+ # Main effects
- !type:MovementSpeedModifier
- walkSpeedModifier: 1.25
- sprintSpeedModifier: 1.25
- - !type:HealthChange
- conditions:
- - !type:ReagentCondition
- reagent: Ephedrine
- min: 20
- damage:
- types:
- Poison: 2 # this is added to the base damage of the meth.
- Asphyxiation: 2
- - !type:Jitter
+ walkSpeedModifier: 1.15
+ sprintSpeedModifier: 1.15
- !type:ModifyStatusEffect
effectProto: StatusEffectStunned
time: 1
@@ -99,6 +100,24 @@
- !type:ModifyKnockdown
time: 1
type: Remove
+ # Side effects
+ - !type:Jitter
+ - !type:PopupMessage
+ visualType: Medium
+ messages: ["ephedrine-effect-tight-pain", "ephedrine-effect-heart-pounds"]
+ type: Local
+ probability: 0.05
+ # OD
+ - !type:HealthChange
+ conditions:
+ - !type:ReagentCondition
+ reagent: Ephedrine
+ min: 20
+ damage:
+ types:
+ Poison: 2
+ Asphyxiation: 2
+ # Interactions
- !type:ModifyStatusEffect
conditions:
- !type:ReagentCondition
@@ -107,18 +126,14 @@
effectProto: StatusEffectDrowsiness
time: 10
type: Remove
- - !type:PopupMessage
- visualType: Medium
- messages: ["ephedrine-effect-tight-pain", "ephedrine-effect-heart-pounds"]
- type: Local
- probability: 0.05
Medicine:
effects:
+ # Side effects
- !type:ResetNarcolepsy
conditions:
- !type:ReagentCondition
reagent: Ephedrine
- min: 30
+ min: 15
- type: reagent
id: Stimulants
@@ -133,41 +148,38 @@
meltingPoint: 170.0
metabolisms:
Narcotic:
- metabolismRate: 1.0
effects:
+ # Main effects
- !type:MovementSpeedModifier
- walkSpeedModifier: 1.3
- sprintSpeedModifier: 1.3
- - !type:HealthChange
- conditions:
- - !type:ReagentCondition
- reagent: Stimulants
- min: 80 #please wait 3 minutes before using another stimpack
- damage:
- types:
- Poison: 1
- - !type:AdjustReagent
- conditions:
- - !type:ReagentCondition
- reagent: ChloralHydrate
- min: 1
- reagent: ChloralHydrate
- amount: -10
+ walkSpeedModifier: 1.25
+ sprintSpeedModifier: 1.25
- !type:ModifyStatusEffect
effectProto: StatusEffectStunned
- time: 3
+ time: 3.5
type: Remove
- !type:ModifyKnockdown
- time: 3
+ time: 3.5
type: Remove
- - !type:GenericStatusEffect
- key: StaminaModifier
- component: StaminaModifier
- time: 3
- !type:ModifyStatusEffect
effectProto: StatusEffectForcedSleeping
time: 3
type: Remove
+ - !type:GenericStatusEffect
+ key: Adrenaline
+ component: IgnoreSlowOnDamage
+ time: 3
+ # Side effects
+ - !type:Jitter
+ # OD
+ - !type:HealthChange
+ conditions:
+ - !type:ReagentCondition
+ reagent: Stimulants
+ min: 50
+ damage:
+ types:
+ Poison: 1
+ # Interactions
- !type:ModifyStatusEffect
conditions:
- !type:ReagentCondition
@@ -176,22 +188,38 @@
effectProto: StatusEffectDrowsiness
time: 10
type: Remove
+ - !type:AdjustReagent
+ conditions:
+ - !type:ReagentCondition
+ reagent: ChloralHydrate
+ min: 1
+ reagent: ChloralHydrate
+ amount: -10
Medicine:
- metabolismRate: 1.0
effects:
- - !type:ResetNarcolepsy
- - !type:SatiateHunger
- factor: 1
- - !type:SatiateThirst
- factor: 1
- - !type:HealthChange
- conditions:
- - !type:TotalDamageCondition
- min: 70 # only heals when you're more dead than alive
- damage: # heals at the same rate as tricordrazine, doesn't heal poison because if you OD'd I'm not giving you a safety net
- groups:
- Burn: -1
- Brute: -1
+ # Main effects
+ - !type:ModifyBleed
+ amount: -1.5
+ - !type:EvenHealthChange
+ conditions:
+ - !type:TotalDamageCondition
+ min: 70 # only heals when you're more dead than alive
+ damage: # Doesn't heal poison because if you OD'd I'm not giving you a safety net
+ Burn: -0.5
+ Brute: -0.5
+ - !type:HealthChange
+ conditions:
+ - !type:TotalDamageCondition
+ min: 95 # Just to bring you back from the brink of death
+ damage:
+ types:
+ Asphyxiation: -2
+ # Side effects
+ - !type:ResetNarcolepsy
+ - !type:SatiateHunger
+ factor: 1
+ - !type:SatiateThirst
+ factor: 1
- type: reagent
id: THC
@@ -215,7 +243,7 @@
time: 16
type: Add
# Delta-V - Addictive
- - !type:Addicting
+ - !type:Addicting
probability: 0.4 # Chance of Addiction rising per tick
conditions:
- !type:ReagentCondition
From 376d1cdafc7cb30a67886668fb20acf793f7d625 Mon Sep 17 00:00:00 2001
From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Date: Fri, 5 Dec 2025 01:42:03 +0100
Subject: [PATCH 024/360] Warfarin and Hemorrhinol, Hemophilia turned into a
StatusEffect (#41685)
* init
* yeah
* move folders
* comments
* i hate cloning sometimes
* review
* review squred
* fix stuff
---
.../StatusEffectSystem.Relay.cs | 3 ++
.../Traits/Assorted/HemophiliaComponent.cs | 16 --------
.../HemophiliaStatusEffectComponent.cs | 22 +++++++++++
.../Traits/Assorted/HemophiliaSystem.cs | 10 +++--
.../Locale/en-US/reagents/meta/medicine.ftl | 3 ++
.../Locale/en-US/reagents/meta/toxins.ftl | 3 ++
.../Prototypes/Entities/Mobs/Player/clone.yml | 1 -
.../Entities/StatusEffects/body.yml | 37 +++++++++++++++++--
Resources/Prototypes/Reagents/medicine.yml | 24 ++++++++++++
Resources/Prototypes/Reagents/toxins.yml | 19 ++++++++++
.../Prototypes/Recipes/Reactions/medicine.yml | 26 ++++++++++++-
Resources/Prototypes/Traits/disabilities.yml | 19 ++++++----
12 files changed, 151 insertions(+), 32 deletions(-)
delete mode 100644 Content.Shared/Traits/Assorted/HemophiliaComponent.cs
create mode 100644 Content.Shared/Traits/Assorted/HemophiliaStatusEffectComponent.cs
diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs
index 9b16aadff0..30c5d9f67e 100644
--- a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs
+++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs
@@ -1,3 +1,4 @@
+using Content.Shared.Body.Events;
using Content.Shared.Damage.Events;
using Content.Shared.Mobs.Events;
using Content.Shared.Movement.Events;
@@ -31,6 +32,8 @@ public sealed partial class StatusEffectsSystem
SubscribeLocalEvent(RelayStatusEffectEvent);
SubscribeLocalEvent(RelayStatusEffectEvent);
+
+ SubscribeLocalEvent(RefRelayStatusEffectEvent);
}
private void RefRelayStatusEffectEvent(EntityUid uid, StatusEffectContainerComponent component, ref T args) where T : struct
diff --git a/Content.Shared/Traits/Assorted/HemophiliaComponent.cs b/Content.Shared/Traits/Assorted/HemophiliaComponent.cs
deleted file mode 100644
index 208883f11c..0000000000
--- a/Content.Shared/Traits/Assorted/HemophiliaComponent.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Traits.Assorted;
-
-///
-/// This component is used for the Hemophilia Trait, it reduces the passive bleed stack reduction amount so entities with it bleed for longer.
-///
-[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
-public sealed partial class HemophiliaComponent : Component
-{
- ///
- /// What multiplier should be applied to the BleedReduction when an entity bleeds?
- ///
- [DataField, AutoNetworkedField]
- public float HemophiliaBleedReductionMultiplier = 0.33f;
-}
diff --git a/Content.Shared/Traits/Assorted/HemophiliaStatusEffectComponent.cs b/Content.Shared/Traits/Assorted/HemophiliaStatusEffectComponent.cs
new file mode 100644
index 0000000000..3216052257
--- /dev/null
+++ b/Content.Shared/Traits/Assorted/HemophiliaStatusEffectComponent.cs
@@ -0,0 +1,22 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Traits.Assorted;
+
+///
+/// This component is used for the Hemophilia Trait, it reduces the passive bleed stack reduction amount so entities with it bleed for longer.
+///
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class HemophiliaStatusEffectComponent : Component
+{
+ ///
+ /// Multiplier to use for the amount of bloodloss reduction during a bleed tick.
+ ///
+ [DataField, AutoNetworkedField]
+ public float BleedReductionMultiplier = 0.33f;
+
+ ///
+ /// Multiplier to use for the amount of blood lost during a bleed tick.
+ ///
+ [DataField, AutoNetworkedField]
+ public float BleedAmountMultiplier = 1f;
+}
diff --git a/Content.Shared/Traits/Assorted/HemophiliaSystem.cs b/Content.Shared/Traits/Assorted/HemophiliaSystem.cs
index 53f6609575..4544007ac7 100644
--- a/Content.Shared/Traits/Assorted/HemophiliaSystem.cs
+++ b/Content.Shared/Traits/Assorted/HemophiliaSystem.cs
@@ -1,4 +1,5 @@
using Content.Shared.Body.Events;
+using Content.Shared.StatusEffectNew;
namespace Content.Shared.Traits.Assorted;
@@ -6,11 +7,14 @@ public sealed class HemophiliaSystem : EntitySystem
{
public override void Initialize()
{
- SubscribeLocalEvent(OnBleedModifier);
+ SubscribeLocalEvent>(OnBleedModifier);
}
- private void OnBleedModifier(Entity ent, ref BleedModifierEvent args)
+ private void OnBleedModifier(Entity ent, ref StatusEffectRelayedEvent args)
{
- args.BleedReductionAmount *= ent.Comp.HemophiliaBleedReductionMultiplier;
+ var ev = args.Args;
+ ev.BleedReductionAmount *= ent.Comp.BleedReductionMultiplier;
+ ev.BleedAmount *= ent.Comp.BleedAmountMultiplier;
+ args.Args = ev;
}
}
diff --git a/Resources/Locale/en-US/reagents/meta/medicine.ftl b/Resources/Locale/en-US/reagents/meta/medicine.ftl
index 7d71b43c1d..3a675dc3f4 100644
--- a/Resources/Locale/en-US/reagents/meta/medicine.ftl
+++ b/Resources/Locale/en-US/reagents/meta/medicine.ftl
@@ -156,3 +156,6 @@ reagent-desc-haloperidol = Removes most stimulating and hallucinogenic drugs. Re
reagent-name-stelloxadone = stelloxadone
reagent-desc-stelloxadone = A cryogenics chemical. Used to aggressively dissolve toxins from the body. Works regardless of the patient being alive or dead.
+
+reagent-name-warfarin = warfarin
+reagent-desc-warfarin = Commonly used as an anticoagulant medication. Causes blood to have difficulty forming clots. Can cause internal bleeding when overdosed.
diff --git a/Resources/Locale/en-US/reagents/meta/toxins.ftl b/Resources/Locale/en-US/reagents/meta/toxins.ftl
index c0cf06fe26..5c4657d34b 100644
--- a/Resources/Locale/en-US/reagents/meta/toxins.ftl
+++ b/Resources/Locale/en-US/reagents/meta/toxins.ftl
@@ -84,3 +84,6 @@ reagent-desc-mechanotoxin = A neurotoxin used as venom by some species of spider
reagent-name-toxintrash = reprocessed material
reagent-desc-toxintrash = An awful-smelling slurry efficiently refined from discarded matter. It represents a perfect, zero-waste conversion of salvage into Vox sustenance, though it is a violent poison to others.
+
+reagent-name-hemorrhinol = hemorrhinol
+reagent-desc-hemorrhinol = A toxin that causes severe damage to blood vessels, causing rapid bleeding.
diff --git a/Resources/Prototypes/Entities/Mobs/Player/clone.yml b/Resources/Prototypes/Entities/Mobs/Player/clone.yml
index 6b0af7e86b..b5572e53d7 100644
--- a/Resources/Prototypes/Entities/Mobs/Player/clone.yml
+++ b/Resources/Prototypes/Entities/Mobs/Player/clone.yml
@@ -16,7 +16,6 @@
# traits
- BlackAndWhiteOverlay
- Clumsy
- - Hemophilia
- ImpairedMobility
# - LegsParalyzed (you get healed)
- LightweightDrunk
diff --git a/Resources/Prototypes/Entities/StatusEffects/body.yml b/Resources/Prototypes/Entities/StatusEffects/body.yml
index 6b6f704a2c..739c9c3b22 100644
--- a/Resources/Prototypes/Entities/StatusEffects/body.yml
+++ b/Resources/Prototypes/Entities/StatusEffects/body.yml
@@ -9,13 +9,19 @@
- Bloodstream
- type: entity
- parent: [ BloodstreamStatusEffectBase ]
+ parent: BloodstreamStatusEffectBase
+ id: BloodstreamStatusEffectDebuff
+ abstract: true
+ components:
+ - type: RejuvenateRemovedStatusEffect
+
+- type: entity
+ parent: BloodstreamStatusEffectDebuff
id: StatusEffectBloodloss
name: bloodloss
components:
- type: StutteringAccent
- type: DrunkStatusEffect
- - type: RejuvenateRemovedStatusEffect
- type: entity
parent: MobStatusEffectBase
@@ -29,5 +35,30 @@
- type: PainNumbnessStatusEffect
- type: entity
- parent: PainNumbnessTraitStatusEffect
+ parent: BloodstreamStatusEffectBase
+ id: StatusEffectHemophiliaTrait
+ components:
+ - type: HemophiliaStatusEffect
+
+- type: entity
+ parent: BloodstreamStatusEffectDebuff
+ id: StatusEffectAnticoagulant # Decreases the amount of blood coagulation when bleeding.
+ name: thin blood
+ components:
+ - type: HemophiliaStatusEffect
+ bleedReductionMultiplier: 0.33
+ bleedAmountMultiplier: 1
+
+- type: entity
+ parent: BloodstreamStatusEffectDebuff
+ id: StatusEffectHemorrhage # Increases the amount of blood lost when bleeding.
+ name: hemorrhage
+ components:
+ - type: HemophiliaStatusEffect
+ bleedReductionMultiplier: 1 # We don't want it to also reduce coagulation, it would stack with Anticoagulant.
+ bleedAmountMultiplier: 1.33
+
+- type: entity
+ parent: [ PainNumbnessTraitStatusEffect, MobStatusEffectDebuff ]
id: StatusEffectPainNumbness
+ name: pain numbness
diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml
index 4a51ebd26e..bda7d27e07 100644
--- a/Resources/Prototypes/Reagents/medicine.yml
+++ b/Resources/Prototypes/Reagents/medicine.yml
@@ -1558,3 +1558,27 @@
- !type:AdjustReagent
reagent: MindbreakerToxin
amount: -3.0
+
+- type: reagent
+ id: Warfarin
+ name: reagent-name-warfarin
+ group: Medicine
+ desc: reagent-desc-warfarin
+ physicalDesc: reagent-physical-desc-thin
+ allowedDepartments:
+ - Medical
+ flavor: medicine
+ color: "#d0e21b"
+ metabolisms:
+ Medicine:
+ effects: # One day this could be useful for treating blood clots, if we get those
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectAnticoagulant
+ time: 30 # 10 bleed ticks, you bleed every 3 seconds by default.
+ type: Update
+ - !type:ModifyBleed
+ conditions:
+ - !type:ReagentCondition
+ reagent: Warfarin
+ min: 15
+ amount: 0.25
diff --git a/Resources/Prototypes/Reagents/toxins.yml b/Resources/Prototypes/Reagents/toxins.yml
index a582b28561..daf0d9554f 100644
--- a/Resources/Prototypes/Reagents/toxins.yml
+++ b/Resources/Prototypes/Reagents/toxins.yml
@@ -776,3 +776,22 @@
conditions:
- !type:MetabolizerTypeCondition
type: [Vox]
+
+- type: reagent
+ id: Hemorrhinol
+ name: reagent-name-hemorrhinol
+ group: Toxins
+ desc: reagent-desc-hemorrhinol
+ physicalDesc: reagent-physical-desc-thin
+ contrabandSeverity: Major
+ flavor: sharp
+ color: "#96424f"
+ metabolisms:
+ Poison:
+ effects:
+ - !type:ModifyStatusEffect
+ effectProto: StatusEffectHemorrhage
+ time: 21 # 7 bleed ticks, you bleed every 3 seconds by default.
+ type: Update
+ - !type:ModifyBleed
+ amount: 0.5
diff --git a/Resources/Prototypes/Recipes/Reactions/medicine.yml b/Resources/Prototypes/Recipes/Reactions/medicine.yml
index 5b8af49e20..46289b6b4c 100644
--- a/Resources/Prototypes/Recipes/Reactions/medicine.yml
+++ b/Resources/Prototypes/Recipes/Reactions/medicine.yml
@@ -691,7 +691,7 @@
amount: 1
products:
Traumoxadone: 3
-
+
- type: reaction
id: Stelloxadone
impact: Medium
@@ -706,3 +706,27 @@
Stelloxadone: 5
Water: 3
Fiber: 2
+
+- type: reaction
+ id: Warfarin
+ reactants:
+ SulfuricAcid:
+ amount: 1
+ Nitrogen:
+ amount: 1
+ Sodium:
+ amount: 1
+ products:
+ Warfarin: 2
+
+- type: reaction
+ id: Hemorrhinol
+ reactants:
+ Warfarin:
+ amount: 2
+ Razorium:
+ amount: 2
+ Plasma:
+ amount: 1
+ products:
+ Hemorrhinol: 2
diff --git a/Resources/Prototypes/Traits/disabilities.yml b/Resources/Prototypes/Traits/disabilities.yml
index b9a8e1fd54..3ff8529607 100644
--- a/Resources/Prototypes/Traits/disabilities.yml
+++ b/Resources/Prototypes/Traits/disabilities.yml
@@ -93,18 +93,21 @@
# - PainNumbnessTraitStatusEffect
#
#- type: trait
+# id: Hemophilia
+# name: trait-hemophilia-name
+# description: trait-hemophilia-desc
+# category: Disabilities
+# specials:
+# - !type:ApplyStatusEffectSpecial
+# statusEffects:
+# - StatusEffectHemophiliaTrait
+#
+#- type: trait
# id: ImpairedMobility
# name: trait-impaired-mobility-name
# description: trait-impaired-mobility-desc
# traitGear: OffsetCane
# category: Disabilities
# components:
-# - type: ImpairedMobility
+# - type: ImpairedMobility
#
-#- type: trait
-# id: Hemophilia
-# name: trait-hemophilia-name
-# description: trait-hemophilia-desc
-# category: Disabilities
-# components:
-# - type: Hemophilia
From b671ea6b1005bfd5b5664643c203bb367215d6f3 Mon Sep 17 00:00:00 2001
From: BarryNorfolk
Date: Sat, 24 Jan 2026 16:35:56 +0100
Subject: [PATCH 025/360] Downstream fix for Hemophilia status effect change
---
Resources/Prototypes/_DV/Traits/disabilities.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Resources/Prototypes/_DV/Traits/disabilities.yml b/Resources/Prototypes/_DV/Traits/disabilities.yml
index b021d5c9ca..1c2762b152 100644
--- a/Resources/Prototypes/_DV/Traits/disabilities.yml
+++ b/Resources/Prototypes/_DV/Traits/disabilities.yml
@@ -127,6 +127,6 @@
description: trait-hemophilia-desc
category: Disabilities
effects:
- - !type:AddCompsEffect
- components:
- - type: Hemophilia
+ - !type:ApplyStatusEffect
+ statusEffects:
+ - StatusEffectHemophiliaTrait
From 37b0a845f30f53391f452dd3c12e86dc26cbc903 Mon Sep 17 00:00:00 2001
From: BarryNorfolk
Date: Sat, 24 Jan 2026 11:21:57 +0100
Subject: [PATCH 026/360] Move Stelloxadone ftl and reaction to _Floof
namespace
---
.../en-US/_Floof/reagents/meta/medicine.ftl | 2 ++
Resources/Locale/en-US/reagents/meta/medicine.ftl | 3 ---
.../Prototypes/Recipes/Reactions/medicine.yml | 15 ---------------
.../_Floof/Recipes/Reactions/medicine.yml | 14 ++++++++++++++
4 files changed, 16 insertions(+), 18 deletions(-)
create mode 100644 Resources/Locale/en-US/_Floof/reagents/meta/medicine.ftl
create mode 100644 Resources/Prototypes/_Floof/Recipes/Reactions/medicine.yml
diff --git a/Resources/Locale/en-US/_Floof/reagents/meta/medicine.ftl b/Resources/Locale/en-US/_Floof/reagents/meta/medicine.ftl
new file mode 100644
index 0000000000..90d167b28e
--- /dev/null
+++ b/Resources/Locale/en-US/_Floof/reagents/meta/medicine.ftl
@@ -0,0 +1,2 @@
+reagent-name-stelloxadone = stelloxadone
+reagent-desc-stelloxadone = A cryogenics chemical. Used to aggressively dissolve toxins from the body. Works regardless of the patient being alive or dead.
diff --git a/Resources/Locale/en-US/reagents/meta/medicine.ftl b/Resources/Locale/en-US/reagents/meta/medicine.ftl
index 3a675dc3f4..b6240170da 100644
--- a/Resources/Locale/en-US/reagents/meta/medicine.ftl
+++ b/Resources/Locale/en-US/reagents/meta/medicine.ftl
@@ -154,8 +154,5 @@ reagent-desc-potassium-iodide = Will reduce the damaging effects of radiation by
reagent-name-haloperidol = haloperidol
reagent-desc-haloperidol = Removes most stimulating and hallucinogenic drugs. Reduces druggy effects and jitteriness. Causes drowsiness.
-reagent-name-stelloxadone = stelloxadone
-reagent-desc-stelloxadone = A cryogenics chemical. Used to aggressively dissolve toxins from the body. Works regardless of the patient being alive or dead.
-
reagent-name-warfarin = warfarin
reagent-desc-warfarin = Commonly used as an anticoagulant medication. Causes blood to have difficulty forming clots. Can cause internal bleeding when overdosed.
diff --git a/Resources/Prototypes/Recipes/Reactions/medicine.yml b/Resources/Prototypes/Recipes/Reactions/medicine.yml
index 46289b6b4c..b98accf093 100644
--- a/Resources/Prototypes/Recipes/Reactions/medicine.yml
+++ b/Resources/Prototypes/Recipes/Reactions/medicine.yml
@@ -692,21 +692,6 @@
products:
Traumoxadone: 3
-- type: reaction
- id: Stelloxadone
- impact: Medium
- reactants:
- Stellibinin:
- amount: 5
- Cryoxadone:
- amount: 3
- Arithrazine:
- amount: 2
- products:
- Stelloxadone: 5
- Water: 3
- Fiber: 2
-
- type: reaction
id: Warfarin
reactants:
diff --git a/Resources/Prototypes/_Floof/Recipes/Reactions/medicine.yml b/Resources/Prototypes/_Floof/Recipes/Reactions/medicine.yml
new file mode 100644
index 0000000000..0b067204a5
--- /dev/null
+++ b/Resources/Prototypes/_Floof/Recipes/Reactions/medicine.yml
@@ -0,0 +1,14 @@
+- type: reaction
+ id: Stelloxadone
+ impact: Medium
+ reactants:
+ Stellibinin:
+ amount: 5
+ Cryoxadone:
+ amount: 3
+ Arithrazine:
+ amount: 2
+ products:
+ Stelloxadone: 5
+ Water: 3
+ Fiber: 2
From c9627733c13a1aabfc6504cb0bab9a5a555753f8 Mon Sep 17 00:00:00 2001
From: MissKay1994 <15877268+MissKay1994@users.noreply.github.com>
Date: Fri, 5 Dec 2025 05:34:00 -0500
Subject: [PATCH 027/360] Add missing vox unequipped sprite for explorer mask
(#41405)
Mask on face
---
.../Clothing/Mask/gasexplorer.rsi/meta.json | 6 +++++-
.../Mask/gasexplorer.rsi/up-equipped-MASK-vox.png | Bin 0 -> 947 bytes
2 files changed, 5 insertions(+), 1 deletion(-)
create mode 100644 Resources/Textures/Clothing/Mask/gasexplorer.rsi/up-equipped-MASK-vox.png
diff --git a/Resources/Textures/Clothing/Mask/gasexplorer.rsi/meta.json b/Resources/Textures/Clothing/Mask/gasexplorer.rsi/meta.json
index baaca1f25f..976c85ed20 100644
--- a/Resources/Textures/Clothing/Mask/gasexplorer.rsi/meta.json
+++ b/Resources/Textures/Clothing/Mask/gasexplorer.rsi/meta.json
@@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
- "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4f6190e2895e09116663ef282d3ce1d8b35c032e. Reptilian edit by Nairod(Github). Vox state by Flareguy for SS14 | vulpkanin version edited by Floofers. Feroxi sprites made by @Stxcking",
+ "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4f6190e2895e09116663ef282d3ce1d8b35c032e. Reptilian edit by Nairod(Github). Vox state by Flareguy for SS14 | vulpkanin version edited by Floofers. Feroxi sprites made by @Stxcking | Vox unequipped by MissKay1994(Github)",
"size": {
"x": 32,
"y": 32
@@ -39,6 +39,10 @@
{
"name": "equipped-MASK-reptilian",
"directions": 4
+ },
+ {
+ "name": "up-equipped-MASK-vox",
+ "directions": 4
}
]
}
diff --git a/Resources/Textures/Clothing/Mask/gasexplorer.rsi/up-equipped-MASK-vox.png b/Resources/Textures/Clothing/Mask/gasexplorer.rsi/up-equipped-MASK-vox.png
new file mode 100644
index 0000000000000000000000000000000000000000..bbf58361038b1c6c2398bb4e63f744a08ebbdd33
GIT binary patch
literal 947
zcmV;k15EshP)Px&ZAnByRCt{2nomd+Q5?s=BZ3GrJ0e1kJGt&2TrngpG^xW9brMPh2~95^VqtGf
z2tjy=z>9bjqJt+<#LE(R2nsx8h|!)JcRMK7$|x++>|7)eejVg3JKLf=f81C3KBqVT
ze((4D&ilPL%=-ZX!KFs3_Qs8k4cl=t5hvE3UwBou9lw~G7%%p}xU^jCKNgEQvQy`#
z^JF4Uirj=~BnU-rvWZbklz{BnhGPMei8xtZEoh$}OC9A+v`>$vtge=ti?Fz~ESUw%
z_V(T2NB|c6;N(b3*E2AOuBUW8gTcv>nut(3>;d4>(Css?S3|uUM|3@N+F1E8jIleZ
z;_(He1Z2FPe@Ot)(bfzAc)PMd-wuBkuj9aQWYduVX2HU(lOVR!nPK_s52W?1)VBh%
zx?0eG(ul2s<*0L~m<0>j-GhpgZZZ)k(?$PPr@++2I4J=cTj?G2pEM$^XC(kg>shJ)
zq!C-`9fUO1)i87d>$lQ*H$hfd&aAtN+3TY}T&Af|C%hKNvyc-0B?iyb%KMc7QiR0NxJpMhL*$0p47C0bKtQ^9!ec
zasB4fj{qTnR|CmJoYg$P8Y1^w^*98*gBolFpMuGre!ujP3g4V(oy}eL^BaL~AEj
zhp=6LjE=TuL?c1mxYkWxgNI)!z=B`P8@AekkfwTeFr=xrI)t^nQ53@8M&=-?Z6xi{}&w2USu6GFI0&0RW?|
zDh7Hx|G3ZV2hTunr!9CTAR`=9`K17Z)diG_0Kk{GQ)+8BY0BR6gYWgosd@t`V)BLYj*AA3sTctONpqKp+qZ1OkCTAP@)y0)gQ2@Egq|
VXtvYfgDn66002ovPDHLkV1iEyuo?gW
literal 0
HcmV?d00001
From 371968842922dd39d9692be0a88a9927579c1864 Mon Sep 17 00:00:00 2001
From: alexalexmax <149889301+alexalexmax@users.noreply.github.com>
Date: Fri, 5 Dec 2025 02:42:23 -0800
Subject: [PATCH 028/360] Fix recharging spray painters (#41725)
change one line
---
Content.Shared/SprayPainter/SharedSprayPainterSystem.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs b/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs
index a1316e3565..d1f19d0c25 100644
--- a/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs
+++ b/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs
@@ -188,7 +188,7 @@ public abstract class SharedSprayPainterSystem : EntitySystem
args.Handled = true;
if (TryComp(args.Used, out var charges)
- && charges.LastCharges < targetGroup.Cost)
+ && Charges.GetCurrentCharges((args.Used, charges)) < targetGroup.Cost)
{
var msg = Loc.GetString("spray-painter-interact-no-charges");
_popup.PopupClient(msg, args.User, args.User);
From 8cfc9e71842f4ae7eea15d6ff9959501f6f9a38c Mon Sep 17 00:00:00 2001
From: Hitlinemoss <209321380+Hitlinemoss@users.noreply.github.com>
Date: Fri, 5 Dec 2025 07:24:22 -0500
Subject: [PATCH 029/360] New figurine voicelines (#41723)
---
Resources/Locale/en-US/datasets/figurines.ftl | 50 ++++++++++++++-----
Resources/Prototypes/Datasets/figurines.yml | 20 ++++----
2 files changed, 48 insertions(+), 22 deletions(-)
diff --git a/Resources/Locale/en-US/datasets/figurines.ftl b/Resources/Locale/en-US/datasets/figurines.ftl
index b6428c0560..9a27dbc584 100644
--- a/Resources/Locale/en-US/datasets/figurines.ftl
+++ b/Resources/Locale/en-US/datasets/figurines.ftl
@@ -17,9 +17,8 @@ figurines-qm-3 = I didn't approve that shipment of guns!
figurines-qm-4 = One toy box for my fellow clown!
figurines-qm-5 = Time to gamble!
figurines-qm-6 = Viva la Cargonia!
-figurines-qm-7 = Fill out the form.
-figurines-qm-8 = Where'd all our money go?
-figurines-qm-9 = 99% of gamblers quit right before they hit it big!
+figurines-qm-7 = Where'd all our money go?
+figurines-qm-8 = 99% of gamblers quit right before they hit it big!
figurines-cargotech-1 = DRAGON ON ATS!
figurines-cargotech-2 = I sold the station!
@@ -27,6 +26,11 @@ figurines-cargotech-3 = Brain bounty? I don't have a brain.
figurines-cargotech-4 = You're worth 3000 spesos. Congrats.
figurines-cargotech-5 = Vegetable bounty? Nobody eats those anyways.
figurines-cargotech-6 = WE ARE SECEDING!! ALL HAIL CARGONIA!!
+figurines-cargotech-7 = Pizza party at cargo!
+figurines-cargotech-8 = The mail never stops...
+figurines-cargotech-9 = Nothing stops the mail!
+figurines-cargotech-10 = Clown mask bounty? Alright, let me just find a weapon...
+figurines-cargotech-11 = Laser bounty? Nobody will notice if we ship practice lasers instead, right?
figurines-salvage-1 = Megafauna? It was mega easy.
figurines-salvage-2 = We're lost. Anyone bring a GPS?
@@ -34,6 +38,9 @@ figurines-salvage-3 = Anyone have oxygen?
figurines-salvage-4 = I found a blood-red and e-sword!
figurines-salvage-5 = There's bears in space?
figurines-salvage-6 = Crusher? I barely know her!
+figurines-salvage-7 = Can someone come pick me up?
+figurines-salvage-8 = I found that contraband on a wreck! It's not mine!
+figurines-salvage-9 = Can we borrow the cargo shuttle? Please?
# Engineering
@@ -51,6 +58,8 @@ figurines-atmostech-4 = Tritium...
figurines-atmostech-5 = Glory to Atmosia!
figurines-atmostech-6 = Distro? That's short for disposal.
figurines-atmostech-7 = TEG: Thermal Energy? Gone!
+figurines-atmostech-8 = Does anyone else hear glass cracking?
+figurines-atmostech-9 = I promise this burn chamber is totally safe and will NOT explode.
figurines-engineer-1 = SINGULOOSE!
figurines-engineer-2 = TESLOOSE!
@@ -74,12 +83,15 @@ figurines-chemist-3 = I am the one who knocks!
figurines-chemist-4 = Say my name.
figurines-chemist-5 = 99.8% purity.
figurines-chemist-6 = Epinephrine? Didn't you say methamphetamine?
+figurines-chemist-7 = Pills here!
+figurines-chemist-8 = Legally speaking, it's not actually a bomb until you mix both beakers together.
figurines-doctor-1 = The patient is already dead!
figurines-doctor-2 = CLEAR!
figurines-doctor-3 = Saw makes BRRR.
figurines-doctor-4 = Just a week away...
figurines-doctor-5 = I knew it...
+figurines-doctor-6 = Well, at least we have body bags.
figurines-paramedic-1 = Insuls and tools!
figurines-paramedic-2 = I need AA for saving people!
@@ -140,8 +152,10 @@ figurines-warden-6 = You're going away for a long time, buddy.
figurines-hop-1 = Papers, please.
figurines-hop-2 = You are fired.
figurines-hop-3 = BRB.
-figurines-hop-4 = You can get AA if you fill out the form.
+figurines-hop-4 = Fill out the form.
figurines-hop-5 = I was gone for two seconds...
+figurines-hop-6 = Go get this form stamped.
+figurines-hop-7 = Has anyone seen Ian?
figurines-bartender-1 = Where's my monkey?
figurines-bartender-2 = Sec won't drink.
@@ -179,11 +193,12 @@ figurines-chef-6 = Where'd Pun Pun go? No idea...
figurines-clown-1 = Honk!
figurines-clown-2 = Banana!
-figurines-clown-3 = Soap!
-figurines-clown-4 = HoP has one clown, HoS has the whole department.
-figurines-clown-5 = Do I annoy you?
-figurines-clown-6 = Can I have AA? Please?
-figurines-clown-7 = I'm a clown, but you're the whole circus!
+figurines-clown-3 = Pie!
+figurines-clown-4 = Soap!
+figurines-clown-5 = Service has one clown, but Security has a whole department of them.
+figurines-clown-6 = Do I annoy you?
+figurines-clown-7 = Can I have AA? Please?
+figurines-clown-8 = I'm a clown, but you're the whole circus!
figurines-greytider-1 = Man, this party stinks. I fucking hate these people.
figurines-greytider-2 = Uh-oh, who's lost their stunbaton?
@@ -207,6 +222,13 @@ figurines-lawyer-2 = Objection!
figurines-lawyer-3 = Did you know that you have rights?
figurines-lawyer-4 = Space law says!
figurines-lawyer-5 = Sign the contract first.
+figurines-lawyer-6 = My client is innocent!
+figurines-lawyer-7 = I'm suing.
+figurines-lawyer-8 = You may be entitled to financial compensation!
+figurines-lawyer-9 = Come back with a warrant!
+figurines-lawyer-10 = See you in court!
+figurines-lawyer-11 = Guilty!
+figurines-lawyer-12 = Not guilty!
figurines-librarian-1 = Silence!
figurines-librarian-2 = One day while...
@@ -322,10 +344,14 @@ figurines-thief-3 = Theres nothing suspicious about this satchel at all, officer
figurines-thief-4 = I have NO idea where your pet is...
figurines-thief-5 = Huh, I didn't know that wall could open up...
-figurines-wizard-1 = Ei Nath!!
-figurines-wizard-2 = Real wizards support trans rights.
+figurines-wizard-1 = EI NATH!!
+figurines-wizard-2 = ONI'SOMA!!
figurines-wizard-3 = Skidaddle skadoodle!
-figurines-wizard-4 = FIREBALL!
+figurines-wizard-4 = Real wizards support trans rights.
+figurines-wizard-5 = Which one of you NERDS is ready to be shoved in a locker?
+figurines-wizard-6 = I'm not the wizard! I'm the captain! I got mind-swapped!
+figurines-wizard-7 = Now you see me, now you don't!
+figurines-wizard-8 = Guns are for losers who can't explode people with their mind.
# Animals
diff --git a/Resources/Prototypes/Datasets/figurines.yml b/Resources/Prototypes/Datasets/figurines.yml
index 53ba40da64..8d1a256b53 100644
--- a/Resources/Prototypes/Datasets/figurines.yml
+++ b/Resources/Prototypes/Datasets/figurines.yml
@@ -2,7 +2,7 @@
id: FigurinesHoP
values:
prefix: figurines-hop-
- count: 5
+ count: 7
- type: localizedDataset
id: FigurinesPassenger
@@ -20,7 +20,7 @@
id: FigurinesClown
values:
prefix: figurines-clown-
- count: 7
+ count: 8
- type: localizedDataset
id: FigurinesHoloClown
@@ -80,25 +80,25 @@
id: FigurinesLawyer
values:
prefix: figurines-lawyer-
- count: 5
+ count: 12
- type: localizedDataset
id: FigurinesCargoTech
values:
prefix: figurines-cargotech-
- count: 6
+ count: 11
- type: localizedDataset
id: FigurinesSalvage
values:
prefix: figurines-salvage-
- count: 6
+ count: 9
- type: localizedDataset
id: FigurinesQM
values:
prefix: figurines-qm-
- count: 9
+ count: 8
- type: localizedDataset
id: FigurinesCE
@@ -116,7 +116,7 @@
id: FigurinesAtmosTech
values:
prefix: figurines-atmostech-
- count: 7
+ count: 9
- type: localizedDataset
id: FigurinesRD
@@ -140,7 +140,7 @@
id: FigurinesChemist
values:
prefix: figurines-chemist-
- count: 6
+ count: 8
- type: localizedDataset
id: FigurinesParamedic
@@ -152,7 +152,7 @@
id: FigurinesDoctor
values:
prefix: figurines-doctor-
- count: 5
+ count: 6
- type: localizedDataset
id: FigurinesLibrarian
@@ -218,7 +218,7 @@
id: FigurinesWizard
values:
prefix: figurines-wizard-
- count: 4
+ count: 8
- type: localizedDataset
id: FigurinesSpaceDragon
From 4dc0ff5c6a59b12525ff75373ef687fb5bba263b Mon Sep 17 00:00:00 2001
From: Pieter-Jan Briers
Date: Fri, 5 Dec 2025 13:37:02 +0100
Subject: [PATCH 030/360] C# 14 fixes (#41708)
Necessary for the move to .NET 10 & C# 14. Actual PR to change SS14 to C# 14 will be separate.
---
Content.Client/Parallax/Managers/ParallaxManager.cs | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/Content.Client/Parallax/Managers/ParallaxManager.cs b/Content.Client/Parallax/Managers/ParallaxManager.cs
index bc7d7d60d6..bd46288ebf 100644
--- a/Content.Client/Parallax/Managers/ParallaxManager.cs
+++ b/Content.Client/Parallax/Managers/ParallaxManager.cs
@@ -98,10 +98,13 @@ public sealed class ParallaxManager : IParallaxManager
}
else
{
- layers = await Task.WhenAll(
+ // Explicitly allocate params array to avoid sandbox violation since C# 14.
+ var tasks = new[]
+ {
LoadParallaxLayers(parallaxPrototype.Layers, loadedLayers, cancel),
- LoadParallaxLayers(parallaxPrototype.LayersLQ, loadedLayers, cancel)
- );
+ LoadParallaxLayers(parallaxPrototype.LayersLQ, loadedLayers, cancel),
+ };
+ layers = await Task.WhenAll(tasks);
}
cancel.ThrowIfCancellationRequested();
From dc3eeb917cba3452545111fb9927be885cef1b3e Mon Sep 17 00:00:00 2001
From: Pieter-Jan Briers
Date: Fri, 5 Dec 2025 15:53:35 +0100
Subject: [PATCH 031/360] Fix shuttle FTL with UI scale (#40933)
Fixed click inputs being broken
Fixed rendering of background parallax.
---
Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs b/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs
index 70ac02bda9..daf2622d81 100644
--- a/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs
+++ b/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs
@@ -124,7 +124,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
else
{
// We'll send the "adjusted" position and server will adjust it back when relevant.
- var mapCoords = new MapCoordinates(InverseMapPosition(args.RelativePosition), ViewingMap);
+ var mapCoords = new MapCoordinates(InverseMapPosition(args.RelativePixelPosition), ViewingMap);
RequestFTL?.Invoke(mapCoords, _ftlAngle);
}
}
@@ -180,7 +180,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl
// Remove offset so we can floor.
var botLeft = new Vector2(0f, 0f);
- var topRight = botLeft + Size;
+ var topRight = botLeft + PixelSize;
var flooredBL = botLeft - originBL;
From 6a2537f5117c24b44a08b7a753c13084e617007e Mon Sep 17 00:00:00 2001
From: B_Kirill <153602297+B-Kirill@users.noreply.github.com>
Date: Sat, 6 Dec 2025 01:51:48 +1000
Subject: [PATCH 032/360] Remove static IoC from Replay and Shared EntryPoint
(#41707)
---
Content.Replay/EntryPoint.cs | 4 ++--
Content.Shared/Entry/EntryPoint.cs | 15 ++++++++-------
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/Content.Replay/EntryPoint.cs b/Content.Replay/EntryPoint.cs
index ed6460a7e7..6a6658f5ef 100644
--- a/Content.Replay/EntryPoint.cs
+++ b/Content.Replay/EntryPoint.cs
@@ -19,8 +19,8 @@ public sealed class EntryPoint : GameClient
public override void Init()
{
base.Init();
- IoCManager.BuildGraph();
- IoCManager.InjectDependencies(this);
+ Dependencies.BuildGraph();
+ Dependencies.InjectDependencies(this);
}
public override void PostInit()
diff --git a/Content.Shared/Entry/EntryPoint.cs b/Content.Shared/Entry/EntryPoint.cs
index db8d6a6abd..1b5755dd66 100644
--- a/Content.Shared/Entry/EntryPoint.cs
+++ b/Content.Shared/Entry/EntryPoint.cs
@@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Content.Shared.Humanoid.Markings;
-using Content.Shared.IoC;
using Content.Shared.Maps;
using Robust.Shared;
using Robust.Shared.Configuration;
@@ -21,12 +20,15 @@ namespace Content.Shared.Entry
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
[Dependency] private readonly IResourceManager _resMan = default!;
+#if DEBUG
+ [Dependency] private readonly IConfigurationManager _configurationManager = default!;
+#endif
private readonly ResPath _ignoreFileDirectory = new("/IgnoredPrototypes/");
public override void PreInit()
{
- IoCManager.InjectDependencies(this);
+ Dependencies.InjectDependencies(this);
}
public override void Shutdown()
@@ -44,13 +46,12 @@ namespace Content.Shared.Entry
base.PostInit();
InitTileDefinitions();
- IoCManager.Resolve().Initialize();
+ Dependencies.Resolve().Initialize();
#if DEBUG
- var configMan = IoCManager.Resolve();
- configMan.OverrideDefault(CVars.NetFakeLagMin, 0.075f);
- configMan.OverrideDefault(CVars.NetFakeLoss, 0.005f);
- configMan.OverrideDefault(CVars.NetFakeDuplicates, 0.005f);
+ _configurationManager.OverrideDefault(CVars.NetFakeLagMin, 0.075f);
+ _configurationManager.OverrideDefault(CVars.NetFakeLoss, 0.005f);
+ _configurationManager.OverrideDefault(CVars.NetFakeDuplicates, 0.005f);
#endif
}
From 29ca8f78ffeab21b19e8677fff59f4808cf85df0 Mon Sep 17 00:00:00 2001
From: beck-thompson <107373427+beck-thompson@users.noreply.github.com>
Date: Sun, 22 Jun 2025 12:28:21 -0700
Subject: [PATCH 033/360] Allow admins to export round logs to CSV files
(#38206)
---
.../UI/Logs/AdminLogsControl.xaml | 1 +
.../Administration/UI/Logs/AdminLogsEui.cs | 69 +++++++++++++++++++
.../en-US/administration/ui/admin-logs.ftl | 1 +
3 files changed, 71 insertions(+)
diff --git a/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml b/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml
index 9b5da77801..86b72d9392 100644
--- a/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml
+++ b/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml
@@ -55,6 +55,7 @@
+
diff --git a/Content.Client/Administration/UI/Logs/AdminLogsEui.cs b/Content.Client/Administration/UI/Logs/AdminLogsEui.cs
index 6ad7de347d..496078a8b8 100644
--- a/Content.Client/Administration/UI/Logs/AdminLogsEui.cs
+++ b/Content.Client/Administration/UI/Logs/AdminLogsEui.cs
@@ -17,6 +17,12 @@ public sealed class AdminLogsEui : BaseEui
{
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
+ [Dependency] private readonly IFileDialogManager _dialogManager = default!;
+ [Dependency] private readonly ILogManager _log = default!;
+
+ private ISawmill _sawmill;
+
+ private bool _currentlyExportingLogs = false;
public AdminLogsEui()
{
@@ -28,6 +34,9 @@ public sealed class AdminLogsEui : BaseEui
LogsControl.RefreshButton.OnPressed += _ => RequestLogs();
LogsControl.NextButton.OnPressed += _ => NextLogs();
LogsControl.PopOutButton.OnPressed += _ => PopOut();
+ LogsControl.ExportLogs.OnPressed += _ => ExportLogs();
+
+ _sawmill = _log.GetSawmill("admin.logs.ui");
}
private WindowRoot? Root { get; set; }
@@ -76,6 +85,66 @@ public sealed class AdminLogsEui : BaseEui
SendMessage(request);
}
+ private async void ExportLogs()
+ {
+ if (_currentlyExportingLogs)
+ return;
+
+ _currentlyExportingLogs = true;
+ LogsControl.ExportLogs.Disabled = true;
+
+ var file = await _dialogManager.SaveFile(new FileDialogFilters(new FileDialogFilters.Group("csv")));
+
+ if (file == null)
+ return;
+
+ try
+ {
+ await using var writer = new StreamWriter(file.Value.fileStream);
+ foreach (var child in LogsControl.LogsContainer.Children)
+ {
+ if (child is not AdminLogLabel logLabel || !child.Visible)
+ continue;
+
+ var log = logLabel.Log;
+
+ // I swear to god if someone adds ,s or "s to the other fields...
+ await writer.WriteAsync(log.Date.ToString("s", System.Globalization.CultureInfo.InvariantCulture));
+ await writer.WriteAsync(',');
+ await writer.WriteAsync(log.Id.ToString());
+ await writer.WriteAsync(',');
+ await writer.WriteAsync(log.Impact.ToString());
+ await writer.WriteAsync(',');
+ // Message
+ await writer.WriteAsync('"');
+ await writer.WriteAsync(log.Message.Replace("\"", "\"\""));
+ await writer.WriteAsync('"');
+ // End of message
+ await writer.WriteAsync(',');
+
+ var players = log.Players;
+ for (var i = 0; i < players.Length; i++)
+ {
+ await writer.WriteAsync(players[i] + (i == players.Length - 1 ? "" : " "));
+ }
+
+ await writer.WriteAsync(',');
+ await writer.WriteAsync(log.Type.ToString());
+ await writer.WriteLineAsync();
+ }
+ }
+ catch (Exception exc)
+ {
+ _sawmill.Error($"Error when exporting admin log:\n{exc.StackTrace}");
+ }
+ finally
+ {
+ await file.Value.fileStream.DisposeAsync();
+ _currentlyExportingLogs = false;
+ LogsControl.ExportLogs.Disabled = false;
+ }
+ }
+
private void PopOut()
{
if (LogsWindow == null)
diff --git a/Resources/Locale/en-US/administration/ui/admin-logs.ftl b/Resources/Locale/en-US/administration/ui/admin-logs.ftl
index 8c30863a55..0d9894d5ee 100644
--- a/Resources/Locale/en-US/administration/ui/admin-logs.ftl
+++ b/Resources/Locale/en-US/administration/ui/admin-logs.ftl
@@ -1,5 +1,6 @@
admin-logs-title = Admin Logs Panel
admin-logs-count = Showing {$showing}/{$total} of {$round}
+admin-logs-export = Export
admin-logs-pop-out = Pop Out
# Round
From 0bfa6e8d147add0235338d6016f784da12afbe48 Mon Sep 17 00:00:00 2001
From: Kowlin <10947836+Kowlin@users.noreply.github.com>
Date: Mon, 30 Jun 2025 23:09:16 +0200
Subject: [PATCH 034/360] Tweaks to admin CSV exporting (#38531)
---
.../Administration/UI/Logs/AdminLogsEui.cs | 37 ++++++++++++-------
1 file changed, 23 insertions(+), 14 deletions(-)
diff --git a/Content.Client/Administration/UI/Logs/AdminLogsEui.cs b/Content.Client/Administration/UI/Logs/AdminLogsEui.cs
index 496078a8b8..52364111c0 100644
--- a/Content.Client/Administration/UI/Logs/AdminLogsEui.cs
+++ b/Content.Client/Administration/UI/Logs/AdminLogsEui.cs
@@ -20,6 +20,10 @@ public sealed class AdminLogsEui : BaseEui
[Dependency] private readonly IFileDialogManager _dialogManager = default!;
[Dependency] private readonly ILogManager _log = default!;
+ private const char CsvSeparator = ',';
+ private const string CsvQuote = "\"";
+ private const string CsvHeader = "Date,ID,PlayerID,Severity,Type,Message";
+
private ISawmill _sawmill;
private bool _currentlyExportingLogs = false;
@@ -100,7 +104,9 @@ public sealed class AdminLogsEui : BaseEui
try
{
- await using var writer = new StreamWriter(file.Value.fileStream);
+ // Buffer is set to 4KB for performance reasons. As the average export of 1000 logs is ~200KB
+ await using var writer = new StreamWriter(file.Value.fileStream, bufferSize: 4096);
+ await writer.WriteLineAsync(CsvHeader);
foreach (var child in LogsControl.LogsContainer.Children)
{
if (child is not AdminLogLabel logLabel || !child.Visible)
@@ -108,28 +114,31 @@ public sealed class AdminLogsEui : BaseEui
var log = logLabel.Log;
+ // Date
// I swear to god if someone adds ,s or "s to the other fields...
await writer.WriteAsync(log.Date.ToString("s", System.Globalization.CultureInfo.InvariantCulture));
- await writer.WriteAsync(',');
+ await writer.WriteAsync(CsvSeparator);
+ // ID
await writer.WriteAsync(log.Id.ToString());
- await writer.WriteAsync(',');
- await writer.WriteAsync(log.Impact.ToString());
- await writer.WriteAsync(',');
- // Message
- await writer.WriteAsync('"');
- await writer.WriteAsync(log.Message.Replace("\"", "\"\""));
- await writer.WriteAsync('"');
- // End of message
- await writer.WriteAsync(',');
-
+ await writer.WriteAsync(CsvSeparator);
+ // PlayerID
var players = log.Players;
for (var i = 0; i < players.Length; i++)
{
await writer.WriteAsync(players[i] + (i == players.Length - 1 ? "" : " "));
}
-
- await writer.WriteAsync(',');
+ await writer.WriteAsync(CsvSeparator);
+ // Severity
+ await writer.WriteAsync(log.Impact.ToString());
+ await writer.WriteAsync(CsvSeparator);
+ // Type
await writer.WriteAsync(log.Type.ToString());
+ await writer.WriteAsync(CsvSeparator);
+ // Message
+ await writer.WriteAsync(CsvQuote);
+ await writer.WriteAsync(log.Message.Replace(CsvQuote, CsvQuote + CsvQuote));
+ await writer.WriteAsync(CsvQuote);
+
await writer.WriteLineAsync();
}
}
From 95f9d4805104c0f23dd0aba9a614cff9fec77a84 Mon Sep 17 00:00:00 2001
From: Vasilis The Pikachu
Date: Sun, 31 Aug 2025 17:50:37 +0200
Subject: [PATCH 035/360] Revert "Admin Log Browser Improvements (#39130)"
This reverts commit f67cebf7a4ed451f1911aa5c8bf6f04c198b917f.
Per request of @Kowlin and @southbridge-fur
Check out https://github.com/space-wizards/space-station-14/issues/39960 for further information
---
.../UI/CustomControls/AdminLogLabel.cs | 33 +
.../CustomControls/PlayerListControl.xaml.cs | 18 +-
.../UI/CustomControls/PlayerListEntry.xaml.cs | 1 +
.../UI/Logs/AdminLogsControl.xaml | 10 +-
.../UI/Logs/AdminLogsControl.xaml.cs | 85 +-
.../Administration/UI/Logs/AdminLogsEui.cs | 2 +-
.../UI/Logs/Entries/AdminLogEntry.xaml | 14 -
.../UI/Logs/Entries/AdminLogEntry.xaml.cs | 79 -
.../UI/Logs/Entries/AdminLogEntryDetails.xaml | 44 -
.../Logs/Entries/AdminLogEntryDetails.xaml.cs | 98 -
.../UI/Tabs/PlayerTab/PlayerTab.xaml.cs | 2 +
.../Options/UI/Tabs/AdminOptionsTab.xaml | 3 -
.../Options/UI/Tabs/AdminOptionsTab.xaml.cs | 2 -
...0250723055137_AdminLogsCurtime.Designer.cs | 2125 -----------------
.../20250723055137_AdminLogsCurtime.cs | 29 -
.../PostgresServerDbContextModelSnapshot.cs | 7 +-
...0250723055127_AdminLogsCurtime.Designer.cs | 2048 ----------------
.../Sqlite/20250723055127_AdminLogsCurtime.cs | 29 -
.../SqliteServerDbContextModelSnapshot.cs | 4 -
Content.Server.Database/Model.cs | 5 -
.../Logs/AdminLogManager.Cache.cs | 2 +-
.../Administration/Logs/AdminLogManager.cs | 3 -
Content.Server/Database/ServerDbBase.cs | 2 +-
.../Administration/Logs/SharedAdminLog.cs | 1 -
Content.Shared/CCVar/CCVars.Interface.cs | 6 -
.../en-US/administration/ui/admin-logs.ftl | 15 -
.../en-US/escape-menu/ui/options-menu.ftl | 4 -
27 files changed, 65 insertions(+), 4606 deletions(-)
create mode 100644 Content.Client/Administration/UI/CustomControls/AdminLogLabel.cs
delete mode 100644 Content.Client/Administration/UI/Logs/Entries/AdminLogEntry.xaml
delete mode 100644 Content.Client/Administration/UI/Logs/Entries/AdminLogEntry.xaml.cs
delete mode 100644 Content.Client/Administration/UI/Logs/Entries/AdminLogEntryDetails.xaml
delete mode 100644 Content.Client/Administration/UI/Logs/Entries/AdminLogEntryDetails.xaml.cs
delete mode 100644 Content.Server.Database/Migrations/Postgres/20250723055137_AdminLogsCurtime.Designer.cs
delete mode 100644 Content.Server.Database/Migrations/Postgres/20250723055137_AdminLogsCurtime.cs
delete mode 100644 Content.Server.Database/Migrations/Sqlite/20250723055127_AdminLogsCurtime.Designer.cs
delete mode 100644 Content.Server.Database/Migrations/Sqlite/20250723055127_AdminLogsCurtime.cs
diff --git a/Content.Client/Administration/UI/CustomControls/AdminLogLabel.cs b/Content.Client/Administration/UI/CustomControls/AdminLogLabel.cs
new file mode 100644
index 0000000000..0de38ce234
--- /dev/null
+++ b/Content.Client/Administration/UI/CustomControls/AdminLogLabel.cs
@@ -0,0 +1,33 @@
+using Content.Shared.Administration.Logs;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+
+namespace Content.Client.Administration.UI.CustomControls;
+
+public sealed class AdminLogLabel : RichTextLabel
+{
+ public AdminLogLabel(ref SharedAdminLog log, HSeparator separator)
+ {
+ Log = log;
+ Separator = separator;
+
+ SetMessage($"{log.Date:HH:mm:ss}: {log.Message}");
+ OnVisibilityChanged += VisibilityChanged;
+ }
+
+ public SharedAdminLog Log { get; }
+
+ public HSeparator Separator { get; }
+
+ private void VisibilityChanged(Control control)
+ {
+ Separator.Visible = Visible;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+
+ OnVisibilityChanged -= VisibilityChanged;
+ }
+}
diff --git a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs
index 8027a00c54..c7fbf6c2dc 100644
--- a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs
+++ b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs
@@ -1,15 +1,16 @@
using System.Linq;
-using System.Text.RegularExpressions;
using Content.Client.Administration.Systems;
using Content.Client.UserInterface.Controls;
using Content.Client.Verbs.UI;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
+using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Input;
+using Robust.Shared.Utility;
namespace Content.Client.Administration.UI.CustomControls;
@@ -95,26 +96,13 @@ public sealed partial class PlayerListControl : BoxContainer
private void FilterList()
{
_sortedPlayerList.Clear();
-
- Regex filterRegex;
- // There is no neat way to handle invalid regex being submitted other than
- // catching and ignoring the exception which gets thrown when it's invalid.
- try
- {
- filterRegex = new Regex(FilterLineEdit.Text, RegexOptions.IgnoreCase);
- }
- catch (ArgumentException)
- {
- return;
- }
-
foreach (var info in _playerList)
{
var displayName = $"{info.CharacterName} ({info.Username})";
if (info.IdentityName != info.CharacterName)
displayName += $" [{info.IdentityName}]";
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
- && !filterRegex.IsMatch(displayName))
+ && !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
continue;
_sortedPlayerList.Add(info);
}
diff --git a/Content.Client/Administration/UI/CustomControls/PlayerListEntry.xaml.cs b/Content.Client/Administration/UI/CustomControls/PlayerListEntry.xaml.cs
index 00e4d06044..cf2e776ea8 100644
--- a/Content.Client/Administration/UI/CustomControls/PlayerListEntry.xaml.cs
+++ b/Content.Client/Administration/UI/CustomControls/PlayerListEntry.xaml.cs
@@ -1,6 +1,7 @@
using Content.Client.Stylesheets;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
+using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
diff --git a/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml b/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml
index 86b72d9392..f92a307de3 100644
--- a/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml
+++ b/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml
@@ -1,6 +1,5 @@
+ xmlns:aui="clr-namespace:Content.Client.Administration.UI.CustomControls">
@@ -58,13 +57,6 @@
-
-
-
-
-
diff --git a/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml.cs b/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml.cs
index 00b5e40e9a..e12d598733 100644
--- a/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml.cs
+++ b/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml.cs
@@ -1,8 +1,6 @@
using System.Linq;
using System.Runtime.InteropServices;
-using System.Text.RegularExpressions;
using Content.Client.Administration.UI.CustomControls;
-using Content.Client.Administration.UI.Logs.Entries;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Robust.Client.AutoGenerated;
@@ -40,9 +38,6 @@ public sealed partial class AdminLogsControl : Control
SelectAllPlayersButton.OnPressed += SelectAllPlayers;
SelectNoPlayersButton.OnPressed += SelectNoPlayers;
- RenderRichTextButton.OnPressed += RenderRichTextChanged;
- RemoveMarkupButton.OnPressed += RemoveMarkupChanged;
-
RoundSpinBox.IsValid = i => i > 0 && i <= CurrentRound;
RoundSpinBox.ValueChanged += RoundSpinBoxChanged;
RoundSpinBox.InitDefaultButtons();
@@ -63,8 +58,6 @@ public sealed partial class AdminLogsControl : Control
private int CurrentRound { get; set; }
- private Regex LogSearchRegex { get; set; } = new("");
-
public int SelectedRoundId => RoundSpinBox.Value;
public string Search => LogSearch.Text;
private int ShownLogs { get; set; }
@@ -84,8 +77,6 @@ public sealed partial class AdminLogsControl : Control
///
private List RawAdminLogs { get; set; } = new();
- private bool RenderRichText { get; set; }
- private bool RemoveMarkup { get; set; }
public HashSet SelectedTypes { get; } = new();
public HashSet SelectedPlayers { get; } = new();
@@ -132,19 +123,6 @@ public sealed partial class AdminLogsControl : Control
private void LogSearchChanged(LineEditEventArgs args)
{
- // This exception is thrown if the regex is invalid, which happens often, so we ignore it.
- try
- {
- LogSearchRegex = new Regex(
- "(" + LogSearch.Text + ")",
- RegexOptions.IgnoreCase,
- TimeSpan.FromSeconds(1));
- }
- catch (ArgumentException)
- {
- return;
- }
-
UpdateLogs();
}
@@ -226,26 +204,6 @@ public sealed partial class AdminLogsControl : Control
UpdateLogs();
}
- private void RenderRichTextChanged(ButtonEventArgs args)
- {
- RenderRichText = args.Button.Pressed;
-
- RemoveMarkup = RemoveMarkup && !RenderRichText;
- RemoveMarkupButton.Pressed = RemoveMarkup;
-
- UpdateLogs();
- }
-
- private void RemoveMarkupChanged(ButtonEventArgs args)
- {
- RemoveMarkup = args.Button.Pressed;
-
- RenderRichText = !RemoveMarkup && RenderRichText;
- RenderRichTextButton.Pressed = RenderRichText;
-
- UpdateLogs();
- }
-
public void SetTypesSelection(HashSet selectedTypes, bool invert = false)
{
SelectedTypes.Clear();
@@ -304,15 +262,16 @@ public sealed partial class AdminLogsControl : Control
foreach (var child in LogsContainer.Children)
{
- if (child is not AdminLogEntry log)
+ if (child is not AdminLogLabel log)
+ {
continue;
+ }
child.Visible = ShouldShowLog(log);
- if (!child.Visible)
- continue;
-
- log.RenderResults(LogSearchRegex, RenderRichText, RemoveMarkup);
- ShownLogs++;
+ if (child.Visible)
+ {
+ ShownLogs++;
+ }
}
UpdateCount();
@@ -330,30 +289,30 @@ public sealed partial class AdminLogsControl : Control
button.Text.Contains(PlayerSearch.Text, StringComparison.OrdinalIgnoreCase);
}
- private bool LogMatchesPlayerFilter(AdminLogEntry entry)
+ private bool LogMatchesPlayerFilter(AdminLogLabel label)
{
- if (entry.Log.Players.Length == 0)
+ if (label.Log.Players.Length == 0)
return SelectedPlayers.Count == 0 || IncludeNonPlayerLogs;
- return SelectedPlayers.Overlaps(entry.Log.Players);
+ return SelectedPlayers.Overlaps(label.Log.Players);
}
- private bool ShouldShowLog(AdminLogEntry entry)
+ private bool ShouldShowLog(AdminLogLabel label)
{
// Check log type
- if (!SelectedTypes.Contains(entry.Log.Type))
+ if (!SelectedTypes.Contains(label.Log.Type))
return false;
// Check players
- if (!LogMatchesPlayerFilter(entry))
+ if (!LogMatchesPlayerFilter(label))
return false;
// Check impact
- if (!SelectedImpacts.Contains(entry.Log.Impact))
+ if (!SelectedImpacts.Contains(label.Log.Impact))
return false;
// Check search
- if (!LogSearchRegex.IsMatch(entry.Log.Message))
+ if (!label.Log.Message.Contains(LogSearch.Text, StringComparison.OrdinalIgnoreCase))
return false;
return true;
@@ -427,16 +386,15 @@ public sealed partial class AdminLogsControl : Control
{
var log = tempLogs[i];
ref var logRef = ref log; // It didn't like me doing this as one line lmao
- var entry = new AdminLogEntry(ref log);
- //var label = new AdminLogLabel(ref logRef, separator);
- //label.Visible = ShouldShowLog(label);
+ var separator = new HSeparator();
+ var label = new AdminLogLabel(ref logRef, separator);
+ label.Visible = ShouldShowLog(label);
- TotalLogs++;
- //if (label.Visible) ShownLogs++;
+ if (label.Visible) ShownLogs++;
- //LogsContainer.AddChild(label);
- LogsContainer.AddChild(entry);
+ LogsContainer.AddChild(label);
+ LogsContainer.AddChild(separator);
}
UpdateLogs();
}
@@ -636,7 +594,6 @@ public sealed partial class AdminLogsControl : Control
SelectAllTypesButton.OnPressed -= SelectAllTypes;
SelectNoTypesButton.OnPressed -= SelectNoTypes;
- IncludeNonPlayersButton.OnPressed -= IncludeNonPlayers;
IncludeNonPlayersButton.OnPressed -= IncludeNonPlayers;
SelectAllPlayersButton.OnPressed -= SelectAllPlayers;
SelectNoPlayersButton.OnPressed -= SelectNoPlayers;
diff --git a/Content.Client/Administration/UI/Logs/AdminLogsEui.cs b/Content.Client/Administration/UI/Logs/AdminLogsEui.cs
index 52364111c0..28aca23f75 100644
--- a/Content.Client/Administration/UI/Logs/AdminLogsEui.cs
+++ b/Content.Client/Administration/UI/Logs/AdminLogsEui.cs
@@ -1,6 +1,6 @@
using System.IO;
using System.Linq;
-using Content.Client.Administration.UI.Logs.Entries;
+using Content.Client.Administration.UI.CustomControls;
using Content.Client.Eui;
using Content.Shared.Administration.Logs;
using Content.Shared.Eui;
diff --git a/Content.Client/Administration/UI/Logs/Entries/AdminLogEntry.xaml b/Content.Client/Administration/UI/Logs/Entries/AdminLogEntry.xaml
deleted file mode 100644
index 7733c2b1cb..0000000000
--- a/Content.Client/Administration/UI/Logs/Entries/AdminLogEntry.xaml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Content.Client/Administration/UI/Logs/Entries/AdminLogEntry.xaml.cs b/Content.Client/Administration/UI/Logs/Entries/AdminLogEntry.xaml.cs
deleted file mode 100644
index fc9f960b66..0000000000
--- a/Content.Client/Administration/UI/Logs/Entries/AdminLogEntry.xaml.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-using System.Text.RegularExpressions;
-using Content.Client.Message;
-using Content.Shared.Administration.Logs;
-using Content.Shared.CCVar;
-using Robust.Client.AutoGenerated;
-using Robust.Client.UserInterface.Controls;
-using Robust.Client.UserInterface.XAML;
-using Robust.Shared.Configuration;
-using Robust.Shared.Utility;
-
-namespace Content.Client.Administration.UI.Logs.Entries;
-
-[GenerateTypedNameReferences]
-public sealed partial class AdminLogEntry : BoxContainer
-{
- private readonly IConfigurationManager _cfgManager;
-
- public SharedAdminLog Log { get; }
-
- private readonly string _rawMessage;
-
- public AdminLogEntry(ref SharedAdminLog log)
- {
- _cfgManager = IoCManager.Resolve();
-
- RobustXamlLoader.Load(this);
-
- Log = log;
-
- _rawMessage = $"{log.Date:HH:mm:ss}: {log.Message}";
- Message.SetMessage(_rawMessage);
-
- DetailsHeading.OnToggled += DetailsToggled;
- }
-
- ///
- /// Sets text to be highlighted from a search result, and renders rich text, or removes all rich text markup.
- ///
- public void RenderResults(Regex highlightRegex, bool renderRichText, bool removeMarkup)
- {
- var color = _cfgManager.GetCVar(CCVars.AdminLogsHighlightColor);
- var formattedMessage = renderRichText
- ? _rawMessage
- : removeMarkup
- ? FormattedMessage.RemoveMarkupPermissive(_rawMessage)
- : FormattedMessage.EscapeText(_rawMessage);
-
- // Want to avoid highlighting smaller strings
- if (highlightRegex.ToString().Length > 4)
- {
- try
- {
- formattedMessage = highlightRegex.Replace(formattedMessage, $"[color={color}]$1[/color]", 3);
- }
- catch (RegexMatchTimeoutException)
- {
- // if we time out then don't bother highlighting results
- }
- }
-
- if (!FormattedMessage.TryFromMarkup(formattedMessage, out var outputMessage))
- return;
-
- Message.SetMessage(outputMessage);
- }
-
- ///
- /// We perform some extra calculations in the dropdown, so we want to render that only when
- /// the dropdown is actually opened.
- /// This also removes itself from the event listener so it doesn't trigger again.
- ///
- private void DetailsToggled(BaseButton.ButtonToggledEventArgs args)
- {
- if (!args.Pressed || DetailsBody.ChildCount > 0)
- return;
- DetailsBody.AddChild(new AdminLogEntryDetails(Log));
- DetailsHeading.OnToggled -= DetailsToggled;
- }
-}
diff --git a/Content.Client/Administration/UI/Logs/Entries/AdminLogEntryDetails.xaml b/Content.Client/Administration/UI/Logs/Entries/AdminLogEntryDetails.xaml
deleted file mode 100644
index 23391046b9..0000000000
--- a/Content.Client/Administration/UI/Logs/Entries/AdminLogEntryDetails.xaml
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Content.Client/Administration/UI/Logs/Entries/AdminLogEntryDetails.xaml.cs b/Content.Client/Administration/UI/Logs/Entries/AdminLogEntryDetails.xaml.cs
deleted file mode 100644
index 611626e544..0000000000
--- a/Content.Client/Administration/UI/Logs/Entries/AdminLogEntryDetails.xaml.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-using System.Linq;
-using Content.Client.Administration.Systems;
-using Content.Client.Administration.UI.CustomControls;
-using Content.Client.UserInterface.Controls;
-using Content.Client.Verbs.UI;
-using Content.Shared.Administration.Logs;
-using Robust.Client.AutoGenerated;
-using Robust.Client.UserInterface;
-using Robust.Client.UserInterface.Controls;
-using Robust.Client.UserInterface.XAML;
-using Robust.Shared.Input;
-using Robust.Shared.Network;
-
-namespace Content.Client.Administration.UI.Logs.Entries;
-
-[GenerateTypedNameReferences]
-public sealed partial class AdminLogEntryDetails : BoxContainer
-{
- private readonly AdminSystem _adminSystem;
- private readonly IUserInterfaceManager _uiManager;
- private readonly IEntityManager _entManager;
-
- public AdminLogEntryDetails(SharedAdminLog log)
- {
- RobustXamlLoader.Load(this);
- _entManager = IoCManager.Resolve();
- _uiManager = IoCManager.Resolve();
- _adminSystem = _entManager.System();
-
- Type.Text = log.Type.ToString();
- Impact.Text = log.Impact.ToString();
-
- LocalTime.Text = $"{log.Date.ToLocalTime():HH:mm:ss}";
- UTCTime.Text = $"{log.Date:HH:mm:ss}";
- // TimeSpan and DateTime use different formatting string conventions for some completely logical reason
- // that mere mortals such as myself will never be able to understand.
- CurTime.Text = new TimeSpan(log.CurTime).ToString(@"hh\:mm\:ss");
-
- PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
- PlayerListContainer.GenerateItem += GenerateButton;
- PopulateList(log.Players);
- }
-
- private void PopulateList(Guid[] players)
- {
- if (players.Length == 0)
- return;
-
- if (_adminSystem.PlayerList is not { } allPlayers || allPlayers.Count == 0)
- return;
-
- var listData = new List();
- foreach (var playerGuid in players)
- {
- var netUserId = new NetUserId(playerGuid);
-
- // Linq here is fine since this runs in response to admin input in the UI and
- // this loop only tends to go through 1-4 iterations.
- if (allPlayers.FirstOrDefault(player => player.SessionId == netUserId) is not { } playerInfo)
- continue;
-
- listData.Add(new PlayerListData(playerInfo));
- }
-
- if (listData.Count == 0)
- return;
-
- PlayerListContainer.PopulateList(listData);
- }
-
- private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
- {
- if (args == null || data is not PlayerListData { Info: var selectedPlayer })
- return;
-
- if (!(args.Function == EngineKeyFunctions.UIRightClick
- || args.Function == EngineKeyFunctions.UIClick)
- || selectedPlayer.NetEntity == null)
- return;
-
- _uiManager.GetUIController().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
- args.Handle();
- }
-
- private void GenerateButton(ListData data, ListContainerButton button)
- {
- if (data is not PlayerListData { Info: var info })
- return;
-
- var entryLabel = new Label();
- entryLabel.Text = $"{info.CharacterName} ({info.Username})";
- var entry = new BoxContainer();
- entry.AddChild(entryLabel);
-
- button.AddChild(entry);
- button.AddStyleClass(ListContainer.StyleClassListContainerButton);
- }
-}
diff --git a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs
index 371b9952e4..d999df6d98 100644
--- a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs
+++ b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs
@@ -56,6 +56,7 @@ public sealed partial class PlayerTab : Control
_config.OnValueChanged(CCVars.AdminPlayerTabMarkGhosted, MarkGhostedChanged, true); // DeltaV
_config.OnValueChanged(CCVars.AdminPlayerTabMarkWatchlisted, MarkWatchlistedChanged, true); // DeltaV
+
OverlayButton.OnPressed += OverlayButtonPressed;
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
@@ -68,6 +69,7 @@ public sealed partial class PlayerTab : Control
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
RefreshPlayerList(_adminSystem.PlayerList);
+
}
// DeltaV - mark ghosted, watchlisted START
diff --git a/Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml b/Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml
index 53e6e29cee..dfa9a89b77 100644
--- a/Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml
+++ b/Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml
@@ -11,9 +11,6 @@
-
-
diff --git a/Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml.cs b/Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml.cs
index 1b6ef25f87..f6e55c4ddf 100644
--- a/Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml.cs
+++ b/Content.Client/Options/UI/Tabs/AdminOptionsTab.xaml.cs
@@ -51,8 +51,6 @@ public sealed partial class AdminOptionsTab : Control
playerTabSymbolSettings.Add(new OptionDropDownCVar.ValueOption(setting.ToString()!, Loc.GetString($"ui-options-admin-player-tab-symbol-setting-{setting.ToString()!.ToLower()}")));
}
- Control.AddOptionColorSlider(CCVars.AdminLogsHighlightColor, ColorSliderLogsHighlight);
-
Control.AddOptionDropDown(CCVars.AdminPlayerTabSymbolSetting, DropDownPlayerTabSymbolSetting, playerTabSymbolSettings);
Control.AddOptionDropDown(CCVars.AdminPlayerTabRoleSetting, DropDownPlayerTabRoleSetting, playerTabRoleSettings);
Control.AddOptionDropDown(CCVars.AdminPlayerTabColorSetting, DropDownPlayerTabColorSetting, playerTabColorSettings);
diff --git a/Content.Server.Database/Migrations/Postgres/20250723055137_AdminLogsCurtime.Designer.cs b/Content.Server.Database/Migrations/Postgres/20250723055137_AdminLogsCurtime.Designer.cs
deleted file mode 100644
index ea06967c89..0000000000
--- a/Content.Server.Database/Migrations/Postgres/20250723055137_AdminLogsCurtime.Designer.cs
+++ /dev/null
@@ -1,2125 +0,0 @@
-//
-using System;
-using System.Collections.Generic;
-using System.Net;
-using System.Text.Json;
-using Content.Server.Database;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
-using NpgsqlTypes;
-
-#nullable disable
-
-namespace Content.Server.Database.Migrations.Postgres
-{
- [DbContext(typeof(PostgresServerDbContext))]
- [Migration("20250723055137_AdminLogsCurtime")]
- partial class AdminLogsCurtime
- {
- ///
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder
- .HasAnnotation("ProductVersion", "9.0.1")
- .HasAnnotation("Relational:MaxIdentifierLength", 63);
-
- NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
-
- modelBuilder.Entity("Content.Server.Database.Admin", b =>
- {
- b.Property("UserId")
- .ValueGeneratedOnAdd()
- .HasColumnType("uuid")
- .HasColumnName("user_id");
-
- b.Property("AdminRankId")
- .HasColumnType("integer")
- .HasColumnName("admin_rank_id");
-
- b.Property("Deadminned")
- .HasColumnType("boolean")
- .HasColumnName("deadminned");
-
- b.Property("Suspended")
- .HasColumnType("boolean")
- .HasColumnName("suspended");
-
- b.Property("Title")
- .HasColumnType("text")
- .HasColumnName("title");
-
- b.HasKey("UserId")
- .HasName("PK_admin");
-
- b.HasIndex("AdminRankId")
- .HasDatabaseName("IX_admin_admin_rank_id");
-
- b.ToTable("admin", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminFlag", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("admin_flag_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("AdminId")
- .HasColumnType("uuid")
- .HasColumnName("admin_id");
-
- b.Property("Flag")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("flag");
-
- b.Property("Negative")
- .HasColumnType("boolean")
- .HasColumnName("negative");
-
- b.HasKey("Id")
- .HasName("PK_admin_flag");
-
- b.HasIndex("AdminId")
- .HasDatabaseName("IX_admin_flag_admin_id");
-
- b.HasIndex("Flag", "AdminId")
- .IsUnique();
-
- b.ToTable("admin_flag", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminLog", b =>
- {
- b.Property("RoundId")
- .HasColumnType("integer")
- .HasColumnName("round_id");
-
- b.Property("Id")
- .HasColumnType("integer")
- .HasColumnName("admin_log_id");
-
- b.Property("CurTime")
- .HasColumnType("bigint")
- .HasColumnName("cur_time");
-
- b.Property("Date")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("date");
-
- b.Property("Impact")
- .HasColumnType("smallint")
- .HasColumnName("impact");
-
- b.Property("Json")
- .IsRequired()
- .HasColumnType("jsonb")
- .HasColumnName("json");
-
- b.Property("Message")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("message");
-
- b.Property("Type")
- .HasColumnType("integer")
- .HasColumnName("type");
-
- b.HasKey("RoundId", "Id")
- .HasName("PK_admin_log");
-
- b.HasIndex("Date");
-
- b.HasIndex("Message")
- .HasAnnotation("Npgsql:TsVectorConfig", "english");
-
- NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Message"), "GIN");
-
- b.HasIndex("Type")
- .HasDatabaseName("IX_admin_log_type");
-
- b.ToTable("admin_log", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b =>
- {
- b.Property("RoundId")
- .HasColumnType("integer")
- .HasColumnName("round_id");
-
- b.Property("LogId")
- .HasColumnType("integer")
- .HasColumnName("log_id");
-
- b.Property("PlayerUserId")
- .HasColumnType("uuid")
- .HasColumnName("player_user_id");
-
- b.HasKey("RoundId", "LogId", "PlayerUserId")
- .HasName("PK_admin_log_player");
-
- b.HasIndex("PlayerUserId")
- .HasDatabaseName("IX_admin_log_player_player_user_id");
-
- b.ToTable("admin_log_player", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminMessage", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("admin_messages_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("CreatedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("created_at");
-
- b.Property("CreatedById")
- .HasColumnType("uuid")
- .HasColumnName("created_by_id");
-
- b.Property("Deleted")
- .HasColumnType("boolean")
- .HasColumnName("deleted");
-
- b.Property("DeletedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("deleted_at");
-
- b.Property("DeletedById")
- .HasColumnType("uuid")
- .HasColumnName("deleted_by_id");
-
- b.Property("Dismissed")
- .HasColumnType("boolean")
- .HasColumnName("dismissed");
-
- b.Property("ExpirationTime")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("expiration_time");
-
- b.Property("LastEditedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("last_edited_at");
-
- b.Property("LastEditedById")
- .HasColumnType("uuid")
- .HasColumnName("last_edited_by_id");
-
- b.Property("Message")
- .IsRequired()
- .HasMaxLength(4096)
- .HasColumnType("character varying(4096)")
- .HasColumnName("message");
-
- b.Property("PlayerUserId")
- .HasColumnType("uuid")
- .HasColumnName("player_user_id");
-
- b.Property("PlaytimeAtNote")
- .HasColumnType("interval")
- .HasColumnName("playtime_at_note");
-
- b.Property("RoundId")
- .HasColumnType("integer")
- .HasColumnName("round_id");
-
- b.Property("Seen")
- .HasColumnType("boolean")
- .HasColumnName("seen");
-
- b.HasKey("Id")
- .HasName("PK_admin_messages");
-
- b.HasIndex("CreatedById");
-
- b.HasIndex("DeletedById");
-
- b.HasIndex("LastEditedById");
-
- b.HasIndex("PlayerUserId")
- .HasDatabaseName("IX_admin_messages_player_user_id");
-
- b.HasIndex("RoundId")
- .HasDatabaseName("IX_admin_messages_round_id");
-
- b.ToTable("admin_messages", null, t =>
- {
- t.HasCheckConstraint("NotDismissedAndSeen", "NOT dismissed OR seen");
- });
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminNote", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("admin_notes_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("CreatedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("created_at");
-
- b.Property("CreatedById")
- .HasColumnType("uuid")
- .HasColumnName("created_by_id");
-
- b.Property("Deleted")
- .HasColumnType("boolean")
- .HasColumnName("deleted");
-
- b.Property("DeletedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("deleted_at");
-
- b.Property("DeletedById")
- .HasColumnType("uuid")
- .HasColumnName("deleted_by_id");
-
- b.Property("ExpirationTime")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("expiration_time");
-
- b.Property("LastEditedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("last_edited_at");
-
- b.Property("LastEditedById")
- .HasColumnType("uuid")
- .HasColumnName("last_edited_by_id");
-
- b.Property("Message")
- .IsRequired()
- .HasMaxLength(4096)
- .HasColumnType("character varying(4096)")
- .HasColumnName("message");
-
- b.Property("PlayerUserId")
- .HasColumnType("uuid")
- .HasColumnName("player_user_id");
-
- b.Property("PlaytimeAtNote")
- .HasColumnType("interval")
- .HasColumnName("playtime_at_note");
-
- b.Property("RoundId")
- .HasColumnType("integer")
- .HasColumnName("round_id");
-
- b.Property("Secret")
- .HasColumnType("boolean")
- .HasColumnName("secret");
-
- b.Property("Severity")
- .HasColumnType("integer")
- .HasColumnName("severity");
-
- b.HasKey("Id")
- .HasName("PK_admin_notes");
-
- b.HasIndex("CreatedById");
-
- b.HasIndex("DeletedById");
-
- b.HasIndex("LastEditedById");
-
- b.HasIndex("PlayerUserId")
- .HasDatabaseName("IX_admin_notes_player_user_id");
-
- b.HasIndex("RoundId")
- .HasDatabaseName("IX_admin_notes_round_id");
-
- b.ToTable("admin_notes", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminRank", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("admin_rank_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("name");
-
- b.HasKey("Id")
- .HasName("PK_admin_rank");
-
- b.ToTable("admin_rank", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("admin_rank_flag_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("AdminRankId")
- .HasColumnType("integer")
- .HasColumnName("admin_rank_id");
-
- b.Property("Flag")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("flag");
-
- b.HasKey("Id")
- .HasName("PK_admin_rank_flag");
-
- b.HasIndex("AdminRankId");
-
- b.HasIndex("Flag", "AdminRankId")
- .IsUnique();
-
- b.ToTable("admin_rank_flag", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("admin_watchlists_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("CreatedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("created_at");
-
- b.Property("CreatedById")
- .HasColumnType("uuid")
- .HasColumnName("created_by_id");
-
- b.Property("Deleted")
- .HasColumnType("boolean")
- .HasColumnName("deleted");
-
- b.Property("DeletedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("deleted_at");
-
- b.Property("DeletedById")
- .HasColumnType("uuid")
- .HasColumnName("deleted_by_id");
-
- b.Property("ExpirationTime")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("expiration_time");
-
- b.Property("LastEditedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("last_edited_at");
-
- b.Property("LastEditedById")
- .HasColumnType("uuid")
- .HasColumnName("last_edited_by_id");
-
- b.Property("Message")
- .IsRequired()
- .HasMaxLength(4096)
- .HasColumnType("character varying(4096)")
- .HasColumnName("message");
-
- b.Property("PlayerUserId")
- .HasColumnType("uuid")
- .HasColumnName("player_user_id");
-
- b.Property("PlaytimeAtNote")
- .HasColumnType("interval")
- .HasColumnName("playtime_at_note");
-
- b.Property("RoundId")
- .HasColumnType("integer")
- .HasColumnName("round_id");
-
- b.HasKey("Id")
- .HasName("PK_admin_watchlists");
-
- b.HasIndex("CreatedById");
-
- b.HasIndex("DeletedById");
-
- b.HasIndex("LastEditedById");
-
- b.HasIndex("PlayerUserId")
- .HasDatabaseName("IX_admin_watchlists_player_user_id");
-
- b.HasIndex("RoundId")
- .HasDatabaseName("IX_admin_watchlists_round_id");
-
- b.ToTable("admin_watchlists", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.Antag", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("antag_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("AntagName")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("antag_name");
-
- b.Property("ProfileId")
- .HasColumnType("integer")
- .HasColumnName("profile_id");
-
- b.HasKey("Id")
- .HasName("PK_antag");
-
- b.HasIndex("ProfileId", "AntagName")
- .IsUnique();
-
- b.ToTable("antag", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.AssignedUserId", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("assigned_user_id_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("UserId")
- .HasColumnType("uuid")
- .HasColumnName("user_id");
-
- b.Property("UserName")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("user_name");
-
- b.HasKey("Id")
- .HasName("PK_assigned_user_id");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.HasIndex("UserName")
- .IsUnique();
-
- b.ToTable("assigned_user_id", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.BanTemplate", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("ban_template_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("AutoDelete")
- .HasColumnType("boolean")
- .HasColumnName("auto_delete");
-
- b.Property("ExemptFlags")
- .HasColumnType("integer")
- .HasColumnName("exempt_flags");
-
- b.Property("Hidden")
- .HasColumnType("boolean")
- .HasColumnName("hidden");
-
- b.Property("Length")
- .HasColumnType("interval")
- .HasColumnName("length");
-
- b.Property("Reason")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("reason");
-
- b.Property("Severity")
- .HasColumnType("integer")
- .HasColumnName("severity");
-
- b.Property("Title")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("title");
-
- b.HasKey("Id")
- .HasName("PK_ban_template");
-
- b.ToTable("ban_template", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.Blacklist", b =>
- {
- b.Property("UserId")
- .ValueGeneratedOnAdd()
- .HasColumnType("uuid")
- .HasColumnName("user_id");
-
- b.HasKey("UserId")
- .HasName("PK_blacklist");
-
- b.ToTable("blacklist", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("connection_log_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("Address")
- .IsRequired()
- .HasColumnType("inet")
- .HasColumnName("address");
-
- b.Property("Denied")
- .HasColumnType("smallint")
- .HasColumnName("denied");
-
- b.Property("ServerId")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasDefaultValue(0)
- .HasColumnName("server_id");
-
- b.Property("Time")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("time");
-
- b.Property("Trust")
- .HasColumnType("real")
- .HasColumnName("trust");
-
- b.Property("UserId")
- .HasColumnType("uuid")
- .HasColumnName("user_id");
-
- b.Property("UserName")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("user_name");
-
- b.HasKey("Id")
- .HasName("PK_connection_log");
-
- b.HasIndex("ServerId")
- .HasDatabaseName("IX_connection_log_server_id");
-
- b.HasIndex("Time");
-
- b.HasIndex("UserId");
-
- b.ToTable("connection_log", null, t =>
- {
- t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
- });
- });
-
- modelBuilder.Entity("Content.Server.Database.IPIntelCache", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("ipintel_cache_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("Address")
- .IsRequired()
- .HasColumnType("inet")
- .HasColumnName("address");
-
- b.Property("Score")
- .HasColumnType("real")
- .HasColumnName("score");
-
- b.Property("Time")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("time");
-
- b.HasKey("Id")
- .HasName("PK_ipintel_cache");
-
- b.ToTable("ipintel_cache", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.Job", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("job_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("JobName")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("job_name");
-
- b.Property("Priority")
- .HasColumnType("integer")
- .HasColumnName("priority");
-
- b.Property("ProfileId")
- .HasColumnType("integer")
- .HasColumnName("profile_id");
-
- b.HasKey("Id")
- .HasName("PK_job");
-
- b.HasIndex("ProfileId");
-
- b.HasIndex("ProfileId", "JobName")
- .IsUnique();
-
- b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority")
- .IsUnique()
- .HasFilter("priority = 3");
-
- b.ToTable("job", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.PlayTime", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("play_time_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("PlayerId")
- .HasColumnType("uuid")
- .HasColumnName("player_id");
-
- b.Property("TimeSpent")
- .HasColumnType("interval")
- .HasColumnName("time_spent");
-
- b.Property("Tracker")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("tracker");
-
- b.HasKey("Id")
- .HasName("PK_play_time");
-
- b.HasIndex("PlayerId", "Tracker")
- .IsUnique();
-
- b.ToTable("play_time", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.Player", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("player_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("FirstSeenTime")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("first_seen_time");
-
- b.Property("LastReadRules")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("last_read_rules");
-
- b.Property("LastSeenAddress")
- .IsRequired()
- .HasColumnType("inet")
- .HasColumnName("last_seen_address");
-
- b.Property("LastSeenTime")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("last_seen_time");
-
- b.Property("LastSeenUserName")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("last_seen_user_name");
-
- b.Property("UserId")
- .HasColumnType("uuid")
- .HasColumnName("user_id");
-
- b.HasKey("Id")
- .HasName("PK_player");
-
- b.HasAlternateKey("UserId")
- .HasName("ak_player_user_id");
-
- b.HasIndex("LastSeenUserName");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("player", null, t =>
- {
- t.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address");
- });
- });
-
- modelBuilder.Entity("Content.Server.Database.Preference", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("preference_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("AdminOOCColor")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("admin_ooc_color");
-
- b.PrimitiveCollection>("ConstructionFavorites")
- .IsRequired()
- .HasColumnType("text[]")
- .HasColumnName("construction_favorites");
-
- b.Property("SelectedCharacterSlot")
- .HasColumnType("integer")
- .HasColumnName("selected_character_slot");
-
- b.Property("UserId")
- .HasColumnType("uuid")
- .HasColumnName("user_id");
-
- b.HasKey("Id")
- .HasName("PK_preference");
-
- b.HasIndex("UserId")
- .IsUnique();
-
- b.ToTable("preference", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.Profile", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("profile_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("Age")
- .HasColumnType("integer")
- .HasColumnName("age");
-
- b.Property("CharacterName")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("char_name");
-
- b.Property("EyeColor")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("eye_color");
-
- b.Property("FacialHairColor")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("facial_hair_color");
-
- b.Property("FacialHairName")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("facial_hair_name");
-
- b.Property("FlavorText")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("flavor_text");
-
- b.Property("Gender")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("gender");
-
- b.Property("HairColor")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("hair_color");
-
- b.Property("HairName")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("hair_name");
-
- b.Property("Markings")
- .HasColumnType("jsonb")
- .HasColumnName("markings");
-
- b.Property("PreferenceId")
- .HasColumnType("integer")
- .HasColumnName("preference_id");
-
- b.Property("PreferenceUnavailable")
- .HasColumnType("integer")
- .HasColumnName("pref_unavailable");
-
- b.Property("Sex")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("sex");
-
- b.Property("SkinColor")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("skin_color");
-
- b.Property("Slot")
- .HasColumnType("integer")
- .HasColumnName("slot");
-
- b.Property("SpawnPriority")
- .HasColumnType("integer")
- .HasColumnName("spawn_priority");
-
- b.Property("Species")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("species");
-
- b.HasKey("Id")
- .HasName("PK_profile");
-
- b.HasIndex("PreferenceId")
- .HasDatabaseName("IX_profile_preference_id");
-
- b.HasIndex("Slot", "PreferenceId")
- .IsUnique();
-
- b.ToTable("profile", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("profile_loadout_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("LoadoutName")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("loadout_name");
-
- b.Property("ProfileLoadoutGroupId")
- .HasColumnType("integer")
- .HasColumnName("profile_loadout_group_id");
-
- b.HasKey("Id")
- .HasName("PK_profile_loadout");
-
- b.HasIndex("ProfileLoadoutGroupId");
-
- b.ToTable("profile_loadout", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("profile_loadout_group_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("GroupName")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("group_name");
-
- b.Property("ProfileRoleLoadoutId")
- .HasColumnType("integer")
- .HasColumnName("profile_role_loadout_id");
-
- b.HasKey("Id")
- .HasName("PK_profile_loadout_group");
-
- b.HasIndex("ProfileRoleLoadoutId");
-
- b.ToTable("profile_loadout_group", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("profile_role_loadout_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("EntityName")
- .HasMaxLength(256)
- .HasColumnType("character varying(256)")
- .HasColumnName("entity_name");
-
- b.Property("ProfileId")
- .HasColumnType("integer")
- .HasColumnName("profile_id");
-
- b.Property("RoleName")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("role_name");
-
- b.HasKey("Id")
- .HasName("PK_profile_role_loadout");
-
- b.HasIndex("ProfileId");
-
- b.ToTable("profile_role_loadout", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b =>
- {
- b.Property("PlayerUserId")
- .HasColumnType("uuid")
- .HasColumnName("player_user_id");
-
- b.Property("RoleId")
- .HasColumnType("text")
- .HasColumnName("role_id");
-
- b.HasKey("PlayerUserId", "RoleId")
- .HasName("PK_role_whitelists");
-
- b.ToTable("role_whitelists", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.Round", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("round_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("ServerId")
- .HasColumnType("integer")
- .HasColumnName("server_id");
-
- b.Property("StartDate")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("start_date");
-
- b.HasKey("Id")
- .HasName("PK_round");
-
- b.HasIndex("ServerId")
- .HasDatabaseName("IX_round_server_id");
-
- b.HasIndex("StartDate");
-
- b.ToTable("round", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.Server", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("server_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("name");
-
- b.HasKey("Id")
- .HasName("PK_server");
-
- b.ToTable("server", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.ServerBan", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("server_ban_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("Address")
- .HasColumnType("inet")
- .HasColumnName("address");
-
- b.Property("AutoDelete")
- .HasColumnType("boolean")
- .HasColumnName("auto_delete");
-
- b.Property("BanTime")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("ban_time");
-
- b.Property("BanningAdmin")
- .HasColumnType("uuid")
- .HasColumnName("banning_admin");
-
- b.Property("ExemptFlags")
- .HasColumnType("integer")
- .HasColumnName("exempt_flags");
-
- b.Property("ExpirationTime")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("expiration_time");
-
- b.Property("Hidden")
- .HasColumnType("boolean")
- .HasColumnName("hidden");
-
- b.Property("LastEditedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("last_edited_at");
-
- b.Property("LastEditedById")
- .HasColumnType("uuid")
- .HasColumnName("last_edited_by_id");
-
- b.Property("PlayerUserId")
- .HasColumnType("uuid")
- .HasColumnName("player_user_id");
-
- b.Property("PlaytimeAtNote")
- .HasColumnType("interval")
- .HasColumnName("playtime_at_note");
-
- b.Property("Reason")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("reason");
-
- b.Property("RoundId")
- .HasColumnType("integer")
- .HasColumnName("round_id");
-
- b.Property("Severity")
- .HasColumnType("integer")
- .HasColumnName("severity");
-
- b.HasKey("Id")
- .HasName("PK_server_ban");
-
- b.HasIndex("Address");
-
- b.HasIndex("BanningAdmin");
-
- b.HasIndex("LastEditedById");
-
- b.HasIndex("PlayerUserId")
- .HasDatabaseName("IX_server_ban_player_user_id");
-
- b.HasIndex("RoundId")
- .HasDatabaseName("IX_server_ban_round_id");
-
- b.ToTable("server_ban", null, t =>
- {
- t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
-
- t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL");
- });
- });
-
- modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b =>
- {
- b.Property("UserId")
- .ValueGeneratedOnAdd()
- .HasColumnType("uuid")
- .HasColumnName("user_id");
-
- b.Property("Flags")
- .HasColumnType("integer")
- .HasColumnName("flags");
-
- b.HasKey("UserId")
- .HasName("PK_server_ban_exemption");
-
- b.ToTable("server_ban_exemption", null, t =>
- {
- t.HasCheckConstraint("FlagsNotZero", "flags != 0");
- });
- });
-
- modelBuilder.Entity("Content.Server.Database.ServerBanHit", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("server_ban_hit_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("BanId")
- .HasColumnType("integer")
- .HasColumnName("ban_id");
-
- b.Property("ConnectionId")
- .HasColumnType("integer")
- .HasColumnName("connection_id");
-
- b.HasKey("Id")
- .HasName("PK_server_ban_hit");
-
- b.HasIndex("BanId")
- .HasDatabaseName("IX_server_ban_hit_ban_id");
-
- b.HasIndex("ConnectionId")
- .HasDatabaseName("IX_server_ban_hit_connection_id");
-
- b.ToTable("server_ban_hit", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("server_role_ban_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("Address")
- .HasColumnType("inet")
- .HasColumnName("address");
-
- b.Property("BanTime")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("ban_time");
-
- b.Property("BanningAdmin")
- .HasColumnType("uuid")
- .HasColumnName("banning_admin");
-
- b.Property("ExpirationTime")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("expiration_time");
-
- b.Property("Hidden")
- .HasColumnType("boolean")
- .HasColumnName("hidden");
-
- b.Property("LastEditedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("last_edited_at");
-
- b.Property("LastEditedById")
- .HasColumnType("uuid")
- .HasColumnName("last_edited_by_id");
-
- b.Property("PlayerUserId")
- .HasColumnType("uuid")
- .HasColumnName("player_user_id");
-
- b.Property("PlaytimeAtNote")
- .HasColumnType("interval")
- .HasColumnName("playtime_at_note");
-
- b.Property("Reason")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("reason");
-
- b.Property("RoleId")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("role_id");
-
- b.Property("RoundId")
- .HasColumnType("integer")
- .HasColumnName("round_id");
-
- b.Property("Severity")
- .HasColumnType("integer")
- .HasColumnName("severity");
-
- b.HasKey("Id")
- .HasName("PK_server_role_ban");
-
- b.HasIndex("Address");
-
- b.HasIndex("BanningAdmin");
-
- b.HasIndex("LastEditedById");
-
- b.HasIndex("PlayerUserId")
- .HasDatabaseName("IX_server_role_ban_player_user_id");
-
- b.HasIndex("RoundId")
- .HasDatabaseName("IX_server_role_ban_round_id");
-
- b.ToTable("server_role_ban", null, t =>
- {
- t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
-
- t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL");
- });
- });
-
- modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("role_unban_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("BanId")
- .HasColumnType("integer")
- .HasColumnName("ban_id");
-
- b.Property("UnbanTime")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("unban_time");
-
- b.Property("UnbanningAdmin")
- .HasColumnType("uuid")
- .HasColumnName("unbanning_admin");
-
- b.HasKey("Id")
- .HasName("PK_server_role_unban");
-
- b.HasIndex("BanId")
- .IsUnique();
-
- b.ToTable("server_role_unban", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.ServerUnban", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("unban_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("BanId")
- .HasColumnType("integer")
- .HasColumnName("ban_id");
-
- b.Property("UnbanTime")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("unban_time");
-
- b.Property("UnbanningAdmin")
- .HasColumnType("uuid")
- .HasColumnName("unbanning_admin");
-
- b.HasKey("Id")
- .HasName("PK_server_unban");
-
- b.HasIndex("BanId")
- .IsUnique();
-
- b.ToTable("server_unban", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.Trait", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("trait_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("ProfileId")
- .HasColumnType("integer")
- .HasColumnName("profile_id");
-
- b.Property("TraitName")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("trait_name");
-
- b.HasKey("Id")
- .HasName("PK_trait");
-
- b.HasIndex("ProfileId", "TraitName")
- .IsUnique();
-
- b.ToTable("trait", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasColumnName("uploaded_resource_log_id");
-
- NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
-
- b.Property("Data")
- .IsRequired()
- .HasColumnType("bytea")
- .HasColumnName("data");
-
- b.Property("Date")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("date");
-
- b.Property("Path")
- .IsRequired()
- .HasColumnType("text")
- .HasColumnName("path");
-
- b.Property("UserId")
- .HasColumnType("uuid")
- .HasColumnName("user_id");
-
- b.HasKey("Id")
- .HasName("PK_uploaded_resource_log");
-
- b.ToTable("uploaded_resource_log", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.Whitelist", b =>
- {
- b.Property("UserId")
- .ValueGeneratedOnAdd()
- .HasColumnType("uuid")
- .HasColumnName("user_id");
-
- b.HasKey("UserId")
- .HasName("PK_whitelist");
-
- b.ToTable("whitelist", (string)null);
- });
-
- modelBuilder.Entity("PlayerRound", b =>
- {
- b.Property("PlayersId")
- .HasColumnType("integer")
- .HasColumnName("players_id");
-
- b.Property("RoundsId")
- .HasColumnType("integer")
- .HasColumnName("rounds_id");
-
- b.HasKey("PlayersId", "RoundsId")
- .HasName("PK_player_round");
-
- b.HasIndex("RoundsId")
- .HasDatabaseName("IX_player_round_rounds_id");
-
- b.ToTable("player_round", (string)null);
- });
-
- modelBuilder.Entity("Content.Server.Database.Admin", b =>
- {
- b.HasOne("Content.Server.Database.AdminRank", "AdminRank")
- .WithMany("Admins")
- .HasForeignKey("AdminRankId")
- .OnDelete(DeleteBehavior.SetNull)
- .HasConstraintName("FK_admin_admin_rank_admin_rank_id");
-
- b.Navigation("AdminRank");
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminFlag", b =>
- {
- b.HasOne("Content.Server.Database.Admin", "Admin")
- .WithMany("Flags")
- .HasForeignKey("AdminId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired()
- .HasConstraintName("FK_admin_flag_admin_admin_id");
-
- b.Navigation("Admin");
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminLog", b =>
- {
- b.HasOne("Content.Server.Database.Round", "Round")
- .WithMany("AdminLogs")
- .HasForeignKey("RoundId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired()
- .HasConstraintName("FK_admin_log_round_round_id");
-
- b.Navigation("Round");
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b =>
- {
- b.HasOne("Content.Server.Database.Player", "Player")
- .WithMany("AdminLogs")
- .HasForeignKey("PlayerUserId")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired()
- .HasConstraintName("FK_admin_log_player_player_player_user_id");
-
- b.HasOne("Content.Server.Database.AdminLog", "Log")
- .WithMany("Players")
- .HasForeignKey("RoundId", "LogId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired()
- .HasConstraintName("FK_admin_log_player_admin_log_round_id_log_id");
-
- b.Navigation("Log");
-
- b.Navigation("Player");
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminMessage", b =>
- {
- b.HasOne("Content.Server.Database.Player", "CreatedBy")
- .WithMany("AdminMessagesCreated")
- .HasForeignKey("CreatedById")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.SetNull)
- .HasConstraintName("FK_admin_messages_player_created_by_id");
-
- b.HasOne("Content.Server.Database.Player", "DeletedBy")
- .WithMany("AdminMessagesDeleted")
- .HasForeignKey("DeletedById")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.SetNull)
- .HasConstraintName("FK_admin_messages_player_deleted_by_id");
-
- b.HasOne("Content.Server.Database.Player", "LastEditedBy")
- .WithMany("AdminMessagesLastEdited")
- .HasForeignKey("LastEditedById")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.SetNull)
- .HasConstraintName("FK_admin_messages_player_last_edited_by_id");
-
- b.HasOne("Content.Server.Database.Player", "Player")
- .WithMany("AdminMessagesReceived")
- .HasForeignKey("PlayerUserId")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .HasConstraintName("FK_admin_messages_player_player_user_id");
-
- b.HasOne("Content.Server.Database.Round", "Round")
- .WithMany()
- .HasForeignKey("RoundId")
- .HasConstraintName("FK_admin_messages_round_round_id");
-
- b.Navigation("CreatedBy");
-
- b.Navigation("DeletedBy");
-
- b.Navigation("LastEditedBy");
-
- b.Navigation("Player");
-
- b.Navigation("Round");
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminNote", b =>
- {
- b.HasOne("Content.Server.Database.Player", "CreatedBy")
- .WithMany("AdminNotesCreated")
- .HasForeignKey("CreatedById")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.SetNull)
- .HasConstraintName("FK_admin_notes_player_created_by_id");
-
- b.HasOne("Content.Server.Database.Player", "DeletedBy")
- .WithMany("AdminNotesDeleted")
- .HasForeignKey("DeletedById")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.SetNull)
- .HasConstraintName("FK_admin_notes_player_deleted_by_id");
-
- b.HasOne("Content.Server.Database.Player", "LastEditedBy")
- .WithMany("AdminNotesLastEdited")
- .HasForeignKey("LastEditedById")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.SetNull)
- .HasConstraintName("FK_admin_notes_player_last_edited_by_id");
-
- b.HasOne("Content.Server.Database.Player", "Player")
- .WithMany("AdminNotesReceived")
- .HasForeignKey("PlayerUserId")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .HasConstraintName("FK_admin_notes_player_player_user_id");
-
- b.HasOne("Content.Server.Database.Round", "Round")
- .WithMany()
- .HasForeignKey("RoundId")
- .HasConstraintName("FK_admin_notes_round_round_id");
-
- b.Navigation("CreatedBy");
-
- b.Navigation("DeletedBy");
-
- b.Navigation("LastEditedBy");
-
- b.Navigation("Player");
-
- b.Navigation("Round");
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b =>
- {
- b.HasOne("Content.Server.Database.AdminRank", "Rank")
- .WithMany("Flags")
- .HasForeignKey("AdminRankId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired()
- .HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id");
-
- b.Navigation("Rank");
- });
-
- modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b =>
- {
- b.HasOne("Content.Server.Database.Player", "CreatedBy")
- .WithMany("AdminWatchlistsCreated")
- .HasForeignKey("CreatedById")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.SetNull)
- .HasConstraintName("FK_admin_watchlists_player_created_by_id");
-
- b.HasOne("Content.Server.Database.Player", "DeletedBy")
- .WithMany("AdminWatchlistsDeleted")
- .HasForeignKey("DeletedById")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.SetNull)
- .HasConstraintName("FK_admin_watchlists_player_deleted_by_id");
-
- b.HasOne("Content.Server.Database.Player", "LastEditedBy")
- .WithMany("AdminWatchlistsLastEdited")
- .HasForeignKey("LastEditedById")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.SetNull)
- .HasConstraintName("FK_admin_watchlists_player_last_edited_by_id");
-
- b.HasOne("Content.Server.Database.Player", "Player")
- .WithMany("AdminWatchlistsReceived")
- .HasForeignKey("PlayerUserId")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .HasConstraintName("FK_admin_watchlists_player_player_user_id");
-
- b.HasOne("Content.Server.Database.Round", "Round")
- .WithMany()
- .HasForeignKey("RoundId")
- .HasConstraintName("FK_admin_watchlists_round_round_id");
-
- b.Navigation("CreatedBy");
-
- b.Navigation("DeletedBy");
-
- b.Navigation("LastEditedBy");
-
- b.Navigation("Player");
-
- b.Navigation("Round");
- });
-
- modelBuilder.Entity("Content.Server.Database.Antag", b =>
- {
- b.HasOne("Content.Server.Database.Profile", "Profile")
- .WithMany("Antags")
- .HasForeignKey("ProfileId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired()
- .HasConstraintName("FK_antag_profile_profile_id");
-
- b.Navigation("Profile");
- });
-
- modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
- {
- b.HasOne("Content.Server.Database.Server", "Server")
- .WithMany("ConnectionLogs")
- .HasForeignKey("ServerId")
- .OnDelete(DeleteBehavior.SetNull)
- .IsRequired()
- .HasConstraintName("FK_connection_log_server_server_id");
-
- b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 =>
- {
- b1.Property("ConnectionLogId")
- .HasColumnType("integer")
- .HasColumnName("connection_log_id");
-
- b1.Property("Hwid")
- .IsRequired()
- .HasColumnType("bytea")
- .HasColumnName("hwid");
-
- b1.Property("Type")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasDefaultValue(0)
- .HasColumnName("hwid_type");
-
- b1.HasKey("ConnectionLogId");
-
- b1.ToTable("connection_log");
-
- b1.WithOwner()
- .HasForeignKey("ConnectionLogId")
- .HasConstraintName("FK_connection_log_connection_log_connection_log_id");
- });
-
- b.Navigation("HWId");
-
- b.Navigation("Server");
- });
-
- modelBuilder.Entity("Content.Server.Database.Job", b =>
- {
- b.HasOne("Content.Server.Database.Profile", "Profile")
- .WithMany("Jobs")
- .HasForeignKey("ProfileId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired()
- .HasConstraintName("FK_job_profile_profile_id");
-
- b.Navigation("Profile");
- });
-
- modelBuilder.Entity("Content.Server.Database.Player", b =>
- {
- b.OwnsOne("Content.Server.Database.TypedHwid", "LastSeenHWId", b1 =>
- {
- b1.Property("PlayerId")
- .HasColumnType("integer")
- .HasColumnName("player_id");
-
- b1.Property("Hwid")
- .IsRequired()
- .HasColumnType("bytea")
- .HasColumnName("last_seen_hwid");
-
- b1.Property("Type")
- .ValueGeneratedOnAdd()
- .HasColumnType("integer")
- .HasDefaultValue(0)
- .HasColumnName("last_seen_hwid_type");
-
- b1.HasKey("PlayerId");
-
- b1.ToTable("player");
-
- b1.WithOwner()
- .HasForeignKey("PlayerId")
- .HasConstraintName("FK_player_player_player_id");
- });
-
- b.Navigation("LastSeenHWId");
- });
-
- modelBuilder.Entity("Content.Server.Database.Profile", b =>
- {
- b.HasOne("Content.Server.Database.Preference", "Preference")
- .WithMany("Profiles")
- .HasForeignKey("PreferenceId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired()
- .HasConstraintName("FK_profile_preference_preference_id");
-
- b.Navigation("Preference");
- });
-
- modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b =>
- {
- b.HasOne("Content.Server.Database.ProfileLoadoutGroup", "ProfileLoadoutGroup")
- .WithMany("Loadouts")
- .HasForeignKey("ProfileLoadoutGroupId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired()
- .HasConstraintName("FK_profile_loadout_profile_loadout_group_profile_loadout_group~");
-
- b.Navigation("ProfileLoadoutGroup");
- });
-
- modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b =>
- {
- b.HasOne("Content.Server.Database.ProfileRoleLoadout", "ProfileRoleLoadout")
- .WithMany("Groups")
- .HasForeignKey("ProfileRoleLoadoutId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired()
- .HasConstraintName("FK_profile_loadout_group_profile_role_loadout_profile_role_loa~");
-
- b.Navigation("ProfileRoleLoadout");
- });
-
- modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b =>
- {
- b.HasOne("Content.Server.Database.Profile", "Profile")
- .WithMany("Loadouts")
- .HasForeignKey("ProfileId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired()
- .HasConstraintName("FK_profile_role_loadout_profile_profile_id");
-
- b.Navigation("Profile");
- });
-
- modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b =>
- {
- b.HasOne("Content.Server.Database.Player", "Player")
- .WithMany("JobWhitelists")
- .HasForeignKey("PlayerUserId")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired()
- .HasConstraintName("FK_role_whitelists_player_player_user_id");
-
- b.Navigation("Player");
- });
-
- modelBuilder.Entity("Content.Server.Database.Round", b =>
- {
- b.HasOne("Content.Server.Database.Server", "Server")
- .WithMany("Rounds")
- .HasForeignKey("ServerId")
- .OnDelete(DeleteBehavior.Cascade)
- .IsRequired()
- .HasConstraintName("FK_round_server_server_id");
-
- b.Navigation("Server");
- });
-
- modelBuilder.Entity("Content.Server.Database.ServerBan", b =>
- {
- b.HasOne("Content.Server.Database.Player", "CreatedBy")
- .WithMany("AdminServerBansCreated")
- .HasForeignKey("BanningAdmin")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.SetNull)
- .HasConstraintName("FK_server_ban_player_banning_admin");
-
- b.HasOne("Content.Server.Database.Player", "LastEditedBy")
- .WithMany("AdminServerBansLastEdited")
- .HasForeignKey("LastEditedById")
- .HasPrincipalKey("UserId")
- .OnDelete(DeleteBehavior.SetNull)
- .HasConstraintName("FK_server_ban_player_last_edited_by_id");
-
- b.HasOne("Content.Server.Database.Round", "Round")
- .WithMany()
- .HasForeignKey("RoundId")
- .HasConstraintName("FK_server_ban_round_round_id");
-
- b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 =>
- {
- b1.Property