6xFCpHc|t-n1tEyrvj+2^Cq_wRSMT0CFC2;psh4H{eZNs@Z`q (NSmxu`RV4P!k6KWNh40}(_n||#Lou2lwhq=&(eUbQFxnS*{
zXteid^e7}$-w *^6T#TQL(pbHvdG4Ek5>0
egOGL&U3o;Q{NvW~@*s@Iz0?*1WD&}_3onXz~p@db*n zz|T~NEr7IRaTzKuP_P7$W-Km42KRRHRv?@P&d13%l$ig}l9t3#U)m{ejD>Y%))tWQ ui922CwBN-ldV6IWuC9Htj) zo4)t+rwc_}1(=q0MP^j~HJRxW_i26&b4p<1&Kkw7s#Cl}r=1q}5EH%rXPtfHqdoI> zU7sJlPRyw=&i?nEi%dXeTf0PD*WI|PHR*M9K>&mOp3EB)+q;`HZ>&?z;eNs2<|8J0 zO?K-^lblPkQ*}JMO>=JOcq-ow1PZ+ByQR|2^_st}zUzkAhrg+Uf)mXmC%;=U0%~MbHCoI01WL&D1QY{VCzUAbsjOTOIf$l%XzxRIFtq*^C(@y`F-?`=9pMBAm z>@ZI k%U@f7M+vfRVr7f8VvhrVmE1_PH!zs?(XgM@NxG@aqb@0LHlK z0(zpySJ+L-Kd@7kW&b0YtL;HxR*>nc=Zl|j|IPpH->;K>x{5Jc9~b|+Td;>EGUP__ rZ`CUsI`>rST@X67*5eB= sd+Tap4RVF2tDnm{r-UW|9iiMG literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Janitorial/wirebrush.rsi/meta.json b/Resources/Textures/Objects/Specific/Janitorial/wirebrush.rsi/meta.json new file mode 100644 index 0000000000..4a9131da5e --- /dev/null +++ b/Resources/Textures/Objects/Specific/Janitorial/wirebrush.rsi/meta.json @@ -0,0 +1,25 @@ +{ + "version": 1, + "license": "CC-BY-SA-4.0", + "copyright": "Sprites by TiniestShark (Github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "storage" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Specific/Janitorial/wirebrush.rsi/storage.png b/Resources/Textures/Objects/Specific/Janitorial/wirebrush.rsi/storage.png new file mode 100644 index 0000000000000000000000000000000000000000..36997e2cd84887c65d61ada15d6daff9e4ae43d3 GIT binary patch literal 449 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^T<)t)YnArY;~ z2@-w^+jl&&Us*L{MQUYE(5?UK{qjZ;5jQ72;czR`=}vs_w6VqT7vJIO`tv2{*;gJa zcyuFi(wP)Rw~4c->)$!#FK=gHFef!ZAaz>ugOn%f2EEf$Bdp7%4;Mc^s}g *M#T+!pGRb j^|Td( Q)-N$zfvat6i;gQu&X%Q~loCIEmJz1;u+ literal 0 HcmV?d00001 From a2ac401db3053122512700a53652c4402231605d Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 4 Jul 2025 02:03:42 +0000 Subject: [PATCH 008/214] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 815a1d9317..7a02e80d6d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: slarticodefast - changes: - - message: Fire extinguishers and sprays now allow you to push the grids you are - standing on when you have magboots on. - type: Add - id: 8224 - time: '2025-04-18T03:41:27.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/31754 - author: aada changes: - message: Filter categories added to the security techfab. @@ -3887,3 +3879,11 @@ id: 8735 time: '2025-07-04T00:04:27.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38699 +- author: TiniestShark + changes: + - message: Added a Wire Brush to the Janitor's tool kit for scrubbing unsightly + rust off of the station's walls! + type: Add + id: 8736 + time: '2025-07-04T02:02:35.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38667 From e82064916a891f835bc09269c721c1e22112ef8c Mon Sep 17 00:00:00 2001 From: Velken <8467292+Velken@users.noreply.github.com> Date: Fri, 4 Jul 2025 02:08:45 -0300 Subject: [PATCH 009/214] [BUGFIX] Fixed revenant malfunction ability not working properly only MediBots and Stasis bed (#38664) * fixed * clean up * orks fix smart * review fix 1 * more requested changes * less cursed * more descriptive description * better wording --- .../EntitySystems/RevenantSystem.Abilities.cs | 4 ++-- Content.Shared/Emag/Systems/EmagSystem.cs | 15 +++++++++------ .../Prototypes/Entities/Mobs/NPCs/revenant.yml | 2 ++ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs index 51cbb6d4d5..0ef7b8f533 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs @@ -35,6 +35,7 @@ namespace Content.Server.Revenant.EntitySystems; public sealed partial class RevenantSystem { + [Dependency] private readonly EmagSystem _emagSystem = default!; [Dependency] private readonly ThrowingSystem _throwing = default!; [Dependency] private readonly EntityStorageSystem _entityStorage = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; @@ -345,8 +346,7 @@ public sealed partial class RevenantSystem _whitelistSystem.IsBlacklistPass(component.MalfunctionBlacklist, ent)) continue; - var ev = new GotEmaggedEvent(uid, EmagType.Interaction | EmagType.Access); - RaiseLocalEvent(ent, ref ev); + _emagSystem.TryEmagEffect(uid, uid, ent); } } } diff --git a/Content.Shared/Emag/Systems/EmagSystem.cs b/Content.Shared/Emag/Systems/EmagSystem.cs index 7aa4303471..912c4bdaea 100644 --- a/Content.Shared/Emag/Systems/EmagSystem.cs +++ b/Content.Shared/Emag/Systems/EmagSystem.cs @@ -51,9 +51,9 @@ public sealed class EmagSystem : EntitySystem } /// - /// Does the emag effect on a specified entity + /// Does the emag effect on a specified entity with a specified EmagType. The optional field customEmagType can be used to override the emag type defined in the component. /// - public bool TryEmagEffect(Entityent, EntityUid user, EntityUid target) + public bool TryEmagEffect(Entity ent, EntityUid user, EntityUid target, EmagType? customEmagType = null) { if (!Resolve(ent, ref ent.Comp, false)) return false; @@ -68,7 +68,9 @@ public sealed class EmagSystem : EntitySystem return false; } - var emaggedEvent = new GotEmaggedEvent(user, ent.Comp.EmagType); + var typeToUse = customEmagType ?? ent.Comp.EmagType; + + var emaggedEvent = new GotEmaggedEvent(user, typeToUse); RaiseLocalEvent(target, ref emaggedEvent); if (!emaggedEvent.Handled) @@ -78,7 +80,7 @@ public sealed class EmagSystem : EntitySystem _audio.PlayPredicted(ent.Comp.EmagSound, ent, ent); - _adminLogger.Add(LogType.Emag, LogImpact.High, $"{ToPrettyString(user):player} emagged {ToPrettyString(target):target} with flag(s): {ent.Comp.EmagType}"); + _adminLogger.Add(LogType.Emag, LogImpact.High, $"{ToPrettyString(user):player} emagged {ToPrettyString(target):target} with flag(s): {typeToUse}"); if (emaggedEvent.Handled) _sharedCharges.TryUseCharge(chargesEnt); @@ -87,7 +89,7 @@ public sealed class EmagSystem : EntitySystem { EnsureComp (target, out var emaggedComp); - emaggedComp.EmagType |= ent.Comp.EmagType; + emaggedComp.EmagType |= typeToUse; Dirty(target, emaggedComp); } @@ -129,9 +131,10 @@ public sealed class EmagSystem : EntitySystem [Flags] [Serializable, NetSerializable] -public enum EmagType : byte +public enum EmagType { None = 0, + All = ~None, Interaction = 1 << 1, Access = 1 << 2 } diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml index 1c70e55d66..13bce86b06 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml @@ -6,6 +6,8 @@ name: revenant description: A spooky ghostie. components: + - type: Emag + emagType: All - type: Input context: "ghost" - type: Spectral From 7b6cc535dd5318c66e2ed5014ae033b34aeb65af Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 4 Jul 2025 05:09:52 +0000 Subject: [PATCH 010/214] 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 7a02e80d6d..705d1e7d4f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: aada - changes: - - message: Filter categories added to the security techfab. - type: Add - id: 8225 - time: '2025-04-18T03:52:05.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/35594 - author: Fildrance changes: - message: returned deconstruct to the top level option on RCD @@ -3887,3 +3880,11 @@ id: 8736 time: '2025-07-04T02:02:35.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38667 +- author: Velken + changes: + - message: Revenants' malfunction ability now properly works on MediBots and Stasis + Beds again. + type: Fix + id: 8737 + time: '2025-07-04T05:08:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38664 From 4e73c67d1b277cc5fe857aa865044bdbb520af8a Mon Sep 17 00:00:00 2001 From: Kyle Tyo <36606155+VerinSenpai@users.noreply.github.com> Date: Fri, 4 Jul 2025 02:58:56 -0400 Subject: [PATCH 011/214] Tiny Tiny Cleanup of the EyeClosingSystem. (#38734) Update EyeClosingSystem.cs --- Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs b/Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs index 1b62c45af4..68ccf4a6b8 100644 --- a/Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs +++ b/Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs @@ -15,7 +15,6 @@ public sealed class EyeClosingSystem : EntitySystem [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly ISharedPlayerManager _playerManager = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; public override void Initialize() { @@ -120,7 +119,7 @@ public sealed class EyeClosingSystem : EntitySystem var ev = new GetBlurEvent(blindable.Comp.EyeDamage); RaiseLocalEvent(blindable.Owner, ev); - if (_entityManager.TryGetComponent (blindable, out var eyelids) && !eyelids.NaturallyCreated) + if (EntityManager.TryGetComponent (blindable, out var eyelids) && !eyelids.NaturallyCreated) return; if (ev.Blur < BlurryVisionComponent.MaxMagnitude || ev.Blur >= blindable.Comp.MaxDamage) @@ -135,6 +134,4 @@ public sealed class EyeClosingSystem : EntitySystem } } -public sealed partial class ToggleEyesActionEvent : InstantActionEvent -{ -} +public sealed partial class ToggleEyesActionEvent : InstantActionEvent; From d5d386478401baf8dc83d4298662780f93632a41 Mon Sep 17 00:00:00 2001 From: Minerva <218184747+mnva0@users.noreply.github.com> Date: Fri, 4 Jul 2025 12:46:23 +0000 Subject: [PATCH 012/214] Typofixes for figurine dialogue (#38737) * Typofixes for figurine dialogue * Forgot two --- Resources/Locale/en-US/datasets/figurines.ftl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Locale/en-US/datasets/figurines.ftl b/Resources/Locale/en-US/datasets/figurines.ftl index 6d66566c20..c000c37c1e 100644 --- a/Resources/Locale/en-US/datasets/figurines.ftl +++ b/Resources/Locale/en-US/datasets/figurines.ftl @@ -134,9 +134,9 @@ figurines-ce-6 = Power's out again. figurines-engineer-1 = SINGULOOSE! figurines-engineer-2 = TESLOOSE! figurines-engineer-3 = What is AME? -figurines-engineer-4 = Free insuls at engineering +figurines-engineer-4 = Free insuls at Engineering! figurines-engineer-5 = Where'd the power go? -figurines-engineer-6 = Someone bombed medbay... again... +figurines-engineer-6 = Someone bombed the medbay... again... figurines-engineer-7 = Well, why don't you come and fix it? figurines-atmostech-1 = I put plasma in distro. @@ -148,7 +148,7 @@ figurines-atmostech-6 = Distro? That's short for disposal. figurines-atmostech-7 = TEG: Thermal Energy? Gone! figurines-rd-1 = Blowing up all of the borgs! -figurines-rd-2 = Tier 3 arsenal? No way. +figurines-rd-2 = Tier 3 Arsenal? No way. figurines-rd-3 = Now where did I leave my hardsuit...? figurines-rd-4 = Now you're thinking with portals! figurines-rd-5 = The cake is a lie! @@ -245,22 +245,22 @@ figurines-nukie-3 = The nuke makes boom. figurines-nukie-4 = What's the code? figurines-nukie-5 = Commander...? ...That's a balloon... -figurines-nukie-elite-1 = Not a word in nanotrasen. +figurines-nukie-elite-1 = Not a word in Nanotrasen. figurines-nukie-elite-2 = THAT'S A KEG! -figurines-nukie-elite-3 = Guys are you alive? +figurines-nukie-elite-3 = Guys, are you alive? figurines-nukie-elite-4 = Breach and clear! figurines-nukie-elite-5 = Leave no survivors. figurines-nukie-elite-6 = Good work, team. figurines-nukie-commander-1 = GET DAT FUKKEN DISK! figurines-nukie-commander-2 = Fuckin' flukies. -figurines-nukie-commander-3 = The syndicate sends its regards. +figurines-nukie-commander-3 = The Syndicate sends its regards. figurines-nukie-commander-4 = Failure is not an option. figurines-nukie-commander-5 = Whoops. figurines-footsoldier-1 = I'm an evil boy. Less boy every day, more evil every day. figurines-footsoldier-2 = Who will you choose? Them or us? Us or them? -figurines-footsoldier-3 = Glory to the syndicate! +figurines-footsoldier-3 = Glory to the Syndicate! figurines-footsoldier-4 = Down with Nanotrasen! figurines-footsoldier-5 = I'd rather die than join Nanotrasen. From 1a2e827674e173a1f0ba9cee9358f925e2e17858 Mon Sep 17 00:00:00 2001 From: Perry Fraser Date: Fri, 4 Jul 2025 08:48:16 -0400 Subject: [PATCH 013/214] fix: Use PredictedQueueDel for gib spell (#38729) --- Content.Shared/Gibbing/Systems/GibbingSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Gibbing/Systems/GibbingSystem.cs b/Content.Shared/Gibbing/Systems/GibbingSystem.cs index 8c83de0cf2..9eaadbb970 100644 --- a/Content.Shared/Gibbing/Systems/GibbingSystem.cs +++ b/Content.Shared/Gibbing/Systems/GibbingSystem.cs @@ -187,7 +187,7 @@ public sealed class GibbingSystem : EntitySystem } if (gibType == GibType.Gib) - QueueDel(gibbable); + PredictedQueueDel(gibbable.Owner); return true; } @@ -292,7 +292,7 @@ public sealed class GibbingSystem : EntitySystem var gibbedEvent = new EntityGibbedEvent(gibbable, localGibs); RaiseLocalEvent(gibbable, ref gibbedEvent); if (deleteTarget) - QueueDel(gibbable); + PredictedQueueDel(gibbable.Owner); return localGibs; } From d0c067f006cc2f2f63b1bcadb65fbb6bd639d232 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Fri, 4 Jul 2025 09:22:26 -0400 Subject: [PATCH 014/214] Fix human skin tone distribution (#38701) --- Content.Shared/Humanoid/HumanoidCharacterAppearance.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Content.Shared/Humanoid/HumanoidCharacterAppearance.cs b/Content.Shared/Humanoid/HumanoidCharacterAppearance.cs index 05bebea075..7d4a17b337 100644 --- a/Content.Shared/Humanoid/HumanoidCharacterAppearance.cs +++ b/Content.Shared/Humanoid/HumanoidCharacterAppearance.cs @@ -153,8 +153,7 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance, switch (skinType) { case HumanoidSkinColor.HumanToned: - var tone = Math.Round(Humanoid.SkinColor.HumanSkinToneFromColor(newSkinColor)); - newSkinColor = Humanoid.SkinColor.HumanSkinTone((int)tone); + newSkinColor = Humanoid.SkinColor.HumanSkinTone(random.Next(0, 101)); break; case HumanoidSkinColor.Hues: break; From bfb73a1aad4f40d40fbdcd0facba155641da32e8 Mon Sep 17 00:00:00 2001 From: Perry Fraser Date: Fri, 4 Jul 2025 12:15:24 -0400 Subject: [PATCH 015/214] fix: don't default to uncharged sprite state for cells (#38730) --- Content.Client/PowerCell/PowerCellSystem.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Content.Client/PowerCell/PowerCellSystem.cs b/Content.Client/PowerCell/PowerCellSystem.cs index fb40b911d1..8d9dd5ebdd 100644 --- a/Content.Client/PowerCell/PowerCellSystem.cs +++ b/Content.Client/PowerCell/PowerCellSystem.cs @@ -48,8 +48,9 @@ public sealed class PowerCellSystem : SharedPowerCellSystem if (!_sprite.LayerExists((uid, args.Sprite), PowerCellVisualLayers.Unshaded)) return; + // If no appearance data is set, rely on whatever existing sprite state is set being correct. if (!_appearance.TryGetData (uid, PowerCellVisuals.ChargeLevel, out var level, args.Component)) - level = 0; + return; var positiveCharge = level > 0; _sprite.LayerSetVisible((uid, args.Sprite), PowerCellVisualLayers.Unshaded, positiveCharge); From c3267c6db03b99017b96c0dfdf4afe3b30a82950 Mon Sep 17 00:00:00 2001 From: Arthur Fiorese de Andrade <170277843+ADM2PLAY@users.noreply.github.com> Date: Fri, 4 Jul 2025 17:41:49 -0300 Subject: [PATCH 016/214] Fix Hristov description - remove inaccurate technical specs (#38746) - Removes inaccurate 'armor piercing 14.5mm shells' reference - Replaces it with a more funny description, matching the style of the other snipers and guns - Keeps ammunition type '.60 anti-materiel ammo' specification - Fixes issue #38590 Co-authored-by: Arthur Fiorese de Andrade --- .../Entities/Objects/Weapons/Guns/Snipers/snipers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml index 420650afc1..5514db42e6 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml @@ -54,7 +54,7 @@ name: Hristov parent: [BaseWeaponSniper, BaseGunWieldable, BaseSyndicateContraband] id: WeaponSniperHristov - description: A portable anti-materiel rifle. Fires armor piercing 14.5mm shells. Uses .60 anti-materiel ammo. + description: For when you absolutely, positively need to make someone regret their life choices from a safe distance. Uses .60 anti-materiel ammo. components: - type: Sprite sprite: Objects/Weapons/Guns/Snipers/heavy_sniper.rsi From 3a278bca8bf690d8e2b8553773ac8e7c5b2f544f Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Fri, 4 Jul 2025 16:48:55 -0400 Subject: [PATCH 017/214] Validate `ProtoId`s in tests (#38745) * Convert string literals to protoids in Content.Tests * Convert string literals to protoids or consts in Content.IntegrationTests * Fix linter failures Tricksy static using misled me --- .../Tests/Atmos/AlarmThresholdTest.cs | 8 +- .../Tests/Commands/RejuvenateTest.cs | 4 +- .../Tests/Commands/SuicideCommandTests.cs | 4 +- .../Construction/Interaction/WindowRepair.cs | 4 +- .../Tests/Damageable/DamageableTest.cs | 75 +++++++++++-------- .../DestructibleDamageGroupTest.cs | 4 +- .../DestructibleDamageTypeTest.cs | 4 +- .../DestructibleDestructionTest.cs | 2 +- .../DestructibleTestPrototypes.cs | 44 ++++++----- .../DestructibleThresholdActivationTest.cs | 2 +- .../Tests/Minds/MindTests.cs | 4 +- .../Tests/PostMapInitTest.cs | 4 +- .../Tests/Station/StationJobsTest.cs | 14 ++-- .../Tests/Vending/VendingInteractionTest.cs | 4 +- .../Tests/VendingMachineRestockTest.cs | 4 +- Content.Tests/Shared/DamageTest.cs | 23 ++++-- .../Shared/LocalizedDatasetPrototypeTest.cs | 8 +- 17 files changed, 129 insertions(+), 83 deletions(-) diff --git a/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs b/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs index 064b4f9a2d..b74c35ba11 100644 --- a/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs @@ -7,10 +7,12 @@ namespace Content.IntegrationTests.Tests.Atmos [TestOf(typeof(AtmosAlarmThreshold))] public sealed class AlarmThresholdTest { + private const string AlarmThresholdTestDummyId = "AlarmThresholdTestDummy"; + [TestPrototypes] - private const string Prototypes = @" + private const string Prototypes = $@" - type: alarmThreshold - id: AlarmThresholdTestDummy + id: {AlarmThresholdTestDummyId} upperBound: !type:AlarmThresholdSetting threshold: 5 lowerBound: !type:AlarmThresholdSetting @@ -30,7 +32,7 @@ namespace Content.IntegrationTests.Tests.Atmos var prototypeManager = server.ResolveDependency (); AtmosAlarmThreshold threshold = default!; - var proto = prototypeManager.Index ("AlarmThresholdTestDummy"); + var proto = prototypeManager.Index (AlarmThresholdTestDummyId); threshold = new(proto); await server.WaitAssertion(() => diff --git a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs index cfc8007306..e4ec7e907a 100644 --- a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs +++ b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs @@ -15,6 +15,8 @@ namespace Content.IntegrationTests.Tests.Commands [TestOf(typeof(RejuvenateSystem))] public sealed class RejuvenateTest { + private static readonly ProtoId TestDamageGroup = "Toxin"; + [TestPrototypes] private const string Prototypes = @" - type: entity @@ -62,7 +64,7 @@ namespace Content.IntegrationTests.Tests.Commands }); // Kill the entity - DamageSpecifier damage = new(prototypeManager.Index ("Toxin"), FixedPoint2.New(10000000)); + DamageSpecifier damage = new(prototypeManager.Index(TestDamageGroup), FixedPoint2.New(10000000)); damSystem.TryChangeDamage(human, damage, true); diff --git a/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs b/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs index b53b87dd5c..61b8d54448 100644 --- a/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs +++ b/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs @@ -53,6 +53,8 @@ public sealed class SuicideCommandTests components: - type: MaterialReclaimer"; private static readonly ProtoId CannotSuicideTag = "CannotSuicide"; + private static readonly ProtoId DamageType = "Slash"; + /// /// Run the suicide command in the console /// Should successfully kill the player and ghost them @@ -144,7 +146,7 @@ public sealed class SuicideCommandTests mobThresholdsComp = entManager.GetComponent (player); damageableComp = entManager.GetComponent (player); - if (protoMan.TryIndex ("Slash", out var slashProto)) + if (protoMan.TryIndex(DamageType, out var slashProto)) damageableSystem.TryChangeDamage(player, new DamageSpecifier(slashProto, FixedPoint2.New(46.5))); }); diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/WindowRepair.cs b/Content.IntegrationTests/Tests/Construction/Interaction/WindowRepair.cs index 6eea519af3..192604edfd 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/WindowRepair.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/WindowRepair.cs @@ -8,6 +8,8 @@ namespace Content.IntegrationTests.Tests.Construction.Interaction; public sealed class WindowRepair : InteractionTest { + private static readonly ProtoId BluntDamageType = "Blunt"; + [Test] public async Task RepairReinforcedWindow() { @@ -16,7 +18,7 @@ public sealed class WindowRepair : InteractionTest // Damage the entity. var sys = SEntMan.System (); var comp = Comp (); - var damageType = Server.ResolveDependency ().Index ("Blunt"); + var damageType = Server.ProtoMan.Index(BluntDamageType); var damage = new DamageSpecifier(damageType, FixedPoint2.New(10)); Assert.That(comp.Damage.GetTotal(), Is.EqualTo(FixedPoint2.Zero)); await Server.WaitPost(() => sys.TryChangeDamage(SEntMan.GetEntity(Target), damage, ignoreResistances: true)); diff --git a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs index 69069fc82f..f610ab732e 100644 --- a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs +++ b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs @@ -1,9 +1,7 @@ -using System.Linq; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Prototypes; @@ -14,66 +12,79 @@ namespace Content.IntegrationTests.Tests.Damageable [TestOf(typeof(DamageableSystem))] public sealed class DamageableTest { + private const string TestDamageableEntityId = "TestDamageableEntityId"; + private const string TestGroup1 = "TestGroup1"; + private const string TestGroup2 = "TestGroup2"; + private const string TestGroup3 = "TestGroup3"; + private const string TestDamage1 = "TestDamage1"; + private const string TestDamage2a = "TestDamage2a"; + private const string TestDamage2b = "TestDamage2b"; + + private const string TestDamage3a = "TestDamage3a"; + + private const string TestDamage3b = "TestDamage3b"; + private const string TestDamage3c = "TestDamage3c"; + [TestPrototypes] - private const string Prototypes = @" + private const string Prototypes = $@" # Define some damage groups - type: damageType - id: TestDamage1 + id: {TestDamage1} name: damage-type-blunt - type: damageType - id: TestDamage2a + id: {TestDamage2a} name: damage-type-blunt - type: damageType - id: TestDamage2b + id: {TestDamage2b} name: damage-type-blunt - type: damageType - id: TestDamage3a + id: {TestDamage3a} name: damage-type-blunt - type: damageType - id: TestDamage3b + id: {TestDamage3b} name: damage-type-blunt - type: damageType - id: TestDamage3c + id: {TestDamage3c} name: damage-type-blunt # Define damage Groups with 1,2,3 damage types - type: damageGroup - id: TestGroup1 + id: {TestGroup1} name: damage-group-brute damageTypes: - - TestDamage1 + - {TestDamage1} - type: damageGroup - id: TestGroup2 + id: {TestGroup2} name: damage-group-brute damageTypes: - - TestDamage2a - - TestDamage2b + - {TestDamage2a} + - {TestDamage2b} - type: damageGroup - id: TestGroup3 + id: {TestGroup3} name: damage-group-brute damageTypes: - - TestDamage3a - - TestDamage3b - - TestDamage3c + - {TestDamage3a} + - {TestDamage3b} + - {TestDamage3c} # This container should not support TestDamage1 or TestDamage2b - type: damageContainer id: testDamageContainer supportedGroups: - - TestGroup3 + - {TestGroup3} supportedTypes: - - TestDamage2a + - {TestDamage2a} - type: entity - id: TestDamageableEntityId - name: TestDamageableEntityId + id: {TestDamageableEntityId} + name: {TestDamageableEntityId} components: - type: Damageable damageContainer: testDamageContainer @@ -113,20 +124,20 @@ namespace Content.IntegrationTests.Tests.Damageable { var coordinates = map.MapCoords; - sDamageableEntity = sEntityManager.SpawnEntity("TestDamageableEntityId", coordinates); + sDamageableEntity = sEntityManager.SpawnEntity(TestDamageableEntityId, coordinates); sDamageableComponent = sEntityManager.GetComponent (sDamageableEntity); sDamageableSystem = sEntitySystemManager.GetEntitySystem (); - group1 = sPrototypeManager.Index ("TestGroup1"); - group2 = sPrototypeManager.Index ("TestGroup2"); - group3 = sPrototypeManager.Index ("TestGroup3"); + group1 = sPrototypeManager.Index (TestGroup1); + group2 = sPrototypeManager.Index (TestGroup2); + group3 = sPrototypeManager.Index (TestGroup3); - type1 = sPrototypeManager.Index ("TestDamage1"); - type2a = sPrototypeManager.Index ("TestDamage2a"); - type2b = sPrototypeManager.Index ("TestDamage2b"); - type3a = sPrototypeManager.Index ("TestDamage3a"); - type3b = sPrototypeManager.Index ("TestDamage3b"); - type3c = sPrototypeManager.Index ("TestDamage3c"); + type1 = sPrototypeManager.Index (TestDamage1); + type2a = sPrototypeManager.Index (TestDamage2a); + type2b = sPrototypeManager.Index (TestDamage2b); + type3a = sPrototypeManager.Index (TestDamage3a); + type3b = sPrototypeManager.Index (TestDamage3b); + type3c = sPrototypeManager.Index (TestDamage3c); }); await server.WaitRunTicks(5); diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs index d37de7a504..0da07ad5a1 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs @@ -54,8 +54,8 @@ namespace Content.IntegrationTests.Tests.Destructible await server.WaitAssertion(() => { - var bruteDamageGroup = sPrototypeManager.Index ("TestBrute"); - var burnDamageGroup = sPrototypeManager.Index ("TestBurn"); + var bruteDamageGroup = sPrototypeManager.Index (TestBruteDamageGroupId); + var burnDamageGroup = sPrototypeManager.Index (TestBurnDamageGroupId); DamageSpecifier bruteDamage = new(bruteDamageGroup, FixedPoint2.New(5)); DamageSpecifier burnDamage = new(burnDamageGroup, FixedPoint2.New(5)); diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs index 69891cbd89..ccd459668b 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs @@ -49,8 +49,8 @@ namespace Content.IntegrationTests.Tests.Destructible await server.WaitAssertion(() => { - var bluntDamageType = protoManager.Index ("TestBlunt"); - var slashDamageType = protoManager.Index ("TestSlash"); + var bluntDamageType = protoManager.Index (TestBluntDamageTypeId); + var slashDamageType = protoManager.Index (TestSlashDamageTypeId); var bluntDamage = new DamageSpecifier(bluntDamageType, 5); var slashDamage = new DamageSpecifier(slashDamageType, 5); diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs index a50238d8f5..4e169e7dfa 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs @@ -39,7 +39,7 @@ namespace Content.IntegrationTests.Tests.Destructible await server.WaitAssertion(() => { var coordinates = sEntityManager.GetComponent (sDestructibleEntity).Coordinates; - var bruteDamageGroup = sPrototypeManager.Index ("TestBrute"); + var bruteDamageGroup = sPrototypeManager.Index (TestBruteDamageGroupId); DamageSpecifier bruteDamage = new(bruteDamageGroup, 50); #pragma warning disable NUnit2045 // Interdependent assertions. diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleTestPrototypes.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleTestPrototypes.cs index 7ff9242398..872e414ac3 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleTestPrototypes.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleTestPrototypes.cs @@ -7,48 +7,56 @@ namespace Content.IntegrationTests.Tests.Destructible public const string DestructibleDestructionEntityId = "DestructibleTestsDestructibleDestructionEntity"; public const string DestructibleDamageTypeEntityId = "DestructibleTestsDestructibleDamageTypeEntity"; public const string DestructibleDamageGroupEntityId = "DestructibleTestsDestructibleDamageGroupEntity"; + public const string TestBruteDamageGroupId = "TestBrute"; + public const string TestBurnDamageGroupId = "TestBurn"; + public const string TestBluntDamageTypeId = "TestBlunt"; + public const string TestSlashDamageTypeId = "TestSlash"; + public const string TestPiercingDamageTypeId = "TestPiercing"; + public const string TestHeatDamageTypeId = "TestHeat"; + public const string TestShockDamageTypeId = "TestShock"; + public const string TestColdDamageTypeId = "TestCold"; [TestPrototypes] public const string DamagePrototypes = $@" - type: damageType - id: TestBlunt + id: {TestBluntDamageTypeId} name: damage-type-blunt - type: damageType - id: TestSlash + id: {TestSlashDamageTypeId} name: damage-type-slash - type: damageType - id: TestPiercing + id: {TestPiercingDamageTypeId} name: damage-type-piercing - type: damageType - id: TestHeat + id: {TestHeatDamageTypeId} name: damage-type-heat - type: damageType - id: TestShock + id: {TestShockDamageTypeId} name: damage-type-shock - type: damageType - id: TestCold + id: {TestColdDamageTypeId} name: damage-type-cold - type: damageGroup - id: TestBrute + id: {TestBruteDamageGroupId} name: damage-group-brute damageTypes: - - TestBlunt - - TestSlash - - TestPiercing + - {TestBluntDamageTypeId} + - {TestSlashDamageTypeId} + - {TestPiercingDamageTypeId} - type: damageGroup - id: TestBurn + id: {TestBurnDamageGroupId} name: damage-group-burn damageTypes: - - TestHeat - - TestShock - - TestCold + - {TestHeatDamageTypeId} + - {TestShockDamageTypeId} + - {TestColdDamageTypeId} - type: entity id: {SpawnedEntityId} @@ -114,10 +122,10 @@ namespace Content.IntegrationTests.Tests.Destructible !type:AndTrigger triggers: - !type:DamageTypeTrigger - damageType: TestBlunt + damageType: {TestBluntDamageTypeId} damage: 10 - !type:DamageTypeTrigger - damageType: TestSlash + damageType: {TestSlashDamageTypeId} damage: 10 - type: entity @@ -131,10 +139,10 @@ namespace Content.IntegrationTests.Tests.Destructible !type:AndTrigger triggers: - !type:DamageGroupTrigger - damageGroup: TestBrute + damageGroup: {TestBruteDamageGroupId} damage: 10 - !type:DamageGroupTrigger - damageGroup: TestBurn + damageGroup: {TestBurnDamageGroupId} damage: 10"; } } diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs index 80f750c715..af86e406ef 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs @@ -61,7 +61,7 @@ namespace Content.IntegrationTests.Tests.Destructible await server.WaitAssertion(() => { - var bluntDamage = new DamageSpecifier(sPrototypeManager.Index ("TestBlunt"), 10); + var bluntDamage = new DamageSpecifier(sPrototypeManager.Index (TestBluntDamageTypeId), 10); sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage, true); diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.cs b/Content.IntegrationTests/Tests/Minds/MindTests.cs index d4d551f4e0..60e98b69fe 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.cs @@ -24,6 +24,8 @@ namespace Content.IntegrationTests.Tests.Minds; [TestFixture] public sealed partial class MindTests { + private static readonly ProtoId BluntDamageType = "Blunt"; + [TestPrototypes] private const string Prototypes = @" - type: entity @@ -144,7 +146,7 @@ public sealed partial class MindTests await server.WaitAssertion(() => { var damageable = entMan.GetComponent (entity); - if (!protoMan.TryIndex ("Blunt", out var prototype)) + if (!protoMan.TryIndex(BluntDamageType, out var prototype)) { return; } diff --git a/Content.IntegrationTests/Tests/PostMapInitTest.cs b/Content.IntegrationTests/Tests/PostMapInitTest.cs index 97be2b8705..a7a50a5270 100644 --- a/Content.IntegrationTests/Tests/PostMapInitTest.cs +++ b/Content.IntegrationTests/Tests/PostMapInitTest.cs @@ -77,6 +77,8 @@ namespace Content.IntegrationTests.Tests "Exo", }; + private static readonly ProtoId DoNotMapCategory = "DoNotMap"; + /// /// Asserts that specific files have been saved as grids and not maps. /// @@ -254,7 +256,7 @@ namespace Content.IntegrationTests.Tests return; var yamlEntities = node["entities"]; - if (!protoManager.TryIndex("DoNotMap", out var dnmCategory)) + if (!protoManager.TryIndex(DoNotMapCategory, out var dnmCategory)) return; Assert.Multiple(() => diff --git a/Content.IntegrationTests/Tests/Station/StationJobsTest.cs b/Content.IntegrationTests/Tests/Station/StationJobsTest.cs index d68fdafb76..3fee4a146c 100644 --- a/Content.IntegrationTests/Tests/Station/StationJobsTest.cs +++ b/Content.IntegrationTests/Tests/Station/StationJobsTest.cs @@ -17,8 +17,10 @@ namespace Content.IntegrationTests.Tests.Station; [TestOf(typeof(StationJobsSystem))] public sealed class StationJobsTest { + private const string StationMapId = "FooStation"; + [TestPrototypes] - private const string Prototypes = @" + private const string Prototypes = $@" - type: playTimeTracker id: PlayTimeDummyAssistant @@ -35,13 +37,13 @@ public sealed class StationJobsTest id: PlayTimeDummyChaplain - type: gameMap - id: FooStation + id: {StationMapId} minPlayers: 0 - mapName: FooStation + mapName: {StationMapId} mapPath: /Maps/Test/empty.yml stations: Station: - mapNameTemplate: FooStation + mapNameTemplate: {StationMapId} stationProto: StandardNanotrasenStation components: - type: StationJobs @@ -87,7 +89,7 @@ public sealed class StationJobsTest var server = pair.Server; var prototypeManager = server.ResolveDependency (); - var fooStationProto = prototypeManager.Index ("FooStation"); + var fooStationProto = prototypeManager.Index (StationMapId); var entSysMan = server.ResolveDependency ().EntitySysManager; var stationJobs = entSysMan.GetEntitySystem (); var stationSystem = entSysMan.GetEntitySystem (); @@ -161,7 +163,7 @@ public sealed class StationJobsTest var server = pair.Server; var prototypeManager = server.ResolveDependency (); - var fooStationProto = prototypeManager.Index ("FooStation"); + var fooStationProto = prototypeManager.Index (StationMapId); var entSysMan = server.ResolveDependency ().EntitySysManager; var stationJobs = entSysMan.GetEntitySystem (); var stationSystem = entSysMan.GetEntitySystem (); diff --git a/Content.IntegrationTests/Tests/Vending/VendingInteractionTest.cs b/Content.IntegrationTests/Tests/Vending/VendingInteractionTest.cs index bcaf4343b6..3645667737 100644 --- a/Content.IntegrationTests/Tests/Vending/VendingInteractionTest.cs +++ b/Content.IntegrationTests/Tests/Vending/VendingInteractionTest.cs @@ -5,6 +5,7 @@ using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; using Content.Shared.VendingMachines; +using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Vending; @@ -17,6 +18,7 @@ public sealed class VendingInteractionTest : InteractionTest private const string RestockBoxProtoId = "InteractionTestRestockBox"; private const string RestockBoxOtherProtoId = "InteractionTestRestockBoxOther"; + private static readonly ProtoId TestDamageType = "Blunt"; [TestPrototypes] private const string TestPrototypes = $@" @@ -196,7 +198,7 @@ public sealed class VendingInteractionTest : InteractionTest Assert.That(damageableComp.Damage.GetTotal(), Is.EqualTo(FixedPoint2.Zero), $"{VendingMachineProtoId} started with unexpected damage."); // Damage the vending machine to the point that it breaks - var damageType = ProtoMan.Index ("Blunt"); + var damageType = ProtoMan.Index(TestDamageType); var damage = new DamageSpecifier(damageType, FixedPoint2.New(100)); await Server.WaitPost(() => damageableSys.TryChangeDamage(SEntMan.GetEntity(Target), damage, ignoreResistances: true)); await RunTicks(5); diff --git a/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs b/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs index 4644768901..f30eed0651 100644 --- a/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs +++ b/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs @@ -20,6 +20,8 @@ namespace Content.IntegrationTests.Tests [TestOf(typeof(VendingMachineSystem))] public sealed class VendingMachineRestockTest : EntitySystem { + private static readonly ProtoId TestDamageType = "Blunt"; + [TestPrototypes] private const string Prototypes = @" - type: entity @@ -293,7 +295,7 @@ namespace Content.IntegrationTests.Tests "Did not start with zero ramen."); restock = entityManager.SpawnEntity("TestRestockExplode", coordinates); - var damageSpec = new DamageSpecifier(prototypeManager.Index ("Blunt"), 100); + var damageSpec = new DamageSpecifier(prototypeManager.Index(TestDamageType), 100); var damageResult = damageableSystem.TryChangeDamage(restock, damageSpec); #pragma warning disable NUnit2045 diff --git a/Content.Tests/Shared/DamageTest.cs b/Content.Tests/Shared/DamageTest.cs index 58fa8fbde3..27543e6533 100644 --- a/Content.Tests/Shared/DamageTest.cs +++ b/Content.Tests/Shared/DamageTest.cs @@ -15,6 +15,11 @@ namespace Content.Tests.Shared [TestOf(typeof(DamageGroupPrototype))] public sealed class DamageTest : ContentUnitTest { + private static readonly ProtoId BruteDamageGroup = "Brute"; + private static readonly ProtoId RadiationDamageType = "Radiation"; + private static readonly ProtoId SlashDamageType = "Slash"; + private static readonly ProtoId PiercingDamageType = "Piercing"; + private IPrototypeManager _prototypeManager; private DamageSpecifier _damageSpec; @@ -29,9 +34,9 @@ namespace Content.Tests.Shared _prototypeManager.ResolveResults(); // Create a damage data set - _damageSpec = new(_prototypeManager.Index ("Brute"), 6); - _damageSpec += new DamageSpecifier(_prototypeManager.Index ("Radiation"), 3); - _damageSpec += new DamageSpecifier(_prototypeManager.Index ("Slash"), -1); // already exists in brute + _damageSpec = new(_prototypeManager.Index(BruteDamageGroup), 6); + _damageSpec += new DamageSpecifier(_prototypeManager.Index(RadiationDamageType), 3); + _damageSpec += new DamageSpecifier(_prototypeManager.Index(SlashDamageType), -1); // already exists in brute } //Check that DamageSpecifier will split groups and can do arithmetic operations @@ -100,7 +105,7 @@ namespace Content.Tests.Shared Assert.That(damage, Is.EqualTo(FixedPoint2.New(3))); // Lets also test the constructor with damage types and damage groups works properly. - damageSpec = new(_prototypeManager.Index ("Brute"), 4); + damageSpec = new(_prototypeManager.Index(BruteDamageGroup), 4); Assert.That(damageSpec.DamageDict.TryGetValue("Blunt", out damage)); Assert.That(damage, Is.EqualTo(FixedPoint2.New(1.33))); Assert.That(damageSpec.DamageDict.TryGetValue("Slash", out damage)); @@ -108,7 +113,7 @@ namespace Content.Tests.Shared Assert.That(damageSpec.DamageDict.TryGetValue("Piercing", out damage)); Assert.That(damage, Is.EqualTo(FixedPoint2.New(1.34))); // doesn't divide evenly, so the 0.01 goes to the last one - damageSpec = new(_prototypeManager.Index ("Piercing"), 4); + damageSpec = new(_prototypeManager.Index(PiercingDamageType), 4); Assert.That(damageSpec.DamageDict.TryGetValue("Piercing", out damage)); Assert.That(damage, Is.EqualTo(FixedPoint2.New(4))); } @@ -121,7 +126,7 @@ namespace Content.Tests.Shared DamageSpecifier damageSpec = 10 * new DamageSpecifier(_damageSpec); // Create a modifier set - var modifierSet = _prototypeManager.Index ("ModifierTestSet"); + var modifierSet = _prototypeManager.Index (ModifierTestSetId); //damage is initially 20 / 20 / 10 / 30 //Each time we subtract -5 / 0 / 8 / 0.5 @@ -142,8 +147,10 @@ namespace Content.Tests.Shared Assert.That(damageSpec.DamageDict["Radiation"], Is.EqualTo(FixedPoint2.New(65.62))); } + private const string ModifierTestSetId = "ModifierTestSet"; + // Default damage Yaml - private string _damagePrototypes = @" + private readonly string _damagePrototypes = $@" - type: damageType id: Blunt name: damage-type-blunt @@ -268,7 +275,7 @@ namespace Content.Tests.Shared Blunt: 5 - type: damageModifierSet - id: ModifierTestSet + id: {ModifierTestSetId} coefficients: Piercing: -2 Slash: 3 diff --git a/Content.Tests/Shared/LocalizedDatasetPrototypeTest.cs b/Content.Tests/Shared/LocalizedDatasetPrototypeTest.cs index b07b18efa0..77f001bfe1 100644 --- a/Content.Tests/Shared/LocalizedDatasetPrototypeTest.cs +++ b/Content.Tests/Shared/LocalizedDatasetPrototypeTest.cs @@ -12,6 +12,8 @@ namespace Content.Tests.Shared; [TestOf(typeof(LocalizedDatasetPrototype))] public sealed class LocalizedDatasetPrototypeTest : ContentUnitTest { + private const string TestDatasetId = "Test"; + private IPrototypeManager _prototypeManager; [OneTimeSetUp] @@ -24,9 +26,9 @@ public sealed class LocalizedDatasetPrototypeTest : ContentUnitTest _prototypeManager.ResolveResults(); } - private const string TestPrototypes = @" + private const string TestPrototypes = $@" - type: localizedDataset - id: Test + id: {TestDatasetId} values: prefix: test-dataset- count: 4 @@ -35,7 +37,7 @@ public sealed class LocalizedDatasetPrototypeTest : ContentUnitTest [Test] public void LocalizedDatasetTest() { - var testPrototype = _prototypeManager.Index ("Test"); + var testPrototype = _prototypeManager.Index (TestDatasetId); var values = new ValueList (); foreach (var value in testPrototype.Values) { From f09bade8e783bf36bb94718759d5b427ae07bec3 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Fri, 4 Jul 2025 16:55:45 -0400 Subject: [PATCH 018/214] Validate remaining `ProtoId` strings (#38747) Validate remaining ProtoId strings --- .../Access/UI/GroupedAccessLevelChecklist.xaml.cs | 4 +++- Content.Client/Delivery/DeliveryVisualizerSystem.cs | 2 +- Content.Server/Botany/Systems/MutationSystem.cs | 4 +++- Content.Server/Cluwne/CluwneSystem.cs | 4 +++- .../Behaviors/WeightedSpawnEntityBehavior.cs | 4 +++- Content.Server/EntityEffects/EntityEffectSystem.cs | 4 +++- .../Gateway/Systems/GatewayGeneratorSystem.cs | 11 ++++++----- Content.Server/Ghost/GhostSystem.cs | 3 ++- .../Salvage/SalvageSystem.ExpeditionConsole.cs | 6 +++--- Content.Server/Salvage/SpawnSalvageMissionJob.cs | 2 +- .../StationEvents/Events/RandomSentienceRule.cs | 7 +++++-- Content.Shared/Body/Systems/SharedBodySystem.Parts.cs | 4 +++- Content.Shared/Chat/SharedSuicideSystem.cs | 6 ++++-- Content.Shared/Humanoid/NamingSystem.cs | 6 ++++-- 14 files changed, 44 insertions(+), 23 deletions(-) diff --git a/Content.Client/Access/UI/GroupedAccessLevelChecklist.xaml.cs b/Content.Client/Access/UI/GroupedAccessLevelChecklist.xaml.cs index da68653ce5..4f07c31009 100644 --- a/Content.Client/Access/UI/GroupedAccessLevelChecklist.xaml.cs +++ b/Content.Client/Access/UI/GroupedAccessLevelChecklist.xaml.cs @@ -15,6 +15,8 @@ namespace Content.Client.Access.UI; [GenerateTypedNameReferences] public sealed partial class GroupedAccessLevelChecklist : BoxContainer { + private static readonly ProtoId GeneralAccessGroup = "General"; + [Dependency] private readonly IPrototypeManager _protoManager = default!; private bool _isMonotone; @@ -63,7 +65,7 @@ public sealed partial class GroupedAccessLevelChecklist : BoxContainer // Ensure that the 'general' access group is added to handle // misc. access levels that aren't associated with any group - if (_protoManager.TryIndex ("General", out var generalAccessProto)) + if (_protoManager.TryIndex(GeneralAccessGroup, out var generalAccessProto)) _groupedAccessLevels.TryAdd(generalAccessProto, new()); // Assign known access levels with their associated groups diff --git a/Content.Client/Delivery/DeliveryVisualizerSystem.cs b/Content.Client/Delivery/DeliveryVisualizerSystem.cs index 846688149f..d5afd34533 100644 --- a/Content.Client/Delivery/DeliveryVisualizerSystem.cs +++ b/Content.Client/Delivery/DeliveryVisualizerSystem.cs @@ -24,7 +24,7 @@ public sealed class DeliveryVisualizerSystem : VisualizerSystem (job, out var icon)) { - SpriteSystem.LayerSetTexture((uid, args.Sprite), DeliveryVisualLayers.JobStamp, SpriteSystem.Frame0(_prototype.Index("JobIconUnknown"))); + SpriteSystem.LayerSetTexture((uid, args.Sprite), DeliveryVisualLayers.JobStamp, SpriteSystem.Frame0(_prototype.Index(UnknownIcon).Icon)); return; } diff --git a/Content.Server/Botany/Systems/MutationSystem.cs b/Content.Server/Botany/Systems/MutationSystem.cs index 37b68d9475..ee35db48e3 100644 --- a/Content.Server/Botany/Systems/MutationSystem.cs +++ b/Content.Server/Botany/Systems/MutationSystem.cs @@ -9,13 +9,15 @@ namespace Content.Server.Botany; public sealed class MutationSystem : EntitySystem { + private static ProtoId RandomPlantMutations = "RandomPlantMutations"; + [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; private RandomPlantMutationListPrototype _randomMutations = default!; public override void Initialize() { - _randomMutations = _prototypeManager.Index ("RandomPlantMutations"); + _randomMutations = _prototypeManager.Index(RandomPlantMutations); } /// diff --git a/Content.Server/Cluwne/CluwneSystem.cs b/Content.Server/Cluwne/CluwneSystem.cs index 73476ece17..2bad4e0b96 100644 --- a/Content.Server/Cluwne/CluwneSystem.cs +++ b/Content.Server/Cluwne/CluwneSystem.cs @@ -21,6 +21,8 @@ namespace Content.Server.Cluwne; public sealed class CluwneSystem : EntitySystem { + private static readonly ProtoId public sealed class NamingSystem : EntitySystem { + private static readonly ProtoIdGeneticDamageGroup = "Genetic"; + [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; @@ -53,7 +55,7 @@ public sealed class CluwneSystem : EntitySystem RemComp (uid); RemComp (uid); RemComp (uid); - var damageSpec = new DamageSpecifier(_prototypeManager.Index ("Genetic"), 300); + var damageSpec = new DamageSpecifier(_prototypeManager.Index(GeneticDamageGroup), 300); _damageableSystem.TryChangeDamage(uid, damageSpec); } } diff --git a/Content.Server/Destructible/Thresholds/Behaviors/WeightedSpawnEntityBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/WeightedSpawnEntityBehavior.cs index e02ed87322..96fa4dd438 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/WeightedSpawnEntityBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/WeightedSpawnEntityBehavior.cs @@ -19,6 +19,8 @@ namespace Content.Server.Destructible.Thresholds.Behaviors; [DataDefinition] public sealed partial class WeightedSpawnEntityBehavior : IThresholdBehavior { + private static readonly EntProtoId TempEntityProtoId = "TemporaryEntityForTimedDespawnSpawners"; + /// /// A table of entities with assigned weights to randomly pick from /// @@ -64,7 +66,7 @@ public sealed partial class WeightedSpawnEntityBehavior : IThresholdBehavior if (SpawnAfter != 0) { // if it fails to get the spawner, this won't ever work so just return - if (!system.PrototypeManager.TryIndex("TemporaryEntityForTimedDespawnSpawners", out var tempSpawnerProto)) + if (!system.PrototypeManager.TryIndex(TempEntityProtoId, out var tempSpawnerProto)) return; // spawn the spawner, assign it a lifetime, and assign the entity that it will spawn when despawned diff --git a/Content.Server/EntityEffects/EntityEffectSystem.cs b/Content.Server/EntityEffects/EntityEffectSystem.cs index e18b3b1470..54270ca53d 100644 --- a/Content.Server/EntityEffects/EntityEffectSystem.cs +++ b/Content.Server/EntityEffects/EntityEffectSystem.cs @@ -48,6 +48,8 @@ namespace Content.Server.EntityEffects; public sealed class EntityEffectSystem : EntitySystem { + private static readonly ProtoIdRandomPickBotanyReagent = "RandomPickBotanyReagent"; + [Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly BloodstreamSystem _bloodstream = default!; [Dependency] private readonly ChatSystem _chat = default!; @@ -854,7 +856,7 @@ public sealed class EntityEffectSystem : EntitySystem return; var chemicals = plantholder.Seed.Chemicals; - var randomChems = _protoManager.Index ("RandomPickBotanyReagent").Fills; + var randomChems = _protoManager.Index(RandomPickBotanyReagent).Fills; // Add a random amount of a random chemical to this set of chemicals if (randomChems != null) diff --git a/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs b/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs index 83471cdbc1..a88cf6b428 100644 --- a/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs +++ b/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs @@ -36,8 +36,9 @@ public sealed class GatewayGeneratorSystem : EntitySystem [Dependency] private readonly SharedSalvageSystem _salvage = default!; [Dependency] private readonly TileSystem _tile = default!; - [ValidatePrototypeId ] - private const string PlanetNames = "NamesBorer"; + private static readonly ProtoId PlanetNames = "NamesBorer"; + private static readonly ProtoId BiomeTemplate = "Continental"; + private static readonly ProtoId DungeonConfig = "Experiment"; // TODO: // Fix shader some more @@ -101,7 +102,7 @@ public sealed class GatewayGeneratorSystem : EntitySystem var random = new Random(seed); var mapUid = _maps.CreateMap(); - var gatewayName = _salvage.GetFTLName(_protoManager.Index (PlanetNames), seed); + var gatewayName = _salvage.GetFTLName(_protoManager.Index(PlanetNames), seed); _metadata.SetEntityName(mapUid, gatewayName); var origin = new Vector2i(random.Next(-MaxOffset, MaxOffset), random.Next(-MaxOffset, MaxOffset)); @@ -111,7 +112,7 @@ public sealed class GatewayGeneratorSystem : EntitySystem }; AddComp(mapUid, restricted); - _biome.EnsurePlanet(mapUid, _protoManager.Index ("Continental"), seed); + _biome.EnsurePlanet(mapUid, _protoManager.Index(BiomeTemplate), seed); var grid = Comp (mapUid); @@ -183,7 +184,7 @@ public sealed class GatewayGeneratorSystem : EntitySystem var dungeonRotation = _dungeon.GetDungeonRotation(seed); var dungeonPosition = (origin + dungeonRotation.RotateVec(new Vector2i(0, dungeonDistance))).Floored(); - _dungeon.GenerateDungeon(_protoManager.Index ("Experiment"), args.MapUid, grid, dungeonPosition, seed); + _dungeon.GenerateDungeon(_protoManager.Index(DungeonConfig), args.MapUid, grid, dungeonPosition, seed); // TODO: Dungeon mobs + loot. diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 33de2c6f39..3e09d86189 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -74,6 +74,7 @@ namespace Content.Server.Ghost private EntityQuery _physicsQuery; private static readonly ProtoId AllowGhostShownByEventTag = "AllowGhostShownByEvent"; + private static readonly ProtoId AsphyxiationDamageType = "Asphyxiation"; public override void Initialize() { @@ -585,7 +586,7 @@ namespace Content.Server.Ghost dealtDamage = playerDeadThreshold - damageable.TotalDamage; } - DamageSpecifier damage = new(_prototypeManager.Index ("Asphyxiation"), dealtDamage); + DamageSpecifier damage = new(_prototypeManager.Index(AsphyxiationDamageType), dealtDamage); _damageable.TryChangeDamage(playerEntity, damage, true); } diff --git a/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs b/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs index a9d8314f57..aa501368de 100644 --- a/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs +++ b/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs @@ -8,8 +8,8 @@ namespace Content.Server.Salvage; public sealed partial class SalvageSystem { - [ValidatePrototypeId ] - public const string CoordinatesDisk = "CoordinatesDisk"; + public static readonly EntProtoId CoordinatesDisk = "CoordinatesDisk"; + public static readonly ProtoId PlanetNames = "NamesBorer"; private void OnSalvageClaimMessage(EntityUid uid, SalvageExpeditionConsoleComponent component, ClaimSalvageMessage args) { @@ -28,7 +28,7 @@ public sealed partial class SalvageSystem var mission = GetMission(_prototypeManager.Index (missionparams.Difficulty), missionparams.Seed); data.NextOffer = _timing.CurTime + mission.Duration + TimeSpan.FromSeconds(1); - _labelSystem.Label(cdUid, GetFTLName(_prototypeManager.Index ("NamesBorer"), missionparams.Seed)); + _labelSystem.Label(cdUid, GetFTLName(_prototypeManager.Index(PlanetNames), missionparams.Seed)); _audio.PlayPvs(component.PrintSound, uid); UpdateConsoles((station.Value, data)); diff --git a/Content.Server/Salvage/SpawnSalvageMissionJob.cs b/Content.Server/Salvage/SpawnSalvageMissionJob.cs index cbce4dc692..8b5e9898ad 100644 --- a/Content.Server/Salvage/SpawnSalvageMissionJob.cs +++ b/Content.Server/Salvage/SpawnSalvageMissionJob.cs @@ -100,7 +100,7 @@ public sealed class SpawnSalvageMissionJob : Job destComp.Enabled = true; _metaData.SetEntityName( mapUid, - _entManager.System ().GetFTLName(_prototypeManager.Index ("NamesBorer"), _missionParams.Seed)); + _entManager.System ().GetFTLName(_prototypeManager.Index(SalvageSystem.PlanetNames), _missionParams.Seed)); _entManager.AddComponent (mapUid); // Saving the mission mapUid to a CD is made optional, in case one is somehow made in a process without a CD entity diff --git a/Content.Server/StationEvents/Events/RandomSentienceRule.cs b/Content.Server/StationEvents/Events/RandomSentienceRule.cs index 3d2e457a34..359c3edae4 100644 --- a/Content.Server/StationEvents/Events/RandomSentienceRule.cs +++ b/Content.Server/StationEvents/Events/RandomSentienceRule.cs @@ -11,6 +11,9 @@ namespace Content.Server.StationEvents.Events; public sealed class RandomSentienceRule : StationEventSystem { + private static readonly ProtoId DataSourceNames = "RandomSentienceEventData"; + private static readonly ProtoId IntelligenceLevelNames = "RandomSentienceEventStrength"; + [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IRobustRandom _random = default!; protected override void Started(EntityUid uid, RandomSentienceRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) @@ -72,8 +75,8 @@ public sealed class RandomSentienceRule : StationEventSystem ("RandomSentienceEventData"))), - ("strength", _random.Pick(_prototype.Index ("RandomSentienceEventStrength"))) + ("data", _random.Pick(_prototype.Index(DataSourceNames))), + ("strength", _random.Pick(_prototype.Index(IntelligenceLevelNames))) ), playDefaultSound: false, colorOverride: Color.Gold diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs index 0917197e29..78d270ddc9 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs @@ -8,12 +8,14 @@ using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.Movement.Components; using Robust.Shared.Containers; +using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Shared.Body.Systems; public partial class SharedBodySystem { + private static readonly ProtoId BloodlossDamageType = "Bloodloss"; private void InitializeParts() { // TODO: This doesn't handle comp removal on child ents. @@ -178,7 +180,7 @@ public partial class SharedBodySystem ) { // TODO BODY SYSTEM KILL : remove this when wounding and required parts are implemented properly - var damage = new DamageSpecifier(Prototypes.Index ("Bloodloss"), 300); + var damage = new DamageSpecifier(Prototypes.Index(BloodlossDamageType), 300); Damageable.TryChangeDamage(bodyEnt, damage); } } diff --git a/Content.Shared/Chat/SharedSuicideSystem.cs b/Content.Shared/Chat/SharedSuicideSystem.cs index d341ea89a8..4b9eaf24b7 100644 --- a/Content.Shared/Chat/SharedSuicideSystem.cs +++ b/Content.Shared/Chat/SharedSuicideSystem.cs @@ -8,6 +8,8 @@ namespace Content.Shared.Chat; public sealed class SharedSuicideSystem : EntitySystem { + private static readonly ProtoId FallbackDamageType = "Blunt"; + [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; @@ -57,8 +59,8 @@ public sealed class SharedSuicideSystem : EntitySystem // We don't want structural damage for the same reasons listed above if (!_prototypeManager.TryIndex(damageType, out var damagePrototype) || damagePrototype.ID == "Structural") { - Log.Error($"{nameof(SharedSuicideSystem)} could not find the damage type prototype associated with {damageType}. Falling back to Blunt"); - damagePrototype = _prototypeManager.Index ("Blunt"); + Log.Error($"{nameof(SharedSuicideSystem)} could not find the damage type prototype associated with {damageType}. Falling back to {FallbackDamageType}"); + damagePrototype = _prototypeManager.Index(FallbackDamageType); } var damage = new DamageSpecifier(damagePrototype, lethalAmountOfDamage); diff --git a/Content.Shared/Humanoid/NamingSystem.cs b/Content.Shared/Humanoid/NamingSystem.cs index a2085c6692..98afb34015 100644 --- a/Content.Shared/Humanoid/NamingSystem.cs +++ b/Content.Shared/Humanoid/NamingSystem.cs @@ -12,6 +12,8 @@ namespace Content.Shared.Humanoid /// FallbackSpecies = "Human"; + [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; @@ -21,8 +23,8 @@ namespace Content.Shared.Humanoid // Some downstream is probably gonna have this eventually but then they can deal with fallbacks. if (!_prototypeManager.TryIndex(species, out SpeciesPrototype? speciesProto)) { - speciesProto = _prototypeManager.Index ("Human"); - Log.Warning($"Unable to find species {species} for name, falling back to Human"); + speciesProto = _prototypeManager.Index(FallbackSpecies); + Log.Warning($"Unable to find species {species} for name, falling back to {FallbackSpecies}"); } switch (speciesProto.Naming) From 4905e097c8947e2384b0243d920567e08f0e6c3c Mon Sep 17 00:00:00 2001 From: Perry Fraser Date: Fri, 4 Jul 2025 17:50:44 -0400 Subject: [PATCH 019/214] feat: allow mopping evaporating puddles (#38743) --- .../Fluids/EntitySystems/AbsorbentSystem.cs | 20 ++++++++++++------- .../EntitySystems/PuddleSystem.Evaporation.cs | 1 + .../fluids/components/absorbent-component.ftl | 4 ++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs b/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs index 1177c24304..28c36602e1 100644 --- a/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs +++ b/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs @@ -277,17 +277,22 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem if (!_solutionContainerSystem.ResolveSolution(target, puddle.SolutionName, ref puddle.Solution, out var puddleSolution) || puddleSolution.Volume <= 0) return false; - // Check if the puddle has any non-evaporative reagents - if (_puddleSystem.CanFullyEvaporate(puddleSolution)) - { - _popups.PopupEntity(Loc.GetString("mopping-system-puddle-evaporate", ("target", target)), user, user); - return true; - } - Solution puddleSplit; var isRemoved = false; if (absorber.UseAbsorberSolution) { + // No reason to mop something that 1) can evaporate, 2) is an absorber, and 3) is being mopped with + // something that uses absorbers. + var puddleAbsorberVolume = + puddleSolution.GetTotalPrototypeQuantity(_puddleSystem.GetAbsorbentReagents(puddleSolution)); + if (puddleAbsorberVolume == puddleSolution.Volume) + { + _popups.PopupEntity(Loc.GetString("mopping-system-puddle-already-mopped", ("target", target)), + user, + user); + return true; + } + // Check if we have any evaporative reagents on our absorber to transfer var absorberSolution = absorberSoln.Comp.Solution; var available = absorberSolution.GetTotalPrototypeQuantity(_puddleSystem.GetAbsorbentReagents(absorberSolution)); @@ -317,6 +322,7 @@ public sealed class AbsorbentSystem : SharedAbsorbentSystem } else { + // Note: arguably shouldn't this get all solutions? puddleSplit = puddleSolution.SplitSolutionWithout(absorber.PickupAmount, _puddleSystem.GetAbsorbentReagents(puddleSolution)); // Despawn if we're done if (puddleSolution.Volume == FixedPoint2.Zero) diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.Evaporation.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.Evaporation.cs index f92504e74c..245ab8308f 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.Evaporation.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.Evaporation.cs @@ -46,6 +46,7 @@ public sealed partial class PuddleSystem if (!_solutionContainerSystem.ResolveSolution(uid, puddle.SolutionName, ref puddle.Solution, out var puddleSolution)) continue; + // Yes, this means that 50u water + 50u holy water evaporates twice as fast as 100u water. foreach ((string evaporatingReagent, FixedPoint2 evaporatingSpeed) in GetEvaporationSpeeds(puddleSolution)) { var reagentTick = evaporation.EvaporationAmount * EvaporationCooldown.TotalSeconds * evaporatingSpeed; diff --git a/Resources/Locale/en-US/fluids/components/absorbent-component.ftl b/Resources/Locale/en-US/fluids/components/absorbent-component.ftl index 51e500a6fe..d6cb60e25e 100644 --- a/Resources/Locale/en-US/fluids/components/absorbent-component.ftl +++ b/Resources/Locale/en-US/fluids/components/absorbent-component.ftl @@ -1,7 +1,7 @@ mopping-system-target-container-empty = { CAPITALIZE(THE($target)) } is empty! mopping-system-target-container-empty-water = { CAPITALIZE(THE($target)) } has no water! -mopping-system-puddle-space = { CAPITALIZE(THE($used)) } is full of water -mopping-system-puddle-evaporate = { CAPITALIZE(THE($target)) } is evaporating +mopping-system-puddle-space = { CAPITALIZE(THE($used)) } is full of water. +mopping-system-puddle-already-mopped = { CAPITALIZE(THE($target)) } is already mopped. mopping-system-no-water = { CAPITALIZE(THE($used)) } has no water! mopping-system-no-hands = You have no hands! From a4ac8eec82728f636fbf77db7e6931f6dfe71551 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 4 Jul 2025 21:51:51 +0000 Subject: [PATCH 020/214] 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 705d1e7d4f..3ea47a96ae 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Fildrance - changes: - - message: returned deconstruct to the top level option on RCD - type: Fix - id: 8226 - time: '2025-04-18T04:50:15.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36486 - author: ciaran changes: - message: Added a button to scroll back to the bottom of the chat box @@ -3888,3 +3881,11 @@ id: 8737 time: '2025-07-04T05:08:45.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38664 +- author: perryprog + changes: + - message: You can now mop space lube, letting it evaporate faster than it would + on its own. + type: Tweak + id: 8738 + time: '2025-07-04T21:50:44.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38743 From b36aedde6bfed7362d4119e2be23d1cf6a675c3a Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sat, 5 Jul 2025 00:19:31 +0200 Subject: [PATCH 021/214] Cleanup warning in StomachSystem (#38748) you did not see this --- Content.Shared/Body/Systems/StomachSystem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Content.Shared/Body/Systems/StomachSystem.cs b/Content.Shared/Body/Systems/StomachSystem.cs index d0ecb6b93b..8b2df453a0 100644 --- a/Content.Shared/Body/Systems/StomachSystem.cs +++ b/Content.Shared/Body/Systems/StomachSystem.cs @@ -1,7 +1,6 @@ using Content.Shared.Body.Components; using Content.Shared.Body.Events; using Content.Shared.Body.Organ; -using Content.Shared.Body.Events; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.EntitySystems; From edbde7b7b99d0286fc01015a3a2c05491115b371 Mon Sep 17 00:00:00 2001 From: Mora <46364955+TrixxedHeart@users.noreply.github.com> Date: Fri, 4 Jul 2025 18:06:55 -0500 Subject: [PATCH 022/214] Vox scars (#38592) * Added vox scars n'stuff, renamed vox_tattoos.ftl to just vox.ftl * Revert "Added vox scars n'stuff, renamed vox_tattoos.ftl to just vox.ftl" This reverts commit c73da55ba3b39ddf93b493aecd85604c54dd8a15. * locale key fix * Changed top surgery scar names to be more generalized * Adjusted face scars * Formatting fixes --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> --- Resources/Locale/en-US/markings/vox.ftl | 47 +++++++ .../Locale/en-US/markings/vox_tattoos.ftl | 11 -- .../Mobs/Customization/Markings/vox_scars.yml | 118 ++++++++++++++++++ .../Customization/vox_scars.rsi/meta.json | 59 +++++++++ .../vox_scars.rsi/vox_scar_chest.png | Bin 0 -> 3165 bytes .../vox_scars.rsi/vox_scar_chest_bullets.png | Bin 0 -> 3065 bytes .../vox_scars.rsi/vox_scar_eye_left.png | Bin 0 -> 3011 bytes .../vox_scars.rsi/vox_scar_eye_left_small.png | Bin 0 -> 2992 bytes .../vox_scars.rsi/vox_scar_eye_right.png | Bin 0 -> 3007 bytes .../vox_scar_eye_right_small.png | Bin 0 -> 2993 bytes .../vox_scars.rsi/vox_scar_face_1.png | Bin 0 -> 3244 bytes .../vox_scars.rsi/vox_scar_face_2.png | Bin 0 -> 3126 bytes .../vox_scars.rsi/vox_scar_neck.png | Bin 0 -> 2978 bytes .../vox_scar_stomach_bullets.png | Bin 0 -> 3028 bytes .../vox_scars.rsi/vox_top_surgery_long.png | Bin 0 -> 3002 bytes .../vox_scars.rsi/vox_top_surgery_short.png | Bin 0 -> 3005 bytes 16 files changed, 224 insertions(+), 11 deletions(-) create mode 100644 Resources/Locale/en-US/markings/vox.ftl delete mode 100644 Resources/Locale/en-US/markings/vox_tattoos.ftl create mode 100644 Resources/Prototypes/Entities/Mobs/Customization/Markings/vox_scars.yml create mode 100644 Resources/Textures/Mobs/Customization/vox_scars.rsi/meta.json create mode 100644 Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_chest.png create mode 100644 Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_chest_bullets.png create mode 100644 Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_eye_left.png create mode 100644 Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_eye_left_small.png create mode 100644 Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_eye_right.png create mode 100644 Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_eye_right_small.png create mode 100644 Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_face_1.png create mode 100644 Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_face_2.png create mode 100644 Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_neck.png create mode 100644 Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_stomach_bullets.png create mode 100644 Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_top_surgery_long.png create mode 100644 Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_top_surgery_short.png diff --git a/Resources/Locale/en-US/markings/vox.ftl b/Resources/Locale/en-US/markings/vox.ftl new file mode 100644 index 0000000000..37502bc7e0 --- /dev/null +++ b/Resources/Locale/en-US/markings/vox.ftl @@ -0,0 +1,47 @@ +marking-TattooVoxHeartLeftArm-heart_l_arm = Vox Left Arm Tattoo (Heart) +marking-TattooVoxHeartLeftArm = Vox Left Arm Tattoo (Heart) + +marking-TattooVoxHeartRightArm-heart_r_arm = Vox Right Arm Tattoo (Heart) +marking-TattooVoxHeartRightArm = Vox Right Arm Tattoo (Heart) + +marking-TattooVoxHiveChest-hive_s = Vox Chest Tattoo (hive) +marking-TattooVoxHiveChest = Vox Chest Tattoo (hive) + +marking-TattooVoxNightlingChest-nightling_s = Vox Chest Tattoo (nightling) +marking-TattooVoxNightlingChest = Vox Chest Tattoo (nightling) + +marking-VoxScarEyeRight-vox_scar_eye_right = Right Eye Scar +marking-VoxScarEyeRight = Eye Scar (Right) + +marking-VoxScarEyeLeft-vox_scar_eye_left = Left Eye Scar +marking-VoxScarEyeLeft = Eye Scar (Left) + +marking-VoxScarTopSurgeryShort-vox_scar_top_surgery_short = Thoracotomy Scar +marking-VoxScarTopSurgeryShort = Thoracotomy Scar + +marking-VoxScarTopSurgeryLong-vox_scar_top_surgery_long = Clamshell Scar +marking-VoxScarTopSurgeryLong = Clamshell Scar + +marking-VoxScarChest-vox_scar_chest = Chest Scar +marking-VoxScarChest = Chest Scar + +marking-VoxScarNeck-vox_scar_neck = Neck Scar +marking-VoxScarNeck = Neck Scar + +marking-VoxScarChestBullets-vox_scar_chest_bullets = Bullet Holes Scar +marking-VoxScarChestBullets = Chest Scar (Bullets) + +marking-VoxScarStomachBullets-vox_scar_stomach_bullets = Bullet Holes Scar +marking-VoxScarStomachBullets = Stomach Scar (Bullets) + +marking-VoxScarFace1-vox_scar_face_1 = Face Scar +marking-VoxScarFace1 = Face Scar (Large) + +marking-VoxScarFace2-vox_scar_face_2 = Face Scar +marking-VoxScarFace2 = Face Scar (Small) + +marking-VoxScarEyeRightSmall-vox_scar_eye_right_small = Right Eye Scar (Small) +marking-VoxScarEyeRightSmall = Small Eye Scar (Right) + +marking-VoxScarEyeLeftSmall-vox_scar_eye_left_small = Left Eye Scar (Small) +marking-VoxScarEyeLeftSmall = Small Eye Scar (Left) \ No newline at end of file diff --git a/Resources/Locale/en-US/markings/vox_tattoos.ftl b/Resources/Locale/en-US/markings/vox_tattoos.ftl deleted file mode 100644 index f7f3c7292c..0000000000 --- a/Resources/Locale/en-US/markings/vox_tattoos.ftl +++ /dev/null @@ -1,11 +0,0 @@ -marking-TattooVoxHeartLeftArm-heart_l_arm = Vox Left Arm Tattoo (Heart) -marking-TattooVoxHeartLeftArm = Vox Left Arm Tattoo (Heart) - -marking-TattooVoxHeartRightArm-heart_r_arm = Vox Right Arm Tattoo (Heart) -marking-TattooVoxHeartRightArm = Vox Right Arm Tattoo (Heart) - -marking-TattooVoxHiveChest-hive_s = Vox Chest Tattoo (hive) -marking-TattooVoxHiveChest = Vox Chest Tattoo (hive) - -marking-TattooVoxNightlingChest-nightling_s = Vox Chest Tattoo (nightling) -marking-TattooVoxNightlingChest = Vox Chest Tattoo (nightling) diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/vox_scars.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/vox_scars.yml new file mode 100644 index 0000000000..5998ddc237 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/vox_scars.yml @@ -0,0 +1,118 @@ +- type: marking + id: VoxScarEyeRight + bodyPart: Head + markingCategory: Head + speciesRestriction: [Vox] + sprites: + - sprite: Mobs/Customization/vox_scars.rsi + state: vox_scar_eye_right + +- type: marking + id: VoxScarEyeLeft + bodyPart: Head + markingCategory: Head + speciesRestriction: [Vox] + followSkinColor: true + sprites: + - sprite: Mobs/Customization/vox_scars.rsi + state: vox_scar_eye_left + +- type: marking + id: VoxScarTopSurgeryShort + bodyPart: Chest + markingCategory: Chest + speciesRestriction: [Vox] + followSkinColor: true + sprites: + - sprite: Mobs/Customization/vox_scars.rsi + state: vox_top_surgery_short + +- type: marking + id: VoxScarTopSurgeryLong + bodyPart: Chest + markingCategory: Chest + speciesRestriction: [Vox] + followSkinColor: true + sprites: + - sprite: Mobs/Customization/vox_scars.rsi + state: vox_top_surgery_long + +- type: marking + id: VoxScarChest + bodyPart: Chest + markingCategory: Chest + speciesRestriction: [Vox] + followSkinColor: true + sprites: + - sprite: Mobs/Customization/vox_scars.rsi + state: vox_scar_chest + +- type: marking + id: VoxScarNeck + bodyPart: Head + markingCategory: Head + speciesRestriction: [Vox] + followSkinColor: true + sprites: + - sprite: Mobs/Customization/vox_scars.rsi + state: vox_scar_neck + +- type: marking + id: VoxScarChestBullets + bodyPart: Chest + markingCategory: Chest + speciesRestriction: [Vox] + followSkinColor: true + sprites: + - sprite: Mobs/Customization/vox_scars.rsi + state: vox_scar_chest_bullets + +- type: marking + id: VoxScarStomachBullets + bodyPart: Chest + markingCategory: Chest + speciesRestriction: [Vox] + followSkinColor: true + sprites: + - sprite: Mobs/Customization/vox_scars.rsi + state: vox_scar_stomach_bullets + +- type: marking + id: VoxScarFace1 + bodyPart: Head + markingCategory: Head + speciesRestriction: [Vox] + followSkinColor: true + sprites: + - sprite: Mobs/Customization/vox_scars.rsi + state: vox_scar_face_1 + +- type: marking + id: VoxScarFace2 + bodyPart: Head + markingCategory: Head + speciesRestriction: [Vox] + followSkinColor: true + sprites: + - sprite: Mobs/Customization/vox_scars.rsi + state: vox_scar_face_2 + +- type: marking + id: VoxScarEyeRightSmall + bodyPart: Head + markingCategory: Head + speciesRestriction: [Vox] + followSkinColor: true + sprites: + - sprite: Mobs/Customization/vox_scars.rsi + state: vox_scar_eye_right_small + +- type: marking + id: VoxScarEyeLeftSmall + bodyPart: Head + markingCategory: Head + speciesRestriction: [Vox] + followSkinColor: true + sprites: + - sprite: Mobs/Customization/vox_scars.rsi + state: vox_scar_eye_left_small \ No newline at end of file diff --git a/Resources/Textures/Mobs/Customization/vox_scars.rsi/meta.json b/Resources/Textures/Mobs/Customization/vox_scars.rsi/meta.json new file mode 100644 index 0000000000..7e74ffb8b6 --- /dev/null +++ b/Resources/Textures/Mobs/Customization/vox_scars.rsi/meta.json @@ -0,0 +1,59 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "vox_scar_chest, vox_scar_eye_left, vox_scar_eye_right, vox_scar_top_surgery_long and vox_scar_top_surgery_short drawn by Ubaser, vox_scar_neck, vox_scar_chest_bullets, vox_scar_stomach_bullets, vox_scar_face_1 and vox_scar_face_2 originally drawn by Ktyria(discord) edited by TrixxedHeart, vox_scar_eye_left_small and vox_scar_eye_right_small modified from vox_scar_eye_left and vox_scar_eye_right originally by Boaz1111(github) edited by TrixxedHeart.", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "vox_scar_eye_left", + "directions": 4 + }, + { + "name": "vox_scar_eye_right", + "directions": 4 + }, + { + "name": "vox_top_surgery_short", + "directions": 4 + }, + { + "name": "vox_top_surgery_long", + "directions": 4 + }, + { + "name": "vox_scar_chest", + "directions": 4 + }, + { + "name": "vox_scar_neck", + "directions": 4 + }, + { + "name": "vox_scar_chest_bullets", + "directions": 4 + }, + { + "name": "vox_scar_stomach_bullets", + "directions": 4 + }, + { + "name": "vox_scar_face_1", + "directions": 4 + }, + { + "name": "vox_scar_face_2", + "directions": 4 + }, + { + "name": "vox_scar_eye_left_small", + "directions": 4 + }, + { + "name": "vox_scar_eye_right_small", + "directions": 4 + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_chest.png b/Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_chest.png new file mode 100644 index 0000000000000000000000000000000000000000..59c04641bd2ffcdbd570284357a144218536360f GIT binary patch literal 3165 zcmV-j45IUiP) f6 zXi@@54ZTQ_E-Enz5K6$103tR-RB%L5k){YTDBysjLy@r}iiH7DvFijGMAUI`6dRUF zWUU$Bym{} eS9U O(Z2>7`&z9wUXbV-Il#&6`Y8GKGQ04S2&F6MJnWNa;Ck|;8QE#r9r;7G||@X{|> z%+C|c55>;RS}qbKr-&IQTvLXPlM{>K&(BTgi^a?^4mXV>;xX8n8Ce|RasXz}{8imI52H3ZN4bfe_i~WlJ|C&UW9+{8AKoW!}eExnGFE2re(F+`iE_46#!l9 z0Z_aBhs|Iw0E)7{bq;-T9=d#9QpDmcXDh4R++0fmpKB>E= %LdZt9g$j;($`3&Zthxi`{{&gM}5&R^+h%b~yM9Zd3AWW9ETgVfL z1(`yIK=_}U_z%PWq}jQaiQ4!P(3V&Nr6C$XejWfQDiI(Fdt@un?|lo#M+5oIi_w{w zo%_#%{(V=tO#a9gB!7-$M?^BX5>d|Vn*3S !?g~$*UQipUPL&zMmg; !4Do9IA%up=Rh? z=qPj=x&RGBx1dpI68aT-2O}^EromdU5o`ssU{5#*j)WJ%$?!5bA1;Eoz?E iTr=n?cd`V|I)p<|3Oju?MT93~aB0<#&j8`F+Cg&D?-VWzQItUA^l>xvD z RIYI4MQ`g1<+DyrL=EogS06Xii({| zv`U^zjmmKqDIK93(F5q|^fLNk`gQs{RV`IdRle#b)i%{Ds;|}NsClUI)k@Ub)kf6b zsWa4l)YH_rsduU0(?DsMX@qO!YV6TCtMPOWZH~(v?wpc2hv(eZgf-1HBQ#fN?$aF5 zoYvCT^3%%Fs?s{6^;Da#?V+8jy+iwi_M{F~$4y6|vqR^k&SQoO!;_KDsATjprgSxR z{dFa}^}2()GkV5)QF?`X?Rxk03HmJkB>f%wz4}uIItC#I1qQ7Kw+-=zEW;GTU55RJ zuZ@h2VvIHzbs0S}Rx=JT&Npr~zH34@aW`3J(qMAU6l2OVO*7qXdf5y%vo}jIt1%lg zhs_<#1?IcWhb_<+P8LFo28$a^64R5J!)#@aTGB0pEekEXET35!SjAgyv+B3{Xl-wu zZrx~o$A)4PXj5p@WAm%6nJw40#`fA=@?77!tLJvleQsxN$G6*KchjC~A7a13zSsVP zgQJ7Uq0M2^(ZDg$vDWbhi^d9LZDyT!LOXdmt#&%*^w!zIS?qk+`4< X~g?%56 z2@eae34a)26HyS+zks@6$%2*zuOhu7%OdYYnM6sVdZQJi6QY}=U&naIl*dS8tzuWk zUW(I*6U24LW8 oFzvR(TOpM zEs5_rp_~TJ^wNN(wM(bCZ0;`Z6P^ce2XB(^$}i_nB)KM)Cp}7bP2Qe7nc|*Ok@8f) z7E}wKr~0SXrM^xJP1~RLDLp2=Jp-4Km~m7{5vB?IGPN`FGKaIwvx>8%%bb_(Ts9>N z5;bK**^9Ef#WdN^)PTf9vR*Qp {o-l7 zTcBI8wqSIn=gRt3(5j`YdRObOE?Pal#&6AmwS={4Ykw%TE-Wv6xh`g1Pmxy9nxe7w ze(PI{6^cd0H#WFzsN0CzDA+i-Y3`<~O&?2mB^OJrODjs>Z{}{k_?699m0x|@lC)*8 z%%N=0R?Jr6*6Z8cw;d=~F3&F?+a9vLa|dHb$&Qyhm+ZVyVOLSNi?B >BD~Ee(8aT1AWbo&CM;EEoH56tE6@EV8X%6-*|u1-NtOIZ>P7H z9s-9XhaP{M`0e$>L5F*fu#U8SXZT%h2eqT56Y5;vIn|ZYCGC#u9zGg)w718lr{jCe z@An_mJyvsE<#^c%!il02pHAkVoIaIx>gnm^(__6$dheWxJ#(!uyl?Pq(Ao3ne9xWf z_v}A;-u3*k3(gmgUSwVDy5w-FbHIL};|Kd6ItCpEJBJ*Hx-UCj?irppeBz4xmD5+f zub#UWaP88_{E^}7QP*$YNVp-r$-DXJR{E{yw{vdK+*xxMeYfPE(!GlNn)e%iH2tw% z>L5Kn>ODH}V8MesW8ASPKV|>)e!S=*`C-L`&P4Mg+egPHeJ3wJUif(YN!F8@r^P=j z|6Kdb c>FRj6+1QlT=e|YubW?}zu5oM?q% 0Dy!50Qvv` z0D$NK0Cg|`0P0`>06Lfe02gqax=}m;000SaNLh0L01m?d01m?e$8V@)0000RbVXQn zQ*UN;cVTj607GSLb9r+hQ*?D?X>TA@Z*OeDr{R1600C)9L_t(|obB47ZUR9N2H;s! zLa5@QP<8b^3FuRsATPnN8dg$&4%2%SETV?P6hDY&lS}39^d=Mj{{iM^znM!e*>lWH z&m0E;002O{2h{)o0001hb`PonfD}OMJ-=_aPNdj~&k+G=y=Nk#`|U y$B2+QDQ`7L ceCKSfBbZ_!0yF005xf zgK7W(0000$y9d<(00000fOZe60RR91008YCR09A20Khgt7XtrWuS7(Yb31JsAYIUw z)G!P1wcGK%@3W>pj7fmfwI9YDKw5jLpEAq=e6H90vEMt1_n*ihz;U(W>TqxgqO_hl zfE1}Kdb0ph0RR91008YCR09A20002mJ*Wl%&kpba+>K9=4xDnc00000NkvXXu0mjf D4e{Ne literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_chest_bullets.png b/Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_chest_bullets.png new file mode 100644 index 0000000000000000000000000000000000000000..ff9bff22762fb324cac8e3d0de03c96c5c2bfe37 GIT binary patch literal 3065 zcmV f6 zXi@@54ZTQ_E-Enz5K6$103tR-RB%L5k){YTDBysjLy@r}iiH7DvFijGMAUI`6dRUF zWUU$Bym{} eS9U O(Z2>7`&z9wUXbV-Il#&6`Y8GKGQ04S2&F6MJnWNa;Ck|;8QE#r9r;7G||@X{|> z%+C|c55>;RS}qbKr-&IQTvLXPlM{>K&(BTgi^a?^4mXV>;xX8n8Ce|RasXz}{8imI52H3ZN4bfe_i~WlJ|C&UW9+{8AKoW!}eExnGFE2re(F+`iE_46#!l9 z0Z_aBhs|Iw0E)7{bq;-T9=d#9QpDmcXDh4R++0fmpKB>E= %LdZt9g$j;($`3&Zthxi`{{&gM}5&R^+h%b~yM9Zd3AWW9ETgVfL z1(`yIK=_}U_z%PWq}jQaiQ4!P(3V&Nr6C$XejWfQDiI(Fdt@un?|lo#M+5oIi_w{w zo%_#%{(V=tO#a9gB!7-$M?^BX5>d|Vn*3S !?g~$*UQipUPL&zMmg; !4Do9IA%up=Rh? z=qPj=x&RGBx1dpI68aT-2O}^EromdU5o`ssU{5#*j)WJ%$?!5bA1;Eoz?E iTr=n?cd`V|I)p<|3Oju?MT93~aB0<#&j8`F+Cg&D?-VWzQItUA^l>xvD z RIYI4MQ`g1<+DyrL=EogS06Xii({| zv`U^zjmmKqDIK93(F5q|^fLNk`gQs{RV`IdRle#b)i%{Ds;|}NsClUI)k@Ub)kf6b zsWa4l)YH_rsduU0(?DsMX@qO!YV6TCtMPOWZH~(v?wpc2hv(eZgf-1HBQ#fN?$aF5 zoYvCT^3%%Fs?s{6^;Da#?V+8jy+iwi_M{F~$4y6|vqR^k&SQoO!;_KDsATjprgSxR z{dFa}^}2()GkV5)QF?`X?Rxk03HmJkB>f%wz4}uIItC#I1qQ7Kw+-=zEW;GTU55RJ zuZ@h2VvIHzbs0S}Rx=JT&Npr~zH34@aW`3J(qMAU6l2OVO*7qXdf5y%vo}jIt1%lg zhs_<#1?IcWhb_<+P8LFo28$a^64R5J!)#@aTGB0pEekEXET35!SjAgyv+B3{Xl-wu zZrx~o$A)4PXj5p@WAm%6nJw40#`fA=@?77!tLJvleQsxN$G6*KchjC~A7a13zSsVP zgQJ7Uq0M2^(ZDg$vDWbhi^d9LZDyT!LOXdmt#&%*^w!zIS?qk+`4< X~g?%56 z2@eae34a)26HyS+zks@6$%2*zuOhu7%OdYYnM6sVdZQJi6QY}=U&naIl*dS8tzuWk zUW(I*6U24LW8 oFzvR(TOpM zEs5_rp_~TJ^wNN(wM(bCZ0;`Z6P^ce2XB(^$}i_nB)KM)Cp}7bP2Qe7nc|*Ok@8f) z7E}wKr~0SXrM^xJP1~RLDLp2=Jp-4Km~m7{5vB?IGPN`FGKaIwvx>8%%bb_(Ts9>N z5;bK**^9Ef#WdN^)PTf9vR*Qp {o-l7 zTcBI8wqSIn=gRt3(5j`YdRObOE?Pal#&6AmwS={4Ykw%TE-Wv6xh`g1Pmxy9nxe7w ze(PI{6^cd0H#WFzsN0CzDA+i-Y3`<~O&?2mB^OJrODjs>Z{}{k_?699m0x|@lC)*8 z%%N=0R?Jr6*6Z8cw;d=~F3&F?+a9vLa|dHb$&Qyhm+ZVyVOLSNi?B >BD~Ee(8aT1AWbo&CM;EEoH56tE6@EV8X%6-*|u1-NtOIZ>P7H z9s-9XhaP{M`0e$>L5F*fu#U8SXZT%h2eqT56Y5;vIn|ZYCGC#u9zGg)w718lr{jCe z@An_mJyvsE<#^c%!il02pHAkVoIaIx>gnm^(__6$dheWxJ#(!uyl?Pq(Ao3ne9xWf z_v}A;-u3*k3(gmgUSwVDy5w-FbHIL};|Kd6ItCpEJBJ*Hx-UCj?irppeBz4xmD5+f zub#UWaP88_{E^}7QP*$YNVp-r$-DXJR{E{yw{vdK+*xxMeYfPE(!GlNn)e%iH2tw% z>L5Kn>ODH}V8MesW8ASPKV|>)e!S=*`C-L`&P4Mg+egPHeJ3wJUif(YN!F8@r^P=j z|6Kdb c>FRj6+1QlT=e|YubW?}zu5oM?q% 0Dy!50Qvv` z0D$NK0Cg|`0P0`>06Lfe02gqax=}m;000SaNLh0L01m?d01m?e$8V@)0000RbVXQn zQ*UN;cVTj607GSLb9r+hQ*?D?X>TA@Z*OeDr{R16009L_L_t(|obB2%5`r)gg<*0B zdRrSkhLy+h7#xR+V@N|w&mGn(GZc<9fh8}I|5bMT_!6+$U}nvk6951J0Cfjx00000 z0Ms3%0RR91_5n 00000 z>JHKX00000s5?jl00000pza_I00000fVzV;000000O}6O@!S!p62LRn0Ti_#phz+M z0soR}0gBiWQpRg1p;`d1000000H`}i0{{R30HE$5&FKL?5^X<_rdW}T00000NkvXX Hu0mjfzrd`6 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_eye_left.png b/Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_eye_left.png new file mode 100644 index 0000000000000000000000000000000000000000..b369711f3b8808825b80ea99f40c6444c0b1feb7 GIT binary patch literal 3011 zcmV;!3q16RP) f6 zXi@@54ZTQ_E-Enz5K6$103tR-RB%L5k){YTDBysjLy@r}iiH7DvFijGMAUI`6dRUF zWUU$Bym{} eS9U O(Z2>7`&z9wUXbV-Il#&6`Y8GKGQ04S2&F6MJnWNa;Ck|;8QE#r9r;7G||@X{|> z%+C|c55>;RS}qbKr-&IQTvLXPlM{>K&(BTgi^a?^4mXV>;xX8n8Ce|RasXz}{8imI52H3ZN4bfe_i~WlJ|C&UW9+{8AKoW!}eExnGFE2re(F+`iE_46#!l9 z0Z_aBhs|Iw0E)7{bq;-T9=d#9QpDmcXDh4R++0fmpKB>E= %LdZt9g$j;($`3&Zthxi`{{&gM}5&R^+h%b~yM9Zd3AWW9ETgVfL z1(`yIK=_}U_z%PWq}jQaiQ4!P(3V&Nr6C$XejWfQDiI(Fdt@un?|lo#M+5oIi_w{w zo%_#%{(V=tO#a9gB!7-$M?^BX5>d|Vn*3S !?g~$*UQipUPL&zMmg; !4Do9IA%up=Rh? z=qPj=x&RGBx1dpI68aT-2O}^EromdU5o`ssU{5#*j)WJ%$?!5bA1;Eoz?E iTr=n?cd`V|I)p<|3Oju?MT93~aB0<#&j8`F+Cg&D?-VWzQItUA^l>xvD z RIYI4MQ`g1<+DyrL=EogS06Xii({| zv`U^zjmmKqDIK93(F5q|^fLNk`gQs{RV`IdRle#b)i%{Ds;|}NsClUI)k@Ub)kf6b zsWa4l)YH_rsduU0(?DsMX@qO!YV6TCtMPOWZH~(v?wpc2hv(eZgf-1HBQ#fN?$aF5 zoYvCT^3%%Fs?s{6^;Da#?V+8jy+iwi_M{F~$4y6|vqR^k&SQoO!;_KDsATjprgSxR z{dFa}^}2()GkV5)QF?`X?Rxk03HmJkB>f%wz4}uIItC#I1qQ7Kw+-=zEW;GTU55RJ zuZ@h2VvIHzbs0S}Rx=JT&Npr~zH34@aW`3J(qMAU6l2OVO*7qXdf5y%vo}jIt1%lg zhs_<#1?IcWhb_<+P8LFo28$a^64R5J!)#@aTGB0pEekEXET35!SjAgyv+B3{Xl-wu zZrx~o$A)4PXj5p@WAm%6nJw40#`fA=@?77!tLJvleQsxN$G6*KchjC~A7a13zSsVP zgQJ7Uq0M2^(ZDg$vDWbhi^d9LZDyT!LOXdmt#&%*^w!zIS?qk+`4< X~g?%56 z2@eae34a)26HyS+zks@6$%2*zuOhu7%OdYYnM6sVdZQJi6QY}=U&naIl*dS8tzuWk zUW(I*6U24LW8 oFzvR(TOpM zEs5_rp_~TJ^wNN(wM(bCZ0;`Z6P^ce2XB(^$}i_nB)KM)Cp}7bP2Qe7nc|*Ok@8f) z7E}wKr~0SXrM^xJP1~RLDLp2=Jp-4Km~m7{5vB?IGPN`FGKaIwvx>8%%bb_(Ts9>N z5;bK**^9Ef#WdN^)PTf9vR*Qp {o-l7 zTcBI8wqSIn=gRt3(5j`YdRObOE?Pal#&6AmwS={4Ykw%TE-Wv6xh`g1Pmxy9nxe7w ze(PI{6^cd0H#WFzsN0CzDA+i-Y3`<~O&?2mB^OJrODjs>Z{}{k_?699m0x|@lC)*8 z%%N=0R?Jr6*6Z8cw;d=~F3&F?+a9vLa|dHb$&Qyhm+ZVyVOLSNi?B >BD~Ee(8aT1AWbo&CM;EEoH56tE6@EV8X%6-*|u1-NtOIZ>P7H z9s-9XhaP{M`0e$>L5F*fu#U8SXZT%h2eqT56Y5;vIn|ZYCGC#u9zGg)w718lr{jCe z@An_mJyvsE<#^c%!il02pHAkVoIaIx>gnm^(__6$dheWxJ#(!uyl?Pq(Ao3ne9xWf z_v}A;-u3*k3(gmgUSwVDy5w-FbHIL};|Kd6ItCpEJBJ*Hx-UCj?irppeBz4xmD5+f zub#UWaP88_{E^}7QP*$YNVp-r$-DXJR{E{yw{vdK+*xxMeYfPE(!GlNn)e%iH2tw% z>L5Kn>ODH}V8MesW8ASPKV|>)e!S=*`C-L`&P4Mg+egPHeJ3wJUif(YN!F8@r^P=j z|6Kdb c>FRj6+1QlT=e|YubW?}zu5oM?q% 0Dy!50Qvv` z0D$NK0Cg|`0P0`>06Lfe02gqax=}m;000SaNLh0L01m?d01m?e$8V@)0000RbVXQn zQ*UN;cVTj607GSLb9r+hQ*?D?X>TA@Z*OeDr{R16007TPL_t(|obB165yC(aMq#cF z8bG816b?b^h+ j~1fVyd2_S#~WdOXVTQs{v_78M10CQCgz;n37raRSp0pG_xmfL;30COdP z00Ia=Z$J}3009J`H=qe1fB*u}8_)z0KmY;g4QSN^`~VSe(}65(%47fl002ovPDHLk FV1k)#kaqw8 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_eye_left_small.png b/Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_eye_left_small.png new file mode 100644 index 0000000000000000000000000000000000000000..5fc01b19b01ad5553549e93980f1175b95c49669 GIT binary patch literal 2992 zcmV;h3s3ZkP) f6 zXi@@54ZTQ_E-Enz5K6$103tR-RB%L5k){YTDBysjLy@r}iiH7DvFijGMAUI`6dRUF zWUU$Bym{} eS9U O(Z2>7`&z9wUXbV-Il#&6`Y8GKGQ04S2&F6MJnWNa;Ck|;8QE#r9r;7G||@X{|> z%+C|c55>;RS}qbKr-&IQTvLXPlM{>K&(BTgi^a?^4mXV>;xX8n8Ce|RasXz}{8imI52H3ZN4bfe_i~WlJ|C&UW9+{8AKoW!}eExnGFE2re(F+`iE_46#!l9 z0Z_aBhs|Iw0E)7{bq;-T9=d#9QpDmcXDh4R++0fmpKB>E= %LdZt9g$j;($`3&Zthxi`{{&gM}5&R^+h%b~yM9Zd3AWW9ETgVfL z1(`yIK=_}U_z%PWq}jQaiQ4!P(3V&Nr6C$XejWfQDiI(Fdt@un?|lo#M+5oIi_w{w zo%_#%{(V=tO#a9gB!7-$M?^BX5>d|Vn*3S !?g~$*UQipUPL&zMmg; !4Do9IA%up=Rh? z=qPj=x&RGBx1dpI68aT-2O}^EromdU5o`ssU{5#*j)WJ%$?!5bA1;Eoz?E iTr=n?cd`V|I)p<|3Oju?MT93~aB0<#&j8`F+Cg&D?-VWzQItUA^l>xvD z RIYI4MQ`g1<+DyrL=EogS06Xii({| zv`U^zjmmKqDIK93(F5q|^fLNk`gQs{RV`IdRle#b)i%{Ds;|}NsClUI)k@Ub)kf6b zsWa4l)YH_rsduU0(?DsMX@qO!YV6TCtMPOWZH~(v?wpc2hv(eZgf-1HBQ#fN?$aF5 zoYvCT^3%%Fs?s{6^;Da#?V+8jy+iwi_M{F~$4y6|vqR^k&SQoO!;_KDsATjprgSxR z{dFa}^}2()GkV5)QF?`X?Rxk03HmJkB>f%wz4}uIItC#I1qQ7Kw+-=zEW;GTU55RJ zuZ@h2VvIHzbs0S}Rx=JT&Npr~zH34@aW`3J(qMAU6l2OVO*7qXdf5y%vo}jIt1%lg zhs_<#1?IcWhb_<+P8LFo28$a^64R5J!)#@aTGB0pEekEXET35!SjAgyv+B3{Xl-wu zZrx~o$A)4PXj5p@WAm%6nJw40#`fA=@?77!tLJvleQsxN$G6*KchjC~A7a13zSsVP zgQJ7Uq0M2^(ZDg$vDWbhi^d9LZDyT!LOXdmt#&%*^w!zIS?qk+`4< X~g?%56 z2@eae34a)26HyS+zks@6$%2*zuOhu7%OdYYnM6sVdZQJi6QY}=U&naIl*dS8tzuWk zUW(I*6U24LW8 oFzvR(TOpM zEs5_rp_~TJ^wNN(wM(bCZ0;`Z6P^ce2XB(^$}i_nB)KM)Cp}7bP2Qe7nc|*Ok@8f) z7E}wKr~0SXrM^xJP1~RLDLp2=Jp-4Km~m7{5vB?IGPN`FGKaIwvx>8%%bb_(Ts9>N z5;bK**^9Ef#WdN^)PTf9vR*Qp {o-l7 zTcBI8wqSIn=gRt3(5j`YdRObOE?Pal#&6AmwS={4Ykw%TE-Wv6xh`g1Pmxy9nxe7w ze(PI{6^cd0H#WFzsN0CzDA+i-Y3`<~O&?2mB^OJrODjs>Z{}{k_?699m0x|@lC)*8 z%%N=0R?Jr6*6Z8cw;d=~F3&F?+a9vLa|dHb$&Qyhm+ZVyVOLSNi?B >BD~Ee(8aT1AWbo&CM;EEoH56tE6@EV8X%6-*|u1-NtOIZ>P7H z9s-9XhaP{M`0e$>L5F*fu#U8SXZT%h2eqT56Y5;vIn|ZYCGC#u9zGg)w718lr{jCe z@An_mJyvsE<#^c%!il02pHAkVoIaIx>gnm^(__6$dheWxJ#(!uyl?Pq(Ao3ne9xWf z_v}A;-u3*k3(gmgUSwVDy5w-FbHIL};|Kd6ItCpEJBJ*Hx-UCj?irppeBz4xmD5+f zub#UWaP88_{E^}7QP*$YNVp-r$-DXJR{E{yw{vdK+*xxMeYfPE(!GlNn)e%iH2tw% z>L5Kn>ODH}V8MesW8ASPKV|>)e!S=*`C-L`&P4Mg+egPHeJ3wJUif(YN!F8@r^P=j z|6Kdb c>FRj6+1QlT=e|YubW?}zu5oM?q% 0Dy!50Qvv` z0D$NK0Cg|`0P0`>06Lfe02gqax=}m;000SaNLh0L01m?d01m?e$8V@)0000RbVXQn zQ*UN;cVTj607GSLb9r+hQ*?D?X>TA@Z*OeDr{R16006v6L_t(|obB165yAivL{ZWW zf(lT0s3Q`E1pzh+nL5Cur~pAXM-LgoPRRQe?7TU5O;s)CTmt|Afb@oF0EPg(Z(S{S zhh~I5KhFgW0eJSOTDNBx@DBk1002OGLo@&Y002mDhz0-v008L?(EtDd03f{~8UO$Q z0HilW0{{TP_W@q_Ypu5XNqmo8z!(6J{!*Lv=mMq?00000q&GwZ0001h^oD2v0001x m-VhA{0002e8=?W2JHQ8hlFxxd>Ki5i0000 f6 zXi@@54ZTQ_E-Enz5K6$103tR-RB%L5k){YTDBysjLy@r}iiH7DvFijGMAUI`6dRUF zWUU$Bym{} eS9U O(Z2>7`&z9wUXbV-Il#&6`Y8GKGQ04S2&F6MJnWNa;Ck|;8QE#r9r;7G||@X{|> z%+C|c55>;RS}qbKr-&IQTvLXPlM{>K&(BTgi^a?^4mXV>;xX8n8Ce|RasXz}{8imI52H3ZN4bfe_i~WlJ|C&UW9+{8AKoW!}eExnGFE2re(F+`iE_46#!l9 z0Z_aBhs|Iw0E)7{bq;-T9=d#9QpDmcXDh4R++0fmpKB>E= %LdZt9g$j;($`3&Zthxi`{{&gM}5&R^+h%b~yM9Zd3AWW9ETgVfL z1(`yIK=_}U_z%PWq}jQaiQ4!P(3V&Nr6C$XejWfQDiI(Fdt@un?|lo#M+5oIi_w{w zo%_#%{(V=tO#a9gB!7-$M?^BX5>d|Vn*3S !?g~$*UQipUPL&zMmg; !4Do9IA%up=Rh? z=qPj=x&RGBx1dpI68aT-2O}^EromdU5o`ssU{5#*j)WJ%$?!5bA1;Eoz?E iTr=n?cd`V|I)p<|3Oju?MT93~aB0<#&j8`F+Cg&D?-VWzQItUA^l>xvD z RIYI4MQ`g1<+DyrL=EogS06Xii({| zv`U^zjmmKqDIK93(F5q|^fLNk`gQs{RV`IdRle#b)i%{Ds;|}NsClUI)k@Ub)kf6b zsWa4l)YH_rsduU0(?DsMX@qO!YV6TCtMPOWZH~(v?wpc2hv(eZgf-1HBQ#fN?$aF5 zoYvCT^3%%Fs?s{6^;Da#?V+8jy+iwi_M{F~$4y6|vqR^k&SQoO!;_KDsATjprgSxR z{dFa}^}2()GkV5)QF?`X?Rxk03HmJkB>f%wz4}uIItC#I1qQ7Kw+-=zEW;GTU55RJ zuZ@h2VvIHzbs0S}Rx=JT&Npr~zH34@aW`3J(qMAU6l2OVO*7qXdf5y%vo}jIt1%lg zhs_<#1?IcWhb_<+P8LFo28$a^64R5J!)#@aTGB0pEekEXET35!SjAgyv+B3{Xl-wu zZrx~o$A)4PXj5p@WAm%6nJw40#`fA=@?77!tLJvleQsxN$G6*KchjC~A7a13zSsVP zgQJ7Uq0M2^(ZDg$vDWbhi^d9LZDyT!LOXdmt#&%*^w!zIS?qk+`4< X~g?%56 z2@eae34a)26HyS+zks@6$%2*zuOhu7%OdYYnM6sVdZQJi6QY}=U&naIl*dS8tzuWk zUW(I*6U24LW8 oFzvR(TOpM zEs5_rp_~TJ^wNN(wM(bCZ0;`Z6P^ce2XB(^$}i_nB)KM)Cp}7bP2Qe7nc|*Ok@8f) z7E}wKr~0SXrM^xJP1~RLDLp2=Jp-4Km~m7{5vB?IGPN`FGKaIwvx>8%%bb_(Ts9>N z5;bK**^9Ef#WdN^)PTf9vR*Qp {o-l7 zTcBI8wqSIn=gRt3(5j`YdRObOE?Pal#&6AmwS={4Ykw%TE-Wv6xh`g1Pmxy9nxe7w ze(PI{6^cd0H#WFzsN0CzDA+i-Y3`<~O&?2mB^OJrODjs>Z{}{k_?699m0x|@lC)*8 z%%N=0R?Jr6*6Z8cw;d=~F3&F?+a9vLa|dHb$&Qyhm+ZVyVOLSNi?B >BD~Ee(8aT1AWbo&CM;EEoH56tE6@EV8X%6-*|u1-NtOIZ>P7H z9s-9XhaP{M`0e$>L5F*fu#U8SXZT%h2eqT56Y5;vIn|ZYCGC#u9zGg)w718lr{jCe z@An_mJyvsE<#^c%!il02pHAkVoIaIx>gnm^(__6$dheWxJ#(!uyl?Pq(Ao3ne9xWf z_v}A;-u3*k3(gmgUSwVDy5w-FbHIL};|Kd6ItCpEJBJ*Hx-UCj?irppeBz4xmD5+f zub#UWaP88_{E^}7QP*$YNVp-r$-DXJR{E{yw{vdK+*xxMeYfPE(!GlNn)e%iH2tw% z>L5Kn>ODH}V8MesW8ASPKV|>)e!S=*`C-L`&P4Mg+egPHeJ3wJUif(YN!F8@r^P=j z|6Kdb c>FRj6+1QlT=e|YubW?}zu5oM?q% 0Dy!50Qvv` z0D$NK0Cg|`0P0`>06Lfe02gqax=}m;000SaNLh0L01m?d01m?e$8V@)0000RbVXQn zQ*UN;cVTj607GSLb9r+hQ*?D?X>TA@Z*OeDr{R16007HLL_t(|obB1M5dmQog;86F z1_UY)$pqA~kr7D=NJU1pbr8rzDiCNu9lsuob!YAQD!6mzWv?ow4EkIJ004mWhG+mj z0eGGdWwcmVE$sPuF5nY@d$TQ*dglWEApigX07!3$1^@s60O<|U00000AiW_P00000 zq&GwZ0001h^oD2v008(tz~gc( aGC%B002ovPDHLkV1mWJ BkhcH; literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_eye_right_small.png b/Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_eye_right_small.png new file mode 100644 index 0000000000000000000000000000000000000000..76efe1412f15126f2450ffa9466407ec88f3a55f GIT binary patch literal 2993 zcmV;i3r_TjP) f6 zXi@@54ZTQ_E-Enz5K6$103tR-RB%L5k){YTDBysjLy@r}iiH7DvFijGMAUI`6dRUF zWUU$Bym{} eS9U O(Z2>7`&z9wUXbV-Il#&6`Y8GKGQ04S2&F6MJnWNa;Ck|;8QE#r9r;7G||@X{|> z%+C|c55>;RS}qbKr-&IQTvLXPlM{>K&(BTgi^a?^4mXV>;xX8n8Ce|RasXz}{8imI52H3ZN4bfe_i~WlJ|C&UW9+{8AKoW!}eExnGFE2re(F+`iE_46#!l9 z0Z_aBhs|Iw0E)7{bq;-T9=d#9QpDmcXDh4R++0fmpKB>E= %LdZt9g$j;($`3&Zthxi`{{&gM}5&R^+h%b~yM9Zd3AWW9ETgVfL z1(`yIK=_}U_z%PWq}jQaiQ4!P(3V&Nr6C$XejWfQDiI(Fdt@un?|lo#M+5oIi_w{w zo%_#%{(V=tO#a9gB!7-$M?^BX5>d|Vn*3S !?g~$*UQipUPL&zMmg; !4Do9IA%up=Rh? z=qPj=x&RGBx1dpI68aT-2O}^EromdU5o`ssU{5#*j)WJ%$?!5bA1;Eoz?E iTr=n?cd`V|I)p<|3Oju?MT93~aB0<#&j8`F+Cg&D?-VWzQItUA^l>xvD z RIYI4MQ`g1<+DyrL=EogS06Xii({| zv`U^zjmmKqDIK93(F5q|^fLNk`gQs{RV`IdRle#b)i%{Ds;|}NsClUI)k@Ub)kf6b zsWa4l)YH_rsduU0(?DsMX@qO!YV6TCtMPOWZH~(v?wpc2hv(eZgf-1HBQ#fN?$aF5 zoYvCT^3%%Fs?s{6^;Da#?V+8jy+iwi_M{F~$4y6|vqR^k&SQoO!;_KDsATjprgSxR z{dFa}^}2()GkV5)QF?`X?Rxk03HmJkB>f%wz4}uIItC#I1qQ7Kw+-=zEW;GTU55RJ zuZ@h2VvIHzbs0S}Rx=JT&Npr~zH34@aW`3J(qMAU6l2OVO*7qXdf5y%vo}jIt1%lg zhs_<#1?IcWhb_<+P8LFo28$a^64R5J!)#@aTGB0pEekEXET35!SjAgyv+B3{Xl-wu zZrx~o$A)4PXj5p@WAm%6nJw40#`fA=@?77!tLJvleQsxN$G6*KchjC~A7a13zSsVP zgQJ7Uq0M2^(ZDg$vDWbhi^d9LZDyT!LOXdmt#&%*^w!zIS?qk+`4< X~g?%56 z2@eae34a)26HyS+zks@6$%2*zuOhu7%OdYYnM6sVdZQJi6QY}=U&naIl*dS8tzuWk zUW(I*6U24LW8 oFzvR(TOpM zEs5_rp_~TJ^wNN(wM(bCZ0;`Z6P^ce2XB(^$}i_nB)KM)Cp}7bP2Qe7nc|*Ok@8f) z7E}wKr~0SXrM^xJP1~RLDLp2=Jp-4Km~m7{5vB?IGPN`FGKaIwvx>8%%bb_(Ts9>N z5;bK**^9Ef#WdN^)PTf9vR*Qp {o-l7 zTcBI8wqSIn=gRt3(5j`YdRObOE?Pal#&6AmwS={4Ykw%TE-Wv6xh`g1Pmxy9nxe7w ze(PI{6^cd0H#WFzsN0CzDA+i-Y3`<~O&?2mB^OJrODjs>Z{}{k_?699m0x|@lC)*8 z%%N=0R?Jr6*6Z8cw;d=~F3&F?+a9vLa|dHb$&Qyhm+ZVyVOLSNi?B >BD~Ee(8aT1AWbo&CM;EEoH56tE6@EV8X%6-*|u1-NtOIZ>P7H z9s-9XhaP{M`0e$>L5F*fu#U8SXZT%h2eqT56Y5;vIn|ZYCGC#u9zGg)w718lr{jCe z@An_mJyvsE<#^c%!il02pHAkVoIaIx>gnm^(__6$dheWxJ#(!uyl?Pq(Ao3ne9xWf z_v}A;-u3*k3(gmgUSwVDy5w-FbHIL};|Kd6ItCpEJBJ*Hx-UCj?irppeBz4xmD5+f zub#UWaP88_{E^}7QP*$YNVp-r$-DXJR{E{yw{vdK+*xxMeYfPE(!GlNn)e%iH2tw% z>L5Kn>ODH}V8MesW8ASPKV|>)e!S=*`C-L`&P4Mg+egPHeJ3wJUif(YN!F8@r^P=j z|6Kdb c>FRj6+1QlT=e|YubW?}zu5oM?q% 0Dy!50Qvv` z0D$NK0Cg|`0P0`>06Lfe02gqax=}m;000SaNLh0L01m?d01m?e$8V@)0000RbVXQn zQ*UN;cVTj607GSLb9r+hQ*?D?X>TA@Z*OeDr{R16006y7L_t(|obB165du*VL_sba z1QDR{5XVUr76e!n^5OuGA_4^2EJ=Kr`gQ#xnCe=$Lsc#2+yDRofb@oF09pWi?$=uG z4nu=If6oQ90C-KOT90QJ&|?4q005BQ5DfqT007b(q5%K^06=;}Gynhq07!3$1^@s6 z0O<|U0002c2f+JrtJQYj`~%$$z_02C;5l7tGag+)>i_@%03f{~8UO$Q0HilW0{{R3 nfb@oF00000klqjtz}x}8U1iUKS@@dn00000NkvXXu0mjfwF-zF literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_face_1.png b/Resources/Textures/Mobs/Customization/vox_scars.rsi/vox_scar_face_1.png new file mode 100644 index 0000000000000000000000000000000000000000..c19ce0cf398a692f66e7f7d234b447119f8377aa GIT binary patch literal 3244 zcmV;d3{&%oP) f6 zXi@@54ZTQ_E-Enz5K6$103tR-RB%L5k){YTDBysjLy@r}iiH7DvFijGMAUI`6dRUF zWUU$Bym{} eS9U O(Z2>7`&z9wUXbV-Il#&6`Y8GKGQ04S2&F6MJnWNa;Ck|;8QE#r9r;7G||@X{|> z%+C|c55>;RS}qbKr-&IQTvLXPlM{>K&(BTgi^a?^4mXV>;xX8n8Ce|RasXz}{8imI52H3ZN4bfe_i~WlJ|C&UW9+{8AKoW!}eExnGFE2re(F+`iE_46#!l9 z0Z_aBhs|Iw0E)7{bq;-T9=d#9QpDmcXDh4R++0fmpKB>E= %LdZt9g$j;($`3&Zthxi`{{&gM}5&R^+h%b~yM9Zd3AWW9ETgVfL z1(`yIK=_}U_z%PWq}jQaiQ4!P(3V&Nr6C$XejWfQDiI(Fdt@un?|lo#M+5oIi_w{w zo%_#%{(V=tO#a9gB!7-$M?^BX5>d|Vn*3S !?g~$*UQipUPL&zMmg; !4Do9IA%up=Rh? z=qPj=x&RGBx1dpI68aT-2O}^EromdU5o`ssU{5#*j)WJ%$?!5bA1;Eoz?E iTr=n?cd`V|I)p<|3Oju?MT93~aB0<#&j8`F+Cg&D?-VWzQItUA^l>xvD z RIYI4MQ`g1<+DyrL=EogS06Xii({| zv`U^zjmmKqDIK93(F5q|^fLNk`gQs{RV`IdRle#b)i%{Ds;|}NsClUI)k@Ub)kf6b zsWa4l)YH_rsduU0(?DsMX@qO!YV6TCtMPOWZH~(v?wpc2hv(eZgf-1HBQ#fN?$aF5 zoYvCT^3%%Fs?s{6^;Da#?V+8jy+iwi_M{F~$4y6|vqR^k&SQoO!;_KDsATjprgSxR z{dFa}^}2()GkV5)QF?`X?Rxk03HmJkB>f%wz4}uIItC#I1qQ7Kw+-=zEW;GTU55RJ zuZ@h2VvIHzbs0S}Rx=JT&Npr~zH34@aW`3J(qMAU6l2OVO*7qXdf5y%vo}jIt1%lg zhs_<#1?IcWhb_<+P8LFo28$a^64R5J!)#@aTGB0pEekEXET35!SjAgyv+B3{Xl-wu zZrx~o$A)4PXj5p@WAm%6nJw40#`fA=@?77!tLJvleQsxN$G6*KchjC~A7a13zSsVP zgQJ7Uq0M2^(ZDg$vDWbhi^d9LZDyT!LOXdmt#&%*^w!zIS?qk+`4< X~g?%56 z2@eae34a)26HyS+zks@6$%2*zuOhu7%OdYYnM6sVdZQJi6QY}=U&naIl*dS8tzuWk zUW(I*6U24LW8 oFzvR(TOpM zEs5_rp_~TJ^wNN(wM(bCZ0;`Z6P^ce2XB(^$}i_nB)KM)Cp}7bP2Qe7nc|*Ok@8f) z7E}wKr~0SXrM^xJP1~RLDLp2=Jp-4Km~m7{5vB?IGPN`FGKaIwvx>8%%bb_(Ts9>N z5;bK**^9Ef#WdN^)PTf9vR*Qp {o-l7 zTcBI8wqSIn=gRt3(5j`YdRObOE?Pal#&6AmwS={4Ykw%TE-Wv6xh`g1Pmxy9nxe7w ze(PI{6^cd0H#WFzsN0CzDA+i-Y3`<~O&?2mB^OJrODjs>Z{}{k_?699m0x|@lC)*8 z%%N=0R?Jr6*6Z8cw;d=~F3&F?+a9vLa|dHb$&Qyhm+ZVyVOLSNi?B >BD~Ee(8aT1AWbo&CM;EEoH56tE6@EV8X%6-*|u1-NtOIZ>P7H z9s-9XhaP{M`0e$>L5F*fu#U8SXZT%h2eqT56Y5;vIn|ZYCGC#u9zGg)w718lr{jCe z@An_mJyvsE<#^c%!il02pHAkVoIaIx>gnm^(__6$dheWxJ#(!uyl?Pq(Ao3ne9xWf z_v}A;-u3*k3(gmgUSwVDy5w-FbHIL};|Kd6ItCpEJBJ*Hx-UCj?irppeBz4xmD5+f zub#UWaP88_{E^}7QP*$YNVp-r$-DXJR{E{yw{vdK+*xxMeYfPE(!GlNn)e%iH2tw% z>L5Kn>ODH}V8MesW8ASPKV|>)e!S=*`C-L`&P4Mg+egPHeJ3wJUif(YN!F8@r^P=j z|6Kdb c>FRj6+1QlT=e|YubW?}zu5oM?q% 0Dy!50Qvv` z0D$NK0Cg|`0P0`>06Lfe02gqax=}m;000SaNLh0L01m?d01m?e$8V@)0000RbVXQn zQ*UN;cVTj607GSLb9r+hQ*?D?X>TA@Z*OeDr{R1600Fp3L_t(|obB4NO2beT2H-!7 zQw`YaA_NDa3f;sa?J9ypaS;S3T_i7&F;9@KxR^J#Z|Za>)JBs&ojWcZJ9L4Y2Pn yGK+UoM03F9M0@s!7 zbNTJfKe=W=&-0|-Y-4kO4*;;XvA&Xn2XX==VI-3tgsT|71_?j_0%-T38Uhf200hwP zK{W&*009V~-GgcfKmY;|K)VOk5P$##Ab@rcswo7Jgpu^$UQlngCvKU~GiB8-1km$5 zNe2S}K T&QlL z8IWz2r*Vv$Wi4!rJaApfY91V)>Meo5b){mO7>`DU@5HR}ZCiF4jnZnC@)pR} f6 zXi@@54ZTQ_E-Enz5K6$103tR-RB%L5k){YTDBysjLy@r}iiH7DvFijGMAUI`6dRUF zWUU$Bym{} eS9U O(Z2>7`&z9wUXbV-Il#&6`Y8GKGQ04S2&F6MJnWNa;Ck|;8QE#r9r;7G||@X{|> z%+C|c55>;RS}qbKr-&IQTvLXPlM{>K&(BTgi^a?^4mXV>;xX8n8Ce|RasXz}{8imI52H3ZN4bfe_i~WlJ|C&UW9+{8AKoW!}eExnGFE2re(F+`iE_46#!l9 z0Z_aBhs|Iw0E)7{bq;-T9=d#9QpDmcXDh4R++0fmpKB>E= %LdZt9g$j;($`3&Zthxi`{{&gM}5&R^+h%b~yM9Zd3AWW9ETgVfL z1(`yIK=_}U_z%PWq}jQaiQ4!P(3V&Nr6C$XejWfQDiI(Fdt@un?|lo#M+5oIi_w{w zo%_#%{(V=tO#a9gB!7-$M?^BX5>d|Vn*3S !?g~$*UQipUPL&zMmg; !4Do9IA%up=Rh? z=qPj=x&RGBx1dpI68aT-2O}^EromdU5o`ssU{5#*j)WJ%$?!5bA1;Eoz?E iTr=n?cd`V|I)p<|3Oju?MT93~aB0<#&j8`F+Cg&D?-VWzQItUA^l>xvD z RIYI4MQ`g1<+DyrL=EogS06Xii({| zv`U^zjmmKqDIK93(F5q|^fLNk`gQs{RV`IdRle#b)i%{Ds;|}NsClUI)k@Ub)kf6b zsWa4l)YH_rsduU0(?DsMX@qO!YV6TCtMPOWZH~(v?wpc2hv(eZgf-1HBQ#fN?$aF5 zoYvCT^3%%Fs?s{6^;Da#?V+8jy+iwi_M{F~$4y6|vqR^k&SQoO!;_KDsATjprgSxR z{dFa}^}2()GkV5)QF?`X?Rxk03HmJkB>f%wz4}uIItC#I1qQ7Kw+-=zEW;GTU55RJ zuZ@h2VvIHzbs0S}Rx=JT&Npr~zH34@aW`3J(qMAU6l2OVO*7qXdf5y%vo}jIt1%lg zhs_<#1?IcWhb_<+P8LFo28$a^64R5J!)#@aTGB0pEekEXET35!SjAgyv+B3{Xl-wu zZrx~o$A)4PXj5p@WAm%6nJw40#`fA=@?77!tLJvleQsxN$G6*KchjC~A7a13zSsVP zgQJ7Uq0M2^(ZDg$vDWbhi^d9LZDyT!LOXdmt#&%*^w!zIS?qk+`4< X~g?%56 z2@eae34a)26HyS+zks@6$%2*zuOhu7%OdYYnM6sVdZQJi6QY}=U&naIl*dS8tzuWk zUW(I*6U24LW8 oFzvR(TOpM zEs5_rp_~TJ^wNN(wM(bCZ0;`Z6P^ce2XB(^$}i_nB)KM)Cp}7bP2Qe7nc|*Ok@8f) z7E}wKr~0SXrM^xJP1~RLDLp2=Jp-4Km~m7{5vB?IGPN`FGKaIwvx>8%%bb_(Ts9>N z5;bK**^9Ef#WdN^)PTf9vR*Qp