From 85acec95352490add20d9383bbfd1529ebd17860 Mon Sep 17 00:00:00 2001 From: Crude Oil <124208219+CroilBird@users.noreply.github.com> Date: Wed, 27 May 2026 21:32:21 +0200 Subject: [PATCH 001/287] [HOTFIX] Fix singularity and tesla friction (#44078) * Vehicle system: Movement & Buckling (#43525) * Part one of Vehicles * Also we need this * And we need this * We REALLY need this * Little fix * fixes (i'm tired) * Apply suggestions from code review Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> * update * fixed * just for cleaner diff * just in case * I think this might be cleaner * small fix --------- Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> * Fix ID inspect message when ending perma sentence early (#44018) * Automatic changelog update * Smarter Entity Targeting (#39571) * Smart targeting * use swidge statement * Undo * merge conflicts * works well * Better logic * GO TO SLEEP --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> * fix: don't delete items on some polymorph reverts (#39423) * Automatic changelog update * Fix error texture for harmpack hand solution visuals (#39442) * fix: check for inhand sprite's existence * refactor: simplify solution container visuals code this made my brain hurt but I think I didn't break anything * Automatic changelog update * fix role assignment when late joining (#41419) * fix role assignment when late joining * push --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> * Automatic changelog update * SSD Players now properly stay asleep when being revived. (#39409) * Fixes and API changes * aaaaaaaaaa * super shitcode * cleanup * fix admin logs --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> * Automatic changelog update * Fix Barber Scissors not playing audio (#43922) * Fix Barber Scissors not playing audio * Add code comment * Requested changes * More requested changes thanks Warock * Requested changes * Automatic changelog update * [MALD PR] Remove mimic vendor gamerule (#44057) remove mimic vender rule * Automatic changelog update * Destructible Automatic Overkill (#43113) * Change to file scoped namespace * Improve variable naming * Remove unnecessary audiosystem * Add automatic overkill * Small cleanups * Alphabetize dependencies * Change to file scoped namespace * Change to OnMapInit * Fix overkill if no destruction node present * Add overkill test * Example cleanup * Performance improvements * Tuple event subscriptions * Correct comment * Realphabetize I might be dyslexic. Or just stupid. * Invert toggle behavior and rename * Change from MapInit to ComponentInit * Revert "Change from MapInit to ComponentInit" This reverts commit 1ed9fa0ad49be00becd9adca5cfee45cf4fef3a2. * Change to use index instead of raw int * Explicit check not required * Clean up overkill test * Improve commenting * Overhaul the test * Little more * Improve commenting * Update DestructibleOverkillTest.cs * Typo * Change to tuple assignment * Alphabetize dependencies * Update Content.IntegrationTests/Tests/Destructible/DestructibleOverkillTest.cs * Update Content.IntegrationTests/Tests/Destructible/DestructibleOverkillTest.cs * Update Content.IntegrationTests/Tests/Destructible/DestructibleOverkillTest.cs --------- Co-authored-by: Tayrtahn * Update Credits (#44060) Co-authored-by: PJBot * fix off by 1 in tips (#44062) fix the thing the uhhhh the thing the thing ynow.... fix uhh the thing. the thing that needs fixing * Remove background box from main menu (#43977) * Menu Change * Change v2 * final change * Format code --------- Co-authored-by: Thomas (Aeshus) * Mini Gravity Generator requires charging (#44061) * Starting charge to zero * Reach starts with gravity * Revert "Reach starts with gravity" This reverts commit 49a7e22fc054687b879a5a13562433bc081f19cd. * Create charged/uncharged prototypes * Fix formatting * Change construction * Review feedback * Automatic changelog update * [BUGFIX] Shaken sodas now spill again (#44033) Tiders will rule the halls once again. * Automatic changelog update * add empty tile friction modifiers * add empty tile friction modifiers * Revert "[Hotfix] Fix MenuButton Missing Negative Style (#44073)" This reverts commit 0f1abd73669c6c7ff291bb289f760090dc9454b6. * Revert "Automatic changelog update" This reverts commit 4a34a711845f0da49686c750cad7807639b56deb. * Revert "[BUGFIX] Shaken sodas now spill again (#44033)" This reverts commit 37447f4d89feeb98baba91d896b0439d30088b80. * Revert "Automatic changelog update" This reverts commit 1ced1977fb73186a8aaefab504e3a860aee219f9. * Revert "Mini Gravity Generator requires charging (#44061)" This reverts commit 51e0f74f49673146973f8ad4966d1df0d97a506d. * Revert "Remove background box from main menu (#43977)" This reverts commit 48f3fd0c5029b384b008fdda9016e78858fcae38. * Revert "fix off by 1 in tips (#44062)" This reverts commit 1616ed806eef33bbdcaca64aaa642c44ab95f869. * Revert "Update Credits (#44060)" This reverts commit 6a9ffb9b4d5d62d191c173c4670e1c3ea6ffadb0. * Revert "Destructible Automatic Overkill (#43113)" This reverts commit 76277b737df94b02cb3fd439d8cda0f63964b85d. * Revert "Automatic changelog update" This reverts commit 5e3a647839dd5f2e3fb942b9fc4693b0bfe6baa8. * Revert "[MALD PR] Remove mimic vendor gamerule (#44057)" This reverts commit 77ee428ce4ab312b4d91124c884c233328ce871c. * Revert "Automatic changelog update" This reverts commit 71c503bf9c8c9fe785110e6b3cd5a5b46138c1d0. * Revert "Fix Barber Scissors not playing audio (#43922)" This reverts commit 989ab25751eb494b463d476c502a28d82b1cca6d. * Revert "Automatic changelog update" This reverts commit 013962c7160912570e361195c9dfcea694d0c514. * Revert "SSD Players now properly stay asleep when being revived. (#39409)" This reverts commit 546c62b38810bd6f4666235046407fb008593289. * Revert "Automatic changelog update" This reverts commit f5d65b43621721dda77d6c5ce6b18202c6b02ad7. * Revert "fix role assignment when late joining (#41419)" This reverts commit bec4c4c827ad69a2206415fc26a35f2f2e00ff1e. * Revert "Automatic changelog update" This reverts commit 0c6d7aa29bb6dbd3b4b57b5f787152a946cc5b39. * Revert "fix: don't delete items on some polymorph reverts (#39423)" This reverts commit 01a2dfb9762fc53f3918199360717208c9162695. * Revert "Fix error texture for harmpack hand solution visuals (#39442)" This reverts commit 42dbb60e0fe670e5bc35c5dd9f4c1f81c69e15cd. * Revert "Automatic changelog update" This reverts commit 1cb150f5bbd8c430eba0d2471fa4d2b4e72ba8ad. * Revert "Smarter Entity Targeting (#39571)" This reverts commit 87b389d1492af8404eb3c43a5a0dcd7623d724b9. * Revert "Automatic changelog update" This reverts commit 927f9e2652f146686be50750ffc04569893cb5d8. * Revert "Fix ID inspect message when ending perma sentence early (#44018)" This reverts commit c7dfbf89484dd332847924643bd6bfed08d37253. * Revert "Vehicle system: Movement & Buckling (#43525)" This reverts commit 8c85a5927564701e1e44eca27cb55d295c2eda11. * revert --------- Co-authored-by: AndrewFenriz <78079974+AndrewFenriz@users.noreply.github.com> Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Co-authored-by: themias <89101928+themias@users.noreply.github.com> Co-authored-by: PJBot Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Co-authored-by: Perry Fraser Co-authored-by: Ignaz "Ian" Kraft Co-authored-by: Rami Co-authored-by: Jessica M Co-authored-by: Connor Huffine Co-authored-by: Tayrtahn Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Co-authored-by: PicklOH <62030853+PicklOH@users.noreply.github.com> Co-authored-by: Lye <128915833+Lyroth001@users.noreply.github.com> Co-authored-by: piskaczek <192605039+piskaczek@users.noreply.github.com> Co-authored-by: riccardi48 <39341154+riccardi48@users.noreply.github.com> Co-authored-by: Thomas (Aeshus) Co-authored-by: ThatGuyUSA Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com> Co-authored-by: Thomas <87614336+Aeshus@users.noreply.github.com> --- .../Structures/Power/Generation/Singularity/singularity.yml | 1 + .../Entities/Structures/Power/Generation/Tesla/energyball.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml index d57af0a41ca..4f69ba3d092 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml @@ -13,6 +13,7 @@ bodyType: Dynamic bodyStatus: InAir - type: CanMoveInAir + - type: TileFrictionModifier - type: EventHorizon # To make the singularity consume things. radius: 0.5 canBreachContainment: false diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml index 52e82847cb5..44ffe8638d9 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml @@ -8,6 +8,7 @@ bodyStatus: InAir sleepingAllowed: false - type: CanMoveInAir + - type: TileFrictionModifier - type: ChasingWalk minSpeed: 1 maxSpeed: 3 From d3ed3865df8ba1cfdc06270a3a1ec98d03e9e863 Mon Sep 17 00:00:00 2001 From: Thomas <87614336+Aeshus@users.noreply.github.com> Date: Sat, 30 May 2026 00:20:38 -0500 Subject: [PATCH 002/287] [HOTFIX] Fix Barber DoAfter Bug (#44162) --- Content.Shared/MagicMirror/MagicMirrorSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/MagicMirror/MagicMirrorSystem.cs b/Content.Shared/MagicMirror/MagicMirrorSystem.cs index 631b514c480..240531e07c3 100644 --- a/Content.Shared/MagicMirror/MagicMirrorSystem.cs +++ b/Content.Shared/MagicMirror/MagicMirrorSystem.cs @@ -68,7 +68,7 @@ private void OnMagicMirrorSelect(Entity ent, ref MagicMirr if (ent.Comp.DoAfter.HasValue) { - _doAfter.Cancel(target, ent.Comp.DoAfter.Value); + _doAfter.Cancel(args.Actor, ent.Comp.DoAfter.Value); ent.Comp.DoAfter = null; } From b8996ec074032963a55b8533116fd33801cab61f Mon Sep 17 00:00:00 2001 From: Kowlin <10947836+Kowlin@users.noreply.github.com> Date: Sat, 30 May 2026 07:21:00 +0200 Subject: [PATCH 003/287] Update the Salamander server description for the server browser: Redux (#44104) Update the MRP config --- Resources/ConfigPresets/WizardsDen/salamander.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/ConfigPresets/WizardsDen/salamander.toml b/Resources/ConfigPresets/WizardsDen/salamander.toml index 0877fc55779..7112fc8d8b3 100644 --- a/Resources/ConfigPresets/WizardsDen/salamander.toml +++ b/Resources/ConfigPresets/WizardsDen/salamander.toml @@ -1,7 +1,7 @@ # Configuration preset used on Wizard's Den Salamander [game] -desc = "Official English Space Station 14 servers. Medium roleplay ruleset. You must apply for a whitelist on the official forums to be able to join. If the current player count is less than 15 players then anyone with enough playtime on Wizard's Den servers may join." +desc = "Official English Space Station 14 servers. Medium roleplay ruleset. Open to anyone with at least 20 hours of playtime on any Wizard's Den server. Freely joinable when the player count is less than 15 players." hostname = "[EN][MRP] Wizard's Den Salamander [US West]" round_restart_time = 300 From 7fa0d2afe3f85a55d7eb0e1fee5d71f033e02353 Mon Sep 17 00:00:00 2001 From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Sat, 30 May 2026 18:57:34 +0200 Subject: [PATCH 004/287] Make objective issuers into prototypes (#44168) * woohoo * yeah * fuck * Update Content.Shared/Objectives/Prototypes/ObjectiveIssuerPrototype.cs --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../Tests/Commands/ObjectiveCommandsTest.cs | 2 +- .../CharacterInfo/CharacterInfoSystem.cs | 10 +++++- Content.Server/Objectives/ObjectivesSystem.cs | 10 ++++-- .../Components/ObjectiveComponent.cs | 8 ++--- .../Prototypes/ObjectiveIssuerPrototype.cs | 27 ++++++++++++++++ .../game-presets/preset-secret.ftl | 2 ++ .../Prototypes/Objectives/changeling.yml | 2 +- Resources/Prototypes/Objectives/dragon.yml | 2 +- Resources/Prototypes/Objectives/issuers.yml | 31 +++++++++++++++++++ Resources/Prototypes/Objectives/ninja.yml | 2 +- .../Prototypes/Objectives/paradoxClone.yml | 2 +- Resources/Prototypes/Objectives/thief.yml | 2 +- Resources/Prototypes/Objectives/traitor.yml | 2 +- Resources/Prototypes/Objectives/wizard.yml | 2 +- 14 files changed, 88 insertions(+), 16 deletions(-) create mode 100644 Content.Shared/Objectives/Prototypes/ObjectiveIssuerPrototype.cs create mode 100644 Resources/Prototypes/Objectives/issuers.yml diff --git a/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs b/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs index eb18d8e1ae0..0422f1bc3db 100644 --- a/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs +++ b/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs @@ -21,7 +21,7 @@ public sealed class ObjectiveCommandsTest : GameTest components: - type: Objective difficulty: 1 - issuer: objective-issuer-syndicate + issuer: TheSyndicate icon: sprite: error.rsi state: error diff --git a/Content.Server/CharacterInfo/CharacterInfoSystem.cs b/Content.Server/CharacterInfo/CharacterInfoSystem.cs index 608c9c49a35..9cc1488265e 100644 --- a/Content.Server/CharacterInfo/CharacterInfoSystem.cs +++ b/Content.Server/CharacterInfo/CharacterInfoSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Objectives; using Content.Shared.Objectives.Components; using Content.Shared.Objectives.Systems; +using Robust.Shared.Prototypes; namespace Content.Server.CharacterInfo; @@ -14,6 +15,7 @@ public sealed partial class CharacterInfoSystem : EntitySystem [Dependency] private MindSystem _minds = default!; [Dependency] private RoleSystem _roles = default!; [Dependency] private SharedObjectivesSystem _objectives = default!; + [Dependency] private IPrototypeManager _protoMan = default!; public override void Initialize() { @@ -42,8 +44,14 @@ private void OnRequestCharacterInfoEvent(RequestCharacterInfoEvent msg, EntitySe if (info == null) continue; + if (!_protoMan.TryIndex(Comp(objective).Issuer, out var issuerProto)) + { + Log.Error($"Found incorrect objective issuer {issuerProto} when generating character info for objective {MetaData(objective).EntityPrototype}."); + continue; + } + // group objectives by their issuer - var issuer = Comp(objective).LocIssuer; + var issuer = issuerProto.LocalizedName; if (!objectives.ContainsKey(issuer)) objectives[issuer] = new List(); objectives[issuer].Add(info.Value); diff --git a/Content.Server/Objectives/ObjectivesSystem.cs b/Content.Server/Objectives/ObjectivesSystem.cs index 53b6d3b71bc..f7c9d41fe3b 100644 --- a/Content.Server/Objectives/ObjectivesSystem.cs +++ b/Content.Server/Objectives/ObjectivesSystem.cs @@ -147,12 +147,18 @@ private void AddSummary(StringBuilder result, string agent, List<(EntityUid, str var agentSummary = new StringBuilder(); agentSummary.AppendLine(Loc.GetString("objectives-with-objectives", ("custody", custody), ("title", title), ("agent", agent))); - foreach (var objectiveGroup in objectives.GroupBy(o => Comp(o).LocIssuer)) + foreach (var objectiveGroup in objectives.GroupBy(o => Comp(o).Issuer)) { //TO DO: //check for the right group here. Getting the target issuer is easy: objectiveGroup.Key //It should be compared to the type of the group's issuer. - agentSummary.AppendLine(objectiveGroup.Key); + if (!_prototypeManager.TryIndex(objectiveGroup.Key, out var issuer)) + { + Log.Error($"Found incorrect objective issuer {issuer} when generating round end text."); + continue; + } + + agentSummary.AppendLine(issuer.LocalizedName); foreach (var objective in objectiveGroup) { diff --git a/Content.Shared/Objectives/Components/ObjectiveComponent.cs b/Content.Shared/Objectives/Components/ObjectiveComponent.cs index fb2e6ca0a68..feb2c444ef2 100644 --- a/Content.Shared/Objectives/Components/ObjectiveComponent.cs +++ b/Content.Shared/Objectives/Components/ObjectiveComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.Mind; using Content.Shared.Objectives; +using Content.Shared.Objectives.Prototypes; using Content.Shared.Objectives.Systems; using Robust.Shared.Utility; using Robust.Shared.Prototypes; @@ -22,11 +23,8 @@ public sealed partial class ObjectiveComponent : Component /// /// Organisation that issued this objective, used for grouping and as a header above common objectives. /// - [DataField("issuer", required: true)] - private LocId Issuer { get; set; } - - [ViewVariables(VVAccess.ReadOnly)] - public string LocIssuer => Loc.GetString(Issuer); + [DataField(required: true)] + public ProtoId Issuer = "Unknown"; /// /// Unique objectives can only have 1 per prototype id. diff --git a/Content.Shared/Objectives/Prototypes/ObjectiveIssuerPrototype.cs b/Content.Shared/Objectives/Prototypes/ObjectiveIssuerPrototype.cs new file mode 100644 index 00000000000..694d345e1e6 --- /dev/null +++ b/Content.Shared/Objectives/Prototypes/ObjectiveIssuerPrototype.cs @@ -0,0 +1,27 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Objectives.Prototypes; + +/// +/// Prototype for objective issuers. +/// They represent organizations that issue objectives, used for grouping and as a header above common objectives. +/// +[Prototype] +public sealed partial class ObjectiveIssuerPrototype : IPrototype +{ + /// + [IdDataField] + public string ID { get; private set; } = default!; + + /// + /// The LocId of the issuer name. + /// + [DataField(required: true)] + private LocId Name { get; set; } + + /// + /// Localized version of the issuer name. + /// + [ViewVariables] + public string LocalizedName => Loc.GetString(Name); +} diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-secret.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-secret.ftl index 2551b0073d4..483b9e0b312 100644 --- a/Resources/Locale/en-US/game-ticking/game-presets/preset-secret.ftl +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-secret.ftl @@ -3,3 +3,5 @@ secret-description = It's a secret to everyone. The threats you encounter are ra dynamic-title = Dynamic dynamic-description = No one knows what's coming. You can encounter any number of threats. + +objective-issuer-unknown = [color=white]Unknown[/color] diff --git a/Resources/Prototypes/Objectives/changeling.yml b/Resources/Prototypes/Objectives/changeling.yml index b9e9a6b67f8..34aea43344d 100644 --- a/Resources/Prototypes/Objectives/changeling.yml +++ b/Resources/Prototypes/Objectives/changeling.yml @@ -4,7 +4,7 @@ id: BaseChangelingObjective components: - type: Objective - issuer: objective-issuer-changeling + issuer: Changeling difficulty: 1 - type: RoleRequirement roles: diff --git a/Resources/Prototypes/Objectives/dragon.yml b/Resources/Prototypes/Objectives/dragon.yml index 35740896e3c..5225d9ede1a 100644 --- a/Resources/Prototypes/Objectives/dragon.yml +++ b/Resources/Prototypes/Objectives/dragon.yml @@ -6,7 +6,7 @@ - type: Objective # difficulty isn't used at all since objective are fixed difficulty: 1.5 - issuer: objective-issuer-dragon + issuer: SpaceDragon - type: RoleRequirement roles: - DragonRole diff --git a/Resources/Prototypes/Objectives/issuers.yml b/Resources/Prototypes/Objectives/issuers.yml new file mode 100644 index 00000000000..92f645c8353 --- /dev/null +++ b/Resources/Prototypes/Objectives/issuers.yml @@ -0,0 +1,31 @@ +- type: objectiveIssuer + id: Unknown + name: objective-issuer-unknown + +- type: objectiveIssuer + id: SpaceWizardsFederation + name: objective-issuer-swf + +- type: objectiveIssuer + id: TheSyndicate + name: objective-issuer-syndicate + +- type: objectiveIssuer + id: SpiderClan + name: objective-issuer-spiderclan + +- type: objectiveIssuer + id: Changeling + name: objective-issuer-changeling + +- type: objectiveIssuer + id: Paradox + name: objective-issuer-paradox + +- type: objectiveIssuer + id: SpaceDragon + name: objective-issuer-dragon + +- type: objectiveIssuer + id: Thief + name: objective-issuer-thief diff --git a/Resources/Prototypes/Objectives/ninja.yml b/Resources/Prototypes/Objectives/ninja.yml index 03980f7b31b..a3b452931f9 100644 --- a/Resources/Prototypes/Objectives/ninja.yml +++ b/Resources/Prototypes/Objectives/ninja.yml @@ -6,7 +6,7 @@ - type: Objective # difficulty isn't used since all objectives are picked difficulty: 1.5 - issuer: objective-issuer-spiderclan + issuer: SpiderClan - type: RoleRequirement roles: - NinjaRole diff --git a/Resources/Prototypes/Objectives/paradoxClone.yml b/Resources/Prototypes/Objectives/paradoxClone.yml index 7006e74a055..495991c9214 100644 --- a/Resources/Prototypes/Objectives/paradoxClone.yml +++ b/Resources/Prototypes/Objectives/paradoxClone.yml @@ -6,7 +6,7 @@ - type: Objective # required but not used difficulty: 1 - issuer: objective-issuer-paradox + issuer: Paradox - type: RoleRequirement roles: - ParadoxCloneRole diff --git a/Resources/Prototypes/Objectives/thief.yml b/Resources/Prototypes/Objectives/thief.yml index ef67b560de2..203d5ed6973 100644 --- a/Resources/Prototypes/Objectives/thief.yml +++ b/Resources/Prototypes/Objectives/thief.yml @@ -4,7 +4,7 @@ id: BaseThiefObjective components: - type: Objective - issuer: objective-issuer-thief + issuer: Thief - type: RoleRequirement roles: - ThiefRole diff --git a/Resources/Prototypes/Objectives/traitor.yml b/Resources/Prototypes/Objectives/traitor.yml index cf1d04559f8..c48e784a028 100644 --- a/Resources/Prototypes/Objectives/traitor.yml +++ b/Resources/Prototypes/Objectives/traitor.yml @@ -4,7 +4,7 @@ id: BaseTraitorObjective components: - type: Objective - issuer: objective-issuer-syndicate + issuer: TheSyndicate - type: RoleRequirement roles: - TraitorRole diff --git a/Resources/Prototypes/Objectives/wizard.yml b/Resources/Prototypes/Objectives/wizard.yml index 91ebc0bf1f9..92c8b46f660 100644 --- a/Resources/Prototypes/Objectives/wizard.yml +++ b/Resources/Prototypes/Objectives/wizard.yml @@ -6,7 +6,7 @@ - type: Objective # required but not used difficulty: 1 - issuer: objective-issuer-swf + issuer: SpaceWizardsFederation - type: RoleRequirement roles: - WizardRole From 2c18196c02997b9f22ee195ace98c1626742994e Mon Sep 17 00:00:00 2001 From: piskaczek <192605039+piskaczek@users.noreply.github.com> Date: Sat, 30 May 2026 19:18:15 +0200 Subject: [PATCH 005/287] Non MapInitialized EntityStorages no longer store gases when closed (#44170) scar asked me to split this off --- .../Storage/EntitySystems/SharedEntityStorageSystem.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs index ad3d9ce426f..dd8079cef69 100644 --- a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs @@ -287,7 +287,8 @@ public void CloseStorage(EntityUid uid, EntityStorageComponent? component = null break; } - TakeGas(uid, component); + if (LifeStage(uid) >= EntityLifeStage.MapInitialized) // stop mappers from serializing air in locker + TakeGas(uid, component); ModifyComponents(uid, component); if (_net.IsClient && _timing.IsFirstTimePredicted) _audio.PlayPvs(component.CloseSound, uid); From 86e4a27ef620a822d61d4635e26de53a474ab0b8 Mon Sep 17 00:00:00 2001 From: Kyle Tyo Date: Sat, 30 May 2026 14:14:23 -0400 Subject: [PATCH 006/287] Random Borg blindness/dark vision (#44030) Update DamageOverlayUiController.cs --- .../Systems/DamageOverlays/DamageOverlayUiController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Client/UserInterface/Systems/DamageOverlays/DamageOverlayUiController.cs b/Content.Client/UserInterface/Systems/DamageOverlays/DamageOverlayUiController.cs index 3b435453b85..0ead2fd10da 100644 --- a/Content.Client/UserInterface/Systems/DamageOverlays/DamageOverlayUiController.cs +++ b/Content.Client/UserInterface/Systems/DamageOverlays/DamageOverlayUiController.cs @@ -69,6 +69,7 @@ private void OnThresholdCheck(ref MobThresholdChecked args) private void ClearOverlay() { + _overlay.State = MobState.Alive; _overlay.DeadLevel = 0f; _overlay.CritLevel = 0f; _overlay.PainLevel = 0f; From 7a36700b9aaa0e6ae89b1a103a05089ffffa689e Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 May 2026 18:33:57 +0000 Subject: [PATCH 007/287] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 79031d6b107..a6be40bb48e 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: K-Dynamic, Cadunkus - changes: - - message: Added new vox hairstyle called "Vox Catfish." - type: Add - id: 9218 - time: '2025-11-21T22:09:36.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40689 - author: ciaran changes: - message: other people still touching a body when a defib is used will be shocked @@ -4098,3 +4091,10 @@ id: 9729 time: '2026-05-29T19:49:10.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/42342 +- author: VerinSenpai + changes: + - message: Borg vision should no longer "randomly" be occluded by the damage overlay. + type: Fix + id: 9730 + time: '2026-05-30T18:32:46.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/44030 From 11722a65e9e01d7cc28abc68e5e915c9adc26168 Mon Sep 17 00:00:00 2001 From: 0-Anon Date: Sat, 30 May 2026 17:05:59 -0400 Subject: [PATCH 008/287] Allow Tea Leaves to be Electrolyzed as a source of Fluorine (#43755) Electroylsis of Tea Leaves Electrolysis Breakdown 5u TeaPowder into: 2 Carbon 2 Fluorine 0.5 Hydrogen 0.5 Oxygen --- Resources/Prototypes/Recipes/Reactions/drinks.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Resources/Prototypes/Recipes/Reactions/drinks.yml b/Resources/Prototypes/Recipes/Reactions/drinks.yml index aa0cf4b6cda..b54bd804894 100644 --- a/Resources/Prototypes/Recipes/Reactions/drinks.yml +++ b/Resources/Prototypes/Recipes/Reactions/drinks.yml @@ -1557,3 +1557,17 @@ amount: 1 products: BacchusBlessing: 4 + +- type: reaction + id: TeaPowderBreakdown + source: true + requiredMixerCategories: + - Electrolysis + reactants: + TeaPowder: + amount: 5 + products: + Carbon: 2 + Fluorine: 2 + Hydrogen: 0.5 + Oxygen: 0.5 From bf297bdbcce0e1a02562b0d9499021fef25951f9 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 30 May 2026 21:25:37 +0000 Subject: [PATCH 009/287] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index a6be40bb48e..631fccbcd44 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: ciaran - changes: - - message: other people still touching a body when a defib is used will be shocked - too - type: Add - id: 9219 - time: '2025-11-22T23:56:49.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/35998 - author: slarticodefast changes: - message: Artifact analyzers are no longer extremely power hungry. @@ -4098,3 +4090,10 @@ id: 9730 time: '2026-05-30T18:32:46.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/44030 +- author: 0-Anon + changes: + - message: Added an electrolysis reaction to tea powder as a source of fluorine. + type: Add + id: 9731 + time: '2026-05-30T21:24:29.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43755 From 933fcbae9d9c7e530e46e5654dfd598bf694fa11 Mon Sep 17 00:00:00 2001 From: Rami Date: Sat, 30 May 2026 20:19:41 -0700 Subject: [PATCH 010/287] add user "Quanteey" to In Memoriam (#44166) add user quanteey to In Memoriam --- Resources/Credits/Immortals.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Credits/Immortals.txt b/Resources/Credits/Immortals.txt index 68d24ffb148..3a359df4720 100644 --- a/Resources/Credits/Immortals.txt +++ b/Resources/Credits/Immortals.txt @@ -4,3 +4,4 @@ [color=white][bold]Desolane900[/bold][/color] - Circa 2023, unknown date [color=white][bold]PWNRA[/bold][/color], A. A. 'Gramps' Silverton - Circa 2025, unknown date [color=white][bold]Honkertron[/bold][/color] - Unknown date +[color=white][bold]Quanteey[/bold][/color] - Circa 2025, unknown date From a19e63fd25f616cac36a6711bf9f2a69c8cb723f Mon Sep 17 00:00:00 2001 From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Sun, 31 May 2026 11:46:14 +0200 Subject: [PATCH 011/287] Fix spawning as ghost on devmap (#44100) * Fix spawning as ghost on devmap * Add passenger slots to Dev * Un-add passenger slots to Dev --- Content.Server/GameTicking/GameTicker.Spawning.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index cae4bfd8631..f095b94435c 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -209,7 +209,12 @@ private void SpawnPlayer(ICommonSession player, speciesId = weights.Pick(_robustRandom); } - character = HumanoidCharacterProfile.RandomWithSpecies(speciesId); + // The random profile must retain the job priorities set by the player + var jobs = character.JobPriorities; + character = HumanoidCharacterProfile.RandomWithSpecies(speciesId).WithJobPriorities(jobs); + + // This does not utilize overflow job slots, so if the character profile + // had no available job priorities (ie Captain on Dev) set, then the player will spawn as a ghost } // We raise this event to allow other systems to handle spawning this player themselves. (e.g. late-join wizard, etc) From 1357e70a0416247e19e9371038da6ce7f4784c2b Mon Sep 17 00:00:00 2001 From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Sun, 31 May 2026 16:21:02 +0200 Subject: [PATCH 012/287] Remove duplicate locale (#44192) yeah --- .../Locale/en-US/game-ticking/game-presets/preset-secret.ftl | 2 -- .../Locale/en-US/game-ticking/game-presets/preset-traitor.ftl | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-secret.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-secret.ftl index 483b9e0b312..2551b0073d4 100644 --- a/Resources/Locale/en-US/game-ticking/game-presets/preset-secret.ftl +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-secret.ftl @@ -3,5 +3,3 @@ secret-description = It's a secret to everyone. The threats you encounter are ra dynamic-title = Dynamic dynamic-description = No one knows what's coming. You can encounter any number of threats. - -objective-issuer-unknown = [color=white]Unknown[/color] diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl index 1b540d65247..4bc140e9b64 100644 --- a/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-traitor.ftl @@ -4,7 +4,7 @@ traitor-round-end-codewords = The codewords were: [color=White]{$codewords}[/col traitor-round-end-agent-name = traitor objective-issuer-syndicate = [color=crimson]The Syndicate[/color] -objective-issuer-unknown = Unknown +objective-issuer-unknown = [color=white]Unknown[/color] # Shown at the end of a round of Traitor From e4ed560bb06ee05b19ccebeefd8516bfa80680bf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 21:51:18 +0200 Subject: [PATCH 013/287] Update Credits (#44180) Co-authored-by: PJBot --- Resources/Credits/GitHub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index 2a6a8406ae1..a567b6cd16b 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0-Anon, 0leshe, 0tito, 0x6273, 11BelowStudio, 12rabbits, 1337dakota, 13spacemen, 154942, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 27alaing, 2DSiggy, 3nderall, 4310v343k, 4dplanner, 5tickman, 612git, 778b, 96flo, aaron, abadaba695, Ablankmann, abregado, Absolute-Potato, Absotively, achookh, Acruid, actioninja, ActiveMammmoth, actually-reb, ada-please, adamsong, Adeinitas, adm2play, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aearo-Deepwater, Aerocrux, Aeshus, Aexolott, Aexxie, afflewaffle, africalimedrop, afrokada, AftrLite, AgentSmithRadio, Agoichi, ahandleman, Ahion, aiden, Aidenkrz, aidenkrz, Aisu9, ajcm, AJCM-git, AjexRose, Alekshhh, alexalexmax, alexkar598, AlexMorgan3817, alexum418, alexumandxgabriel08x, Alice4267, Alithsko, Alkheemist, alliephante, ALMv1, Alpaccalypse, AlphaQwerty, Altoids1, amatwiedle, amylizzle, Andre19926, Andrew-Fall, AndrewEyeke, AndrewFenriz, AndreyCamper, Androclast, anri, Anzarot121, ApolloVector, Appiah, april-gras, ar4ill, Arcane-Waffle, arcanevaliance, archee1, ArchPigeon, ArchRBX, areitpog, Arendian, areyouconfused, arimah, Arkanic, ArkiveDev, armoks, Arteben, arthropodia, ArthurMousatov, ArtisticRoomba, artur, Artxmisery, ArZarLordOfMango, as334, AshBats, AsikKEsel, AsnDen, asperger-sind, aspiringLich, astriloqua, Atakku, Ataman, august-sun, AutoOtter, AverageNotDoingAnythingEnjoyer, avghdev, AwareFoxy, Awlod, Axionyxx, azloserbits, AzzyIsNotHere, azzyisnothere, B-Kirill, B3CKDOOR, baa14453, BackeTako, BadaBoomie, Bakke, BananaFlambe, Baptr0b0t, BarryNorfolk, BasedUser, baynarikattu, bea, BeatusCrow, bebr3ght, beck-thompson, beesterman, bellwetherlogic, ben, benbryant0, benev0, benjamin-burges, BGare, bhespiritu, bibbly, BigfootBravo, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blitzthesquishy, Blobadoodle, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, Bokser815, bolantej, BombasterDS, Booblesnoot42, Boolean-Buckeye, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, breeplayx3, BriBrooo, BRINGit34, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, buunie099, bvelliquette, BWTCK, byondfuckery, c0rigin, c4llv07e, CaasGit, Cabin-keeper, Caconym27, Calecute, Callmore, Camdot, cammusubi, capnsockless, CaptainMaru, captainsqrbeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, catdotjs, catlord, Catofquestionableethics, CatTheSystem, CawsForConcern, CDWimmer, Centronias, Chaboricks, chairbender, chaisftw, Chaoticaa, Charlese2, charlie, chartman, ChaseFlorom, chavonadelal, Cheackraze, CheddaCheez, cheesePizza2, CheesePlated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, ciaran, citrea, civilCornball, claustro305, Clement-O, cloudyias, clyf, Clyybber, CMDR-Piboy314, cnv41, coco, cohanna, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, Compilatron144, CookieMasterT, coolboy911, CoolioDudio, coolmankid12345, Coolsurf6, cooperwallace, corentt, CormosLemming, CrafterKolyan, CraftyRenter, crazybrain23, Crazydave91920, CrazyPhantom779, creadth, CrigCrag, CroilBird, Crotalus, CrudeWax, cryals, CrzyPotato, cubixthree, cutemoongod, Cyberboss, d34d10cc, DaCookieCakes, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, DamianX, dangerrevolution, daniel-cr, DanSAussieITS, Daracke, Darkie, DaturoDewitt, david, DawBla, Daxxi3, dch-GH, ddeegan, de0rix, Deahaka, dean, DEATHB4DEFEAT, Deatherd, deathride58, debugok, Decappi, Decortex, Deeeeja, deepdarkdepths, DeepwaterCreations, Deerstop, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, devinschubert14, dexlerxd, dffdff2423, DieselMohawk, DieselMohawkTheSequel, digitalic, Dimastra, dimmoon1, DinnerCalzone, DinoWattz, Disp-Dev, DisposableCrewmember42, dissidentbullet, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DR-DOCTOR-EVIL-EVIL, Dragonjspider, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, DuckManZach, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, dylanstrategie, dylanwhittingham, Dynexust, Easypoller, echo, EchoOfNothing, eclips_e, eden077, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, emberwinters, Emisse, emmafornash, EmoGarbage404, Endecc, EnrichedCaramel, Entvari, eoineoineoin, ephememory, eris, erohrs2, erorr404v1, Errant-4, ertanic, esguard, estacaoespacialpirata, eternally-confused, eugene, Eveloop, Ewanderer, ewokswagger, exincore, exp111, f0x-n3rd, F1restar4, FacePluslll, Fahasor, FairlySadPanda, farrellka-dev, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, fillervk, FinnishPaladin, firenamefn, Firewars763, FirinMaLazors, Fishfish458, fl-oz, Flareguy, flashgnash, FlipBrooke, FluffiestFloof, FluffMe, FluidRock, flymo5678, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, foxhorn, freeman2651, freeze2222, frobnic8, Froffy025, Fromoriss, froozigiusz, FrostMando, FrostRibbon, Fruitsalad, Funce, FungiFellow, FunkySphere, FunTust, Futuristic-OK, GalacticChimp, gamer3107, Gamewar360, gansulalan, GaussiArson, Gaxeer, gbasood, gcoremans, Geekyhobo, gem, genderGeometries, GeneralGaws, Genkail, Gentleman-Bird, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, GitHubUser53123, gituhabu, GlassEclipse, Glissadia, GnarpGnarp, GNF54, godisdeadLOL, goet, GoldenCan, Goldminermac, Golinth, golubgik, GoodWheatley, Gorox221, GR1231, gradientvera, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GrownSamoyedDog, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, happyrobot33, Hardly3D, harikattar, Hayden, he1acdvv, Hebi, Helix-ctrl, helm4142, Henry, HerCoyote23, Hi-Im-Shot, HighTechPuddle, Hitlinemoss, hiucko, hivehum, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, hoshizora-sayo, Hreno, Hrosts, htmlsystem, Huaqas, hubismal, Hugal31, Hyenh, hyperb1, hyperDelegate, hyphenationc, i-justuser-i, iaada, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, imatsoup, IMCB, impubbi, imrenq, imweax, indeano, Injazz, Insineer, insoPL, IntegerTempest, Interrobang01, Intoxicating-Innocence, IProduceWidgets, itsmethom, Itzbenz, iztokbajcar, Jackal298, Jackrost, JackRyd3r, jacksonbailey, jacksonzck, JackspajfMain, Jacktastic09, Jackw2As, jacob, jamessimo, janekvap-havok, Jark255, Jarmer123, Jaskanbe, JasperJRoth, jbox144, JCGWE30, JerryImMouse, jerryimmouse, Jessetriesagain, jessicamaybe, JesterX666, Jewelots, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, jkwookee, jmcb, JohnGinnane, johnjjohn, JohnJJohn, johnku1, Jophire, Jopogrechkin, joshepvodka, JpegOfAFrog, jproads, JrInventor05, Jrpl, jukereise, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, JustinWinningham, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, kaiserbirch, KaiShibaa, kalane15, kalanosh, KamTheSythe, Kanashi-Panda, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, Kenionatus, kerisargit, keronshb, KeTuFaisPiKiNut, Khaomi, KIBORG04, KieueCaprie, Kimpes, kin98, KingFroozy, kipdotnet, kira-er, kiri-yoshikage, Kirillcas, Kirus59, Kistras, Kit, Kit0vras, KittenColony, Kittygyat, klaypexx, kleinerstation13, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, kontakt, korczoczek, kosticia, koteq, kotobdev, Kowlin, KrasnoshchekovPavel, kresny, Krosus777, Krunklehorn, Kryyto, Kupie, kxvvv, Kyoth25f, kyupolaris, kzhanik, LaCumbiaDelCoronavirus, lajolico, Lamrr, lanedon, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, Le-Arctic-Fox, leahcat, leander-0, leonardo-dabepis, leonidussaks, leonsfriedrich, LeoSantich, LetterN, lettern, Level10Cybermancer, LEVELcat, lever1209, LevitatingTree, Lgibb18, lgruthes, liem161, LightVillet, lilazero, liltenhead, linkbro1, linkuyx, Litraxx, little-meow-meow, LittleBuilderJane, LittleNorthStar, LittleNyanCat, lizelive, ljm862, lmsnoise, localcc, lokachop, lolman360, Lomcastar, Lordbrandon12, LordCarve, LordEclipse, lucas, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luegamer, luizwritescode, LukaSlade, luminight, lunarcomets, Lusatia, Luxeator, lvvova1, Lyndomen, lyroth001, lyxcaster, lzimann, lzk228, M1tht1c, M3739, M4rchy-S, M87S, mac6na6na, MACMAN2003, Macoron, magicalus, magmodius, magnnusson, magnuscrowe, maland1, malchanceux, MaloTV, manelnavola, ManelNavola, Mangohydra, marboww, Markek1, MarkerWicker, marlyn, mastermiller01, matt, Matz05, max, MaxNox7, maylokana, mdrkrg, MDuch369, meara1179, meganerobot, MehimoNemo, Mehnix, MeltedPixel, memeproof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, mifia, mikeysaurus, MilenVolf, MilonPL, Minemoder5000, Minty642, minus1over12, Mirino97, mirrorcult, MishaUnity, MissKay1994, MisterImp, MisterMecky, Mith-randalf, Mixelz, mjarduk, MjrLandWhale, mkanke-real, MLGTASTICa, mnva0, moderatelyaware, modern-nm, mohamedwidar, mokiros, momo, Moneyl, monotheonist, Moomoobeef, moony, Morb0, MossyGreySlope, mqole, mr-bo-jangles, Mr0maks, MrFippik, MrPersival, mrrobdemo, mtrs163, muburu, MureixloI, murolem, murphyneko, musicmanvr, MWKane, Myakot, Myctai, N3X15, nabegator, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, naxel11, NazrinNya, neborsh, nekokiwa, neomoth, neutrino-laser, NickPowers43, nikitosych, nikthechampiongr, Nimfar11, ninruB, Nirnael, NIXC, nkokic, NkoKirkto, nmajask, noctyrnal, noelkathegod, noirogen, nok-ko, NonchalantNoob, NoobyLegion, Nopey, NoreUhh, Not-A-Chair, not-gavnaed, NotActuallyMarty, notafet, notquitehadouken, notsodana, noudoit, noverd, Nox38, NuclearWinter, Nuggets219, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, Nyxilath, och-och, OctoRocket, Ohelig, OldDanceJacket, OliverOtter, onesch, OneZerooo0, OnsenCapy, OnyxTheBrave, opl-, Orange-Winds, OrangeMoronage9622, OrbitSystem07, Orsoniks, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, PAFFhassoocks, paige404, paigemaeforrest, pali6, Palladinium, Pangogie, panzer-iv1, partyaddict, patrikturi, PaulRitter, pavlockblaine03, peccneck, Peptide90, peptron1, perryprog, PeterFuto, PetMudstone, pewter-wiz, PGrayCS, pgraycs, Pgriha, phantom-lily, Pharaz4, pheenty, philingham, Phill101, Phonix, Phooooooooooooooooooooooooooooooosphate, phunnyguy, PicklOH, PilgrimViis, Pill-U, pinkbat5, Piras314, Pireax, piskaczek, Pissachu, pissdemon, Pixel8-dev, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, Pok27, poklj, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, PotRoastPiggy, Princess-Cheeseballs, ProfanedBane, Prole0, ProPandaBear, ProPeperos, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykana, psykzz, PuceTint, pumkin69, PuroSlavKing, PursuitInAshes, Putnam3145, py01, Pyrovi, qrtDaniil, qrwas, Quantum-cross, quasr-9, quatre, QueerNB, QuietlyWhisper, qwerltaz, Radezolid, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, RainyGale, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, RedBookcase, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, Redrover1760, redspyy, ReeZer2, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, rewafflution, rhailrake, rhsvenson, riccardi48, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, rlebell33, RobbyTheFish, robinthedragon, robinthegirlthing, Rockdtben, Rohesie, rok-povsic, rokudara-sen, rolfero, RomanNovo, roryflowers, rosieposieeee, Roudenn, router, ruddygreat, rumaks-xyz, RumiTiger, Ruzihm, rwrv, S1rFl0, S1ss3l, Saakra, SabreML, Sadie-silly, saga3152, saintmuntzer, salarua, Salex08, sam, samgithubaccount, Samuka-C, SaphireLattice, SapphicOverload, sarahon, sativaleanne, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, ScholarNZL, schrodinger71, scrato, Scribbles0, scrivoy, scruq445, scuffedjays, ScumbagDog, SeamLesss, Segonist, semensponge, sephtasm, ser1-1y, Serkket, sewerpig, SG6732, sh18rw, Shaddap1, ShadeAware, ShadowCommander, shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SharkSnake98, Shegare, shepardtothestars, shibechef, Sidzaru, Siginanto, signalsender, SignalWalker, siigiil, silicon14wastaken, Silverfur-underscore, Simyon264, sirdragooon, Sirionaut, SirWarock, Sk1tch, SkaldetSkaeg, Skarletto, skeeka-dev, skrybl, Skybailey-dev, skye, Skyedra, SlamBamActionman, slarticodefast, Slava0135, sleepyyapril, slimmslamm, Slyfox333, Smugman, SnappingOpossum, snebl, snicket, sniperchance, Snowni, snowsignal, SolidSyn, SolidusSnek, solstar2, SomegnihT, SonarZeBat, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, soupkilove, southbridge-fur, sowelipililimute, Soydium, SpaceLizard24, SpaceLizardSky, SpaceManiac, SpaceRox1244, SpaceyLady, Spangs04, spanky-spanky, Sparlight, spartak, SpartanKadence, spderman3333, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, sporkyz, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, starbuckss14, Stealthbomber16, steel, Steffo99, stellar-novas, stewie523, stomf, Stop-Signs, stopbreaking, stopka-html, StrawberryMoses, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, SuperGDPWYL, superjj18, Supernorn, SurrealShibe, SweetAplle, SweptWasTaken, SyaoranFox, Sybil, SYNCHRONIC, Synthestra, Szunti, t, Tainakov, takemysoult, taonewt, tap, TaralGit, Taran, taserthefox, taurie, Tayrtahn, tday93, teamaki, TeenSarlacc, TekuNut, telavivgamers, telyonok, temm1ie, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, Tezzaide, TGODiamond, TGRCdev, tgrkzus, thanosdegraf, ThatGuyUSA, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, TheBlueYowie, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, theexetron, TheFlyingSentry, thefoty, TheGrimbeeper, TheIntoxicatedCat, thekilk, TheMas3212, themias, theomund, TheProNoob678, TherapyGoth, ThereDrD0, TheSecondLord, TheShuEd, thetolbean, thevinter, TheWaffleJesus, Thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, TiniestShark, Titian3, tk-a369, tkdrg, tmtmtl30, ToastEnjoyer, Toby222, TokenStyle, Tollhouse, Toly65, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, tornado-technology, TornadoTechnology, tosatur, TotallyLemon, ToxicSonicFan04, TPgameing, Tr1bute, travis-g-reid, treytipton, TriviaSolari, trixxedbit, TrixxedHeart, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, tuchila-adi-bogdan, Tunguso4ka, TurboTrackerss14, TVK-04, tyashley, Tyler-IN, TytosB, Tyzemol, UbaserB, Uberration, ubis1, UBlueberry, uhbg, UKNOWH, UltimateJester, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, Unkn0wnGh0st333, unusualcrow, UpAndLeaves, Uriende, UristMcDorf, user424242420, Utmanarn, Vaaankas, valentfingerov, valquaint, VanderslootAssgiraffe, Varen, Vasilis, VasilisThePikachu, veliebm, Velken, VelonacepsCalyxEggs, Venomii, veprolet, VerinSenpai, veritable-calamity, Veritius, Vermidia, vero5123, verslebas, vexerot, viceemargo, VigersRay, violet754, Visne, vitopigno, vitusveit, vlad, vlados1408, VMSolidus, vmzd, VoidMeticulous, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, Vortebo, vulppine, wachte1, wafehling, walksanatora, Warentan, WarMechanic, Watermelon914, weaversam8, wertanchik, whateverusername0, whatston3, widgetbeck, Will-Oliver-Br, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, Wolfkey-SomeoneElseTookMyUsername, Worldwaker, wrexbe, wtcwr68, xeri7, xkreksx, xprospero, xRiriq, xsainteer, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, YoungThugSS14, Yousifb26, youtissoum, yunii, YuriyKiss, yuriykiss, zach-hill, Zadeon, Zalycon, zamp, Zandario, Zap527, Zealith-Gamer, zekins3366, ZelteHonor, zero, ZeroDiamond, ZeWaka, zHonys, zionnBE, ZNixian, Zokkie, ZoldorfTheWizard, zonespace27, Zylofan, Zymem, zzylex +0-Anon, 0leshe, 0tito, 0x6273, 11BelowStudio, 12rabbits, 1337dakota, 13spacemen, 154942, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 27alaing, 2DSiggy, 3nderall, 4310v343k, 4dplanner, 5tickman, 612git, 778b, 96flo, aaron, abadaba695, Ablankmann, abregado, Absolute-Potato, Absotively, achookh, Acruid, actioninja, ActiveMammmoth, actually-reb, ada-please, adamsong, Adeinitas, adm2play, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aearo-Deepwater, Aerocrux, Aeshus, Aexolott, Aexxie, afflewaffle, africalimedrop, afrokada, AftrLite, AgentSmithRadio, Agoichi, ahandleman, Ahion, aiden, aidenkrz, Aidenkrz, Aisu9, ajcm, AJCM-git, AjexRose, Alekshhh, alexalexmax, alexkar598, AlexMorgan3817, alexum418, alexumandxgabriel08x, Alice4267, Alithsko, Alkheemist, alliephante, ALMv1, Alpaccalypse, AlphaQwerty, Altoids1, amatwiedle, amylizzle, Andre19926, Andrew-Fall, AndrewEyeke, AndrewFenriz, AndreyCamper, Androclast, anri, Anzarot121, ApolloVector, Appiah, april-gras, ar4ill, Arcane-Waffle, arcanevaliance, archee1, ArchPigeon, ArchRBX, areitpog, Arendian, AreYouConfused, arimah, Arkanic, ArkiveDev, armoks, Arteben, arthropodia, ArthurMousatov, ArtisticRoomba, artur, Artxmisery, ArZarLordOfMango, as334, AshBats, AsikKEsel, AsnDen, asperger-sind, aspiringLich, astriloqua, Atakku, Ataman, august-sun, AutoOtter, AverageNotDoingAnythingEnjoyer, avghdev, AwareFoxy, Awlod, Axionyxx, azloserbits, AzzyIsNotHere, azzyisnothere, B-Kirill, B3CKDOOR, baa14453, BackeTako, BadaBoomie, Bakke, BananaFlambe, Baptr0b0t, BarryNorfolk, BasedUser, baynarikattu, bea, BeatusCrow, bebr3ght, beck-thompson, beesterman, bellwetherlogic, ben, benbryant0, benev0, benjamin-burges, BGare, bhespiritu, bibbly, BigfootBravo, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blitzthesquishy, Blobadoodle, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, Bokser815, bolantej, BombasterDS, Booblesnoot42, Boolean-Buckeye, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, breeplayx3, BriBrooo, BRINGit34, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, buunie099, bvelliquette, BWTCK, byondfuckery, c0rigin, c4llv07e, CaasGit, Cabin-keeper, Caconym27, Calecute, Callmore, Camdot, cammusubi, capnsockless, CaptainMaru, captainsqrbeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, catdotjs, catlord, Catofquestionableethics, CatTheSystem, CawsForConcern, CDWimmer, Centronias, Chaboricks, chairbender, chaisftw, Chaoticaa, Charlese2, charlie, chartman, ChaseFlorom, chavonadelal, Cheackraze, CheddaCheez, cheesePizza2, CheesePlated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, ciaran, citrea, civilCornball, claustro305, Clement-O, cloudyias, clyf, Clyybber, CMDR-Piboy314, cnv41, coco, cohanna, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, Compilatron144, CookieMasterT, coolboy911, CoolioDudio, coolmankid12345, Coolsurf6, cooperwallace, corentt, CormosLemming, CrafterKolyan, CraftyRenter, crazybrain23, Crazydave91920, CrazyPhantom779, creadth, CrigCrag, CroilBird, Crotalus, CrudeWax, cryals, CrzyPotato, cubixthree, cutemoongod, Cyberboss, d34d10cc, DaCookieCakes, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, DamianX, dangerrevolution, daniel-cr, DanSAussieITS, Daracke, Darkie, DaturoDewitt, david, DawBla, Daxxi3, dch-GH, ddeegan, de0rix, Deahaka, dean, DEATHB4DEFEAT, Deatherd, deathride58, debugok, Decappi, Decortex, Deeeeja, deepdarkdepths, DeepwaterCreations, Deerstop, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, devinschubert14, dexlerxd, dffdff2423, DieselMohawk, DieselMohawkTheSequel, digitalic, Dimastra, dimmoon1, DinnerCalzone, DinoWattz, Disp-Dev, DisposableCrewmember42, dissidentbullet, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DR-DOCTOR-EVIL-EVIL, Dragonjspider, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, DuckManZach, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, dylanstrategie, dylanwhittingham, Dynexust, Easypoller, echo, EchoOfNothing, eclips_e, eden077, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, emberwinters, Emisse, emmafornash, EmoGarbage404, Endecc, EnrichedCaramel, Entvari, eoineoineoin, ephememory, eris, erohrs2, erorr404v1, Errant-4, ertanic, esguard, estacaoespacialpirata, eternally-confused, eugene, Eveloop, Ewanderer, ewokswagger, exincore, exp111, f0x-n3rd, F1restar4, FacePluslll, Fahasor, FairlySadPanda, farrellka-dev, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, fillervk, FinnishPaladin, firenamefn, Firewars763, FirinMaLazors, Fishfish458, fl-oz, Flareguy, flashgnash, FlipBrooke, FluffiestFloof, FluffMe, FluidRock, flymo5678, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, foxhorn, freeman2651, freeze2222, frobnic8, Froffy025, Fromoriss, froozigiusz, FrostMando, FrostRibbon, Fruitsalad, Funce, FungiFellow, FunkySphere, FunTust, Futuristic-OK, GalacticChimp, gamer3107, Gamewar360, gansulalan, GaussiArson, Gaxeer, gbasood, gcoremans, Geekyhobo, gem, genderGeometries, GeneralGaws, Genkail, Gentleman-Bird, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, GitHubUser53123, gituhabu, GlassEclipse, Glissadia, GnarpGnarp, GNF54, godisdeadLOL, goet, GoldenCan, Goldminermac, Golinth, golubgik, GoodWheatley, Gorox221, GR1231, gradientvera, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GrownSamoyedDog, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, happyrobot33, Hardly3D, harikattar, Hayden, he1acdvv, Hebi, Helix-ctrl, helm4142, Henry, HerCoyote23, Hi-Im-Shot, HighTechPuddle, Hitlinemoss, hiucko, hivehum, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, Hoshizora, Hreno, Hrosts, htmlsystem, Huaqas, hubismal, Hugal31, Hyenh, hyperb1, hyperDelegate, hyphenationc, i-justuser-i, iaada, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, imatsoup, IMCB, impubbi, imrenq, imweax, indeano, Injazz, Insineer, insoPL, IntegerTempest, Interrobang01, Intoxicating-Innocence, IProduceWidgets, itsmethom, Itzbenz, iztokbajcar, Jackal298, Jackrost, JackRyd3r, jacksonbailey, jacksonzck, JackspajfMain, Jacktastic09, Jackw2As, jacob, jamessimo, janekvap-havok, Jark255, Jarmer123, Jaskanbe, JasperJRoth, jbox144, JCGWE30, JerryImMouse, jerryimmouse, Jessetriesagain, jessicamaybe, JesterX666, Jewelots, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, jkwookee, jmcb, JohnGinnane, johnjjohn, JohnJJohn, johnku1, Jophire, Jopogrechkin, joshepvodka, JpegOfAFrog, jproads, JrInventor05, Jrpl, jukereise, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, JustinWinningham, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, kaiserbirch, KaiShibaa, kalane15, kalanosh, KamTheSythe, Kanashi-Panda, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, Kenionatus, kerisargit, keronshb, KeTuFaisPiKiNut, Khaomi, KIBORG04, KieueCaprie, Kimpes, kin98, KingFroozy, kipdotnet, kira-er, kiri-yoshikage, Kirillcas, Kirus59, Kistras, Kit, Kit0vras, KittenColony, Kittygyat, klaypexx, kleinerstation13, KloopRE, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, kontakt, korczoczek, kosticia, koteq, kotobdev, Kowlin, KrasnoshchekovPavel, kresny, Krosus777, Krunklehorn, Kryyto, Kupie, kxvvv, Kyoth25f, kyupolaris, kzhanik, LaCumbiaDelCoronavirus, lajolico, Lamrr, lanedon, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, Le-Arctic-Fox, leahcat, leander-0, leonardo-dabepis, leonidussaks, leonsfriedrich, LeoSantich, lettern, LetterN, Level10Cybermancer, LEVELcat, lever1209, LevitatingTree, Lgibb18, lgruthes, liem161, LightVillet, lilazero, liltenhead, linkbro1, LinkUyx, Litraxx, little-meow-meow, LittleBuilderJane, LittleNorthStar, LittleNyanCat, lizelive, ljm862, lmsnoise, localcc, lokachop, lolman360, Lomcastar, Lordbrandon12, LordCarve, LordEclipse, lucas, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luegamer, luizwritescode, LukaSlade, luminight, lunarcomets, Lusatia, Luxeator, lvvova1, Lyndomen, lyroth001, lyxcaster, lzimann, lzk228, M1tht1c, M3739, M4rchy-S, M87S, mac6na6na, MACMAN2003, Macoron, magicalus, magmodius, magnnusson, magnuscrowe, maland1, malchanceux, MaloTV, ManelNavola, manelnavola, Mangohydra, marboww, mariano, Markek1, MarkerWicker, marlyn, mastermiller01, matt, Matz05, max, MaxNox7, maylokana, mdrkrg, MDuch369, meara1179, meganerobot, MehimoNemo, Mehnix, MeltedPixel, memeproof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, mifia, mikeysaurus, MilenVolf, MilonPL, Minemoder5000, Minty642, minus1over12, Mirino97, mirrorcult, MishaUnity, MissKay1994, MisterImp, MisterMecky, Mith-randalf, Mixelz, mjarduk, MjrLandWhale, mkanke-real, MLGTASTICa, mnva0, moderatelyaware, modern-nm, mohamedwidar, mokiros, momo, Moneyl, monotheonist, Moomoobeef, moony, Morb0, MossyGreySlope, mqole, mr-bo-jangles, Mr0maks, MrFippik, MrPersival, mrrobdemo, mtrs163, muburu, MureixloI, murolem, murphyneko, musicmanvr, MWKane, Myakot, Myctai, N3X15, nabegator, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, naxel11, NazrinNya, neborsh, nekokiwa, neomoth, neutrino-laser, NickPowers43, nikitosych, nikthechampiongr, Nimfar11, ninruB, Nirnael, NIXC, nkokic, NkoKirkto, nmajask, noctyrnal, noelkathegod, noirogen, nok-ko, NonchalantNoob, NoobyLegion, Nopey, NoreUhh, Not-A-Chair, not-gavnaed, NotActuallyMarty, notafet, notquitehadouken, notsodana, noudoit, noverd, Nox38, NuclearWinter, Nuggets219, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, Nyxilath, och-och, OctoRocket, Ohelig, OldDanceJacket, OliverOtter, onesch, OneZerooo0, OnsenCapy, OnyxTheBrave, opl-, Orange-Winds, OrangeMoronage9622, OrbitSystem07, Orsoniks, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, PAFFhassoocks, paige404, paigemaeforrest, pali6, Palladinium, Pangogie, panzer-iv1, partyaddict, patrikturi, PaulRitter, pavlockblaine03, peccneck, Peptide90, peptron1, perryprog, PeterFuto, PetMudstone, pewter-wiz, pgraycs, PGrayCS, Pgriha, phantom-lily, Pharaz4, pheenty, philingham, Phill101, Phonix, Phooooooooooooooooooooooooooooooosphate, phunnyguy, PicklOH, PilgrimViis, Pill-U, pinkbat5, Piras314, Pireax, piskaczek, Pissachu, pissdemon, Pixel8-dev, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, Pok27, poklj, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, PotRoastPiggy, Princess-Cheeseballs, ProfanedBane, Prole0, ProPandaBear, ProPeperos, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykana, psykzz, PuceTint, pumkin69, PuroSlavKing, PursuitInAshes, Putnam3145, py01, Pyrovi, qrtDaniil, qrwas, Quantum-cross, quasr-9, quatre, QueerNB, QuietlyWhisper, qwerltaz, Radezolid, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, RainyGale, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, RedBookcase, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, Redrover1760, redspyy, ReeZer2, Rem, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, rewafflution, rhailrake, rhsvenson, riccardi48, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, rlebell33, RobbyTheFish, robinthedragon, robinthegirlthing, Rockdtben, Rohesie, rok-povsic, rokudara-sen, rolfero, RomanNovo, roryflowers, rosieposieeee, Roudenn, router, ruddygreat, Ruichka, rumaks-xyz, RumiTiger, Ruzihm, rwrv, S1rFl0, S1ss3l, Saakra, SabreML, Sadie-silly, saga3152, saintmuntzer, salarua, Salex08, sam, samgithubaccount, Samuka-C, SaphireLattice, SapphicOverload, sarahon, sativaleanne, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, ScholarNZL, schrodinger71, scrato, Scribbles0, scrivoy, scruq445, scuffedjays, ScumbagDog, SeamLesss, Segonist, semensponge, sephtasm, ser1-1y, Serkket, sewerpig, SG6732, sh18rw, Shaddap1, ShadeAware, ShadowCommander, shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SharkSnake98, Shegare, shepardtothestars, shibechef, Sidzaru, Siginanto, signalsender, SignalWalker, siigiil, silicon14wastaken, Silverfur-underscore, Simyon264, sirdragooon, Sirionaut, SirWarock, Sk1tch, SkaldetSkaeg, Skarletto, skeeka-dev, skrybl, Skybailey-dev, skye, Skyedra, SlamBamActionman, slarticodefast, Slava0135, sleepyyapril, slimmslamm, Slyfox333, Smugman, SnappingOpossum, snebl, snicket, sniperchance, Snowni, snowsignal, SolidSyn, SolidusSnek, solstar2, SomegnihT, SonarZeBat, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, soupkilove, southbridge-fur, sowelipililimute, Soydium, SpaceLizard24, SpaceLizardSky, SpaceManiac, SpaceRox1244, SpaceyLady, Spangs04, spanky-spanky, Sparlight, spartak, SpartanKadence, spderman3333, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, sporkyz, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, starbuckss14, Stealthbomber16, steel, Steffo99, stellar-novas, stewie523, stomf, Stop-Signs, stopbreaking, stopka-html, StrawberryMoses, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, SuperGDPWYL, superjj18, Supernorn, SurrealShibe, SweetAplle, SweptWasTaken, SyaoranFox, Sybil, SYNCHRONIC, Synthestra, Szunti, t, Tainakov, takemysoult, taonewt, tap, TaralGit, Taran, taserthefox, taurie, Tayrtahn, tday93, teamaki, TeenSarlacc, TekuNut, telavivgamers, telyonok, temm1ie, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, Tezzaide, TGODiamond, TGRCdev, tgrkzus, thanosdegraf, ThatGuyUSA, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, TheBlueYowie, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, theexetron, TheFlyingSentry, thefoty, TheGrimbeeper, TheIntoxicatedCat, thekilk, TheMas3212, themias, theomund, TheProNoob678, TherapyGoth, ThereDrD0, TheSecondLord, TheShuEd, thetolbean, thevinter, TheWaffleJesus, Thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, TiniestShark, Titian3, tk-a369, tkdrg, tmtmtl30, ToastEnjoyer, Toby222, TokenStyle, Tollhouse, Toly65, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, tornado-technology, TornadoTechnology, tosatur, TotallyLemon, ToxicSonicFan04, TPgameing, Tr1bute, travis-g-reid, treytipton, TriviaSolari, trixxedbit, TrixxedHeart, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, tuchila-adi-bogdan, Tunguso4ka, TurboTrackerss14, TVK-04, tyashley, Tyler-IN, TytosB, Tyzemol, UbaserB, Uberration, ubis1, UBlueberry, uhbg, UKNOWH, UltimateJester, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, Unkn0wnGh0st333, unusualcrow, UpAndLeaves, Uriende, UristMcDorf, user424242420, Utmanarn, Vaaankas, valentfingerov, valquaint, VanderslootAssgiraffe, Varen, Vasilis, VasilisThePikachu, veliebm, Velken, VelonacepsCalyxEggs, Venomii, veprolet, VerinSenpai, veritable-calamity, Veritius, Vermidia, vero5123, verslebas, vexerot, viceemargo, VigersRay, violet754, Visne, vitopigno, vitusveit, vlad, vlados1408, VMSolidus, vmzd, VoidMeticulous, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, Vortebo, vulppine, wachte1, wafehling, walksanatora, Warentan, WarMechanic, Watermelon914, weaversam8, wertanchik, whateverusername0, whatston3, widgetbeck, Will-Oliver-Br, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, Wolfkey-SomeoneElseTookMyUsername, Worldwaker, wrexbe, wtcwr68, xeri7, xkreksx, xprospero, xRiriq, xsainteer, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, YoungThugSS14, Yousifb26, youtissoum, yunii, YuriyKiss, yuriykiss, zach-hill, Zadeon, Zalycon, zamp, Zandario, Zap527, Zealith-Gamer, zekins3366, ZelteHonor, zero, ZeroDiamond, ZeWaka, zHonys, zionnBE, ZNixian, Zokkie, ZoldorfTheWizard, zonespace27, Zylofan, Zymem, zzylex From b2437c0ac22be8ec9915ba72da4c4edec2a80bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=81da?= Date: Sun, 31 May 2026 14:48:05 -0500 Subject: [PATCH 014/287] Remove Destructible Overkill Thresholds from YAML (#44091) * non-structures * so many structures ;_; * lint me * oops bad merge resolve --------- Co-authored-by: iaada --- .../Entities/Clothing/Head/hats.yml | 6 +- .../Entities/Mobs/NPCs/living_light.yml | 18 +- .../Drinks/drinks_base_materials.yml | 48 -- .../Drinks/drinks_bottles_glass.yml | 6 - .../Consumable/Food/Containers/box.yml | 5 - .../Entities/Objects/Decoration/flora.yml | 42 +- .../Entities/Objects/Decoration/mining.yml | 24 - .../Objects/Devices/station_beacon.yml | 30 +- .../Entities/Objects/Devices/station_map.yml | 12 +- .../Prototypes/Entities/Objects/Fun/darts.yml | 12 - .../Prototypes/Entities/Objects/Fun/orbs.yml | 6 - .../Objects/Materials/Sheets/glass.yml | 30 -- .../Entities/Objects/Misc/desk_bell.yml | 12 - .../Entities/Objects/Misc/fluff_lights.yml | 6 - .../Entities/Objects/Misc/paper.yml | 6 - .../Entities/Objects/Misc/pet_carrier.yml | 12 +- .../Entities/Objects/Misc/spaceshroom.yml | 12 +- .../Entities/Objects/Misc/tiles.yml | 6 - .../Entities/Objects/Power/lights.yml | 12 - .../Entities/Objects/Shields/shields.yml | 250 +++++----- .../Objects/Specific/Cargo/cargo_pallet.yml | 6 - .../Specific/Chemistry/chemistry-vials.yml | 8 - .../Objects/Specific/Kitchen/foodcarts.yml | 26 +- .../Objects/Specific/Security/barrier.yml | 6 - .../Objects/Specific/Security/target.yml | 12 - .../Service/vending_machine_restock.yml | 8 +- .../Xenoarchaeology/artifact_equipment.yml | 24 +- .../Entities/Objects/Tools/decoys.yml | 10 +- .../Entities/Objects/Tools/fulton.yml | 12 +- .../Entities/Objects/Tools/toolbox.yml | 28 +- .../Guns/Turrets/turrets_ballistic.yml | 6 - .../Weapons/Guns/Turrets/turrets_base.yml | 9 - .../Weapons/Guns/Turrets/turrets_broken.yml | 9 - .../Entities/Objects/Weapons/Melee/spear.yml | 12 - .../Weapons/Throwable/base_grenades.yml | 20 +- .../Objects/Weapons/Throwable/bola.yml | 6 - .../Entities/Objects/base_shadow.yml | 22 +- .../Structures/Decoration/bonfire.yml | 18 +- .../Structures/Decoration/crystals.yml | 394 ++++++++-------- .../Structures/Decoration/curtains.yml | 6 - .../Decoration/decorated_fir_tree.yml | 28 +- .../Structures/Decoration/fireplace.yml | 12 +- .../Structures/Decoration/flesh_blockers.yml | 4 +- .../Structures/Decoration/showcase.yml | 48 +- .../Structures/Decoration/statues.yml | 6 - .../Dispensers/base_structuredispensers.yml | 6 - .../Structures/Doors/Firelocks/firelock.yml | 288 ++++++------ .../Doors/MaterialDoors/material_doors.yml | 30 -- .../Doors/Windoors/base_structurewindoors.yml | 36 -- .../Structures/Doors/Windoors/clockwork.yml | 6 - .../Structures/Furniture/Tables/tables.yml | 107 ----- .../Entities/Structures/Furniture/altar.yml | 18 - .../Entities/Structures/Furniture/beds.yml | 30 -- .../Structures/Furniture/bookshelf.yml | 9 - .../Entities/Structures/Furniture/carpets.yml | 321 +++++-------- .../Entities/Structures/Furniture/chairs.yml | 48 -- .../Entities/Structures/Furniture/dresser.yml | 6 - .../Structures/Furniture/rollerbeds.yml | 150 +++--- .../Entities/Structures/Furniture/sink.yml | 32 +- .../Entities/Structures/Furniture/toilet.yml | 6 - .../Structures/Lighting/base_lighting.yml | 24 - .../Structures/Lighting/ground_lighting.yml | 6 - .../Structures/Lighting/strobe_lighting.yml | 6 - .../Structures/Machines/Computers/frame.yml | 6 - .../Structures/Machines/Medical/cryo_pod.yml | 40 +- .../Machines/base_structuremachines.yml | 6 - .../Machines/crew_monitor_server.yml | 148 +++--- .../Entities/Structures/Machines/frame.yml | 306 ++++++------ .../Entities/Structures/Machines/research.yml | 12 - .../Piping/Atmospherics/portable.yml | 74 ++- .../Structures/Piping/Atmospherics/unary.yml | 6 - .../Disposal/high_pressure_machine_frame.yml | 112 ++--- .../Structures/Piping/Disposal/units.yml | 6 - .../Power/Generation/Singularity/emitter.yml | 34 +- .../Power/Generation/Tesla/coil.yml | 74 ++- .../Structures/Power/Generation/ame.yml | 12 - .../Power/Generation/generators.yml | 50 +- .../Power/Generation/portable_generator.yml | 364 +++++++-------- .../Structures/Power/Generation/solar.yml | 29 +- .../Entities/Structures/Power/apc.yml | 15 - .../Structures/Power/cable_terminal.yml | 88 ++-- .../Entities/Structures/Power/cables.yml | 18 - .../Entities/Structures/Power/chargers.yml | 6 - .../Entities/Structures/Power/substation.yml | 12 - .../Entities/Structures/Shuttles/cannons.yml | 44 +- .../Structures/Shuttles/station_anchor.yml | 106 ++--- .../Structures/Shuttles/thrusters.yml | 240 +++++----- .../Structures/Specific/Anomaly/anomalies.yml | 22 +- .../Structures/Specific/Cargo/mailcart.yml | 173 ++++--- .../Structures/Specific/Janitor/drain.yml | 94 ++-- .../Structures/Specific/Janitor/janicart.yml | 442 +++++++++--------- .../Entities/Structures/Specific/xeno.yml | 12 +- .../Storage/Canisters/gas_canisters.yml | 358 ++++++-------- .../Closets/Lockers/base_structurelockers.yml | 6 - .../Storage/Closets/base_structureclosets.yml | 18 - .../Structures/Storage/Crates/crates.yml | 36 -- .../Storage/Tanks/base_structuretanks.yml | 6 - .../Entities/Structures/Storage/barrels.yml | 6 - .../Structures/Storage/filing_cabinets.yml | 9 - .../Entities/Structures/Storage/glass_box.yml | 33 -- .../Entities/Structures/Storage/ore_box.yml | 6 - .../Entities/Structures/Storage/storage.yml | 6 - .../Structures/Wallmounts/Signs/posters.yml | 9 - .../Storage/Cabinets/defib_cabinet.yml | 5 - .../Storage/Cabinets/extinguisher_cabinet.yml | 68 ++- .../Storage/Cabinets/fireaxe_cabinet.yml | 6 - .../Structures/Wallmounts/Storage/shelfs.yml | 81 ---- .../Wallmounts/Storage/wall_dispensers.yml | 6 - .../Structures/Wallmounts/Switches/switch.yml | 12 - .../Monitors/telescreens.yml | 14 +- .../Monitors/televisions.yml | 72 ++- .../WallmountMachines/air_alarm.yml | 56 +-- .../WallmountMachines/fire_alarm.yml | 56 +-- .../Wallmounts/WallmountMachines/intercom.yml | 56 +-- .../WallmountMachines/station_map.yml | 177 ++++--- .../WallmountMachines/surveillance_camera.yml | 28 +- .../Entities/Structures/Walls/fence_metal.yml | 18 - .../Entities/Structures/Walls/fence_wood.yml | 18 - .../Entities/Structures/Walls/girders.yml | 132 +++--- .../Entities/Structures/Walls/grille.yml | 172 +++---- .../Entities/Structures/Walls/railing.yml | 24 - .../Entities/Structures/Walls/walls.yml | 194 +------- .../Entities/Structures/Windows/clockwork.yml | 18 - .../Entities/Structures/Windows/mining.yml | 6 - .../Entities/Structures/Windows/plasma.yml | 18 - .../Structures/Windows/plastitanium.yml | 12 - .../Structures/Windows/reinforced.yml | 18 - .../Entities/Structures/Windows/rplasma.yml | 15 - .../Entities/Structures/Windows/ruranium.yml | 12 - .../Entities/Structures/Windows/shuttle.yml | 6 - .../Entities/Structures/Windows/uranium.yml | 12 - .../Entities/Structures/Windows/window.yml | 18 - .../Entities/Structures/Windows/xenoresin.yml | 9 - .../Entities/Structures/barricades.yml | 6 - .../Entities/Structures/catwalk.yml | 6 - .../Entities/Structures/ironsand_steps.yml | 6 - .../Entities/Structures/meat_spike.yml | 6 - .../Entities/Structures/spider_web.yml | 6 - .../Prototypes/Entities/Structures/stairs.yml | 6 - .../Prototypes/Entities/Tiles/bananium.yml | 6 - 140 files changed, 2498 insertions(+), 4351 deletions(-) diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index db7c3c5b68f..2f22da38209 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -1464,9 +1464,9 @@ behaviors: - !type:SpawnEntitiesBehavior spawn: - Ash: - min: 1 - max: 1 + Ash: + min: 1 + max: 1 - !type:DoActsBehavior acts: [ "Destruction" ] diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/living_light.yml b/Resources/Prototypes/Entities/Mobs/NPCs/living_light.yml index b982b4b07c2..677e8c7cd24 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/living_light.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/living_light.yml @@ -92,15 +92,15 @@ - SlowImmune - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 25 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak + - trigger: + !type:DamageTrigger + damage: 25 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: GlassBreak - type: entity id: MobLuminousPerson diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_base_materials.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_base_materials.yml index 9ae86ca0164..a81caef76a5 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_base_materials.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_base_materials.yml @@ -13,12 +13,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: # Overkill threshold - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 5 @@ -59,12 +53,6 @@ components: - type: Destructible thresholds: - - trigger: # Overkill threshold - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 5 @@ -90,12 +78,6 @@ # damageModifierSet: FlimsyPlastic TODO - type: Destructible thresholds: - - trigger: # Overkill threshold - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 5 @@ -127,12 +109,6 @@ components: - type: Destructible thresholds: - - trigger: # Overkill threshold - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 20 # can take a few more hits than basic plastic @@ -166,12 +142,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: # Overkill threshold - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 5 @@ -206,12 +176,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: # Overkill threshold - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 10 @@ -245,12 +209,6 @@ damageModifierSet: Metallic - type: Destructible thresholds: - - trigger: # Overkill threshold - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 25 @@ -276,12 +234,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: # Overkill threshold - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 5 # Gold is very weak diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles_glass.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles_glass.yml index ee608ddf1b3..7c64c263f3f 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles_glass.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles_glass.yml @@ -25,12 +25,6 @@ - Trash - type: Destructible # Same as parent, but spawns BrokenBottle instead of ShardGlass thresholds: - - trigger: # Overkill threshold - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 5 diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/box.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/box.yml index 1ca82a7755f..42f9c4ac582 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/box.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/box.yml @@ -192,11 +192,6 @@ storagebase: id: Eggshells amount: 12 - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 10 # Pizza diff --git a/Resources/Prototypes/Entities/Objects/Decoration/flora.yml b/Resources/Prototypes/Entities/Objects/Decoration/flora.yml index cd1b3402c61..266c9eca82c 100644 --- a/Resources/Prototypes/Entities/Objects/Decoration/flora.yml +++ b/Resources/Prototypes/Entities/Objects/Decoration/flora.yml @@ -27,12 +27,12 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity id: BaseTree @@ -69,12 +69,6 @@ variation: 0.05 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 @@ -172,15 +166,15 @@ components: - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 5 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak + - trigger: + !type:DamageTrigger + damage: 5 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: GlassBreak - type: Sprite sprite: Objects/Decoration/Flora/flora_stalagmite.rsi layers: @@ -395,12 +389,6 @@ tree06: "" - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 25 diff --git a/Resources/Prototypes/Entities/Objects/Decoration/mining.yml b/Resources/Prototypes/Entities/Objects/Decoration/mining.yml index aa3fa056a53..7a04fc6fcb5 100644 --- a/Resources/Prototypes/Entities/Objects/Decoration/mining.yml +++ b/Resources/Prototypes/Entities/Objects/Decoration/mining.yml @@ -29,12 +29,6 @@ variation: 0.05 - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 50 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 10 @@ -97,12 +91,6 @@ variation: 0.05 - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 50 @@ -173,12 +161,6 @@ density: 1000 - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 75 @@ -200,12 +182,6 @@ state: support_wall_broken - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 50 diff --git a/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml b/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml index ecc506862b7..2b6e4bdd357 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml @@ -57,25 +57,25 @@ !type:DamageTrigger damage: 200 behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - !type:DoActsBehavior + acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 100 behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - params: - volume: -8 - - !type:SpawnEntitiesBehavior - spawn: - SheetSteel1: - min: 1 - max: 1 - offset: 0 - - !type:DoActsBehavior - acts: ["Breakage"] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + params: + volume: -8 + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel1: + min: 1 + max: 1 + offset: 0 + - !type:DoActsBehavior + acts: ["Breakage"] - type: StaticPrice price: 25 diff --git a/Resources/Prototypes/Entities/Objects/Devices/station_map.yml b/Resources/Prototypes/Entities/Objects/Devices/station_map.yml index 9efe6afc7ac..acd8ad873e8 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/station_map.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/station_map.yml @@ -21,12 +21,12 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: UserInterface interfaces: enum.StationMapUiKey.Key: diff --git a/Resources/Prototypes/Entities/Objects/Fun/darts.yml b/Resources/Prototypes/Entities/Objects/Fun/darts.yml index f46569580e3..7ed61ac8966 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/darts.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/darts.yml @@ -61,12 +61,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 50 #excess damage avoids cost of spawning entities. - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 20 @@ -176,12 +170,6 @@ - darts-popup-miss - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 500 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 300 diff --git a/Resources/Prototypes/Entities/Objects/Fun/orbs.yml b/Resources/Prototypes/Entities/Objects/Fun/orbs.yml index f91dd8e031b..3d31e772b0d 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/orbs.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/orbs.yml @@ -151,12 +151,6 @@ collection: GlassBreak - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 20 diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml index 0856fd0d83a..1f87047b2d9 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml @@ -13,12 +13,6 @@ damageModifierSet: Glass - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 @@ -124,12 +118,6 @@ node: SheetRGlass - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 @@ -198,12 +186,6 @@ node: SheetPGlass - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 @@ -322,12 +304,6 @@ node: SheetUGlass - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 @@ -445,12 +421,6 @@ node: SheetClockworkGlass - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 diff --git a/Resources/Prototypes/Entities/Objects/Misc/desk_bell.yml b/Resources/Prototypes/Entities/Objects/Misc/desk_bell.yml index 413a3fa63fa..6437b383e28 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/desk_bell.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/desk_bell.yml @@ -15,12 +15,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: &DamageTrigger50 - !type:DamageTrigger - damage: 50 - behaviors: &OverkillBehavior - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: &DamageTrigger20 !type:DamageTrigger damage: 20 @@ -70,8 +64,6 @@ node: deskBellNode - type: Destructible thresholds: - - trigger: *DamageTrigger50 - behaviors: *OverkillBehavior - trigger: *DamageTrigger20 behaviors: - !type:PlaySoundBehavior @@ -113,8 +105,6 @@ node: deskBuzzerNode - type: Destructible thresholds: - - trigger: *DamageTrigger50 - behaviors: *OverkillBehavior - trigger: *DamageTrigger20 behaviors: - !type:PlaySoundBehavior @@ -168,8 +158,6 @@ node: deskHornNode - type: Destructible thresholds: - - trigger: *DamageTrigger50 - behaviors: *OverkillBehavior - trigger: *DamageTrigger20 behaviors: - !type:PlaySoundBehavior diff --git a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml index b9cc2db65b1..c65631b1d40 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml @@ -270,12 +270,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 40 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 20 diff --git a/Resources/Prototypes/Entities/Objects/Misc/paper.yml b/Resources/Prototypes/Entities/Objects/Misc/paper.yml index f68b7d829b6..631d5c32647 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/paper.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/paper.yml @@ -28,12 +28,6 @@ - type: Injurable - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 15 diff --git a/Resources/Prototypes/Entities/Objects/Misc/pet_carrier.yml b/Resources/Prototypes/Entities/Objects/Misc/pet_carrier.yml index 3b28f62ae1e..6c99e8977bb 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/pet_carrier.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/pet_carrier.yml @@ -47,12 +47,12 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 50 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Appearance - type: EntityStorageVisuals stateDoorOpen: pet_carrier_open diff --git a/Resources/Prototypes/Entities/Objects/Misc/spaceshroom.yml b/Resources/Prototypes/Entities/Objects/Misc/spaceshroom.yml index 24e8cb090de..f6a63b0e5bf 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/spaceshroom.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/spaceshroom.yml @@ -33,12 +33,12 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 150 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 150 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity name: spaceshroom diff --git a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml index 6cec6bb32f8..a72d9bd4b68 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml @@ -20,12 +20,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 30 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 20 diff --git a/Resources/Prototypes/Entities/Objects/Power/lights.yml b/Resources/Prototypes/Entities/Objects/Power/lights.yml index 14db07dbf35..85a7398473d 100644 --- a/Resources/Prototypes/Entities/Objects/Power/lights.yml +++ b/Resources/Prototypes/Entities/Objects/Power/lights.yml @@ -26,12 +26,6 @@ Blunt: 5 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 5 @@ -82,12 +76,6 @@ bulb: Tube - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 5 diff --git a/Resources/Prototypes/Entities/Objects/Shields/shields.yml b/Resources/Prototypes/Entities/Objects/Shields/shields.yml index c18316e7e68..f4e41ec35a7 100644 --- a/Resources/Prototypes/Entities/Objects/Shields/shields.yml +++ b/Resources/Prototypes/Entities/Objects/Shields/shields.yml @@ -37,29 +37,23 @@ messages: ShieldMessages - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 140 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 100 #This is probably enough damage before it breaks - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:SpawnEntitiesBehavior - spawn: - SheetSteel: - min: 2 - max: 2 - SheetGlass: - min: 2 - max: 2 + - trigger: + !type:DamageTrigger + damage: 100 #This is probably enough damage before it breaks + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel: + min: 2 + max: 2 + SheetGlass: + min: 2 + max: 2 - type: StaticPrice price: 100 @@ -178,26 +172,20 @@ node: woodenBuckler - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 60 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 30 #Weaker shield - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:SpawnEntitiesBehavior - spawn: - MaterialWoodPlank: - min: 5 - max: 5 + - trigger: + !type:DamageTrigger + damage: 30 #Weaker shield + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: WoodDestroy + - !type:SpawnEntitiesBehavior + spawn: + MaterialWoodPlank: + min: 5 + max: 5 - type: StaticPrice price: 150 @@ -231,12 +219,6 @@ node: cardShield - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 25 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 10 #Very very weak shield @@ -286,26 +268,20 @@ node: makeshiftShield - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 40 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 20 #Very weak shield - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:SpawnEntitiesBehavior - spawn: - SheetSteel: - min: 1 - max: 2 + - trigger: + !type:DamageTrigger + damage: 20 #Very weak shield + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel: + min: 1 + max: 2 - type: entity name: web shield @@ -339,26 +315,20 @@ node: shield - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 40 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 20 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - MaterialWebSilk: - min: 1 - max: 1 - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy + - trigger: + !type:DamageTrigger + damage: 20 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + MaterialWebSilk: + min: 1 + max: 1 + - !type:PlaySoundBehavior + sound: + collection: WoodDestroy #Magic/Cult Shields (give these to wizard for now) @@ -424,20 +394,20 @@ path: /Audio/Effects/glass_step.ogg - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 40 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - SheetGlass: - min: 5 - max: 5 + - trigger: + !type:DamageTrigger + damage: 40 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + SheetGlass: + min: 5 + max: 5 - type: entity name: energy shield @@ -518,26 +488,20 @@ messages: EnergyShieldMessages - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 180 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - BrokenEnergyShield: - min: 1 - max: 1 + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + BrokenEnergyShield: + min: 1 + max: 1 - type: StaticPrice price: 350 @@ -599,28 +563,22 @@ - type: Appearance - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 70 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - - !type:SpawnEntitiesBehavior - spawn: - SheetSteel: - min: 1 - max: 1 - SheetGlass: - min: 2 - max: 2 + - trigger: + !type:DamageTrigger + damage: 70 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalGlassBreak + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel: + min: 1 + max: 1 + SheetGlass: + min: 2 + max: 2 - type: StaticPrice price: 150 diff --git a/Resources/Prototypes/Entities/Objects/Specific/Cargo/cargo_pallet.yml b/Resources/Prototypes/Entities/Objects/Specific/Cargo/cargo_pallet.yml index 0f8371a548f..8b62c75f860 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Cargo/cargo_pallet.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Cargo/cargo_pallet.yml @@ -42,12 +42,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 500 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 200 diff --git a/Resources/Prototypes/Entities/Objects/Specific/Chemistry/chemistry-vials.yml b/Resources/Prototypes/Entities/Objects/Specific/Chemistry/chemistry-vials.yml index 5bc8c0c0544..211582bd446 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Chemistry/chemistry-vials.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Chemistry/chemistry-vials.yml @@ -41,12 +41,6 @@ price: 100 - type: Destructible thresholds: - - trigger: &DamageTrigger100 - !type:DamageTrigger - damage: 100 - behaviors: &OverkillBehavior - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 15 # stronger than base @@ -86,8 +80,6 @@ Glass: 10 - type: Destructible thresholds: - - trigger: *DamageTrigger100 - behaviors: *OverkillBehavior - trigger: !type:DamageTrigger damage: 5 # smaller so weaker diff --git a/Resources/Prototypes/Entities/Objects/Specific/Kitchen/foodcarts.yml b/Resources/Prototypes/Entities/Objects/Specific/Kitchen/foodcarts.yml index bc724b006df..fa117d22a8b 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Kitchen/foodcarts.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Kitchen/foodcarts.yml @@ -26,22 +26,16 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:EmptyAllContainersBehaviour - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:EmptyAllContainersBehaviour + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak - type: Appearance - type: UserInterface interfaces: diff --git a/Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml b/Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml index 6551c639ba0..80a6648021e 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml @@ -49,12 +49,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 300 diff --git a/Resources/Prototypes/Entities/Objects/Specific/Security/target.yml b/Resources/Prototypes/Entities/Objects/Specific/Security/target.yml index c9f90aeea24..15feb76cf59 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Security/target.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Security/target.yml @@ -29,12 +29,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 1000 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 500 @@ -92,12 +86,6 @@ state: target_f - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 4000 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 2000 diff --git a/Resources/Prototypes/Entities/Objects/Specific/Service/vending_machine_restock.yml b/Resources/Prototypes/Entities/Objects/Specific/Service/vending_machine_restock.yml index b50adb1e400..4d54bfb2367 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Service/vending_machine_restock.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Service/vending_machine_restock.yml @@ -28,12 +28,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 40 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 20 @@ -339,7 +333,7 @@ - SpaceUpInventory - SodaInventory - DrGibbInventory - - SmiteInventory + - SmiteInventory - type: Sprite layers: - state: base diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/artifact_equipment.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/artifact_equipment.yml index 55de741a0b1..20e864363f0 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/artifact_equipment.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/artifact_equipment.yml @@ -59,12 +59,12 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 50 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Appearance - type: EntityStorageVisuals stateDoorOpen: artifact_container_open @@ -142,12 +142,12 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 50 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: PaperLabel labelSlot: insertVerbText: Attach Label diff --git a/Resources/Prototypes/Entities/Objects/Tools/decoys.yml b/Resources/Prototypes/Entities/Objects/Tools/decoys.yml index 50bb7fbf295..52a3aea628e 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/decoys.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/decoys.yml @@ -20,11 +20,11 @@ !type:DamageTrigger damage: 100 behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - path: /Audio/Effects/pop_high.ogg + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + path: /Audio/Effects/pop_high.ogg - type: Appearance - type: Item size: Normal diff --git a/Resources/Prototypes/Entities/Objects/Tools/fulton.yml b/Resources/Prototypes/Entities/Objects/Tools/fulton.yml index a50896cc958..bde8a370029 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/fulton.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/fulton.yml @@ -80,12 +80,12 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity id: Fulton1 diff --git a/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml b/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml index e1d479af088..5dd20c9c343 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml @@ -85,23 +85,17 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 1 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - WeaponTurretSyndicateDisposable: - min: 1 - max: 1 + - trigger: + !type:DamageTrigger + damage: 1 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior # todo this is not a good way to be doing this. Should probably use a construction graph + spawn: + WeaponTurretSyndicateDisposable: + min: 1 + max: 1 - type: StaticPrice price: 1350 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_ballistic.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_ballistic.yml index 67413d0b247..916e40d65ef 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_ballistic.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_ballistic.yml @@ -18,12 +18,6 @@ - Syndicate - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_base.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_base.yml index da479f41726..1c331c1421f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_base.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_base.yml @@ -37,15 +37,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - trigger: !type:DamageTrigger damage: 300 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_broken.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_broken.yml index 927c52d30ff..9ec514b9a41 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_broken.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_broken.yml @@ -16,15 +16,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 900 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 450 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml index ed01b23b4b8..69375a2e0d8 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml @@ -81,12 +81,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 30 #excess damage avoids cost of spawning entities. - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 20 @@ -191,12 +185,6 @@ graph: SpearBone - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 30 #excess damage avoids cost of spawning entities. - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 20 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/base_grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/base_grenades.yml index cde8ee3145a..5ba248eb05b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/base_grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/base_grenades.yml @@ -79,11 +79,6 @@ components: - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:TimerStartBehavior - trigger: # immediately explode !type:DamageTrigger damage: 45 @@ -92,6 +87,11 @@ keyOut: timer - !type:DoActsBehavior acts: ["Destruction"] + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:TimerStartBehavior - type: entity # Starts fuse after taking 10 damage, is destroyed without activating/detonating after taking 45 damage. abstract: true @@ -99,14 +99,14 @@ components: - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:TimerStartBehavior - trigger: # Disappear !type:DamageTrigger damage: 45 behaviors: - !type:DoActsBehavior acts: ["Destruction"] + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:TimerStartBehavior diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/bola.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/bola.yml index 6f335405bd6..00a84c67ba7 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/bola.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/bola.yml @@ -21,12 +21,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 75 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 15 diff --git a/Resources/Prototypes/Entities/Objects/base_shadow.yml b/Resources/Prototypes/Entities/Objects/base_shadow.yml index 8c30219dbdd..83393b654fd 100644 --- a/Resources/Prototypes/Entities/Objects/base_shadow.yml +++ b/Resources/Prototypes/Entities/Objects/base_shadow.yml @@ -4,17 +4,17 @@ components: - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - path: /Audio/Items/hiss.ogg - params: - variation: 0.08 + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + path: /Audio/Items/hiss.ogg + params: + variation: 0.08 - type: DamagedByFlashing flashDamage: types: diff --git a/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml b/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml index 8de1c238da2..bf6ccdd8f49 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/bonfire.yml @@ -27,15 +27,15 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 50 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: WoodDestroy + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: AmbientSound volume: -5 range: 5 diff --git a/Resources/Prototypes/Entities/Structures/Decoration/crystals.yml b/Resources/Prototypes/Entities/Structures/Decoration/crystals.yml index 7ba8798d4cb..b9e2e4a2381 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/crystals.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/crystals.yml @@ -5,141 +5,141 @@ name: crystal description: A crystaline solid. components: - - type: Sprite - sprite: Structures/Decoration/crystal.rsi - state: crystal_grey - color: "#52ff39" - noRot: true - - type: Reflect - reflectProb: 0.5 - reflects: - - Energy - spread: 75 - - type: Anchorable - delay: 2 - - type: Physics - bodyType: Static - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeCircle - radius: 0.45 - density: 60 - mask: - - MachineMask - layer: - - MidImpassable - - LowImpassable - - BulletImpassable - - Opaque - - type: PointLight - radius: 3 - energy: 3 - color: "#52ff39" - - type: Damageable - damageModifierSet: Glass - - type: Injurable - damageContainer: StructuralInorganic - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 20 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - ShardCrystalGreen: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - type: Sprite + sprite: Structures/Decoration/crystal.rsi + state: crystal_grey + color: "#52ff39" + noRot: true + - type: Reflect + reflectProb: 0.5 + reflects: + - Energy + spread: 75 + - type: Anchorable + delay: 2 + - type: Physics + bodyType: Static + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.45 + density: 60 + mask: + - MachineMask + layer: + - MidImpassable + - LowImpassable + - BulletImpassable + - Opaque + - type: PointLight + radius: 3 + energy: 3 + color: "#52ff39" + - type: Damageable + damageModifierSet: Glass + - type: Injurable + damageContainer: StructuralInorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 20 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardCrystalGreen: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity id: CrystalPink parent: CrystalGreen suffix: pink components: - - type: Sprite - color: "#ff66cc" - - type: PointLight - radius: 3 - energy: 3 - color: "#ff66cc" - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 20 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - ShardCrystalPink: - min: 1 - max: 1 + - type: Sprite + color: "#ff66cc" + - type: PointLight + radius: 3 + energy: 3 + color: "#ff66cc" + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 20 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardCrystalPink: + min: 1 + max: 1 - type: entity id: CrystalGrey parent: CrystalGreen suffix: red components: - - type: Sprite - state: crystal_grey - color: "#fb4747" - - type: PointLight - color: "#fb4747" - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 20 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - ShardCrystalRed: - min: 1 - max: 2 + - type: Sprite + state: crystal_grey + color: "#fb4747" + - type: PointLight + color: "#fb4747" + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 20 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardCrystalRed: + min: 1 + max: 2 - type: entity id: CrystalOrange parent: CrystalGreen suffix: orange components: - - type: Sprite - color: "#ff8227" - - type: PointLight - radius: 3 - energy: 3 - color: "#ff8227" - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 20 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - ShardCrystalOrange: - min: 1 - max: 2 + - type: Sprite + color: "#ff8227" + - type: PointLight + radius: 3 + energy: 3 + color: "#ff8227" + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 20 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardCrystalOrange: + min: 1 + max: 2 - type: entity id: CrystalBlack @@ -154,101 +154,101 @@ color: "#363636" - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 20 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - ShardCrystalBlack: - min: 1 - max: 2 + - trigger: + !type:DamageTrigger + damage: 20 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardCrystalBlack: + min: 1 + max: 2 - type: entity id: CrystalBlue parent: CrystalGreen suffix: blue components: - - type: Sprite - color: "#39a1ff" - - type: PointLight - radius: 3 - energy: 3 - color: "#39a1ff" - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 20 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - ShardCrystalBlue: - min: 1 - max: 2 + - type: Sprite + color: "#39a1ff" + - type: PointLight + radius: 3 + energy: 3 + color: "#39a1ff" + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 20 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardCrystalBlue: + min: 1 + max: 2 - type: entity id: CrystalYellow parent: CrystalGreen suffix: yellow components: - - type: Sprite - color: "#ffde46" - - type: PointLight - radius: 3 - energy: 3 - color: "#ffde46" - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 20 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - ShardCrystalYellow: - min: 1 - max: 2 + - type: Sprite + color: "#ffde46" + - type: PointLight + radius: 3 + energy: 3 + color: "#ffde46" + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 20 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardCrystalYellow: + min: 1 + max: 2 - type: entity id: CrystalCyan parent: CrystalGreen suffix: cyan components: - - type: Sprite - color: "#47f8ff" - - type: PointLight - radius: 3 - energy: 3 - color: "#47f8ff" - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 20 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - ShardCrystalCyan: - min: 1 - max: 2 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - type: Sprite + color: "#47f8ff" + - type: PointLight + radius: 3 + energy: 3 + color: "#47f8ff" + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 20 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardCrystalCyan: + min: 1 + max: 2 + - !type:DoActsBehavior + acts: [ "Destruction" ] diff --git a/Resources/Prototypes/Entities/Structures/Decoration/curtains.yml b/Resources/Prototypes/Entities/Structures/Decoration/curtains.yml index f40ef5aae80..91d59ccc573 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/curtains.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/curtains.yml @@ -40,12 +40,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 50 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 5 diff --git a/Resources/Prototypes/Entities/Structures/Decoration/decorated_fir_tree.yml b/Resources/Prototypes/Entities/Structures/Decoration/decorated_fir_tree.yml index 86e335f7ea6..4de5e3a10b7 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/decorated_fir_tree.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/decorated_fir_tree.yml @@ -24,20 +24,14 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 75 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - Log: - min: 2 - max: 8 + - trigger: + !type:DamageTrigger + damage: 75 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + Log: + min: 2 + max: 8 diff --git a/Resources/Prototypes/Entities/Structures/Decoration/fireplace.yml b/Resources/Prototypes/Entities/Structures/Decoration/fireplace.yml index 594e5b6b04e..803d67ce48c 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/fireplace.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/fireplace.yml @@ -40,12 +40,12 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: AlwaysHot - type: IgnitionSource temperature: 700 diff --git a/Resources/Prototypes/Entities/Structures/Decoration/flesh_blockers.yml b/Resources/Prototypes/Entities/Structures/Decoration/flesh_blockers.yml index fd27afc0358..9d59d89d63b 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/flesh_blockers.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/flesh_blockers.yml @@ -36,5 +36,5 @@ !type:DamageTrigger damage: 25 behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] + - !type:DoActsBehavior + acts: [ "Destruction" ] diff --git a/Resources/Prototypes/Entities/Structures/Decoration/showcase.yml b/Resources/Prototypes/Entities/Structures/Decoration/showcase.yml index 061160ee3df..6783a89ffb4 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/showcase.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/showcase.yml @@ -5,33 +5,27 @@ name: security robot showcase description: A non-functional replica of an old security robot. components: - - type: Anchorable - - type: Damageable - damageModifierSet: Metallic - - type: Injurable - damageContainer: StructuralInorganic - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 150 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 75 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:SpawnEntitiesBehavior - spawn: - PartRodMetal1: - min: 5 - max: 10 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - type: Anchorable + - type: Damageable + damageModifierSet: Metallic + - type: Injurable + damageContainer: StructuralInorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 75 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + PartRodMetal1: + min: 5 + max: 10 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity id: ShowcaseRobot diff --git a/Resources/Prototypes/Entities/Structures/Decoration/statues.yml b/Resources/Prototypes/Entities/Structures/Decoration/statues.yml index 1b67e93351e..bb0c8c29eb8 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/statues.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/statues.yml @@ -59,12 +59,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 150 # Overkill - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml index 571ece4518e..6872a137f8e 100644 --- a/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml +++ b/Resources/Prototypes/Entities/Structures/Dispensers/base_structuredispensers.yml @@ -42,12 +42,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 200 diff --git a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml index 7529d6ceaeb..6fd24165b8e 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml @@ -5,150 +5,150 @@ name: firelock description: Apply crowbar. components: - - type: Anchorable - flags: - - Anchorable - - type: Rotatable - - type: AtmosAlarmable - syncWith: - - FireAlarm - - AirAlarm - - type: ApcPowerReceiver - - type: ExtensionCableReceiver - - type: DeviceNetwork - deviceNetId: AtmosDevices - receiveFrequencyId: AtmosMonitor - - type: DeviceNetworkRequiresPower - - type: InteractionOutline - - type: Damageable - damageModifierSet: StructuralMetallicStrong - - type: Injurable - damageContainer: StructuralInorganic - - type: RCDDeconstructable - cost: 4 - delay: 6 - fx: EffectRCDDeconstruct6 - - type: Tag - tags: - - ForceFixRotations # Allow fixrotations to target these - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 500 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - type: Sprite - sprite: Structures/Doors/Airlocks/Standard/firelock.rsi - snapCardinals: true - layers: - - state: closed - map: ["enum.DoorVisualLayers.Base"] - - state: closed_unlit - shader: unshaded - map: ["enum.DoorVisualLayers.BaseUnlit"] - visible: false - - state: welded - map: ["enum.WeldableLayers.BaseWelded"] - - state: bolted_unlit - shader: unshaded - map: ["enum.DoorVisualLayers.BaseBolted"] - visible: false - - state: panel_open - map: ["enum.WiresVisualLayers.MaintenancePanel"] - - state: pressure_unlit - visible: false - shader: unshaded - map: ["enum.FirelockVisualLayersPressure.Base"] - - state: temperature_unlit - visible: false - shader: unshaded - map: ["enum.FirelockVisualLayersTemperature.Base"] - - type: GenericVisualizer - visuals: - enum.FirelockVisuals.PressureWarning: - enum.FirelockVisualLayersPressure.Base: - True: { visible: true } - False: { visible: false } - enum.FirelockVisuals.TemperatureWarning: - enum.FirelockVisualLayersTemperature.Base: - True: { visible: true } - False: { visible: false } - - type: AnimationPlayer - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.49,-0.49,0.49,0.49" # don't want this colliding with walls or they won't close - density: 100 - mask: - - FullTileMask - layer: - - AirlockLayer - - type: Door - openDrawDepth: WallTops - closeTimeOne: 0.1 - closeTimeTwo: 0.6 - openTimeOne: 0.1 - openTimeTwo: 0.6 - state: Open - bumpOpen: false - clickOpen: true - crushDamage: - types: - Blunt: 15 - openSound: - path: /Audio/Machines/airlock_open.ogg - closeSound: - path: /Audio/Machines/airlock_close.ogg - denySound: - path: /Audio/Machines/airlock_deny.ogg - openingAnimationTime: 0.6 - closingAnimationTime: 0.6 - - type: Weldable - fuel: 5 - time: 3 - - type: Firelock - - type: Appearance - - type: WiresVisuals - - type: WiresPanel - - type: UserInterface - interfaces: - enum.WiresUiKey.Key: - type: WiresBoundUserInterface - - type: Physics - canCollide: false - - type: Airtight - airBlocked: false - noAirWhenFullyAirBlocked: true - - type: RadiationBlocker - enabled: false - - type: Occluder - enabled: false - - type: WallMount - arc: 360 - - type: StaticPrice - price: 150 - - type: AccessReader - access: [ [ "Engineering" ] ] - examinationText: access-reader-examination-functionality-restricted - - type: PryUnpowered - pryModifier: 0.5 - - type: PointLight - energy: 0.5 - radius: 1.75 - color: Red - enabled: false - castShadows: false - - type: NavMapDoor - - type: GuideHelp - guides: - - FireAndGasControl - - Fires - - Spacing - - type: SyncSprite + - type: Anchorable + flags: + - Anchorable + - type: Rotatable + - type: AtmosAlarmable + syncWith: + - FireAlarm + - AirAlarm + - type: ApcPowerReceiver + - type: ExtensionCableReceiver + - type: DeviceNetwork + deviceNetId: AtmosDevices + receiveFrequencyId: AtmosMonitor + - type: DeviceNetworkRequiresPower + - type: InteractionOutline + - type: Damageable + damageModifierSet: StructuralMetallicStrong + - type: Injurable + damageContainer: StructuralInorganic + - type: RCDDeconstructable + cost: 4 + delay: 6 + fx: EffectRCDDeconstruct6 + - type: Tag + tags: + - ForceFixRotations # Allow fixrotations to target these + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 500 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - type: Sprite + sprite: Structures/Doors/Airlocks/Standard/firelock.rsi + snapCardinals: true + layers: + - state: closed + map: ["enum.DoorVisualLayers.Base"] + - state: closed_unlit + shader: unshaded + map: ["enum.DoorVisualLayers.BaseUnlit"] + visible: false + - state: welded + map: ["enum.WeldableLayers.BaseWelded"] + - state: bolted_unlit + shader: unshaded + map: ["enum.DoorVisualLayers.BaseBolted"] + visible: false + - state: panel_open + map: ["enum.WiresVisualLayers.MaintenancePanel"] + - state: pressure_unlit + visible: false + shader: unshaded + map: ["enum.FirelockVisualLayersPressure.Base"] + - state: temperature_unlit + visible: false + shader: unshaded + map: ["enum.FirelockVisualLayersTemperature.Base"] + - type: GenericVisualizer + visuals: + enum.FirelockVisuals.PressureWarning: + enum.FirelockVisualLayersPressure.Base: + True: { visible: true } + False: { visible: false } + enum.FirelockVisuals.TemperatureWarning: + enum.FirelockVisualLayersTemperature.Base: + True: { visible: true } + False: { visible: false } + - type: AnimationPlayer + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.49,-0.49,0.49,0.49" # don't want this colliding with walls or they won't close + density: 100 + mask: + - FullTileMask + layer: + - AirlockLayer + - type: Door + openDrawDepth: WallTops + closeTimeOne: 0.1 + closeTimeTwo: 0.6 + openTimeOne: 0.1 + openTimeTwo: 0.6 + state: Open + bumpOpen: false + clickOpen: true + crushDamage: + types: + Blunt: 15 + openSound: + path: /Audio/Machines/airlock_open.ogg + closeSound: + path: /Audio/Machines/airlock_close.ogg + denySound: + path: /Audio/Machines/airlock_deny.ogg + openingAnimationTime: 0.6 + closingAnimationTime: 0.6 + - type: Weldable + fuel: 5 + time: 3 + - type: Firelock + - type: Appearance + - type: WiresVisuals + - type: WiresPanel + - type: UserInterface + interfaces: + enum.WiresUiKey.Key: + type: WiresBoundUserInterface + - type: Physics + canCollide: false + - type: Airtight + airBlocked: false + noAirWhenFullyAirBlocked: true + - type: RadiationBlocker + enabled: false + - type: Occluder + enabled: false + - type: WallMount + arc: 360 + - type: StaticPrice + price: 150 + - type: AccessReader + access: [ [ "Engineering" ] ] + examinationText: access-reader-examination-functionality-restricted + - type: PryUnpowered + pryModifier: 0.5 + - type: PointLight + energy: 0.5 + radius: 1.75 + color: Red + enabled: false + castShadows: false + - type: NavMapDoor + - type: GuideHelp + guides: + - FireAndGasControl + - Fires + - Spacing + - type: SyncSprite - type: entity id: Firelock diff --git a/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml b/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml index b184c6f62f8..716d6980df1 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml @@ -82,12 +82,6 @@ node: metalDoor - type: Destructible thresholds: - - trigger: &DamageTrigger200 # Overkill threshold - !type:DamageTrigger - damage: 200 - behaviors: &OverkillBehavior - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: &DamageTrigger150 !type:DamageTrigger damage: 150 @@ -115,8 +109,6 @@ node: plasmaDoor - type: Destructible thresholds: - - trigger: *DamageTrigger200 - behaviors: *OverkillBehavior - trigger: *DamageTrigger150 behaviors: - !type:DoActsBehavior @@ -142,8 +134,6 @@ node: goldDoor - type: Destructible thresholds: - - trigger: *DamageTrigger200 - behaviors: *OverkillBehavior - trigger: *DamageTrigger150 behaviors: - !type:DoActsBehavior @@ -169,8 +159,6 @@ node: silverDoor - type: Destructible thresholds: - - trigger: *DamageTrigger200 - behaviors: *OverkillBehavior - trigger: *DamageTrigger150 behaviors: - !type:DoActsBehavior @@ -202,8 +190,6 @@ node: bananiumDoor - type: Destructible thresholds: - - trigger: *DamageTrigger200 - behaviors: *OverkillBehavior - trigger: *DamageTrigger150 behaviors: - !type:DoActsBehavior @@ -238,8 +224,6 @@ damageModifierSet: Wood - type: Destructible thresholds: - - trigger: *DamageTrigger150 - behaviors: *OverkillBehavior - trigger: !type:DamageTrigger damage: 75 @@ -274,8 +258,6 @@ damageModifierSet: Wood - type: Destructible thresholds: - - trigger: *DamageTrigger150 - behaviors: *OverkillBehavior - trigger: !type:DamageTrigger damage: 50 @@ -311,8 +293,6 @@ damageModifierSet: Web - type: Destructible thresholds: - - trigger: *DamageTrigger150 - behaviors: *OverkillBehavior - trigger: !type:DamageTrigger damage: 50 @@ -352,8 +332,6 @@ damageModifierSet: Card - type: Destructible thresholds: - - trigger: *DamageTrigger150 - behaviors: *OverkillBehavior - trigger: !type:DamageTrigger damage: 30 @@ -388,10 +366,6 @@ damageModifierSet: Rock - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: *OverkillBehavior - trigger: !type:DamageTrigger damage: 250 # A pickaxe can destroy this in two hits, since the door is rock. @@ -419,10 +393,6 @@ sprite: Structures/Doors/MineralDoors/encrusted_ironstone_door.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 500 - behaviors: *OverkillBehavior - trigger: !type:DamageTrigger damage: 375 diff --git a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml index dd82d594ef0..e7c81e98b0c 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml @@ -94,12 +94,6 @@ fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 200 @@ -225,12 +219,6 @@ damageModifierSet: RGlass - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 400 @@ -300,12 +288,6 @@ visible: false - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 480 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 240 @@ -370,12 +352,6 @@ visible: false - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 800 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 530 @@ -447,12 +423,6 @@ visible: false - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 240 @@ -517,12 +487,6 @@ visible: false - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 1200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 400 diff --git a/Resources/Prototypes/Entities/Structures/Doors/Windoors/clockwork.yml b/Resources/Prototypes/Entities/Structures/Doors/Windoors/clockwork.yml index b36b88d5acf..e0a726e9c44 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Windoors/clockwork.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Windoors/clockwork.yml @@ -35,12 +35,6 @@ map: ["enum.ElectrifiedLayers.Sparks"] - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 200 diff --git a/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml b/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml index 6e2979b6533..bacad307885 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml @@ -30,12 +30,6 @@ base: state_ - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 25 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 5 @@ -88,12 +82,6 @@ base: state_ - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 15 @@ -132,12 +120,6 @@ base: state_ - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 125 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 25 @@ -174,12 +156,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 125 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 25 @@ -215,12 +191,6 @@ sprite: Structures/Furniture/Tables/reinforced.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 150 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 75 @@ -273,15 +243,6 @@ tableMassLimit: 60 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 25 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - trigger: !type:DamageTrigger damage: 5 @@ -331,14 +292,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 45 #excess damage (nuke?). avoid computational cost of spawning entities. - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: /Audio//Weapons/pierce.ogg - trigger: !type:DamageTrigger damage: 15 @@ -383,15 +336,6 @@ tableMassLimit: 120 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - trigger: !type:DamageTrigger damage: 20 @@ -440,15 +384,6 @@ tableMassLimit: 240 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 250 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - trigger: !type:DamageTrigger damage: 50 @@ -493,12 +428,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 125 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 25 @@ -535,15 +464,6 @@ damageModifierSet: Wood - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - trigger: !type:DamageTrigger damage: 15 @@ -584,15 +504,6 @@ damageModifierSet: Wood - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 15 @@ -724,12 +635,6 @@ damageModifierSet: Wood - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 25 @@ -947,12 +852,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 15 @@ -994,12 +893,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 125 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 25 diff --git a/Resources/Prototypes/Entities/Structures/Furniture/altar.yml b/Resources/Prototypes/Entities/Structures/Furniture/altar.yml index 2ba494e8508..6c9176ea5d8 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/altar.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/altar.yml @@ -42,12 +42,6 @@ state: nanotrasen - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 125 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 25 @@ -176,12 +170,6 @@ damageModifierSet: Wood - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 15 @@ -371,12 +359,6 @@ state: full - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 125 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 25 diff --git a/Resources/Prototypes/Entities/Structures/Furniture/beds.yml b/Resources/Prototypes/Entities/Structures/Furniture/beds.yml index bc862fa36d4..1d4a9224440 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/beds.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/beds.yml @@ -41,12 +41,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 75 @@ -104,15 +98,6 @@ damageModifierSet: Wood - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 75 @@ -152,15 +137,6 @@ state: icon - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 50 @@ -193,12 +169,6 @@ Cold: -0.5 - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 75 diff --git a/Resources/Prototypes/Entities/Structures/Furniture/bookshelf.yml b/Resources/Prototypes/Entities/Structures/Furniture/bookshelf.yml index 0c65eac8f12..d68bb6d1069 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/bookshelf.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/bookshelf.yml @@ -21,15 +21,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroyHeavy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 30 diff --git a/Resources/Prototypes/Entities/Structures/Furniture/carpets.yml b/Resources/Prototypes/Entities/Structures/Furniture/carpets.yml index 641f0b70238..812cf68a31a 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/carpets.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/carpets.yml @@ -55,23 +55,17 @@ sprite: Structures/Furniture/Carpets/red_carpet.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - FloorCarpetItemRed: - min: 1 - max: 1 + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + FloorCarpetItemRed: + min: 1 + max: 1 - type: entity id: CarpetBlack @@ -85,23 +79,17 @@ sprite: Structures/Furniture/Carpets/black_carpet.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - FloorCarpetItemBlack: - min: 1 - max: 1 + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + FloorCarpetItemBlack: + min: 1 + max: 1 - type: entity id: CarpetPink @@ -115,23 +103,17 @@ sprite: Structures/Furniture/Carpets/pink_carpet.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - FloorCarpetItemPink: - min: 1 - max: 1 + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + FloorCarpetItemPink: + min: 1 + max: 1 - type: entity id: CarpetBlue @@ -145,23 +127,17 @@ sprite: Structures/Furniture/Carpets/blue_carpet.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - FloorCarpetItemBlue: - min: 1 - max: 1 + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + FloorCarpetItemBlue: + min: 1 + max: 1 - type: entity id: CarpetGreen @@ -175,23 +151,17 @@ sprite: Structures/Furniture/Carpets/green_carpet.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - FloorCarpetItemGreen: - min: 1 - max: 1 + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + FloorCarpetItemGreen: + min: 1 + max: 1 - type: entity id: CarpetOrange @@ -205,23 +175,17 @@ sprite: Structures/Furniture/Carpets/orange_carpet.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - FloorCarpetItemOrange: - min: 1 - max: 1 + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + FloorCarpetItemOrange: + min: 1 + max: 1 - type: entity id: CarpetSBlue @@ -235,23 +199,17 @@ sprite: Structures/Furniture/Carpets/skyblue_carpet.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - FloorCarpetItemSkyBlue: - min: 1 - max: 1 + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + FloorCarpetItemSkyBlue: + min: 1 + max: 1 - type: entity id: CarpetPurple @@ -265,23 +223,17 @@ sprite: Structures/Furniture/Carpets/purple_carpet.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - FloorCarpetItemPurple: - min: 1 - max: 1 + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + FloorCarpetItemPurple: + min: 1 + max: 1 - type: entity id: CarpetCyan @@ -295,23 +247,17 @@ sprite: Structures/Furniture/Carpets/cyan_carpet.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - FloorCarpetItemCyan: - min: 1 - max: 1 + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + FloorCarpetItemCyan: + min: 1 + max: 1 - type: entity id: CarpetWhite @@ -325,23 +271,17 @@ sprite: Structures/Furniture/Carpets/white_carpet.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - FloorCarpetItemWhite: - min: 1 - max: 1 + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + FloorCarpetItemWhite: + min: 1 + max: 1 # TODO nuke this once tiles support rotating sprites - type: entity @@ -367,12 +307,12 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: TrayScanReveal #cardpet @@ -408,25 +348,18 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 40 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - MaterialCardboard: - min: 1 - max: 1 + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + MaterialCardboard: + min: 1 + max: 1 - type: ToolRefinable refineResult: - id: MaterialCardboard1 - amount: 1 - type: TrayScanReveal diff --git a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml index 2fc058f00f0..2a8b7cb9980 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml @@ -33,12 +33,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 @@ -213,15 +207,6 @@ damageModifierSet: Wood - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 25 @@ -279,15 +264,6 @@ collection: MaleScreams - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - path: /Audio/Voice/Human/womanlaugh.ogg - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 50 @@ -324,15 +300,6 @@ damageModifierSet: Web - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 50 @@ -419,12 +386,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 40 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 10 @@ -450,15 +411,6 @@ node: chairWoodBench - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 50 diff --git a/Resources/Prototypes/Entities/Structures/Furniture/dresser.yml b/Resources/Prototypes/Entities/Structures/Furniture/dresser.yml index 8c2974d9e70..335beab7e34 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/dresser.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/dresser.yml @@ -13,12 +13,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 50 diff --git a/Resources/Prototypes/Entities/Structures/Furniture/rollerbeds.yml b/Resources/Prototypes/Entities/Structures/Furniture/rollerbeds.yml index a9558846c21..d9480bdbe27 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/rollerbeds.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/rollerbeds.yml @@ -4,84 +4,78 @@ name: rollerbed description: Used to carry patients around without damaging them. components: - - type: Transform - noRot: true - - type: Item - size: Normal - heldPrefix: rollerbed - - type: Sprite - sprite: Structures/Furniture/rollerbeds.rsi - noRot: true - layers: - - state: rollerbed - map: ["unfoldedLayer"] - - state: rollerbed_folded - map: ["foldedLayer"] - visible: false - - state: rollerbed_buckled - map: ["buckledLayer"] - visible: false - - type: MovedByPressure - - type: DamageOnHighSpeedImpact - soundHit: - collection: MetalThud - - type: InteractionOutline - - type: Physics - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeCircle - radius: 0.35 - density: 20 - mask: - - MobMask - - type: Damageable - - type: Injurable - damageContainer: Inorganic - - type: Destructible - thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - trigger: - !type:DamageTrigger - damage: 50 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:SpawnEntitiesBehavior - spawn: - SheetSteel1: - min: 1 - max: 1 - - type: Pullable - - type: Strap - position: Down - rotation: -90 - buckleOffset: "0,0.15" - buckleOnInteractHand: False - - type: Appearance - - type: GenericVisualizer - visuals: - enum.StrapVisuals.State: - buckledLayer: - True: {visible: true} - False: {visible: false} - unfoldedLayer: - True: { visible: false } - False: { visible: true } - enum.FoldedVisuals.State: # Copypasta from BaseFoldable b/c collections don't merge when overriding component prototypes. - foldedLayer: - True: {visible: true} - False: {visible: false} - unfoldedLayer: - True: {visible: false} - - type: StaticPrice - price: 120 + - type: Transform + noRot: true + - type: Item + size: Normal + heldPrefix: rollerbed + - type: Sprite + sprite: Structures/Furniture/rollerbeds.rsi + noRot: true + layers: + - state: rollerbed + map: ["unfoldedLayer"] + - state: rollerbed_folded + map: ["foldedLayer"] + visible: false + - state: rollerbed_buckled + map: ["buckledLayer"] + visible: false + - type: MovedByPressure + - type: DamageOnHighSpeedImpact + soundHit: + collection: MetalThud + - type: InteractionOutline + - type: Physics + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.35 + density: 20 + mask: + - MobMask + - type: Damageable + - type: Injurable + damageContainer: Inorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel1: + min: 1 + max: 1 + - type: Pullable + - type: Strap + position: Down + rotation: -90 + buckleOffset: "0,0.15" + buckleOnInteractHand: False + - type: Appearance + - type: GenericVisualizer + visuals: + enum.StrapVisuals.State: + buckledLayer: + True: {visible: true} + False: {visible: false} + unfoldedLayer: + True: { visible: false } + False: { visible: true } + enum.FoldedVisuals.State: # Copypasta from BaseFoldable b/c collections don't merge when overriding component prototypes. + foldedLayer: + True: {visible: true} + False: {visible: false} + unfoldedLayer: + True: {visible: false} + - type: StaticPrice + price: 120 - type: entity parent: RollerBed diff --git a/Resources/Prototypes/Entities/Structures/Furniture/sink.yml b/Resources/Prototypes/Entities/Structures/Furniture/sink.yml index 4654c703740..3f3b3e1134f 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/sink.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/sink.yml @@ -12,10 +12,10 @@ - type: Sprite sprite: Structures/Furniture/sink.rsi layers: - - state: sink_stem - - map: [ "enum.SolutionContainerLayers.Fill" ] - state: sink-fill-1 - visible: false + - state: sink_stem + - map: [ "enum.SolutionContainerLayers.Fill" ] + state: sink-fill-1 + visible: false - type: Appearance - type: SolutionContainerVisuals maxFillLevels: 1 @@ -42,21 +42,15 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 80 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 40 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak + - trigger: + !type:DamageTrigger + damage: 40 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak - type: AmbientSound enabled: false volume: -8 diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index 6f6ee83c3e1..33ac061450e 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -185,12 +185,6 @@ price: 1500 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml index 926ca78eb83..7b9a89052e0 100644 --- a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml +++ b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml @@ -60,15 +60,6 @@ - TabletopMachineLayer - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: #excess damage, don't spawn entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - trigger: !type:DamageTrigger damage: 50 @@ -287,12 +278,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage, don't spawn entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 25 @@ -482,15 +467,6 @@ node: emergencyLight - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage, don't spawn entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - trigger: !type:DamageTrigger damage: 25 diff --git a/Resources/Prototypes/Entities/Structures/Lighting/ground_lighting.yml b/Resources/Prototypes/Entities/Structures/Lighting/ground_lighting.yml index ebde012f456..2f6c86f4c58 100644 --- a/Resources/Prototypes/Entities/Structures/Lighting/ground_lighting.yml +++ b/Resources/Prototypes/Entities/Structures/Lighting/ground_lighting.yml @@ -43,12 +43,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/Lighting/strobe_lighting.yml b/Resources/Prototypes/Entities/Structures/Lighting/strobe_lighting.yml index 9ae7550ad75..f2f01a1f4cf 100644 --- a/Resources/Prototypes/Entities/Structures/Lighting/strobe_lighting.yml +++ b/Resources/Prototypes/Entities/Structures/Lighting/strobe_lighting.yml @@ -57,12 +57,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage, don't spawn entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 25 diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/frame.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/frame.yml index a4fb6951f86..6d6b2e4f772 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/frame.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/frame.yml @@ -79,12 +79,6 @@ state: broken - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 diff --git a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml index d858bdcba0a..2d3e2bcf723 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml @@ -10,18 +10,18 @@ noRot: true offset: 0, 0.5 layers: - - sprite: Structures/Piping/Atmospherics/pipe.rsi - state: pipeHalf - offset: 0, -0.5 - map: [ "enum.PipeVisualLayers.Pipe" ] - - state: pod-open - map: [ "enum.CryoPodVisualLayers.Base" ] - - state: cover-on - map: [ "enum.CryoPodVisualLayers.Cover" ] - visible: false - - state: pod-panel - map: [ "enum.WiresVisualLayers.MaintenancePanel" ] - visible: false + - sprite: Structures/Piping/Atmospherics/pipe.rsi + state: pipeHalf + offset: 0, -0.5 + map: [ "enum.PipeVisualLayers.Pipe" ] + - state: pod-open + map: [ "enum.CryoPodVisualLayers.Base" ] + - state: cover-on + map: [ "enum.CryoPodVisualLayers.Cover" ] + visible: false + - state: pod-panel + map: [ "enum.WiresVisualLayers.MaintenancePanel" ] + visible: false - type: InteractionOutline - type: Transform noRot: true @@ -63,14 +63,14 @@ !type:DamageTrigger damage: 100 behaviors: - - !type:EmptyAllContainersBehaviour - - !type:PlaySoundBehavior - sound: - collection: WindowShatter - - !type:ChangeConstructionNodeBehavior - node: machineFrame - - !type:DoActsBehavior - acts: [ "Destruction" ] + - !type:EmptyAllContainersBehaviour + - !type:PlaySoundBehavior + sound: + collection: WindowShatter + - !type:ChangeConstructionNodeBehavior + node: machineFrame + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: ApcPowerReceiver powerLoad: 3000 - type: ExtensionCableReceiver diff --git a/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml b/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml index a250df351c5..c0c41357028 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/base_structuremachines.yml @@ -28,12 +28,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/Machines/crew_monitor_server.yml b/Resources/Prototypes/Entities/Structures/Machines/crew_monitor_server.yml index 369f0681495..5fbbb2875ff 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/crew_monitor_server.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/crew_monitor_server.yml @@ -4,80 +4,74 @@ name: crew monitoring server description: Receives and relays the status of all active suit sensors on the station. components: - - type: Sprite - sprite: Structures/Machines/server.rsi - snapCardinals: true - layers: - - state: server-off - - state: server-on - visible: false - map: [ "enum.PowerDeviceVisualLayers.Powered" ] - - state: variant-crew - - state: server_o - map: ["enum.WiresVisualLayers.MaintenancePanel"] - - type: Construction - graph: Machine - node: machine - containers: - - machine_board - - machine_parts - - type: Machine - board: CrewMonitoringServerMachineCircuitboard - - type: ContainerContainer - containers: - machine_board: !type:Container - machine_parts: !type:Container - - type: CrewMonitoringServer - - type: SingletonDeviceNetServer - - type: DeviceNetwork - deviceNetId: Wireless - transmitFrequencyId: CrewMonitor - receiveFrequencyId: SuitSensor - autoConnect: false - - type: WirelessNetworkConnection - range: 500 - - type: StationLimitedNetwork - - type: ApcPowerReceiver - powerLoad: 200 - - type: ExtensionCableReceiver - - type: WiresPanel - - type: WiresVisuals - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - - !type:SpawnEntitiesBehavior - spawn: - SheetSteel1: - min: 1 - max: 2 - - type: Appearance - - type: GenericVisualizer - visuals: - enum.PowerDeviceVisuals.Powered: - enum.PowerDeviceVisualLayers.Powered: - True: {visible: true} - False: {visible: false} - - type: AmbientOnPowered - - type: AmbientSound - volume: -9 - range: 5 - enabled: false - sound: - path: /Audio/Ambience/Objects/server_fans.ogg - - type: GuideHelp - guides: - - Medical + - type: Sprite + sprite: Structures/Machines/server.rsi + snapCardinals: true + layers: + - state: server-off + - state: server-on + visible: false + map: [ "enum.PowerDeviceVisualLayers.Powered" ] + - state: variant-crew + - state: server_o + map: ["enum.WiresVisualLayers.MaintenancePanel"] + - type: Construction + graph: Machine + node: machine + containers: + - machine_board + - machine_parts + - type: Machine + board: CrewMonitoringServerMachineCircuitboard + - type: ContainerContainer + containers: + machine_board: !type:Container + machine_parts: !type:Container + - type: CrewMonitoringServer + - type: SingletonDeviceNetServer + - type: DeviceNetwork + deviceNetId: Wireless + transmitFrequencyId: CrewMonitor + receiveFrequencyId: SuitSensor + autoConnect: false + - type: WirelessNetworkConnection + range: 500 + - type: StationLimitedNetwork + - type: ApcPowerReceiver + powerLoad: 200 + - type: ExtensionCableReceiver + - type: WiresPanel + - type: WiresVisuals + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 300 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: MetalGlassBreak + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel1: + min: 1 + max: 2 + - type: Appearance + - type: GenericVisualizer + visuals: + enum.PowerDeviceVisuals.Powered: + enum.PowerDeviceVisualLayers.Powered: + True: {visible: true} + False: {visible: false} + - type: AmbientOnPowered + - type: AmbientSound + volume: -9 + range: 5 + enabled: false + sound: + path: /Audio/Ambience/Objects/server_fans.ogg + - type: GuideHelp + guides: + - Medical diff --git a/Resources/Prototypes/Entities/Structures/Machines/frame.yml b/Resources/Prototypes/Entities/Structures/Machines/frame.yml index f8d44f2b401..cd90ae1efd6 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/frame.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/frame.yml @@ -6,66 +6,57 @@ placement: mode: SnapgridCenter components: - - type: Transform - anchored: true - - type: Physics - bodyType: Static - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.25,-0.5,0.25,0.5" - density: 190 - mask: - - MachineMask - layer: - - MachineLayer - - type: Clickable - - type: InteractionOutline - - type: Anchorable - - type: Rotatable - - type: Pullable - - type: Construction - containers: - - machine_board - - machine_parts - graph: Machine - node: missingWires - - type: ContainerContainer - containers: - machine_board: !type:Container - machine_parts: !type:Container - - type: Damageable - damageModifierSet: Metallic - - type: Injurable - damageContainer: StructuralInorganic - - type: Destructible - thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:DoActsBehavior - acts: ["Destruction"] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:ChangeConstructionNodeBehavior - node: start - - !type:DoActsBehavior - acts: ["Destruction"] - - type: Sprite - sprite: Structures/Machines/parts.rsi - state: box_0 - snapCardinals: true + - type: Transform + anchored: true + - type: Physics + bodyType: Static + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.25,-0.5,0.25,0.5" + density: 190 + mask: + - MachineMask + layer: + - MachineLayer + - type: Clickable + - type: InteractionOutline + - type: Anchorable + - type: Rotatable + - type: Pullable + - type: Construction + containers: + - machine_board + - machine_parts + graph: Machine + node: missingWires + - type: ContainerContainer + containers: + machine_board: !type:Container + machine_parts: !type:Container + - type: Damageable + damageModifierSet: Metallic + - type: Injurable + damageContainer: StructuralInorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:ChangeConstructionNodeBehavior + node: start + - !type:DoActsBehavior + acts: ["Destruction"] + - type: Sprite + sprite: Structures/Machines/parts.rsi + state: box_0 + snapCardinals: true - type: entity id: MachineFrame @@ -74,76 +65,67 @@ placement: mode: SnapgridCenter components: - - type: Transform - anchored: true - - type: Physics - bodyType: Static - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.25,-0.5,0.25,0.5" - density: 190 - mask: - - MachineMask - layer: - - MachineLayer - - type: Clickable - - type: InteractionOutline - - type: Anchorable - - type: Rotatable - - type: Pullable - - type: Construction - graph: Machine - node: machineFrame - defaultTarget: machine - containers: - - machine_board - - machine_parts - - type: Damageable - damageModifierSet: Metallic - - type: Injurable - damageContainer: StructuralInorganic - - type: Destructible - thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:DoActsBehavior - acts: ["Destruction"] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:ChangeConstructionNodeBehavior - node: missingWires - - !type:DoActsBehavior - acts: ["Destruction"] - - type: MachineFrame - - type: ContainerContainer - containers: - machine_board: !type:Container - machine_parts: !type:Container - - type: Sprite - sprite: Structures/Machines/parts.rsi - state: box_1 - snapCardinals: true - - type: Appearance - - type: ItemMapper - mapLayers: - box_2: - minCount: 1 - whitelist: - components: - - MachineBoard + - type: Transform + anchored: true + - type: Physics + bodyType: Static + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.25,-0.5,0.25,0.5" + density: 190 + mask: + - MachineMask + layer: + - MachineLayer + - type: Clickable + - type: InteractionOutline + - type: Anchorable + - type: Rotatable + - type: Pullable + - type: Construction + graph: Machine + node: machineFrame + defaultTarget: machine + containers: + - machine_board + - machine_parts + - type: Damageable + damageModifierSet: Metallic + - type: Injurable + damageContainer: StructuralInorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:ChangeConstructionNodeBehavior + node: missingWires + - !type:DoActsBehavior + acts: ["Destruction"] + - type: MachineFrame + - type: ContainerContainer + containers: + machine_board: !type:Container + machine_parts: !type:Container + - type: Sprite + sprite: Structures/Machines/parts.rsi + state: box_1 + snapCardinals: true + - type: Appearance + - type: ItemMapper + mapLayers: + box_2: + minCount: 1 + whitelist: + components: + - MachineBoard - type: entity id: MachineFrameDestroyed @@ -152,38 +134,32 @@ placement: mode: SnapgridCenter components: - - type: InteractionOutline - - type: Transform - anchored: true - noRot: true - - type: Physics - bodyType: Static - - type: Construction - graph: Machine - node: destroyedMachineFrame - - type: Damageable - damageModifierSet: Metallic - - type: Injurable - damageContainer: StructuralInorganic - - type: Destructible - thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - SheetSteel1: - min: 1 - max: 3 - - !type:DoActsBehavior - acts: ["Destruction"] - - type: Sprite - sprite: Structures/Machines/parts.rsi - state: destroyed + - type: InteractionOutline + - type: Transform + anchored: true + noRot: true + - type: Physics + bodyType: Static + - type: Construction + graph: Machine + node: destroyedMachineFrame + - type: Damageable + damageModifierSet: Metallic + - type: Injurable + damageContainer: StructuralInorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel1: + min: 1 + max: 3 + - !type:DoActsBehavior + acts: ["Destruction"] + - type: Sprite + sprite: Structures/Machines/parts.rsi + state: destroyed diff --git a/Resources/Prototypes/Entities/Structures/Machines/research.yml b/Resources/Prototypes/Entities/Structures/Machines/research.yml index 40abd4b6558..8b62b2c07c5 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/research.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/research.yml @@ -38,12 +38,6 @@ board: ResearchAndDevelopmentServerMachineCircuitboard - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 300 @@ -113,12 +107,6 @@ False: {visible: false} - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 200 diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/portable.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/portable.yml index a7f534dfe0d..8b9bf5e7cc7 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/portable.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/portable.yml @@ -73,29 +73,23 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:SpawnEntitiesBehavior - spawn: - SheetSteel1: - min: 1 - max: 3 - SheetGlass1: - min: 1 - max: 2 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 300 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel1: + min: 1 + max: 3 + SheetGlass1: + min: 1 + max: 2 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: GuideHelp guides: - PortableScrubber @@ -165,26 +159,20 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:SpawnEntitiesBehavior - spawn: - SheetSteel1: - min: 1 - max: 3 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 300 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel1: + min: 1 + max: 3 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: GuideHelp guides: - Thermomachines diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml index 663861db03b..72936097dc9 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml @@ -441,12 +441,6 @@ - type: WiresVisuals - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/high_pressure_machine_frame.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/high_pressure_machine_frame.yml index 6f290791d63..1d7acf3efe6 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/high_pressure_machine_frame.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/high_pressure_machine_frame.yml @@ -5,59 +5,59 @@ placement: mode: SnapgridCenter components: - - type: Transform - anchored: true - noRot: true - - type: Physics - bodyType: Static - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.25,-0.4,0.25,0.4" - density: 190 - mask: - - MachineMask - layer: - - MachineLayer - - type: Clickable - - type: InteractionOutline - - type: Anchorable - - type: Pullable - - type: Construction - graph: DisposalMachine - node: frame - - type: Damageable - damageModifierSet: Metallic - - type: Injurable - damageContainer: StructuralInorganic - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:ChangeConstructionNodeBehavior - node: start - - !type:DoActsBehavior - acts: [ "Destruction" ] - - type: Sprite - sprite: Structures/Piping/high_pressure_machine_frame.rsi - layers: - - state: frame - map: [ "enum.ConstructionVisuals.Layer" ] - - type: Appearance - - type: GenericVisualizer - visuals: - enum.ConstructionVisuals.Key: - enum.ConstructionVisuals.Layer: - frame_cable: { state: frame_cables } - frame_electronics: { state: frame_electronics } - frame_unit: { state: frame_unit } - frame_mailing: { state: frame_unit } # not a typo, there is no frame_mailing state. - frame_inlet: { state: frame_inlet } - frame_outlet: { state: frame_outlet } + - type: Transform + anchored: true + noRot: true + - type: Physics + bodyType: Static + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.25,-0.4,0.25,0.4" + density: 190 + mask: + - MachineMask + layer: + - MachineLayer + - type: Clickable + - type: InteractionOutline + - type: Anchorable + - type: Pullable + - type: Construction + graph: DisposalMachine + node: frame + - type: Damageable + damageModifierSet: Metallic + - type: Injurable + damageContainer: StructuralInorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:ChangeConstructionNodeBehavior + node: start + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: Sprite + sprite: Structures/Piping/high_pressure_machine_frame.rsi + layers: + - state: frame + map: [ "enum.ConstructionVisuals.Layer" ] + - type: Appearance + - type: GenericVisualizer + visuals: + enum.ConstructionVisuals.Key: + enum.ConstructionVisuals.Layer: + frame_cable: { state: frame_cables } + frame_electronics: { state: frame_electronics } + frame_unit: { state: frame_unit } + frame_mailing: { state: frame_unit } # not a typo, there is no frame_mailing state. + frame_inlet: { state: frame_inlet } + frame_outlet: { state: frame_outlet } diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml index 1eb3d1343f2..43a39e37720 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml @@ -87,12 +87,6 @@ - MachineLayer - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml index eca8da0b61b..b7675fa7010 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml @@ -62,26 +62,20 @@ - Engineering - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:SpawnEntitiesBehavior - spawn: - SheetSteel1: - min: 5 - max: 5 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel1: + min: 5 + max: 5 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: Anchorable - type: Pullable - type: Rotatable diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml index 0f58aa9d60b..22a6618282e 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml @@ -93,29 +93,20 @@ sprite: Structures/Power/Generation/Tesla/coil_cracks.rsi - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 400 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:DoActsBehavior - acts: ["Destruction"] - - trigger: - !type:DamageTrigger - damage: 225 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:SpawnEntitiesBehavior - spawn: - SheetSteel1: - min: 2 - max: 4 + - trigger: + !type:DamageTrigger + damage: 225 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel1: + min: 2 + max: 4 - type: GuideHelp guides: - TeslaEngine @@ -189,29 +180,20 @@ sprite: Structures/Power/Generation/Tesla/groundingrod_cracks.rsi - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 600 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:DoActsBehavior - acts: ["Destruction"] - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:SpawnEntitiesBehavior - spawn: - SheetSteel1: - min: 2 - max: 4 + - trigger: + !type:DamageTrigger + damage: 300 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel1: + min: 2 + max: 4 - type: GuideHelp guides: - TeslaEngine diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/ame.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/ame.yml index e213aab339a..4a44fce8107 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/ame.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/ame.yml @@ -34,12 +34,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 100 @@ -167,12 +161,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml index 797ffd7959b..4c3c3749ebe 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml @@ -51,12 +51,6 @@ - type: PacifismDangerousAttack - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 @@ -256,26 +250,20 @@ - Power - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:SpawnEntitiesBehavior - spawn: - GeneratorRTGDamaged: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 300 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + GeneratorRTGDamaged: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] # TODO: need radioactive fallout when destroyed @@ -291,8 +279,8 @@ - type: Sprite sprite: Structures/Power/Generation/rtg.rsi layers: - - state: rtg_damaged - - state: rtg_glow + - state: rtg_damaged + - state: rtg_glow - type: PowerMonitoringDevice sprite: Structures/Power/Generation/rtg.rsi state: rtg_damaged @@ -300,12 +288,6 @@ intensity: 2 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml index 873ecd636e9..a685a5e779a 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml @@ -8,98 +8,92 @@ id: PortableGeneratorBase parent: [ BaseMachine, SmallConstructibleMachine, StructureWheeled] components: - # Basic properties - - type: Transform - anchored: False - - type: Physics - bodyType: Dynamic - - type: StaticPrice - price: 500 - - type: AmbientSound - range: 5 - volume: -5 - sound: - path: /Audio/Ambience/Objects/engine_hum.ogg - enabled: false - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.40,-0.40,0.40,0.40" - # Despite the heavy weight, it has wheels, so it's still fairly portable. - density: 155 - mask: - - MachineMask - layer: - - MachineLayer + # Basic properties + - type: Transform + anchored: False + - type: Physics + bodyType: Dynamic + - type: StaticPrice + price: 500 + - type: AmbientSound + range: 5 + volume: -5 + sound: + path: /Audio/Ambience/Objects/engine_hum.ogg + enabled: false + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.40,-0.40,0.40,0.40" + # Despite the heavy weight, it has wheels, so it's still fairly portable. + density: 155 + mask: + - MachineMask + layer: + - MachineLayer - # Visuals - - type: Appearance - - type: Sprite - sprite: Structures/Power/Generation/portable_generator.rsi - noRot: true + # Visuals + - type: Appearance + - type: Sprite + sprite: Structures/Power/Generation/portable_generator.rsi + noRot: true - # Construction, interaction - - type: WiresPanel - - type: UserInterface - interfaces: - enum.GeneratorComponentUiKey.Key: - type: PortableGeneratorBoundUserInterface - - type: ActivatableUI - key: enum.GeneratorComponentUiKey.Key - - type: Electrified - onHandInteract: false - onInteractUsing: false - onBump: false - requirePower: true - highVoltageNode: output + # Construction, interaction + - type: WiresPanel + - type: UserInterface + interfaces: + enum.GeneratorComponentUiKey.Key: + type: PortableGeneratorBoundUserInterface + - type: ActivatableUI + key: enum.GeneratorComponentUiKey.Key + - type: Electrified + onHandInteract: false + onInteractUsing: false + onBump: false + requirePower: true + highVoltageNode: output - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:ChangeConstructionNodeBehavior - node: machineFrame - - !type:DoActsBehavior - acts: ["Destruction"] - - type: GuideHelp - guides: - - PortableGenerator - - Power + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:ChangeConstructionNodeBehavior + node: machineFrame + - !type:DoActsBehavior + acts: ["Destruction"] + - type: GuideHelp + guides: + - PortableGenerator + - Power # Core functionality - - type: PortableGenerator - startSoundEmpty: { collection: GeneratorTugEmpty } - startSound: { collection: GeneratorTug } - - type: FuelGenerator - - type: PowerSupplier - supplyRate: 3000 - supplyRampRate: 500 - supplyRampTolerance: 500 - enabled: false - - type: DeviceLinkSink - ports: - - On - - Off - - Toggle - - type: GeneratorSignalControl - - type: DeviceNetwork - deviceNetId: Wireless - receiveFrequencyId: BasicDevice - - type: WirelessNetworkConnection - range: 200 + - type: PortableGenerator + startSoundEmpty: { collection: GeneratorTugEmpty } + startSound: { collection: GeneratorTug } + - type: FuelGenerator + - type: PowerSupplier + supplyRate: 3000 + supplyRampRate: 500 + supplyRampTolerance: 500 + enabled: false + - type: DeviceLinkSink + ports: + - On + - Off + - Toggle + - type: GeneratorSignalControl + - type: DeviceNetwork + deviceNetId: Wireless + receiveFrequencyId: BasicDevice + - type: WirelessNetworkConnection + range: 200 - type: entity abstract: true @@ -254,107 +248,101 @@ Rated ages 3 and up. suffix: Welding Fuel, 8 kW components: - - type: AmbientSound - range: 4 - volume: -8 + - type: AmbientSound + range: 4 + volume: -8 - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.30,-0.30,0.30,0.30" - density: 80 - mask: - - MachineMask - layer: - - MachineLayer + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.30,-0.30,0.30,0.30" + density: 80 + mask: + - MachineMask + layer: + - MachineLayer - - type: Sprite - layers: - - state: portgen3 - map: [ "enum.GeneratorVisualLayers.Body" ] - - state: portgen3on_unlit - map: [ "enum.GeneratorVisualLayers.Unlit" ] - shader: unshaded - visible: false - - type: GenericVisualizer - visuals: - enum.GeneratorVisuals.Running: - enum.GeneratorVisualLayers.Body: - True: { state: portgen3on } - False: { state: portgen3 } - enum.GeneratorVisualLayers.Unlit: - True: { visible: true } - False: { visible: false } + - type: Sprite + layers: + - state: portgen3 + map: [ "enum.GeneratorVisualLayers.Body" ] + - state: portgen3on_unlit + map: [ "enum.GeneratorVisualLayers.Unlit" ] + shader: unshaded + visible: false + - type: GenericVisualizer + visuals: + enum.GeneratorVisuals.Running: + enum.GeneratorVisualLayers.Body: + True: { state: portgen3on } + False: { state: portgen3 } + enum.GeneratorVisualLayers.Unlit: + True: { visible: true } + False: { visible: false } - - type: Machine - board: PortableGeneratorJrPacmanMachineCircuitboard - - type: FuelGenerator - targetPower: 5000 - minTargetPower: 4000 - optimalPower: 8000 - maxTargetPower: 8000 - # 7.5 minutes at full tank. - optimalBurnRate: 0.11111111 - # Shallow curve that allows you to just barely eek out 12 minutes at lowest. - fuelEfficiencyConstant: 0.3 - - type: ChemicalFuelGeneratorAdapter - solution: tank - reagents: - WeldingFuel: 1 - - type: RefillableSolution - solution: tank - - type: PortableGenerator - # Unreliable bugger - startChance: 0.5 - - type: NodeContainer - examinable: true - nodes: - output: - !type:CableDeviceNode - nodeGroupID: Apc - - type: PowerMonitoringDevice - group: Generator - loadNode: output - sprite: Structures/Power/Generation/portable_generator.rsi - state: portgen3 - - type: PowerSupplier - # No ramping needed on this bugger. - voltage: Apc - supplyRampTolerance: 2000 - - type: GeneratorExhaustGas - gasType: CarbonDioxide - # Full tank is 25 moles of gas - moleRatio: 0.5 - - type: Explosive - explosionType: Default - tileBreakScale: 0 - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:SpillBehavior - solution: tank - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:ChangeConstructionNodeBehavior - node: machineFrame - - !type:DoActsBehavior - acts: ["Destruction"] - - trigger: - !type:DamageTypeTrigger - damageType: Piercing - damage: 75 - behaviors: - - !type:SolutionExplosionBehavior - solution: tank + - type: Machine + board: PortableGeneratorJrPacmanMachineCircuitboard + - type: FuelGenerator + targetPower: 5000 + minTargetPower: 4000 + optimalPower: 8000 + maxTargetPower: 8000 + # 7.5 minutes at full tank. + optimalBurnRate: 0.11111111 + # Shallow curve that allows you to just barely eek out 12 minutes at lowest. + fuelEfficiencyConstant: 0.3 + - type: ChemicalFuelGeneratorAdapter + solution: tank + reagents: + WeldingFuel: 1 + - type: RefillableSolution + solution: tank + - type: PortableGenerator + # Unreliable bugger + startChance: 0.5 + - type: NodeContainer + examinable: true + nodes: + output: + !type:CableDeviceNode + nodeGroupID: Apc + - type: PowerMonitoringDevice + group: Generator + loadNode: output + sprite: Structures/Power/Generation/portable_generator.rsi + state: portgen3 + - type: PowerSupplier + # No ramping needed on this bugger. + voltage: Apc + supplyRampTolerance: 2000 + - type: GeneratorExhaustGas + gasType: CarbonDioxide + # Full tank is 25 moles of gas + moleRatio: 0.5 + - type: Explosive + explosionType: Default + tileBreakScale: 0 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:SpillBehavior + solution: tank + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:ChangeConstructionNodeBehavior + node: machineFrame + - !type:DoActsBehavior + acts: ["Destruction"] + - trigger: + !type:DamageTypeTrigger + damageType: Piercing + damage: 75 + behaviors: + - !type:SolutionExplosionBehavior + solution: tank diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml index 9de622d4d32..fe63697c872 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml @@ -186,15 +186,6 @@ state: solar_panel_glass_broken - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 50 @@ -224,15 +215,6 @@ state: solar_panel_plasma_broken - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 50 @@ -262,15 +244,6 @@ state: solar_panel_uranium_broken - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 50 @@ -323,7 +296,7 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities + - trigger: !type:DamageTrigger damage: 200 behaviors: diff --git a/Resources/Prototypes/Entities/Structures/Power/apc.yml b/Resources/Prototypes/Entities/Structures/Power/apc.yml index 8ccd311ea35..15e9c4594d2 100644 --- a/Resources/Prototypes/Entities/Structures/Power/apc.yml +++ b/Resources/Prototypes/Entities/Structures/Power/apc.yml @@ -117,15 +117,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: #excess damage, don't spawn entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - trigger: !type:DamageTrigger damage: 50 @@ -175,12 +166,6 @@ node: apcFrame - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: #excess damage, don't spawn entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 diff --git a/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml b/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml index d57748a5dff..39b515b5828 100644 --- a/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml +++ b/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml @@ -5,50 +5,50 @@ placement: mode: SnapgridCenter components: - - type: Sprite - sprite: Structures/Power/cable_terminal.rsi - state: term - drawdepth: BelowFloor - - type: Clickable - - type: InteractionOutline - - type: CollideOnAnchor - - type: Transform - anchored: true - - type: Damageable - damageModifierSet: Metallic - - type: Injurable - damageContainer: StructuralInorganic - - type: RCDDeconstructable - cost: 2 - delay: 0 - fx: EffectRCDConstruct0 - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - type: Visibility - layer: 1 - - type: Appearance - - type: SubFloorHide - blockAmbience: false - blockInteractions: false - - type: Construction - graph: CableTerminal - node: cable_terminal - - type: NodeContainer - nodes: - # Just define nodes for both MV and HV. One will end up unused in 99% of cases - # but it means one cable terminal type. - powerHV: - !type:CableTerminalNode - nodeGroupID: HVPower - powerMV: - !type:CableTerminalNode - nodeGroupID: MVPower + - type: Sprite + sprite: Structures/Power/cable_terminal.rsi + state: term + drawdepth: BelowFloor + - type: Clickable + - type: InteractionOutline + - type: CollideOnAnchor + - type: Transform + anchored: true + - type: Damageable + damageModifierSet: Metallic + - type: Injurable + damageContainer: StructuralInorganic + - type: RCDDeconstructable + cost: 2 + delay: 0 + fx: EffectRCDConstruct0 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - type: Visibility + layer: 1 + - type: Appearance + - type: SubFloorHide + blockAmbience: false + blockInteractions: false + - type: Construction + graph: CableTerminal + node: cable_terminal + - type: NodeContainer + nodes: + # Just define nodes for both MV and HV. One will end up unused in 99% of cases + # but it means one cable terminal type. + powerHV: + !type:CableTerminalNode + nodeGroupID: HVPower + powerMV: + !type:CableTerminalNode + nodeGroupID: MVPower - type: entity id: CableTerminalUncuttable diff --git a/Resources/Prototypes/Entities/Structures/Power/cables.yml b/Resources/Prototypes/Entities/Structures/Power/cables.yml index e898e28f518..94a51cc0dc4 100644 --- a/Resources/Prototypes/Entities/Structures/Power/cables.yml +++ b/Resources/Prototypes/Entities/Structures/Power/cables.yml @@ -82,12 +82,6 @@ cableType: HighVoltage - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 @@ -142,12 +136,6 @@ cableType: MediumVoltage - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 @@ -197,12 +185,6 @@ cableType: Apc - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: #excess damage (nuke?). avoid computational cost of spawning entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 diff --git a/Resources/Prototypes/Entities/Structures/Power/chargers.yml b/Resources/Prototypes/Entities/Structures/Power/chargers.yml index 469de4bad35..cab8135c7e9 100644 --- a/Resources/Prototypes/Entities/Structures/Power/chargers.yml +++ b/Resources/Prototypes/Entities/Structures/Power/chargers.yml @@ -19,12 +19,6 @@ delay: 1 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 80 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 40 diff --git a/Resources/Prototypes/Entities/Structures/Power/substation.yml b/Resources/Prototypes/Entities/Structures/Power/substation.yml index bb478a532b0..786621ee5de 100644 --- a/Resources/Prototypes/Entities/Structures/Power/substation.yml +++ b/Resources/Prototypes/Entities/Structures/Power/substation.yml @@ -102,12 +102,6 @@ state: substation_static - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 @@ -195,12 +189,6 @@ state: substation_wall_static - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml b/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml index 786ec54b23f..df0c803ea18 100644 --- a/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml +++ b/Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml @@ -64,10 +64,10 @@ !type:DamageTrigger damage: 150 behaviors: - - !type:ChangeConstructionNodeBehavior - node: machineFrame - - !type:DoActsBehavior - acts: ["Destruction"] + - !type:ChangeConstructionNodeBehavior + node: machineFrame + - !type:DoActsBehavior + acts: ["Destruction"] - type: Gun fireRate: 5 useKey: false @@ -122,10 +122,10 @@ !type:DamageTrigger damage: 300 behaviors: - - !type:ChangeConstructionNodeBehavior - node: machineFrame - - !type:DoActsBehavior - acts: ["Destruction"] + - !type:ChangeConstructionNodeBehavior + node: machineFrame + - !type:DoActsBehavior + acts: ["Destruction"] - type: Gun fireRate: 1 soundGunshot: @@ -181,10 +181,10 @@ !type:DamageTrigger damage: 200 behaviors: - - !type:ChangeConstructionNodeBehavior - node: machineFrame - - !type:DoActsBehavior - acts: ["Destruction"] + - !type:ChangeConstructionNodeBehavior + node: machineFrame + - !type:DoActsBehavior + acts: ["Destruction"] - type: Gun projectileSpeed: 80 fireRate: 1 @@ -235,10 +235,10 @@ !type:DamageTrigger damage: 350 behaviors: - - !type:ChangeConstructionNodeBehavior - node: machineFrame - - !type:DoActsBehavior - acts: ["Destruction"] + - !type:ChangeConstructionNodeBehavior + node: machineFrame + - !type:DoActsBehavior + acts: ["Destruction"] - type: Gun projectileSpeed: 40 fireRate: 0.3 @@ -291,8 +291,8 @@ !type:DamageTrigger damage: 300 behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - !type:DoActsBehavior + acts: ["Destruction"] - type: Sprite sprite: Objects/Weapons/Guns/Shuttles/pirate_cannon.rsi layers: @@ -332,10 +332,10 @@ !type:DamageTrigger damage: 100 behaviors: - - !type:ChangeConstructionNodeBehavior - node: machineFrame - - !type:DoActsBehavior - acts: ["Destruction"] + - !type:ChangeConstructionNodeBehavior + node: machineFrame + - !type:DoActsBehavior + acts: ["Destruction"] - type: Gun projectileSpeed: 20 fireRate: 2 diff --git a/Resources/Prototypes/Entities/Structures/Shuttles/station_anchor.yml b/Resources/Prototypes/Entities/Structures/Shuttles/station_anchor.yml index 5de18edfc46..952ee35eca5 100644 --- a/Resources/Prototypes/Entities/Structures/Shuttles/station_anchor.yml +++ b/Resources/Prototypes/Entities/Structures/Shuttles/station_anchor.yml @@ -57,59 +57,59 @@ placement: mode: AlignTileAny components: - - type: StationAiWhitelist - - type: PowerCharge - windowTitle: station-anchor-window-title - idlePower: 50 - activePower: 2500 - chargeRate: 0.5 - - type: ActivatableUI - key: enum.PowerChargeUiKey.Key - - type: ActivatableUIRequiresPower - - type: Anchorable - - type: ApcPowerReceiver - powerLoad: 2500 - - type: ExtensionCableReceiver - - type: Damageable - damageModifierSet: Metallic - - type: Injurable - damageContainer: Inorganic - - type: Repairable - fuelCost: 10 - doAfterDelay: 5 - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 150 - behaviors: - - !type:DoActsBehavior - acts: [ "Breakage" ] - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - type: StaticPrice - price: 10000 - - type: Machine - board: StationAnchorCircuitboard - - type: ContainerContainer - containers: - machine_board: !type:Container - machine_parts: !type:Container - - type: Construction - containers: - - machine_parts - - machine_board - - type: UserInterface - interfaces: - enum.PowerChargeUiKey.Key: - type: PowerChargeBoundUserInterface + - type: StationAiWhitelist + - type: PowerCharge + windowTitle: station-anchor-window-title + idlePower: 50 + activePower: 2500 + chargeRate: 0.5 + - type: ActivatableUI + key: enum.PowerChargeUiKey.Key + - type: ActivatableUIRequiresPower + - type: Anchorable + - type: ApcPowerReceiver + powerLoad: 2500 + - type: ExtensionCableReceiver + - type: Damageable + damageModifierSet: Metallic + - type: Injurable + damageContainer: Inorganic + - type: Repairable + fuelCost: 10 + doAfterDelay: 5 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 150 + behaviors: + - !type:DoActsBehavior + acts: [ "Breakage" ] + - trigger: + !type:DamageTrigger + damage: 600 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - type: StaticPrice + price: 10000 + - type: Machine + board: StationAnchorCircuitboard + - type: ContainerContainer + containers: + machine_board: !type:Container + machine_parts: !type:Container + - type: Construction + containers: + - machine_parts + - machine_board + - type: UserInterface + interfaces: + enum.PowerChargeUiKey.Key: + type: PowerChargeBoundUserInterface - type: entity parent: StationAnchor diff --git a/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml b/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml index 3264d9bd6fb..02576ea1996 100644 --- a/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml +++ b/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml @@ -4,62 +4,62 @@ name: thruster description: A thruster that allows a shuttle to move. abstract: true - components: - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.45,-0.45,0.45,0.45" - density: 60 - mask: - - MachineMask - layer: - - MachineLayer - - type: AmbientSound - enabled: false - range: 4 - volume: -4 - sound: - path: /Audio/Effects/shuttle_thruster.ogg - - type: Transform - anchored: true - - type: Physics - bodyType: Static - - type: Rotatable - rotateWhileAnchored: true - - type: Thruster - damage: - types: - Heat: 40 - - type: InteractionOutline - - type: Sprite - - type: Appearance - - type: ApcPowerReceiver - powerLoad: 1500 - - type: ExtensionCableReceiver - - type: Damageable - damageModifierSet: Electronic - - type: Injurable - damageContainer: StructuralInorganic - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 300 # Changed 100->300 because impact damage is real - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - type: StaticPrice - price: 300 - - type: GuideHelp - guides: - - ShuttleCraft placement: mode: SnapgridCenter + components: + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.45,-0.45,0.45,0.45" + density: 60 + mask: + - MachineMask + layer: + - MachineLayer + - type: AmbientSound + enabled: false + range: 4 + volume: -4 + sound: + path: /Audio/Effects/shuttle_thruster.ogg + - type: Transform + anchored: true + - type: Physics + bodyType: Static + - type: Rotatable + rotateWhileAnchored: true + - type: Thruster + damage: + types: + Heat: 40 + - type: InteractionOutline + - type: Sprite + - type: Appearance + - type: ApcPowerReceiver + powerLoad: 1500 + - type: ExtensionCableReceiver + - type: Damageable + damageModifierSet: Electronic + - type: Injurable + damageContainer: StructuralInorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 300 # Changed 100->300 because impact damage is real + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - type: StaticPrice + price: 300 + - type: GuideHelp + guides: + - ShuttleCraft - type: entity id: Thruster @@ -70,23 +70,17 @@ board: ThrusterMachineCircuitboard - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 300 behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:ChangeConstructionNodeBehavior - node: machineFrame + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:ChangeConstructionNodeBehavior + node: machineFrame - type: Sprite sprite: Structures/Shuttles/thruster.rsi layers: @@ -126,21 +120,15 @@ components: - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 1800 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 900 behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak - type: Fixtures fixtures: fix1: @@ -256,23 +244,17 @@ board: GyroscopeMachineCircuitboard - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 100 behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:ChangeConstructionNodeBehavior - node: machineFrame + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:ChangeConstructionNodeBehavior + node: machineFrame - type: Damageable damageModifierSet: Electronic - type: Injurable @@ -323,41 +305,41 @@ name: rusted thruster description: An unrepairable thruster, broken down from wear and tear. Only useful for scrapping. components: - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.45,-0.45,0.45,0.45" - density: 60 - mask: - - MachineMask - layer: - - MachineLayer - - type: Transform - anchored: true - - type: Physics - bodyType: Static - - type: Rotatable - rotateWhileAnchored: true - - type: Sprite - sprite: Structures/Shuttles/old_thruster.rsi - state: base - - type: Damageable - damageModifierSet: Electronic - - type: Injurable - damageContainer: Inorganic - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 100 # Considering we need a lot of thrusters didn't want to make an individual one too tanky - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - type: PhysicalComposition - materialComposition: - Steel: 1200 # 12 sheets + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.45,-0.45,0.45,0.45" + density: 60 + mask: + - MachineMask + layer: + - MachineLayer + - type: Transform + anchored: true + - type: Physics + bodyType: Static + - type: Rotatable + rotateWhileAnchored: true + - type: Sprite + sprite: Structures/Shuttles/old_thruster.rsi + state: base + - type: Damageable + damageModifierSet: Electronic + - type: Injurable + damageContainer: Inorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 # Considering we need a lot of thrusters didn't want to make an individual one too tanky + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - type: PhysicalComposition + materialComposition: + Steel: 1200 # 12 sheets diff --git a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml index 9e828925cc5..01e678d0cfb 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml @@ -849,17 +849,17 @@ damageContainer: Biological - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 1 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:SpawnEntitiesBehavior - spawn: - EffectAnomalyFloraBulb: - min: 1 - max: 1 + - trigger: + !type:DamageTrigger + damage: 1 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:SpawnEntitiesBehavior + spawn: + EffectAnomalyFloraBulb: + min: 1 + max: 1 - type: PointLight radius: 2.0 energy: 4.5 diff --git a/Resources/Prototypes/Entities/Structures/Specific/Cargo/mailcart.yml b/Resources/Prototypes/Entities/Structures/Specific/Cargo/mailcart.yml index f0ff7f578c0..658562e89ce 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Cargo/mailcart.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Cargo/mailcart.yml @@ -5,92 +5,87 @@ parent: [BaseStructureDynamic, StructureWheeled] description: Deliver packages with style and efficiency. components: - - type: Sprite - noRot: true - sprite: Objects/Specific/Cargo/mailcart.rsi - layers: - - state: mailcart_base - - type: InteractionOutline - - type: Storage - grid: - - 0,0,15,7 - quickInsert: true - maxItemSize: Huge - whitelist: - components: - - Delivery - - WrappedParcel - tags: - - Paper - - Document - - BoxCardboard - - Folder - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.18,-0.2,0.18,0.2" - density: 60 - mask: - - FullTileMask - layer: - - LargeMobLayer - - type: Damageable - damageModifierSet: Metallic - - type: Injurable - damageContainer: Inorganic - - type: Destructible - thresholds: - - trigger: !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - trigger: !type:DamageTrigger - damage: 200 - behaviors: - - !type:EmptyAllContainersBehaviour - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - type: ItemMapper - mapLayers: - package_1: - minCount: 1 - whitelist: &PackageWhitelist - tags: - - PackageDelivery - package_2: - minCount: 2 - whitelist: *PackageWhitelist - package_3: - minCount: 3 - whitelist: *PackageWhitelist - package_4: - minCount: 4 - whitelist: *PackageWhitelist - package_5: - minCount: 5 - whitelist: *PackageWhitelist - package_6: - minCount: 6 - whitelist: *PackageWhitelist - package_7: - minCount: 7 - whitelist: *PackageWhitelist - package_8: - minCount: 8 - whitelist: *PackageWhitelist - sprite: Objects/Specific/Cargo/mailcart.rsi - - type: Appearance - - type: UserInterface - interfaces: - enum.StorageUiKey.Key: - type: StorageBoundUserInterface - - type: ContainerContainer - containers: - storagebase: !type:Container - ents: [] + - type: Sprite + noRot: true + sprite: Objects/Specific/Cargo/mailcart.rsi + layers: + - state: mailcart_base + - type: InteractionOutline + - type: Storage + grid: + - 0,0,15,7 + quickInsert: true + maxItemSize: Huge + whitelist: + components: + - Delivery + - WrappedParcel + tags: + - Paper + - Document + - BoxCardboard + - Folder + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.18,-0.2,0.18,0.2" + density: 60 + mask: + - FullTileMask + layer: + - LargeMobLayer + - type: Damageable + damageModifierSet: Metallic + - type: Injurable + damageContainer: Inorganic + - type: Destructible + thresholds: + - trigger: !type:DamageTrigger + damage: 200 + behaviors: + - !type:EmptyAllContainersBehaviour + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - type: ItemMapper + mapLayers: + package_1: + minCount: 1 + whitelist: &PackageWhitelist + tags: + - PackageDelivery + package_2: + minCount: 2 + whitelist: *PackageWhitelist + package_3: + minCount: 3 + whitelist: *PackageWhitelist + package_4: + minCount: 4 + whitelist: *PackageWhitelist + package_5: + minCount: 5 + whitelist: *PackageWhitelist + package_6: + minCount: 6 + whitelist: *PackageWhitelist + package_7: + minCount: 7 + whitelist: *PackageWhitelist + package_8: + minCount: 8 + whitelist: *PackageWhitelist + sprite: Objects/Specific/Cargo/mailcart.rsi + - type: Appearance + - type: UserInterface + interfaces: + enum.StorageUiKey.Key: + type: StorageBoundUserInterface + - type: ContainerContainer + containers: + storagebase: !type:Container + ents: [] diff --git a/Resources/Prototypes/Entities/Structures/Specific/Janitor/drain.yml b/Resources/Prototypes/Entities/Structures/Specific/Janitor/drain.yml index 0520a0e3303..4c301068222 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Janitor/drain.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Janitor/drain.yml @@ -6,56 +6,50 @@ placement: mode: SnapgridCenter components: - - type: Sprite - drawdepth: FloorObjects - sprite: Objects/Specific/Janitorial/drain.rsi - layers: - - state: icon - - map: [ "enum.SolutionContainerLayers.Fill" ] - state: fill-1 - visible: false - - type: InteractionOutline - - type: Clickable - - type: Transform - anchored: true - - type: Physics - bodyType: Static - canCollide: false - - type: AmbientSound - enabled: false - volume: -8 - range: 8 - sound: - path: /Audio/Ambience/Objects/drain.ogg - - type: Drain - - type: DumpableSolution - solution: drainBuffer - - type: Appearance - - type: SolutionContainerVisuals - maxFillLevels: 1 - fillBaseName: fill- - solutionName: drainBuffer - - type: Damageable - damageModifierSet: StructuralMetallic - - type: Injurable - damageContainer: StructuralInorganic - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak + - type: Sprite + drawdepth: FloorObjects + sprite: Objects/Specific/Janitorial/drain.rsi + layers: + - state: icon + - map: [ "enum.SolutionContainerLayers.Fill" ] + state: fill-1 + visible: false + - type: InteractionOutline + - type: Clickable + - type: Transform + anchored: true + - type: Physics + bodyType: Static + canCollide: false + - type: AmbientSound + enabled: false + volume: -8 + range: 8 + sound: + path: /Audio/Ambience/Objects/drain.ogg + - type: Drain + - type: DumpableSolution + solution: drainBuffer + - type: Appearance + - type: SolutionContainerVisuals + maxFillLevels: 1 + fillBaseName: fill- + solutionName: drainBuffer + - type: Damageable + damageModifierSet: StructuralMetallic + - type: Injurable + damageContainer: StructuralInorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak - type: entity parent: [SolutionDrain, SolutionNormal] diff --git a/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml b/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml index 84c8208320b..597d2932962 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml @@ -149,227 +149,221 @@ name: janitorial trolley description: This is the alpha and omega of sanitation. components: - - type: Sprite - noRot: true - sprite: Objects/Specific/Janitorial/janitorial_cart.rsi - layers: - - state: cart - - state: cart_water-1 - map: ["enum.SolutionContainerLayers.Fill"] - visible: false - - type: Rotatable - - type: InteractionOutline - # Removing storage until OnInteractUsing logic resolved - #- type: Storage - # popup: false - # capacity: 80 - # blacklist: # there is exclusive item slots for that - # tags: - # - Mop - # - TrashBag - # - Bucket - - type: ItemSlots - slots: - mop_slot: - name: janitorial-trolley-slot-component-slot-name-mop - whitelist: - tags: - - Mop - insertOnInteract: false # or it conflicts with bucket logic - priority: 9 # Higher than bucket slot - plunger_slot: - name: janitorial-trolley-slot-component-slot-name-plunger - whitelist: - tags: - - Plunger - - GoldenPlunger - priority: 8 - wetfloorsign_slot4: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - wetfloorsign_slot3: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - wetfloorsign_slot2: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - wetfloorsign_slot1: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - lightreplacer_slot: - name: janitorial-trolley-slot-component-slot-name-lightreplacer - whitelist: - components: - - LightReplacer - priority: 6 - spraybottle_slot: - name: janitorial-trolley-slot-component-slot-name-spray - whitelist: - tags: - - Spray - insertOnInteract: false # or it conflicts with bucket logic - priority: 5 # Higher than bucket slot - bucket_slot: - name: janitorial-trolley-slot-component-slot-name-bucket - whitelist: - tags: - - Bucket - insertOnInteract: false # or it also conflicts with bucket logic - priority: 4 # Higher than trash bag slot - trashbag_slot: - name: janitorial-trolley-slot-component-slot-name-trashbag - whitelist: - tags: - - TrashBag - priority: 3 # Higher than drinking priority - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeCircle - radius: 0.3 - density: 250 - layer: - - MobLayer - mask: - - MobMask - - type: Spillable - solution: tank - spillDelay: 3.0 - spillWhenThrown: false - - type: Solution - solution: - reagents: - - ReagentId: Water - Quantity: 960 - - type: DrainableSolution - solution: tank - - type: RefillableSolution - solution: tank - - type: ExaminableSolution - solution: tank - - type: Damageable - damageModifierSet: Metallic - - type: Injurable - damageContainer: Inorganic - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:EmptyAllContainersBehaviour - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - type: ItemMapper - mapLayers: - cart_plunger: - whitelist: - tags: - - Plunger - cart_goldenplunger: - whitelist: - tags: - - GoldenPlunger - cart_mop: - whitelist: - tags: - - MopBasic - cart_advmop: - whitelist: - tags: - - MopAdv - cart_garbage: - whitelist: - tags: - - TrashBag - cart_garbage_blue: - whitelist: - tags: - - TrashBagBlue - cart_replacer: - whitelist: - components: - - LightReplacer - cart_spray: - whitelist: - tags: - - Spray - cart_sign1: # this is like stack of floor signs - minCount: 1 - whitelist: - tags: - - WetFloorSign - cart_sign2: - minCount: 2 - whitelist: - tags: - - WetFloorSign - cart_sign3: - minCount: 3 - whitelist: - tags: - - WetFloorSign - cart_sign4: - minCount: 4 - whitelist: - tags: - - WetFloorSign - cart_bucket: - whitelist: - tags: - - Bucket - sprite: Objects/Specific/Janitorial/janitorial_cart.rsi - - type: Appearance - - type: SolutionContainerVisuals - maxFillLevels: 3 - fillBaseName: cart_water- - - type: UserInterface - interfaces: - enum.StorageUiKey.Key: - type: StorageBoundUserInterface - - type: Edible - edible: Drink - solution: tank - destroyOnEmpty: false - utensil: Spoon - - type: ContainerContainer - containers: - storagebase: !type:Container - ents: [] - mop_slot: !type:ContainerSlot {} - trashbag_slot: !type:ContainerSlot {} - bucket_slot: !type:ContainerSlot {} - plunger_slot: !type:ContainerSlot {} - goldenplunger_slot: !type:ContainerSlot {} - wetfloorsign_slot4: !type:ContainerSlot {} - wetfloorsign_slot3: !type:ContainerSlot {} - wetfloorsign_slot2: !type:ContainerSlot {} - wetfloorsign_slot1: !type:ContainerSlot {} - lightreplacer_slot: !type:ContainerSlot {} - spraybottle_slot: !type:ContainerSlot {} - - type: GuideHelp - guides: - - Janitorial - - type: DnaSubstanceTrace + - type: Sprite + noRot: true + sprite: Objects/Specific/Janitorial/janitorial_cart.rsi + layers: + - state: cart + - state: cart_water-1 + map: ["enum.SolutionContainerLayers.Fill"] + visible: false + - type: Rotatable + - type: InteractionOutline + # Removing storage until OnInteractUsing logic resolved + #- type: Storage + # popup: false + # capacity: 80 + # blacklist: # there is exclusive item slots for that + # tags: + # - Mop + # - TrashBag + # - Bucket + - type: ItemSlots + slots: + mop_slot: + name: janitorial-trolley-slot-component-slot-name-mop + whitelist: + tags: + - Mop + insertOnInteract: false # or it conflicts with bucket logic + priority: 9 # Higher than bucket slot + plunger_slot: + name: janitorial-trolley-slot-component-slot-name-plunger + whitelist: + tags: + - Plunger + - GoldenPlunger + priority: 8 + wetfloorsign_slot4: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + wetfloorsign_slot3: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + wetfloorsign_slot2: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + wetfloorsign_slot1: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + lightreplacer_slot: + name: janitorial-trolley-slot-component-slot-name-lightreplacer + whitelist: + components: + - LightReplacer + priority: 6 + spraybottle_slot: + name: janitorial-trolley-slot-component-slot-name-spray + whitelist: + tags: + - Spray + insertOnInteract: false # or it conflicts with bucket logic + priority: 5 # Higher than bucket slot + bucket_slot: + name: janitorial-trolley-slot-component-slot-name-bucket + whitelist: + tags: + - Bucket + insertOnInteract: false # or it also conflicts with bucket logic + priority: 4 # Higher than trash bag slot + trashbag_slot: + name: janitorial-trolley-slot-component-slot-name-trashbag + whitelist: + tags: + - TrashBag + priority: 3 # Higher than drinking priority + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.3 + density: 250 + layer: + - MobLayer + mask: + - MobMask + - type: Spillable + solution: tank + spillDelay: 3.0 + spillWhenThrown: false + - type: Solution + solution: + reagents: + - ReagentId: Water + Quantity: 960 + - type: DrainableSolution + solution: tank + - type: RefillableSolution + solution: tank + - type: ExaminableSolution + solution: tank + - type: Damageable + damageModifierSet: Metallic + - type: Injurable + damageContainer: Inorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:EmptyAllContainersBehaviour + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - type: ItemMapper + mapLayers: + cart_plunger: + whitelist: + tags: + - Plunger + cart_goldenplunger: + whitelist: + tags: + - GoldenPlunger + cart_mop: + whitelist: + tags: + - MopBasic + cart_advmop: + whitelist: + tags: + - MopAdv + cart_garbage: + whitelist: + tags: + - TrashBag + cart_garbage_blue: + whitelist: + tags: + - TrashBagBlue + cart_replacer: + whitelist: + components: + - LightReplacer + cart_spray: + whitelist: + tags: + - Spray + cart_sign1: # this is like stack of floor signs + minCount: 1 + whitelist: + tags: + - WetFloorSign + cart_sign2: + minCount: 2 + whitelist: + tags: + - WetFloorSign + cart_sign3: + minCount: 3 + whitelist: + tags: + - WetFloorSign + cart_sign4: + minCount: 4 + whitelist: + tags: + - WetFloorSign + cart_bucket: + whitelist: + tags: + - Bucket + sprite: Objects/Specific/Janitorial/janitorial_cart.rsi + - type: Appearance + - type: SolutionContainerVisuals + maxFillLevels: 3 + fillBaseName: cart_water- + - type: UserInterface + interfaces: + enum.StorageUiKey.Key: + type: StorageBoundUserInterface + - type: Edible + edible: Drink + solution: tank + destroyOnEmpty: false + utensil: Spoon + - type: ContainerContainer + containers: + storagebase: !type:Container + ents: [] + mop_slot: !type:ContainerSlot {} + trashbag_slot: !type:ContainerSlot {} + bucket_slot: !type:ContainerSlot {} + plunger_slot: !type:ContainerSlot {} + goldenplunger_slot: !type:ContainerSlot {} + wetfloorsign_slot4: !type:ContainerSlot {} + wetfloorsign_slot3: !type:ContainerSlot {} + wetfloorsign_slot2: !type:ContainerSlot {} + wetfloorsign_slot1: !type:ContainerSlot {} + lightreplacer_slot: !type:ContainerSlot {} + spraybottle_slot: !type:ContainerSlot {} + - type: GuideHelp + guides: + - Janitorial + - type: DnaSubstanceTrace diff --git a/Resources/Prototypes/Entities/Structures/Specific/xeno.yml b/Resources/Prototypes/Entities/Structures/Specific/xeno.yml index bfcfca0342a..d95c862497c 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/xeno.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/xeno.yml @@ -44,12 +44,12 @@ density: 1000 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 50 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity parent: XenoWardingTower diff --git a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml index ef749cfafee..f1439caedfa 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml @@ -5,152 +5,146 @@ name: gas canister description: A canister that can contain any type of gas. It can be attached to connector ports using a wrench. components: - - type: Animateable - - type: InteractionOutline - - type: Transform - noRot: true - - type: Sprite - sprite: Structures/Storage/canister.rsi - noRot: true - layers: - - state: grey - - state: paper - visible: false - map: [ "enum.PaperLabelVisuals.Layer" ] - - state: integrity-unshaded-0 - map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] - shader: unshaded - visible: false - - state: mask-grey - map: [ "enum.MaxPressureVisualLayers.Base" ] - visible: false - - type: Appearance - - type: GenericVisualizer - visuals: - enum.AnchorVisuals.Anchored: - connectedToPort: - False: { state: can-connector, visible: false } - True: { state: can-connector, visible: true } - enum.GasCanisterVisuals.TankInserted: - tankInserted: - False: { state: can-open, visible: false } - True: { state: can-open, visible: true } - enum.LockVisuals.Locked: - locked: - False: { state: unlocked, shader: "unshaded" } - True: { state: locked, shader: "unshaded" } - enum.GasCanisterVisuals.PressureState: - pressureLight: - 0: { state: can-o0, shader: "unshaded" } - 1: { state: can-o1, shader: "unshaded" } - 2: { state: can-o2, shader: "unshaded" } - 3: { state: can-o3, shader: "unshaded" } - enum.PaperLabelVisuals.HasLabel: - enum.PaperLabelVisuals.Layer: - True: { visible: true } - False: { visible: false } - enum.StorageVisuals.Open: - enum.PaperLabelVisuals.Layer: - True: { visible: false } - enum.PaperLabelVisuals.LabelType: - enum.PaperLabelVisuals.Layer: - Paper: { state: paper } - Bounty: { state: bounty } - CaptainsPaper: { state: captains_paper } - Invoice: { state: invoice } - - type: ActivatableUI - key: enum.GasCanisterUiKey.Key - - type: UIRequiresLock - userInterfaceKeys: - - enum.GasCanisterUiKey.Key - - type: UserInterface - interfaces: - enum.GasCanisterUiKey.Key: - type: GasCanisterBoundUserInterface - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:SpawnEntitiesBehavior - spawn: - GasCanisterBrokenBase: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:DumpCanisterBehavior - - type: Damageable - damageModifierSet: Metallic - - type: Injurable - damageContainer: Inorganic - - type: Physics - bodyType: Dynamic - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.25,-0.25,0.25,0.25" - density: 190 - mask: - - SmallMobMask - layer: - - MachineLayer - - type: AtmosDevice - requireAnchored: false - joinSystem: true - - type: ContainerContainer - containers: - tank_slot: !type:ContainerSlot {} - paper_label: !type:ContainerSlot - - type: NodeContainer - nodes: - port: - !type:PortablePipeNode - nodeGroupID: Pipe - rotationsEnabled: false - volume: 1 - - type: GasPortable - - type: MaxPressureVisuals - integrityMask: mask-grey - - type: GasCanister - maxIntegrity: 15 # 5 times stronger than a gas tank - integrity: 15 - maxReleasePressure: 1013.25 - safetyPressure: 10132.5 # High enough that liquid tanks don't burst immediately, with an extra 33% wiggle room to work with - overpressure: 20265 # Purposefully high because cans react really fucking fast, may need to redo this value if reactions change! - gasTankSlot: - name: comp-gas-canister-slot-name-gas-tank - ejectOnBreak: true - whitelist: - components: - - GasTank - - type: Explosive - explosionType: Minibomb # Mostly a pressure wave explosive so primarily blunt damage - maxIntensity: 100 - tileBreakScale: 0.2 # Keeps the can from spacing all the hot gas it has now vented :). If this proves to be too much it can be removed. - - type: StaticPrice - price: 200 - - type: AccessReader - access: [["Atmospherics"], ["Engineering"], ["Research"]] - - type: Lock - locked: false - - type: GuideHelp - guides: - - GasCanisters - - type: Paintable - group: Canisters + - type: Animateable + - type: InteractionOutline + - type: Transform + noRot: true + - type: Sprite + sprite: Structures/Storage/canister.rsi + noRot: true + layers: + - state: grey + - state: paper + visible: false + map: [ "enum.PaperLabelVisuals.Layer" ] + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask-grey + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false + - type: Appearance + - type: GenericVisualizer + visuals: + enum.AnchorVisuals.Anchored: + connectedToPort: + False: { state: can-connector, visible: false } + True: { state: can-connector, visible: true } + enum.GasCanisterVisuals.TankInserted: + tankInserted: + False: { state: can-open, visible: false } + True: { state: can-open, visible: true } + enum.LockVisuals.Locked: + locked: + False: { state: unlocked, shader: "unshaded" } + True: { state: locked, shader: "unshaded" } + enum.GasCanisterVisuals.PressureState: + pressureLight: + 0: { state: can-o0, shader: "unshaded" } + 1: { state: can-o1, shader: "unshaded" } + 2: { state: can-o2, shader: "unshaded" } + 3: { state: can-o3, shader: "unshaded" } + enum.PaperLabelVisuals.HasLabel: + enum.PaperLabelVisuals.Layer: + True: { visible: true } + False: { visible: false } + enum.StorageVisuals.Open: + enum.PaperLabelVisuals.Layer: + True: { visible: false } + enum.PaperLabelVisuals.LabelType: + enum.PaperLabelVisuals.Layer: + Paper: { state: paper } + Bounty: { state: bounty } + CaptainsPaper: { state: captains_paper } + Invoice: { state: invoice } + - type: ActivatableUI + key: enum.GasCanisterUiKey.Key + - type: UIRequiresLock + userInterfaceKeys: + - enum.GasCanisterUiKey.Key + - type: UserInterface + interfaces: + enum.GasCanisterUiKey.Key: + type: GasCanisterBoundUserInterface + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 300 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + GasCanisterBrokenBase: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:DumpCanisterBehavior + - type: Damageable + damageModifierSet: Metallic + - type: Injurable + damageContainer: Inorganic + - type: Physics + bodyType: Dynamic + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.25,-0.25,0.25,0.25" + density: 190 + mask: + - SmallMobMask + layer: + - MachineLayer + - type: AtmosDevice + requireAnchored: false + joinSystem: true + - type: ContainerContainer + containers: + tank_slot: !type:ContainerSlot {} + paper_label: !type:ContainerSlot + - type: NodeContainer + nodes: + port: + !type:PortablePipeNode + nodeGroupID: Pipe + rotationsEnabled: false + volume: 1 + - type: GasPortable + - type: MaxPressureVisuals + integrityMask: mask-grey + - type: GasCanister + maxIntegrity: 15 # 5 times stronger than a gas tank + integrity: 15 + maxReleasePressure: 1013.25 + safetyPressure: 10132.5 # High enough that liquid tanks don't burst immediately, with an extra 33% wiggle room to work with + overpressure: 20265 # Purposefully high because cans react really fucking fast, may need to redo this value if reactions change! + gasTankSlot: + name: comp-gas-canister-slot-name-gas-tank + ejectOnBreak: true + whitelist: + components: + - GasTank + - type: Explosive + explosionType: Minibomb # Mostly a pressure wave explosive so primarily blunt damage + maxIntensity: 100 + tileBreakScale: 0.2 # Keeps the can from spacing all the hot gas it has now vented :). If this proves to be too much it can be removed. + - type: StaticPrice + price: 200 + - type: AccessReader + access: [["Atmospherics"], ["Engineering"], ["Research"]] + - type: Lock + locked: false + - type: GuideHelp + guides: + - GasCanisters + - type: Paintable + group: Canisters - type: entity parent: GasCanister @@ -178,12 +172,6 @@ temperature: 293.15 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 300 @@ -233,12 +221,6 @@ temperature: 293.15 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 300 @@ -284,12 +266,6 @@ temperature: 293.15 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 300 @@ -350,12 +326,6 @@ temperature: 293.15 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 300 @@ -416,12 +386,6 @@ temperature: 293.15 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 300 @@ -484,12 +448,6 @@ temperature: 293.15 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 300 @@ -537,12 +495,6 @@ temperature: 293.15 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 300 @@ -590,12 +542,6 @@ temperature: 293.15 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 300 @@ -641,12 +587,6 @@ temperature: 293.15 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 300 @@ -694,12 +634,6 @@ temperature: 293.15 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 300 @@ -747,12 +681,6 @@ temperature: 293.15 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 300 @@ -797,12 +725,6 @@ components: - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml index 99ff75de47d..51a7f6058e1 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml @@ -62,12 +62,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 150 diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml index 8d16d778aa6..4a84b69a09c 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml @@ -59,12 +59,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 @@ -155,12 +149,6 @@ ents: [] - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 @@ -279,12 +267,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 200 diff --git a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml index c622ad8961d..16fbfb2e689 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml @@ -161,15 +161,6 @@ damageModifierSet: Web - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 50 @@ -423,15 +414,6 @@ sprite: Structures/Storage/Crates/cage.rsi - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 75 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 15 @@ -725,15 +707,6 @@ state: base - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 75 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 15 @@ -776,15 +749,6 @@ state: base - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 400 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 200 # discourage just beating the grave to break it open diff --git a/Resources/Prototypes/Entities/Structures/Storage/Tanks/base_structuretanks.yml b/Resources/Prototypes/Entities/Structures/Storage/Tanks/base_structuretanks.yml index bf2b443e6a4..d9d025a86f2 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Tanks/base_structuretanks.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Tanks/base_structuretanks.yml @@ -28,12 +28,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 50 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTypeTrigger damageType: Heat diff --git a/Resources/Prototypes/Entities/Structures/Storage/barrels.yml b/Resources/Prototypes/Entities/Structures/Storage/barrels.yml index b429f69584f..195e502ae30 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/barrels.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/barrels.yml @@ -22,12 +22,6 @@ - MachineLayer - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 25 diff --git a/Resources/Prototypes/Entities/Structures/Storage/filing_cabinets.yml b/Resources/Prototypes/Entities/Structures/Storage/filing_cabinets.yml index 842a300c953..a2d295fa890 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/filing_cabinets.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/filing_cabinets.yml @@ -80,15 +80,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 50 diff --git a/Resources/Prototypes/Entities/Structures/Storage/glass_box.yml b/Resources/Prototypes/Entities/Structures/Storage/glass_box.yml index 6ec05b77939..ca7de0e1bb7 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/glass_box.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/glass_box.yml @@ -79,21 +79,6 @@ collection: GlassSmash - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 300 - behaviors: - - !type:EmptyAllContainersBehaviour - - !type:PlaySoundBehavior - sound: - collection: WindowShatter - - !type:PlaySoundBehavior - sound: - path: /Audio/Machines/warning_buzzer.ogg - params: - volume: 10 - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 150 @@ -181,15 +166,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 100 @@ -221,15 +197,6 @@ node: brokenGlassBox - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/Storage/ore_box.yml b/Resources/Prototypes/Entities/Structures/Storage/ore_box.yml index 79b94eac495..40397f973bb 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/ore_box.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/ore_box.yml @@ -14,12 +14,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 150 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/Storage/storage.yml b/Resources/Prototypes/Entities/Structures/Storage/storage.yml index 57c2ddedb82..9c5c493a24f 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/storage.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/storage.yml @@ -36,12 +36,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 60 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 30 diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml index e4ec45e2d6c..2a55a4ee046 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml @@ -11,15 +11,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 50 - behaviors: - - !type:PlaySoundBehavior - sound: - path: /Audio/Effects/poster_broken.ogg - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 15 diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/Cabinets/defib_cabinet.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/Cabinets/defib_cabinet.yml index 66528890a88..390fd7691dd 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/Cabinets/defib_cabinet.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/Cabinets/defib_cabinet.yml @@ -29,11 +29,6 @@ node: done - type: Destructible thresholds: - - trigger: !type:DamageTrigger - damage: 80 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 40 behaviors: diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/Cabinets/extinguisher_cabinet.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/Cabinets/extinguisher_cabinet.yml index 1e051ed75ed..207f42d7558 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/Cabinets/extinguisher_cabinet.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/Cabinets/extinguisher_cabinet.yml @@ -4,43 +4,37 @@ name: extinguisher cabinet description: A small wall mounted cabinet designed to hold a fire extinguisher. components: - - type: Sprite - sprite: Structures/Wallmounts/extinguisher_cabinet.rsi - snapCardinals: true - layers: - - state: frame - - state: extinguisher - map: ["enum.ItemCabinetVisuals.Layer"] - visible: true - - state: closed - map: ["enum.OpenableVisuals.Layer"] - - type: ItemSlots - slots: - ItemCabinet: - ejectOnInteract: true - whitelist: - components: - - SpraySafety - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 80 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 40 - behaviors: - - !type:EmptyAllContainersBehaviour - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - params: - volume: -4 + - type: Sprite + sprite: Structures/Wallmounts/extinguisher_cabinet.rsi + snapCardinals: true + layers: + - state: frame + - state: extinguisher + map: ["enum.ItemCabinetVisuals.Layer"] + visible: true + - state: closed + map: ["enum.OpenableVisuals.Layer"] + - type: ItemSlots + slots: + ItemCabinet: + ejectOnInteract: true + whitelist: + components: + - SpraySafety + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 40 + behaviors: + - !type:EmptyAllContainersBehaviour + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalGlassBreak + params: + volume: -4 - type: entity parent: ExtinguisherCabinet diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/Cabinets/fireaxe_cabinet.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/Cabinets/fireaxe_cabinet.yml index 0c97f046e71..74d13fe8ac5 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/Cabinets/fireaxe_cabinet.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/Cabinets/fireaxe_cabinet.yml @@ -8,12 +8,6 @@ components: - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 200 #20ish crowbar hits diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/shelfs.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/shelfs.yml index cff55ef7cb2..1d2894a2976 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/shelfs.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/shelfs.yml @@ -77,15 +77,6 @@ components: - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroyHeavy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 60 @@ -123,15 +114,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 120 @@ -165,15 +147,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WindowShatter - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 50 @@ -212,15 +185,6 @@ shader: unshaded - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 400 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroyHeavy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 215 @@ -249,15 +213,6 @@ sprite: Structures/Storage/Shelfs/metal.rsi - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 900 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 450 @@ -292,15 +247,6 @@ sprite: Structures/Storage/Shelfs/glass.rsi - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 500 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WindowShatter - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 250 @@ -345,15 +291,6 @@ fillBaseName: bar - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroyHeavy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 100 @@ -395,15 +332,6 @@ fillBaseName: kitchen - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 300 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 150 @@ -457,15 +385,6 @@ fillBaseName: chem - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 600 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 330 diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/wall_dispensers.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/wall_dispensers.yml index e4d7607b9fd..0600a854feb 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/wall_dispensers.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Storage/wall_dispensers.yml @@ -8,12 +8,6 @@ - type: Appearance - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 50 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTypeTrigger damageType: Heat diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Switches/switch.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Switches/switch.yml index ac4d41d3068..a835367cd64 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/Switches/switch.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Switches/switch.yml @@ -33,12 +33,6 @@ anchored: false - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 80 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 40 @@ -85,12 +79,6 @@ anchored: false - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 80 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 40 diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/Monitors/telescreens.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/Monitors/telescreens.yml index 0705a60b492..9ddcb4064f5 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/Monitors/telescreens.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/Monitors/telescreens.yml @@ -58,18 +58,12 @@ - type: Sprite sprite: Structures/Machines/computers.rsi layers: - - map: ["computerLayerBody"] - state: telescreen_frame - - map: ["computerLayerScreen"] - state: telescreen + - map: ["computerLayerBody"] + state: telescreen_frame + - map: ["computerLayerScreen"] + state: telescreen - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: #excess damage, don't spawn entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/Monitors/televisions.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/Monitors/televisions.yml index fd1e895f707..514e7ff6409 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/Monitors/televisions.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/Monitors/televisions.yml @@ -11,10 +11,10 @@ - type: Sprite sprite: Structures/Wallmounts/flatscreentv.rsi layers: - - map: ["computerLayerBody"] - state: television_wall - - map: ["computerLayerScreen"] - state: television_wallscreen + - map: ["computerLayerBody"] + state: television_wall + - map: ["computerLayerScreen"] + state: television_wallscreen - type: DeviceNetwork deviceNetId: Wireless receiveFrequencyId: SurveillanceCamera @@ -39,23 +39,17 @@ color: "#b89f25" - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - params: - volume: -4 + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalGlassBreak + params: + volume: -4 - type: StationAiWhitelist - type: entity @@ -71,26 +65,20 @@ drawdepth: WallMountedItems sprite: Structures/Wallmounts/flatscreentv.rsi layers: - - map: ["computerLayerBody"] - state: television_wall - - map: ["computerLayerScreen"] - state: television_wallscreen + - map: ["computerLayerBody"] + state: television_wall + - map: ["computerLayerScreen"] + state: television_wallscreen - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 20 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 15 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - params: - volume: -4 + - trigger: + !type:DamageTrigger + damage: 15 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalGlassBreak + params: + volume: -4 diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/air_alarm.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/air_alarm.yml index 3f7c6b4c2da..b57c801c31d 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/air_alarm.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/air_alarm.yml @@ -89,23 +89,17 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - params: - volume: -4 + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalGlassBreak + params: + volume: -4 - type: GuideHelp guides: - AirAlarms @@ -136,23 +130,17 @@ anchored: true - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 20 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 15 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - params: - volume: -4 + - trigger: + !type:DamageTrigger + damage: 15 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalGlassBreak + params: + volume: -4 - type: entity id: AirAlarmXeno diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/fire_alarm.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/fire_alarm.yml index 99c1645c4e7..c13007abae4 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/fire_alarm.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/fire_alarm.yml @@ -90,23 +90,17 @@ - Networking - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 160 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 80 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - params: - volume: -4 + - trigger: + !type:DamageTrigger + damage: 80 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalGlassBreak + params: + volume: -4 - type: StationAiWhitelist - type: entity @@ -132,23 +126,17 @@ node: assembly - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 20 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 15 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - params: - volume: -4 + - trigger: + !type:DamageTrigger + damage: 15 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalGlassBreak + params: + volume: -4 - type: entity id: FireAlarmXeno diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml index fda76c6ce1c..0a8c85931a1 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml @@ -78,23 +78,17 @@ - key_slots - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - params: - volume: -4 + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalGlassBreak + params: + volume: -4 - type: GenericVisualizer visuals: enum.WiresVisualLayers.MaintenancePanel: @@ -130,23 +124,17 @@ map: [ "wires" ] - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalGlassBreak - params: - volume: -4 + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalGlassBreak + params: + volume: -4 - type: Appearance - type: GenericVisualizer visuals: diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/station_map.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/station_map.yml index 823d82119be..1b16c3361c2 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/station_map.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/station_map.yml @@ -5,22 +5,22 @@ description: A virtual map of the surrounding station. suffix: Wall broken components: - - type: Sprite - sprite: Structures/Machines/station_map.rsi - drawdepth: WallMountedItems - layers: - - state: station_map_broken - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:DoActsBehavior - acts: [ "Destruction" ] + - type: Sprite + sprite: Structures/Machines/station_map.rsi + drawdepth: WallMountedItems + layers: + - state: station_map_broken + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity parent: BaseWallmountMachine @@ -29,83 +29,74 @@ description: A virtual map of the surrounding station. suffix: Wall components: - - type: WallMount - arc: 360 - - type: Appearance - - type: StationMap - - type: Sprite - sprite: Structures/Machines/station_map.rsi - layers: - - state: station_map0 - - state: unshaded - shader: unshaded - map: [ "enum.PowerDeviceVisualLayers.Powered" ] - - type: Icon - sprite: Structures/Machines/station_map.rsi - state: station_map0 - - type: GenericVisualizer - visuals: - enum.PowerDeviceVisuals.Powered: - enum.PowerDeviceVisualLayers.Powered: - True: { visible: true } - False: { visible: false } - - type: ContainerFill - containers: - board: [ StationMapCircuitboard ] - - type: ContainerContainer - containers: - board: !type:Container - - type: ApcPowerReceiver - powerLoad: 50 - - type: PowerState - idlePowerDraw: 50 - workingPowerDraw: 200 - - type: UIPowerState - - type: Construction - graph: StationMap - node: station_map - - type: ActivatableUIRequiresPower - - type: ActivatableUI - key: enum.StationMapUiKey.Key - - type: Destructible - thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - StationMapBroken: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] - - type: UserInterface - interfaces: - enum.StationMapUiKey.Key: - type: StationMapBoundUserInterface - - type: EmitSoundOnUIOpen - sound: - collection: Keyboard - params: - volume: -1 - variation: 0.10 - pitch: 1.10 # low pitch keyboard sounds feel kinda weird - blacklist: - tags: - - NoConsoleSound + - type: WallMount + arc: 360 + - type: Appearance + - type: StationMap + - type: Sprite + sprite: Structures/Machines/station_map.rsi + layers: + - state: station_map0 + - state: unshaded + shader: unshaded + map: [ "enum.PowerDeviceVisualLayers.Powered" ] + - type: Icon + sprite: Structures/Machines/station_map.rsi + state: station_map0 + - type: GenericVisualizer + visuals: + enum.PowerDeviceVisuals.Powered: + enum.PowerDeviceVisualLayers.Powered: + True: { visible: true } + False: { visible: false } + - type: ContainerFill + containers: + board: [ StationMapCircuitboard ] + - type: ContainerContainer + containers: + board: !type:Container + - type: ApcPowerReceiver + powerLoad: 50 + - type: PowerState + idlePowerDraw: 50 + workingPowerDraw: 200 + - type: UIPowerState + - type: Construction + graph: StationMap + node: station_map + - type: ActivatableUIRequiresPower + - type: ActivatableUI + key: enum.StationMapUiKey.Key + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + StationMapBroken: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: UserInterface + interfaces: + enum.StationMapUiKey.Key: + type: StationMapBoundUserInterface + - type: EmitSoundOnUIOpen + sound: + collection: Keyboard + params: + volume: -1 + variation: 0.10 + pitch: 1.10 # low pitch keyboard sounds feel kinda weird + blacklist: + tags: + - NoConsoleSound - type: entity parent: BaseWallmountMetallic diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml index b5d354ba39f..98475e88415 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml @@ -79,23 +79,17 @@ price: 200 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - params: - volume: -8 + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + params: + volume: -8 placement: mode: SnapgridCenter snap: diff --git a/Resources/Prototypes/Entities/Structures/Walls/fence_metal.yml b/Resources/Prototypes/Entities/Structures/Walls/fence_metal.yml index 7a1e1629784..a71eaf8912f 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/fence_metal.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/fence_metal.yml @@ -23,12 +23,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 150 @@ -115,12 +109,6 @@ collection: FenceRattle - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 @@ -174,12 +162,6 @@ collection: FenceRattle - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/Walls/fence_wood.yml b/Resources/Prototypes/Entities/Structures/Walls/fence_wood.yml index f0a27214cf7..e5dbfe5b248 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/fence_wood.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/fence_wood.yml @@ -30,15 +30,6 @@ fx: EffectRCDDeconstruct2 - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 125 @@ -66,15 +57,6 @@ components: - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 75 diff --git a/Resources/Prototypes/Entities/Structures/Walls/girders.yml b/Resources/Prototypes/Entities/Structures/Walls/girders.yml index 15893d76c75..7acadb48be4 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/girders.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/girders.yml @@ -33,12 +33,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: #excess damage, don't spawn entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 @@ -62,42 +56,33 @@ name: reinforced girder description: A large structural assembly made out of metal and plasteel; It requires a layer of plasteel before it can be considered a reinforced wall. components: - - type: Sprite - sprite: Structures/Walls/solid.rsi - state: reinforced_wall_girder - - type: Construction - graph: Girder - node: reinforcedGirder - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - trigger: - !type:DamageTrigger - damage: 50 - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - SheetSteel1: - min: 1 - max: 1 - SheetPlasteel1: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - type: StaticPrice - price: 130 + - type: Sprite + sprite: Structures/Walls/solid.rsi + state: reinforced_wall_girder + - type: Construction + graph: Girder + node: reinforcedGirder + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel1: + min: 1 + max: 1 + SheetPlasteel1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - type: StaticPrice + price: 130 - type: entity id: ClockworkGirder @@ -105,39 +90,30 @@ name: wall gear description: A large gear with mounting brackets for additional plating; This could be made into a wall with more brass plating. components: - - type: Sprite - sprite: Structures/Walls/clock.rsi - state: wall_gear - - type: Construction - graph: ClockworkGirder - node: clockGirder - - type: StaticPrice - price: 75 - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - trigger: - !type:DamageTrigger - damage: 50 - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - SheetBrass1: - min: 1 - max: 2 - PartRodMetal1: - min: 1 - max: 2 - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak + - type: Sprite + sprite: Structures/Walls/clock.rsi + state: wall_gear + - type: Construction + graph: ClockworkGirder + node: clockGirder + - type: StaticPrice + price: 75 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + SheetBrass1: + min: 1 + max: 2 + PartRodMetal1: + min: 1 + max: 2 + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak diff --git a/Resources/Prototypes/Entities/Structures/Walls/grille.yml b/Resources/Prototypes/Entities/Structures/Walls/grille.yml index e6009b69c1b..3d6beb053c8 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/grille.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/grille.yml @@ -4,92 +4,92 @@ name: grille description: A flimsy framework of iron rods. components: - - type: Transform # required - noRot: true - - type: MeleeSound - soundGroups: - Brute: - path: - "/Audio/Weapons/grille_hit.ogg" - - type: RCDDeconstructable - cost: 6 - delay: 4 - fx: EffectRCDDeconstruct4 - - type: CanBuildWindowOnTop - - type: Sprite - drawdepth: Walls - sprite: Structures/Walls/grille.rsi - layers: - - state: grille - - state: electrified - sprite: Effects/electricity.rsi - map: ["enum.ElectrifiedLayers.Sparks"] - shader: unshaded - visible: false - - type: Icon - sprite: Structures/Walls/grille.rsi - state: grille - - type: Construction - graph: Grille - node: grille - deconstructionTarget: start - - type: Damageable - damageModifierSet: PerforatedMetallic - - type: Injurable - damageContainer: StructuralInorganic - - type: Electrified - requirePower: true - noWindowInTile: true - highVoltageNode: high - mediumVoltageNode: medium - lowVoltageNode: low - - type: NodeContainer - nodes: - high: - !type:CableDeviceNode - nodeGroupID: HVPower - medium: - !type:CableDeviceNode - nodeGroupID: MVPower - low: - !type:CableDeviceNode - nodeGroupID: Apc - - type: Physics - bodyType: Static - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.5,-0.5,0.5,0.5" - mask: - - FullTileMask - layer: - - GlassLayer - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 130 #excess damage (nuke?). avoid computational cost of spawning entities. - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:ChangeConstructionNodeBehavior - node: grilleBroken - - !type:DoActsBehavior - acts: ["Breakage"] - - type: Appearance - - type: GenericVisualizer - visuals: - enum.ElectrifiedVisuals.ShowSparks: - enum.ElectrifiedLayers.Sparks: - True: { visible: True } - False: { visible: False } - - type: AnimationPlayer + - type: Transform # required + noRot: true + - type: MeleeSound + soundGroups: + Brute: + path: + "/Audio/Weapons/grille_hit.ogg" + - type: RCDDeconstructable + cost: 6 + delay: 4 + fx: EffectRCDDeconstruct4 + - type: CanBuildWindowOnTop + - type: Sprite + drawdepth: Walls + sprite: Structures/Walls/grille.rsi + layers: + - state: grille + - state: electrified + sprite: Effects/electricity.rsi + map: ["enum.ElectrifiedLayers.Sparks"] + shader: unshaded + visible: false + - type: Icon + sprite: Structures/Walls/grille.rsi + state: grille + - type: Construction + graph: Grille + node: grille + deconstructionTarget: start + - type: Damageable + damageModifierSet: PerforatedMetallic + - type: Injurable + damageContainer: StructuralInorganic + - type: Electrified + requirePower: true + noWindowInTile: true + highVoltageNode: high + mediumVoltageNode: medium + lowVoltageNode: low + - type: NodeContainer + nodes: + high: + !type:CableDeviceNode + nodeGroupID: HVPower + medium: + !type:CableDeviceNode + nodeGroupID: MVPower + low: + !type:CableDeviceNode + nodeGroupID: Apc + - type: Physics + bodyType: Static + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.5,-0.5,0.5,0.5" + mask: + - FullTileMask + layer: + - GlassLayer + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 200 #excess damage (nuke?). avoid computational cost of spawning entities. + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:ChangeConstructionNodeBehavior + node: grilleBroken + - !type:DoActsBehavior + acts: ["Breakage"] + - type: Appearance + - type: GenericVisualizer + visuals: + enum.ElectrifiedVisuals.ShowSparks: + enum.ElectrifiedLayers.Sparks: + True: { visible: True } + False: { visible: False } + - type: AnimationPlayer - type: entity id: ClockworkGrille diff --git a/Resources/Prototypes/Entities/Structures/Walls/railing.yml b/Resources/Prototypes/Entities/Structures/Walls/railing.yml index 433ffc9a9f9..17c6e084402 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/railing.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/railing.yml @@ -47,12 +47,6 @@ - TableLayer - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 20 @@ -102,12 +96,6 @@ - TableLayer - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 20 @@ -148,12 +136,6 @@ - TableLayer - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 20 @@ -212,12 +194,6 @@ - TableLayer - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 20 diff --git a/Resources/Prototypes/Entities/Structures/Walls/walls.yml b/Resources/Prototypes/Entities/Structures/Walls/walls.yml index 828b20059ca..709796c49c3 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/walls.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/walls.yml @@ -338,15 +338,6 @@ fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 150 @@ -417,15 +408,6 @@ fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 150 @@ -461,15 +443,6 @@ fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - trigger: !type:DamageTrigger damage: 150 @@ -522,19 +495,19 @@ name: plastitanium wall suffix: "" components: - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 1000 - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 1000 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity parent: WallDiagonalBase @@ -605,15 +578,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - trigger: !type:DamageTrigger damage: 400 @@ -675,15 +639,6 @@ base: reinf_over - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 500 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - trigger: !type:DamageTrigger damage: 350 @@ -714,15 +669,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - trigger: !type:DamageTrigger damage: 300 @@ -818,15 +764,6 @@ fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - trigger: !type:DamageTrigger damage: 150 @@ -883,15 +820,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - trigger: !type:DamageTrigger damage: 300 @@ -922,15 +850,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - trigger: !type:DamageTrigger damage: 400 @@ -1002,15 +921,6 @@ fx: EffectRCDDeconstruct8 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - trigger: !type:DamageTrigger damage: 200 @@ -1040,15 +950,6 @@ state: state0 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - trigger: !type:DamageTrigger damage: 200 @@ -1079,15 +980,6 @@ base: solid - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - trigger: !type:DamageTrigger damage: 150 @@ -1110,15 +1002,6 @@ - Wall - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - trigger: !type:DamageTrigger damage: 200 @@ -1161,15 +1044,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - trigger: !type:DamageTrigger damage: 400 @@ -1245,15 +1119,6 @@ node: uraniumWall - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - trigger: !type:DamageTrigger damage: 150 @@ -1297,15 +1162,6 @@ node: woodWall - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: WoodDestroyHeavy - trigger: !type:DamageTrigger damage: 150 @@ -1340,15 +1196,6 @@ sprite: Structures/Walls/web.rsi - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 100 - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WoodDestroy - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 30 @@ -1438,15 +1285,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 600 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalSlam - trigger: !type:DamageTrigger damage: 300 @@ -1751,12 +1589,6 @@ - FlimsyLayer - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 80 #excess damage (nuke?). avoid computational cost of spawning entities. - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 40 diff --git a/Resources/Prototypes/Entities/Structures/Windows/clockwork.yml b/Resources/Prototypes/Entities/Structures/Windows/clockwork.yml index 7c58e5fc97c..0922122ff7e 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/clockwork.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/clockwork.yml @@ -18,15 +18,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 150 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: WindowShatter - trigger: !type:DamageTrigger damage: 75 @@ -84,15 +75,6 @@ damageModifierSet: RGlass - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 75 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: WindowShatter - trigger: !type:DamageTrigger damage: 37 diff --git a/Resources/Prototypes/Entities/Structures/Windows/mining.yml b/Resources/Prototypes/Entities/Structures/Windows/mining.yml index 5d2eef32d94..ebdbc1e0a76 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/mining.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/mining.yml @@ -17,12 +17,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/Windows/plasma.yml b/Resources/Prototypes/Entities/Structures/Windows/plasma.yml index 0112f3103df..b71f93db2a3 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/plasma.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/plasma.yml @@ -14,15 +14,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 150 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: WindowShatter - trigger: !type:DamageTrigger damage: 75 @@ -82,15 +73,6 @@ sprite: Structures/Windows/cracks_directional.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 75 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: WindowShatter - trigger: !type:DamageTrigger damage: 37 diff --git a/Resources/Prototypes/Entities/Structures/Windows/plastitanium.yml b/Resources/Prototypes/Entities/Structures/Windows/plastitanium.yml index 6327ed316c6..a1ecd488ba2 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/plastitanium.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/plastitanium.yml @@ -88,12 +88,6 @@ components: - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 1000 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 500 @@ -184,12 +178,6 @@ components: - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 1000 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 500 diff --git a/Resources/Prototypes/Entities/Structures/Windows/reinforced.yml b/Resources/Prototypes/Entities/Structures/Windows/reinforced.yml index b85289e3e87..fbd098a1460 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/reinforced.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/reinforced.yml @@ -21,15 +21,6 @@ fx: EffectRCDDeconstruct6 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 150 #excess damage (nuke?). avoid computational cost of spawning entities. - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: WindowShatter - trigger: !type:DamageTrigger damage: 75 @@ -91,15 +82,6 @@ fx: EffectRCDDeconstruct4 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 75 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: WindowShatter - trigger: !type:DamageTrigger damage: 37 diff --git a/Resources/Prototypes/Entities/Structures/Windows/rplasma.yml b/Resources/Prototypes/Entities/Structures/Windows/rplasma.yml index a34c201b88e..a8f9d8e385f 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/rplasma.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/rplasma.yml @@ -16,15 +16,6 @@ resistance: 4 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: #excess damage, don't spawn entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: WindowShatter - trigger: !type:DamageTrigger damage: 150 @@ -89,12 +80,6 @@ resistance: 2 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 150 - behaviors: #excess damage, don't spawn entities. - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 75 diff --git a/Resources/Prototypes/Entities/Structures/Windows/ruranium.yml b/Resources/Prototypes/Entities/Structures/Windows/ruranium.yml index 36d8981321b..96381de72e0 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/ruranium.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/ruranium.yml @@ -14,12 +14,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 450 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 225 @@ -84,12 +78,6 @@ damageModifierSet: RGlass - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 225 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 113 diff --git a/Resources/Prototypes/Entities/Structures/Windows/shuttle.yml b/Resources/Prototypes/Entities/Structures/Windows/shuttle.yml index a09b04c727d..b3ead67f1b8 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/shuttle.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/shuttle.yml @@ -17,12 +17,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 1000 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 500 diff --git a/Resources/Prototypes/Entities/Structures/Windows/uranium.yml b/Resources/Prototypes/Entities/Structures/Windows/uranium.yml index 161fb7d9fe0..73adc37fae3 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/uranium.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/uranium.yml @@ -15,12 +15,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 @@ -80,12 +74,6 @@ sprite: Structures/Windows/cracks_directional.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 100 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 diff --git a/Resources/Prototypes/Entities/Structures/Windows/window.yml b/Resources/Prototypes/Entities/Structures/Windows/window.yml index ee5a9242fed..5ecd83aece9 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/window.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/window.yml @@ -52,15 +52,6 @@ fx: EffectRCDDeconstruct4 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 150 #excess damage (nuke?). avoid computational cost of spawning entities. - behaviors: - - !type:PlaySoundBehavior - sound: - collection: WindowShatter - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 @@ -181,15 +172,6 @@ fx: EffectRCDDeconstruct2 - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 50 #excess damage (nuke?). avoid computational cost of spawning entities. - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: WindowShatter - trigger: !type:DamageTrigger damage: 25 diff --git a/Resources/Prototypes/Entities/Structures/Windows/xenoresin.yml b/Resources/Prototypes/Entities/Structures/Windows/xenoresin.yml index 9f111e65478..989ffcca9b3 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/xenoresin.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/xenoresin.yml @@ -11,15 +11,6 @@ sprite: Structures/Windows/xeno_resin.rsi - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 #excess damage (nuke?). avoid computational cost of spawning entities. - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: WindowShatter - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/barricades.yml b/Resources/Prototypes/Entities/Structures/barricades.yml index 4737e565286..016b3a4a70f 100644 --- a/Resources/Prototypes/Entities/Structures/barricades.yml +++ b/Resources/Prototypes/Entities/Structures/barricades.yml @@ -32,12 +32,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 60 diff --git a/Resources/Prototypes/Entities/Structures/catwalk.yml b/Resources/Prototypes/Entities/Structures/catwalk.yml index 58d2ff773c5..ba767c43907 100644 --- a/Resources/Prototypes/Entities/Structures/catwalk.yml +++ b/Resources/Prototypes/Entities/Structures/catwalk.yml @@ -39,12 +39,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 500 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 200 diff --git a/Resources/Prototypes/Entities/Structures/ironsand_steps.yml b/Resources/Prototypes/Entities/Structures/ironsand_steps.yml index 95a6dac95ba..8549400a9a2 100644 --- a/Resources/Prototypes/Entities/Structures/ironsand_steps.yml +++ b/Resources/Prototypes/Entities/Structures/ironsand_steps.yml @@ -13,12 +13,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 500 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 diff --git a/Resources/Prototypes/Entities/Structures/meat_spike.yml b/Resources/Prototypes/Entities/Structures/meat_spike.yml index c8532b553f4..cef80165d6a 100644 --- a/Resources/Prototypes/Entities/Structures/meat_spike.yml +++ b/Resources/Prototypes/Entities/Structures/meat_spike.yml @@ -18,12 +18,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 100 diff --git a/Resources/Prototypes/Entities/Structures/spider_web.yml b/Resources/Prototypes/Entities/Structures/spider_web.yml index edd8fb9e64c..6a223e91795 100644 --- a/Resources/Prototypes/Entities/Structures/spider_web.yml +++ b/Resources/Prototypes/Entities/Structures/spider_web.yml @@ -70,12 +70,6 @@ - MidImpassable - type: Destructible thresholds: - - trigger: # Excess damage, don't spawn entities - !type:DamageTrigger - damage: 50 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 10 diff --git a/Resources/Prototypes/Entities/Structures/stairs.yml b/Resources/Prototypes/Entities/Structures/stairs.yml index 2b1b4b9d83c..ce7dfcdc874 100644 --- a/Resources/Prototypes/Entities/Structures/stairs.yml +++ b/Resources/Prototypes/Entities/Structures/stairs.yml @@ -30,12 +30,6 @@ damageContainer: StructuralInorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 500 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 50 diff --git a/Resources/Prototypes/Entities/Tiles/bananium.yml b/Resources/Prototypes/Entities/Tiles/bananium.yml index 42a436aa5ab..897227eab4d 100644 --- a/Resources/Prototypes/Entities/Tiles/bananium.yml +++ b/Resources/Prototypes/Entities/Tiles/bananium.yml @@ -27,12 +27,6 @@ damageContainer: Inorganic - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 500 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - trigger: !type:DamageTrigger damage: 200 From 35134cc693a81a77ad9811c7b9ae55f7e1b7d995 Mon Sep 17 00:00:00 2001 From: Robert Date: Sun, 31 May 2026 13:49:20 -0600 Subject: [PATCH 015/287] Fix: Move belt assembly suffix to name (#44194) Move belt assembly suffix to name --- Resources/Prototypes/Entities/Structures/conveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/conveyor.yml b/Resources/Prototypes/Entities/Structures/conveyor.yml index bcb7c4d4ba1..56a6be5d984 100644 --- a/Resources/Prototypes/Entities/Structures/conveyor.yml +++ b/Resources/Prototypes/Entities/Structures/conveyor.yml @@ -71,8 +71,7 @@ - type: entity id: ConveyorBeltAssembly parent: BaseItem - name: conveyor belt - suffix: assembly + name: conveyor belt assembly description: A conveyor belt assembly. Used to construct a conveyor belt. components: - type: Sprite From 470acb50b0ddf8e6f24edb23de8728baf429b7b9 Mon Sep 17 00:00:00 2001 From: B_Kirill <153602297+B-Kirill@users.noreply.github.com> Date: Mon, 1 Jun 2026 05:50:48 +1000 Subject: [PATCH 016/287] Fix shooting from inside a morgue exploit (#44188) Fix shooting inside morgue exploit --- Resources/Prototypes/Entities/Structures/Storage/morgue.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Structures/Storage/morgue.yml b/Resources/Prototypes/Entities/Structures/Storage/morgue.yml index 496ea416119..94769c5942e 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/morgue.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/morgue.yml @@ -34,7 +34,7 @@ mask: - MachineMask layer: - - HalfWallLayer + - MachineLayer - type: EntityStorage isCollidableWhenOpen: true showContents: false @@ -73,6 +73,7 @@ - type: AntiRottingContainer - type: StaticPrice price: 200 + - type: RequireProjectileTarget - type: entity id: Crematorium @@ -143,3 +144,4 @@ False: { visible: false } - type: Transform anchored: true + - type: RequireProjectileTarget From 12ca6aa635c904a510c06871acb7073100b292b4 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 31 May 2026 20:14:07 +0000 Subject: [PATCH 017/287] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 631fccbcd44..9cd05f96b89 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: slarticodefast - changes: - - message: Artifact analyzers are no longer extremely power hungry. - type: Tweak - id: 9220 - time: '2025-11-23T00:10:33.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/41543 - author: Jrpl changes: - message: Using entities in hand that have a Toggle UI option will now default @@ -4097,3 +4090,10 @@ id: 9731 time: '2026-05-30T21:24:29.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/43755 +- author: B_Kirill + changes: + - message: Fixed shooting from inside a morgue exploit. + type: Fix + id: 9732 + time: '2026-05-31T20:12:56.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/44188 From 2e35342b7b15d3394d3a5a23e7f708486934e4af Mon Sep 17 00:00:00 2001 From: Whatstone <166147148+whatston3@users.noreply.github.com> Date: Sun, 31 May 2026 16:10:54 -0400 Subject: [PATCH 018/287] Restore foam IconSmooth key (#44182) restore foam merge key --- Resources/Prototypes/Entities/Effects/chemistry_effects.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml index ca837b7accf..9fa832e2f37 100644 --- a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml +++ b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml @@ -102,6 +102,7 @@ - type: SmoothEdge - type: IconSmooth mode: NoSprite + key: foam - type: FoamVisuals animationTime: 0.6 animationState: foam-dissolve From dfd42624f28a999b84395f2cefc085223e1704b3 Mon Sep 17 00:00:00 2001 From: Mora Date: Sun, 31 May 2026 22:48:00 -0500 Subject: [PATCH 019/287] [Aseprite Tool] Add Copy Displaced To New Canvas Button (#41614) Add Copy Displaced To New Canvas button Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> --- .../Displacement Map Visualizer.lua | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Tools/SS14 Aseprite Plugins/Displacement Map Visualizer.lua b/Tools/SS14 Aseprite Plugins/Displacement Map Visualizer.lua index efbf2e9a17e..3d19d51f8f7 100644 --- a/Tools/SS14 Aseprite Plugins/Displacement Map Visualizer.lua +++ b/Tools/SS14 Aseprite Plugins/Displacement Map Visualizer.lua @@ -266,4 +266,42 @@ dialog:color{ end } +dialog:button{ + id = "save-displaced", + text = "Copy Displaced Sprite To New Canvas", + onclick = function(ev) + local layerDisplacement = findLayer(sprite, dialog.data["displacement-select"]) + local layerTarget = findLayer(sprite, dialog.data["reference-select"]) + if not layerDisplacement or not layerTarget then + app.alert("Select both displacement and reference layers!") + return + end + + local celDisplacement = layerDisplacement:cel(1) + local celTarget = layerTarget:cel(1) + if not celDisplacement or not celTarget then + app.alert("Selected layers must have a cel in the first frame!") + return + end + + local displacedImage = applyDisplacementMap( + sprite.width, sprite.height, + celDisplacement, + celTarget + ) + + local newSprite = Sprite(sprite.width, sprite.height, sprite.colorMode) + if sprite.colorMode == ColorMode.INDEXED then + for i=0,sprite.palettes[1].size-1 do + newSprite.palettes[1]:setColor(i, sprite.palettes[1]:getColor(i)) + end + end + + local newLayer = newSprite.layers[1] + local newCel = newLayer:cel(1) + newCel.image:drawImage(displacedImage, 0, 0) + app.activeSprite = newSprite + end +} + dialog:show{wait = false} From 7b022c09a50cfa2de3b072cd19ca743f741cb7f4 Mon Sep 17 00:00:00 2001 From: Kyle Tyo Date: Mon, 1 Jun 2026 16:42:58 -0400 Subject: [PATCH 020/287] Fix ToggleRoundEndSummaryWindow requiring two presses. (#44206) Update keybinds.yml --- Resources/keybinds.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/keybinds.yml b/Resources/keybinds.yml index aa7b2918e1c..dc8b425481e 100644 --- a/Resources/keybinds.yml +++ b/Resources/keybinds.yml @@ -490,7 +490,7 @@ binds: type: State key: F8 - function: ToggleRoundEndSummaryWindow - type: Toggle + type: State key: F9 - function: OpenSandboxWindow type: State From 1c44d95c2b10fbc1e1bafc36b0d9982f8ad8970d Mon Sep 17 00:00:00 2001 From: Whatstone <166147148+whatston3@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:56:21 -0400 Subject: [PATCH 021/287] Bugfix: Banana cream pies stun when target is already creamed (#44209) OnCreamPiedHitBy: Paralyze before exit --- .../Nutrition/EntitySystems/SharedCreamPieSystem.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs b/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs index b8283e234fb..67988072dcc 100644 --- a/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs @@ -118,12 +118,17 @@ private void OnCreamPieHit(Entity ent, ref ThrowDoHitEvent ar private void OnCreamPiedHitBy(Entity creamPied, ref ThrowHitByEvent args) { - if (creamPied.Comp.CreamPied || !Exists(args.Thrown) || !TryComp(args.Thrown, out var creamPie)) + if (!Exists(args.Thrown) || !TryComp(args.Thrown, out var creamPie)) + return; + + _stunSystem.TryUpdateParalyzeDuration(creamPied.Owner, creamPie.ParalyzeTime); + + // Already creamed, no need to spam popups. + if (creamPied.Comp.CreamPied) return; // TODO: Check if they even have a head that can be hit. SetCreamPied(creamPied.AsNullable(), true); - _stunSystem.TryUpdateParalyzeDuration(creamPied.Owner, creamPie.ParalyzeTime); // Throwing is not predicted, so the thrower is not equal to the client predicting the collision, so we cannot pass in a user. // TODO: Make the popup API sane. From 17dc62f9967d60420a800a9099358a16f7de1ace Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 1 Jun 2026 21:31:01 +0000 Subject: [PATCH 022/287] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 9cd05f96b89..812091d3adf 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: Jrpl - changes: - - message: Using entities in hand that have a Toggle UI option will now default - to it over other context menu options. The other options can still be used through - the context menu. - type: Tweak - id: 9221 - time: '2025-11-23T13:11:18.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/41547 - author: Happyrobot33 changes: - message: Added lobby credits for the background art @@ -4097,3 +4088,10 @@ id: 9732 time: '2026-05-31T20:12:56.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/44188 +- author: whatston3 + changes: + - message: Banana cream pies now stun targets that already have pie on their face. + type: Fix + id: 9733 + time: '2026-06-01T21:29:51.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/44209 From c57976f209070befac68bc8fdaf5b13411f43b4f Mon Sep 17 00:00:00 2001 From: KeTuFaisPiKiNut <85769816+ketufaispikinut@users.noreply.github.com> Date: Mon, 1 Jun 2026 17:18:36 -0400 Subject: [PATCH 023/287] Evil Changeling Shop Icon (#44203) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ye ole shoppe Co-authored-by: nomdéraisonnablementlong --- Resources/Prototypes/Actions/changeling.yml | 3 +++ .../Interface/Actions/changeling2.rsi/meta.json | 5 ++++- .../Interface/Actions/changeling2.rsi/shop.png | Bin 0 -> 1915 bytes 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 Resources/Textures/Interface/Actions/changeling2.rsi/shop.png diff --git a/Resources/Prototypes/Actions/changeling.yml b/Resources/Prototypes/Actions/changeling.yml index d7fb2b56107..e72663f78aa 100644 --- a/Resources/Prototypes/Actions/changeling.yml +++ b/Resources/Prototypes/Actions/changeling.yml @@ -66,6 +66,9 @@ id: ActionChangelingStore name: DNA Store description: Opens the ability store. + components: + - type: Action + icon: { sprite : Interface/Actions/changeling2.rsi, state: "shop" } - type: entity parent: BaseAction diff --git a/Resources/Textures/Interface/Actions/changeling2.rsi/meta.json b/Resources/Textures/Interface/Actions/changeling2.rsi/meta.json index 6a4ade2d242..3fe25a4de18 100644 --- a/Resources/Textures/Interface/Actions/changeling2.rsi/meta.json +++ b/Resources/Textures/Interface/Actions/changeling2.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by ketufaispikinut from a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (scientist suit), a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (hydro suit) and the changeling ability border/background sprites created by TiniestShark. Biodegrade, Mimicry and Extract Dna On/Off by ketufaispikinut, based on TiniestShark's textures.", + "copyright": "Made by ketufaispikinut from a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (scientist suit), a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (hydro suit) and the changeling ability border/background sprites created by TiniestShark. Biodegrade, Mimicry and Extract Dna On/Off by ketufaispikinut, based on TiniestShark's textures. Shop icon by ketufaispikinut, based on TiniestShark's textures.", "size": { "x": 32, "y": 32 @@ -26,6 +26,9 @@ , { "name": "extract_dna_off" + }, + { + "name": "shop" } ] } diff --git a/Resources/Textures/Interface/Actions/changeling2.rsi/shop.png b/Resources/Textures/Interface/Actions/changeling2.rsi/shop.png new file mode 100644 index 0000000000000000000000000000000000000000..865a38f1639fb3a09c9be2fbd399e55a76f08da3 GIT binary patch literal 1915 zcmV->2ZZ>EP)Px+HAzH4R9JTqk6)75Bu?Cc^JaKRx&TpG5~3AEBqS2?BH_gvs@kPn z%cN>3AEs&=>ZeMbrcLUnsnb48niaN56`*LSLIQ$D%W5P9th55#6cxyp^dV1PnzlHv zb{xlk_TlKoiFxt?>YCL`#=AC&bb;TIoH=`Eqay#s9D>@k3arc>Y&>D8=lQva_^PV(LDT>ylEn-3Bk-t1NLejv>YOl#YDXZNxdZW6Zhror$V zuUP7oukGAviOq#bMRRJ|abo-Ucs?&Sud2Wp|59!I#q8`lq)b;3y|D>^9r>>TSoAET zNf@Qk=#s_{``Vc|4dRb80QC0+xO8hlE!Mlk55Tmx9ROo`itNIinz!rA|Ghi>#2;ty z1?tG>$_{$Y_4Qd-CMRj}dKq2P6g=_A8TMBf0BG9RLO79Rt4ny0h3&Q=;lu**$C;AjtC&*)so#G7x#qbtImxl+W(Au(eb`@yZzPsf3)5J~ zNUZEkWU#wDU(yBG>M9vG)&T~C13Ix`!K1cnu{nV2fL@1d`)1lhA$9suGecyslZm36 ziK3gxU?-`eyQD6Uks7+|n3J22#F7q2Bb$Lu7+8zq2+v5==Gr%KayE3)NRu>zb z%B>LD{K+_GrX26s?~@M1B2k@@i-vN{fe7N}9BXbC9B2=PSY#Ph^Pj2!&|j;-?Q%f|-aFK$oQXtr$2t>5H<75$<|k<$rsk13#{=?SFDvK(I?4_c z8#d_g32^G#D1~B?k<*pT92IlMnN{vwT_Jk9l3#rI2!KO0I`|-wOby-T_L7^n>e9ML z_F1lHGhSq{vrITpP@lRsN+z47P$=N{`M7g+1pt4;LG~W4!te8u%jL-Ba$H-QCN^v+ zTe-PB9H6qIEiw>)oDpOjx+`K>f-#=?iq(7`zt2a7*URuvgmZVUu8^6zj=$j`WACrv z_xY3~*OsOo2k1E2%|_zdaFjBU`g;NtibdAe*2Jd2S^}W+vswxtRdD;QqH;ny=JALO z-`^8pa6ngwk_Uov4O!=a{rX7gbGaOTpUl|y;tZfn`lu4++CqUD_7C;cl%s`=7R0d=3uW;w;8l9ijVu<>~-EcdZZ~1$jT;VB$lMVC*W92!aQ?c|=+7?uvj>MAm`s;}!@<4^x%blw$9Q-NFoY%PhmIzOG z&_|M-+8~q7;`jOJ_1AMRvEZmF{XGHhKlwKwoP67QY3NYLWOOc;skFg?da(M`w6ZX!{gdx-_Y zeu0EUW8iF^gK!srmg5gccJkh#HfPwT=gL>xmWrilr(b>30x<5!+0{|YH>F;#<7Bt9 zoKDX%a#8dId%B!Ws)YJpVxg>G5_|hLQFODXD}Xjr$rrP;n69A6gyvI`7BAgeAnY%v zO=-AMU6*lPIX*UbkBzMgr=cs|eTFYdz3tS61!T=sn(KWMcz zrRCFZbrr<;bh$;B@sC7x%nZE#VmaruheBw-KKdgI;9p-RIM&>Z)sUlUTTsCriR#c; zx2~a(WZ_^CGo7YA6x#54Wf$4T+vUI6Y@`dYn9mCb01kvgnCY}b zVLYE#IXiY_p9S^@EbDaTBNd#v#ke1L=!SzqYP?=?6lr&` z`v^B5BpkYbP2F(l{=Bo(dah7V-%s6c{+LW^{{b0Co%;|gpK1UA002ovPDHLkV1nB# B!2AFJ literal 0 HcmV?d00001 From 1e73c99d6954298610f63289a2a0f0834ee00f77 Mon Sep 17 00:00:00 2001 From: Ewanderer <6307595+Ewanderer@users.noreply.github.com> Date: Tue, 2 Jun 2026 03:13:52 +0200 Subject: [PATCH 024/287] constructed toilets no longer can spawn items (#44210) * made constructed toilet not plungable and not rummagable to avoid item generation. * cleaning up and adding comment. * restored solution generation to base toilet * Update Resources/Prototypes/Entities/Structures/Furniture/toilet.yml --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../Entities/Structures/Furniture/toilet.yml | 39 ++++++++++++------- .../Construction/Graphs/furniture/toilet.yml | 2 +- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index 33ac061450e..8eef0443467 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -24,7 +24,7 @@ False: { state: dispover-ready, visible: true } enum.PowerDeviceVisuals.Powered: enum.DisposalUnitVisualLayers.OverlayCharging: - False: { visible: false } + False: { visible: false } enum.AnchorVisuals.Anchored: enum.DisposalUnitVisualLayers.Base: True: { state: disposal } @@ -81,7 +81,6 @@ - MachineMask layer: - None - - type: PlungerUse - type: Appearance - type: SecretStash secretStashName: secret-stash-toilet @@ -100,17 +99,9 @@ interfaces: enum.DisposalUnitUiKey.Key: type: DisposalUnitBoundUserInterface - - type: Rummageable - table: !type:NestedSelector - tableId: RatKingLoot - type: SolutionManager solutions: - SolutionDrainNormal - - type: SolutionRegeneration - generated: - reagents: - - ReagentId: Water - Quantity: 1 - type: DrainableSolution solution: tank - type: ReagentTank @@ -121,18 +112,36 @@ solution: tank destroyOnEmpty: false utensil: Spoon + - type: SolutionRegeneration + generated: + reagents: + - ReagentId: Water + Quantity: 1 +# constructed toilets don't come with Rummageable or plungeruse component to avoid infinite item spawning. - type: entity parent: BaseToilet - id: ToiletEmpty + id: ConstructedToilet name: toilet description: The HT-451, a torque rotation-based, waste disposal unit for small matter. This one seems remarkably clean. - suffix: Empty + suffix: Constructed, Unplungable components: - type: Construction graph: Toilet node: toilet +- type: entity + parent: BaseToilet + id: ToiletEmpty + name: toilet + description: The HT-451, a torque rotation-based, waste disposal unit for small matter. This one seems remarkably clean. + suffix: Empty + components: + - type: PlungerUse + - type: Rummageable + table: !type:NestedSelector + tableId: RatKingLoot + # so theres not actually any way to replenish the gastrotoxin / gold in these. # I wouldn't add it to the solutionregeneration comp because that doesn't make a lot of sense imo. - type: entity @@ -201,6 +210,10 @@ IngotGold1: min: 5 max: 5 + - type: PlungerUse + - type: Rummageable + table: !type:NestedSelector + tableId: RatKingLoot - type: entity parent: ToiletGoldenEmpty @@ -217,4 +230,4 @@ - ReagentId: Gold Quantity: 20 - ReagentId: GastroToxin - Quantity: 20 \ No newline at end of file + Quantity: 20 diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/furniture/toilet.yml b/Resources/Prototypes/Recipes/Construction/Graphs/furniture/toilet.yml index d9a4cd02fb4..d316cc3abed 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/furniture/toilet.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/furniture/toilet.yml @@ -12,7 +12,7 @@ amount: 5 doAfter: 1 - node: toilet - entity: ToiletEmpty + entity: ConstructedToilet edges: - to: start completed: From b2e9308ca00662bfa0e44abfae5068b30086db7c Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 2 Jun 2026 01:32:46 +0000 Subject: [PATCH 025/287] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 812091d3adf..84ce52f223a 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Happyrobot33 - changes: - - message: Added lobby credits for the background art - type: Add - id: 9222 - time: '2025-11-23T13:24:57.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/41328 - author: Samuka changes: - message: There is a now a chance for ion storm to set borgs to xenoborg or mothership @@ -4095,3 +4088,10 @@ id: 9733 time: '2026-06-01T21:29:51.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/44209 +- author: Ewanderer + changes: + - message: You can no longer generate infinite items from constructing toilets. + type: Fix + id: 9734 + time: '2026-06-02T01:31:37.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/44210 From b5a8acdf9f34df82a924e49633f70f588b70b9e5 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Mon, 1 Jun 2026 19:50:53 -0700 Subject: [PATCH 026/287] [MICROBALANCE PR] Explosives no longer pierce armor (#40090) * Explosiv nerf * Minor changes * Whoops * whoopsie doodles. * Address half of the review. * forgot this too (flat reductions are applied before the modifier so we need to account for that :P) * fix merge conflicts * it's time * whoops * me when I logmissing true * they call me the forgor cause I forgor --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../EntitySystems/ExplosionSystem.Airtight.cs | 96 +++++++++++++++---- .../ExplosionSystem.Processing.cs | 34 ++----- .../EntitySystems/ExplosionSystem.cs | 2 + .../Damage/Systems/DamageableSystem.API.cs | 16 +++- .../en-US/explosions/explosion-resistance.ftl | 2 +- .../Entities/Clothing/OuterClothing/armor.yml | 10 +- .../Clothing/OuterClothing/hardsuits.yml | 12 +-- 7 files changed, 111 insertions(+), 61 deletions(-) diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs index 1f6949f491d..2b420bde1b7 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs @@ -1,4 +1,3 @@ -using System.Linq; using System.Runtime.InteropServices; using Content.Server.Atmos.Components; using Content.Server.Explosion.Components; @@ -214,12 +213,16 @@ private void GetExplosionTolerance(EntityUid uid, Span explosionTol // that this will result in a non-airtight entity.Entities that ONLY break via construction graph node changes // are currently effectively "invincible" as far as this is concerned. This really should be done more rigorously. var totalDamageTarget = FixedPoint2.MaxValue; - if (_destructibleQuery.TryGetComponent(uid, out var destructible)) + if (_destructibleQuery.TryComp(uid, out var destructible)) { totalDamageTarget = _destructibleSystem.DestroyedAt(uid, destructible); } - if (totalDamageTarget == FixedPoint2.MaxValue || !_injurableQuery.TryGetComponent(uid, out var injurable)) + // We are assuming airtight entities don't need to relay since they shouldn't have inventories. + var modifiers = _damageableSystem.GetDamageModifierSet(uid); + var explosionComp = _explosionResistanceQuery.CompOrNull(uid); + + if (totalDamageTarget == FixedPoint2.MaxValue || !_injurableQuery.TryComp(uid, out var injurable)) { for (var i = 0; i < explosionTolerance.Length; i++) { @@ -238,33 +241,92 @@ private void GetExplosionTolerance(EntityUid uid, Span explosionTol { // TODO EXPLOSION SYSTEM // cache explosion type damage. - if (!_prototypeManager.Resolve(id, out ExplosionPrototype? explosionType)) + if (!_prototypeManager.Resolve(id, out var explosionType)) continue; // evaluate the damage that this damage type would do to this entity var damagePerIntensity = FixedPoint2.Zero; + + // Create a dictionary of intensity thresholds which dictates when damagePerIntensity increases! + var damageThresholds = new SortedDictionary(); foreach (var (type, value) in explosionType.DamagePerIntensity.DamageDict) { if (!_damageableSystem.CanBeDamagedBy((uid, injurable), type)) continue; - // TODO EXPLOSION SYSTEM - // add a variant of the event that gets raised once, instead of once per prototype. - // Or better yet, just calculate this manually w/o the event. - // The event mainly exists for indirect resistances via things like inventory & clothing - // But this shouldn't matter for airtight entities. - var ev = new GetExplosionResistanceEvent(explosionType.ID); - RaiseLocalEvent(uid, ref ev); - - damagePerIntensity += value * mod * Math.Max(0, ev.DamageCoefficient); + var modifier = mod; + if (explosionComp != null) + { + modifier *= explosionComp.DamageCoefficient; + if (explosionComp.Modifiers.TryGetValue(explosionType.ID, out var typeMod)) + modifier *= typeMod; + } + + if (modifiers != null) + { + if (modifiers.Coefficients.TryGetValue(type, out var armorMod)) + modifier *= armorMod; + + if (modifiers.FlatReduction.TryGetValue(type, out var flat)) + { + if (flat > 0) + { + // If the flat modifier is reducing damage, we cache the extra damage per intensity for later! + var intensity = flat / value; + var damage = damageThresholds.GetValueOrDefault(intensity); + damageThresholds[intensity] = value * Math.Max(0, modifier) + damage; + continue; + } + } + } + + damagePerIntensity += value * Math.Max(0, modifier); } - var toleranceValue = damagePerIntensity > 0 - ? (float) ((totalDamageTarget - _damageableSystem.GetTotalDamage(uid)) / damagePerIntensity) - : ToleranceValues.Invulnerable; + explosionTolerance[index] = GetExplosionTolerance(uid, totalDamageTarget, damagePerIntensity, damageThresholds); + } + } - explosionTolerance[index] = toleranceValue; + private FixedPoint2 GetExplosionTolerance(EntityUid uid, + FixedPoint2 totalDamageTarget, + FixedPoint2 damagePerIntensity, + SortedDictionary damageThresholds) + { + return GetExplosionTolerance(totalDamageTarget - _damageableSystem.GetTotalDamage(uid), + damagePerIntensity, + damageThresholds); + } + + private FixedPoint2 GetExplosionTolerance(FixedPoint2 damageTarget, + FixedPoint2 damagePerIntensity, + SortedDictionary damageThresholds) + { + var tolerance = damagePerIntensity > 0 ? damageTarget / damagePerIntensity : ToleranceValues.Invulnerable; + var prevIntensity = FixedPoint2.Zero; + /* + * Calculated through a pretty simple equation which relies on this dictionary being sorted. + * We precalculate the intensity at which an explosion's damage type exceeds the flat reduction of an entity's armor + * That is done above and stored in our `damageThresholds` SortedDictionary. If you can find a more mem efficient way to do this be my guest, + * but these values *have* to be sorted. + */ + foreach (var (intensity, damage) in damageThresholds) + { + // Check if the object would break before hitting this threshold, if so, return the current tolerance value + if (intensity > tolerance) + return tolerance; + + /* + * If the object breaks after this threshold, reduce the HP left by the amount of HP lost between the last flat reduction and this one + * Then adjust our damagePerIntensity and new tolerance values accordingly. + * Lastly store this intensity value so we can calculate the delta next loop. + */ + damageTarget -= (intensity - prevIntensity) * damagePerIntensity; + damagePerIntensity += damage; + tolerance = intensity + damageTarget / damagePerIntensity; + prevIntensity = intensity; } + + return tolerance; } private void OnAirtightGridRemoved(EntityUid entity) diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs index 003a9462c97..e7695efaf3f 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs @@ -1,24 +1,20 @@ using Content.Shared.CCVar; using Content.Shared.Damage; -using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; using Content.Shared.Database; using Content.Shared.Explosion; using Content.Shared.Explosion.Components; using Content.Shared.Maps; using Content.Shared.Physics; -using Content.Shared.Projectiles; -using Content.Shared.Tag; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Dynamics; using Robust.Shared.Random; using Robust.Shared.Timing; -using Robust.Shared.Utility; using System.Numerics; -using Content.Shared.Damage.Systems; using Robust.Shared.Prototypes; +using Robust.Shared.Utility; using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent; namespace Content.Server.Explosion.EntitySystems; @@ -228,34 +224,25 @@ internal bool ExplodeTile(BroadphaseComponent lookup, ProcessEntity(uid, epicenter, damage, throwForce, id, xform, fireStacks, cause); } - // process anchored entities - var tileBlocked = false; - _anchored.Clear(); - _map.GetAnchoredEntities(grid, tile, _anchored); - foreach (var entity in _anchored) - { - processed.Add(entity); - ProcessEntity(entity, epicenter, damage, throwForce, id, null, fireStacks, cause); - } - // heat the atmosphere if (temperature != null) { _atmosphere.HotspotExpose(grid.Owner, tile, temperature.Value, currentIntensity, cause, true); } + // We process anchored entities last, these should've been caught by the lookups earlier. // Walls and reinforced walls will break into girders. These girders will also be considered turf-blocking for // the purposes of destroying floors. Again, ideally the process of damaging an entity should somehow return // information about the entities that were spawned as a result, but without that information we just have to - // re-check for new anchored entities. Compared to entity spawning & deleting, this should still be relatively minor. + var tileBlocked = false; + _map.GetAnchoredEntities(grid, tile, _anchored); if (_anchored.Count > 0) { - _anchored.Clear(); - _map.GetAnchoredEntities(grid, tile, _anchored); foreach (var entity in _anchored) { tileBlocked |= IsBlockingTurf(entity); } + _anchored.Clear(); } // Next, we get the intersecting entities AGAIN, but purely for throwing. This way, glass shards spawned from @@ -441,7 +428,7 @@ private void ProcessEntity( DamageSpecifier? originalDamage, float throwForce, string id, - TransformComponent? xform, + TransformComponent xform, float? fireStacksOnIgnite, EntityUid? cause) { @@ -454,7 +441,7 @@ private void ProcessEntity( continue; // TODO EXPLOSIONS turn explosions into entities, and pass the the entity in as the damage origin. - _damageableSystem.TryChangeDamage((entity, damageable), damage, ignoreResistances: true, ignoreGlobalModifiers: true); + _damageableSystem.ChangeDamage((entity, damageable), damage); if (_actorQuery.HasComp(entity)) { @@ -478,8 +465,7 @@ private void ProcessEntity( } // throw - if (xform != null // null implies anchored or in a container - && !xform.Anchored + if (!xform.Anchored && throwForce > 0 && !EntityManager.IsQueuedForDeletion(uid) && _physicsQuery.TryGetComponent(uid, out var physics) @@ -704,7 +690,7 @@ struct ExplosionData private readonly IEntityManager _entMan; private readonly ExplosionSystem _system; private readonly SharedMapSystem _mapSystem; - private readonly Shared.Damage.Systems.DamageableSystem _damageable; + private readonly DamageableSystem _damageable; public readonly EntityUid VisualEnt; diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index 2a43695dd4e..2d25d224558 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Destructible; using Content.Server.NodeContainer.EntitySystems; using Content.Server.NPC.Pathfinding; +using Content.Shared.Armor; using Content.Shared.Atmos.Components; using Content.Shared.Camera; using Content.Shared.CCVar; @@ -64,6 +65,7 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem [Dependency] private EntityQuery _actorQuery = default!; [Dependency] private EntityQuery _destructibleQuery = default!; [Dependency] private EntityQuery _damageableQuery = default!; + [Dependency] private EntityQuery _explosionResistanceQuery = default!; [Dependency] private EntityQuery _injurableQuery = default!; [Dependency] private EntityQuery _airtightQuery = default!; [Dependency] private EntityQuery _tileHistoryQuery = default!; diff --git a/Content.Shared/Damage/Systems/DamageableSystem.API.cs b/Content.Shared/Damage/Systems/DamageableSystem.API.cs index c2b92bf5667..d5a7454828d 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.API.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.API.cs @@ -17,6 +17,17 @@ private bool SupportsType(ProtoId? container, ProtoId< return _supportedTypesByContainer[container.Value].Contains(type); } + public DamageModifierSet? GetDamageModifierSet(Entity entity) + { + if (!_damageableQuery.Resolve(entity, ref entity.Comp, false) + || entity.Comp.DamageModifierSetId is not { } proto + || !_prototypeManager.Resolve(proto, out var modifierSet) + ) + return null; + + return modifierSet; + } + /// /// Directly sets the damage in a damageable component. /// @@ -132,10 +143,7 @@ public DamageSpecifier ChangeDamage( // Apply resistances if (!ignoreResistances) { - if ( - ent.Comp.DamageModifierSetId != null && - _prototypeManager.Resolve(ent.Comp.DamageModifierSetId, out var modifierSet) - ) + if (GetDamageModifierSet(ent) is { } modifierSet) damage = DamageSpecifier.ApplyModifierSet(damage, modifierSet); // TODO DAMAGE diff --git a/Resources/Locale/en-US/explosions/explosion-resistance.ftl b/Resources/Locale/en-US/explosions/explosion-resistance.ftl index 6ebb406b9a9..50268ef32c8 100644 --- a/Resources/Locale/en-US/explosions/explosion-resistance.ftl +++ b/Resources/Locale/en-US/explosions/explosion-resistance.ftl @@ -1,2 +1,2 @@ -explosion-resistance-coefficient-value = - [color=orange]Explosion[/color] damage reduced by [color=lightblue]{$value}%[/color]. +explosion-resistance-coefficient-value = - [color=orange]Explosion[/color] damage reduced by an additional [color=lightblue]{$value}%[/color]. explosion-resistance-contents-coefficient-value = - [color=orange]Explosion[/color] damage [color=white]to contents[/color] reduced by [color=lightblue]{$value}%[/color]. diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml index b7a1e3cd30d..510f9895b07 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml @@ -114,8 +114,6 @@ Piercing: 0.6 Heat: 0.5 Caustic: 0.9 - - type: ExplosionResistance - damageCoefficient: 0.65 - type: GroupExamine - type: entity @@ -187,8 +185,6 @@ Heat: 0.3 Radiation: 0.5 Caustic: 0.5 - - type: ExplosionResistance - damageCoefficient: 0.5 - type: FireProtection reduction: 0.85 - type: StaticPrice @@ -238,8 +234,6 @@ Piercing: 0.35 Heat: 0.35 Caustic: 0.5 - - type: ExplosionResistance - damageCoefficient: 0.35 - type: StaminaResistance # do not add these to other equipment or mobs, we don't want to microbalance these everywhere damageCoefficient: 0.45 - type: ClothingSpeedModifier @@ -425,7 +419,7 @@ sprintModifier: 0.65 - type: HeldSpeedModifier - type: ExplosionResistance - damageCoefficient: 0.5 + damageCoefficient: 0.75 - type: GroupExamine - type: ProtectedFromStepTriggers slots: WITHOUT_POCKET @@ -451,7 +445,7 @@ sprintModifier: 0.8 - type: HeldSpeedModifier - type: ExplosionResistance - damageCoefficient: 0.4 + damageCoefficient: 0.6 - type: GroupExamine - type: Construction graph: BoneArmor diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml index e0d956ee59c..34272150493 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml @@ -162,7 +162,7 @@ highPressureMultiplier: 0.5 lowPressureMultiplier: 1000 - type: ExplosionResistance - damageCoefficient: 0.3 + damageCoefficient: 0.5 - type: Armor modifiers: coefficients: @@ -234,7 +234,7 @@ highPressureMultiplier: 0.5 lowPressureMultiplier: 1000 - type: ExplosionResistance - damageCoefficient: 0.4 + damageCoefficient: 0.5 - type: Armor modifiers: coefficients: @@ -296,7 +296,7 @@ highPressureMultiplier: 0.5 lowPressureMultiplier: 1000 - type: ExplosionResistance - damageCoefficient: 0.4 + damageCoefficient: 0.5 - type: Armor modifiers: coefficients: @@ -327,7 +327,7 @@ highPressureMultiplier: 0.02 lowPressureMultiplier: 1000 - type: ExplosionResistance - damageCoefficient: 0.5 + damageCoefficient: 0.7 - type: Armor modifiers: coefficients: @@ -608,7 +608,7 @@ heatingCoefficient: 0.001 coolingCoefficient: 0.001 - type: ExplosionResistance - damageCoefficient: 0.2 + damageCoefficient: 0.3 - type: FireProtection reduction: 0.8 - type: StaminaResistance # do not add these to other equipment or mobs, we don't want to microbalance these everywhere @@ -688,8 +688,6 @@ - type: PressureProtection highPressureMultiplier: 0.2 lowPressureMultiplier: 1000 - - type: ExplosionResistance - damageCoefficient: 0.3 # - type: StaminaResistance # Should not have stamina resistance, this is purely so people know it was not forgotten. - type: Armor modifiers: From 5611fb459dc19417e8a59a6368d46483497a491e Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 2 Jun 2026 03:04:02 +0000 Subject: [PATCH 027/287] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 84ce52f223a..ccd187b9b17 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Samuka - changes: - - message: There is a now a chance for ion storm to set borgs to xenoborg or mothership - core lawset - type: Add - id: 9223 - time: '2025-11-23T19:11:21.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/41532 - author: ReWAFFlution changes: - message: Balloons now float and slide smoothly. @@ -4095,3 +4087,10 @@ id: 9734 time: '2026-06-02T01:31:37.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/44210 +- author: Princess-Cheeseballs + changes: + - message: Explosive resistance now stacks with other damage resistances. + type: Tweak + id: 9735 + time: '2026-06-02T03:02:54.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/40090 From d8f546748f13c2de294fb7c7f5fc3b8002ad843c Mon Sep 17 00:00:00 2001 From: Rami Date: Mon, 1 Jun 2026 21:06:07 -0700 Subject: [PATCH 028/287] Fix Organs being Unedible (#44197) * Fix Organs being InEditable * Fix Spelling mistake * Requested change * I love breaking changes * shrink solution volume * fix lizards being unable to eat raw meat --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- Resources/Prototypes/Body/Animals/animal.yml | 2 +- .../Prototypes/Body/Species/arachnid.yml | 2 +- Resources/Prototypes/Body/Species/diona.yml | 2 +- Resources/Prototypes/Body/Species/dwarf.yml | 2 +- .../Prototypes/Body/Species/gingerbread.yml | 2 +- Resources/Prototypes/Body/Species/human.yml | 2 +- Resources/Prototypes/Body/Species/moth.yml | 2 +- .../Prototypes/Body/Species/reptilian.yml | 2 +- .../Prototypes/Body/Species/skeleton.yml | 2 +- Resources/Prototypes/Body/Species/slime.yml | 2 +- Resources/Prototypes/Body/Species/vox.yml | 2 +- .../Prototypes/Body/Species/vulpkanin.yml | 2 +- Resources/Prototypes/Body/base_organs.yml | 29 ++++++++++++++++++- 13 files changed, 40 insertions(+), 13 deletions(-) diff --git a/Resources/Prototypes/Body/Animals/animal.yml b/Resources/Prototypes/Body/Animals/animal.yml index bf8d15c9211..03787b582cc 100644 --- a/Resources/Prototypes/Body/Animals/animal.yml +++ b/Resources/Prototypes/Body/Animals/animal.yml @@ -6,7 +6,7 @@ metabolizerTypes: [ Animal ] - type: entity - parent: OrganBase + parent: OrganBaseOrganic id: OrganAnimal abstract: true suffix: Animal diff --git a/Resources/Prototypes/Body/Species/arachnid.yml b/Resources/Prototypes/Body/Species/arachnid.yml index 92c9a0c496b..4fc54b799b9 100644 --- a/Resources/Prototypes/Body/Species/arachnid.yml +++ b/Resources/Prototypes/Body/Species/arachnid.yml @@ -144,7 +144,7 @@ state: creampie_arachnid - type: entity - parent: OrganBase + parent: OrganBaseOrganic id: OrganArachnid abstract: true suffix: Arachnid diff --git a/Resources/Prototypes/Body/Species/diona.yml b/Resources/Prototypes/Body/Species/diona.yml index 4e257f5a3b9..6330e4f0e4d 100644 --- a/Resources/Prototypes/Body/Species/diona.yml +++ b/Resources/Prototypes/Body/Species/diona.yml @@ -172,7 +172,7 @@ - type: Rootable - type: entity - parent: OrganBase + parent: OrganBaseOrganic id: OrganDiona abstract: true suffix: Diona diff --git a/Resources/Prototypes/Body/Species/dwarf.yml b/Resources/Prototypes/Body/Species/dwarf.yml index 5b053f0735c..6159f2921a3 100644 --- a/Resources/Prototypes/Body/Species/dwarf.yml +++ b/Resources/Prototypes/Body/Species/dwarf.yml @@ -69,7 +69,7 @@ speechSounds: Bass - type: entity - parent: OrganBase + parent: OrganBaseOrganic id: OrganDwarf abstract: true suffix: Dwarf diff --git a/Resources/Prototypes/Body/Species/gingerbread.yml b/Resources/Prototypes/Body/Species/gingerbread.yml index 088d7d685a2..a56edcc6996 100644 --- a/Resources/Prototypes/Body/Species/gingerbread.yml +++ b/Resources/Prototypes/Body/Species/gingerbread.yml @@ -79,7 +79,7 @@ proto: gingerbread - type: entity - parent: OrganBase + parent: OrganBaseOrganic id: OrganGingerbread abstract: true suffix: gingerbread diff --git a/Resources/Prototypes/Body/Species/human.yml b/Resources/Prototypes/Body/Species/human.yml index 9b50ba8c534..50b24d43db3 100644 --- a/Resources/Prototypes/Body/Species/human.yml +++ b/Resources/Prototypes/Body/Species/human.yml @@ -99,7 +99,7 @@ amount: 5 - type: entity - parent: OrganBase + parent: OrganBaseOrganic id: OrganHuman abstract: true suffix: human diff --git a/Resources/Prototypes/Body/Species/moth.yml b/Resources/Prototypes/Body/Species/moth.yml index d40ccad10d5..d3e40797ea5 100644 --- a/Resources/Prototypes/Body/Species/moth.yml +++ b/Resources/Prototypes/Body/Species/moth.yml @@ -200,7 +200,7 @@ 250: 0.7 - type: entity - parent: OrganBase + parent: OrganBaseOrganic id: OrganMoth abstract: true suffix: Moth diff --git a/Resources/Prototypes/Body/Species/reptilian.yml b/Resources/Prototypes/Body/Species/reptilian.yml index 1ea0fdacfc7..5b25b146f48 100644 --- a/Resources/Prototypes/Body/Species/reptilian.yml +++ b/Resources/Prototypes/Body/Species/reptilian.yml @@ -172,7 +172,7 @@ - type: Wagging - type: entity - parent: OrganBase + parent: OrganBaseOrganic id: OrganReptilian abstract: true suffix: Reptilian diff --git a/Resources/Prototypes/Body/Species/skeleton.yml b/Resources/Prototypes/Body/Species/skeleton.yml index 16776434784..778f267e65b 100644 --- a/Resources/Prototypes/Body/Species/skeleton.yml +++ b/Resources/Prototypes/Body/Species/skeleton.yml @@ -185,7 +185,7 @@ messagePerceivedByOthers: hugging-success-generic-others - type: entity - parent: OrganBase + parent: OrganBaseOrganic id: OrganSkeletonPerson abstract: true suffix: SkeletonPerson diff --git a/Resources/Prototypes/Body/Species/slime.yml b/Resources/Prototypes/Body/Species/slime.yml index 6369afc0a3f..f27416a737d 100644 --- a/Resources/Prototypes/Body/Species/slime.yml +++ b/Resources/Prototypes/Body/Species/slime.yml @@ -175,7 +175,7 @@ maxSaturation: 15 - type: entity - parent: OrganBase + parent: OrganBaseOrganic id: OrganSlimePerson abstract: true suffix: slime person diff --git a/Resources/Prototypes/Body/Species/vox.yml b/Resources/Prototypes/Body/Species/vox.yml index 9491a5dd463..bc8baf37a0b 100644 --- a/Resources/Prototypes/Body/Species/vox.yml +++ b/Resources/Prototypes/Body/Species/vox.yml @@ -258,7 +258,7 @@ - type: Wagging - type: entity - parent: OrganBase + parent: OrganBaseOrganic id: OrganVox abstract: true suffix: Vox diff --git a/Resources/Prototypes/Body/Species/vulpkanin.yml b/Resources/Prototypes/Body/Species/vulpkanin.yml index 4bb845a9133..07ad667b0c2 100644 --- a/Resources/Prototypes/Body/Species/vulpkanin.yml +++ b/Resources/Prototypes/Body/Species/vulpkanin.yml @@ -213,7 +213,7 @@ state: creampie_vulpkanin - type: entity - parent: OrganBase + parent: OrganBaseOrganic id: OrganVulpkanin abstract: true suffix: vulpkanin diff --git a/Resources/Prototypes/Body/base_organs.yml b/Resources/Prototypes/Body/base_organs.yml index 924759a43ec..1333a3c90f5 100644 --- a/Resources/Prototypes/Body/base_organs.yml +++ b/Resources/Prototypes/Body/base_organs.yml @@ -23,7 +23,27 @@ - type: Organ - type: entity - parent: OrganBase + id: OrganBaseOrganic + name: organ + abstract: true + parent: [ OrganBase, SolutionFood, SolutionTiny ] + components: + - type: FlavorProfile + flavors: + - chicken # INTRODUCING OUR NEW EVERY-MEAT BURRITO, IT'S GOT EVERY MEAT! + - type: Edible + - type: Solution + solution: + reagents: + - ReagentId: UncookedAnimalProteins + Quantity: 20 + - type: Tag # I am nearing my limit of tolerance for TagComponent. + tags: + - Raw + - Meat + +- type: entity + parent: OrganBaseOrganic id: OrganBaseTorso name: torso abstract: true @@ -255,6 +275,13 @@ - type: Examiner - type: BlockMovement - type: BadFood + - type: Solution + solution: + reagents: + - ReagentId: UncookedAnimalProteins + Quantity: 15 + - ReagentId: GreyMatter + Quantity: 10 - type: FoodSequenceElement entries: Burger: Brain From 96a977a8a2359440f2f07e844d2754330826c66e Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 2 Jun 2026 04:24:26 +0000 Subject: [PATCH 029/287] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index ccd187b9b17..48cfbe14599 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: ReWAFFlution - changes: - - message: Balloons now float and slide smoothly. - type: Tweak - id: 9224 - time: '2025-11-23T20:41:17.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/41102 - author: slarticodefast changes: - message: Fixed the gas filter gas selection. @@ -4094,3 +4087,10 @@ id: 9735 time: '2026-06-02T03:02:54.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/40090 +- author: YoungThugSS14 + changes: + - message: Organs are edible again + type: Fix + id: 9736 + time: '2026-06-02T04:23:18.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/44197 From faa9ead59488f956ab2471f9ef190c968a223a19 Mon Sep 17 00:00:00 2001 From: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Date: Tue, 2 Jun 2026 12:52:50 -0700 Subject: [PATCH 030/287] Migrate Atmospherics to TensorPrimitives (#44139) --- Content.Benchmarks/DeltaPressureBenchmark.cs | 2 +- .../EntitySystems/AtmosphereSystem.Gases.cs | 19 +++++------ .../EntitySystems/AtmosphereSystem.API.cs | 8 ++--- .../AtmosphereSystem.DeltaPressure.cs | 7 ++-- .../EntitySystems/AtmosphereSystem.Gases.cs | 32 ++++++++----------- .../SharedAtmosphereSystem.Gases.cs | 5 +-- Content.Shared/Atmos/GasMixture.cs | 9 +++--- Content.Shared/Content.Shared.csproj | 1 + .../Server/Atmos/AddMolsToMixtureTest.cs | 4 +-- 9 files changed, 43 insertions(+), 44 deletions(-) diff --git a/Content.Benchmarks/DeltaPressureBenchmark.cs b/Content.Benchmarks/DeltaPressureBenchmark.cs index dac79e03760..f4ae62a4652 100644 --- a/Content.Benchmarks/DeltaPressureBenchmark.cs +++ b/Content.Benchmarks/DeltaPressureBenchmark.cs @@ -32,7 +32,7 @@ public class DeltaPressureBenchmark /// /// Number of entities (windows, really) to spawn with a . /// - [Params(1, 10, 100, 1000, 5000, 10000, 50000, 100000)] + [Params(100, 1000, 5000, 10000)] public int EntityCount; /// diff --git a/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs index 24f3ffaa261..0619e26c4b2 100644 --- a/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -1,3 +1,4 @@ +using System.Numerics.Tensors; using System.Runtime.CompilerServices; using Content.Shared.Atmos; using Content.Shared.Atmos.Reactions; @@ -26,15 +27,15 @@ public override ReactionResult React(GasMixture mixture, IGasMixtureHolder? hold public override bool IsMixtureFuel(GasMixture mixture, float epsilon = Atmospherics.Epsilon) { var tmp = new float[Atmospherics.AdjustedNumberOfGases]; - NumericsHelpers.Multiply(mixture.Moles, GasFuelMask, tmp); - return NumericsHelpers.HorizontalAdd(tmp) > epsilon; + TensorPrimitives.Multiply(mixture.Moles, GasFuelMask, tmp); + return TensorPrimitives.Sum(tmp) > epsilon; } public override bool IsMixtureOxidizer(GasMixture mixture, float epsilon = Atmospherics.Epsilon) { var tmp = new float[Atmospherics.AdjustedNumberOfGases]; - NumericsHelpers.Multiply(mixture.Moles, GasOxidizerMask, tmp); - return NumericsHelpers.HorizontalAdd(tmp) > epsilon; + TensorPrimitives.Multiply(mixture.Moles, GasOxidizerMask, tmp); + return TensorPrimitives.Sum(tmp) > epsilon; } public override float GetMass(GasMixture mix) @@ -45,17 +46,17 @@ public override float GetMass(GasMixture mix) public override float GetMass(float[] moles) { var tmp = new float[moles.Length]; - NumericsHelpers.Multiply(moles, GasMolarMasses, tmp); + TensorPrimitives.Multiply(moles, GasMolarMasses, tmp); // Conversion of grams to kilograms. - return NumericsHelpers.HorizontalAdd(tmp) * Atmospherics.gToKg; + return TensorPrimitives.Sum(tmp) * Atmospherics.gToKg; } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override float GetHeatCapacityCalculation(float[] moles, bool space) { // Little hack to make space gas mixtures have heat capacity, therefore allowing them to cool down rooms. - if (space && MathHelper.CloseTo(NumericsHelpers.HorizontalAdd(moles), 0f)) + if (space && MathHelper.CloseTo(TensorPrimitives.Sum(moles), 0f)) { return Atmospherics.SpaceHeatCapacity; } @@ -65,9 +66,9 @@ protected override float GetHeatCapacityCalculation(float[] moles, bool space) // though this isnt the hottest code path so it should be fine // the gc can eat a little as a treat var tmp = new float[moles.Length]; - NumericsHelpers.Multiply(moles, GasMolarHeatCapacities, tmp); + TensorPrimitives.Multiply(moles, GasMolarHeatCapacities, tmp); // Adjust heat capacity by speedup, because this is primarily what // determines how quickly gases heat up/cool. - return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity); + return MathF.Max(TensorPrimitives.Sum(tmp), Atmospherics.MinimumHeatCapacity); } } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs index 27e9f33653a..50c3add7486 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs @@ -1,5 +1,6 @@ using System.Buffers; using System.Diagnostics; +using System.Numerics.Tensors; using Content.Server.NodeContainer.NodeGroups; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; @@ -366,10 +367,9 @@ public static void GetBulkGasMixturePressures(Span mixtures, Span tmp = stackalloc float[moles.Length]; - NumericsHelpers.Multiply(moles, GasMolarMasses, tmp); + TensorPrimitives.Multiply(moles, GasMolarMasses, tmp); // Conversion of grams to kilograms. - return NumericsHelpers.HorizontalAdd(tmp) * Atmospherics.gToKg; + return TensorPrimitives.Sum(tmp) * Atmospherics.gToKg; } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override float GetHeatCapacityCalculation(float[] moles, bool space) { // Little hack to make space gas mixtures have heat capacity, therefore allowing them to cool down rooms. - if (space && MathHelper.CloseTo(NumericsHelpers.HorizontalAdd(moles), 0f)) + if (space && MathHelper.CloseTo(TensorPrimitives.Sum(moles), 0f)) { return Atmospherics.SpaceHeatCapacity; } Span tmp = stackalloc float[moles.Length]; - NumericsHelpers.Multiply(moles, GasMolarHeatCapacities, tmp); + TensorPrimitives.Multiply(moles, GasMolarHeatCapacities, tmp); // Adjust heat capacity by speedup, because this is primarily what // determines how quickly gases heat up/cool. - return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity); + return MathF.Max(TensorPrimitives.Sum(tmp), Atmospherics.MinimumHeatCapacity); } public override bool IsMixtureFuel(GasMixture mixture, float epsilon = Atmospherics.Epsilon) { Span tmp = stackalloc float[Atmospherics.AdjustedNumberOfGases]; - NumericsHelpers.Multiply(mixture.Moles, GasFuelMask, tmp); - return NumericsHelpers.HorizontalAdd(tmp) > epsilon; + TensorPrimitives.Multiply(mixture.Moles, GasFuelMask, tmp); + return TensorPrimitives.Sum(tmp) > epsilon; } public override bool IsMixtureOxidizer(GasMixture mixture, float epsilon = Atmospherics.Epsilon) { Span tmp = stackalloc float[Atmospherics.AdjustedNumberOfGases]; - NumericsHelpers.Multiply(mixture.Moles, GasOxidizerMask, tmp); - return NumericsHelpers.HorizontalAdd(tmp) > epsilon; + TensorPrimitives.Multiply(mixture.Moles, GasOxidizerMask, tmp); + return TensorPrimitives.Sum(tmp) > epsilon; } /// @@ -131,8 +132,8 @@ public void DivideInto(GasMixture source, List receivers) } // transfer moles - NumericsHelpers.Multiply(source.Moles, fraction, buffer); - NumericsHelpers.Add(receiver.Moles, buffer); + TensorPrimitives.Multiply(source.Moles, fraction, buffer); + TensorPrimitives.Add(receiver.Moles, buffer, receiver.Moles); } } @@ -325,13 +326,8 @@ public override ReactionResult React(GasMixture mixture, IGasMixtureHolder? hold [PublicAPI] public static void AddMolsToMixture(GasMixture mixture, ReadOnlySpan molsToAdd) { - // Span length should be as long as the length of the gas array. - // Technically this is a redundant check because NumericsHelpers will do the same thing, - // but eh. - ArgumentOutOfRangeException.ThrowIfNotEqual(mixture.Moles.Length, molsToAdd.Length, nameof(mixture.Moles.Length)); - - NumericsHelpers.Add(mixture.Moles, molsToAdd); - NumericsHelpers.Max(mixture.Moles, 0f); + TensorPrimitives.Add(mixture.Moles, molsToAdd, mixture.Moles); + TensorPrimitives.Max(mixture.Moles, 0f, mixture.Moles); } public enum GasCompareResult diff --git a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs index 240711f048a..7a2c8339d7f 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs @@ -1,3 +1,4 @@ +using System.Numerics.Tensors; using System.Runtime.CompilerServices; using Content.Shared.Atmos.Prototypes; using Content.Shared.Atmos.Reactions; @@ -110,7 +111,7 @@ Most usages are going to want the scaled value anyway. [PublicAPI] public void GetFlammableMoles(GasMixture mixture, float[] buffer) { - NumericsHelpers.Multiply(mixture.Moles, GasOxidiserFuelMask, buffer); + TensorPrimitives.Multiply(mixture.Moles, GasOxidiserFuelMask, buffer); } /// @@ -213,7 +214,7 @@ public void Merge(GasMixture receiver, GasMixture giver) } } - NumericsHelpers.Add(receiver.Moles, giver.Moles); + TensorPrimitives.Add(receiver.Moles, giver.Moles, receiver.Moles); } /// diff --git a/Content.Shared/Atmos/GasMixture.cs b/Content.Shared/Atmos/GasMixture.cs index 3da7827cdd8..a5fc04e611b 100644 --- a/Content.Shared/Atmos/GasMixture.cs +++ b/Content.Shared/Atmos/GasMixture.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Numerics.Tensors; using System.Runtime.CompilerServices; using Content.Shared.Atmos.EntitySystems; using Content.Shared.Atmos.Reactions; @@ -42,7 +43,7 @@ public sealed partial class GasMixture : IEquatable, ISerializationH public float TotalMoles { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => NumericsHelpers.HorizontalAdd(Moles); + get => TensorPrimitives.Sum(Moles); } [ViewVariables] @@ -177,9 +178,9 @@ public GasMixture RemoveRatio(float ratio) var removed = new GasMixture(Volume) { Temperature = Temperature }; Moles.CopyTo(removed.Moles.AsSpan()); - NumericsHelpers.Multiply(removed.Moles, ratio); + TensorPrimitives.Multiply(removed.Moles, ratio, removed.Moles); if (!Immutable) - NumericsHelpers.Sub(Moles, removed.Moles); + TensorPrimitives.Subtract(Moles, removed.Moles, Moles); for (var i = 0; i < Moles.Length; i++) { @@ -223,7 +224,7 @@ public void Clear() public void Multiply(float multiplier) { if (Immutable) return; - NumericsHelpers.Multiply(Moles, multiplier); + TensorPrimitives.Multiply(Moles, multiplier, Moles); } void ISerializationHooks.AfterDeserialization() diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj index da32cdf817d..f8f986c9c55 100644 --- a/Content.Shared/Content.Shared.csproj +++ b/Content.Shared/Content.Shared.csproj @@ -6,6 +6,7 @@ + diff --git a/Content.Tests/Server/Atmos/AddMolsToMixtureTest.cs b/Content.Tests/Server/Atmos/AddMolsToMixtureTest.cs index 07b45bfe11d..936a9f13d6a 100644 --- a/Content.Tests/Server/Atmos/AddMolsToMixtureTest.cs +++ b/Content.Tests/Server/Atmos/AddMolsToMixtureTest.cs @@ -21,10 +21,8 @@ public void AddMolsToMixture_Throws_OnLengthMismatch(int num) var mixture = new GasMixture(); var wrongLength = new float[Atmospherics.AdjustedNumberOfGases + num]; - var ex = Assert.Throws(() => + Assert.Throws(() => AtmosphereSystem.AddMolsToMixture(mixture, wrongLength)); - - Assert.That(ex!.ParamName, Is.EqualTo("Length")); } /// From 61d5f0ea39786c915cc39f0b564ce0c62bc88330 Mon Sep 17 00:00:00 2001 From: Samuka <47865393+Samuka-C@users.noreply.github.com> Date: Wed, 3 Jun 2026 13:39:20 -0300 Subject: [PATCH 031/287] Slown down super bonk smite (#44223) * make it 1.5 seconds * empty commit --- Content.Server/Administration/Components/SuperBonkComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Administration/Components/SuperBonkComponent.cs b/Content.Server/Administration/Components/SuperBonkComponent.cs index 9c20c4d88a4..635a75cf7a5 100644 --- a/Content.Server/Administration/Components/SuperBonkComponent.cs +++ b/Content.Server/Administration/Components/SuperBonkComponent.cs @@ -20,7 +20,7 @@ public sealed partial class SuperBonkComponent : Component /// How often should we bonk. /// [DataField] - public TimeSpan BonkCooldown = TimeSpan.FromMilliseconds(100); + public TimeSpan BonkCooldown = TimeSpan.FromMilliseconds(1500); /// /// Next time when we will bonk. From 34160b2eea88db94ed7b9e2bfa5729b5c78b2086 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 3 Jun 2026 16:57:59 +0000 Subject: [PATCH 032/287] Automatic changelog update --- Resources/Changelog/Admin.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index 090b882779c..ddcf505413b 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -1739,5 +1739,12 @@ Entries: id: 212 time: '2026-05-12T19:52:05.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/43934 +- author: Samuka + changes: + - message: Super bonk smite is slower now, to avoid giving people seizures + type: Tweak + id: 213 + time: '2026-06-03T16:56:47.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/44223 Name: Admin Order: 3 From 946a516d7210db5749a54fa61110d3fef4894418 Mon Sep 17 00:00:00 2001 From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Wed, 3 Jun 2026 21:03:40 +0200 Subject: [PATCH 033/287] Make NumberObjective Max inclusive (#44228) * yeah * comment * Apply suggestion from @slarticodefast --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../Objectives/Components/NumberObjectiveComponent.cs | 4 ++-- Content.Server/Objectives/Systems/NumberObjectiveSystem.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Server/Objectives/Components/NumberObjectiveComponent.cs b/Content.Server/Objectives/Components/NumberObjectiveComponent.cs index d4f4508b586..93ecd5ba9e6 100644 --- a/Content.Server/Objectives/Components/NumberObjectiveComponent.cs +++ b/Content.Server/Objectives/Components/NumberObjectiveComponent.cs @@ -16,13 +16,13 @@ public sealed partial class NumberObjectiveComponent : Component public int Target; /// - /// Minimum number for target to roll. + /// Minimum number for target to roll. (Inclusive) /// [DataField(required: true)] public int Min; /// - /// Maximum number for target to roll. + /// Maximum number for target to roll. (Inclusive) /// [DataField(required: true)] public int Max; diff --git a/Content.Server/Objectives/Systems/NumberObjectiveSystem.cs b/Content.Server/Objectives/Systems/NumberObjectiveSystem.cs index d2b8b905ac8..06dd1067bc5 100644 --- a/Content.Server/Objectives/Systems/NumberObjectiveSystem.cs +++ b/Content.Server/Objectives/Systems/NumberObjectiveSystem.cs @@ -23,7 +23,7 @@ public override void Initialize() private void OnAssigned(EntityUid uid, NumberObjectiveComponent comp, ref ObjectiveAssignedEvent args) { - comp.Target = _random.Next(comp.Min, comp.Max); + comp.Target = _random.Next(comp.Min, comp.Max + 1); } private void OnAfterAssign(EntityUid uid, NumberObjectiveComponent comp, ref ObjectiveAfterAssignEvent args) From 27d23df0d1adc7f13cfad3b4319f16fa1807ac5f Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 3 Jun 2026 19:23:22 +0000 Subject: [PATCH 034/287] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 48cfbe14599..3be227b819f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: slarticodefast - changes: - - message: Fixed the gas filter gas selection. - type: Fix - id: 9225 - time: '2025-11-24T16:14:04.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/41567 - author: slarticodefast changes: - message: Power cells are now predicted. Please keep an eye out for bugs with any @@ -4094,3 +4087,11 @@ id: 9736 time: '2026-06-02T04:23:18.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/44197 +- author: ScarKy0 + changes: + - message: The "Crit Anomalies" objective will now correctly randomize the number + of anomalies to crit. + type: Fix + id: 9737 + time: '2026-06-03T19:22:13.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/44228 From 45b77c2c257005ecc65b8557a0ecf6d94dd59ef6 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Wed, 3 Jun 2026 17:01:14 -0400 Subject: [PATCH 035/287] Fix `GameTest.CTryComp` using the server entity manager (#44230) Fix GameTest.CTryComp using the server entity manager --- Content.IntegrationTests/Fixtures/GameTest.Entities.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.IntegrationTests/Fixtures/GameTest.Entities.cs b/Content.IntegrationTests/Fixtures/GameTest.Entities.cs index 2afdeec8ef5..9f6687d98f7 100644 --- a/Content.IntegrationTests/Fixtures/GameTest.Entities.cs +++ b/Content.IntegrationTests/Fixtures/GameTest.Entities.cs @@ -100,12 +100,12 @@ public T CComp(EntityUid target) } /// - /// Attempts to retrieve the given component from an entity on the server. + /// Attempts to retrieve the given component from an entity on the client. /// public bool CTryComp(EntityUid? target, [NotNullWhen(true)] out T? component) where T : IComponent { - return SEntMan.TryGetComponent(target, out component); + return CEntMan.TryGetComponent(target, out component); } /// From c588cad97169d84449b9e7576b5878d87743e17b Mon Sep 17 00:00:00 2001 From: KeTuFaisPiKiNut <85769816+ketufaispikinut@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:36:54 -0400 Subject: [PATCH 036/287] Changeling Random Objectives (#44212) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * changeling random objectives * addressed slarti's comments (i think :godo:) * Apply suggestion from @slarticodefast --------- Co-authored-by: nomdéraisonnablementlong Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Resources/Prototypes/GameRules/roundstart.yml | 14 ++++--- .../Prototypes/Objectives/changeling.yml | 21 +++++++++++ .../Prototypes/Objectives/objectiveGroups.yml | 37 ++++++++++++++++++- 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index 4659a6babf6..55373460107 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -225,11 +225,15 @@ range: min: 1 max: 3 - - type: AntagObjectives - objectives: - # - ChangelingSurviveObjective TODO: make a random selection of objectives when there are more of them - - ChangelingUniqueIdentityObjective - - ChangelingEscapeIdentityObjective + - type: AntagRandomObjectives + sets: + - groups: ChangelingObjectiveGroupDevourDummy + maxPicks: 1 + - groups: ChangelingObjectiveGroupEscapeDummy + maxPicks: 1 + - groups: ChangelingObjectiveGroupKillDummy + maxPicks: 1 + maxDifficulty: 8 - type: entity id: Revolutionary diff --git a/Resources/Prototypes/Objectives/changeling.yml b/Resources/Prototypes/Objectives/changeling.yml index 34aea43344d..674db20e307 100644 --- a/Resources/Prototypes/Objectives/changeling.yml +++ b/Resources/Prototypes/Objectives/changeling.yml @@ -57,3 +57,24 @@ blacklist: components: - ChangelingRole + +- type: entity + parent: [BaseChangelingObjective, BaseKillObjective] + id: ChangelingKillRandomPersonObjective + description: This person is our prey. We must ensure they stay dead on the station. + components: + - type: Objective + difficulty: 2.0 + unique: false + - type: TargetObjective + title: objective-condition-kill-maroon-title + - type: PickRandomPerson + filters: + # Can't have multiple objectives to kill the same person. + - !type:TargetObjectiveMindFilter + blacklist: + components: + - KillPersonCondition + - type: KillPersonCondition + requireDead: true + requireMaroon: true diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml index e4cee1239d3..bbe1b567940 100644 --- a/Resources/Prototypes/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/Objectives/objectiveGroups.yml @@ -135,4 +135,39 @@ id: ThiefObjectiveGroupEscape weights: EscapeThiefShuttleObjective: 1 -#Changeling, crew, wizard, when you code it... + +# Changeling +- type: weightedRandom # we need these dummy groups since its shitty code that won't stand having less than two groups before the objectives themselves + id: ChangelingObjectiveGroupDevourDummy + weights: + ChangelingObjectiveGroupDevour: 1 + +- type: weightedRandom + id: ChangelingObjectiveGroupDevour + weights: + ChangelingUniqueIdentityObjective: 1 + # eventually: devour most + +- type: weightedRandom + id: ChangelingObjectiveGroupEscapeDummy + weights: + ChangelingObjectiveGroupEscape: 1 + +- type: weightedRandom + id: ChangelingObjectiveGroupEscape + weights: + ChangelingSurviveObjective: 0.5 + ChangelingEscapeIdentityObjective: 1 + # eventually: escape as a job/dept + +- type: weightedRandom + id: ChangelingObjectiveGroupKillDummy + weights: + ChangelingObjectiveGroupKill: 1 + +- type: weightedRandom + id: ChangelingObjectiveGroupKill + weights: + ChangelingKillRandomPersonObjective: 1 + +# Crew, wizard, when you code it... From 5f61dda4ef709b438891e13a28e692948e9fec45 Mon Sep 17 00:00:00 2001 From: "Ignaz \"Ian\" Kraft" Date: Thu, 4 Jun 2026 06:08:14 +0200 Subject: [PATCH 037/287] make TestAdminLogsWindow more reliable (#43710) --- .../Tests/Administration/Logs/LogWindowTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Content.IntegrationTests/Tests/Administration/Logs/LogWindowTest.cs b/Content.IntegrationTests/Tests/Administration/Logs/LogWindowTest.cs index 80236c7d90f..7447f25ec12 100644 --- a/Content.IntegrationTests/Tests/Administration/Logs/LogWindowTest.cs +++ b/Content.IntegrationTests/Tests/Administration/Logs/LogWindowTest.cs @@ -40,9 +40,9 @@ public async Task TestAdminLogsWindow() // Search for the log we added earlier. await Client.WaitPost(() => search.Text = guid.ToString()); await ClickControl(refresh); - await RunTicks(5); + await RunTicks(10); var searchResult = cont.Children.Where(x => x.Visible && x is AdminLogLabel).Cast().ToArray(); - Assert.That(searchResult.Length, Is.EqualTo(1)); + Assert.That(searchResult, Has.Length.EqualTo(1)); Assert.That(searchResult[0].Log.Message, Contains.Substring($" test log 1: {guid}")); // Add a new log @@ -52,9 +52,9 @@ public async Task TestAdminLogsWindow() // Update the search and refresh await Client.WaitPost(() => search.Text = guid.ToString()); await ClickControl(refresh); - await RunTicks(5); + await RunTicks(10); searchResult = cont.Children.Where(x => x.Visible && x is AdminLogLabel).Cast().ToArray(); - Assert.That(searchResult.Length, Is.EqualTo(1)); + Assert.That(searchResult, Has.Length.EqualTo(1)); Assert.That(searchResult[0].Log.Message, Contains.Substring($" test log 2: {guid}")); } } From 124c16e11e382c95fe6d238a492b8a2b9efd8cf1 Mon Sep 17 00:00:00 2001 From: B_Kirill <153602297+B-Kirill@users.noreply.github.com> Date: Thu, 4 Jun 2026 20:26:27 +1000 Subject: [PATCH 038/287] Meat spike localization fixes (#44191) * Meat spike localization fixes * Review --- .../Kitchen/SharedKitchenSpikeSystem.cs | 27 +++++++++---------- .../components/kitchen-spike-component.ftl | 2 ++ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs b/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs index 178227be599..375e42f0e6a 100644 --- a/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs +++ b/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs @@ -26,6 +26,7 @@ using Robust.Shared.Timing; using Content.Shared.Tools.Systems; using Content.Shared.Tools.Components; +using Robust.Shared.Prototypes; namespace Content.Shared.Kitchen; @@ -36,6 +37,7 @@ public sealed partial class SharedKitchenSpikeSystem : EntitySystem { [Dependency] private IGameTiming _gameTiming = default!; [Dependency] private ISharedAdminLogManager _logger = default!; + [Dependency] private IPrototypeManager _protoManager = default!; [Dependency] private DamageableSystem _damageableSystem = default!; [Dependency] private ExamineSystemShared _examineSystem = default!; [Dependency] private MetaDataSystem _metaDataSystem = default!; @@ -130,8 +132,10 @@ private void OnInteractHand(Entity ent, ref InteractHandE if (args.Handled || !victim.HasValue) return; - _popupSystem.PopupClient(Loc.GetString("butcherable-need-knife", - ("target", Identity.Entity(victim.Value, EntityManager))), + var quality = _protoManager.Index(ent.Comp.RequiredToolQuality); + _popupSystem.PopupClient(Loc.GetString("comp-kitchen-spike-need-tool-quality", + ("target", Identity.Entity(victim.Value, EntityManager)), + ("quality", Loc.GetString(quality.Name))), ent, args.User, PopupType.Medium); @@ -150,11 +154,13 @@ private void OnInteractUsing(Entity ent, ref InteractUsin if (!TryComp(args.Used, out var tool) || !_toolSystem.HasQuality(args.Used, ent.Comp.RequiredToolQuality, tool)) { - _popupSystem.PopupClient(Loc.GetString("butcherable-need-knife", - ("target", Identity.Entity(victim.Value, EntityManager))), - ent, - args.User, - PopupType.Medium); + var quality = _protoManager.Index(ent.Comp.RequiredToolQuality); + _popupSystem.PopupClient(Loc.GetString("comp-kitchen-spike-need-tool-quality", + ("target", Identity.Entity(victim.Value, EntityManager)), + ("quality", Loc.GetString(quality.Name))), + ent, + args.User, + PopupType.Medium); return; } @@ -338,13 +344,6 @@ private void OnSpikeButcherDoAfter(Entity ent, ref SpikeB _audioSystem.PlayPredicted(ent.Comp.ButcherSound, ent, args.User); - _popupSystem.PopupClient(Loc.GetString("butcherable-knife-butchered-success", - ("target", Identity.Entity(args.Target.Value, EntityManager)), - ("knife", args.Used.Value)), - ent, - args.User, - PopupType.Medium); - args.Handled = true; } diff --git a/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl b/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl index 39877571b9a..d56d92e8355 100644 --- a/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl +++ b/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl @@ -28,6 +28,8 @@ comp-kitchen-spike-begin-butcher = { CAPITALIZE(THE($user)) } begins to butcher comp-kitchen-spike-butcher-self = You butchered { THE($victim) }! comp-kitchen-spike-butcher = { CAPITALIZE(THE($user)) } butchered { THE($victim) }! +comp-kitchen-spike-need-tool-quality = { $quality } tool required to butcher { THE($target) }. + comp-kitchen-spike-unhook-verb = Unhook comp-kitchen-spike-hooked = [color=red]{ CAPITALIZE(THE($victim)) } is on this spike![/color] From 32c39d1110dd9a513cedec62e4abeb9de0e8407c Mon Sep 17 00:00:00 2001 From: Matrix Date: Thu, 4 Jun 2026 11:31:57 -0600 Subject: [PATCH 039/287] add a drum kit (#42987) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DrumKit A drum unit was added to the cargo for 1500 speso, which exclusively plays channel 9 through a crutch with an “empty” sound font. * Apply suggestion from @noxypaws Co-authored-by: Noxy * prototype fix --------- Co-authored-by: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Co-authored-by: Noxy --- .../Audio/MidiCustom/space-station-14.sf2 | Bin 2499596 -> 2499718 bytes .../Prototypes/Catalog/Cargo/cargo_fun.yml | 10 ++++++++++ .../Instruments/instruments_structures.yml | 13 +++++++++++++ .../structureinstruments.rsi/drum.png | Bin 0 -> 3622 bytes .../structureinstruments.rsi/meta.json | 6 +++++- 5 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 Resources/Textures/Objects/Fun/Instruments/structureinstruments.rsi/drum.png diff --git a/Resources/Audio/MidiCustom/space-station-14.sf2 b/Resources/Audio/MidiCustom/space-station-14.sf2 index 879bb082dcef03c7cdeebd301db41c91631747eb..1811c90f74ae1d0392d7c669006ecb7a6dfeccbc 100644 GIT binary patch delta 61544 zcmeEu<#!f4)GsZRQfLbW3X}q+c;NuW-QBggYjKBz_i-85%w!Dr;t)6-+}+)^SaB#; zdNc3G`w!gru5}ixA9(W1w(Mk*oxMYzQbN3^m5{={JGF1Wq(Y@!v&M~@)U{KezU}jG zE1VjV=S;p_xdcZ@#~mj^YUS%WW#sIc6Q<3cFuH7&wi9NJo;Gt#QvT{?`%IWTcIxP{ zWvkRmPHvo>+#sp!qOxsfjvYCB?3l81W=)tnzHHBFljl!Qoi-I?=8l~?Yr?dtWoy@{ zmz2wu72(T@2*H1$_%AogipVoGy~u0xn?6D-1ShHE)s1Q@jMA&3@hH!v`vsu2$b4XI zG^&|j(0ws%Z)i+utEG{nbSz##4%1bpXQqgK>oY9iB)o?wms%2^gjadlX&)Xs?{G`>$ zDrOBp_w$Cg35}XzeKvmRKK-aZ*x*t12>e?C$|B7;v#hzoq|w3x;VnXA9$FWSdHQlJ zms@MC&osWEvr*yALSrUa6O4~qUhM$<3U6xVj7=!65XKiwHP7g>RzxeKZPkwGJ<<5W zHkGM{Q>zSRl~CKmRPDPSjZjgWN zLI0|i(;w-@%`>Q7+3LTapl50gceA%2AVgFyT)R32YOlow?|a93YvS2 zPsVOkt8#ewq_|AuvQ`6@S0^b=m7dB5rKeg04$`jZ^H487JS;RxFeO;2!ME3Y-s|$E`=+uCM*PVxp`Hgf`dj!8dgpqheSi5c1WJ*s>3_Lo>5=jS71S{2gqvmo?J&qAJ>^Gp z1e?N|>>}<7e?a^qr>cW>H)>{t7YQwY0!mUM*N<8doa&G9jrC6PR`sp(FAhE?Gnqa@ zbx=v`jb>Rky-%=#Jb=&7>?Wg#pMfWVPQjr>4q2ah$bA=6m8NLP}l{9 zt-*9Eo-hdIt?-W7KqdwW|4d&Y-*{hJ|JA@pf}=g$L9w$^N`HZxtqYF|Eq_G&A)n!^ zGa|V!2m|E;T?4NJ4T&UbFms>#A=Xj$YKzg74dF4N37Og{;1kbsml;Usr*Be2>A6fk zE>cL9>MFIhp~%>Tc_%hDPAaXWVSF;Xf=;KVQ2VG^^bV#s*G;%CtyT_d@#ykqY>~vy z`XBJONOJv{M)b4smG+ze}bxbp+KeLUw$`0g<$*n)Sn zEtpw!CmP^Up39cytBFJ8NcFQ;AEg|!d6&|wDS4zJd=l%XRjN7Fk@`t}r2XtX{RLlBhC>OsSk8P&J-reOVpDH9mASe9%LPZ zZ)KmbgY7^sCOZ*Zf>iKAa5E7@U86sPN?{4oAONKx$;zO$Tg(z5ygXV{LTFd{(t;i0*#2K zl)|*<>q+yJ{aP>7=`5yP`Ht2IMxgq~Ay^3fbnCF}8{or}%N%vmxIRYz*IQlvpFU|vz+BvlTB3=oS9-CX# zFzFY|Q_f&t{|)a+&kzsrqy`d7^j@ocy#-+O+8e z+2xkpr99!@G@l-bBl9yg_!ZJ%wIYhRhS5pGV2X5rRmrvi!RvJ=x`sJZoxh!bT=PAW zuWm4uYQ~-x27snoUDV`;EzK#tH)zH`riKMme6b$gwb>~;i@4(4$2_tA^x#UWC^tzg zro7a4ph>rE{2sjs2<0{EWZ;Zy;zX5$*^$yv|pyd!Fw8O2i!c6SqS` z>PmDl!^St$oYE||Aqo79r?u;r<9QmL_9iXUu?UY^l>bMtKE08vC1oqu(EC?5KFpX3 z>hrs)w}DmOLGDA&<5;4zjxEk&?)RQ~{^7)Y`YpFg3aT?v!dqMCEUOZHB-Y0qp7`5& zhq=Ev2RH{hZ#%!c)_b=4$^@gR*6d+n256$yM!nwI9GrT#oW*UTiUjle*1C(i`ZyDu zQ=R=>SKLRubp!dx3Jk@UmG7uWk>i7ne`J(aN(k%ecfpgsj-C~+)y_svpL3MUa4+

39{n{Q`^n# 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Instruments/structureinstruments.rsi/meta.json b/Resources/Textures/Objects/Fun/Instruments/structureinstruments.rsi/meta.json index ee4215faa60..0544b0293bd 100644 --- a/Resources/Textures/Objects/Fun/Instruments/structureinstruments.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/Instruments/structureinstruments.rsi/meta.json @@ -5,7 +5,7 @@ "y": 32 }, "license": "CC-BY-SA-3.0", - "copyright": "https://github.com/vgstation-coders/vgstation13 at 8d9c91e19cb52713c7f7f1804c2b6f7203f8d331, daw-base/display from /tg/staiton at commit https://github.com/tgstation/tgstation/commit/ebcf2ab180037d400a7e8ac7a2ba011a0f03edf3, harp.png, timpani.png and contrabass.png by Hyenh, tuba.png by EmoGarbage404", + "copyright": "https://github.com/vgstation-coders/vgstation13 at 8d9c91e19cb52713c7f7f1804c2b6f7203f8d331, daw-base/display from /tg/staiton at commit https://github.com/tgstation/tgstation/commit/ebcf2ab180037d400a7e8ac7a2ba011a0f03edf3, harp.png, timpani.png and contrabass.png by Hyenh, tuba.png by EmoGarbage404, drum.png by matriotic (Discord)", "states": [ { "name": "minimoog", @@ -85,6 +85,10 @@ 0.3 ] ] + }, + { + "name": "drum", + "directions": 4 } ] } From 9cfddca15c9e088b048287d008e25c7b59800a37 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 4 Jun 2026 17:51:58 +0000 Subject: [PATCH 040/287] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 3be227b819f..aa4c5cad5ce 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: slarticodefast - changes: - - message: Power cells are now predicted. Please keep an eye out for bugs with any - battery powered device. - type: Tweak - id: 9226 - time: '2025-11-24T17:04:34.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/41379 - author: YoungThugSS14 changes: - message: Tourniquets can now fit in medical belts @@ -4095,3 +4087,10 @@ id: 9737 time: '2026-06-03T19:22:13.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/44228 +- author: matriotic + changes: + - message: Added drum kit. It can be ordered in cargo for 1500 spesos. + type: Add + id: 9738 + time: '2026-06-04T17:50:48.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/42987 From 61f98c14d3346ebd961bd80fdd382156c0af8746 Mon Sep 17 00:00:00 2001 From: Pok <113675512+Pok27@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:29:21 +0300 Subject: [PATCH 041/287] Changeling reagent stings (#43805) * Changeling-Stings * update yml * attempt failed dead * sprite and ChangelingNeurodepressantSting * Update changeling_catalog.yml * merge * update * review * review 2 * clean up, remove non-blind stings * fix stuff * lethal, cryogenic, hallucination + armblade nerf * validate deez * a * review --------- Co-authored-by: ScarKy0 Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../Components/EntityEffectActionComponent.cs | 23 ++++ .../Components/PopupOnActionComponent.cs | 35 +++++++ .../Actions/EntityEffectActionSystem.cs | 27 +++++ Content.Shared/Actions/PopupOnActionSystem.cs | 62 +++++++++++ .../Systems/ChangelingAbilitySystem.cs | 1 - Content.Shared/Chemistry/ReactiveSystem.cs | 1 + ...dReagentToBloodstreamEntityEffectSystem.cs | 51 +++++++++ .../guidebook/entity-effects/effects.ftl | 6 ++ .../Locale/en-US/store/changeling-catalog.ftl | 12 +++ Resources/Prototypes/Actions/changeling.yml | 99 +++++++++++++++++- .../Prototypes/Catalog/changeling_catalog.yml | 60 +++++++++++ .../Objects/Weapons/Melee/armblade.yml | 6 +- .../Actions/changeling2.rsi/blind_sting.png | Bin 0 -> 1395 bytes .../changeling2.rsi/cryogenic_sting.png | Bin 0 -> 1336 bytes .../changeling2.rsi/hallucination_sting.png | Bin 0 -> 1413 bytes .../Actions/changeling2.rsi/lethal_sting.png | Bin 0 -> 1372 bytes .../Actions/changeling2.rsi/meta.json | 28 +++-- .../Actions/changeling2.rsi/mute_sting.png | Bin 0 -> 1400 bytes .../changeling2.rsi/neurodepressant_sting.png | Bin 0 -> 1405 bytes 19 files changed, 400 insertions(+), 11 deletions(-) create mode 100644 Content.Shared/Actions/Components/EntityEffectActionComponent.cs create mode 100644 Content.Shared/Actions/Components/PopupOnActionComponent.cs create mode 100644 Content.Shared/Actions/EntityEffectActionSystem.cs create mode 100644 Content.Shared/Actions/PopupOnActionSystem.cs create mode 100644 Content.Shared/EntityEffects/Effects/Body/AddReagentToBloodstreamEntityEffectSystem.cs create mode 100644 Resources/Textures/Interface/Actions/changeling2.rsi/blind_sting.png create mode 100644 Resources/Textures/Interface/Actions/changeling2.rsi/cryogenic_sting.png create mode 100644 Resources/Textures/Interface/Actions/changeling2.rsi/hallucination_sting.png create mode 100644 Resources/Textures/Interface/Actions/changeling2.rsi/lethal_sting.png create mode 100644 Resources/Textures/Interface/Actions/changeling2.rsi/mute_sting.png create mode 100644 Resources/Textures/Interface/Actions/changeling2.rsi/neurodepressant_sting.png diff --git a/Content.Shared/Actions/Components/EntityEffectActionComponent.cs b/Content.Shared/Actions/Components/EntityEffectActionComponent.cs new file mode 100644 index 00000000000..d32130df63f --- /dev/null +++ b/Content.Shared/Actions/Components/EntityEffectActionComponent.cs @@ -0,0 +1,23 @@ +using Content.Shared.EntityEffects; +using Robust.Shared.GameStates; + +namespace Content.Shared.Actions.Components; + +///

+/// An action component that applies a list of entity effects to the target when performed. +/// Requires and . +/// +[RegisterComponent, NetworkedComponent, Access(typeof(EntityEffectActionSystem))] +public sealed partial class EntityEffectActionComponent : Component +{ + /// + /// List of entity effects to apply to the target when this action is performed. + /// + [DataField(required: true)] + public List Effects = []; +} + +/// +/// Event raised when an entity effect action is performed. +/// +public sealed partial class EntityEffectActionEvent : EntityTargetActionEvent; diff --git a/Content.Shared/Actions/Components/PopupOnActionComponent.cs b/Content.Shared/Actions/Components/PopupOnActionComponent.cs new file mode 100644 index 00000000000..104e78ff9d5 --- /dev/null +++ b/Content.Shared/Actions/Components/PopupOnActionComponent.cs @@ -0,0 +1,35 @@ +using Content.Shared.Popups; +using Robust.Shared.GameStates; + +namespace Content.Shared.Actions.Components; + +/// +/// Displays a popup message when the action is successfully performed. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(PopupOnActionSystem))] +public sealed partial class PopupOnActionComponent : Component +{ + /// + /// The locale ID of the message to the performer of the action. + /// + [DataField] + public LocId? SelfMessage; + + /// + /// The message to show to those around the performer of the action. + /// + [DataField] + public LocId? OthersMessage; + + /// + /// The message to show to the action target. + /// + [DataField] + public LocId? TargetMessage; + + /// + /// The visual style of the popup. + /// + [DataField] + public PopupType PopupType = PopupType.Small; +} diff --git a/Content.Shared/Actions/EntityEffectActionSystem.cs b/Content.Shared/Actions/EntityEffectActionSystem.cs new file mode 100644 index 00000000000..288cc43a42b --- /dev/null +++ b/Content.Shared/Actions/EntityEffectActionSystem.cs @@ -0,0 +1,27 @@ +using Content.Shared.Actions.Components; +using Content.Shared.EntityEffects; + +namespace Content.Shared.Actions; + +/// +/// Handles applying entity effects when an entity effect action is performed. +/// +public sealed partial class EntityEffectActionSystem : EntitySystem +{ + [Dependency] private SharedEntityEffectsSystem _effects = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnEntityEffectAction); + } + + private void OnEntityEffectAction(Entity ent, ref EntityEffectActionEvent args) + { + foreach (var effect in ent.Comp.Effects) + { + if (_effects.TryApplyEffect(args.Target, effect, user: args.Performer)) + args.Handled = true; + } + } +} diff --git a/Content.Shared/Actions/PopupOnActionSystem.cs b/Content.Shared/Actions/PopupOnActionSystem.cs new file mode 100644 index 00000000000..6312c47dbf9 --- /dev/null +++ b/Content.Shared/Actions/PopupOnActionSystem.cs @@ -0,0 +1,62 @@ +using Content.Shared.Actions.Components; +using Content.Shared.Actions.Events; +using Content.Shared.IdentityManagement; +using Content.Shared.Popups; +using Robust.Shared.Player; + +namespace Content.Shared.Actions; + +/// +/// Handles displaying popup messages when actions are successfully performed. +/// +public sealed partial class PopupOnActionSystem : EntitySystem +{ + [Dependency] private SharedPopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnActionPerformed); + } + + private void OnActionPerformed(Entity ent, ref ActionPerformedEvent args) + { + EntityUid? target = null; + + if (TryComp(ent, out var entityTarget) && entityTarget.Event is { } ev) + target = ev.Target; + + var userName = Identity.Name(args.Performer, EntityManager); + var targetName = target != null ? Identity.Name(target.Value, EntityManager) : string.Empty; + + var selfMessage = ent.Comp.SelfMessage != null + ? Loc.GetString(ent.Comp.SelfMessage, ("target", targetName), ("user", userName)) + : null; + + var othersMessage = ent.Comp.OthersMessage != null + ? Loc.GetString(ent.Comp.OthersMessage, ("target", targetName), ("user", userName)) + : null; + + var targetMessage = ent.Comp.TargetMessage != null + ? Loc.GetString(ent.Comp.TargetMessage, ("target", targetName), ("user", userName)) + : null; + + // Popup to show to the performer. + // If there is a target the popup is located on the target, if there is no target it is located on the user. + _popup.PopupClient(selfMessage, target ?? args.Performer, args.Performer, ent.Comp.PopupType); + + // Popup to show to the target. + // Located on the target. + if (target != null) + _popup.PopupEntity(targetMessage, target.Value, target.Value, ent.Comp.PopupType); + + // Popup for everyone else. + // Located on the performer. + var filter = Filter.PvsExcept(args.Performer, entityManager: EntityManager); + if (target != null) + filter = filter.RemovePlayerByAttachedEntity(target.Value); + + _popup.PopupEntity(othersMessage, args.Performer, filter, true, ent.Comp.PopupType); + } +} diff --git a/Content.Shared/Changeling/Systems/ChangelingAbilitySystem.cs b/Content.Shared/Changeling/Systems/ChangelingAbilitySystem.cs index 3f4766c6e49..822af7bb3e6 100644 --- a/Content.Shared/Changeling/Systems/ChangelingAbilitySystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingAbilitySystem.cs @@ -80,7 +80,6 @@ private void OnStingDna(Entity ent, ref ChangelingS if (!_changelingDevour.CanDevour(ent.Owner, args.Target, checkDead: false, checkProtected: false)) return; - _popup.PopupClient(Loc.GetString("changeling-sting-success", ("target", Identity.Entity(args.Target, EntityManager))), args.Target, ent.Owner, PopupType.Medium); _changelingIdentity.GrantIdentity(ent, args.Target); args.Handled = true; diff --git a/Content.Shared/Chemistry/ReactiveSystem.cs b/Content.Shared/Chemistry/ReactiveSystem.cs index 62de02c5c25..b988a382b87 100644 --- a/Content.Shared/Chemistry/ReactiveSystem.cs +++ b/Content.Shared/Chemistry/ReactiveSystem.cs @@ -11,6 +11,7 @@ public sealed partial class ReactiveSystem : EntitySystem { [Dependency] private IPrototypeManager _proto = default!; + // TODO: Someone add documentation, I beg you public void DoEntityReaction(EntityUid uid, Solution solution, ReactionMethod method) { foreach (var reagent in solution.Contents.ToArray()) diff --git a/Content.Shared/EntityEffects/Effects/Body/AddReagentToBloodstreamEntityEffectSystem.cs b/Content.Shared/EntityEffects/Effects/Body/AddReagentToBloodstreamEntityEffectSystem.cs new file mode 100644 index 00000000000..67e8690d0ca --- /dev/null +++ b/Content.Shared/EntityEffects/Effects/Body/AddReagentToBloodstreamEntityEffectSystem.cs @@ -0,0 +1,51 @@ +using Content.Shared.Body.Components; +using Content.Shared.Body.Systems; +using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; + +namespace Content.Shared.EntityEffects.Effects.Body; + +/// +/// Adds a reagent directly to the target's bloodstream. +/// Quantity is modified by scale. +/// +/// +public sealed partial class AddReagentToBloodstreamEntityEffectSystem : EntityEffectSystem +{ + [Dependency] private SharedBloodstreamSystem _bloodstream = default!; + [Dependency] private ReactiveSystem _reactive = default!; + + protected override void Effect(Entity entity, ref EntityEffectEvent args) + { + var solution = new Content.Shared.Chemistry.Components.Solution(); + solution.AddReagent(args.Effect.Reagent, args.Effect.Quantity * args.Scale); + + _bloodstream.TryAddToBloodstream(entity.AsNullable(), solution); + _reactive.DoEntityReaction(entity, solution, ReactionMethod.Injection); // TODO: This should be part of TryAddToBloodstream + } +} + +/// +public sealed partial class AddReagentToBloodstream : EntityEffectBase +{ + /// + /// Prototype of the reagent we're adding. + /// + [DataField(required: true)] + public ProtoId Reagent; + + /// + /// Amount of reagent to add to the bloodstream. + /// + [DataField] + public FixedPoint2 Quantity = 1.0; + + public override string? EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return prototype.Resolve(Reagent, out var proto) + ? Loc.GetString("entity-effect-guidebook-add-reagent-to-bloodstream", ("chance", Probability), ("reagent", proto.LocalizedName), ("quantity", Quantity)) + : null; + } +} diff --git a/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl b/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl index 6e7fae92aeb..6186281080f 100644 --- a/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl +++ b/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl @@ -530,3 +530,9 @@ entity-effect-guidebook-plant-mutate-chemicals = [1] Mutates *[other] mutate } a plant to produce {$name} + +entity-effect-guidebook-add-reagent-to-bloodstream = + { $chance -> + [1] Injects + *[other] inject + } {$quantity} of {$reagent} directly into the bloodstream diff --git a/Resources/Locale/en-US/store/changeling-catalog.ftl b/Resources/Locale/en-US/store/changeling-catalog.ftl index 134ad32b081..d42415b1fdf 100644 --- a/Resources/Locale/en-US/store/changeling-catalog.ftl +++ b/Resources/Locale/en-US/store/changeling-catalog.ftl @@ -12,3 +12,15 @@ changeling-catalog-biodegrade-desc = You learn to utilize acid glands within you changeling-catalog-sting-dna-name = Extract DNA changeling-catalog-sting-dna-desc = Learn to utilize your stinger to absorb the identity of unsuspecting victims. Does not grant DNA to your store or objective progress. + +changeling-catalog-blind-sting-name = Blind Sting +changeling-catalog-blind-sting-desc = Causes temporary blindness in the target. Good to escape or initiate a fight. Can be used on self. + +changeling-catalog-cryogenic-sting-name = Cryogenic Sting +changeling-catalog-cryogenic-sting-desc = Causes movement slowdown in the target. Perfect for keeping prey from running away. Can be used on self. + +changeling-catalog-lethal-sting-name = Lethal Sting +changeling-catalog-lethal-sting-desc = Causes very slow damage to the target. Useless in combat, but ensures they will require medical attention. Can be used on self. + +changeling-catalog-hallucinogenic-sting-name = Hallucinogenic Sting +changeling-catalog-hallucinogenic-sting-desc = Causes the target to get high and have difficulty speaking. Causes confusion among the crew. Can be used on self. diff --git a/Resources/Prototypes/Actions/changeling.yml b/Resources/Prototypes/Actions/changeling.yml index e72663f78aa..0ebb7bdbed2 100644 --- a/Resources/Prototypes/Actions/changeling.yml +++ b/Resources/Prototypes/Actions/changeling.yml @@ -102,6 +102,103 @@ canTargetSelf: false whitelist: components: - - MobState - HumanoidProfile event: !type:ChangelingStingDnaEvent + - type: PopupOnAction + popupType: Medium + selfMessage: changeling-sting-success + +- type: entity + abstract: true + parent: BaseAction + id: BaseActionChangelingSting + components: + - type: TargetAction + interactOnMiss: false + - type: EntityTargetAction + whitelist: + components: + - Bloodstream + event: !type:EntityEffectActionEvent + - type: PopupOnAction + popupType: Medium + selfMessage: changeling-sting-success + +- type: entity + parent: BaseActionChangelingSting + id: ActionChangelingStingBlind + name: Blind Sting + description: Silently sting a target and worsen their sight for a short duration. + components: + - type: Action + useDelay: 60 # TODO: Chemical cost + raiseOnAction: true + itemIconStyle: BigAction + icon: + sprite: Interface/Actions/changeling2.rsi + state: blind_sting + - type: EntityEffectAction + effects: + - !type:AddReagentToBloodstream + reagent: NorepinephricAcid + quantity: 23 + +- type: entity + parent: BaseActionChangelingSting + id: ActionChangelingStingCryogenic + name: Cryogenic Sting + description: Silently sting a target and slow down their movement drastically. + components: + - type: Action + useDelay: 60 # TODO: Chemical cost + raiseOnAction: true + itemIconStyle: BigAction + icon: + sprite: Interface/Actions/changeling2.rsi + state: cryogenic_sting + - type: EntityEffectAction + effects: + - !type:AddReagentToBloodstream + reagent: Fresium + quantity: 10 # About 20 seconds of slowdown. + +- type: entity + parent: BaseActionChangelingSting + id: ActionChangelingStingLead + name: Lethal Sting + description: Silently sting a target and inject them with a lethal dose of a slow acting poison. + components: + - type: Action + useDelay: 180 # TODO: Chemical cost + raiseOnAction: true + itemIconStyle: BigAction + icon: + sprite: Interface/Actions/changeling2.rsi + state: lethal_sting + - type: EntityEffectAction + effects: + - !type:AddReagentToBloodstream + reagent: Lead + quantity: 5 # Exactly 100 poison damage over 3 minutes. + +- type: entity + parent: BaseActionChangelingSting + id: ActionChangelingStingHallucinogenic + name: Hallucinogenic Sting + description: Silently sting a target and inject them with chemicals that slow down their higher brain functions. + components: + - type: Action + useDelay: 90 # TODO: Chemical cost + raiseOnAction: true + itemIconStyle: BigAction + icon: + sprite: Interface/Actions/changeling2.rsi + state: hallucination_sting + - type: EntityEffectAction + effects: + - !type:AddReagentToBloodstream + reagent: SpaceDrugs + quantity: 10 # About 100s of being high + - !type:AddReagentToBloodstream + reagent: Cryptobiolin + quantity: 10 # About 20s of your speech being scrambled diff --git a/Resources/Prototypes/Catalog/changeling_catalog.yml b/Resources/Prototypes/Catalog/changeling_catalog.yml index f03ddeb7b1e..1477569e8b5 100644 --- a/Resources/Prototypes/Catalog/changeling_catalog.yml +++ b/Resources/Prototypes/Catalog/changeling_catalog.yml @@ -70,3 +70,63 @@ - !type:ListingLimitedStockCondition stock: 1 productAction: ActionChangelingStingDna + +- type: listing + id: ChangelingStingBlind + name: changeling-catalog-blind-sting-name + description: changeling-catalog-blind-sting-desc + icon: { sprite: /Textures/Interface/Actions/changeling2.rsi, state: blind_sting } + applyToMob: true + cost: + ChangelingDNA: 15 + categories: + - ChangelingStoreCombat + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + productAction: ActionChangelingStingBlind + +- type: listing + id: ChangelingStingCryogenic + name: changeling-catalog-cryogenic-sting-name + description: changeling-catalog-cryogenic-sting-desc + icon: { sprite: /Textures/Interface/Actions/changeling2.rsi, state: cryogenic_sting } + applyToMob: true + cost: + ChangelingDNA: 15 + categories: + - ChangelingStoreCombat + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + productAction: ActionChangelingStingCryogenic + +- type: listing + id: ChangelingStingLead + name: changeling-catalog-lethal-sting-name + description: changeling-catalog-lethal-sting-desc + icon: { sprite: /Textures/Interface/Actions/changeling2.rsi, state: lethal_sting } + applyToMob: true + cost: + ChangelingDNA: 10 + categories: + - ChangelingStoreUtility + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + productAction: ActionChangelingStingLead + +- type: listing + id: ChangelingStingHallucinogenic + name: changeling-catalog-hallucinogenic-sting-name + description: changeling-catalog-hallucinogenic-sting-desc + icon: { sprite: /Textures/Interface/Actions/changeling2.rsi, state: hallucination_sting } + applyToMob: true + cost: + ChangelingDNA: 10 + categories: + - ChangelingStoreUtility + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + productAction: ActionChangelingStingHallucinogenic diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml index 02dc4e1ed64..ad286c54cea 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/armblade.yml @@ -18,9 +18,9 @@ wideAnimationRotation: 90 attackRate: 1 damage: - types: # Same as energy sword, but slash/pierce instead of slash/heat - Slash: 15 - Piercing: 15 + types: + Slash: 11 + Piercing: 11 Structural: 20 - type: Item size: Normal diff --git a/Resources/Textures/Interface/Actions/changeling2.rsi/blind_sting.png b/Resources/Textures/Interface/Actions/changeling2.rsi/blind_sting.png new file mode 100644 index 0000000000000000000000000000000000000000..3fabd2e3aa74191f6c864b045f9f3904c6900b6f GIT binary patch literal 1395 zcmV-(1&sQMP)Px)ElET{R9JCsVzP1sO4Y>M}P-q{O)Z--a* zr(b39?eLP)e&dTf4;6qPI}e&WgY|s=(@jOS2OHB~(%|s`7LglgPMH~9V`s3QjIK#P z?@E7zOjWefAnPqoVlWBol zCLHzooFij_cs%saM(}TQ z^X0)-et0;wZa`smjdvaYQ!k^$ljHc^1J?i5o&t$ate8U8zL^Z%ADTkZy+HPl0>vCY{zXZ^&Jd4e_V_^U$z{G5cTXNTzB(?s9Qi~^u`)K}vQ z`e_E3jgZl`lI-nSh@kBUPL(AWYHX7v{MDfnf>Y0+IS^nd95zpnjw-4phX!h4ZtdB} zmb)EU2$}zn3Dp7A=0JcfIrRA)!P+WE_Nmn#32x6ML>tk$$wJ096Yc9+gU5rYIVy5r z=gK;llaag%{_0R!K)Dd}{pH4!FG#MT23$fZ_}R;>mFdW;d|~<4Mb>cn#T2EpAAy_Q>rOotu!k z2@Eh59awk1|BC`K!=SB7N94OT(z}cWq9O#MKb%_kJESUvQ-CeoxNbDUuDsz@4;$>seEk_JE4-q z;zSg{!z1S%M{uE?5+c)o&T{MQ1@pwU%SuTd@Vf^j2`Vfyd)6XT`&4<=K?y8;QmZ`< z66sf209+@oT~_p^1oa;sDk3q!$s<=wj^c9Qe)j;`l{LpBz=lW8OEtt;ZDC%(XO>I>wt4reg8;;qd-4(^+hfb!4idv7 zR9FL*7bAdjz3ZF_piSSSe>OtHcF&fIOZi<;894Q3l`HKXJWV9fT|R3Y>Pv-EoLyPl zQgHzb;P<-&jym9XZ#*)%mPYAq=`gn|f0KFrny!`>zP`@&M}2h(i1?5cCdPew zpp*Mg9yxsbACEbFziMqYcdZsahnC!KzMq;>{sre(n-x)DaU1{u002ovPDHLkV1i4h BpZov- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Actions/changeling2.rsi/cryogenic_sting.png b/Resources/Textures/Interface/Actions/changeling2.rsi/cryogenic_sting.png new file mode 100644 index 0000000000000000000000000000000000000000..3a34fd7b336d18fb9cfdc9360b253f52e6b8b1dd GIT binary patch literal 1336 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 LNkvXXu0mjfUgD#3 literal 0 HcmV?d00001 From 350a0e69d4a76c4ea99cf7f4154937233be5f32a Mon Sep 17 00:00:00 2001 From: portfiend <109661617+portfiend@users.noreply.github.com> Date: Thu, 4 Jun 2026 15:59:53 -0500 Subject: [PATCH 042/287] fix remaining butcherable components (#44225) * relocalize these two messages * fix erroneous butcherable components pt 1 * fix argocyte butchery * redundant --- .../Clothing/OuterClothing/wintercoats.yml | 6 +- .../Entities/Clothing/Shoes/boots.yml | 6 +- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 6 +- .../Entities/Mobs/NPCs/argocyte.yml | 60 ++++++------- .../Prototypes/Entities/Mobs/NPCs/carp.yml | 90 +++++++++---------- .../Entities/Mobs/NPCs/lavaland.yml | 6 +- .../Prototypes/Entities/Mobs/NPCs/mimic.yml | 6 +- .../Entities/Mobs/NPCs/miscellaneous.yml | 6 +- .../Prototypes/Entities/Mobs/NPCs/space.yml | 6 +- .../Objects/Consumable/Food/cottonburger.yml | 8 +- 10 files changed, 100 insertions(+), 100 deletions(-) diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/wintercoats.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/wintercoats.yml index f67d9eb82e5..bf193de55ca 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/wintercoats.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/wintercoats.yml @@ -834,7 +834,7 @@ sprite: Clothing/OuterClothing/WinterCoats/coatnomi.rsi - type: entity - parent: ClothingOuterWinterCoatToggleable + parent: [ClothingOuterWinterCoatToggleable, BaseSlicingRefinable] id: ClothingOuterWinterWeb name: web winter coat description: Feels like the inside of a cocoon, not that this would make you less afraid of being in one. @@ -857,8 +857,8 @@ - type: Construction graph: WebObjects node: coat - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: MaterialWebSilk1 amount: 5 - type: FlavorProfile diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml index 67596970ca9..e4de9cc3cf5 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml @@ -176,7 +176,7 @@ sprite: Clothing/Shoes/Boots/winterbootssyndicate.rsi - type: entity - parent: ClothingShoesBaseWinterBoots + parent: [ClothingShoesBaseWinterBoots, BaseSlicingRefinable] id: ClothingShoesBootsWinterWeb name: web winter boots description: Boots made out of dense webbing to help survive even the coldest of winters. @@ -195,8 +195,8 @@ reagents: - ReagentId: Fiber Quantity: 20 - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: MaterialWebSilk1 amount: 2 - type: Construction diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 25690d14a1c..748aa802c95 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1131,7 +1131,7 @@ - type: entity abstract: true - parent: BaseMobRuminant + parent: [BaseMobRuminant, BaseSlicingRefinable] id: MobSheepBase name: sheepBase description: A sheep. @@ -1214,8 +1214,8 @@ solution: wool requiresSpecialDigestion: true requireDead: false - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: FoodMeat amount: 4 - type: Grammar diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/argocyte.yml b/Resources/Prototypes/Entities/Mobs/NPCs/argocyte.yml index 7aa6cf1f857..6d4266d2524 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/argocyte.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/argocyte.yml @@ -51,7 +51,7 @@ group: GenericNumber - type: entity - parent: BaseMobArgocyte + parent: [BaseMobArgocyte, BaseSlicingRefinable] id: MobArgocyteSlurva name: slurva description: A pathetic creature, incapable of doing much. @@ -73,8 +73,8 @@ thresholds: 0: Alive 30: Dead - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: FoodMeatXeno amount: 1 - type: MovementSpeedModifier @@ -86,7 +86,7 @@ Blunt: 3 - type: entity - parent: BaseMobArgocyte + parent: [BaseMobArgocyte, BaseSlicingRefinable] id: MobArgocyteBarrier name: barrier components: @@ -107,8 +107,8 @@ thresholds: 0: Alive 30: Dead - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: FoodMeatXeno amount: 1 - type: Fixtures @@ -128,7 +128,7 @@ Blunt: 3 - type: entity - parent: BaseMobArgocyte + parent: [BaseMobArgocyte, BaseSlicingRefinable] id: MobArgocyteSkitter name: skitter description: A devious little alien... Make sure they don't run off with your rations! @@ -147,8 +147,8 @@ thresholds: 0: Alive 30: Dead - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: FoodMeatXeno amount: 1 - type: Fixtures @@ -168,7 +168,7 @@ Slash: 3 - type: entity - parent: BaseMobArgocyte + parent: [BaseMobArgocyte, BaseSlicingRefinable] id: MobArgocyteSwiper name: swiper description: Where did that stack of steel go? @@ -187,15 +187,15 @@ thresholds: 0: Alive 60: Dead - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: FoodMeatXeno amount: 2 - type: MovementSpeedModifier baseSprintSpeed : 5 - type: entity - parent: BaseMobArgocyte + parent: [BaseMobArgocyte, BaseSlicingRefinable] id: MobArgocyteMolder name: molder components: @@ -213,8 +213,8 @@ thresholds: 0: Alive 100: Dead - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: FoodMeatXeno amount: 2 - type: MovementSpeedModifier @@ -222,7 +222,7 @@ baseWalkSpeed : 3.5 - type: entity - parent: BaseMobArgocyte + parent: [BaseMobArgocyte, BaseSlicingRefinable] id: MobArgocytePouncer name: pouncer components: @@ -240,8 +240,8 @@ thresholds: 0: Alive 100: Dead - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: FoodMeatXeno amount: 2 - type: MeleeWeapon @@ -251,7 +251,7 @@ Slash: 7.5 - type: entity - parent: BaseMobArgocyte + parent: [BaseMobArgocyte, BaseSlicingRefinable] id: MobArgocyteGlider name: glider components: @@ -269,8 +269,8 @@ thresholds: 0: Alive 100: Dead - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: FoodMeatXeno amount: 2 - type: MeleeWeapon @@ -283,7 +283,7 @@ baseWalkSpeed: 4.5 - type: entity - parent: BaseMobArgocyte + parent: [BaseMobArgocyte, BaseSlicingRefinable] id: MobArgocyteHarvester name: harvester components: @@ -301,8 +301,8 @@ thresholds: 0: Alive 150: Dead - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: FoodMeatXeno amount: 2 - type: MeleeWeapon @@ -313,7 +313,7 @@ Structural: 5 - type: entity - parent: BaseMobArgocyte + parent: [BaseMobArgocyte, BaseSlicingRefinable] id: MobArgocyteCrawler name: crawler description: Deadly, pack-animals that maul unsuspecting travelers. @@ -332,8 +332,8 @@ thresholds: 0: Alive 150: Dead - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: FoodMeatXeno amount: 2 - type: MeleeWeapon @@ -347,7 +347,7 @@ baseWalkSpeed: 5 - type: entity - parent: BaseMobArgocyte + parent: [BaseMobArgocyte, BaseSlicingRefinable] id: MobArgocyteEnforcer name: enforcer components: @@ -365,8 +365,8 @@ thresholds: 0: Alive 300: Dead - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: FoodMeatXeno amount: 3 - type: MeleeWeapon diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml index 073215d9391..5458c7bd5bc 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml @@ -206,54 +206,54 @@ - type: entity name: sharkminnow - parent: [ BaseMobBloodsucker, BaseMobCarp ] + parent: [ BaseMobBloodsucker, BaseMobCarp, BaseSlicingRefinable ] id: MobShark description: A dangerous shark from the blackness of endless space, who loves to drink blood. components: - - type: Sprite - sprite: Mobs/Aliens/Carps/sharkminnow.rsi - layers: - - map: [ "enum.DamageStateVisualLayers.Base" ] - state: alive - - map: [ "enum.DamageStateVisualLayers.BaseUnshaded" ] - state: mouth - shader: unshaded - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeCircle - radius: 0.40 - density: 100 - mask: - - FlyingMobMask - layer: - - FlyingMobLayer - - type: MobThresholds - thresholds: - 0: Alive - 82: Dead # Might seem random, but this brings up the hits to kill with a crusher mark to 3 - - type: Stamina - baseCritThreshold: 150 - - type: DamageStateVisuals - states: - Alive: - Base: alive - BaseUnshaded: mouth - Dead: - Base: dead - BaseUnshaded: dead_mouth - - type: Butcherable - spawned: - - id: FoodMeatFish - amount: 4 - - id: MaterialToothSharkminnow1 - amount: 3 - - type: MeleeWeapon - damage: - types: - Slash: 10 - Bloodloss: 5 + - type: Sprite + sprite: Mobs/Aliens/Carps/sharkminnow.rsi + layers: + - map: [ "enum.DamageStateVisualLayers.Base" ] + state: alive + - map: [ "enum.DamageStateVisualLayers.BaseUnshaded" ] + state: mouth + shader: unshaded + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.40 + density: 100 + mask: + - FlyingMobMask + layer: + - FlyingMobLayer + - type: MobThresholds + thresholds: + 0: Alive + 82: Dead # Might seem random, but this brings up the hits to kill with a crusher mark to 3 + - type: Stamina + baseCritThreshold: 150 + - type: DamageStateVisuals + states: + Alive: + Base: alive + BaseUnshaded: mouth + Dead: + Base: dead + BaseUnshaded: dead_mouth + - type: ToolRefinable + refineResult: + - id: FoodMeatFish + amount: 4 + - id: MaterialToothSharkminnow1 + amount: 3 + - type: MeleeWeapon + damage: + types: + Slash: 10 + Bloodloss: 5 - type: entity id: MobSharkSalvage diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/lavaland.yml b/Resources/Prototypes/Entities/Mobs/NPCs/lavaland.yml index 1f3faf21554..65bc1c30fe6 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/lavaland.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/lavaland.yml @@ -1,7 +1,7 @@ - type: entity name: watcher id: MobWatcherBase - parent: [ SimpleSpaceMobBase, FlyingMobBase ] + parent: [ SimpleSpaceMobBase, FlyingMobBase, BaseSlicingRefinable ] abstract: true description: It's like it's staring right through you. components: @@ -43,8 +43,8 @@ thresholds: 0: Alive 50: Dead - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: DiamondOre1 amount: 1 - type: MovementSpeedModifier diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml b/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml index 829bbe4bf14..e8325e026c3 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml @@ -1,7 +1,7 @@ - type: entity name: mimic id: MobMimic - parent: [ SimpleMobBase, MobCombat ] + parent: [ SimpleMobBase, MobCombat, BaseSlicingRefinable ] description: Surprise. # When this gets a proper write this should use the object's actual description >:) components: - type: Tag @@ -40,7 +40,7 @@ - type: MovementSpeedModifier baseWalkSpeed : 1 baseSprintSpeed : 1 - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: DrinkChangelingStingCan amount: 1 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml b/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml index fa283398762..108464284d2 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml @@ -1,7 +1,7 @@ - type: entity name: laser raptor id: MobLaserRaptor - parent: SimpleMobBase + parent: [SimpleMobBase, BaseSlicingRefinable] description: From the Viking age. components: - type: NpcFactionMember @@ -37,8 +37,8 @@ thresholds: 0: Alive 100: Dead - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: FoodMeatChicken amount: 2 - type: MovementSpeedModifier diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/space.yml b/Resources/Prototypes/Entities/Mobs/NPCs/space.yml index 64a022aee85..a8edf3eaa38 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/space.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/space.yml @@ -124,7 +124,7 @@ - type: entity name: space kangaroo id: MobKangarooSpace - parent: [MobSpaceBasic, StripableInventoryBase] + parent: [MobSpaceBasic, StripableInventoryBase, BaseSlicingRefinable] description: It looks friendly. Why don't you give it a hug? components: - type: Sprite @@ -167,8 +167,8 @@ Unsexed: Kangaroo - type: ReplacementAccent accent: kangaroo - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: FoodMeat amount: 1 - type: MobThresholds diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/cottonburger.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/cottonburger.yml index 1a2162f250c..62bab884abb 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/cottonburger.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/cottonburger.yml @@ -1,7 +1,7 @@ # Cotton Bun - type: entity - parent: FoodBreadSliceBase + parent: [FoodBreadSliceBase, BaseSlicingRefinable] id: FoodCottonBun name: cotton bun description: A cotton hamburger bun. Soft, round and convenient to hold. @@ -19,9 +19,9 @@ Quantity: 5 - ReagentId: Fiber Quantity: 5 - - type: Butcherable - butcherDelay: 1 - spawned: + - type: ToolRefinable + refineTime: 1 + refineResult: - id: FoodCottonBunTop amount: 1 - id: FoodCottonBunBottom From 34007d64892dc68a077bd5ac58eee8d45cce8978 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 4 Jun 2026 21:20:01 +0000 Subject: [PATCH 043/287] Automatic changelog update --- Resources/Changelog/Changelog.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index aa4c5cad5ce..5af4341805d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: YoungThugSS14 - changes: - - message: Tourniquets can now fit in medical belts - type: Tweak - id: 9227 - time: '2025-11-26T17:29:56.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/41584 - author: CDWimmer changes: - message: The Syndicate now commits one less war crime; the Syndicate medical bundle @@ -4094,3 +4087,17 @@ id: 9738 time: '2026-06-04T17:50:48.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/42987 +- author: portfiend + changes: + - message: A couple of broken messages related to meatspiking have been fixed. + type: Fix + - message: Cotton buns, web winter coats, and web winter boots are now sliceable + again. + type: Fix + - message: A bunch of mobs that erroneously required a meat spike no longer need + to do so. The only mobs that should need meatspiking to butcher are humanoid + species, monkeys, kobolds, scurrets, and xenos. + type: Fix + id: 9739 + time: '2026-06-04T21:18:52.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/44225 From 1b7dbb3aba2214ce85fdb970c4ad12352c0f19d1 Mon Sep 17 00:00:00 2001 From: Andrew Malcolm O'Neill Date: Thu, 4 Jun 2026 23:20:36 +0100 Subject: [PATCH 044/287] chore(guidebook): Updating uplink information (#44240) * chore(guidebook): Updating uplink information - Updating reference to let players know that the code can be used on any PDA by any player if shared * Web commit --- Resources/ServerInfo/Guidebook/Antagonist/Traitors.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/ServerInfo/Guidebook/Antagonist/Traitors.xml b/Resources/ServerInfo/Guidebook/Antagonist/Traitors.xml index c7a2c9e983c..10927e941d2 100644 --- a/Resources/ServerInfo/Guidebook/Antagonist/Traitors.xml +++ b/Resources/ServerInfo/Guidebook/Antagonist/Traitors.xml @@ -15,7 +15,7 @@ ## Uplink & Activation The [color=cyan]uplink[/color] is your most important tool as a traitor. You can exchange the 20 [color=red]telecrystals[/color] (TC) you start with for items that will help you with your objectives. - By pressing [color=yellow][bold][keybind="OpenCharacterMenu"][/bold][/color], you'll see your personal uplink code. [bold]Setting your PDA's ringtone as this code will open the uplink.[/bold] + By pressing [color=yellow][bold][keybind="OpenCharacterMenu"][/bold][/color], you'll see your personal uplink code. To activate your uplink, set your PDA's ringtone to the code. This can be used with [bold]any PDA, and by anyone[/bold], so be wary of sharing it! Pressing [color=yellow][bold][keybind="OpenCharacterMenu"][/bold][/color] also lets you view your objectives and the codewords. If you do not have a PDA when you are activated, an [color=cyan]uplink implant[/color] is provided [bold]for the full [color=red]TC[/color] price of the implant.[/bold] From 1520aa03dcb31556ff795afd4292626a1378351c Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 4 Jun 2026 22:39:32 +0000 Subject: [PATCH 045/287] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 5af4341805d..f8bf281b41d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: CDWimmer - changes: - - message: The Syndicate now commits one less war crime; the Syndicate medical bundle - cross is now blue. - type: Fix - id: 9228 - time: '2025-11-26T21:13:37.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/41541 - author: Compilatron144 changes: - message: Plasma got a Christmas makeover. @@ -4101,3 +4093,10 @@ id: 9739 time: '2026-06-04T21:18:52.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/44225 +- author: maland1 + changes: + - message: Updated Traitor guidebook to properly describe uplinks. + type: Tweak + id: 9740 + time: '2026-06-04T22:38:24.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/44240 From 043c1d6b6082fc02b1f4e6383184e257ef84021b Mon Sep 17 00:00:00 2001 From: Whatstone <166147148+whatston3@users.noreply.github.com> Date: Fri, 5 Jun 2026 11:02:16 -0400 Subject: [PATCH 046/287] Remove instances of the broken Caution style class (#44244) --- Content.Client/Holopad/HolopadWindow.xaml.cs | 5 +++-- Content.Client/Lathe/UI/LatheMenu.xaml | 2 +- Content.Client/Lathe/UI/QueuedRecipeControl.xaml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Content.Client/Holopad/HolopadWindow.xaml.cs b/Content.Client/Holopad/HolopadWindow.xaml.cs index 6a19e46d039..c1c39171010 100644 --- a/Content.Client/Holopad/HolopadWindow.xaml.cs +++ b/Content.Client/Holopad/HolopadWindow.xaml.cs @@ -1,4 +1,5 @@ using Content.Client.Popups; +using Content.Client.Stylesheets; using Content.Client.UserInterface.Controls; using Content.Shared.Access.Systems; using Content.Shared.Holopad; @@ -61,8 +62,8 @@ public HolopadWindow() // XML formatting AnswerCallButton.AddStyleClass("ButtonAccept"); - EndCallButton.AddStyleClass("Caution"); - StartBroadcastButton.AddStyleClass("Caution"); + EndCallButton.AddStyleClass(StyleClass.Negative); + StartBroadcastButton.AddStyleClass(StyleClass.Negative); HolopadContactListPanel.PanelOverride = new StyleBoxFlat { diff --git a/Content.Client/Lathe/UI/LatheMenu.xaml b/Content.Client/Lathe/UI/LatheMenu.xaml index 84f1d7835a9..f8e58403e50 100644 --- a/Content.Client/Lathe/UI/LatheMenu.xaml +++ b/Content.Client/Lathe/UI/LatheMenu.xaml @@ -120,7 +120,7 @@ HorizontalAlignment="Right" ToolTip="{Loc 'lathe-menu-delete-fabricating-tooltip'}"> - Caution + negative OpenLeft diff --git a/Content.Client/Lathe/UI/QueuedRecipeControl.xaml b/Content.Client/Lathe/UI/QueuedRecipeControl.xaml index b1d4b496a14..09b1883e1b4 100644 --- a/Content.Client/Lathe/UI/QueuedRecipeControl.xaml +++ b/Content.Client/Lathe/UI/QueuedRecipeControl.xaml @@ -27,7 +27,7 @@ Text="✖" ToolTip="{Loc 'lathe-menu-delete-item-tooltip'}"> - Caution + negative OpenLeft From d7d72c1db8b5eab6952c02fd9d466a5e4cc56dc7 Mon Sep 17 00:00:00 2001 From: Zekins <136648667+Zekins3366@users.noreply.github.com> Date: Fri, 5 Jun 2026 18:06:30 +0300 Subject: [PATCH 047/287] Construction Cache Reload (#44243) ConstructionReload --- Content.Client/Construction/ConstructionSystem.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Content.Client/Construction/ConstructionSystem.cs b/Content.Client/Construction/ConstructionSystem.cs index 5e4020365cd..e38b13a7dd7 100644 --- a/Content.Client/Construction/ConstructionSystem.cs +++ b/Content.Client/Construction/ConstructionSystem.cs @@ -45,6 +45,7 @@ public override void Initialize() WarmupRecipesCache(); UpdatesOutsidePrediction = true; + SubscribeLocalEvent(OnPrototypeReload); SubscribeLocalEvent(HandlePlayerAttached); SubscribeNetworkEvent(HandleAckStructure); SubscribeNetworkEvent(OnConstructionGuideReceived); @@ -76,8 +77,16 @@ public bool TryGetRecipePrototype(string constructionProtoId, [NotNullWhen(true) return false; } + private void OnPrototypeReload(PrototypesReloadedEventArgs obj) + { + if (obj.WasModified()) + WarmupRecipesCache(); + } + private void WarmupRecipesCache() { + _recipesMetadataCache.Clear(); + foreach (var constructionProto in PrototypeManager.EnumeratePrototypes()) { if (!PrototypeManager.Resolve(constructionProto.Graph, out var graphProto)) From 480f0ca376ec2e7fe6678cc219a9fab6d79cc246 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 5 Jun 2026 15:25:46 +0000 Subject: [PATCH 048/287] Automatic changelog update --- Resources/Changelog/Admin.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index ddcf505413b..a8354f9a19f 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -1746,5 +1746,13 @@ Entries: id: 213 time: '2026-06-03T16:56:47.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/44223 +- author: Zekins3366 + changes: + - message: Construction menu now updates immediately after reloading construction + prototypes. + type: Fix + id: 214 + time: '2026-06-05T15:24:37.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/44243 Name: Admin Order: 3 From c4d92b33bc7d3b38a3b279b880a2a076bae81b61 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 11 Jun 2026 23:29:06 +0200 Subject: [PATCH 049/287] Make admin remarks load from DB faster (#42614) 4 sequential DB queries too slow? Just do 4 at once! --- Content.Server/Database/ServerDbBase.cs | 56 +++++++++++++++++-------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/Content.Server/Database/ServerDbBase.cs b/Content.Server/Database/ServerDbBase.cs index d6e571adf18..c0c8a128c3a 100644 --- a/Content.Server/Database/ServerDbBase.cs +++ b/Content.Server/Database/ServerDbBase.cs @@ -1233,24 +1233,37 @@ private AdminMessageRecord MakeAdminMessageRecord(AdminMessage entity) public async Task> GetAllAdminRemarks(Guid player) { - await using var db = await GetDb(); - List notes = new(); - notes.AddRange( - (await (from note in db.DbContext.AdminNotes - where note.PlayerUserId == player && - !note.Deleted && - (note.ExpirationTime == null || DateTime.UtcNow < note.ExpirationTime) - select note) - .Include(note => note.Round) - .ThenInclude(r => r!.Server) - .Include(note => note.CreatedBy) - .Include(note => note.LastEditedBy) - .Include(note => note.Player) - .ToListAsync()).Select(MakeAdminNoteRecord)); - notes.AddRange(await GetActiveWatchlistsImpl(db, player)); - notes.AddRange(await GetMessagesImpl(db, player)); - notes.AddRange(await GetBansAsNotesForUser(db, player)); - return notes; + return await ParallelCollect( + async () => + { + await using var db = await GetDb(); + return (await (from note in db.DbContext.AdminNotes + where note.PlayerUserId == player && + !note.Deleted && + (note.ExpirationTime == null || DateTime.UtcNow < note.ExpirationTime) + select note) + .Include(note => note.Round) + .ThenInclude(r => r!.Server) + .Include(note => note.CreatedBy) + .Include(note => note.LastEditedBy) + .Include(note => note.Player) + .ToListAsync()).Select(MakeAdminNoteRecord); + }, + async () => + { + await using var db = await GetDb(); + return await GetActiveWatchlistsImpl(db, player); + }, + async () => + { + await using var db = await GetDb(); + return await GetMessagesImpl(db, player); + }, + async () => + { + await using var db = await GetDb(); + return await GetBansAsNotesForUser(db, player); + }); } public async Task EditAdminNote(int id, string message, NoteSeverity severity, bool secret, Guid editedBy, DateTimeOffset editedAt, DateTimeOffset? expiryTime) { @@ -1676,5 +1689,12 @@ private static async Task> AsyncSelect( return [..results]; } + + private static async Task> ParallelCollect(params IEnumerable>>> tasks) + { + var taskInstances = tasks.Select(a => a()); + var results = await Task.WhenAll(taskInstances); + return results.SelectMany(x => x).ToList(); + } } } From 0cd25ac6536fce2839f741f784d2c6a6c7749f22 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Jun 2026 23:51:05 +0200 Subject: [PATCH 050/287] Update Credits (#44260) Co-authored-by: PJBot --- Resources/Credits/GitHub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index a567b6cd16b..584e0fc000e 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0-Anon, 0leshe, 0tito, 0x6273, 11BelowStudio, 12rabbits, 1337dakota, 13spacemen, 154942, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 27alaing, 2DSiggy, 3nderall, 4310v343k, 4dplanner, 5tickman, 612git, 778b, 96flo, aaron, abadaba695, Ablankmann, abregado, Absolute-Potato, Absotively, achookh, Acruid, actioninja, ActiveMammmoth, actually-reb, ada-please, adamsong, Adeinitas, adm2play, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aearo-Deepwater, Aerocrux, Aeshus, Aexolott, Aexxie, afflewaffle, africalimedrop, afrokada, AftrLite, AgentSmithRadio, Agoichi, ahandleman, Ahion, aiden, aidenkrz, Aidenkrz, Aisu9, ajcm, AJCM-git, AjexRose, Alekshhh, alexalexmax, alexkar598, AlexMorgan3817, alexum418, alexumandxgabriel08x, Alice4267, Alithsko, Alkheemist, alliephante, ALMv1, Alpaccalypse, AlphaQwerty, Altoids1, amatwiedle, amylizzle, Andre19926, Andrew-Fall, AndrewEyeke, AndrewFenriz, AndreyCamper, Androclast, anri, Anzarot121, ApolloVector, Appiah, april-gras, ar4ill, Arcane-Waffle, arcanevaliance, archee1, ArchPigeon, ArchRBX, areitpog, Arendian, AreYouConfused, arimah, Arkanic, ArkiveDev, armoks, Arteben, arthropodia, ArthurMousatov, ArtisticRoomba, artur, Artxmisery, ArZarLordOfMango, as334, AshBats, AsikKEsel, AsnDen, asperger-sind, aspiringLich, astriloqua, Atakku, Ataman, august-sun, AutoOtter, AverageNotDoingAnythingEnjoyer, avghdev, AwareFoxy, Awlod, Axionyxx, azloserbits, AzzyIsNotHere, azzyisnothere, B-Kirill, B3CKDOOR, baa14453, BackeTako, BadaBoomie, Bakke, BananaFlambe, Baptr0b0t, BarryNorfolk, BasedUser, baynarikattu, bea, BeatusCrow, bebr3ght, beck-thompson, beesterman, bellwetherlogic, ben, benbryant0, benev0, benjamin-burges, BGare, bhespiritu, bibbly, BigfootBravo, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blitzthesquishy, Blobadoodle, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, Bokser815, bolantej, BombasterDS, Booblesnoot42, Boolean-Buckeye, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, breeplayx3, BriBrooo, BRINGit34, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, buunie099, bvelliquette, BWTCK, byondfuckery, c0rigin, c4llv07e, CaasGit, Cabin-keeper, Caconym27, Calecute, Callmore, Camdot, cammusubi, capnsockless, CaptainMaru, captainsqrbeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, catdotjs, catlord, Catofquestionableethics, CatTheSystem, CawsForConcern, CDWimmer, Centronias, Chaboricks, chairbender, chaisftw, Chaoticaa, Charlese2, charlie, chartman, ChaseFlorom, chavonadelal, Cheackraze, CheddaCheez, cheesePizza2, CheesePlated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, ciaran, citrea, civilCornball, claustro305, Clement-O, cloudyias, clyf, Clyybber, CMDR-Piboy314, cnv41, coco, cohanna, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, Compilatron144, CookieMasterT, coolboy911, CoolioDudio, coolmankid12345, Coolsurf6, cooperwallace, corentt, CormosLemming, CrafterKolyan, CraftyRenter, crazybrain23, Crazydave91920, CrazyPhantom779, creadth, CrigCrag, CroilBird, Crotalus, CrudeWax, cryals, CrzyPotato, cubixthree, cutemoongod, Cyberboss, d34d10cc, DaCookieCakes, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, DamianX, dangerrevolution, daniel-cr, DanSAussieITS, Daracke, Darkie, DaturoDewitt, david, DawBla, Daxxi3, dch-GH, ddeegan, de0rix, Deahaka, dean, DEATHB4DEFEAT, Deatherd, deathride58, debugok, Decappi, Decortex, Deeeeja, deepdarkdepths, DeepwaterCreations, Deerstop, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, devinschubert14, dexlerxd, dffdff2423, DieselMohawk, DieselMohawkTheSequel, digitalic, Dimastra, dimmoon1, DinnerCalzone, DinoWattz, Disp-Dev, DisposableCrewmember42, dissidentbullet, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DR-DOCTOR-EVIL-EVIL, Dragonjspider, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, DuckManZach, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, dylanstrategie, dylanwhittingham, Dynexust, Easypoller, echo, EchoOfNothing, eclips_e, eden077, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, emberwinters, Emisse, emmafornash, EmoGarbage404, Endecc, EnrichedCaramel, Entvari, eoineoineoin, ephememory, eris, erohrs2, erorr404v1, Errant-4, ertanic, esguard, estacaoespacialpirata, eternally-confused, eugene, Eveloop, Ewanderer, ewokswagger, exincore, exp111, f0x-n3rd, F1restar4, FacePluslll, Fahasor, FairlySadPanda, farrellka-dev, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, fillervk, FinnishPaladin, firenamefn, Firewars763, FirinMaLazors, Fishfish458, fl-oz, Flareguy, flashgnash, FlipBrooke, FluffiestFloof, FluffMe, FluidRock, flymo5678, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, foxhorn, freeman2651, freeze2222, frobnic8, Froffy025, Fromoriss, froozigiusz, FrostMando, FrostRibbon, Fruitsalad, Funce, FungiFellow, FunkySphere, FunTust, Futuristic-OK, GalacticChimp, gamer3107, Gamewar360, gansulalan, GaussiArson, Gaxeer, gbasood, gcoremans, Geekyhobo, gem, genderGeometries, GeneralGaws, Genkail, Gentleman-Bird, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, GitHubUser53123, gituhabu, GlassEclipse, Glissadia, GnarpGnarp, GNF54, godisdeadLOL, goet, GoldenCan, Goldminermac, Golinth, golubgik, GoodWheatley, Gorox221, GR1231, gradientvera, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GrownSamoyedDog, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, happyrobot33, Hardly3D, harikattar, Hayden, he1acdvv, Hebi, Helix-ctrl, helm4142, Henry, HerCoyote23, Hi-Im-Shot, HighTechPuddle, Hitlinemoss, hiucko, hivehum, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, Hoshizora, Hreno, Hrosts, htmlsystem, Huaqas, hubismal, Hugal31, Hyenh, hyperb1, hyperDelegate, hyphenationc, i-justuser-i, iaada, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, imatsoup, IMCB, impubbi, imrenq, imweax, indeano, Injazz, Insineer, insoPL, IntegerTempest, Interrobang01, Intoxicating-Innocence, IProduceWidgets, itsmethom, Itzbenz, iztokbajcar, Jackal298, Jackrost, JackRyd3r, jacksonbailey, jacksonzck, JackspajfMain, Jacktastic09, Jackw2As, jacob, jamessimo, janekvap-havok, Jark255, Jarmer123, Jaskanbe, JasperJRoth, jbox144, JCGWE30, JerryImMouse, jerryimmouse, Jessetriesagain, jessicamaybe, JesterX666, Jewelots, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, jkwookee, jmcb, JohnGinnane, johnjjohn, JohnJJohn, johnku1, Jophire, Jopogrechkin, joshepvodka, JpegOfAFrog, jproads, JrInventor05, Jrpl, jukereise, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, JustinWinningham, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, kaiserbirch, KaiShibaa, kalane15, kalanosh, KamTheSythe, Kanashi-Panda, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, Kenionatus, kerisargit, keronshb, KeTuFaisPiKiNut, Khaomi, KIBORG04, KieueCaprie, Kimpes, kin98, KingFroozy, kipdotnet, kira-er, kiri-yoshikage, Kirillcas, Kirus59, Kistras, Kit, Kit0vras, KittenColony, Kittygyat, klaypexx, kleinerstation13, KloopRE, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, kontakt, korczoczek, kosticia, koteq, kotobdev, Kowlin, KrasnoshchekovPavel, kresny, Krosus777, Krunklehorn, Kryyto, Kupie, kxvvv, Kyoth25f, kyupolaris, kzhanik, LaCumbiaDelCoronavirus, lajolico, Lamrr, lanedon, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, Le-Arctic-Fox, leahcat, leander-0, leonardo-dabepis, leonidussaks, leonsfriedrich, LeoSantich, lettern, LetterN, Level10Cybermancer, LEVELcat, lever1209, LevitatingTree, Lgibb18, lgruthes, liem161, LightVillet, lilazero, liltenhead, linkbro1, LinkUyx, Litraxx, little-meow-meow, LittleBuilderJane, LittleNorthStar, LittleNyanCat, lizelive, ljm862, lmsnoise, localcc, lokachop, lolman360, Lomcastar, Lordbrandon12, LordCarve, LordEclipse, lucas, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luegamer, luizwritescode, LukaSlade, luminight, lunarcomets, Lusatia, Luxeator, lvvova1, Lyndomen, lyroth001, lyxcaster, lzimann, lzk228, M1tht1c, M3739, M4rchy-S, M87S, mac6na6na, MACMAN2003, Macoron, magicalus, magmodius, magnnusson, magnuscrowe, maland1, malchanceux, MaloTV, ManelNavola, manelnavola, Mangohydra, marboww, mariano, Markek1, MarkerWicker, marlyn, mastermiller01, matt, Matz05, max, MaxNox7, maylokana, mdrkrg, MDuch369, meara1179, meganerobot, MehimoNemo, Mehnix, MeltedPixel, memeproof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, mifia, mikeysaurus, MilenVolf, MilonPL, Minemoder5000, Minty642, minus1over12, Mirino97, mirrorcult, MishaUnity, MissKay1994, MisterImp, MisterMecky, Mith-randalf, Mixelz, mjarduk, MjrLandWhale, mkanke-real, MLGTASTICa, mnva0, moderatelyaware, modern-nm, mohamedwidar, mokiros, momo, Moneyl, monotheonist, Moomoobeef, moony, Morb0, MossyGreySlope, mqole, mr-bo-jangles, Mr0maks, MrFippik, MrPersival, mrrobdemo, mtrs163, muburu, MureixloI, murolem, murphyneko, musicmanvr, MWKane, Myakot, Myctai, N3X15, nabegator, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, naxel11, NazrinNya, neborsh, nekokiwa, neomoth, neutrino-laser, NickPowers43, nikitosych, nikthechampiongr, Nimfar11, ninruB, Nirnael, NIXC, nkokic, NkoKirkto, nmajask, noctyrnal, noelkathegod, noirogen, nok-ko, NonchalantNoob, NoobyLegion, Nopey, NoreUhh, Not-A-Chair, not-gavnaed, NotActuallyMarty, notafet, notquitehadouken, notsodana, noudoit, noverd, Nox38, NuclearWinter, Nuggets219, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, Nyxilath, och-och, OctoRocket, Ohelig, OldDanceJacket, OliverOtter, onesch, OneZerooo0, OnsenCapy, OnyxTheBrave, opl-, Orange-Winds, OrangeMoronage9622, OrbitSystem07, Orsoniks, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, PAFFhassoocks, paige404, paigemaeforrest, pali6, Palladinium, Pangogie, panzer-iv1, partyaddict, patrikturi, PaulRitter, pavlockblaine03, peccneck, Peptide90, peptron1, perryprog, PeterFuto, PetMudstone, pewter-wiz, pgraycs, PGrayCS, Pgriha, phantom-lily, Pharaz4, pheenty, philingham, Phill101, Phonix, Phooooooooooooooooooooooooooooooosphate, phunnyguy, PicklOH, PilgrimViis, Pill-U, pinkbat5, Piras314, Pireax, piskaczek, Pissachu, pissdemon, Pixel8-dev, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, Pok27, poklj, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, PotRoastPiggy, Princess-Cheeseballs, ProfanedBane, Prole0, ProPandaBear, ProPeperos, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykana, psykzz, PuceTint, pumkin69, PuroSlavKing, PursuitInAshes, Putnam3145, py01, Pyrovi, qrtDaniil, qrwas, Quantum-cross, quasr-9, quatre, QueerNB, QuietlyWhisper, qwerltaz, Radezolid, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, RainyGale, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, RedBookcase, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, Redrover1760, redspyy, ReeZer2, Rem, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, rewafflution, rhailrake, rhsvenson, riccardi48, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, rlebell33, RobbyTheFish, robinthedragon, robinthegirlthing, Rockdtben, Rohesie, rok-povsic, rokudara-sen, rolfero, RomanNovo, roryflowers, rosieposieeee, Roudenn, router, ruddygreat, Ruichka, rumaks-xyz, RumiTiger, Ruzihm, rwrv, S1rFl0, S1ss3l, Saakra, SabreML, Sadie-silly, saga3152, saintmuntzer, salarua, Salex08, sam, samgithubaccount, Samuka-C, SaphireLattice, SapphicOverload, sarahon, sativaleanne, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, ScholarNZL, schrodinger71, scrato, Scribbles0, scrivoy, scruq445, scuffedjays, ScumbagDog, SeamLesss, Segonist, semensponge, sephtasm, ser1-1y, Serkket, sewerpig, SG6732, sh18rw, Shaddap1, ShadeAware, ShadowCommander, shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SharkSnake98, Shegare, shepardtothestars, shibechef, Sidzaru, Siginanto, signalsender, SignalWalker, siigiil, silicon14wastaken, Silverfur-underscore, Simyon264, sirdragooon, Sirionaut, SirWarock, Sk1tch, SkaldetSkaeg, Skarletto, skeeka-dev, skrybl, Skybailey-dev, skye, Skyedra, SlamBamActionman, slarticodefast, Slava0135, sleepyyapril, slimmslamm, Slyfox333, Smugman, SnappingOpossum, snebl, snicket, sniperchance, Snowni, snowsignal, SolidSyn, SolidusSnek, solstar2, SomegnihT, SonarZeBat, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, soupkilove, southbridge-fur, sowelipililimute, Soydium, SpaceLizard24, SpaceLizardSky, SpaceManiac, SpaceRox1244, SpaceyLady, Spangs04, spanky-spanky, Sparlight, spartak, SpartanKadence, spderman3333, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, sporkyz, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, starbuckss14, Stealthbomber16, steel, Steffo99, stellar-novas, stewie523, stomf, Stop-Signs, stopbreaking, stopka-html, StrawberryMoses, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, SuperGDPWYL, superjj18, Supernorn, SurrealShibe, SweetAplle, SweptWasTaken, SyaoranFox, Sybil, SYNCHRONIC, Synthestra, Szunti, t, Tainakov, takemysoult, taonewt, tap, TaralGit, Taran, taserthefox, taurie, Tayrtahn, tday93, teamaki, TeenSarlacc, TekuNut, telavivgamers, telyonok, temm1ie, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, Tezzaide, TGODiamond, TGRCdev, tgrkzus, thanosdegraf, ThatGuyUSA, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, TheBlueYowie, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, theexetron, TheFlyingSentry, thefoty, TheGrimbeeper, TheIntoxicatedCat, thekilk, TheMas3212, themias, theomund, TheProNoob678, TherapyGoth, ThereDrD0, TheSecondLord, TheShuEd, thetolbean, thevinter, TheWaffleJesus, Thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, TiniestShark, Titian3, tk-a369, tkdrg, tmtmtl30, ToastEnjoyer, Toby222, TokenStyle, Tollhouse, Toly65, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, tornado-technology, TornadoTechnology, tosatur, TotallyLemon, ToxicSonicFan04, TPgameing, Tr1bute, travis-g-reid, treytipton, TriviaSolari, trixxedbit, TrixxedHeart, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, tuchila-adi-bogdan, Tunguso4ka, TurboTrackerss14, TVK-04, tyashley, Tyler-IN, TytosB, Tyzemol, UbaserB, Uberration, ubis1, UBlueberry, uhbg, UKNOWH, UltimateJester, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, Unkn0wnGh0st333, unusualcrow, UpAndLeaves, Uriende, UristMcDorf, user424242420, Utmanarn, Vaaankas, valentfingerov, valquaint, VanderslootAssgiraffe, Varen, Vasilis, VasilisThePikachu, veliebm, Velken, VelonacepsCalyxEggs, Venomii, veprolet, VerinSenpai, veritable-calamity, Veritius, Vermidia, vero5123, verslebas, vexerot, viceemargo, VigersRay, violet754, Visne, vitopigno, vitusveit, vlad, vlados1408, VMSolidus, vmzd, VoidMeticulous, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, Vortebo, vulppine, wachte1, wafehling, walksanatora, Warentan, WarMechanic, Watermelon914, weaversam8, wertanchik, whateverusername0, whatston3, widgetbeck, Will-Oliver-Br, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, Wolfkey-SomeoneElseTookMyUsername, Worldwaker, wrexbe, wtcwr68, xeri7, xkreksx, xprospero, xRiriq, xsainteer, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, YoungThugSS14, Yousifb26, youtissoum, yunii, YuriyKiss, yuriykiss, zach-hill, Zadeon, Zalycon, zamp, Zandario, Zap527, Zealith-Gamer, zekins3366, ZelteHonor, zero, ZeroDiamond, ZeWaka, zHonys, zionnBE, ZNixian, Zokkie, ZoldorfTheWizard, zonespace27, Zylofan, Zymem, zzylex +0-Anon, 0leshe, 0tito, 0x6273, 11BelowStudio, 12rabbits, 1337dakota, 13spacemen, 154942, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 27alaing, 2DSiggy, 3nderall, 4310v343k, 4dplanner, 5tickman, 612git, 778b, 96flo, aaron, abadaba695, Ablankmann, abregado, Absolute-Potato, Absotively, achookh, Acruid, actioninja, ActiveMammmoth, actually-reb, ada-please, adamsong, Adeinitas, adm2play, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aearo-Deepwater, Aerocrux, Aeshus, Aexolott, Aexxie, afflewaffle, africalimedrop, afrokada, AftrLite, AgentSmithRadio, Agoichi, ahandleman, Ahion, aiden, Aidenkrz, aidenkrz, Aisu9, ajcm, AJCM-git, AjexRose, Alekshhh, alexalexmax, alexkar598, AlexMorgan3817, alexum418, alexumandxgabriel08x, Alice4267, Alithsko, Alkheemist, alliephante, ALMv1, Alpaccalypse, AlphaQwerty, Altoids1, amatwiedle, amylizzle, Andre19926, Andrew-Fall, AndrewEyeke, AndrewFenriz, AndreyCamper, Androclast, anri, Anzarot121, ApolloVector, Appiah, april-gras, ar4ill, Arcane-Waffle, arcanevaliance, archee1, ArchPigeon, ArchRBX, areitpog, Arendian, AreYouConfused, arimah, Arkanic, ArkiveDev, armoks, Arteben, arthropodia, ArthurMousatov, ArtisticRoomba, artur, Artxmisery, ArZarLordOfMango, as334, AshBats, AsikKEsel, AsnDen, asperger-sind, aspiringLich, astriloqua, Atakku, Ataman, august-sun, AutoOtter, AverageNotDoingAnythingEnjoyer, avghdev, AwareFoxy, Awlod, Axionyxx, azloserbits, AzzyIsNotHere, azzyisnothere, B-Kirill, B3CKDOOR, baa14453, BackeTako, BadaBoomie, Bakke, BananaFlambe, Baptr0b0t, BarryNorfolk, BasedUser, baynarikattu, bea, BeatusCrow, bebr3ght, beck-thompson, beesterman, bellwetherlogic, ben, benbryant0, benev0, benjamin-burges, BGare, bhespiritu, bibbly, BigfootBravo, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blitzthesquishy, Blobadoodle, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, Bokser815, bolantej, BombasterDS, Booblesnoot42, Boolean-Buckeye, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, breeplayx3, BriBrooo, BRINGit34, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, buunie099, bvelliquette, BWTCK, byondfuckery, c0rigin, c4llv07e, CaasGit, Cabin-keeper, Caconym27, Calecute, Callmore, Camdot, cammusubi, capnsockless, CaptainMaru, captainsqrbeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, catdotjs, catlord, Catofquestionableethics, CatTheSystem, CawsForConcern, CDWimmer, Centronias, Chaboricks, chairbender, chaisftw, Chaoticaa, Charlese2, charlie, chartman, ChaseFlorom, chavonadelal, Cheackraze, CheddaCheez, cheesePizza2, CheesePlated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, ciaran, citrea, civilCornball, claustro305, Clement-O, cloudyias, clyf, Clyybber, CMDR-Piboy314, cnv41, coco, cohanna, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, Compilatron144, CookieMasterT, coolboy911, CoolioDudio, coolmankid12345, Coolsurf6, cooperwallace, corentt, CormosLemming, CrafterKolyan, CraftyRenter, crazybrain23, Crazydave91920, CrazyPhantom779, creadth, CrigCrag, CroilBird, Crotalus, CrudeWax, cryals, CrzyPotato, cubixthree, cutemoongod, Cyberboss, d34d10cc, DaCookieCakes, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, DamianX, dangerrevolution, daniel-cr, DanSAussieITS, Daracke, Darkie, DaturoDewitt, david, DawBla, Daxxi3, dch-GH, ddeegan, de0rix, Deahaka, dean, DEATHB4DEFEAT, Deatherd, deathride58, debugok, Decappi, Decortex, Deeeeja, deepdarkdepths, DeepwaterCreations, Deerstop, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, devinschubert14, dexlerxd, dffdff2423, DieselMohawk, DieselMohawkTheSequel, digitalic, Dimastra, dimmoon1, DinnerCalzone, DinoWattz, Disp-Dev, DisposableCrewmember42, dissidentbullet, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DR-DOCTOR-EVIL-EVIL, Dragonjspider, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, DuckManZach, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, dylanstrategie, dylanwhittingham, Dynexust, Easypoller, echo, EchoOfNothing, eclips_e, eden077, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, emberwinters, Emisse, emmafornash, EmoGarbage404, Endecc, EnrichedCaramel, Entvari, eoineoineoin, ephememory, eris, erohrs2, erorr404v1, Errant-4, ertanic, esguard, estacaoespacialpirata, eternally-confused, eugene, Eveloop, Ewanderer, ewokswagger, exincore, exp111, f0x-n3rd, F1restar4, FacePluslll, Fahasor, FairlySadPanda, farrellka-dev, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, fillervk, FinnishPaladin, firenamefn, Firewars763, FirinMaLazors, Fishfish458, fl-oz, Flareguy, flashgnash, FlipBrooke, FluffiestFloof, FluffMe, FluidRock, flymo5678, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, foxhorn, freeman2651, freeze2222, frobnic8, Froffy025, Fromoriss, froozigiusz, FrostMando, FrostRibbon, Fruitsalad, Funce, FungiFellow, FunkySphere, FunTust, Futuristic-OK, GalacticChimp, gamer3107, Gamewar360, gansulalan, GaussiArson, Gaxeer, gbasood, gcoremans, Geekyhobo, gem, genderGeometries, GeneralGaws, Genkail, Gentleman-Bird, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, GitHubUser53123, gituhabu, GlassEclipse, Glissadia, GnarpGnarp, GNF54, godisdeadLOL, goet, GoldenCan, Goldminermac, Golinth, golubgik, GoodWheatley, Gorox221, GR1231, gradientvera, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GrownSamoyedDog, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, happyrobot33, Hardly3D, harikattar, Hayden, he1acdvv, Hebi, Helix-ctrl, helm4142, Henry, HerCoyote23, Hi-Im-Shot, HighTechPuddle, Hitlinemoss, hiucko, hivehum, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, Hoshizora, Hreno, Hrosts, htmlsystem, Huaqas, hubismal, Hugal31, Hyenh, hyperb1, hyperDelegate, hyphenationc, i-justuser-i, iaada, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, imatsoup, IMCB, impubbi, imrenq, imweax, indeano, Injazz, Insineer, insoPL, IntegerTempest, Interrobang01, Intoxicating-Innocence, IProduceWidgets, itsmethom, Itzbenz, iztokbajcar, Jackal298, Jackrost, JackRyd3r, jacksonbailey, jacksonzck, JackspajfMain, Jacktastic09, Jackw2As, jacob, jamessimo, janekvap-havok, Jark255, Jarmer123, Jaskanbe, JasperJRoth, jbox144, JCGWE30, jerryimmouse, JerryImMouse, Jessetriesagain, jessicamaybe, JesterX666, Jewelots, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, jkwookee, jmcb, JohnGinnane, JohnJJohn, johnjjohn, johnku1, Jophire, Jopogrechkin, joshepvodka, JpegOfAFrog, jproads, JrInventor05, Jrpl, jukereise, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, JustinWinningham, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, kaiserbirch, KaiShibaa, kalane15, kalanosh, KamTheSythe, Kanashi-Panda, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, Kenionatus, kerisargit, keronshb, ketufaispikinut, Khaomi, KIBORG04, KieueCaprie, Kimpes, kin98, KingFroozy, kipdotnet, kira-er, kiri-yoshikage, Kirillcas, Kirus59, Kistras, Kit, Kit0vras, KittenColony, Kittygyat, klaypexx, kleinerstation13, KloopRE, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, kontakt, korczoczek, kosticia, koteq, kotobdev, Kowlin, KrasnoshchekovPavel, kresny, Krosus777, Krunklehorn, Kryyto, Kupie, kxvvv, Kyoth25f, kyupolaris, kzhanik, LaCumbiaDelCoronavirus, lajolico, Lamrr, lanedon, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, Le-Arctic-Fox, leahcat, leander-0, leonardo-dabepis, leonidussaks, leonsfriedrich, LeoSantich, lettern, LetterN, Level10Cybermancer, LEVELcat, lever1209, LevitatingTree, Lgibb18, lgruthes, liem161, LightVillet, lilazero, liltenhead, linkbro1, LinkUyx, Litraxx, little-meow-meow, LittleBuilderJane, LittleNorthStar, LittleNyanCat, lizelive, ljm862, lmsnoise, localcc, lokachop, lolman360, Lomcastar, Lordbrandon12, LordCarve, LordEclipse, lucas, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luegamer, luizwritescode, LukaSlade, luminight, lunarcomets, Lusatia, Luxeator, lvvova1, Lyndomen, lyroth001, lyxcaster, lzimann, lzk228, M1tht1c, M3739, M4rchy-S, M87S, mac6na6na, MACMAN2003, Macoron, magicalus, magmodius, magnnusson, magnuscrowe, maland1, malchanceux, MaloTV, ManelNavola, manelnavola, Mangohydra, marboww, mariano, Markek1, MarkerWicker, marlyn, mastermiller01, matrix, matt, Matz05, max, MaxNox7, maylokana, mdrkrg, MDuch369, meara1179, meganerobot, MehimoNemo, Mehnix, MeltedPixel, memeproof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, mifia, mikeysaurus, MilenVolf, MilonPL, Minemoder5000, Minty642, minus1over12, Mirino97, mirrorcult, MishaUnity, MissKay1994, MisterImp, MisterMecky, Mith-randalf, Mixelz, mjarduk, MjrLandWhale, mkanke-real, MLGTASTICa, mnva0, moderatelyaware, modern-nm, mohamedwidar, mokiros, momo, Moneyl, monotheonist, Moomoobeef, moony, mora, Morb0, MossyGreySlope, mqole, mr-bo-jangles, Mr0maks, MrFippik, MrPersival, mrrobdemo, mtrs163, muburu, MureixloI, murolem, murphyneko, musicmanvr, MWKane, Myakot, Myctai, N3X15, nabegator, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, naxel11, NazrinNya, neborsh, nekokiwa, neomoth, neutrino-laser, NickPowers43, nikitosych, nikthechampiongr, Nimfar11, ninruB, Nirnael, NIXC, nkokic, NkoKirkto, nmajask, noctyrnal, noelkathegod, noirogen, nok-ko, NonchalantNoob, NoobyLegion, Nopey, NoreUhh, Not-A-Chair, not-gavnaed, NotActuallyMarty, notafet, notquitehadouken, notsodana, noudoit, noverd, Nox38, NuclearWinter, Nuggets219, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, Nyxilath, och-och, OctoRocket, Ohelig, OldDanceJacket, OliverOtter, onesch, OneZerooo0, OnsenCapy, OnyxTheBrave, opl-, Orange-Winds, OrangeMoronage9622, OrbitSystem07, Orsoniks, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, PAFFhassoocks, paige404, paigemaeforrest, pali6, Palladinium, Pangogie, panzer-iv1, partyaddict, patrikturi, PaulRitter, pavlockblaine03, peccneck, Peptide90, peptron1, perryprog, PeterFuto, PetMudstone, pewter-wiz, PGrayCS, pgraycs, Pgriha, phantom-lily, Pharaz4, pheenty, philingham, Phill101, Phonix, Phooooooooooooooooooooooooooooooosphate, phunnyguy, PicklOH, PilgrimViis, Pill-U, pinkbat5, Piras314, Pireax, piskaczek, Pissachu, pissdemon, Pixel8-dev, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, Pok27, poklj, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, PotRoastPiggy, Princess-Cheeseballs, ProfanedBane, Prole0, ProPandaBear, ProPeperos, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykana, psykzz, PuceTint, pumkin69, PuroSlavKing, PursuitInAshes, Putnam3145, py01, Pyrovi, qrtDaniil, qrwas, Quantum-cross, quasr-9, quatre, QueerNB, QuietlyWhisper, qwerltaz, Radezolid, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, RainyGale, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, RedBookcase, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, Redrover1760, redspyy, ReeZer2, Rem, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, rewafflution, rhailrake, rhsvenson, riccardi48, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, rlebell33, RobbyTheFish, robert, robinthedragon, robinthegirlthing, Rockdtben, Rohesie, rok-povsic, rokudara-sen, rolfero, RomanNovo, roryflowers, rosieposieeee, Roudenn, router, ruddygreat, Ruichka, rumaks-xyz, RumiTiger, Ruzihm, rwrv, S1rFl0, S1ss3l, Saakra, SabreML, Sadie-silly, saga3152, saintmuntzer, salarua, Salex08, sam, samgithubaccount, Samuka-C, SaphireLattice, SapphicOverload, sarahon, sativaleanne, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, ScholarNZL, schrodinger71, scrato, Scribbles0, scrivoy, scruq445, scuffedjays, ScumbagDog, SeamLesss, Segonist, semensponge, sephtasm, ser1-1y, Serkket, sewerpig, SG6732, sh18rw, Shaddap1, ShadeAware, ShadowCommander, shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SharkSnake98, Shegare, shepardtothestars, shibechef, Sidzaru, Siginanto, signalsender, SignalWalker, siigiil, silicon14wastaken, Silverfur-underscore, Simyon264, sirdragooon, Sirionaut, SirWarock, Sk1tch, SkaldetSkaeg, Skarletto, skeeka-dev, skrybl, Skybailey-dev, skye, Skyedra, SlamBamActionman, slarticodefast, Slava0135, sleepyyapril, slimmslamm, Slyfox333, Smugman, SnappingOpossum, snebl, snicket, sniperchance, Snowni, snowsignal, SolidSyn, SolidusSnek, solstar2, SomegnihT, SonarZeBat, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, soupkilove, southbridge-fur, sowelipililimute, Soydium, SpaceLizard24, SpaceLizardSky, SpaceManiac, SpaceRox1244, SpaceyLady, Spangs04, spanky-spanky, Sparlight, spartak, SpartanKadence, spderman3333, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, sporkyz, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, starbuckss14, Stealthbomber16, steel, Steffo99, stellar-novas, stewie523, stomf, Stop-Signs, stopbreaking, stopka-html, StrawberryMoses, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, SuperGDPWYL, superjj18, Supernorn, SurrealShibe, SweetAplle, SweptWasTaken, SyaoranFox, Sybil, SYNCHRONIC, Synthestra, Szunti, t, Tainakov, takemysoult, taonewt, tap, TaralGit, Taran, taserthefox, taurie, Tayrtahn, tday93, teamaki, TeenSarlacc, TekuNut, telavivgamers, telyonok, temm1ie, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, Tezzaide, TGODiamond, TGRCdev, tgrkzus, thanosdegraf, ThatGuyUSA, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, TheBlueYowie, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, theexetron, TheFlyingSentry, thefoty, TheGrimbeeper, TheIntoxicatedCat, thekilk, TheMas3212, themias, theomund, TheProNoob678, TherapyGoth, ThereDrD0, TheSecondLord, TheShuEd, thetolbean, thevinter, TheWaffleJesus, Thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, TiniestShark, Titian3, tk-a369, tkdrg, tmtmtl30, ToastEnjoyer, Toby222, TokenStyle, Tollhouse, Toly65, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, tornado-technology, TornadoTechnology, tosatur, TotallyLemon, ToxicSonicFan04, TPgameing, Tr1bute, travis-g-reid, treytipton, TriviaSolari, trixxedbit, TrixxedHeart, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, tuchila-adi-bogdan, Tunguso4ka, TurboTrackerss14, TVK-04, tyashley, Tyler-IN, TytosB, Tyzemol, UbaserB, Uberration, ubis1, UBlueberry, uhbg, UKNOWH, UltimateJester, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, Unkn0wnGh0st333, unusualcrow, UpAndLeaves, Uriende, UristMcDorf, user424242420, Utmanarn, Vaaankas, valentfingerov, valquaint, VanderslootAssgiraffe, Varen, Vasilis, VasilisThePikachu, veliebm, Velken, VelonacepsCalyxEggs, Venomii, veprolet, VerinSenpai, veritable-calamity, Veritius, Vermidia, vero5123, verslebas, vexerot, viceemargo, VigersRay, violet754, Visne, vitopigno, vitusveit, vlad, vlados1408, VMSolidus, vmzd, VoidMeticulous, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, Vortebo, vulppine, wachte1, wafehling, walksanatora, Warentan, WarMechanic, Watermelon914, weaversam8, wertanchik, whateverusername0, whatston3, widgetbeck, Will-Oliver-Br, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, Wolfkey-SomeoneElseTookMyUsername, Worldwaker, wrexbe, wtcwr68, xeri7, xkreksx, xprospero, xRiriq, xsainteer, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, YoungThugSS14, Yousifb26, youtissoum, yunii, YuriyKiss, yuriykiss, zach-hill, Zadeon, Zalycon, zamp, Zandario, Zap527, Zealith-Gamer, zekins3366, ZelteHonor, zero, ZeroDiamond, ZeWaka, zHonys, zionnBE, ZNixian, Zokkie, ZoldorfTheWizard, zonespace27, Zylofan, Zymem, zzylex From 9da1cff8552d349bcd4b9b6f47ca90becb8e1431 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 11 Jun 2026 23:46:18 +0200 Subject: [PATCH 051/287] Log custom votes to database. (#42650) * Log custom votes to database. * Add navigation property from round entity --- .../20260126003838_CustomVoteLog.Designer.cs | 2235 +++++++++++++++++ .../Postgres/20260126003838_CustomVoteLog.cs | 85 + .../PostgresServerDbContextModelSnapshot.cs | 106 + .../20260126003831_CustomVoteLog.Designer.cs | 2152 ++++++++++++++++ .../Sqlite/20260126003831_CustomVoteLog.cs | 84 + .../SqliteServerDbContextModelSnapshot.cs | 104 + .../Model.CustomVoteLog.cs | 129 + Content.Server.Database/Model.cs | 5 + Content.Server/Database/ServerDbBase.cs | 62 + Content.Server/Database/ServerDbManager.cs | 62 + Content.Server/GameTicking/GameTicker.cs | 5 + Content.Server/Voting/VoteCommands.cs | 18 +- 12 files changed, 5044 insertions(+), 3 deletions(-) create mode 100644 Content.Server.Database/Migrations/Postgres/20260126003838_CustomVoteLog.Designer.cs create mode 100644 Content.Server.Database/Migrations/Postgres/20260126003838_CustomVoteLog.cs create mode 100644 Content.Server.Database/Migrations/Sqlite/20260126003831_CustomVoteLog.Designer.cs create mode 100644 Content.Server.Database/Migrations/Sqlite/20260126003831_CustomVoteLog.cs create mode 100644 Content.Server.Database/Model.CustomVoteLog.cs diff --git a/Content.Server.Database/Migrations/Postgres/20260126003838_CustomVoteLog.Designer.cs b/Content.Server.Database/Migrations/Postgres/20260126003838_CustomVoteLog.Designer.cs new file mode 100644 index 00000000000..9bcfcafad05 --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20260126003838_CustomVoteLog.Designer.cs @@ -0,0 +1,2235 @@ +// +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("20260126003838_CustomVoteLog")] + partial class CustomVoteLog + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.0") + .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("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.Ban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("ban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + 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("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.Property("Type") + .HasColumnType("smallint") + .HasColumnName("type"); + + b.HasKey("Id") + .HasName("PK_ban"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.ToTable("ban", null, t => + { + t.HasCheckConstraint("NoExemptOnRoleBan", "type = 0 OR exempt_flags = 0"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.BanAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("ban_address_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.HasKey("Id") + .HasName("PK_ban_address"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_ban_address_ban_id"); + + b.ToTable("ban_address", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.BanHwid", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("ban_hwid_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.HasKey("Id") + .HasName("PK_ban_hwid"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_ban_hwid_ban_id"); + + b.ToTable("ban_hwid", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanPlayer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("ban_player_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_ban_player"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_ban_player_ban_id"); + + b.HasIndex("UserId", "BanId") + .IsUnique(); + + b.ToTable("ban_player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("ban_role_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("role_id"); + + b.Property("RoleType") + .IsRequired() + .HasColumnType("text") + .HasColumnName("role_type"); + + b.HasKey("Id") + .HasName("PK_ban_role"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_ban_role_ban_id"); + + b.HasIndex("RoleType", "RoleId", "BanId") + .IsUnique(); + + b.ToTable("ban_role", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanRound", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("ban_round_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.HasKey("Id") + .HasName("PK_ban_round"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_ban_round_ban_id"); + + b.HasIndex("RoundId", "BanId") + .IsUnique(); + + b.ToTable("ban_round", (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.CustomVoteLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("custom_vote_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("InitiatorId") + .HasColumnType("uuid") + .HasColumnName("initiator_id"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("State") + .HasColumnType("smallint") + .HasColumnName("state"); + + b.Property("TimeCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("time_created"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("PK_custom_vote_log"); + + b.HasIndex("InitiatorId"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_custom_vote_log_round_id"); + + b.ToTable("custom_vote_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.CustomVoteLogOption", b => + { + b.Property("VoteId") + .HasColumnType("integer") + .HasColumnName("vote_id"); + + b.Property("OptionIdx") + .HasColumnType("smallint") + .HasColumnName("option_idx"); + + b.Property("Text") + .IsRequired() + .HasColumnType("text") + .HasColumnName("text"); + + b.Property("VoteCount") + .HasColumnType("integer") + .HasColumnName("vote_count"); + + b.HasKey("VoteId", "OptionIdx") + .HasName("PK_custom_vote_log_option"); + + b.ToTable("custom_vote_log_option", (string)null); + }); + + 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("OrganMarkings") + .HasColumnType("jsonb") + .HasColumnName("organ_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.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.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.Unban", 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_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("unban", (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.Ban", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_ban_player_last_edited_by_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("LastEditedBy"); + }); + + modelBuilder.Entity("Content.Server.Database.BanAddress", b => + { + b.HasOne("Content.Server.Database.Ban", "Ban") + .WithMany("Addresses") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_ban_address_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.BanHwid", b => + { + b.HasOne("Content.Server.Database.Ban", "Ban") + .WithMany("Hwids") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_ban_hwid_ban_ban_id"); + + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("BanHwidId") + .HasColumnType("integer") + .HasColumnName("ban_hwid_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b1.Property("Type") + .HasColumnType("integer") + .HasColumnName("hwid_type"); + + b1.HasKey("BanHwidId"); + + b1.ToTable("ban_hwid"); + + b1.WithOwner() + .HasForeignKey("BanHwidId") + .HasConstraintName("FK_ban_hwid_ban_hwid_ban_hwid_id"); + }); + + b.Navigation("Ban"); + + b.Navigation("HWId") + .IsRequired(); + }); + + modelBuilder.Entity("Content.Server.Database.BanPlayer", b => + { + b.HasOne("Content.Server.Database.Ban", "Ban") + .WithMany("Players") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_ban_player_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.BanRole", b => + { + b.HasOne("Content.Server.Database.Ban", "Ban") + .WithMany("Roles") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_ban_role_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.BanRound", b => + { + b.HasOne("Content.Server.Database.Ban", "Ban") + .WithMany("Rounds") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_ban_round_ban_ban_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_ban_round_round_round_id"); + + b.Navigation("Ban"); + + b.Navigation("Round"); + }); + + 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.CustomVoteLog", b => + { + b.HasOne("Content.Server.Database.Player", "Initiator") + .WithMany() + .HasForeignKey("InitiatorId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_custom_vote_log_player_initiator_id1"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("CustomVoteLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_custom_vote_log_round_round_id"); + + b.Navigation("Initiator"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.CustomVoteLogOption", b => + { + b.HasOne("Content.Server.Database.CustomVoteLog", "Vote") + .WithMany("Options") + .HasForeignKey("VoteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_custom_vote_log_option_custom_vote_log_vote_id"); + + b.Navigation("Vote"); + }); + + 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.ServerBanHit", b => + { + b.HasOne("Content.Server.Database.Ban", "Ban") + .WithMany("BanHits") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_ban_ban_id"); + + b.HasOne("Content.Server.Database.ConnectionLog", "Connection") + .WithMany("BanHits") + .HasForeignKey("ConnectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_connection_log_connection_id"); + + b.Navigation("Ban"); + + b.Navigation("Connection"); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Traits") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_trait_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Unban", b => + { + b.HasOne("Content.Server.Database.Ban", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.Unban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_unban_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.HasOne("Content.Server.Database.Player", null) + .WithMany() + .HasForeignKey("PlayersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_player_players_id"); + + b.HasOne("Content.Server.Database.Round", null) + .WithMany() + .HasForeignKey("RoundsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_round_rounds_id"); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Navigation("Players"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Navigation("Admins"); + + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.Ban", b => + { + b.Navigation("Addresses"); + + b.Navigation("BanHits"); + + b.Navigation("Hwids"); + + b.Navigation("Players"); + + b.Navigation("Roles"); + + b.Navigation("Rounds"); + + b.Navigation("Unban"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Navigation("BanHits"); + }); + + modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b => + { + b.Navigation("Options"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("AdminMessagesCreated"); + + b.Navigation("AdminMessagesDeleted"); + + b.Navigation("AdminMessagesLastEdited"); + + b.Navigation("AdminMessagesReceived"); + + b.Navigation("AdminNotesCreated"); + + b.Navigation("AdminNotesDeleted"); + + b.Navigation("AdminNotesLastEdited"); + + b.Navigation("AdminNotesReceived"); + + b.Navigation("AdminServerBansCreated"); + + b.Navigation("AdminServerBansLastEdited"); + + b.Navigation("AdminWatchlistsCreated"); + + b.Navigation("AdminWatchlistsDeleted"); + + b.Navigation("AdminWatchlistsLastEdited"); + + b.Navigation("AdminWatchlistsReceived"); + + b.Navigation("JobWhitelists"); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Navigation("Antags"); + + b.Navigation("Jobs"); + + b.Navigation("Loadouts"); + + b.Navigation("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.Navigation("Loadouts"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.Navigation("Groups"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("CustomVoteLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Navigation("ConnectionLogs"); + + b.Navigation("Rounds"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/20260126003838_CustomVoteLog.cs b/Content.Server.Database/Migrations/Postgres/20260126003838_CustomVoteLog.cs new file mode 100644 index 00000000000..0545c55a05a --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20260126003838_CustomVoteLog.cs @@ -0,0 +1,85 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Content.Server.Database.Migrations.Postgres +{ + /// + public partial class CustomVoteLog : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "custom_vote_log", + columns: table => new + { + custom_vote_log_id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + round_id = table.Column(type: "integer", nullable: false), + time_created = table.Column(type: "timestamp with time zone", nullable: false), + title = table.Column(type: "text", nullable: false), + initiator_id = table.Column(type: "uuid", nullable: true), + state = table.Column(type: "smallint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_custom_vote_log", x => x.custom_vote_log_id); + table.ForeignKey( + name: "FK_custom_vote_log_player_initiator_id1", + column: x => x.initiator_id, + principalTable: "player", + principalColumn: "user_id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_custom_vote_log_round_round_id", + column: x => x.round_id, + principalTable: "round", + principalColumn: "round_id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "custom_vote_log_option", + columns: table => new + { + vote_id = table.Column(type: "integer", nullable: false), + option_idx = table.Column(type: "smallint", nullable: false), + text = table.Column(type: "text", nullable: false), + vote_count = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_custom_vote_log_option", x => new { x.vote_id, x.option_idx }); + table.ForeignKey( + name: "FK_custom_vote_log_option_custom_vote_log_vote_id", + column: x => x.vote_id, + principalTable: "custom_vote_log", + principalColumn: "custom_vote_log_id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_custom_vote_log_initiator_id", + table: "custom_vote_log", + column: "initiator_id"); + + migrationBuilder.CreateIndex( + name: "IX_custom_vote_log_round_id", + table: "custom_vote_log", + column: "round_id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "custom_vote_log_option"); + + migrationBuilder.DropTable( + name: "custom_vote_log"); + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs index 1c85aac946e..593d719ebd3 100644 --- a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs +++ b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs @@ -849,6 +849,72 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); }); + modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("custom_vote_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("InitiatorId") + .HasColumnType("uuid") + .HasColumnName("initiator_id"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("State") + .HasColumnType("smallint") + .HasColumnName("state"); + + b.Property("TimeCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("time_created"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("PK_custom_vote_log"); + + b.HasIndex("InitiatorId"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_custom_vote_log_round_id"); + + b.ToTable("custom_vote_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.CustomVoteLogOption", b => + { + b.Property("VoteId") + .HasColumnType("integer") + .HasColumnName("vote_id"); + + b.Property("OptionIdx") + .HasColumnType("smallint") + .HasColumnName("option_idx"); + + b.Property("Text") + .IsRequired() + .HasColumnType("text") + .HasColumnName("text"); + + b.Property("VoteCount") + .HasColumnType("integer") + .HasColumnName("vote_count"); + + b.HasKey("VoteId", "OptionIdx") + .HasName("PK_custom_vote_log_option"); + + b.ToTable("custom_vote_log_option", (string)null); + }); + modelBuilder.Entity("Content.Server.Database.IPIntelCache", b => { b.Property("Id") @@ -1831,6 +1897,39 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Server"); }); + modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b => + { + b.HasOne("Content.Server.Database.Player", "Initiator") + .WithMany() + .HasForeignKey("InitiatorId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_custom_vote_log_player_initiator_id1"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("CustomVoteLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_custom_vote_log_round_round_id"); + + b.Navigation("Initiator"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.CustomVoteLogOption", b => + { + b.HasOne("Content.Server.Database.CustomVoteLog", "Vote") + .WithMany("Options") + .HasForeignKey("VoteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_custom_vote_log_option_custom_vote_log_vote_id"); + + b.Navigation("Vote"); + }); + modelBuilder.Entity("Content.Server.Database.Job", b => { b.HasOne("Content.Server.Database.Profile", "Profile") @@ -2048,6 +2147,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("BanHits"); }); + modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b => + { + b.Navigation("Options"); + }); + modelBuilder.Entity("Content.Server.Database.Player", b => { b.Navigation("AdminLogs"); @@ -2112,6 +2216,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Content.Server.Database.Round", b => { b.Navigation("AdminLogs"); + + b.Navigation("CustomVoteLogs"); }); modelBuilder.Entity("Content.Server.Database.Server", b => diff --git a/Content.Server.Database/Migrations/Sqlite/20260126003831_CustomVoteLog.Designer.cs b/Content.Server.Database/Migrations/Sqlite/20260126003831_CustomVoteLog.Designer.cs new file mode 100644 index 00000000000..d5ea0388947 --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20260126003831_CustomVoteLog.Designer.cs @@ -0,0 +1,2152 @@ +// +using System; +using Content.Server.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Content.Server.Database.Migrations.Sqlite +{ + [DbContext(typeof(SqliteServerDbContext))] + [Migration("20260126003831_CustomVoteLog")] + partial class CustomVoteLog + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.0"); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("AdminRankId") + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Deadminned") + .HasColumnType("INTEGER") + .HasColumnName("deadminned"); + + b.Property("Suspended") + .HasColumnType("INTEGER") + .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"); + + b.Property("AdminId") + .HasColumnType("TEXT") + .HasColumnName("admin_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flag"); + + b.Property("Negative") + .HasColumnType("INTEGER") + .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("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("Impact") + .HasColumnType("INTEGER") + .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("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("TEXT") + .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"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("Dismissed") + .HasColumnType("INTEGER") + .HasColumnName("dismissed"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Seen") + .HasColumnType("INTEGER") + .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"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Secret") + .HasColumnType("INTEGER") + .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"); + + 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"); + + 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"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .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"); + + 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"); + + b.Property("UserId") + .HasColumnType("TEXT") + .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.Ban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("AutoDelete") + .HasColumnType("INTEGER") + .HasColumnName("auto_delete"); + + b.Property("BanTime") + .HasColumnType("TEXT") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("banning_admin"); + + b.Property("ExemptFlags") + .HasColumnType("INTEGER") + .HasColumnName("exempt_flags"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.Property("Type") + .HasColumnType("INTEGER") + .HasColumnName("type"); + + b.HasKey("Id") + .HasName("PK_ban"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.ToTable("ban", null, t => + { + t.HasCheckConstraint("NoExemptOnRoleBan", "type = 0 OR exempt_flags = 0"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.BanAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ban_address_id"); + + b.Property("Address") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.HasKey("Id") + .HasName("PK_ban_address"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_ban_address_ban_id"); + + b.ToTable("ban_address", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanHwid", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ban_hwid_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.HasKey("Id") + .HasName("PK_ban_hwid"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_ban_hwid_ban_id"); + + b.ToTable("ban_hwid", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanPlayer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ban_player_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_ban_player"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_ban_player_ban_id"); + + b.HasIndex("UserId", "BanId") + .IsUnique(); + + b.ToTable("ban_player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ban_role_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("role_id"); + + b.Property("RoleType") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("role_type"); + + b.HasKey("Id") + .HasName("PK_ban_role"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_ban_role_ban_id"); + + b.HasIndex("RoleType", "RoleId", "BanId") + .IsUnique(); + + b.ToTable("ban_role", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanRound", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ban_round_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.HasKey("Id") + .HasName("PK_ban_round"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_ban_round_ban_id"); + + b.HasIndex("RoundId", "BanId") + .IsUnique(); + + b.ToTable("ban_round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ban_template_id"); + + b.Property("AutoDelete") + .HasColumnType("INTEGER") + .HasColumnName("auto_delete"); + + b.Property("ExemptFlags") + .HasColumnType("INTEGER") + .HasColumnName("exempt_flags"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("Length") + .HasColumnType("TEXT") + .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("TEXT") + .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"); + + b.Property("Address") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("Denied") + .HasColumnType("INTEGER") + .HasColumnName("denied"); + + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("server_id"); + + b.Property("Time") + .HasColumnType("TEXT") + .HasColumnName("time"); + + b.Property("Trust") + .HasColumnType("REAL") + .HasColumnName("trust"); + + b.Property("UserId") + .HasColumnType("TEXT") + .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", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("custom_vote_log_id"); + + b.Property("InitiatorId") + .HasColumnType("TEXT") + .HasColumnName("initiator_id"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("State") + .HasColumnType("INTEGER") + .HasColumnName("state"); + + b.Property("TimeCreated") + .HasColumnType("TEXT") + .HasColumnName("time_created"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("PK_custom_vote_log"); + + b.HasIndex("InitiatorId"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_custom_vote_log_round_id"); + + b.ToTable("custom_vote_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.CustomVoteLogOption", b => + { + b.Property("VoteId") + .HasColumnType("INTEGER") + .HasColumnName("vote_id"); + + b.Property("OptionIdx") + .HasColumnType("INTEGER") + .HasColumnName("option_idx"); + + b.Property("Text") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("text"); + + b.Property("VoteCount") + .HasColumnType("INTEGER") + .HasColumnName("vote_count"); + + b.HasKey("VoteId", "OptionIdx") + .HasName("PK_custom_vote_log_option"); + + b.ToTable("custom_vote_log_option", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.IPIntelCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ipintel_cache_id"); + + b.Property("Address") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("Score") + .HasColumnType("REAL") + .HasColumnName("score"); + + b.Property("Time") + .HasColumnType("TEXT") + .HasColumnName("time"); + + b.HasKey("Id") + .HasName("PK_ipintel_cache"); + + b.HasIndex("Address") + .IsUnique(); + + b.ToTable("ipintel_cache", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("job_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"); + + b.Property("PlayerId") + .HasColumnType("TEXT") + .HasColumnName("player_id"); + + b.Property("TimeSpent") + .HasColumnType("TEXT") + .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"); + + b.Property("FirstSeenTime") + .HasColumnType("TEXT") + .HasColumnName("first_seen_time"); + + b.Property("LastReadRules") + .HasColumnType("TEXT") + .HasColumnName("last_read_rules"); + + b.Property("LastSeenAddress") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_seen_address"); + + b.Property("LastSeenTime") + .HasColumnType("TEXT") + .HasColumnName("last_seen_time"); + + b.Property("LastSeenUserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_seen_user_name"); + + b.Property("UserId") + .HasColumnType("TEXT") + .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", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("preference_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("TEXT") + .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"); + + 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("OrganMarkings") + .HasColumnType("jsonb") + .HasColumnName("organ_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"); + + 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"); + + 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"); + + b.Property("EntityName") + .HasMaxLength(256) + .HasColumnType("TEXT") + .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("TEXT") + .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"); + + b.Property("ServerId") + .HasColumnType("INTEGER") + .HasColumnName("server_id"); + + b.Property("StartDate") + .HasColumnType("TEXT") + .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"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_server"); + + b.ToTable("server", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .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"); + + 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.Trait", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("trait_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.Unban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("unban_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("TEXT") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("uploaded_resource_log_id"); + + b.Property("Data") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("data"); + + b.Property("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("path"); + + b.Property("UserId") + .HasColumnType("TEXT") + .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("TEXT") + .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.Ban", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_ban_player_last_edited_by_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("LastEditedBy"); + }); + + modelBuilder.Entity("Content.Server.Database.BanAddress", b => + { + b.HasOne("Content.Server.Database.Ban", "Ban") + .WithMany("Addresses") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_ban_address_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.BanHwid", b => + { + b.HasOne("Content.Server.Database.Ban", "Ban") + .WithMany("Hwids") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_ban_hwid_ban_ban_id"); + + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("BanHwidId") + .HasColumnType("INTEGER") + .HasColumnName("ban_hwid_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b1.Property("Type") + .HasColumnType("INTEGER") + .HasColumnName("hwid_type"); + + b1.HasKey("BanHwidId"); + + b1.ToTable("ban_hwid"); + + b1.WithOwner() + .HasForeignKey("BanHwidId") + .HasConstraintName("FK_ban_hwid_ban_hwid_ban_hwid_id"); + }); + + b.Navigation("Ban"); + + b.Navigation("HWId") + .IsRequired(); + }); + + modelBuilder.Entity("Content.Server.Database.BanPlayer", b => + { + b.HasOne("Content.Server.Database.Ban", "Ban") + .WithMany("Players") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_ban_player_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.BanRole", b => + { + b.HasOne("Content.Server.Database.Ban", "Ban") + .WithMany("Roles") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_ban_role_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.BanRound", b => + { + b.HasOne("Content.Server.Database.Ban", "Ban") + .WithMany("Rounds") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_ban_round_ban_ban_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_ban_round_round_round_id"); + + b.Navigation("Ban"); + + b.Navigation("Round"); + }); + + 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("BLOB") + .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.CustomVoteLog", b => + { + b.HasOne("Content.Server.Database.Player", "Initiator") + .WithMany() + .HasForeignKey("InitiatorId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_custom_vote_log_player_initiator_id1"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("CustomVoteLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_custom_vote_log_round_round_id"); + + b.Navigation("Initiator"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.CustomVoteLogOption", b => + { + b.HasOne("Content.Server.Database.CustomVoteLog", "Vote") + .WithMany("Options") + .HasForeignKey("VoteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_custom_vote_log_option_custom_vote_log_vote_id"); + + b.Navigation("Vote"); + }); + + 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("BLOB") + .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_id"); + + 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_loadout_id"); + + 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.ServerBanHit", b => + { + b.HasOne("Content.Server.Database.Ban", "Ban") + .WithMany("BanHits") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_ban_ban_id"); + + b.HasOne("Content.Server.Database.ConnectionLog", "Connection") + .WithMany("BanHits") + .HasForeignKey("ConnectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_connection_log_connection_id"); + + b.Navigation("Ban"); + + b.Navigation("Connection"); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Traits") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_trait_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Unban", b => + { + b.HasOne("Content.Server.Database.Ban", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.Unban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_unban_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.HasOne("Content.Server.Database.Player", null) + .WithMany() + .HasForeignKey("PlayersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_player_players_id"); + + b.HasOne("Content.Server.Database.Round", null) + .WithMany() + .HasForeignKey("RoundsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_round_rounds_id"); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Navigation("Players"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Navigation("Admins"); + + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.Ban", b => + { + b.Navigation("Addresses"); + + b.Navigation("BanHits"); + + b.Navigation("Hwids"); + + b.Navigation("Players"); + + b.Navigation("Roles"); + + b.Navigation("Rounds"); + + b.Navigation("Unban"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Navigation("BanHits"); + }); + + modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b => + { + b.Navigation("Options"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("AdminMessagesCreated"); + + b.Navigation("AdminMessagesDeleted"); + + b.Navigation("AdminMessagesLastEdited"); + + b.Navigation("AdminMessagesReceived"); + + b.Navigation("AdminNotesCreated"); + + b.Navigation("AdminNotesDeleted"); + + b.Navigation("AdminNotesLastEdited"); + + b.Navigation("AdminNotesReceived"); + + b.Navigation("AdminServerBansCreated"); + + b.Navigation("AdminServerBansLastEdited"); + + b.Navigation("AdminWatchlistsCreated"); + + b.Navigation("AdminWatchlistsDeleted"); + + b.Navigation("AdminWatchlistsLastEdited"); + + b.Navigation("AdminWatchlistsReceived"); + + b.Navigation("JobWhitelists"); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Navigation("Antags"); + + b.Navigation("Jobs"); + + b.Navigation("Loadouts"); + + b.Navigation("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.Navigation("Loadouts"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.Navigation("Groups"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("CustomVoteLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Navigation("ConnectionLogs"); + + b.Navigation("Rounds"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/20260126003831_CustomVoteLog.cs b/Content.Server.Database/Migrations/Sqlite/20260126003831_CustomVoteLog.cs new file mode 100644 index 00000000000..2d34b3017f5 --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20260126003831_CustomVoteLog.cs @@ -0,0 +1,84 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Content.Server.Database.Migrations.Sqlite +{ + /// + public partial class CustomVoteLog : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "custom_vote_log", + columns: table => new + { + custom_vote_log_id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + round_id = table.Column(type: "INTEGER", nullable: false), + time_created = table.Column(type: "TEXT", nullable: false), + title = table.Column(type: "TEXT", nullable: false), + initiator_id = table.Column(type: "TEXT", nullable: true), + state = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_custom_vote_log", x => x.custom_vote_log_id); + table.ForeignKey( + name: "FK_custom_vote_log_player_initiator_id1", + column: x => x.initiator_id, + principalTable: "player", + principalColumn: "user_id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_custom_vote_log_round_round_id", + column: x => x.round_id, + principalTable: "round", + principalColumn: "round_id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "custom_vote_log_option", + columns: table => new + { + vote_id = table.Column(type: "INTEGER", nullable: false), + option_idx = table.Column(type: "INTEGER", nullable: false), + text = table.Column(type: "TEXT", nullable: false), + vote_count = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_custom_vote_log_option", x => new { x.vote_id, x.option_idx }); + table.ForeignKey( + name: "FK_custom_vote_log_option_custom_vote_log_vote_id", + column: x => x.vote_id, + principalTable: "custom_vote_log", + principalColumn: "custom_vote_log_id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_custom_vote_log_initiator_id", + table: "custom_vote_log", + column: "initiator_id"); + + migrationBuilder.CreateIndex( + name: "IX_custom_vote_log_round_id", + table: "custom_vote_log", + column: "round_id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "custom_vote_log_option"); + + migrationBuilder.DropTable( + name: "custom_vote_log"); + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs index b7ae8c5d1fb..fad65ac2cc1 100644 --- a/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs +++ b/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs @@ -798,6 +798,70 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("connection_log", (string)null); }); + modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("custom_vote_log_id"); + + b.Property("InitiatorId") + .HasColumnType("TEXT") + .HasColumnName("initiator_id"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("State") + .HasColumnType("INTEGER") + .HasColumnName("state"); + + b.Property("TimeCreated") + .HasColumnType("TEXT") + .HasColumnName("time_created"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("PK_custom_vote_log"); + + b.HasIndex("InitiatorId"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_custom_vote_log_round_id"); + + b.ToTable("custom_vote_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.CustomVoteLogOption", b => + { + b.Property("VoteId") + .HasColumnType("INTEGER") + .HasColumnName("vote_id"); + + b.Property("OptionIdx") + .HasColumnType("INTEGER") + .HasColumnName("option_idx"); + + b.Property("Text") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("text"); + + b.Property("VoteCount") + .HasColumnType("INTEGER") + .HasColumnName("vote_count"); + + b.HasKey("VoteId", "OptionIdx") + .HasName("PK_custom_vote_log_option"); + + b.ToTable("custom_vote_log_option", (string)null); + }); + modelBuilder.Entity("Content.Server.Database.IPIntelCache", b => { b.Property("Id") @@ -1750,6 +1814,39 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Server"); }); + modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b => + { + b.HasOne("Content.Server.Database.Player", "Initiator") + .WithMany() + .HasForeignKey("InitiatorId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_custom_vote_log_player_initiator_id1"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("CustomVoteLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_custom_vote_log_round_round_id"); + + b.Navigation("Initiator"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.CustomVoteLogOption", b => + { + b.HasOne("Content.Server.Database.CustomVoteLog", "Vote") + .WithMany("Options") + .HasForeignKey("VoteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_custom_vote_log_option_custom_vote_log_vote_id"); + + b.Navigation("Vote"); + }); + modelBuilder.Entity("Content.Server.Database.Job", b => { b.HasOne("Content.Server.Database.Profile", "Profile") @@ -1967,6 +2064,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("BanHits"); }); + modelBuilder.Entity("Content.Server.Database.CustomVoteLog", b => + { + b.Navigation("Options"); + }); + modelBuilder.Entity("Content.Server.Database.Player", b => { b.Navigation("AdminLogs"); @@ -2031,6 +2133,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Content.Server.Database.Round", b => { b.Navigation("AdminLogs"); + + b.Navigation("CustomVoteLogs"); }); modelBuilder.Entity("Content.Server.Database.Server", b => diff --git a/Content.Server.Database/Model.CustomVoteLog.cs b/Content.Server.Database/Model.CustomVoteLog.cs new file mode 100644 index 00000000000..69fe14b5900 --- /dev/null +++ b/Content.Server.Database/Model.CustomVoteLog.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; + +namespace Content.Server.Database; + +// +// Contains model definitions primarily related to custom vote logging. +// + +internal static class ModelCustomVoteLog +{ + public static void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasOne(cvl => cvl.Initiator) + .WithMany() + .HasForeignKey(cvl => cvl.InitiatorId) + .HasPrincipalKey(p => p.UserId) + .OnDelete(DeleteBehavior.SetNull) + .IsRequired(false); + + modelBuilder.Entity() + .HasKey(cvl => new { cvl.VoteId, cvl.OptionIdx }); + } +} + +/// +/// A single admin-initiated custom vote logged in the database. +/// +public sealed class CustomVoteLog +{ + public int Id { get; set; } + + /// + /// The round ID the vote was made in. + /// + public int RoundId { get; set; } + + /// + /// The time the vote was created at. + /// + public DateTime TimeCreated { get; set; } + + /// + /// The player-facing title for the vote. + /// + public required string Title { get; set; } + + /// + /// The user ID of the admin that created the vote. + /// + public Guid? InitiatorId { get; set; } + + /// + /// State of the vote. + /// + public CustomVoteState State { get; set; } + + /// + /// The playing-facing options the vote has. + /// + public List? Options { get; set; } + + /// + /// The round the vote was made in. + /// + public Round? Round { get; set; } + + /// + /// The admin that created the vote. + /// + public Player? Initiator { get; set; } +} + +/// +/// An option for a logged . +/// +public sealed class CustomVoteLogOption +{ + /// + /// The vote ID this option is part of. + /// + public int VoteId { get; set; } + + /// + /// The index of the option in the option list. + /// + public short OptionIdx { get; set; } + + /// + /// The player-facing text for this option. + /// + public required string Text { get; set; } + + /// + /// How many players voted for this option. + /// + /// + /// This field is only populated for votes with state . + /// + public int VoteCount { get; set; } + + /// + /// The vote this option is part of. + /// + public CustomVoteLog? Vote { get; set; } +} + +/// +/// Possible states for entries. +/// +public enum CustomVoteState : byte +{ + /// + /// The vote is still active (likely was just made). + /// + Active, + + /// + /// The vote finished and vote counts were written to database. + /// + Finished, + + /// + /// The vote was canceled, no vote counts have been written to database. + /// + Cancelled, +} diff --git a/Content.Server.Database/Model.cs b/Content.Server.Database/Model.cs index f54bba7e44c..d65db3cc388 100644 --- a/Content.Server.Database/Model.cs +++ b/Content.Server.Database/Model.cs @@ -49,6 +49,8 @@ protected ServerDbContext(DbContextOptions options) : base(options) public DbSet RoleWhitelists { get; set; } = null!; public DbSet BanTemplate { get; set; } = null!; public DbSet IPIntelCache { get; set; } = null!; + public DbSet CustomVoteLog { get; set; } = null!; + public DbSet CustomVoteLogOption { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -296,6 +298,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasDefaultValue(HwidType.Legacy); ModelBan.OnModelCreating(modelBuilder); + ModelCustomVoteLog.OnModelCreating(modelBuilder); } public virtual IQueryable SearchLogs(IQueryable query, string searchText) @@ -595,6 +598,8 @@ public class Round public List AdminLogs { get; set; } = default!; + public List CustomVoteLogs { get; set; } = default!; + [ForeignKey("Server")] public int ServerId { get; set; } public Server Server { get; set; } = default!; } diff --git a/Content.Server/Database/ServerDbBase.cs b/Content.Server/Database/ServerDbBase.cs index c0c8a128c3a..d759b2454a7 100644 --- a/Content.Server/Database/ServerDbBase.cs +++ b/Content.Server/Database/ServerDbBase.cs @@ -1632,6 +1632,68 @@ await db.DbContext.IPIntelCache #endregion + #region Custom vote logging + + public async Task CustomVoteLogAdd( + string title, + int roundId, + Guid? initiator, + ImmutableArray options) + { + await using var db = await GetDb(); + + var log = new CustomVoteLog + { + Title = title, + RoundId = roundId, + InitiatorId = initiator, + State = CustomVoteState.Active, + TimeCreated = DateTime.UtcNow, + Options = options.Select((o, i) => new CustomVoteLogOption + { + Text = o, + OptionIdx = (short)i, + VoteCount = 0, + }) + .ToList(), + }; + + db.DbContext.CustomVoteLog.Add(log); + await db.DbContext.SaveChangesAsync(); + + return log.Id; + } + + public async Task CustomVoteLogFinish(int voteId, ImmutableArray voteCounts) + { + await using var db = await GetDb(); + + var log = await db.DbContext.CustomVoteLog + .Include(cvl => cvl.Options) + .SingleAsync(v => v.Id == voteId); + + log.State = CustomVoteState.Finished; + + for (var i = 0; i < log.Options!.Count; i++) + { + log.Options[i].VoteCount = voteCounts[i]; + } + + await db.DbContext.SaveChangesAsync(); + } + + public async Task CustomVoteLogCancel(int voteId) + { + await using var db = await GetDb(); + + var log = await db.DbContext.CustomVoteLog.SingleAsync(v => v.Id == voteId); + log.State = CustomVoteState.Cancelled; + + await db.DbContext.SaveChangesAsync(); + } + + #endregion + public abstract Task SendNotification(DatabaseNotification notification); // SQLite returns DateTime as Kind=Unspecified, Npgsql actually knows for sure it's Kind=Utc. diff --git a/Content.Server/Database/ServerDbManager.cs b/Content.Server/Database/ServerDbManager.cs index f07149abe64..2fbddd60268 100644 --- a/Content.Server/Database/ServerDbManager.cs +++ b/Content.Server/Database/ServerDbManager.cs @@ -329,6 +329,46 @@ Task AddConnectionLogAsync( Task SendNotification(DatabaseNotification notification); #endregion + + #region Custom vote log + + /// + /// Log a new custom vote to the database. + /// + /// The player-facing title for the custom vote. + /// The round ID this vote was initiated. + /// The user ID of the admin that initiated the vote. + /// The player-facing contents of each vote option. + /// + /// The created vote is initially in the state. + /// + /// + /// The ID of the database entry, + /// for subsequent calls to or . + /// + Task CustomVoteLogAdd(string title, int roundId, NetUserId? initiator, ImmutableArray options); + + /// + /// Mark a logged custom vote as finished. + /// + /// + /// The database ID of the custom vote, as returned by . + /// + /// + /// The counts each option received. The indexes are matched to the options given in + /// . + /// + Task CustomVoteLogFinish(int voteId, ImmutableArray voteCounts); + + /// + /// Mark a logged custom vote as canceled. + /// + /// + /// The database ID of the custom vote, as returned by . + /// + Task CustomVoteLogCancel(int voteId); + + #endregion } /// @@ -997,6 +1037,28 @@ public Task SendNotification(DatabaseNotification notification) return RunDbCommand(() => _db.SendNotification(notification)); } + public Task CustomVoteLogAdd( + string title, + int roundId, + NetUserId? initiator, + ImmutableArray options) + { + DbWriteOpsMetric.Inc(); + return RunDbCommand(() => _db.CustomVoteLogAdd(title, roundId, initiator, options)); + } + + public Task CustomVoteLogFinish(int voteId, ImmutableArray voteCounts) + { + DbWriteOpsMetric.Inc(); + return RunDbCommand(() => _db.CustomVoteLogFinish(voteId, voteCounts)); + } + + public Task CustomVoteLogCancel(int voteId) + { + DbWriteOpsMetric.Inc(); + return RunDbCommand(() => _db.CustomVoteLogCancel(voteId)); + } + private async void HandleDatabaseNotification(DatabaseNotification notification) { lock (_notificationHandlers) diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index ace65bce51d..8ce51377949 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -133,5 +133,10 @@ public override void Update(float frameTime) UpdateRoundFlow(frameTime); UpdateGameRules(); } + + public static int GetRoundId(IEntitySystemManager esm) + { + return esm.GetEntitySystemOrNull()?.RoundId ?? 0; + } } } diff --git a/Content.Server/Voting/VoteCommands.cs b/Content.Server/Voting/VoteCommands.cs index 600d235de5f..57338490a46 100644 --- a/Content.Server/Voting/VoteCommands.cs +++ b/Content.Server/Voting/VoteCommands.cs @@ -2,7 +2,9 @@ using Content.Server.Administration; using Content.Server.Administration.Logs; using Content.Server.Chat.Managers; +using Content.Server.Database; using Content.Server.Discord.WebhookMessages; +using Content.Server.GameTicking; using Content.Server.Voting.Managers; using Content.Shared.Administration; using Content.Shared.CCVar; @@ -71,12 +73,14 @@ public sealed partial class CreateCustomCommand : LocalizedEntityCommands [Dependency] private IChatManager _chatManager = default!; [Dependency] private VoteWebhooks _voteWebhooks = default!; [Dependency] private IConfigurationManager _cfg = default!; + [Dependency] private IServerDbManager _dbManager = default!; + [Dependency] private IEntitySystemManager _esm = default!; private const int MaxArgCount = 10; public override string Command => "customvote"; - public override void Execute(IConsoleShell shell, string argStr, string[] args) + public override async void Execute(IConsoleShell shell, string argStr, string[] args) { if (args.Length < 3 || args.Length > MaxArgCount) { @@ -106,9 +110,15 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) var vote = _voteManager.CreateVote(options); + var voteLogId = await _dbManager.CustomVoteLogAdd( + title, + GameTicker.GetRoundId(_esm), + shell.Player?.UserId, + [..options.Options.Select(x => x.text)]); + var webhookState = _voteWebhooks.CreateWebhookIfConfigured(options, _cfg.GetCVar(CCVars.DiscordVoteWebhook)); - vote.OnFinished += (_, eventArgs) => + vote.OnFinished += async (_, eventArgs) => { if (eventArgs.Winner == null) { @@ -123,11 +133,13 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) } _voteWebhooks.UpdateWebhookIfConfigured(webhookState, eventArgs); + await _dbManager.CustomVoteLogFinish(voteLogId, [..eventArgs.Votes]); }; - vote.OnCancelled += _ => + vote.OnCancelled += async _ => { _voteWebhooks.UpdateCancelledWebhookIfConfigured(webhookState); + await _dbManager.CustomVoteLogCancel(voteLogId); }; } From a50fe8b0145d17537e25120d45c9efd4c629e9cf Mon Sep 17 00:00:00 2001 From: Hyper B <137433177+HyperB1@users.noreply.github.com> Date: Fri, 12 Jun 2026 20:46:55 +0500 Subject: [PATCH 052/287] Fix slimepeoples' left arm sprite (#44282) fix slimepeople left arm sprite --- .../Mobs/Species/Slime/parts.rsi/l_arm.png | Bin 347 -> 309 bytes .../Mobs/Species/Slime/parts.rsi/l_hand.png | Bin 296 -> 269 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/Textures/Mobs/Species/Slime/parts.rsi/l_arm.png b/Resources/Textures/Mobs/Species/Slime/parts.rsi/l_arm.png index 5d9c26442cc06070e4cc73330f3cc588de4eabd6..5c500a971c28e45cb2518499ff1a1cd5631c11ea 100644 GIT binary patch delta 282 zcmcc3w3TUsNGZx^prw85kH?(j9#r85lP9bN@+X1@buyJR*x37`O#Nn6W-3 zej-rtl&6bhNJZS+n+JIhEAX^FT+&=2bS#mJ|AFePOeOzAN-sNCJqj}j&HVqqC2mQF zO@Q7#GoUU8!832NHgjygSDoD)s8r;Vf9;H(pyjQ4n?pTJPTniz^_AW(7I?_Iqq^_j znvR@z^KRQXRWOEnW&RMGadT?WtfH^0KQM)q_S@-}pI^0Wdu7W9CXk_E@L}n)p4oOU zZO&K6e($+3O}=+((7f048sF%POo;ccxc-TC#j7~4m-F28;}2+XWi(Ii{lg8`_x}mw XJK=tpvIwb&tsntUS3j3^P62BR01_nl6 zPZ!6KinzD8F7h@j2plWC?Kr2Ua846r%%Zt%2Athn;)0|ePi}oLl=;u@@$|>#FHRf! z+Hfc|Ffc)h1p*P*zb-vtnO5=i-qoY$KK}_3&#s)ddfnAeHVkU2PIv!Id%3aR*5O=b zAV~bRqJll+))N9w-#~`~cA=&`p`sz)x-N#_ zVf}-t_ws*=^VBnayPokT{z2*FCtkhF#89kR5dDDnrj5^~{Xv_FVdQ&MBb@ E0MO=v2mk;8 diff --git a/Resources/Textures/Mobs/Species/Slime/parts.rsi/l_hand.png b/Resources/Textures/Mobs/Species/Slime/parts.rsi/l_hand.png index 4e5988ca6c95904fe8ad3f0696e46c64b55a96f0..dd374b9c9d9a3b079c490f5db845da97fd6c4f08 100644 GIT binary patch delta 241 zcmZ3%)XOwMrJk`k$lZxy-8q?;3=9k`>5jgR3=A9lx&I`x0{NT;9+AZi4BUbs%vhfi zKM^Rn$kW9!q$2L^t&5z^1|qB%wlrIGKDKCO-lJMF>w$`1QvECc1+BAgDA_(&|G;Ga zp)Nm&38?GA{yV1}*LK|awQqi~S7zk2M497<^S7MIs(Kp7msJ{TK7F&;xl_6O8cJi^ zpGU^YZhHP^=X3$VXqTL)e>$c`0u2Cxvo*U<^mOPxw|P2expKbw`s3$ceZ0SN<)hj9 l55jHh&I;+Bmj~Kq_nkrbn{(}fD;`Q9O`fiPF6*2UngAOjVo(48 literal 296 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9GGLLkg|>2BR0px`Y} z7srr_xVN`1ayA=?um(h1xvLe*EH7YK{=&lVkNvhXmHm7AwN*C* zBNGdUfPzBhy<(a>})c#+$^Ro^AV~e9OESRB}- z&EkKj;*=SEvqH}c9p8XQrRr@e4-=VBy5aEUxiZ5U)txwcLl7j{_b%X z-RW#bv(B Date: Fri, 12 Jun 2026 10:47:54 -0500 Subject: [PATCH 053/287] Fix: Screens on Serpentcrest now properly show evac timers (#44279) --- Resources/Maps/Shuttles/emergency_syndicate.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Resources/Maps/Shuttles/emergency_syndicate.yml b/Resources/Maps/Shuttles/emergency_syndicate.yml index d23548de59d..ca3b9b0d4ea 100644 --- a/Resources/Maps/Shuttles/emergency_syndicate.yml +++ b/Resources/Maps/Shuttles/emergency_syndicate.yml @@ -194,6 +194,12 @@ entities: chunkSize: 4 - type: GasTileOverlay - type: RadiationGridResistance + - type: DeviceNetwork + deviceNetId: Wireless + configurators: [] + deviceLists: [] + transmitFrequencyId: ShuttleTimer + - type: NavMap - type: ExplosionAirtightGrid - proto: AirlockCargoGlassLocked entities: From d3cb88cd74a26843840c388ccc719f5b1fa90909 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 12 Jun 2026 18:01:39 +0200 Subject: [PATCH 054/287] Ghosts have follow buttons in chat (#44284) --- Content.Client/Options/UI/Tabs/MiscTab.xaml | 1 + .../Options/UI/Tabs/MiscTab.xaml.cs | 1 + Content.Server/Chat/Managers/ChatManager.cs | 39 ++++++++++++++++++- Content.Server/Chat/Managers/IChatManager.cs | 2 + .../Ghost/GhostFollowEntityCommand.cs | 26 +++++++++++++ Content.Server/Ghost/GhostSystem.cs | 39 +++++++++++++------ .../Radio/EntitySystems/RadioSystem.cs | 25 +++++++++++- Content.Shared/CCVar/CCVars.Interface.cs | 6 +++ Content.Shared/Chat/MsgChatMessage.cs | 14 +++++++ .../en-US/chat/managers/chat-manager.ftl | 2 + .../en-US/escape-menu/ui/options-menu.ftl | 1 + 11 files changed, 140 insertions(+), 16 deletions(-) create mode 100644 Content.Server/Ghost/GhostFollowEntityCommand.cs diff --git a/Content.Client/Options/UI/Tabs/MiscTab.xaml b/Content.Client/Options/UI/Tabs/MiscTab.xaml index c1733e209db..42142efc944 100644 --- a/Content.Client/Options/UI/Tabs/MiscTab.xaml +++ b/Content.Client/Options/UI/Tabs/MiscTab.xaml @@ -18,6 +18,7 @@ +