From 68a1e5156abddf0358cbe3897fc395b456ccec21 Mon Sep 17 00:00:00 2001 From: TytosB <54259736+TytosB@users.noreply.github.com> Date: Thu, 17 Apr 2025 00:48:48 -0500 Subject: [PATCH 01/31] loop minor pipe fix (#36646) pipe --- Resources/Maps/loop.yml | 46 +++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/Resources/Maps/loop.yml b/Resources/Maps/loop.yml index af441f6106..a1f65f2521 100644 --- a/Resources/Maps/loop.yml +++ b/Resources/Maps/loop.yml @@ -4,8 +4,8 @@ meta: engineVersion: 253.0.0 forkId: "" forkVersion: "" - time: 04/14/2025 22:52:39 - entityCount: 17812 + time: 04/17/2025 05:34:31 + entityCount: 17813 maps: - 1 grids: @@ -79,7 +79,7 @@ entities: version: 6 -1,0: ind: -1,0 - tiles: HQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAIQAAAAAAIQAAAAAAIQAAAAAAIQAAAAAAIQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAAAHgAAAAABHgAAAAAAHgAAAAABHgAAAAADHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAACHgAAAAACHgAAAAAAHgAAAAAAHgAAAAACHgAAAAADHgAAAAABHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAACHgAAAAACHgAAAAACHgAAAAABHgAAAAAAHgAAAAACHgAAAAADHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAAAHgAAAAACHgAAAAACHgAAAAABHgAAAAADHgAAAAAAHgAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAABHgAAAAABHgAAAAADHgAAAAABHgAAAAABHgAAAAACHgAAAAABHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAAAHgAAAAADHgAAAAABHgAAAAABHgAAAAADHgAAAAADHgAAAAAAHgAAAAAAHgAAAAACHgAAAAAAHgAAAAADHgAAAAABHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAACHgAAAAACHgAAAAABHgAAAAABHgAAAAAAHgAAAAABHgAAAAACHgAAAAACHgAAAAAAHQAAAAAAHgAAAAADHgAAAAABHgAAAAADHgAAAAADHgAAAAABHgAAAAADHgAAAAABHgAAAAACHgAAAAADHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAABHgAAAAAAHQAAAAAAHgAAAAACHgAAAAABHgAAAAADHgAAAAABHgAAAAAAHgAAAAADHgAAAAAAHgAAAAACHgAAAAACHgAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAADHQAAAAAAHgAAAAAAHgAAAAABHgAAAAABHgAAAAADHgAAAAADHgAAAAABHgAAAAACHgAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAABHgAAAAABHgAAAAAAHQAAAAAAHgAAAAADHgAAAAAAHgAAAAABHgAAAAACHgAAAAAAHgAAAAADHgAAAAABHgAAAAAAHgAAAAACHgAAAAADHgAAAAACHgAAAAADHgAAAAAAHgAAAAACHgAAAAACHQAAAAAAHgAAAAADHgAAAAACHgAAAAADHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAACHgAAAAADHgAAAAADHgAAAAACHgAAAAABHgAAAAADHgAAAAADHgAAAAACHQAAAAAAHgAAAAABHgAAAAABHgAAAAADHgAAAAACHgAAAAADHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAAAHgAAAAACHgAAAAABHQAAAAAAHgAAAAADHgAAAAABHgAAAAAAHQAAAAAAHgAAAAADHgAAAAABHgAAAAADHgAAAAABHgAAAAAB + tiles: HQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAIQAAAAAAIQAAAAAAIQAAAAAAIQAAAAAAIQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAAAHgAAAAABHgAAAAAAHgAAAAABHgAAAAADHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAACHgAAAAACHgAAAAAAHgAAAAAAHgAAAAACHgAAAAADHgAAAAABHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAACHgAAAAACHgAAAAACHgAAAAABHgAAAAAAHgAAAAACHgAAAAADHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAHQAAAAAAHgAAAAAAHgAAAAACHgAAAAACHgAAAAABHgAAAAADHgAAAAAAHgAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAABHgAAAAABHgAAAAADHgAAAAABHgAAAAABHgAAAAACHgAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAAAHgAAAAADHgAAAAABHgAAAAABHgAAAAADHgAAAAADHgAAAAAAHgAAAAAAHgAAAAACHgAAAAAAHgAAAAADHgAAAAABHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAACHgAAAAACHgAAAAABHgAAAAABHgAAAAAAHgAAAAABHgAAAAACHgAAAAACHgAAAAAAHQAAAAAAHgAAAAADHgAAAAABHgAAAAADHgAAAAADHgAAAAABHgAAAAADHgAAAAABHgAAAAACHgAAAAADHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAABHgAAAAAAHQAAAAAAHgAAAAACHgAAAAABHgAAAAADHgAAAAABHgAAAAAAHgAAAAADHgAAAAAAHgAAAAACHgAAAAACHgAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAADHQAAAAAAHgAAAAAAHgAAAAABHgAAAAABHgAAAAADHgAAAAADHgAAAAABHgAAAAACHgAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAABHgAAAAABHgAAAAAAHQAAAAAAHgAAAAADHgAAAAAAHgAAAAABHgAAAAACHgAAAAAAHgAAAAADHgAAAAABHgAAAAAAHgAAAAACHgAAAAADHgAAAAACHgAAAAADHgAAAAAAHgAAAAACHgAAAAACHQAAAAAAHgAAAAADHgAAAAACHgAAAAADHgAAAAABHgAAAAAAHgAAAAAAHgAAAAADHgAAAAACHgAAAAADHgAAAAADHgAAAAACHgAAAAABHgAAAAADHgAAAAADHgAAAAACHQAAAAAAHgAAAAABHgAAAAABHgAAAAADHgAAAAACHgAAAAADHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHgAAAAAAHgAAAAABHgAAAAAAHgAAAAAAHgAAAAAAHgAAAAACHgAAAAABHQAAAAAAHgAAAAADHgAAAAABHgAAAAAAHQAAAAAAHgAAAAADHgAAAAABHgAAAAADHgAAAAABHgAAAAAB version: 6 0,-1: ind: 0,-1 @@ -7277,7 +7277,7 @@ entities: pos: -30.5,13.5 parent: 2 - type: Door - secondsUntilStateChange: -52499.977 + secondsUntilStateChange: -52533.863 state: Opening - type: DeviceLinkSource lastSignals: @@ -43892,7 +43892,7 @@ entities: pos: -9.5,51.5 parent: 2 - type: Door - secondsUntilStateChange: -300255.53 + secondsUntilStateChange: -300289.4 state: Opening - uid: 6747 components: @@ -43900,7 +43900,7 @@ entities: pos: -8.5,51.5 parent: 2 - type: Door - secondsUntilStateChange: -300256.25 + secondsUntilStateChange: -300290.12 state: Opening - uid: 6749 components: @@ -43908,7 +43908,7 @@ entities: pos: -6.5,51.5 parent: 2 - type: Door - secondsUntilStateChange: -300254.8 + secondsUntilStateChange: -300288.7 state: Opening - uid: 6750 components: @@ -43916,7 +43916,7 @@ entities: pos: -5.5,51.5 parent: 2 - type: Door - secondsUntilStateChange: -300254.2 + secondsUntilStateChange: -300288.06 state: Opening - uid: 9721 components: @@ -54147,14 +54147,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0055CCFF' - - uid: 11648 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -4.5,6.5 - parent: 2 - - type: AtmosPipeColor - color: '#990000FF' - uid: 11654 components: - type: Transform @@ -54513,6 +54505,11 @@ entities: parent: 2 - type: AtmosPipeColor color: '#990000FF' + - uid: 14534 + components: + - type: Transform + pos: -3.5,6.5 + parent: 2 - uid: 15192 components: - type: Transform @@ -66771,6 +66768,12 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0055CCFF' + - uid: 11648 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -4.5,6.5 + parent: 2 - uid: 11655 components: - type: Transform @@ -104939,12 +104942,6 @@ entities: - type: Transform pos: 3.5,-6.5 parent: 2 - - uid: 14534 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,6.5 - parent: 2 - uid: 14555 components: - type: Transform @@ -105657,6 +105654,11 @@ entities: rot: 3.141592653589793 rad pos: 12.5,-7.5 parent: 2 + - uid: 17813 + components: + - type: Transform + pos: -3.5,6.5 + parent: 2 - proto: WallSolid entities: - uid: 19 From 7d64599d2d60c16303c46327b9ae2739bde81128 Mon Sep 17 00:00:00 2001 From: Kyle Tyo <36606155+VerinSenpai@users.noreply.github.com> Date: Thu, 17 Apr 2025 06:08:09 -0400 Subject: [PATCH 02/31] Action buttons are now usable in the action window. (#35642) * Update ActionUIController.cs * renamed some inconsistent arguments. * removed a redundant check. * revert some variable renames to reduce the diff. --- .../Systems/Actions/ActionUIController.cs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs b/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs index c2ba35f3c5..d020a71359 100644 --- a/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs +++ b/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs @@ -632,8 +632,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged Date: Thu, 17 Apr 2025 10:09:17 +0000 Subject: [PATCH 03/31] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 7f2298bf34..6b8a640ee4 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Beck Thompson - changes: - - message: Added some new scrap that contains uranium and plasma that can be found - when salvaging! - type: Add - id: 7709 - time: '2024-12-16T12:33:34.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32198 - author: Sirionaut, SpaceBaa changes: - message: Animals no longer consume nutrients when their udder/woolcoat is full. @@ -3909,3 +3901,10 @@ id: 8208 time: '2025-04-17T03:14:17.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/36642 +- author: VerinSenpai + changes: + - message: You can now use actions within the action window. + type: Add + id: 8209 + time: '2025-04-17T10:08:09.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/35642 From 64327326fd19440fb31755122d5723b64cd33fb8 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Thu, 17 Apr 2025 06:10:23 -0400 Subject: [PATCH 04/31] Make AllComponentsOneToOneDeleteTest skip components with Required fields (#30582) --- Content.IntegrationTests/Tests/EntityTest.cs | 36 +++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index 6b8b59a12c..26365d8d96 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -9,6 +9,7 @@ using Robust.Shared.Log; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager.Attributes; namespace Content.IntegrationTests.Tests { @@ -333,6 +334,33 @@ namespace Content.IntegrationTests.Tests await pair.CleanReturnAsync(); } + private static bool HasRequiredDataField(Component component) + { + foreach (var field in component.GetType().GetFields()) + { + foreach (var attribute in field.GetCustomAttributes(true)) + { + if (attribute is not DataFieldAttribute dataField) + continue; + + if (dataField.Required) + return true; + } + } + foreach (var property in component.GetType().GetProperties()) + { + foreach (var attribute in property.GetCustomAttributes(true)) + { + if (attribute is not DataFieldAttribute dataField) + continue; + + if (dataField.Required) + return true; + } + } + return false; + } + [Test] public async Task AllComponentsOneToOneDeleteTest() { @@ -357,9 +385,6 @@ namespace Content.IntegrationTests.Tests "ActivatableUI", // Requires enum key }; - // TODO TESTS - // auto ignore any components that have a "required" data field. - await using var pair = await PoolManager.GetServerClient(); var server = pair.Server; var entityManager = server.ResolveDependency(); @@ -377,9 +402,12 @@ namespace Content.IntegrationTests.Tests foreach (var type in componentFactory.AllRegisteredTypes) { - var component = (Component) componentFactory.GetComponent(type); + var component = (Component)componentFactory.GetComponent(type); var name = componentFactory.GetComponentName(type); + if (HasRequiredDataField(component)) + continue; + // If this component is ignored if (skipComponents.Contains(name)) { From 779fdefa7e1783bf377c3e8fe3b3377538347332 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Thu, 17 Apr 2025 20:12:13 +1000 Subject: [PATCH 05/31] Fix explosion visuals (#36644) --- Resources/Prototypes/explosion.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Resources/Prototypes/explosion.yml b/Resources/Prototypes/explosion.yml index 4f72067376..ad00333892 100644 --- a/Resources/Prototypes/explosion.yml +++ b/Resources/Prototypes/explosion.yml @@ -1,5 +1,11 @@ # Does not currently support prototype hot-reloading. See comments in c# file. +# Note that for every explosion type you define, explosions & nukes will start performing worse +# You should only define a new explopsion type if you really need to +# +# If you just want to modify properties other than `damagePerIntensity`, it'd be better to +# split off explosion damage & explosion visuals/effects into their own separate prototypes. + - type: explosion id: Default damagePerIntensity: @@ -43,7 +49,7 @@ intensityPerState: 20 lightColor: Orange texturePath: /Textures/Effects/fire.rsi - fireStates: 6 + fireStates: 3 - type: explosion id: Radioactive @@ -100,7 +106,7 @@ intensityPerState: 20 lightColor: Orange texturePath: /Textures/Effects/fire.rsi - fireStates: 6 + fireStates: 3 - type: explosion id: HardBomb @@ -116,7 +122,7 @@ intensityPerState: 20 lightColor: Orange texturePath: /Textures/Effects/fire.rsi - fireStates: 6 + fireStates: 3 - type: explosion id: FireBomb @@ -127,7 +133,7 @@ Piercing: 3 lightColor: Orange texturePath: /Textures/Effects/fire.rsi - fireStates: 6 + fireStates: 3 fireStacks: 2 # STOP From 6c05477ca35e55e8624dbcc50855499fcfe7e19d Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 17 Apr 2025 10:13:21 +0000 Subject: [PATCH 06/31] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 6b8a640ee4..ddbb908666 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: Sirionaut, SpaceBaa - changes: - - message: Animals no longer consume nutrients when their udder/woolcoat is full. - type: Fix - - message: Entities with udders display a rough hunger level when examined. - type: Add - id: 7710 - time: '2024-12-16T12:53:22.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32905 - author: thetolbean changes: - message: Teleporting or dashing now causes you to stop pulling objects. @@ -3908,3 +3899,10 @@ id: 8209 time: '2025-04-17T10:08:09.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/35642 +- author: ElectroJr + changes: + - message: Fixed some intense explosion types using incorrect overlay sprites. + type: Fix + id: 8210 + time: '2025-04-17T10:12:14.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36644 From 2fc8cdff00df16b9906ebfdf6db363ac4bc2a512 Mon Sep 17 00:00:00 2001 From: eoineoineoin Date: Thu, 17 Apr 2025 11:15:35 +0100 Subject: [PATCH 07/31] Fix UI issues with Camera Monitor (#31809) * Fix jumpy camera monitor UI Basically copy-pasted same solution from #30292 * Remove duplicate code; use shared ItemList method to sync items --- .../CriminalRecordsConsoleWindow.xaml.cs | 52 +++---------------- .../SurveillanceCameraMonitorWindow.xaml.cs | 20 +++---- 2 files changed, 13 insertions(+), 59 deletions(-) diff --git a/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs b/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs index 85d65020c6..5eaed77041 100644 --- a/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs +++ b/Content.Client/CriminalRecords/CriminalRecordsConsoleWindow.xaml.cs @@ -213,52 +213,14 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow return; } - var entries = listing.ToList(); - entries.Sort((a, b) => string.Compare(a.Value, b.Value, StringComparison.Ordinal)); - // `entries` now contains the definitive list of items which should be in - // our list of records and is in the order we want to present those items. - - // Walk through the existing items in RecordListing and in the updated listing - // in parallel to synchronize the items in RecordListing with `entries`. - int i = RecordListing.Count - 1; - int j = entries.Count - 1; - while (i >= 0 && j >= 0) - { - var strcmp = string.Compare(RecordListing[i].Text, entries[j].Value, StringComparison.Ordinal); - if (strcmp == 0) - { - // This item exists in both RecordListing and `entries`. Nothing to do. - i--; - j--; - } - else if (strcmp > 0) - { - // Item exists in RecordListing, but not in `entries`. Remove it. - RecordListing.RemoveAt(i); - i--; - } - else if (strcmp < 0) - { - // A new entry which doesn't exist in RecordListing. Create it. - RecordListing.Insert(i + 1, new ItemList.Item(RecordListing){Text = entries[j].Value, Metadata = entries[j].Key}); - j--; - } - } - - // Any remaining items in RecordListing don't exist in `entries`, so remove them - while (i >= 0) - { - RecordListing.RemoveAt(i); - i--; - } - - // And finally, any remaining items in `entries`, don't exist in RecordListing. Create them. - while (j >= 0) - { - RecordListing.Insert(0, new ItemList.Item(RecordListing){ Text = entries[j].Value, Metadata = entries[j].Key }); - j--; - } + var entries = listing.Select(i => new ItemList.Item(RecordListing) { + Text = i.Value, + Metadata = i.Key + }).ToList(); + entries.Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.Ordinal)); + RecordListing.SetItems(entries, (a,b) => string.Compare(a.Text, b.Text)); } + private void PopulateRecordContainer(GeneralStationRecord stationRecord, CriminalRecord criminalRecord) { var specifier = new SpriteSpecifier.Rsi(new ResPath("Interface/Misc/job_icons.rsi"), "Unknown"); diff --git a/Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorWindow.xaml.cs b/Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorWindow.xaml.cs index 59ac435acf..0fbaf858da 100644 --- a/Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorWindow.xaml.cs +++ b/Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorWindow.xaml.cs @@ -125,14 +125,12 @@ public sealed partial class SurveillanceCameraMonitorWindow : DefaultWindow private void PopulateCameraList(Dictionary cameras) { - SubnetList.Clear(); - - foreach (var (address, name) in cameras) - { - AddCameraToList(name, address); - } - - SubnetList.SortItemsByText(); + var entries = cameras.Select(i => new ItemList.Item(SubnetList) { + Text = $"{i.Value}: {i.Key}", + Metadata = i.Key + }).ToList(); + entries.Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.Ordinal)); + SubnetList.SetItems(entries, (a,b) => string.Compare(a.Text, b.Text)); } private void SetCameraView(IEye? eye) @@ -187,12 +185,6 @@ public sealed partial class SurveillanceCameraMonitorWindow : DefaultWindow return SubnetSelector.ItemCount - 1; } - private void AddCameraToList(string name, string address) - { - var item = SubnetList.AddItem($"{name}: {address}"); - item.Metadata = address; - } - private void OnSubnetListSelect(ItemList.ItemListSelectedEventArgs args) { CameraSelected!((string) SubnetList[args.ItemIndex].Metadata!); From 4692d3ae48536c4ddaf6ec44a068600e4e93a5b6 Mon Sep 17 00:00:00 2001 From: Morb <14136326+Morb0@users.noreply.github.com> Date: Thu, 17 Apr 2025 13:34:41 +0300 Subject: [PATCH 08/31] Remove test maps from MapRender (#32649) Remove test map prototypes from render list --- Content.MapRenderer/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.MapRenderer/Program.cs b/Content.MapRenderer/Program.cs index 7314119108..5a4ccda0c8 100644 --- a/Content.MapRenderer/Program.cs +++ b/Content.MapRenderer/Program.cs @@ -38,6 +38,7 @@ namespace Content.MapRenderer var mapIds = pair.Server .ResolveDependency() .EnumeratePrototypes() + .Where(map => !pair.IsTestPrototype(map)) .Select(map => map.ID) .ToArray(); From 28b4fd97638403843e9d991d7e1cac81f9166c09 Mon Sep 17 00:00:00 2001 From: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Date: Thu, 17 Apr 2025 03:36:23 -0700 Subject: [PATCH 09/31] Fix trash bag visuals (#32386) * First commit * Dont even look what I did at first I'm silly * more negative diff!! --- .../StorageFillVisualizerSystem.cs | 53 ------------------- .../EntitySystems/SharedStorageSystem.cs | 7 +++ 2 files changed, 7 insertions(+), 53 deletions(-) delete mode 100644 Content.Server/Storage/EntitySystems/StorageFillVisualizerSystem.cs diff --git a/Content.Server/Storage/EntitySystems/StorageFillVisualizerSystem.cs b/Content.Server/Storage/EntitySystems/StorageFillVisualizerSystem.cs deleted file mode 100644 index 04894461ed..0000000000 --- a/Content.Server/Storage/EntitySystems/StorageFillVisualizerSystem.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Content.Shared.Rounding; -using Content.Shared.Storage; -using Content.Shared.Storage.Components; -using Robust.Shared.Containers; - -namespace Content.Server.Storage.EntitySystems; - -public sealed class StorageFillVisualizerSystem : EntitySystem -{ - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnStartup); - SubscribeLocalEvent(OnInserted); - SubscribeLocalEvent(OnRemoved); - } - - private void OnStartup(EntityUid uid, StorageFillVisualizerComponent component, ComponentStartup args) - { - UpdateAppearance(uid, component: component); - } - - private void OnInserted(EntityUid uid, StorageFillVisualizerComponent component, EntInsertedIntoContainerMessage args) - { - UpdateAppearance(uid, component: component); - } - - private void OnRemoved(EntityUid uid, StorageFillVisualizerComponent component, EntRemovedFromContainerMessage args) - { - UpdateAppearance(uid, component: component); - } - - private void UpdateAppearance(EntityUid uid, StorageComponent? storage = null, AppearanceComponent? appearance = null, - StorageFillVisualizerComponent? component = null) - { - if (!Resolve(uid, ref storage, ref appearance, ref component, false)) - return; - - if (component.MaxFillLevels < 1) - return; - - if (!_appearance.TryGetData(uid, StorageVisuals.StorageUsed, out var used, appearance)) - return; - - if (!_appearance.TryGetData(uid, StorageVisuals.Capacity, out var capacity, appearance)) - return; - - var level = ContentHelpers.RoundToLevels(used, capacity, component.MaxFillLevels); - _appearance.SetData(uid, StorageFillVisuals.FillLevel, level, appearance); - } -} diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index f2a2031743..3bf51f7957 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -40,6 +40,7 @@ using Robust.Shared.Random; using Robust.Shared.Serialization; using Robust.Shared.Timing; using Robust.Shared.Utility; +using Content.Shared.Rounding; namespace Content.Shared.Storage.EntitySystems; @@ -880,6 +881,12 @@ public abstract class SharedStorageSystem : EntitySystem _appearance.SetData(uid, StorageVisuals.Open, isOpen, appearance); _appearance.SetData(uid, SharedBagOpenVisuals.BagState, isOpen ? SharedBagState.Open : SharedBagState.Closed, appearance); + if (TryComp(uid, out var storageFillVisualizerComp)) + { + var level = ContentHelpers.RoundToLevels(used, capacity, storageFillVisualizerComp.MaxFillLevels); + _appearance.SetData(uid, StorageFillVisuals.FillLevel, level, appearance); + } + // HideClosedStackVisuals true sets the StackVisuals.Hide to the open state of the storage. // This is for containers that only show their contents when open. (e.g. donut boxes) if (storage.HideStackVisualsWhenClosed) From ab477606abbf6abf6af8ceebdde220aa1ae4953a Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 17 Apr 2025 10:37:30 +0000 Subject: [PATCH 10/31] 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 ddbb908666..7c2b6edb75 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: thetolbean - changes: - - message: Teleporting or dashing now causes you to stop pulling objects. - type: Tweak - id: 7711 - time: '2024-12-16T14:09:20.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33252 - author: KieueCaprie changes: - message: The lizard plushie will now be visible when held in your hands! @@ -3906,3 +3899,11 @@ id: 8210 time: '2025-04-17T10:12:14.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/36644 +- author: Beck Thompson, SlamBamActionman + changes: + - message: Fixed trash bag visuals. The appearance will now update correctly depending + on how many items it contains! + type: Fix + id: 8211 + time: '2025-04-17T10:36:23.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32386 From c979370d57cc12492eb7ee7cb328c82b102a45f9 Mon Sep 17 00:00:00 2001 From: Callmore <22885888+Callmore@users.noreply.github.com> Date: Thu, 17 Apr 2025 11:38:00 +0100 Subject: [PATCH 11/31] Make borgs require prying to open instead of screwing (#32796) make borgs require prying to open instead of screwing... --- Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index ce1fecd03a..7d63ee873b 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -113,6 +113,7 @@ - BorgChassis - RoboticsConsole - type: WiresPanel + openingTool: Prying - type: ActivatableUIRequiresPanel - type: NameIdentifier group: Silicon From 34ac758bf9f2566d7fd4c4839e7d881516d6ea3c Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 17 Apr 2025 10:39:07 +0000 Subject: [PATCH 12/31] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 7c2b6edb75..0c1140a1f1 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: KieueCaprie - changes: - - message: The lizard plushie will now be visible when held in your hands! - type: Add - id: 7712 - time: '2024-12-16T14:14:51.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32583 - author: Bhijn and Myr changes: - message: Admins no longer count towards the playercount cap, meaning you no longer @@ -3907,3 +3900,10 @@ id: 8211 time: '2025-04-17T10:36:23.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/32386 +- author: Callmore + changes: + - message: Cyborgs now require being pryed to open/close the panel instead of screwed. + type: Tweak + id: 8212 + time: '2025-04-17T10:38:00.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/32796 From 0ad56dfddd3ed3f50be140490773e3174b4ee652 Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Thu, 17 Apr 2025 12:39:45 +0200 Subject: [PATCH 13/31] Fix simple hostile (#32898) --- Resources/Prototypes/ai_factions.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Resources/Prototypes/ai_factions.yml b/Resources/Prototypes/ai_factions.yml index 60a052800d..994855fa24 100644 --- a/Resources/Prototypes/ai_factions.yml +++ b/Resources/Prototypes/ai_factions.yml @@ -52,6 +52,8 @@ - PetsNT - Zombie - Revolutionary + - Dragon + - Xeno - AllHostile - Wizard - Dragon From 73e3e9019813f61531e82b60ce7df20110805ece Mon Sep 17 00:00:00 2001 From: Ben Bryant <30258875+benbryant0@users.noreply.github.com> Date: Thu, 17 Apr 2025 12:41:15 +0200 Subject: [PATCH 14/31] Skip unnecessary MaterialStorageControl updates (#32662) Remove unnecessary dictionary copying --- Content.Client/Materials/UI/MaterialStorageControl.xaml.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Content.Client/Materials/UI/MaterialStorageControl.xaml.cs b/Content.Client/Materials/UI/MaterialStorageControl.xaml.cs index 0237b86db7..3cf1792c14 100644 --- a/Content.Client/Materials/UI/MaterialStorageControl.xaml.cs +++ b/Content.Client/Materials/UI/MaterialStorageControl.xaml.cs @@ -3,6 +3,7 @@ using Content.Shared.Materials; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; using Robust.Shared.Timing; namespace Content.Client.Materials.UI; @@ -17,7 +18,7 @@ public sealed partial class MaterialStorageControl : ScrollContainer private EntityUid? _owner; - private Dictionary _currentMaterials = new(); + private Dictionary, int> _currentMaterials = new(); public MaterialStorageControl() { @@ -44,7 +45,7 @@ public sealed partial class MaterialStorageControl : ScrollContainer } var canEject = materialStorage.CanEjectStoredMaterials; - var mats = materialStorage.Storage.Select(pair => (pair.Key.Id, pair.Value)).ToDictionary(); + var mats = materialStorage.Storage; if (_currentMaterials.Equals(mats)) return; From 9b3f37f5afe776cfc3eb2c9f6dd90b2830fbe77b Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Thu, 17 Apr 2025 12:42:39 +0200 Subject: [PATCH 15/31] Improve solar panels descriptions a little (#34925) * improve solar panels descriptions a little * Apply suggestions from code review Co-authored-by: Whatstone <166147148+whatston3@users.noreply.github.com> --------- Co-authored-by: Whatstone <166147148+whatston3@users.noreply.github.com> --- .../Entities/Structures/Power/Generation/solar.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml index f64f6612b0..f4bbf9e686 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml @@ -58,7 +58,7 @@ id: SolarPanelPlasma parent: SolarPanelBasePhysSprite name: solar panel plasma - description: A plasma solar panel that generates power. + description: A few sheets of plasma glass that generate electricity when hit by photons. components: - type: PowerSupplier supplyRampTolerance: 500 @@ -92,7 +92,7 @@ id: SolarPanelUranium parent: SolarPanelBasePhysSprite name: solar panel uranium - description: A uranium solar panel that generates power. + description: A few sheets of uranium glass that generate electricity when hit by photons. components: - type: PowerSupplier supplyRampTolerance: 500 @@ -126,7 +126,7 @@ id: SolarPanel parent: SolarPanelBasePhysSprite name: solar panel - description: A solar panel that generates power. + description: A few sheets of glass that generate electricity when hit by photons. components: - type: PowerSupplier supplyRampTolerance: 500 From f46bb301fbf03b3b3bb43d87bb96e4e0913658e2 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Thu, 17 Apr 2025 21:07:51 +1000 Subject: [PATCH 16/31] Fix lathe arbitrage test (#34449) * Fix lathe arbitrage test * Add refinables * nullable * nullable2 * Fix merge * Ignore failures --- .../Tests/MaterialArbitrageTest.cs | 144 +++++++++++++++--- Content.Server/Lathe/LatheSystem.cs | 19 +-- Content.Shared/Lathe/LatheComponent.cs | 12 +- Content.Shared/Lathe/SharedLatheSystem.cs | 19 +++ 4 files changed, 156 insertions(+), 38 deletions(-) diff --git a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs index e6422f0ec4..4b020e9850 100644 --- a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs +++ b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs @@ -1,12 +1,13 @@ +#nullable enable using System.Collections.Generic; using Content.Server.Cargo.Systems; using Content.Server.Construction.Completions; using Content.Server.Construction.Components; using Content.Server.Destructible; using Content.Server.Destructible.Thresholds.Behaviors; +using Content.Server.Lathe; using Content.Server.Stack; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Construction.Components; using Content.Shared.Construction.Prototypes; using Content.Shared.Construction.Steps; using Content.Shared.FixedPoint; @@ -14,10 +15,9 @@ using Content.Shared.Lathe; using Content.Shared.Materials; using Content.Shared.Research.Prototypes; using Content.Shared.Stacks; +using Content.Shared.Tools.Components; using Robust.Shared.GameObjects; -using Robust.Shared.Map; using Robust.Shared.Prototypes; -using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests; @@ -28,6 +28,21 @@ namespace Content.IntegrationTests.Tests; [TestFixture] public sealed class MaterialArbitrageTest { + // These recipes are currently broken and need fixing. You should not be adding to these sets. + private readonly HashSet _destructionArbitrageIgnore = + [ + "BaseChemistryEmptyVial", "DrinkShotGlass", "Beaker", "SodiumLightTube", "DrinkGlassCoupeShaped", + "LedLightBulb", "ExteriorLightTube", "LightTube", "DrinkGlass", "DimLightBulb", "LightBulb", "LedLightTube", + "SheetRGlass1", "ChemistryEmptyBottle01", "WarmLightBulb", + ]; + + private readonly HashSet _compositionArbitrageIgnore = + [ + "FoodPlateSmall", "AirTank", "FoodPlateTin", "FoodPlateMuffinTin", "WeaponCapacitorRechargerCircuitboard", + "WeaponCapacitorRechargerCircuitboard", "BorgChargerCircuitboard", "BorgChargerCircuitboard", "FoodPlate", + "CellRechargerCircuitboard", "CellRechargerCircuitboard", + ]; + [Test] public async Task NoMaterialArbitrage() { @@ -38,13 +53,12 @@ public sealed class MaterialArbitrageTest await server.WaitIdleAsync(); var entManager = server.ResolveDependency(); - var mapManager = server.ResolveDependency(); var protoManager = server.ResolveDependency(); var pricing = entManager.System(); var stackSys = entManager.System(); var mapSystem = server.System(); - var latheSys = server.System(); + var latheSys = server.System(); var compFact = server.ResolveDependency(); Assert.That(mapSystem.IsInitialized(testMap.MapId)); @@ -53,13 +67,24 @@ public sealed class MaterialArbitrageTest var compositionName = compFact.GetComponentName(typeof(PhysicalCompositionComponent)); var materialName = compFact.GetComponentName(typeof(MaterialComponent)); var destructibleName = compFact.GetComponentName(typeof(DestructibleComponent)); + var refinableName = compFact.GetComponentName(typeof(ToolRefinableComponent)); // get the inverted lathe recipe dictionary var latheRecipes = latheSys.InverseRecipes; - // Lets assume the possible lathe for resource multipliers: - // TODO: each recipe can technically have its own cost multiplier associated with it, so this test needs redone to factor that in. - var multiplier = MathF.Pow(0.85f, 3); + // Find the lowest multiplier / optimal lathe that can be used to construct a recipie. + var minMultiplier = new Dictionary, float>(); + + foreach (var (_, lathe) in pair.GetPrototypesWithComponent()) + { + foreach (var recipe in latheSys.GetAllPossibleRecipes(lathe)) + { + if (!minMultiplier.TryGetValue(recipe, out var min)) + min = 1; + + minMultiplier[recipe] = Math.Min(min, lathe.MaterialUseMultiplier); + } + } // create construction dictionary Dictionary constructionRecipes = new(); @@ -122,6 +147,65 @@ public sealed class MaterialArbitrageTest Dictionary Ents, Dictionary Mats)> spawnedOnDestroy = new(); + // cache the compositions of entities + // If the entity is refineable (i.e. glass shared can be turned into glass, we take the greater of the two compositions. + Dictionary> compositions = new(); + foreach (var proto in protoManager.EnumeratePrototypes()) + { + Dictionary? baseComposition = null; + + if (proto.Components.ContainsKey(materialName) + && proto.Components.TryGetValue(compositionName, out var compositionReg)) + { + var compositionComp = (PhysicalCompositionComponent)compositionReg.Component; + baseComposition = compositionComp.MaterialComposition; + + } + + if (!proto.Components.TryGetValue(refinableName, out var refinableReg)) + { + if (baseComposition != null) + compositions[proto.ID] = new(baseComposition); + continue; + } + + var composition = new Dictionary(); + compositions.Add(proto.ID, composition); + + var refinable = (ToolRefinableComponent)refinableReg.Component; + foreach (var refineResult in refinable.RefineResult) + { + if (refineResult.PrototypeId == null) + continue; + + var refineProto = protoManager.Index(refineResult.PrototypeId.Value); + if (!refineProto.Components.ContainsKey(materialName)) + continue; + + if (!refineProto.Components.TryGetValue(compositionName, out var refinedCompositionReg)) + continue; + + var refinedCompositionComp = (PhysicalCompositionComponent)refinedCompositionReg.Component; + + // This assumes refine results do not have complex spawn behaviours like exclusive groups. + var quantity = refineResult.MaxAmount; + + foreach (var (matId, amount) in refinedCompositionComp.MaterialComposition) + { + composition[matId] = quantity * amount + composition.GetValueOrDefault(matId); + } + } + + if (baseComposition == null) + continue; + + // If the un-refined material quantity is greater than the refined quantity, we use that instead. + foreach (var (matId, amount) in baseComposition) + { + composition[matId] = Math.Max(amount, composition.GetValueOrDefault(matId)); + } + } + // Here we get the set of entities/materials spawned when destroying an entity. foreach (var proto in protoManager.EnumeratePrototypes()) { @@ -151,16 +235,10 @@ public sealed class MaterialArbitrageTest { spawnedEnts[key] = spawnedEnts.GetValueOrDefault(key) + value.Max; - var spawnProto = protoManager.Index(key); - - // get the amount of each material included in the entity - - if (!spawnProto.Components.ContainsKey(materialName) || - !spawnProto.Components.TryGetValue(compositionName, out var compositionReg)) + if (!compositions.TryGetValue(key, out var composition)) continue; - var mat = (PhysicalCompositionComponent) compositionReg.Component; - foreach (var (matId, amount) in mat.MaterialComposition) + foreach (var (matId, amount) in composition) { spawnedMats[matId] = value.Max * amount + spawnedMats.GetValueOrDefault(matId); } @@ -173,10 +251,13 @@ public sealed class MaterialArbitrageTest } // This is the main loop where we actually check for destruction arbitrage - Assert.Multiple(async () => + await Assert.MultipleAsync(async () => { foreach (var (id, (spawnedEnts, spawnedMats)) in spawnedOnDestroy) { + if (_destructionArbitrageIgnore.Contains(id)) + continue; + // Check cargo sell price // several constructible entities have no sell price // also this test only really matters if the entity is also purchaseable.... eh.. @@ -190,6 +271,11 @@ public sealed class MaterialArbitrageTest { foreach (var recipe in recipes) { + if (!minMultiplier.TryGetValue(recipe, out var multiplier)) + { + server.Log.Info($"Unused lathe recipe? {recipe.ID}?"); + continue; + } foreach (var (matId, amount) in recipe.Materials) { var actualAmount = SharedLatheSystem.AdjustMaterial(amount, recipe.ApplyMaterialDiscount, multiplier); @@ -231,6 +317,9 @@ public sealed class MaterialArbitrageTest var edge = cur.GetEdge(node.Name); cur = node; + if (edge == null) + continue; + foreach (var completion in edge.Completed) { if (completion is not SpawnPrototype spawnCompletion) @@ -253,9 +342,9 @@ public sealed class MaterialArbitrageTest } // This is functionally the same loop as before, but now testing deconstruction rather than destruction. - // This is pretty braindead. In principle construction graphs can have loops and whatnot. + // This is pretty brain-dead. In principle construction graphs can have loops and whatnot. - Assert.Multiple(async () => + await Assert.MultipleAsync(async () => { foreach (var (id, deconstructedMats) in deconstructionMaterials) { @@ -270,6 +359,11 @@ public sealed class MaterialArbitrageTest { foreach (var recipe in recipes) { + if (!minMultiplier.TryGetValue(recipe, out var multiplier)) + { + server.Log.Info($"Unused lathe recipe? {recipe.ID}?"); + continue; + } foreach (var (matId, amount) in recipe.Materials) { var actualAmount = SharedLatheSystem.AdjustMaterial(amount, recipe.ApplyMaterialDiscount, multiplier); @@ -291,7 +385,7 @@ public sealed class MaterialArbitrageTest } }); - // create phyiscal composition dictionary + // create physical composition dictionary // this doesn't account for the chemicals in the composition Dictionary physicalCompositions = new(); foreach (var proto in protoManager.EnumeratePrototypes()) @@ -308,10 +402,13 @@ public sealed class MaterialArbitrageTest // This is functionally the same loop as before, but now testing composition rather than destruction or deconstruction. // This doesn't take into account chemicals generated when deconstructing. Maybe it should. - Assert.Multiple(async () => + await Assert.MultipleAsync(async () => { foreach (var (id, compositionComponent) in physicalCompositions) { + if (_compositionArbitrageIgnore.Contains(id)) + continue; + // Check cargo sell price var materialPrice = await GetDeconstructedPrice(compositionComponent.MaterialComposition); var chemicalPrice = await GetChemicalCompositionPrice(compositionComponent.ChemicalComposition); @@ -325,6 +422,11 @@ public sealed class MaterialArbitrageTest { foreach (var recipe in recipes) { + if (!minMultiplier.TryGetValue(recipe, out var multiplier)) + { + server.Log.Info($"Unused lathe recipe? {recipe.ID}?"); + continue; + } foreach (var (matId, amount) in recipe.Materials) { var actualAmount = SharedLatheSystem.AdjustMaterial(amount, recipe.ApplyMaterialDiscount, multiplier); diff --git a/Content.Server/Lathe/LatheSystem.cs b/Content.Server/Lathe/LatheSystem.cs index 4851f6b63d..0212bb6eeb 100644 --- a/Content.Server/Lathe/LatheSystem.cs +++ b/Content.Server/Lathe/LatheSystem.cs @@ -61,7 +61,6 @@ namespace Content.Server.Lathe /// Per-tick cache /// private readonly List _environments = new(); - private readonly HashSet> _availableRecipes = new(); public override void Initialize() { @@ -162,12 +161,8 @@ namespace Content.Server.Lathe public List> GetAvailableRecipes(EntityUid uid, LatheComponent component, bool getUnavailable = false) { - _availableRecipes.Clear(); - AddRecipesFromPacks(_availableRecipes, component.StaticPacks); - var ev = new LatheGetRecipesEvent(uid, getUnavailable) - { - Recipes = _availableRecipes - }; + var ev = new LatheGetRecipesEvent((uid, component), getUnavailable); + AddRecipesFromPacks(ev.Recipes, component.StaticPacks); RaiseLocalEvent(uid, ev); return ev.Recipes.ToList(); } @@ -290,7 +285,7 @@ namespace Content.Server.Lathe var pack = _proto.Index(id); foreach (var recipe in pack.Recipes) { - if (args.getUnavailable || database.UnlockedRecipes.Contains(recipe)) + if (args.GetUnavailable || database.UnlockedRecipes.Contains(recipe)) args.Recipes.Add(recipe); } } @@ -298,10 +293,8 @@ namespace Content.Server.Lathe private void OnGetRecipes(EntityUid uid, TechnologyDatabaseComponent component, LatheGetRecipesEvent args) { - if (uid != args.Lathe || !TryComp(uid, out var latheComponent)) - return; - - AddRecipesFromDynamicPacks(ref args, component, latheComponent.DynamicPacks); + if (uid == args.Lathe) + AddRecipesFromDynamicPacks(ref args, component, args.Comp.DynamicPacks); } private void GetEmagLatheRecipes(EntityUid uid, EmagLatheRecipesComponent component, LatheGetRecipesEvent args) @@ -309,7 +302,7 @@ namespace Content.Server.Lathe if (uid != args.Lathe) return; - if (!args.getUnavailable && !_emag.CheckFlag(uid, EmagType.Interaction)) + if (!args.GetUnavailable && !_emag.CheckFlag(uid, EmagType.Interaction)) return; AddRecipesFromPacks(args.Recipes, component.EmagStaticPacks); diff --git a/Content.Shared/Lathe/LatheComponent.cs b/Content.Shared/Lathe/LatheComponent.cs index aaf273e0fe..80f4f62a31 100644 --- a/Content.Shared/Lathe/LatheComponent.cs +++ b/Content.Shared/Lathe/LatheComponent.cs @@ -21,6 +21,9 @@ namespace Content.Shared.Lathe /// [DataField] public List> DynamicPacks = new(); + // Note that this shouldn't be modified dynamically. + // I.e., this + the static recipies should represent all recipies that the lathe can ever make + // Otherwise the material arbitrage test and/or LatheSystem.GetAllBaseRecipes needs to be updated /// /// The lathe's construction queue @@ -81,15 +84,16 @@ namespace Content.Shared.Lathe public sealed class LatheGetRecipesEvent : EntityEventArgs { public readonly EntityUid Lathe; + public readonly LatheComponent Comp; - public bool getUnavailable; + public bool GetUnavailable; public HashSet> Recipes = new(); - public LatheGetRecipesEvent(EntityUid lathe, bool forced) + public LatheGetRecipesEvent(Entity lathe, bool forced) { - Lathe = lathe; - getUnavailable = forced; + (Lathe, Comp) = lathe; + GetUnavailable = forced; } } diff --git a/Content.Shared/Lathe/SharedLatheSystem.cs b/Content.Shared/Lathe/SharedLatheSystem.cs index ae5519d16c..524d83fd84 100644 --- a/Content.Shared/Lathe/SharedLatheSystem.cs +++ b/Content.Shared/Lathe/SharedLatheSystem.cs @@ -34,6 +34,25 @@ public abstract class SharedLatheSystem : EntitySystem BuildInverseRecipeDictionary(); } + /// + /// Get the set of all recipes that a lathe could possibly ever create (e.g., if all techs were unlocked). + /// + public HashSet> GetAllPossibleRecipes(LatheComponent component) + { + var recipes = new HashSet>(); + foreach (var pack in component.StaticPacks) + { + recipes.UnionWith(_proto.Index(pack).Recipes); + } + + foreach (var pack in component.DynamicPacks) + { + recipes.UnionWith(_proto.Index(pack).Recipes); + } + + return recipes; + } + /// /// Add every recipe in the list of recipe packs to a single hashset. /// From 9b1a7dea1da1086eab0eb2421f53b0a49f3185e4 Mon Sep 17 00:00:00 2001 From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Thu, 17 Apr 2025 13:24:47 +0200 Subject: [PATCH 17/31] Geiger counters can now be heard by everyone nearby (#30463) * haha geiger counter go brrrrrrr * move hardcoded values to datafields * syntax cleanup --- .../Radiation/Systems/GeigerSystem.cs | 18 +++--- .../Radiation/Components/GeigerComponent.cs | 18 ++++++ .../Entities/Objects/Devices/geiger.yml | 62 +++++++++---------- 3 files changed, 59 insertions(+), 39 deletions(-) diff --git a/Content.Server/Radiation/Systems/GeigerSystem.cs b/Content.Server/Radiation/Systems/GeigerSystem.cs index 9b6ed31781..6cf17c49c8 100644 --- a/Content.Server/Radiation/Systems/GeigerSystem.cs +++ b/Content.Server/Radiation/Systems/GeigerSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Radiation.Systems; using Robust.Server.Audio; using Robust.Server.GameObjects; using Robust.Server.Player; +using Robust.Shared.Player; namespace Content.Server.Radiation.Systems; @@ -155,16 +156,17 @@ public sealed class GeigerSystem : SharedGeigerSystem if (!component.Sounds.TryGetValue(component.DangerLevel, out var sounds)) return; - if (component.User == null) - return; - - if (!_player.TryGetSessionByEntity(component.User.Value, out var session)) - return; - var sound = _audio.ResolveSound(sounds); - var param = sounds.Params.WithLoop(true).WithVolume(-4f); + var param = sounds.Params.WithLoop(true).WithVolume(component.Volume); - component.Stream = _audio.PlayGlobal(sound, session, param)?.Entity; + if (component.BroadcastAudio) + { + // For some reason PlayPvs sounds quieter even at distance 0, so we need to boost the volume a bit for consistency + param = sounds.Params.WithLoop(true).WithVolume(component.Volume + 1.5f).WithMaxDistance(component.BroadcastRange); + component.Stream = _audio.PlayPvs(sound, uid, param)?.Entity; + } + else if(component.User is not null && _player.TryGetSessionByEntity(component.User.Value, out var session)) + component.Stream = _audio.PlayGlobal(sound, session, param)?.Entity; } public static GeigerDangerLevel RadsToLevel(float rads) diff --git a/Content.Shared/Radiation/Components/GeigerComponent.cs b/Content.Shared/Radiation/Components/GeigerComponent.cs index 71edb70b37..34a262e131 100644 --- a/Content.Shared/Radiation/Components/GeigerComponent.cs +++ b/Content.Shared/Radiation/Components/GeigerComponent.cs @@ -83,6 +83,24 @@ public sealed partial class GeigerComponent : Component /// Played only for current user. /// public EntityUid? Stream; + + /// + /// Mark true if the audio should be heard by everyone around the device + /// + [DataField] + public bool BroadcastAudio = false; + + /// + /// The distance within which the broadcast tone can be heard. + /// + [DataField] + public float BroadcastRange = 4f; + + /// + /// The volume of the warning tone. + /// + [DataField] + public float Volume = -4f; } [Serializable, NetSerializable] diff --git a/Resources/Prototypes/Entities/Objects/Devices/geiger.yml b/Resources/Prototypes/Entities/Objects/Devices/geiger.yml index f8ee24c5c6..518d28f2ee 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/geiger.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/geiger.yml @@ -4,34 +4,34 @@ name: Geiger counter description: A handheld device used for detecting and measuring radiation pulses. components: - - type: Sprite - sprite: Objects/Tools/geiger.rsi - layers: - - state: geiger_base - - state: geiger_on_idle - map: ["enum.GeigerLayers.Screen"] - shader: unshaded - visible: false - - type: Item - sprite: Objects/Tools/geiger.rsi - - type: Geiger - showControl: true - showExamine: true - - type: Appearance - - type: GenericVisualizer - visuals: - enum.GeigerVisuals.IsEnabled: - GeigerLayers.Screen: - True: { visible: True } - False: { visible: False } - enum.GeigerVisuals.DangerLevel: - GeigerLayers.Screen: - None: {state: geiger_on_idle} - Low: {state: geiger_on_low} - Med: {state: geiger_on_med} - High: {state: geiger_on_high} - Extreme: {state: geiger_on_ext} - - type: PhysicalComposition - materialComposition: - Plastic: 100 - + - type: Sprite + sprite: Objects/Tools/geiger.rsi + layers: + - state: geiger_base + - state: geiger_on_idle + map: ["enum.GeigerLayers.Screen"] + shader: unshaded + visible: false + - type: Item + sprite: Objects/Tools/geiger.rsi + - type: Geiger + showControl: true + showExamine: true + broadcastAudio: true + - type: Appearance + - type: GenericVisualizer + visuals: + enum.GeigerVisuals.IsEnabled: + GeigerLayers.Screen: + True: { visible: True } + False: { visible: False } + enum.GeigerVisuals.DangerLevel: + GeigerLayers.Screen: + None: {state: geiger_on_idle} + Low: {state: geiger_on_low} + Med: {state: geiger_on_med} + High: {state: geiger_on_high} + Extreme: {state: geiger_on_ext} + - type: PhysicalComposition + materialComposition: + Plastic: 100 From a46ab100ffc58ea2794c86077898861a70d75561 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 17 Apr 2025 11:25:54 +0000 Subject: [PATCH 18/31] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 0c1140a1f1..ff72829f91 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: Bhijn and Myr - changes: - - message: Admins no longer count towards the playercount cap, meaning you no longer - need to wait for multiple people to leave in order to join after an admin has - joined. - type: Tweak - id: 7713 - time: '2024-12-16T14:19:15.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33424 - author: dragonryan06 changes: - message: 'New cocktail: the Zombie.' @@ -3907,3 +3898,10 @@ id: 8212 time: '2025-04-17T10:38:00.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/32796 +- author: Errant + changes: + - message: Handheld geiger counters can now by heard by everyone nearby. + type: Tweak + id: 8213 + time: '2025-04-17T11:24:47.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/30463 From d8281c7d2d867552c88d9f60dda919206b4fa222 Mon Sep 17 00:00:00 2001 From: Velken <8467292+Velken@users.noreply.github.com> Date: Thu, 17 Apr 2025 08:35:27 -0300 Subject: [PATCH 19/31] New component: change alert level on trigger (#36581) * adds component that lets change alert level on trigger * oops, put stuff on wrong folder * missclick, forgor a file * slarti finds ways to improve code * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../AlertLevelChangeOnTriggerComponent.cs | 33 +++++++++++++++++++ Content.Server/AlertLevel/AlertLevelSystem.cs | 14 ++++++++ .../AlertLevelChangeOnTriggerSystem.cs | 27 +++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 Content.Server/AlertLevel/AlertLevelChangeOnTriggerComponent.cs create mode 100644 Content.Server/AlertLevel/Systems/AlertLevelChangeOnTriggerSystem.cs diff --git a/Content.Server/AlertLevel/AlertLevelChangeOnTriggerComponent.cs b/Content.Server/AlertLevel/AlertLevelChangeOnTriggerComponent.cs new file mode 100644 index 0000000000..aa6c5ba2bd --- /dev/null +++ b/Content.Server/AlertLevel/AlertLevelChangeOnTriggerComponent.cs @@ -0,0 +1,33 @@ +using Content.Server.AlertLevel.Systems; + +namespace Content.Server.AlertLevel; +/// +/// This component is for changing the alert level of the station when triggered. +/// +[RegisterComponent, Access(typeof(AlertLevelChangeOnTriggerSystem))] +public sealed partial class AlertLevelChangeOnTriggerComponent : Component +{ + /// + ///The alert level to change to when triggered. + /// + [DataField] + public string Level = "blue"; + + /// + ///Whether to play the sound when the alert level changes. + /// + [DataField] + public bool PlaySound = true; + + /// + ///Whether to say the announcement when the alert level changes. + /// + [DataField] + public bool Announce = true; + + /// + ///Force the alert change. This applies if the alert level is not selectable or not. + /// + [DataField] + public bool Force = false; +} diff --git a/Content.Server/AlertLevel/AlertLevelSystem.cs b/Content.Server/AlertLevel/AlertLevelSystem.cs index 0b49d7e4bf..bc02808e7d 100644 --- a/Content.Server/AlertLevel/AlertLevelSystem.cs +++ b/Content.Server/AlertLevel/AlertLevelSystem.cs @@ -116,6 +116,20 @@ public sealed class AlertLevelSystem : EntitySystem return alert.CurrentDelay; } + /// + /// Get the default alert level for a station entity. + /// Returns an empty string if the station has no alert levels defined. + /// + /// The station entity. + public string GetDefaultLevel(Entity station) + { + if (!Resolve(station.Owner, ref station.Comp) || station.Comp.AlertLevels == null) + { + return string.Empty; + } + return station.Comp.AlertLevels.DefaultLevel; + } + /// /// Set the alert level based on the station's entity ID. /// diff --git a/Content.Server/AlertLevel/Systems/AlertLevelChangeOnTriggerSystem.cs b/Content.Server/AlertLevel/Systems/AlertLevelChangeOnTriggerSystem.cs new file mode 100644 index 0000000000..0c9734b943 --- /dev/null +++ b/Content.Server/AlertLevel/Systems/AlertLevelChangeOnTriggerSystem.cs @@ -0,0 +1,27 @@ +using Content.Server.AlertLevel; +using Content.Server.Explosion.EntitySystems; +using Content.Server.Station.Systems; + +namespace Content.Server.AlertLevel.Systems; + +public sealed class AlertLevelChangeOnTriggerSystem : EntitySystem +{ + [Dependency] private readonly AlertLevelSystem _alertLevelSystem = default!; + [Dependency] private readonly StationSystem _station = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnTrigger); + } + + private void OnTrigger(Entity ent, ref TriggerEvent args) + { + var stationUid = _station.GetOwningStation(ent.Owner); + if (!stationUid.HasValue) + return; + + _alertLevelSystem.SetLevel(stationUid.Value, ent.Comp.Level, ent.Comp.PlaySound, ent.Comp.Announce, ent.Comp.Force); + } +} From a85370cc8147f9a0966d86bbf0e860d0b243d8cb Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Thu, 17 Apr 2025 13:38:53 +0200 Subject: [PATCH 20/31] minor MindShieldSystem cleanup (#35905) clean up MindShieldSystem --- Content.Server/Mindshield/MindShieldSystem.cs | 29 +++++++------------ .../Objects/Misc/subdermal_implants.yml | 3 -- Resources/Prototypes/tags.yml | 3 -- 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/Content.Server/Mindshield/MindShieldSystem.cs b/Content.Server/Mindshield/MindShieldSystem.cs index 63d6bf097e..89ae090acd 100644 --- a/Content.Server/Mindshield/MindShieldSystem.cs +++ b/Content.Server/Mindshield/MindShieldSystem.cs @@ -4,51 +4,44 @@ using Content.Server.Popups; using Content.Server.Roles; using Content.Shared.Database; using Content.Shared.Implants; -using Content.Shared.Implants.Components; using Content.Shared.Mindshield.Components; using Content.Shared.Revolutionary.Components; -using Content.Shared.Tag; using Robust.Shared.Containers; namespace Content.Server.Mindshield; /// -/// System used for checking if the implanted is a Rev or Head Rev. +/// System used for adding or removing components with a mindshield implant +/// as well as checking if the implanted is a Rev or Head Rev. /// public sealed class MindShieldSystem : EntitySystem { [Dependency] private readonly IAdminLogManager _adminLogManager = default!; [Dependency] private readonly RoleSystem _roleSystem = default!; [Dependency] private readonly MindSystem _mindSystem = default!; - [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; - [ValidatePrototypeId] - public const string MindShieldTag = "MindShield"; - public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(ImplantCheck); + + SubscribeLocalEvent(OnImplantImplanted); SubscribeLocalEvent(OnImplantDraw); } - /// - /// Checks if the implant was a mindshield or not - /// - public void ImplantCheck(EntityUid uid, SubdermalImplantComponent comp, ref ImplantImplantedEvent ev) + private void OnImplantImplanted(Entity ent, ref ImplantImplantedEvent ev) { - if (_tag.HasTag(ev.Implant, MindShieldTag) && ev.Implanted != null) - { - EnsureComp(ev.Implanted.Value); - MindShieldRemovalCheck(ev.Implanted.Value, ev.Implant); - } + if (ev.Implanted == null) + return; + + EnsureComp(ev.Implanted.Value); + MindShieldRemovalCheck(ev.Implanted.Value, ev.Implant); } /// /// Checks if the implanted person was a Rev or Head Rev and remove role or destroy mindshield respectively. /// - public void MindShieldRemovalCheck(EntityUid implanted, EntityUid implant) + private void MindShieldRemovalCheck(EntityUid implanted, EntityUid implant) { if (HasComp(implanted)) { diff --git a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml index e071a94988..13b906bbd7 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml @@ -343,9 +343,6 @@ components: - type: SubdermalImplant - type: MindShieldImplant - - type: Tag - tags: - - MindShield # Centcomm implants diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index be6746141d..bd3644d53c 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -870,9 +870,6 @@ - type: Tag id: MimeHappyHonk -- type: Tag - id: MindShield - - type: Tag id: MindTransferTarget From 9b7af5dfcfc3aa49a91b1df8fa4edee16c57bd33 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Thu, 17 Apr 2025 21:43:16 +1000 Subject: [PATCH 21/31] Fix hunger + thirst mispredicts (#36549) * Fix hunger + thirst mispredicts * Review --- Content.Shared/Nutrition/EntitySystems/HungerSystem.cs | 4 ++++ Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs b/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs index a8697ffee4..be8f19f98c 100644 --- a/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs @@ -123,6 +123,7 @@ public sealed class HungerSystem : EntitySystem entity.Comp.LastAuthoritativeHungerChangeTime = _timing.CurTime; entity.Comp.LastAuthoritativeHungerValue = ClampHungerWithinThresholds(entity.Comp, value); DirtyField(entity.Owner, entity.Comp, nameof(HungerComponent.LastAuthoritativeHungerChangeTime)); + DirtyField(entity.Owner, entity.Comp, nameof(HungerComponent.LastAuthoritativeHungerValue)); } private void UpdateCurrentThreshold(EntityUid uid, HungerComponent? component = null) @@ -135,6 +136,7 @@ public sealed class HungerSystem : EntitySystem return; component.CurrentThreshold = calculatedHungerThreshold; + DirtyField(uid, component, nameof(HungerComponent.CurrentThreshold)); DoHungerThresholdEffects(uid, component); } @@ -163,10 +165,12 @@ public sealed class HungerSystem : EntitySystem if (component.HungerThresholdDecayModifiers.TryGetValue(component.CurrentThreshold, out var modifier)) { component.ActualDecayRate = component.BaseDecayRate * modifier; + DirtyField(uid, component, nameof(HungerComponent.ActualDecayRate)); SetAuthoritativeHungerValue((uid, component), GetHunger(component)); } component.LastThreshold = component.CurrentThreshold; + DirtyField(uid, component, nameof(HungerComponent.LastThreshold)); } private void DoContinuousHungerEffects(EntityUid uid, HungerComponent? component = null) diff --git a/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs b/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs index 2937c48d48..052e73cd1b 100644 --- a/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs @@ -49,6 +49,8 @@ public sealed class ThirstSystem : EntitySystem component.CurrentThirst = _random.Next( (int) component.ThirstThresholds[ThirstThreshold.Thirsty] + 10, (int) component.ThirstThresholds[ThirstThreshold.Okay] - 1); + + DirtyField(uid, component, nameof(ThirstComponent.CurrentThirst)); } component.NextUpdateTime = _timing.CurTime; component.CurrentThirstThreshold = GetThirstThreshold(component, component.CurrentThirst); @@ -56,6 +58,8 @@ public sealed class ThirstSystem : EntitySystem // TODO: Check all thresholds make sense and throw if they don't. UpdateEffects(uid, component); + DirtyFields(uid, component, null, nameof(ThirstComponent.NextUpdateTime), nameof(ThirstComponent.CurrentThirstThreshold), nameof(ThirstComponent.LastThirstThreshold)); + TryComp(uid, out MovementSpeedModifierComponent? moveMod); _movement.RefreshMovementSpeedModifiers(uid, moveMod); } @@ -103,7 +107,7 @@ public sealed class ThirstSystem : EntitySystem component.ThirstThresholds[ThirstThreshold.OverHydrated] ); - EntityManager.DirtyField(uid, component, nameof(ThirstComponent.CurrentThirst)); + DirtyField(uid, component, nameof(ThirstComponent.CurrentThirst)); } private bool IsMovementThreshold(ThirstThreshold threshold) @@ -164,6 +168,9 @@ public sealed class ThirstSystem : EntitySystem _alerts.ClearAlertCategory(uid, component.ThirstyCategory); } + DirtyField(uid, component, nameof(ThirstComponent.LastThirstThreshold)); + DirtyField(uid, component, nameof(ThirstComponent.ActualDecayRate)); + switch (component.CurrentThirstThreshold) { case ThirstThreshold.OverHydrated: From db40bce58f7844901902a7f15e14cbcd094933ca Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Thu, 17 Apr 2025 07:43:28 -0400 Subject: [PATCH 22/31] SpawnAndDeleteEntityCountTest improvements (#34069) * Minor improvements to SpawnAndDeleteEntityCountTest Assert messages * * Now lists added/removed entities on test failure. * Wrapped entity loop in MultipleAsync so all failing ents are reported. * Removed Multiple wrappers on client/server pairs, as they made result formatting ugly. --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- Content.IntegrationTests/Tests/EntityTest.cs | 114 ++++++++++--------- 1 file changed, 63 insertions(+), 51 deletions(-) diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index 26365d8d96..3992272b5b 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Text; using Robust.Shared; using Robust.Shared.Audio.Components; using Robust.Shared.Configuration; @@ -270,70 +271,81 @@ namespace Content.IntegrationTests.Tests // We consider only non-audio entities, as some entities will just play sounds when they spawn. int Count(IEntityManager ent) => ent.EntityCount - ent.Count(); + IEnumerable Entities(IEntityManager entMan) => entMan.GetEntities().Where(entMan.HasComponent); - foreach (var protoId in protoIds) + await Assert.MultipleAsync(async () => { - // TODO fix ninja - // Currently ninja fails to equip their own loadout. - if (protoId == "MobHumanSpaceNinja") - continue; - - var count = Count(server.EntMan); - var clientCount = Count(client.EntMan); - EntityUid uid = default; - await server.WaitPost(() => uid = server.EntMan.SpawnEntity(protoId, coords)); - await pair.RunTicksSync(3); - - // If the entity deleted itself, check that it didn't spawn other entities - if (!server.EntMan.EntityExists(uid)) + foreach (var protoId in protoIds) { - if (Count(server.EntMan) != count) + // TODO fix ninja + // Currently ninja fails to equip their own loadout. + if (protoId == "MobHumanSpaceNinja") + continue; + + var count = Count(server.EntMan); + var clientCount = Count(client.EntMan); + var serverEntities = new HashSet(Entities(server.EntMan)); + var clientEntities = new HashSet(Entities(client.EntMan)); + EntityUid uid = default; + await server.WaitPost(() => uid = server.EntMan.SpawnEntity(protoId, coords)); + await pair.RunTicksSync(3); + + // If the entity deleted itself, check that it didn't spawn other entities + if (!server.EntMan.EntityExists(uid)) { - Assert.Fail($"Server prototype {protoId} failed on deleting itself"); + Assert.That(Count(server.EntMan), Is.EqualTo(count), $"Server prototype {protoId} failed on deleting itself\n" + + BuildDiffString(serverEntities, Entities(server.EntMan), server.EntMan)); + Assert.That(Count(client.EntMan), Is.EqualTo(clientCount), $"Client prototype {protoId} failed on deleting itself\n" + + $"Expected {clientCount} and found {client.EntMan.EntityCount}.\n" + + $"Server count was {count}.\n" + + BuildDiffString(clientEntities, Entities(client.EntMan), client.EntMan)); + continue; } - if (Count(client.EntMan) != clientCount) - { - Assert.Fail($"Client prototype {protoId} failed on deleting itself\n" + - $"Expected {clientCount} and found {Count(client.EntMan)}.\n" + - $"Server was {count}."); - } - continue; - } + // Check that the number of entities has increased. + Assert.That(Count(server.EntMan), Is.GreaterThan(count), $"Server prototype {protoId} failed on spawning as entity count didn't increase\n" + + BuildDiffString(serverEntities, Entities(server.EntMan), server.EntMan)); + Assert.That(Count(client.EntMan), Is.GreaterThan(clientCount), $"Client prototype {protoId} failed on spawning as entity count didn't increase\n" + + $"Expected at least {clientCount} and found {client.EntMan.EntityCount}. " + + $"Server count was {count}.\n" + + BuildDiffString(clientEntities, Entities(client.EntMan), client.EntMan)); - // Check that the number of entities has increased. - if (Count(server.EntMan) <= count) - { - Assert.Fail($"Server prototype {protoId} failed on spawning as entity count didn't increase"); - } + await server.WaitPost(() => server.EntMan.DeleteEntity(uid)); + await pair.RunTicksSync(3); - if (Count(client.EntMan) <= clientCount) - { - Assert.Fail($"Client prototype {protoId} failed on spawning as entity count didn't increase" + - $"Expected at least {clientCount} and found {Count(client.EntMan)}. " + - $"Server was {count}"); + // Check that the number of entities has gone back to the original value. + Assert.That(Count(server.EntMan), Is.EqualTo(count), $"Server prototype {protoId} failed on deletion: count didn't reset properly\n" + + BuildDiffString(serverEntities, Entities(server.EntMan), server.EntMan)); + Assert.That(client.EntMan.EntityCount, Is.EqualTo(clientCount), $"Client prototype {protoId} failed on deletion: count didn't reset properly:\n" + + $"Expected {clientCount} and found {client.EntMan.EntityCount}.\n" + + $"Server count was {count}.\n" + + BuildDiffString(clientEntities, Entities(client.EntMan), client.EntMan)); } - - await server.WaitPost(() => server.EntMan.DeleteEntity(uid)); - await pair.RunTicksSync(3); - - // Check that the number of entities has gone back to the original value. - if (Count(server.EntMan) != count) - { - Assert.Fail($"Server prototype {protoId} failed on deletion count didn't reset properly"); - } - - if (Count(client.EntMan) != clientCount) - { - Assert.Fail($"Client prototype {protoId} failed on deletion count didn't reset properly:\n" + - $"Expected {clientCount} and found {Count(client.EntMan)}.\n" + - $"Server was {count}."); - } - } + }); await pair.CleanReturnAsync(); } + private static string BuildDiffString(IEnumerable oldEnts, IEnumerable newEnts, IEntityManager entMan) + { + var sb = new StringBuilder(); + var addedEnts = newEnts.Except(oldEnts); + var removedEnts = oldEnts.Except(newEnts); + if (addedEnts.Any()) + sb.AppendLine("Listing new entities:"); + foreach (var addedEnt in addedEnts) + { + sb.AppendLine(entMan.ToPrettyString(addedEnt)); + } + if (removedEnts.Any()) + sb.AppendLine("Listing removed entities:"); + foreach (var removedEnt in removedEnts) + { + sb.AppendLine("\t" + entMan.ToPrettyString(removedEnt)); + } + return sb.ToString(); + } + private static bool HasRequiredDataField(Component component) { foreach (var field in component.GetType().GetFields()) From b1c1cb96b5c91c0134660d735ea554fef536dc70 Mon Sep 17 00:00:00 2001 From: Nikolai Korolev Date: Thu, 17 Apr 2025 12:45:35 +0100 Subject: [PATCH 23/31] Fix auto-updates of RobustToolbox on Windows (#33323) * Fix auto-updates of RobustToolbox on Windows * Also fix hooks * Upgrade version of hooks in git_helper script --- BuildChecker/BuildChecker.csproj | 1 - BuildChecker/git_helper.py | 2 +- BuildChecker/hooks/post-checkout | 8 +------- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/BuildChecker/BuildChecker.csproj b/BuildChecker/BuildChecker.csproj index 4bd7fcf78c..c0a0c9e1f3 100644 --- a/BuildChecker/BuildChecker.csproj +++ b/BuildChecker/BuildChecker.csproj @@ -15,7 +15,6 @@ https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild python3 - py -3 {C899FCA4-7037-4E49-ABC2-44DE72487110} net4.7.2 false diff --git a/BuildChecker/git_helper.py b/BuildChecker/git_helper.py index becd4506e8..83c41c3403 100644 --- a/BuildChecker/git_helper.py +++ b/BuildChecker/git_helper.py @@ -10,7 +10,7 @@ from typing import List SOLUTION_PATH = Path("..") / "SpaceStation14.sln" # If this doesn't match the saved version we overwrite them all. -CURRENT_HOOKS_VERSION = "2" +CURRENT_HOOKS_VERSION = "3" QUIET = len(sys.argv) == 2 and sys.argv[1] == "--quiet" diff --git a/BuildChecker/hooks/post-checkout b/BuildChecker/hooks/post-checkout index c5662445c2..f34ffd293d 100755 --- a/BuildChecker/hooks/post-checkout +++ b/BuildChecker/hooks/post-checkout @@ -4,10 +4,4 @@ gitroot=`git rev-parse --show-toplevel` cd "$gitroot/BuildChecker" -if [[ `uname` == MINGW* || `uname` == CYGWIN* ]]; then - # Windows - py -3 git_helper.py --quiet -else - # Not Windows, so probably some other Unix thing. - python3 git_helper.py --quiet -fi +python3 git_helper.py --quiet From 05ae563e29d53a857188e429357d2908894d2037 Mon Sep 17 00:00:00 2001 From: eoineoineoin Date: Thu, 17 Apr 2025 12:52:38 +0100 Subject: [PATCH 24/31] Fix exception when telepad system plays beam animations (#33010) Fix exception when order sent to telepad Co-authored-by: Milon --- Content.Client/Cargo/Systems/CargoSystem.Telepad.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Content.Client/Cargo/Systems/CargoSystem.Telepad.cs b/Content.Client/Cargo/Systems/CargoSystem.Telepad.cs index 312c4e8019..99f9b34387 100644 --- a/Content.Client/Cargo/Systems/CargoSystem.Telepad.cs +++ b/Content.Client/Cargo/Systems/CargoSystem.Telepad.cs @@ -75,10 +75,9 @@ public sealed partial class CargoSystem switch (state) { case CargoTelepadState.Teleporting: - if (_player.HasRunningAnimation(uid, TelepadBeamKey)) - return; - _player.Stop(uid, player, TelepadIdleKey); - _player.Play((uid, player), CargoTelepadBeamAnimation, TelepadBeamKey); + _player.Stop((uid, player), TelepadIdleKey); + if (!_player.HasRunningAnimation(uid, TelepadBeamKey)) + _player.Play((uid, player), CargoTelepadBeamAnimation, TelepadBeamKey); break; case CargoTelepadState.Unpowered: sprite.LayerSetVisible(CargoTelepadLayers.Beam, false); From f72604bf928a670c071b31eaeddb4fb4e872da01 Mon Sep 17 00:00:00 2001 From: Centronias Date: Thu, 17 Apr 2025 05:33:31 -0700 Subject: [PATCH 25/31] Make tank harness smaller in inventories and craftable at lathes (#35590) * Make tank harness smaller in inventories and craftable at lathes * pr comment --- .../Prototypes/Entities/Clothing/OuterClothing/vests.yml | 4 ++++ Resources/Prototypes/Recipes/Lathes/Packs/engineering.yml | 1 + Resources/Prototypes/Recipes/Lathes/misc.yml | 8 ++++++++ 3 files changed, 13 insertions(+) diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml index 06bdb73a7c..e008fba094 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml @@ -127,3 +127,7 @@ sprite: Clothing/OuterClothing/Vests/tankharness.rsi - type: Clothing sprite: Clothing/OuterClothing/Vests/tankharness.rsi + - type: Item + size: Normal # Make smaller than typical outer clothing + shape: + - 0, 0, 0, 1 diff --git a/Resources/Prototypes/Recipes/Lathes/Packs/engineering.yml b/Resources/Prototypes/Recipes/Lathes/Packs/engineering.yml index cb868aa9c2..3ecd39cc98 100644 --- a/Resources/Prototypes/Recipes/Lathes/Packs/engineering.yml +++ b/Resources/Prototypes/Recipes/Lathes/Packs/engineering.yml @@ -16,6 +16,7 @@ - HandheldGPSBasic - TRayScanner - UtilityBelt + - ClothingOuterVestTank - HandheldStationMap - ClothingHeadHatWelding - ClothingHeadHatCone diff --git a/Resources/Prototypes/Recipes/Lathes/misc.yml b/Resources/Prototypes/Recipes/Lathes/misc.yml index d0a9b586c0..27316ed08a 100644 --- a/Resources/Prototypes/Recipes/Lathes/misc.yml +++ b/Resources/Prototypes/Recipes/Lathes/misc.yml @@ -237,3 +237,11 @@ completetime: 3 materials: Plastic: 200 + +- type: latheRecipe + id: ClothingOuterVestTank + result: ClothingOuterVestTank + completetime: 2 + materials: + Cloth: 100 + Steel: 50 From 7be38114130885990b24b9895378435b605aedad Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 17 Apr 2025 12:34:38 +0000 Subject: [PATCH 26/31] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index ff72829f91..fbafbed88a 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: dragonryan06 - changes: - - message: 'New cocktail: the Zombie.' - type: Add - id: 7714 - time: '2024-12-16T15:25:06.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32802 - author: SlamBamActionman changes: - message: Added Holy damage to the Chaplain's bible, holy water and the gohei, @@ -3905,3 +3898,13 @@ id: 8213 time: '2025-04-17T11:24:47.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/30463 +- author: Centronias + changes: + - message: Tank harnesses can be made at lathes, similar to utility belts. + type: Add + - message: Tank harnesses take up a 1x2 space in inventories, half of their previous + 2x2. + type: Tweak + id: 8214 + time: '2025-04-17T12:33:31.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/35590 From 700cb02d5e46ba6f5861e4076f3a0b28e399dc2e Mon Sep 17 00:00:00 2001 From: osjarw <62134478+osjarw@users.noreply.github.com> Date: Thu, 17 Apr 2025 15:42:31 +0300 Subject: [PATCH 27/31] Fix HTN task ordering bug (#32909) * first, bad ordering * fix * Revert "first, bad ordering", recording artifacts This reverts commit 22307675d79b417371775a0558c39781f59423d6. --- Content.Server/NPC/HTN/HTNPlanJob.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Content.Server/NPC/HTN/HTNPlanJob.cs b/Content.Server/NPC/HTN/HTNPlanJob.cs index 9c62f5840a..8a57d52a66 100644 --- a/Content.Server/NPC/HTN/HTNPlanJob.cs +++ b/Content.Server/NPC/HTN/HTNPlanJob.cs @@ -56,16 +56,16 @@ public sealed class HTNPlanJob : Job // hence we'll store it here. var appliedStates = new List?>(); - var tasksToProcess = new Queue(); + var tasksToProcess = new Stack(); var finalPlan = new List(); - tasksToProcess.Enqueue(_rootTask); + tasksToProcess.Push(_rootTask); // How many primitive tasks we've added since last record. var primitiveCount = 0; int tasksProcessed = 0; - while (tasksToProcess.TryDequeue(out var currentTask)) + while (tasksToProcess.TryPop(out var currentTask)) { if (tasksProcessed++ > _rootTask.MaximumTasks) throw new Exception("HTN Planner exceeded maximum tasks"); @@ -161,7 +161,7 @@ public sealed class HTNPlanJob : Job /// /// Goes through each compound task branch and tries to find an appropriate one. /// - private bool TryFindSatisfiedMethod(HTNCompoundTask compoundId, Queue tasksToProcess, NPCBlackboard blackboard, ref int mtrIndex) + private bool TryFindSatisfiedMethod(HTNCompoundTask compoundId, Stack tasksToProcess, NPCBlackboard blackboard, ref int mtrIndex) { var compound = _protoManager.Index(compoundId.Task); @@ -182,9 +182,9 @@ public sealed class HTNPlanJob : Job if (!isValid) continue; - foreach (var task in branch.Tasks) + foreach (var task in branch.Tasks.AsEnumerable().Reverse()) { - tasksToProcess.Enqueue(task); + tasksToProcess.Push(task); } return true; @@ -198,7 +198,7 @@ public sealed class HTNPlanJob : Job /// private void RestoreTolastDecomposedTask( Stack decompHistory, - Queue tasksToProcess, + Stack tasksToProcess, List?> appliedStates, List finalPlan, ref int primitiveCount, @@ -223,7 +223,7 @@ public sealed class HTNPlanJob : Job primitiveCount = lastDecomp.PrimitiveCount; blackboard = lastDecomp.Blackboard; - tasksToProcess.Enqueue(lastDecomp.CompoundTask); + tasksToProcess.Push(lastDecomp.CompoundTask); } /// From e16365d1bf6b1a485c3b637034c24ba611a33cea Mon Sep 17 00:00:00 2001 From: Jacktastic09 <148667265+Jacktastic09@users.noreply.github.com> Date: Thu, 17 Apr 2025 23:43:44 +1000 Subject: [PATCH 28/31] Add Solid Headband (#36650) * Added The Solid Headband, avaliable from hacked clothesmates * Added The Solid Headband, avaliable from hacked clothesmates --- .../Inventories/clothesmate.yml | 1 + .../Entities/Clothing/Head/hats.yml | 11 ++++++++ .../solidheadband.rsi/equipped-HELMET.png | Bin 0 -> 464 bytes .../Head/Hats/solidheadband.rsi/icon.png | Bin 0 -> 366 bytes .../Hats/solidheadband.rsi/inhand-left.png | Bin 0 -> 468 bytes .../Hats/solidheadband.rsi/inhand-right.png | Bin 0 -> 462 bytes .../Head/Hats/solidheadband.rsi/meta.json | 26 ++++++++++++++++++ 7 files changed, 38 insertions(+) create mode 100644 Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/meta.json diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/clothesmate.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/clothesmate.yml index 4b5688a46f..8e0f09a287 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/clothesmate.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/clothesmate.yml @@ -99,4 +99,5 @@ ToyFigurinePassenger: 1 ToyFigurineGreytider: 1 ClothingBackpackSatchelSmugglerUnanchored: 1 + ClothingHeadHatSolidHeadband: 2 # DO NOT ADD MORE, USE UNIFORM DYING diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index 6264748293..64d55a2dd9 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -1148,3 +1148,14 @@ sprite: Clothing/Head/Hats/beret_medic.rsi - type: Clothing sprite: Clothing/Head/Hats/beret_medic.rsi + +- type: entity + parent: ClothingHeadBase + id: ClothingHeadHatSolidHeadband + name: solid headband + description: "You'll feel like you're Invisible while wearing this! (DISCLAIMER: DOES NOT ACTUALLY MAKE THE WEARER INVISIBLE)" + components: + - type: Sprite + sprite: Clothing/Head/Hats/solidheadband.rsi + - type: Clothing + sprite: Clothing/Head/Hats/solidheadband.rsi diff --git a/Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/equipped-HELMET.png b/Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/equipped-HELMET.png new file mode 100644 index 0000000000000000000000000000000000000000..8d0b70fb48fb4da4e8242c4527c44717b17c036e GIT binary patch literal 464 zcmV;>0WbcEP)Px$ib+I4RCr$Pnz0SSKnz962o#777=Q*gpk!7`Hh_i!*Z@&5LXeS+tSD0cMR&60 ze5xoh=kND_=j&Dr|8)zWe?;(nc z)T;R5eqP?USdCj)W9xCNLssM^7b+$Jj9pMrL1Pys=IrQ8(qJ!W*vlFA3^f6{q%jFV z4{o`mF9CqmmS`maglOg(I00E95I|OE(YcralvLI_CLpV`=v+(yN-Ap|6Oh$ebS@?U zC6%>~3CQX!Iu{dwlFC}g1Y~scGhW literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/icon.png b/Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..439e6ce1a030fb955f6e3cbbd3ae80b86cde629f GIT binary patch literal 366 zcmV-!0g?WRP)Px$C`m*?R9Hvtl)(!@K@i5*4WT5QcyW*-2PX$7f4G}+au9_BJ@1615NG$P)()m|LLKz@&uUc0tf36>1&c^!86-xSC2aCJkVx^{y?KUer1|aTFMt30y z6AcOy!#C&1_r)2& z&_!c$^(X{EULoHOD_cMhLY&TFuPd6C)|2Or9z#{sa~eXE%1lj&2Ke-Y#Mce*7UaFM zRSHG~2q!lo?Y1d?<1OImsU=@+0lCBw+7t#Qu?1mpzH`?Za0DEI8^(pEAp`Ix_W%F@ M07*qoM6N<$f=vdOWB>pF literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/inhand-left.png b/Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..a667e3c1f4d31dbf86be91e0405b729429df983b GIT binary patch literal 468 zcmV;_0W1EAP)Px$j!8s8RCr$PnlWy|KoCXO7eGSDqNGCwqoU<-NXID z1=F}{Tj0O8y88sAX8_8qsWSkz>9KU+EijgG)H(!!B575J39wo@ly4>gMbfGc6JWJ+ zDBnx~ilkK?CctXtP`;S}6iKT(On}wOp?os|D3Vrnm;kGlL-}R`P$aGDFacI8hw{w? zph$|2%mw9FI}z^VDILJ(3}c}Qh@z{5zJhy$a>56J zGDOX1Ss?82dNM-^Q%0>29>o_~{=BUzeZKi`cu_h7uh_RS6DOGeUE=SYu^^e zBJRic%h{63|4aSyl6;`~Aw1B;DRj@|B;WX34I*b-e7f%bZGUz0^Ch;7F7t=7QWsjL m1by>h3fgtM<<>LK9}Kn39gZo5OEv)`h{4m<&t;ucLK6Tcoy9u< literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/meta.json b/Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/meta.json new file mode 100644 index 0000000000..36f70f342b --- /dev/null +++ b/Resources/Textures/Clothing/Head/Hats/solidheadband.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Sprited by Jacktastic09 (Discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} From d1abf36111ae861396914e2c26506bcd867fe04f Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 17 Apr 2025 13:44:51 +0000 Subject: [PATCH 29/31] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index fbafbed88a..7ca98f88b1 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: SlamBamActionman - changes: - - message: Added Holy damage to the Chaplain's bible, holy water and the gohei, - which can be exclusively dealt to spirits. - type: Add - id: 7715 - time: '2024-12-16T15:33:32.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32755 - author: Vexerot changes: - message: Security Belts and Security Carriers now provide 10% reduced explosion @@ -3908,3 +3900,10 @@ id: 8214 time: '2025-04-17T12:33:31.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/35590 +- author: Jacktastic09 + changes: + - message: Added the Solid Headband, available via hacked ClothesMate vending machines + type: Add + id: 8215 + time: '2025-04-17T13:43:44.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36650 From 6c1b350c12f2fd7ffd78733d60cfc53133506dd6 Mon Sep 17 00:00:00 2001 From: Alexandre Courtin Date: Thu, 17 Apr 2025 16:05:27 +0200 Subject: [PATCH 30/31] Banjo can now be worn on your back. (#34057) * Banjo can now be worn on your back * Added banjo backpack sprite * Reduced banjo backpack sprite size * Added my username to copyright * 2nd try banjo backpack sprite * 3rd banjo backpack sprite * EmoGarbage Review - Adjust Strap Color --------- Co-authored-by: Hoodie Co-authored-by: EmoGarbage404 --- .../Fun/Instruments/instruments_string.yml | 6 ++++++ .../Instruments/banjo.rsi/equipped-BACKPACK.png | Bin 0 -> 983 bytes .../banjo.rsi/equipped-SUITSTORAGE.png | Bin 0 -> 983 bytes .../Objects/Fun/Instruments/banjo.rsi/meta.json | 10 +++++++++- 4 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 Resources/Textures/Objects/Fun/Instruments/banjo.rsi/equipped-BACKPACK.png create mode 100644 Resources/Textures/Objects/Fun/Instruments/banjo.rsi/equipped-SUITSTORAGE.png diff --git a/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_string.yml b/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_string.yml index ed1520ff99..f1ba8faf96 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_string.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_string.yml @@ -184,6 +184,12 @@ - type: Item size: Normal sprite: Objects/Fun/Instruments/banjo.rsi + - type: Clothing + quickEquip: false + slots: + - back + - suitStorage + sprite: Objects/Fun/Instruments/banjo.rsi - type: Tag tags: - StringInstrument diff --git a/Resources/Textures/Objects/Fun/Instruments/banjo.rsi/equipped-BACKPACK.png b/Resources/Textures/Objects/Fun/Instruments/banjo.rsi/equipped-BACKPACK.png new file mode 100644 index 0000000000000000000000000000000000000000..204f53416a12cf5ec90f44b21e725257e90a29bc GIT binary patch literal 983 zcmV;|11S87P)Px&kx4{BRCt{2nomd+Q5?s=Gvn%xe|A~ZgA~ddcv(Xhp`^1{FGe@94pB?69;^

x~n@o^E$|)P`kc;jx#R2&uL)Z z{(j&0d%xdrX5Slt!{Kl^#?d>_M{b>!h|epLeDEzAEh04N$Db`=Gr23sX!}O{Haq}j zw!Le=@48^L{Y>-@^br={h6cdrl}Jq8y*}sfHPZBfga-X!@qHv2KvJ5itA8AP0|1)b z4$|f^zxnyR5_$i$i^;d4k!o@~$jq{0YV>_xiImxZm z_`DLi)Nu5^0RWjgtQnJU^GGBxGeM%iBINfPGe;7C?mAzIAU;Ir6^}6eK%d|Jcf#IdC`}4u`|xa5x-J{?S%%w`4d+r12q(y0IA$SqLMc=g$Xa30*{FAxt%vYF5F~KB^2jF36&e z;AkJ!SgHW*s2ZY5{`?_|Dk^qV&1xAO4u`|xa5$V}&0nroe$g62MPx&kx4{BRCt{2nomd+Q5?s=Gvn%xe|A~ZgA~ddcv(Xhp`^1{FGe@94pB?69;^

x~n@o^E$|)P`kc;jx#R2&uL)Z z{(j&0d%xdrX5Slt!{Kl^#?d>_M{b>!h|epLeDEzAEh04N$Db`=Gr23sX!}O{Haq}j zw!Le=@48^L{Y>-@^br={h6cdrl}Jq8y*}sfHPZBfga-X!@qHv2KvJ5itA8AP0|1)b z4$|f^zxnyR5_$i$i^;d4k!o@~$jq{0YV>_xiImxZm z_`DLi)Nu5^0RWjgtQnJU^GGBxGeM%iBINfPGe;7C?mAzIAU;Ir6^}6eK%d|Jcf#IdC`}4u`|xa5x-J{?S%%w`4d+r12q(y0IA$SqLMc=g$Xa30*{FAxt%vYF5F~KB^2jF36&e z;AkJ!SgHW*s2ZY5{`?_|Dk^qV&1xAO4u`|xa5$V}&0nroe$g62M Date: Thu, 17 Apr 2025 14:06:34 +0000 Subject: [PATCH 31/31] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 7ca98f88b1..db2db28a6c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Vexerot - changes: - - message: Security Belts and Security Carriers now provide 10% reduced explosion - damage to contents. - type: Tweak - id: 7716 - time: '2024-12-16T16:27:40.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33253 - author: goet changes: - message: Spaceshrooms can now be cooked on the electric grill. @@ -3907,3 +3899,10 @@ id: 8215 time: '2025-04-17T13:43:44.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/36650 +- author: Hoodie42 + changes: + - message: The banjo can now be worn on the back or suit storage slots. + type: Tweak + id: 8216 + time: '2025-04-17T14:05:27.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/34057