From 87be2dde27457c560545bf9e1b4f5a8d7abc30ca Mon Sep 17 00:00:00 2001 From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Sat, 29 Mar 2025 12:33:25 +0100 Subject: [PATCH 01/45] Fix codermins appearing as red in-game OOC (#36148) init --- Content.Server/Chat/Managers/ChatManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Chat/Managers/ChatManager.cs b/Content.Server/Chat/Managers/ChatManager.cs index 75c46abe37..c86ff802ce 100644 --- a/Content.Server/Chat/Managers/ChatManager.cs +++ b/Content.Server/Chat/Managers/ChatManager.cs @@ -246,7 +246,7 @@ internal sealed partial class ChatManager : IChatManager Color? colorOverride = null; var wrappedMessage = Loc.GetString("chat-manager-send-ooc-wrap-message", ("playerName",player.Name), ("message", FormattedMessage.EscapeText(message))); - if (_adminManager.HasAdminFlag(player, AdminFlags.Admin)) + if (_adminManager.HasAdminFlag(player, AdminFlags.NameColor)) { var prefs = _preferencesManager.GetPreferences(player.UserId); colorOverride = prefs.AdminOOCColor; From 89ea21bc310530be765bd016be656355f07ebb0c Mon Sep 17 00:00:00 2001 From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Sat, 29 Mar 2025 15:51:46 +0100 Subject: [PATCH 02/45] Fix admeme hand teleproter. (#36147) * init * changes --- Content.Server/Teleportation/HandTeleporterSystem.cs | 8 ++++++++ .../Teleportation/Components/HandTeleporterComponent.cs | 8 +++++++- .../Entities/Objects/Devices/hand_teleporter.yml | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Content.Server/Teleportation/HandTeleporterSystem.cs b/Content.Server/Teleportation/HandTeleporterSystem.cs index 5c9baf1854..aa4f7eec82 100644 --- a/Content.Server/Teleportation/HandTeleporterSystem.cs +++ b/Content.Server/Teleportation/HandTeleporterSystem.cs @@ -95,6 +95,10 @@ public sealed class HandTeleporterSystem : EntitySystem var timeout = EnsureComp(user); timeout.EnteredPortal = null; component.FirstPortal = Spawn(component.FirstPortalPrototype, Transform(user).Coordinates); + + if (component.AllowPortalsOnDifferentMaps && TryComp(component.FirstPortal, out var portal)) + portal.CanTeleportToOtherMaps = true; + _adminLogger.Add(LogType.EntitySpawn, LogImpact.High, $"{ToPrettyString(user):player} opened {ToPrettyString(component.FirstPortal.Value)} at {Transform(component.FirstPortal.Value).Coordinates} using {ToPrettyString(uid)}"); _audio.PlayPvs(component.NewPortalSound, uid); } @@ -113,6 +117,10 @@ public sealed class HandTeleporterSystem : EntitySystem var timeout = EnsureComp(user); timeout.EnteredPortal = null; component.SecondPortal = Spawn(component.SecondPortalPrototype, Transform(user).Coordinates); + + if (component.AllowPortalsOnDifferentMaps && TryComp(component.SecondPortal, out var portal)) + portal.CanTeleportToOtherMaps = true; + _adminLogger.Add(LogType.EntitySpawn, LogImpact.High, $"{ToPrettyString(user):player} opened {ToPrettyString(component.SecondPortal.Value)} at {Transform(component.SecondPortal.Value).Coordinates} linked to {ToPrettyString(component.FirstPortal!.Value)} using {ToPrettyString(uid)}"); _link.TryLink(component.FirstPortal!.Value, component.SecondPortal.Value, true); _audio.PlayPvs(component.NewPortalSound, uid); diff --git a/Content.Shared/Teleportation/Components/HandTeleporterComponent.cs b/Content.Shared/Teleportation/Components/HandTeleporterComponent.cs index 6ea29d3fd6..ea1aa492f3 100644 --- a/Content.Shared/Teleportation/Components/HandTeleporterComponent.cs +++ b/Content.Shared/Teleportation/Components/HandTeleporterComponent.cs @@ -21,11 +21,17 @@ public sealed partial class HandTeleporterComponent : Component public EntityUid? SecondPortal = null; /// - /// Portals can't be placed on different grids? + /// Should the portals be able to be placed across grids? /// [DataField] public bool AllowPortalsOnDifferentGrids; + /// + /// Should the portals work across maps? + /// + [DataField] + public bool AllowPortalsOnDifferentMaps; + [DataField("firstPortalPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] public string FirstPortalPrototype = "PortalRed"; diff --git a/Resources/Prototypes/Entities/Objects/Devices/hand_teleporter.yml b/Resources/Prototypes/Entities/Objects/Devices/hand_teleporter.yml index 192aca65fc..f6e30d1e97 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/hand_teleporter.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/hand_teleporter.yml @@ -28,5 +28,7 @@ - state: icon color: green - type: HandTeleporter + allowPortalsOnDifferentGrids: true + allowPortalsOnDifferentMaps: true firstPortalPrototype: PortalGatewayBlue secondPortalPrototype: PortalGatewayOrange From 0c98ad8b387aaadedfebbfb3145ec091989a5155 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 30 Mar 2025 05:57:28 +1100 Subject: [PATCH 03/45] Fix 1x1 storage windows (#35985) --- .../Systems/Storage/Controls/StorageWindow.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Content.Client/UserInterface/Systems/Storage/Controls/StorageWindow.cs b/Content.Client/UserInterface/Systems/Storage/Controls/StorageWindow.cs index a4afebc217..39ffd883bb 100644 --- a/Content.Client/UserInterface/Systems/Storage/Controls/StorageWindow.cs +++ b/Content.Client/UserInterface/Systems/Storage/Controls/StorageWindow.cs @@ -42,6 +42,9 @@ public sealed class StorageWindow : BaseWindow private ValueList _contained = new(); private ValueList _toRemove = new(); + // Manually store this because you can't have a 0x0 GridContainer but we still need to add child controls for 1x1 containers. + private Vector2i _pieceGridSize; + private TextureButton? _backButton; private bool _isDirty; @@ -408,11 +411,14 @@ public sealed class StorageWindow : BaseWindow _contained.Clear(); _contained.AddRange(storageComp.Container.ContainedEntities.Reverse()); + var width = boundingGrid.Width + 1; + var height = boundingGrid.Height + 1; + // Build the grid representation - if (_pieceGrid.Rows - 1 != boundingGrid.Height || _pieceGrid.Columns - 1 != boundingGrid.Width) + if (_pieceGrid.Rows != _pieceGridSize.Y || _pieceGrid.Columns != _pieceGridSize.X) { - _pieceGrid.Rows = boundingGrid.Height + 1; - _pieceGrid.Columns = boundingGrid.Width + 1; + _pieceGrid.Rows = height; + _pieceGrid.Columns = width; _controlGrid.Clear(); for (var y = boundingGrid.Bottom; y <= boundingGrid.Top; y++) @@ -430,6 +436,7 @@ public sealed class StorageWindow : BaseWindow } } + _pieceGridSize = new(width, height); _toRemove.Clear(); // Remove entities no longer relevant / Update existing ones From 8d7b21988bc3f247840272675d8c6676f1fb0860 Mon Sep 17 00:00:00 2001 From: Killerqu00 <47712032+Killerqu00@users.noreply.github.com> Date: Sat, 29 Mar 2025 21:09:34 +0100 Subject: [PATCH 04/45] Shove down a person on uncuff if harm mode is on (#35193) * stamdamage on uncuff while buckled * pro tip * 99 -> 100 stamdmg and don't count self-uncuffs * review implementation * tip update * guidebook update * merg --- Content.Shared/Cuffs/SharedCuffableSystem.cs | 25 ++++++++++++++++++- .../cuffs/components/cuffable-component.ftl | 1 + Resources/Locale/en-US/tips.ftl | 1 + .../Guidebook/Security/Security.xml | 4 +-- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index a1f5ec2a1c..bdb3a50454 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Administration.Components; using Content.Shared.Administration.Logs; using Content.Shared.Alert; using Content.Shared.Buckle.Components; +using Content.Shared.CombatMode; using Content.Shared.Cuffs.Components; using Content.Shared.Database; using Content.Shared.DoAfter; @@ -53,6 +54,7 @@ namespace Content.Shared.Cuffs [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly UseDelaySystem _delay = default!; + [Dependency] private readonly SharedCombatModeSystem _combatMode = default!; public override void Initialize() { @@ -717,10 +719,31 @@ namespace Content.Shared.Cuffs } } + var shoved = false; + // if combat mode is on, shove the person. + if (_combatMode.IsInCombatMode(user) && target != user && user != null) + { + var eventArgs = new DisarmedEvent { Target = target, Source = user.Value, PushProbability = 1}; + RaiseLocalEvent(target, eventArgs); + shoved = true; + } + if (cuffable.CuffedHandCount == 0) { if (user != null) - _popup.PopupClient(Loc.GetString("cuffable-component-remove-cuffs-success-message"), user.Value, user.Value); + { + if (shoved) + { + _popup.PopupClient(Loc.GetString("cuffable-component-remove-cuffs-push-success-message", + ("otherName", Identity.Name(user.Value, EntityManager, user))), + user.Value, + user.Value); + } + else + { + _popup.PopupClient(Loc.GetString("cuffable-component-remove-cuffs-success-message"), user.Value, user.Value); + } + } if (target != user && user != null) { diff --git a/Resources/Locale/en-US/cuffs/components/cuffable-component.ftl b/Resources/Locale/en-US/cuffs/components/cuffable-component.ftl index a2cb6ed658..092f1d6620 100644 --- a/Resources/Locale/en-US/cuffs/components/cuffable-component.ftl +++ b/Resources/Locale/en-US/cuffs/components/cuffable-component.ftl @@ -8,6 +8,7 @@ cuffable-component-start-uncuffing-target-message = You start unrestraining {$ta cuffable-component-start-uncuffing-by-other-message = {$otherName} starts unrestraining you! cuffable-component-remove-cuffs-success-message = You successfully remove the restraints. +cuffable-component-remove-cuffs-push-success-message = You successfully remove the restraints and push {$otherName} down. cuffable-component-remove-cuffs-by-other-success-message = {$otherName} unrestrains your hands. cuffable-component-remove-cuffs-to-other-partial-success-message = You successfully remove the restraints. {$cuffedHandCount} of {$otherName}'s hands remain restrained. cuffable-component-remove-cuffs-by-other-partial-success-message = {$otherName} removes your restraints. {$cuffedHandCount} of your hands remain restrained. diff --git a/Resources/Locale/en-US/tips.ftl b/Resources/Locale/en-US/tips.ftl index e0e71d66da..ae43ea094a 100644 --- a/Resources/Locale/en-US/tips.ftl +++ b/Resources/Locale/en-US/tips.ftl @@ -135,3 +135,4 @@ tips-dataset-134 = You can tell if an area with firelocks up is spaced by lookin tips-dataset-135 = Instead of picking it up, you can alt-click food to eat it. This also works for mice and other creatures without hands. tips-dataset-136 = If you're trapped behind an electrified door, disable the APC or throw your ID at the door to avoid getting shocked! tips-dataset-137 = If the AI electrifies a door and you have insulated gloves, snip and mend the power wire to reset their electrification! +tips-dataset-138 = If you want to stop your prisoner from escaping from the cell right after being uncuffed, turn on combat mode while uncuffing - this will shove the prisoner down. diff --git a/Resources/ServerInfo/Guidebook/Security/Security.xml b/Resources/ServerInfo/Guidebook/Security/Security.xml index 7306e3f761..3df94c6b9b 100644 --- a/Resources/ServerInfo/Guidebook/Security/Security.xml +++ b/Resources/ServerInfo/Guidebook/Security/Security.xml @@ -15,7 +15,7 @@ They face [textlink="Syndicate Agents" link="Traitors"], [textlink="Nuclear Oper ## Gear -First we have non-lethals a step above simply telling someone to cooperate with instructions. Both the stunbaton and disabler are capable of limiting the movement of an assailant, whereas handcuffs can be applied to deny a criminal free movement and access to their hands. +First we have non-lethals a step above simply telling someone to cooperate with instructions. Both the stunbaton and disabler are capable of limiting the movement of an assailant, whereas handcuffs can be applied to deny a criminal free movement and access to their hands. Cuffs will also shove the person down if your [color=red]harm mode[/color] is active during uncuff. @@ -31,7 +31,7 @@ It is worth noting that flashes can be both used in a large area ([color=yellow] - + ## Lethals Should the situation dictate, [color=#cb0000]Security[/color] have access to laser rifles, shotguns, handguns and automatic rifles able to put down substancial fire against any who would stand against the station. From 41f4d93a15b85fbff351fbe2823d2d578d1cd355 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 29 Mar 2025 20:10:41 +0000 Subject: [PATCH 05/45] 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 f2b2652a54..8a5e4bdf05 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: SaphireLattice - changes: - - message: Utensils can finally go into disposals - type: Fix - id: 7616 - time: '2024-11-16T03:39:19.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33326 - author: K-Dynamic changes: - message: Solar assembly crate now comes with 10 flatpacks and 20 glass to make @@ -3886,3 +3879,10 @@ id: 8115 time: '2025-03-29T17:55:59.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/35123 +- author: Killerqu00 + changes: + - message: Uncuffing someone with combat mode on will shove them down. + type: Add + id: 8116 + time: '2025-03-29T20:09:34.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/35193 From 99e675c5062201ecb7f85bb75fd0878268373b56 Mon Sep 17 00:00:00 2001 From: J Date: Sun, 30 Mar 2025 01:05:22 +0000 Subject: [PATCH 06/45] Mapping warnings cleanup (#36168) * Mapping warnings cleanup * Redo --- Content.Client/Mapping/MappingState.cs | 4 ++-- Content.Server/Mapping/MappingCommand.cs | 3 --- Content.Server/Mapping/MappingManager.cs | 6 +----- Content.Shared/Maps/TurfHelpers.cs | 2 +- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/Content.Client/Mapping/MappingState.cs b/Content.Client/Mapping/MappingState.cs index bcc739fe4f..57b45036e3 100644 --- a/Content.Client/Mapping/MappingState.cs +++ b/Content.Client/Mapping/MappingState.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Numerics; using Content.Client.Administration.Managers; using Content.Client.ContextMenu.UI; @@ -149,7 +149,7 @@ public sealed class MappingState : GameplayStateBase { Deselect(); - var coords = args.Coordinates.ToMap(_entityManager, _transform); + var coords = _transform.ToMapCoordinates(args.Coordinates); if (_verbs.TryGetEntityMenuEntities(coords, out var entities)) _entityMenuController.OpenRootMenu(entities); diff --git a/Content.Server/Mapping/MappingCommand.cs b/Content.Server/Mapping/MappingCommand.cs index 12a7af4484..b44a09869e 100644 --- a/Content.Server/Mapping/MappingCommand.cs +++ b/Content.Server/Mapping/MappingCommand.cs @@ -2,8 +2,6 @@ using System.Linq; using Content.Server.Administration; using Content.Server.GameTicking; using Content.Shared.Administration; -using Content.Shared.CCVar; -using Robust.Shared.Configuration; using Robust.Shared.Console; using Robust.Shared.ContentPack; using Robust.Shared.EntitySerialization; @@ -19,7 +17,6 @@ namespace Content.Server.Mapping { [Dependency] private readonly IEntityManager _entities = default!; [Dependency] private readonly IMapManager _map = default!; - [Dependency] private readonly IConfigurationManager _cfg = default!; public string Command => "mapping"; public string Description => Loc.GetString("cmd-mapping-desc"); diff --git a/Content.Server/Mapping/MappingManager.cs b/Content.Server/Mapping/MappingManager.cs index 0097df2e55..3a46b301e8 100644 --- a/Content.Server/Mapping/MappingManager.cs +++ b/Content.Server/Mapping/MappingManager.cs @@ -1,12 +1,9 @@ -using System.IO; +using System.IO; using Content.Server.Administration.Managers; using Content.Shared.Administration; using Content.Shared.Mapping; -using Robust.Server.GameObjects; using Robust.Server.Player; -using Robust.Shared.EntitySerialization; using Robust.Shared.EntitySerialization.Systems; -using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -19,7 +16,6 @@ public sealed class MappingManager : IPostInjectInit { [Dependency] private readonly IAdminManager _admin = default!; [Dependency] private readonly ILogManager _log = default!; - [Dependency] private readonly IMapManager _map = default!; [Dependency] private readonly IServerNetManager _net = default!; [Dependency] private readonly IPlayerManager _players = default!; [Dependency] private readonly IEntitySystemManager _systems = default!; diff --git a/Content.Shared/Maps/TurfHelpers.cs b/Content.Shared/Maps/TurfHelpers.cs index 71bbb35db7..dfa12f3d8f 100644 --- a/Content.Shared/Maps/TurfHelpers.cs +++ b/Content.Shared/Maps/TurfHelpers.cs @@ -23,7 +23,7 @@ namespace Content.Shared.Maps return null; mapManager ??= IoCManager.Resolve(); - var pos = coordinates.ToMap(entityManager, entityManager.System()); + var pos = entityManager.System().ToMapCoordinates(coordinates); if (!mapManager.TryFindGridAt(pos, out _, out var grid)) return null; From 2f58fdf1e18e28cb7d424c84d88416418396a41f Mon Sep 17 00:00:00 2001 From: J Date: Sun, 30 Mar 2025 01:11:04 +0000 Subject: [PATCH 07/45] Remove warnings from cargo system (#36159) * Remove warnings from cargo system * Guard statement early exit and cleaner object instantiation * Whitespace * Add AnimationPlayer as a component of telepads --- Content.Client/Cargo/Systems/CargoSystem.Telepad.cs | 8 +++++--- .../Prototypes/Entities/Structures/cargo_telepad.yml | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Content.Client/Cargo/Systems/CargoSystem.Telepad.cs b/Content.Client/Cargo/Systems/CargoSystem.Telepad.cs index 50d079737d..312c4e8019 100644 --- a/Content.Client/Cargo/Systems/CargoSystem.Telepad.cs +++ b/Content.Client/Cargo/Systems/CargoSystem.Telepad.cs @@ -67,8 +67,10 @@ public sealed partial class CargoSystem if (!Resolve(uid, ref sprite)) return; + if (!TryComp(uid, out var player)) + return; + _appearance.TryGetData(uid, CargoTelepadVisuals.State, out var state); - AnimationPlayerComponent? player = null; switch (state) { @@ -76,7 +78,7 @@ public sealed partial class CargoSystem if (_player.HasRunningAnimation(uid, TelepadBeamKey)) return; _player.Stop(uid, player, TelepadIdleKey); - _player.Play(uid, player, CargoTelepadBeamAnimation, TelepadBeamKey); + _player.Play((uid, player), CargoTelepadBeamAnimation, TelepadBeamKey); break; case CargoTelepadState.Unpowered: sprite.LayerSetVisible(CargoTelepadLayers.Beam, false); @@ -90,7 +92,7 @@ public sealed partial class CargoSystem _player.HasRunningAnimation(uid, player, TelepadBeamKey)) return; - _player.Play(uid, player, CargoTelepadIdleAnimation, TelepadIdleKey); + _player.Play((uid, player), CargoTelepadIdleAnimation, TelepadIdleKey); break; } } diff --git a/Resources/Prototypes/Entities/Structures/cargo_telepad.yml b/Resources/Prototypes/Entities/Structures/cargo_telepad.yml index a99f35a7d9..a3198b58ea 100644 --- a/Resources/Prototypes/Entities/Structures/cargo_telepad.yml +++ b/Resources/Prototypes/Entities/Structures/cargo_telepad.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity id: CargoTelepad parent: [ BaseMachinePowered, ConstructibleMachine ] name: cargo telepad @@ -49,3 +49,4 @@ - type: CollideOnAnchor - type: NameIdentifier group: CargoTelepads + - type: AnimationPlayer From 6a79c247e39112c63a72957fca1bc92867510e18 Mon Sep 17 00:00:00 2001 From: J Date: Sun, 30 Mar 2025 01:12:14 +0000 Subject: [PATCH 08/45] Fix some atmos warnings (#36157) --- Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs | 3 +-- .../Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs b/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs index a80959b7b4..3c3d8f1509 100644 --- a/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs +++ b/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs @@ -1,8 +1,7 @@ -using Content.Shared.Atmos; +using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Piping.Binary.Components; using Content.Shared.IdentityManagement; -using Content.Shared.Localizations; using JetBrains.Annotations; using Robust.Client.UserInterface; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs index 128754bbf8..c277352b6f 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs @@ -1,10 +1,8 @@ using System.Diagnostics.CodeAnalysis; using Content.Server.Atmos.Piping.Binary.Components; using Content.Server.Atmos.Piping.Unary.Components; -using Content.Server.NodeContainer; using Content.Server.NodeContainer.EntitySystems; using Content.Server.NodeContainer.Nodes; -using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Construction.Components; using JetBrains.Annotations; using Robust.Shared.Map; @@ -16,7 +14,6 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems public sealed class GasPortableSystem : EntitySystem { [Dependency] private readonly SharedMapSystem _mapSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; public override void Initialize() From 0ea3792c56e16e10e16998d840931432292a6712 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 30 Mar 2025 03:29:30 +0200 Subject: [PATCH 09/45] Update Credits (#36172) Co-authored-by: PJBot --- Resources/Credits/GitHub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index a6d5b82974..8d8296eeec 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0tito, 0x6273, 12rabbits, 1337dakota, 13spacemen, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 3nderall, 4310v343k, 4dplanner, 612git, 778b, Ablankmann, abregado, Absolute-Potato, Absotively, achookh, Acruid, ActiveMammmoth, actually-reb, ada-please, adamsong, Adeinitas, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aerocrux, Aeshus, Aexolott, Aexxie, africalimedrop, afrokada, AftrLite, AgentSmithRadio, Agoichi, Ahion, aiden, Aisu9, ajcm, AJCM-git, AjexRose, Alekshhh, alexkar598, AlexMorgan3817, alexum418, alexumandxgabriel08x, Alithsko, alliephante, ALMv1, Alpaccalypse, Alpha-Two, AlphaQwerty, Altoids1, amatwiedle, amylizzle, Andre19926, AndrewEyeke, AndreyCamper, Anzarot121, ApolloVector, Appiah, ar4ill, ArchPigeon, ArchRBX, areitpog, Arendian, arimah, Arkanic, ArkiveDev, armoks, Arteben, ArthurMousatov, ArtisticRoomba, artur, AruMoon, ArZarLordOfMango, as334, AsikKEsel, AsnDen, asperger-sind, aspiringLich, astriloqua, august-sun, AutoOtter, AverageNotDoingAnythingEnjoyer, avghdev, Awlod, AzzyIsNotHere, baa14453, BackeTako, BananaFlambe, Baptr0b0t, BarryNorfolk, BasedUser, beck-thompson, bellwetherlogic, ben, benev0, benjamin-burges, BGare, bhespiritu, bibbly, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, BlitzTheSquishy, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, Booblesnoot42, Boolean-Buckeye, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, BriBrooo, Bright0, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, bvelliquette, byondfuckery, c0rigin, c4llv07e, CaasGit, Caconym27, Calecute, Callmore, capnsockless, CaptainMaru, CaptainSqrBeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, catdotjs, Catofquestionableethics, CatTheSystem, centcomofficer24, Centronias, Chaboricks, chairbender, Charlese2, charlie, ChaseFlorom, chavonadelal, Cheackraze, CheddaCheez, cheesePizza2, CheesePlated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, civilCornball, claustro305, Clement-O, clyf, Clyybber, CMDR-Piboy314, cohanna, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, Compilatron144, CookieMasterT, coolboy911, coolmankid12345, Coolsurf6, cooperwallace, corentt, CormosLemming, CrafterKolyan, crazybrain23, creadth, CrigCrag, croilbird, Crotalus, CrudeWax, CrzyPotato, cutemoongod, Cyberboss, d34d10cc, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, DamianX, dan, dangerrevolution, daniel-cr, DanSAussieITS, Daracke, Darkenson, DawBla, Daxxi3, dch-GH, de0rix, Deahaka, dean, DEATHB4DEFEAT, Deatherd, deathride58, DebugOk, Decappi, Decortex, Deeeeja, deepdarkdepths, DeepwaterCreations, Deerstop, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, dexlerxd, dffdff2423, DieselMohawk, digitalic, Dimastra, dinnercalzone, DinoWattz, DisposableCrewmember42, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DoctorBeard, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DR-DOCTOR-EVIL-EVIL, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, DuckManZach, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, dylanstrategie, dylanwhittingham, Dynexust, Easypoller, echo, eclips_e, eden077, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, Emisse, emmafornash, EmoGarbage404, Endecc, eoineoineoin, eris, erohrs2, ERORR404V1, Errant-4, ertanic, esguard, estacaoespacialpirata, eugene, ewokswagger, exincore, exp111, f0x-n3rd, FacePluslll, Fahasor, FairlySadPanda, farrellka-dev, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, FillerVK, FinnishPaladin, firenamefn, Firewars763, FirinMaLazors, Fishfish458, fl-oz, Flareguy, flashgnash, FluffiestFloof, FluffMe, FluidRock, flymo5678, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, Fouin, foxhorn, freeman2651, freeze2222, frobnic8, Froffy025, Fromoriss, froozigiusz, FrostMando, FungiFellow, FunTust, Futuristic-OK, GalacticChimp, gamer3107, gansulalan, GaussiArson, Gaxeer, gbasood, gcoremans, Geekyhobo, genderGeometries, GeneralGaws, Genkail, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, gituhabu, GlassEclipse, GNF54, godisdeadLOL, goet, GoldenCan, Goldminermac, Golinth, GoodWheatley, Gorox221, gradientvera, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GrownSamoyedDog, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, Hardly3D, harikattar, he1acdvv, Hebi, Helm4142, Henry, HerCoyote23, HighTechPuddle, hitomishirichan, hiucko, hivehum, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, Hreno, htmlsystem, hubismal, Hugal31, Huxellberger, Hyenh, hyperb1, hyperDelegate, hyphenationc, i-justuser-i, iaada, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, IMCB, impubbi, imrenq, imweax, indeano, Injazz, Insineer, IntegerTempest, Interrobang01, Intoxicating-Innocence, IProduceWidgets, itsmethom, Itzbenz, iztokbajcar, Jackal298, Jackrost, jacksonzck, Jackw2As, jacob, jamessimo, janekvap, Jark255, Jarmer123, Jaskanbe, JasperJRoth, jbox144, JerryImMouse, jerryimmouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, jmcb, JohnGinnane, johnku1, Jophire, joshepvodka, Jrpl, jukereise, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, JustinWinningham, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, KaiShibaa, kalane15, kalanosh, Kanashi-Panda, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, kerisargit, keronshb, KIBORG04, KieueCaprie, Killerqu00, Kimpes, KingFroozy, kira-er, Kirillcas, Kirus59, Kistras, Kit0vras, KittenColony, klaypexx, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, kosticia, koteq, KrasnoshchekovPavel, Krunklehorn, Kupie, kxvvv, kyupolaris, kzhanik, LaCumbiaDelCoronavirus, lajolico, Lamrr, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, leander-0, leonardo-dabepis, leonidussaks, leonsfriedrich, LeoSantich, LetterN, lettern, Level10Cybermancer, LEVELcat, lever1209, LevitatingTree, Lgibb18, lgruthes, LightVillet, liltenhead, LinkUyx, Litraxx, LittleBuilderJane, LittleNorthStar, LittleNyanCat, lizelive, lmsnoise, localcc, lokachop, Lomcastar, LordCarve, LordEclipse, lucas, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luizwritescode, Lukasz825700516, luminight, lunarcomets, luringens, Lusatia, lvvova1, Lyndomen, lyroth001, lzimann, lzk228, M3739, mac6na6na, MACMAN2003, Macoron, magicalus, magmodius, MagnusCrowe, malchanceux, MaloTV, ManelNavola, manelnavola, Mangohydra, marboww, Markek1, Matz05, max, MaxNox7, maylokana, MehimoNemo, MeltedPixel, MemeProof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, mifia, MilenVolf, MilonPL, Minemoder5000, Minty642, minus1over12, Mirino97, mirrorcult, misandrie, MishaUnity, MissKay1994, MisterImp, MisterMecky, Mith-randalf, MjrLandWhale, mkanke-real, MLGTASTICa, moderatelyaware, modern-nm, mokiros, momo, Moneyl, monotheonist, Moomoobeef, moony, Morb0, MossyGreySlope, mr-bo-jangles, Mr0maks, MrFippik, mrrobdemo, muburu, MureixloI, musicmanvr, MWKane, Myakot, Myctai, N3X15, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, NazrinNya, neutrino-laser, NickPowers43, nikthechampiongr, Nimfar11, ninruB, Nirnael, NIXC, NkoKirkto, nmajask, noctyrnal, noelkathegod, noirogen, nok-ko, NonchalantNoob, NoobyLegion, Nopey, not-gavnaed, notafet, notquitehadouken, NotSoDana, noudoit, noverd, Nox38, NuclearWinter, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, och-och, OctoRocket, OldDanceJacket, OliverOtter, onesch, OnyxTheBrave, Orange-Winds, OrangeMoronage9622, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, paige404, paigemaeforrest, pali6, Palladinium, Pangogie, panzer-iv1, paolordls, partyaddict, patrikturi, PaulRitter, peccneck, Peptide90, peptron1, PeterFuto, PetMudstone, pewter-wiz, Pgriha, Phantom-Lily, pheenty, Phill101, phunnyguy, PilgrimViis, Pill-U, pinkbat5, Piras314, Pireax, Pissachu, pissdemon, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, pok27, poklj, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, ProfanedBane, PROG-MohamedDwidar, prole0, Pronana, ProPandaBear, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykana, psykzz, PuceTint, pumkin69, PuroSlavKing, PursuitInAshes, Putnam3145, qrtDaniil, Quantum-cross, quatre, QueerNB, QuietlyWhisper, qwerltaz, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, RedBookcase, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, ReeZer2, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, RobbyTheFish, robinthedragon, Rockdtben, Rohesie, rok-povsic, rokudara-sen, rolfero, RomanNovo, rosieposieeee, Roudenn, router, RumiTiger, Ruzihm, S1rFl0, S1ss3l, Saakra, Sadie-silly, saga3152, saintmuntzer, Salex08, sam, samgithubaccount, SaphireLattice, SapphicOverload, sarahon, sativaleanne, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, schrodinger71, scrato, Scribbles0, scrivoy, scruq445, scuffedjays, ScumbagDog, SeamLesss, Segonist, semensponge, sephtasm, Serkket, sewerpig, SG6732, sh18rw, Shaddap1, ShadeAware, ShadowCommander, shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SignalWalker, siigiil, Simyon264, sirdragooon, Sirionaut, Sk1tch, SkaldetSkaeg, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, sleepyyapril, slimmslamm, Slyfox333, snebl, snicket, sniperchance, Snowni, snowsignal, SolidusSnek, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, southbridge-fur, sowelipililimute, Soydium, spacelizard, SpaceLizardSky, SpaceManiac, SpaceRox1244, SpaceyLady, spanky-spanky, Sparlight, spartak, SpartanKadence, spderman3333, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, sporkyz, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, starbuckss14, Stealthbomber16, stellar-novas, stewie523, stomf, stopbreaking, stopka-html, StrawberryMoses, Stray-Pyramid, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, superjj18, Supernorn, SweptWasTaken, Sybil, SYNCHRONIC, Szunti, t, Tainakov, takemysoult, tap, TaralGit, Taran, taurie, Tayrtahn, tday93, teamaki, TeenSarlacc, TekuNut, telyonok, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, Tezzaide, TGODiamond, TGRCdev, tgrkzus, ThatGuyUSA, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, TheIntoxicatedCat, thekilk, themias, theomund, TherapyGoth, TheShuEd, thetolbean, thevinter, TheWaffleJesus, Thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, TiniestShark, Titian3, tk-a369, tkdrg, tmtmtl30, ToastEnjoyer, Toby222, TokenStyle, Tollhouse, Toly65, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, Tornado-Technology, tosatur, TotallyLemon, ToxicSonicFan04, Tr1bute, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, tyashley, Tyler-IN, TytosB, Tyzemol, UbaserB, ubis1, UBlueberry, UKNOWH, UltimateJester, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, unusualcrow, Uriende, UristMcDorf, user424242420, Utmanarn, Vaaankas, valentfingerov, valquaint, Varen, Vasilis, VasilisThePikachu, veliebm, Velken, VelonacepsCalyxEggs, veprolet, VerinSenpai, veritable-calamity, Veritius, Vermidia, vero5123, Verslebas, Vexerot, VigersRay, violet754, Visne, vlados1408, VMSolidus, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, Vortebo, vulppine, wafehling, Warentan, WarMechanic, Watermelon914, weaversam8, wertanchik, whateverusername0, whatston3, widgetbeck, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, wrexbe, WTCWR68, xkreksx, xprospero, xRiriq, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, YoungThugSS14, Yousifb26, youtissoum, yunii, YuriyKiss, yuriykiss, zach-hill, Zadeon, zamp, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zero, ZeroDiamond, ZeWaka, zHonys, zionnBE, ZNixian, Zokkie, ZoldorfTheWizard, zonespace27, Zylofan, Zymem, zzylex +0tito, 0x6273, 12rabbits, 1337dakota, 13spacemen, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 3nderall, 4310v343k, 4dplanner, 612git, 778b, Ablankmann, abregado, Absolute-Potato, Absotively, achookh, Acruid, ActiveMammmoth, actually-reb, ada-please, adamsong, Adeinitas, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aerocrux, Aeshus, Aexolott, Aexxie, africalimedrop, afrokada, AftrLite, AgentSmithRadio, Agoichi, Ahion, aiden, Aisu9, ajcm, AJCM-git, AjexRose, Alekshhh, alexkar598, AlexMorgan3817, alexum418, alexumandxgabriel08x, Alithsko, alliephante, ALMv1, Alpaccalypse, Alpha-Two, AlphaQwerty, Altoids1, amatwiedle, amylizzle, Andre19926, AndrewEyeke, AndreyCamper, Anzarot121, ApolloVector, Appiah, ar4ill, ArchPigeon, ArchRBX, areitpog, Arendian, arimah, Arkanic, ArkiveDev, armoks, Arteben, ArthurMousatov, ArtisticRoomba, artur, AruMoon, ArZarLordOfMango, as334, AsikKEsel, AsnDen, asperger-sind, aspiringLich, astriloqua, august-sun, AutoOtter, AverageNotDoingAnythingEnjoyer, avghdev, Awlod, AzzyIsNotHere, baa14453, BackeTako, BananaFlambe, Baptr0b0t, BarryNorfolk, BasedUser, beck-thompson, bellwetherlogic, ben, benev0, benjamin-burges, BGare, bhespiritu, bibbly, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, BlitzTheSquishy, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, Booblesnoot42, Boolean-Buckeye, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, BriBrooo, Bright0, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, bvelliquette, BWTCK, byondfuckery, c0rigin, c4llv07e, CaasGit, Caconym27, Calecute, Callmore, capnsockless, CaptainMaru, CaptainSqrBeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, catdotjs, Catofquestionableethics, CatTheSystem, centcomofficer24, Centronias, Chaboricks, chairbender, Chaoticaa, Charlese2, charlie, ChaseFlorom, chavonadelal, Cheackraze, CheddaCheez, cheesePizza2, CheesePlated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, civilCornball, claustro305, Clement-O, clyf, Clyybber, CMDR-Piboy314, cohanna, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, Compilatron144, CookieMasterT, coolboy911, coolmankid12345, Coolsurf6, cooperwallace, corentt, CormosLemming, CrafterKolyan, crazybrain23, creadth, CrigCrag, croilbird, Crotalus, CrudeWax, CrzyPotato, cutemoongod, Cyberboss, d34d10cc, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, DamianX, dan, dangerrevolution, daniel-cr, DanSAussieITS, Daracke, Darkenson, DawBla, Daxxi3, dch-GH, de0rix, Deahaka, dean, DEATHB4DEFEAT, Deatherd, deathride58, DebugOk, Decappi, Decortex, Deeeeja, deepdarkdepths, DeepwaterCreations, Deerstop, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, dexlerxd, dffdff2423, DieselMohawk, digitalic, Dimastra, dinnercalzone, DinoWattz, DisposableCrewmember42, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DoctorBeard, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DR-DOCTOR-EVIL-EVIL, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, DuckManZach, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, dylanstrategie, dylanwhittingham, Dynexust, Easypoller, echo, eclips_e, eden077, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, Emisse, emmafornash, EmoGarbage404, Endecc, eoineoineoin, eris, erohrs2, ERORR404V1, Errant-4, ertanic, esguard, estacaoespacialpirata, eugene, ewokswagger, exincore, exp111, f0x-n3rd, FacePluslll, Fahasor, FairlySadPanda, farrellka-dev, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, FillerVK, FinnishPaladin, firenamefn, Firewars763, FirinMaLazors, Fishfish458, fl-oz, Flareguy, flashgnash, FluffiestFloof, FluffMe, FluidRock, flymo5678, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, Fouin, foxhorn, freeman2651, freeze2222, frobnic8, Froffy025, Fromoriss, froozigiusz, FrostMando, FungiFellow, FunTust, Futuristic-OK, GalacticChimp, gamer3107, gansulalan, GaussiArson, Gaxeer, gbasood, gcoremans, Geekyhobo, genderGeometries, GeneralGaws, Genkail, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, gituhabu, GlassEclipse, GNF54, godisdeadLOL, goet, GoldenCan, Goldminermac, Golinth, GoodWheatley, Gorox221, gradientvera, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GrownSamoyedDog, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, Hardly3D, harikattar, he1acdvv, Hebi, Helm4142, Henry, HerCoyote23, HighTechPuddle, hitomishirichan, hiucko, hivehum, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, Hreno, htmlsystem, hubismal, Hugal31, Huxellberger, Hyenh, hyperb1, hyperDelegate, hyphenationc, i-justuser-i, iaada, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, IMCB, impubbi, imrenq, imweax, indeano, Injazz, Insineer, IntegerTempest, Interrobang01, Intoxicating-Innocence, IProduceWidgets, itsmethom, Itzbenz, iztokbajcar, Jackal298, Jackrost, jacksonzck, Jackw2As, jacob, jamessimo, janekvap, Jark255, Jarmer123, Jaskanbe, JasperJRoth, jbox144, jerryimmouse, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, jmcb, JohnGinnane, johnku1, Jophire, joshepvodka, Jrpl, jukereise, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, JustinWinningham, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, KaiShibaa, kalane15, kalanosh, Kanashi-Panda, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, kerisargit, keronshb, KIBORG04, KieueCaprie, Killerqu00, Kimpes, KingFroozy, kira-er, Kirillcas, Kirus59, Kistras, Kit0vras, KittenColony, klaypexx, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, kosticia, koteq, KrasnoshchekovPavel, Krunklehorn, Kupie, kxvvv, kyupolaris, kzhanik, LaCumbiaDelCoronavirus, lajolico, Lamrr, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, leander-0, leonardo-dabepis, leonidussaks, leonsfriedrich, LeoSantich, LetterN, lettern, Level10Cybermancer, LEVELcat, lever1209, LevitatingTree, Lgibb18, lgruthes, LightVillet, liltenhead, LinkUyx, Litraxx, LittleBuilderJane, LittleNorthStar, LittleNyanCat, lizelive, lmsnoise, localcc, lokachop, Lomcastar, LordCarve, LordEclipse, lucas, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luizwritescode, Lukasz825700516, luminight, lunarcomets, luringens, Lusatia, lvvova1, Lyndomen, lyroth001, lzimann, lzk228, M3739, mac6na6na, MACMAN2003, Macoron, magicalus, magmodius, MagnusCrowe, malchanceux, MaloTV, ManelNavola, manelnavola, Mangohydra, marboww, Markek1, Matz05, max, MaxNox7, maylokana, MehimoNemo, MeltedPixel, MemeProof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, mifia, MilenVolf, MilonPL, Minemoder5000, Minty642, minus1over12, Mirino97, mirrorcult, misandrie, MishaUnity, MissKay1994, MisterImp, MisterMecky, Mith-randalf, MjrLandWhale, mkanke-real, MLGTASTICa, moderatelyaware, modern-nm, mokiros, momo, Moneyl, monotheonist, Moomoobeef, moony, Morb0, MossyGreySlope, mr-bo-jangles, Mr0maks, MrFippik, mrrobdemo, muburu, MureixloI, musicmanvr, MWKane, Myakot, Myctai, N3X15, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, NazrinNya, neutrino-laser, NickPowers43, nikthechampiongr, Nimfar11, ninruB, Nirnael, NIXC, NkoKirkto, nmajask, noctyrnal, noelkathegod, noirogen, nok-ko, NonchalantNoob, NoobyLegion, Nopey, not-gavnaed, notafet, notquitehadouken, NotSoDana, noudoit, noverd, Nox38, NuclearWinter, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, och-och, OctoRocket, OldDanceJacket, OliverOtter, onesch, OnyxTheBrave, Orange-Winds, OrangeMoronage9622, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, paige404, paigemaeforrest, pali6, Palladinium, Pangogie, panzer-iv1, paolordls, partyaddict, patrikturi, PaulRitter, peccneck, Peptide90, peptron1, PeterFuto, PetMudstone, pewter-wiz, Pgriha, Phantom-Lily, pheenty, Phill101, phunnyguy, PilgrimViis, Pill-U, pinkbat5, Piras314, Pireax, Pissachu, pissdemon, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, pok27, poklj, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, ProfanedBane, PROG-MohamedDwidar, prole0, Pronana, ProPandaBear, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykana, psykzz, PuceTint, pumkin69, PuroSlavKing, PursuitInAshes, Putnam3145, qrtDaniil, Quantum-cross, quatre, QueerNB, QuietlyWhisper, qwerltaz, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, RedBookcase, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, ReeZer2, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, RobbyTheFish, robinthedragon, Rockdtben, Rohesie, rok-povsic, rokudara-sen, rolfero, RomanNovo, rosieposieeee, Roudenn, router, RumiTiger, Ruzihm, S1rFl0, S1ss3l, Saakra, Sadie-silly, saga3152, saintmuntzer, Salex08, sam, samgithubaccount, SaphireLattice, SapphicOverload, sarahon, sativaleanne, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, schrodinger71, scrato, Scribbles0, scrivoy, scruq445, scuffedjays, ScumbagDog, SeamLesss, Segonist, semensponge, sephtasm, Serkket, sewerpig, SG6732, sh18rw, Shaddap1, ShadeAware, ShadowCommander, shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SignalWalker, siigiil, Simyon264, sirdragooon, Sirionaut, Sk1tch, SkaldetSkaeg, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, sleepyyapril, slimmslamm, Slyfox333, snebl, snicket, sniperchance, Snowni, snowsignal, SolidusSnek, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, southbridge-fur, sowelipililimute, Soydium, spacelizard, SpaceLizardSky, SpaceManiac, SpaceRox1244, SpaceyLady, spanky-spanky, Sparlight, spartak, SpartanKadence, spderman3333, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, sporkyz, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, starbuckss14, Stealthbomber16, stellar-novas, stewie523, stomf, stopbreaking, stopka-html, StrawberryMoses, Stray-Pyramid, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, superjj18, Supernorn, SweptWasTaken, Sybil, SYNCHRONIC, Szunti, t, Tainakov, takemysoult, tap, TaralGit, Taran, taurie, Tayrtahn, tday93, teamaki, TeenSarlacc, TekuNut, telyonok, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, Tezzaide, TGODiamond, TGRCdev, tgrkzus, ThatGuyUSA, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, TheIntoxicatedCat, thekilk, themias, theomund, TheProNoob678, TherapyGoth, TheShuEd, thetolbean, thevinter, TheWaffleJesus, Thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, TiniestShark, Titian3, tk-a369, tkdrg, tmtmtl30, ToastEnjoyer, Toby222, TokenStyle, Tollhouse, Toly65, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, Tornado-Technology, tosatur, TotallyLemon, ToxicSonicFan04, Tr1bute, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, tyashley, Tyler-IN, TytosB, Tyzemol, UbaserB, ubis1, UBlueberry, UKNOWH, UltimateJester, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, unusualcrow, Uriende, UristMcDorf, user424242420, Utmanarn, Vaaankas, valentfingerov, valquaint, Varen, Vasilis, VasilisThePikachu, veliebm, Velken, VelonacepsCalyxEggs, veprolet, VerinSenpai, veritable-calamity, Veritius, Vermidia, vero5123, Verslebas, Vexerot, viceemargo, VigersRay, violet754, Visne, vlados1408, VMSolidus, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, Vortebo, vulppine, wafehling, Warentan, WarMechanic, Watermelon914, weaversam8, wertanchik, whateverusername0, whatston3, widgetbeck, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, wrexbe, WTCWR68, xkreksx, xprospero, xRiriq, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, YoungThugSS14, Yousifb26, youtissoum, yunii, yuriykiss, YuriyKiss, zach-hill, Zadeon, zamp, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zero, ZeroDiamond, ZeWaka, zHonys, zionnBE, ZNixian, Zokkie, ZoldorfTheWizard, zonespace27, Zylofan, Zymem, zzylex From d9b8c0a28fe10913943fabb9b8c97920d8089ff3 Mon Sep 17 00:00:00 2001 From: chromiumboy <50505512+chromiumboy@users.noreply.github.com> Date: Sat, 29 Mar 2025 22:58:05 -0500 Subject: [PATCH 10/45] Draw depth bug fix for sentry turrets (#36175) Initial commit --- .../Entities/Objects/Weapons/Guns/Turrets/turrets_energy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_energy.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_energy.yml index 2a37ba09c5..66860ae98c 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_energy.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Turrets/turrets_energy.yml @@ -29,7 +29,7 @@ # Sprites and appearance - type: Sprite sprite: Objects/Weapons/Guns/Turrets/sentry_turret.rsi - drawdepth: FloorObjects + drawdepth: HighFloorObjects granularLayersRendering: true layers: - state: support From ef11f963440e1bdd390962e30a1cf402c1341198 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 30 Mar 2025 15:06:01 +1100 Subject: [PATCH 11/45] Better jetpack emitter (#36093) * Better jetpack emitter Still need particles this just tilts me whenever I see it. * Update Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Content.Client/Movement/Systems/JetpackSystem.cs | 12 ++++++++---- .../Movement/Components/ActiveJetpackComponent.cs | 6 ++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Content.Client/Movement/Systems/JetpackSystem.cs b/Content.Client/Movement/Systems/JetpackSystem.cs index 2954140d79..804736ab7a 100644 --- a/Content.Client/Movement/Systems/JetpackSystem.cs +++ b/Content.Client/Movement/Systems/JetpackSystem.cs @@ -49,13 +49,17 @@ public sealed class JetpackSystem : SharedJetpackSystem // TODO: Please don't copy-paste this I beg // make a generic particle emitter system / actual particles instead. - var query = EntityQueryEnumerator(); + var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp)) + while (query.MoveNext(out var uid, out var comp, out var xform)) { - if (_timing.CurTime < comp.TargetTime) - continue; + if (_transform.InRange(xform.Coordinates, comp.LastCoordinates, comp.MaxDistance)) + { + if (_timing.CurTime < comp.TargetTime) + continue; + } + comp.LastCoordinates = _transform.GetMoverCoordinates(xform.Coordinates); comp.TargetTime = _timing.CurTime + TimeSpan.FromSeconds(comp.EffectCooldown); CreateParticles(uid); diff --git a/Content.Shared/Movement/Components/ActiveJetpackComponent.cs b/Content.Shared/Movement/Components/ActiveJetpackComponent.cs index 615dc3aee4..03c2a8345d 100644 --- a/Content.Shared/Movement/Components/ActiveJetpackComponent.cs +++ b/Content.Shared/Movement/Components/ActiveJetpackComponent.cs @@ -1,4 +1,5 @@ using Robust.Shared.GameStates; +using Robust.Shared.Map; namespace Content.Shared.Movement.Components; @@ -9,5 +10,10 @@ namespace Content.Shared.Movement.Components; public sealed partial class ActiveJetpackComponent : Component { public float EffectCooldown = 0.3f; + + public float MaxDistance = 0.7f; + + public EntityCoordinates LastCoordinates; + public TimeSpan TargetTime = TimeSpan.Zero; } From b1f542a54cc77d74d57d3c3c9cfeb43e2b97f834 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 30 Mar 2025 04:07:08 +0000 Subject: [PATCH 12/45] 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 8a5e4bdf05..fd524d1c7c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: K-Dynamic - changes: - - message: Solar assembly crate now comes with 10 flatpacks and 20 glass to make - expansion and repairs easier, as well as increasing in price from 525 to 1250 - spesos. - type: Tweak - id: 7617 - time: '2024-11-16T04:30:48.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33019 - author: Aquif changes: - message: There is now a button to view your admin remarks in the character editor, @@ -3886,3 +3877,10 @@ id: 8116 time: '2025-03-29T20:09:34.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/35193 +- author: metalgearsloth + changes: + - message: Jetpacks emit particles more frequently. + type: Tweak + id: 8117 + time: '2025-03-30T04:06:01.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36093 From 0ff70fdb409c1495c2e6c89fec749fc0e45b1d9a Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:02:45 +1100 Subject: [PATCH 13/45] Implement field-deltas for melee (#33977) * Implement field-deltas for melee * Review --- .../Zombies/ZombieSystem.Transform.cs | 10 +++++ .../Weapons/Melee/MeleeWeaponComponent.cs | 40 +++++++++---------- .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 34 ++++++++++++---- 3 files changed, 55 insertions(+), 29 deletions(-) diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index b393850497..47d94984c0 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -132,6 +132,16 @@ public sealed partial class ZombieSystem melee.Angle = 0.0f; melee.HitSound = zombiecomp.BiteSound; + DirtyFields(target, melee, null, fields: + [ + nameof(MeleeWeaponComponent.Animation), + nameof(MeleeWeaponComponent.WideAnimation), + nameof(MeleeWeaponComponent.AltDisarm), + nameof(MeleeWeaponComponent.Range), + nameof(MeleeWeaponComponent.Angle), + nameof(MeleeWeaponComponent.HitSound), + ]); + if (mobState.CurrentState == MobState.Alive) { // Groaning when damaged diff --git a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs index 212c03475c..84e88156ad 100644 --- a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs +++ b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs @@ -10,7 +10,7 @@ namespace Content.Shared.Weapons.Melee; /// /// When given to a mob lets them do unarmed attacks, or when given to an item lets someone wield it to do attacks. /// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true), AutoGenerateComponentPause] public sealed partial class MeleeWeaponComponent : Component { // TODO: This is becoming bloated as shit. @@ -18,28 +18,26 @@ public sealed partial class MeleeWeaponComponent : Component /// /// Does this entity do a disarm on alt attack. /// - [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + [DataField, AutoNetworkedField] public bool AltDisarm = true; /// /// Should the melee weapon's damage stats be examinable. /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField] + [DataField, AutoNetworkedField] public bool Hidden; /// /// Next time this component is allowed to light attack. Heavy attacks are wound up and never have a cooldown. /// [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] - [ViewVariables(VVAccess.ReadWrite)] [AutoPausedField] public TimeSpan NextAttack; /// /// Starts attack cooldown when equipped if true. /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField, AutoNetworkedField] public bool ResetOnHandSelected = true; /* @@ -51,72 +49,70 @@ public sealed partial class MeleeWeaponComponent : Component /// /// How many times we can attack per second. /// - [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + [DataField, AutoNetworkedField] public float AttackRate = 1f; /// /// Are we currently holding down the mouse for an attack. /// Used so we can't just hold the mouse button and attack constantly. /// - [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + [AutoNetworkedField] public bool Attacking = false; /// /// If true, attacks will be repeated automatically without requiring the mouse button to be lifted. /// - [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + [DataField, AutoNetworkedField] public bool AutoAttack; /// /// If true, attacks will bypass armor resistances. /// - [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + [DataField, AutoNetworkedField] public bool ResistanceBypass = false; - + /// /// Base damage for this weapon. Can be modified via heavy damage or other means. /// - [DataField(required: true)] - [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + [DataField(required: true), AutoNetworkedField] public DamageSpecifier Damage = default!; - [DataField] - [ViewVariables(VVAccess.ReadWrite)] + [DataField, AutoNetworkedField] public FixedPoint2 BluntStaminaDamageFactor = FixedPoint2.New(0.5f); /// /// Multiplies damage by this amount for single-target attacks. /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField, AutoNetworkedField] public FixedPoint2 ClickDamageModifier = FixedPoint2.New(1); // TODO: Temporarily 1.5 until interactionoutline is adjusted to use melee, then probably drop to 1.2 /// /// Nearest edge range to hit an entity. /// - [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + [DataField, AutoNetworkedField] public float Range = 1.5f; /// /// Total width of the angle for wide attacks. /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField, AutoNetworkedField] public Angle Angle = Angle.FromDegrees(60); - [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + [DataField, AutoNetworkedField] public EntProtoId Animation = "WeaponArcPunch"; - [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + [DataField, AutoNetworkedField] public EntProtoId WideAnimation = "WeaponArcSlash"; /// /// Rotation of the animation. /// 0 degrees means the top faces the attacker. /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField, AutoNetworkedField] public Angle WideAnimationRotation = Angle.Zero; - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField, AutoNetworkedField] public bool SwingLeft; diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 43be9a5b14..947c969a3e 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -104,7 +104,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem if (gun.NextFire > component.NextAttack) { component.NextAttack = gun.NextFire; - Dirty(uid, component); + DirtyField(uid, component, nameof(MeleeWeaponComponent.NextAttack)); } } @@ -128,7 +128,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem return; component.NextAttack = minimum; - Dirty(uid, component); + DirtyField(uid, component, nameof(MeleeWeaponComponent.NextAttack)); } private void OnGetBonusMeleeDamage(EntityUid uid, BonusMeleeDamageComponent component, ref GetMeleeDamageEvent args) @@ -168,7 +168,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem return; weapon.Attacking = false; - Dirty(weaponUid, weapon); + DirtyField(weaponUid, weapon, nameof(MeleeWeaponComponent.Attacking)); } private void OnLightAttack(LightAttackEvent msg, EntitySessionEventArgs args) @@ -392,7 +392,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem swings++; } - Dirty(weaponUid, weapon); + DirtyField(weaponUid, weapon, nameof(MeleeWeaponComponent.NextAttack)); // Do this AFTER attack so it doesn't spam every tick var ev = new AttemptMeleeEvent(); @@ -442,6 +442,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem RaiseLocalEvent(user, ref attackEv); weapon.Attacking = true; + DirtyField(weaponUid, weapon, nameof(MeleeWeaponComponent.Attacking)); return true; } @@ -838,15 +839,21 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem //Setting deactivated damage to the weapon's regular value before changing it. itemToggleMelee.DeactivatedDamage ??= meleeWeapon.Damage; meleeWeapon.Damage = itemToggleMelee.ActivatedDamage; + DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.Damage)); } - meleeWeapon.HitSound = itemToggleMelee.ActivatedSoundOnHit; + if (meleeWeapon.HitSound?.Equals(itemToggleMelee.ActivatedSoundOnHit) != true) + { + meleeWeapon.HitSound = itemToggleMelee.ActivatedSoundOnHit; + DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.HitSound)); + } if (itemToggleMelee.ActivatedSoundOnHitNoDamage != null) { //Setting the deactivated sound on no damage hit to the weapon's regular value before changing it. itemToggleMelee.DeactivatedSoundOnHitNoDamage ??= meleeWeapon.NoDamageSound; meleeWeapon.NoDamageSound = itemToggleMelee.ActivatedSoundOnHitNoDamage; + DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.NoDamageSound)); } if (itemToggleMelee.ActivatedSoundOnSwing != null) @@ -854,28 +861,41 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem //Setting the deactivated sound on no damage hit to the weapon's regular value before changing it. itemToggleMelee.DeactivatedSoundOnSwing ??= meleeWeapon.SwingSound; meleeWeapon.SwingSound = itemToggleMelee.ActivatedSoundOnSwing; + DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.SwingSound)); } if (itemToggleMelee.DeactivatedSecret) + { meleeWeapon.Hidden = false; + } } else { if (itemToggleMelee.DeactivatedDamage != null) + { meleeWeapon.Damage = itemToggleMelee.DeactivatedDamage; + DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.Damage)); + } meleeWeapon.HitSound = itemToggleMelee.DeactivatedSoundOnHit; + DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.HitSound)); if (itemToggleMelee.DeactivatedSoundOnHitNoDamage != null) + { meleeWeapon.NoDamageSound = itemToggleMelee.DeactivatedSoundOnHitNoDamage; + DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.NoDamageSound)); + } if (itemToggleMelee.DeactivatedSoundOnSwing != null) + { meleeWeapon.SwingSound = itemToggleMelee.DeactivatedSoundOnSwing; + DirtyField(uid, meleeWeapon, nameof(MeleeWeaponComponent.SwingSound)); + } if (itemToggleMelee.DeactivatedSecret) + { meleeWeapon.Hidden = true; + } } - - Dirty(uid, meleeWeapon); } } From 085e28dd00971a44dc60e89220430f57d386c283 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 30 Mar 2025 18:06:24 +1100 Subject: [PATCH 14/45] Fix LoadGameMap running MapInit sometimes (#35241) The map loadpath keeps it as not being mapinit but the grid one does not so this standardises them slightly. --- Content.Server/GameTicking/GameTicker.RoundFlow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index 7ab6cfdc63..ce60aae3d9 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -196,7 +196,7 @@ namespace Content.Server.GameTicking if (ev.GameMap.IsGrid) { - var mapUid = _map.CreateMap(out mapId); + var mapUid = _map.CreateMap(out mapId, runMapInit: options?.InitializeMaps ?? false); if (!_loader.TryLoadGrid(mapId, ev.GameMap.MapPath, out var grid, From 1f1cf06978b80669b206efc2f68ed957c7444417 Mon Sep 17 00:00:00 2001 From: K-Dynamic <20566341+K-Dynamic@users.noreply.github.com> Date: Mon, 31 Mar 2025 00:00:43 +1200 Subject: [PATCH 15/45] More responsive votekick system (reduce timer and successive timeout) (#36044) * reduce votekick timer from 60 to 20 seconds * votekick timeout from 120 to 30 seconds * votekick timer duration from 20 seconds to 45, successive votekick timeout from 30 to 60 seconds --- Content.Shared/CCVar/CCVars.Vote.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Shared/CCVar/CCVars.Vote.cs b/Content.Shared/CCVar/CCVars.Vote.cs index ee9fee7d3d..deb860e03c 100644 --- a/Content.Shared/CCVar/CCVars.Vote.cs +++ b/Content.Shared/CCVar/CCVars.Vote.cs @@ -146,13 +146,13 @@ public sealed partial class CCVars /// The delay for which two votekicks are allowed to be made by separate people, in seconds. /// public static readonly CVarDef VotekickTimeout = - CVarDef.Create("votekick.timeout", 120f, CVar.SERVERONLY); + CVarDef.Create("votekick.timeout", 60f, CVar.SERVERONLY); /// /// Sets the duration of the votekick vote timer. /// public static readonly CVarDef - VotekickTimer = CVarDef.Create("votekick.timer", 60, CVar.SERVERONLY); + VotekickTimer = CVarDef.Create("votekick.timer", 45, CVar.SERVERONLY); /// /// Config for how many hours playtime a player must have to get protection from the Raider votekick type when playing as an antag. From 0180a9db507b136eb325e268cdd158450e6cfda5 Mon Sep 17 00:00:00 2001 From: J Date: Sun, 30 Mar 2025 12:06:43 +0000 Subject: [PATCH 16/45] Examine warnings cleanup (#36162) * Examine warnings cleanup * Revert unnecessary change * SpriteSystem naming conventions --- Content.Client/Examine/ExamineButton.cs | 12 +++++------- Content.Client/Examine/ExamineSystem.cs | 3 ++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Content.Client/Examine/ExamineButton.cs b/Content.Client/Examine/ExamineButton.cs index 839e08f3d4..3d5ac211e4 100644 --- a/Content.Client/Examine/ExamineButton.cs +++ b/Content.Client/Examine/ExamineButton.cs @@ -1,11 +1,7 @@ -using Content.Client.ContextMenu.UI; -using Content.Client.Stylesheets; using Content.Shared.Verbs; -using Robust.Client.AutoGenerated; -using Robust.Client.Graphics; +using Robust.Client.GameObjects; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; -using Robust.Client.UserInterface.XAML; using Robust.Client.Utility; using Robust.Shared.Utility; @@ -27,14 +23,16 @@ public sealed class ExamineButton : ContainerButton public TextureRect Icon; public ExamineVerb Verb; + private SpriteSystem _sprite; - public ExamineButton(ExamineVerb verb) + public ExamineButton(ExamineVerb verb, SpriteSystem spriteSystem) { Margin = new Thickness(Thickness, Thickness, Thickness, Thickness); SetOnlyStyleClass(StyleClassExamineButton); Verb = verb; + _sprite = spriteSystem; if (verb.Disabled) { @@ -61,7 +59,7 @@ public sealed class ExamineButton : ContainerButton if (verb.Icon != null) { - Icon.Texture = verb.Icon.Frame0(); + Icon.Texture = _sprite.Frame0(verb.Icon); Icon.Stretch = TextureRect.StretchMode.KeepAspectCentered; AddChild(Icon); diff --git a/Content.Client/Examine/ExamineSystem.cs b/Content.Client/Examine/ExamineSystem.cs index 07694ac24a..2e8d95c978 100644 --- a/Content.Client/Examine/ExamineSystem.cs +++ b/Content.Client/Examine/ExamineSystem.cs @@ -30,6 +30,7 @@ namespace Content.Client.Examine [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IEyeManager _eyeManager = default!; [Dependency] private readonly VerbSystem _verbSystem = default!; + [Dependency] private readonly SpriteSystem _sprite = default!; public const string StyleClassEntityTooltip = "entity-tooltip"; @@ -332,7 +333,7 @@ namespace Content.Client.Examine if (!examine.ShowOnExamineTooltip) continue; - var button = new ExamineButton(examine); + var button = new ExamineButton(examine, _sprite); if (examine.HoverVerb) { From 504e70be2b5223099b317179f2d462bf89bdb867 Mon Sep 17 00:00:00 2001 From: J Date: Sun, 30 Mar 2025 12:07:34 +0000 Subject: [PATCH 17/45] Chemistry warnings cleanup (#36160) * Chemistry warnings cleanup * Fixing failed ITest * Better entity instantiation * Caching spritesystem and entity instantiation improvement * Correcting naming conventions * Rearranging dependency caching --- Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs | 10 ++++++++-- .../Chemistry/Visualizers/FoamVisualizerSystem.cs | 4 ++-- .../Chemistry/Visualizers/VaporVisualizerSystem.cs | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs index 807ec4c1e7..e264b859a0 100644 --- a/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs +++ b/Content.Client/Chemistry/UI/ChemMasterWindow.xaml.cs @@ -6,7 +6,6 @@ using Robust.Client.AutoGenerated; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; -using Robust.Client.Utility; using Robust.Shared.Prototypes; using Robust.Shared.Utility; using System.Linq; @@ -14,6 +13,7 @@ using System.Numerics; using Content.Shared.FixedPoint; using Robust.Client.Graphics; using static Robust.Client.UserInterface.Controls.BoxContainer; +using Robust.Client.GameObjects; namespace Content.Client.Chemistry.UI { @@ -24,6 +24,10 @@ namespace Content.Client.Chemistry.UI public sealed partial class ChemMasterWindow : FancyWindow { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IEntityManager _entityManager = default!; + + private readonly SpriteSystem _sprite; + public event Action? OnReagentButtonPressed; public readonly Button[] PillTypeButtons; @@ -38,6 +42,8 @@ namespace Content.Client.Chemistry.UI RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); + _sprite = _entityManager.System(); + // Pill type selection buttons, in total there are 20 pills. // Pill rsi file should have states named as pill1, pill2, and so on. var resourcePath = new ResPath(PillsRsiPath); @@ -69,7 +75,7 @@ namespace Content.Client.Chemistry.UI var specifier = new SpriteSpecifier.Rsi(resourcePath, "pill" + (i + 1)); TextureRect pillTypeTexture = new TextureRect { - Texture = specifier.Frame0(), + Texture = _sprite.Frame0(specifier), TextureScale = new Vector2(1.75f, 1.75f), Stretch = TextureRect.StretchMode.KeepCentered, }; diff --git a/Content.Client/Chemistry/Visualizers/FoamVisualizerSystem.cs b/Content.Client/Chemistry/Visualizers/FoamVisualizerSystem.cs index 2ee88956ff..2f895718c7 100644 --- a/Content.Client/Chemistry/Visualizers/FoamVisualizerSystem.cs +++ b/Content.Client/Chemistry/Visualizers/FoamVisualizerSystem.cs @@ -1,4 +1,4 @@ -using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components; using Robust.Client.Animations; using Robust.Client.GameObjects; using Robust.Shared.Timing; @@ -37,7 +37,7 @@ public sealed class FoamVisualizerSystem : VisualizerSystem(uid, out var animPlayer) && !AnimationSystem.HasRunningAnimation(uid, animPlayer, VaporVisualsComponent.AnimationKey)) { - AnimationSystem.Play(uid, animPlayer, comp.VaporFlick, VaporVisualsComponent.AnimationKey); + AnimationSystem.Play((uid, animPlayer), comp.VaporFlick, VaporVisualsComponent.AnimationKey); } } From 4f848e814f74e4ede2a13209ce07280363e31887 Mon Sep 17 00:00:00 2001 From: J Date: Sun, 30 Mar 2025 13:06:20 +0000 Subject: [PATCH 18/45] Movement systems warning cleanup (#36161) * Movement systems warning cleanup * Revert unnecessary change * Reverting variable removal and changing entity query * Reverting VV removals * LocalEntity does in fact exist --- .../Movement/Systems/ClientSpriteMovementSystem.cs | 3 --- Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs | 6 +----- Content.Server/Movement/RotateEyesCommand.cs | 7 ++++--- Content.Server/Movement/Systems/BoundarySystem.cs | 2 +- Content.Server/Movement/Systems/PullController.cs | 6 +++--- 5 files changed, 9 insertions(+), 15 deletions(-) diff --git a/Content.Client/Movement/Systems/ClientSpriteMovementSystem.cs b/Content.Client/Movement/Systems/ClientSpriteMovementSystem.cs index 1700796ede..a6265204b7 100644 --- a/Content.Client/Movement/Systems/ClientSpriteMovementSystem.cs +++ b/Content.Client/Movement/Systems/ClientSpriteMovementSystem.cs @@ -1,7 +1,6 @@ using Content.Shared.Movement.Components; using Content.Shared.Movement.Systems; using Robust.Client.GameObjects; -using Robust.Shared.Timing; namespace Content.Client.Movement.Systems; @@ -10,8 +9,6 @@ namespace Content.Client.Movement.Systems; /// public sealed class ClientSpriteMovementSystem : SharedSpriteMovementSystem { - [Dependency] private readonly IGameTiming _timing = default!; - private EntityQuery _spriteQuery; public override void Initialize() diff --git a/Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs b/Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs index 9e8ca9a9c9..eb524cf4ee 100644 --- a/Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs +++ b/Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs @@ -1,8 +1,6 @@ using System.Numerics; using Content.Client.Movement.Components; using Content.Shared.Camera; -using Content.Shared.Inventory; -using Content.Shared.Movement.Systems; using Robust.Client.Graphics; using Robust.Client.Input; using Robust.Shared.Map; @@ -16,8 +14,6 @@ public sealed partial class EyeCursorOffsetSystem : EntitySystem [Dependency] private readonly IInputManager _inputManager = default!; [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly SharedContentEyeSystem _contentEye = default!; - [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IClyde _clyde = default!; // This value is here to make sure the user doesn't have to move their mouse @@ -42,7 +38,7 @@ public sealed partial class EyeCursorOffsetSystem : EntitySystem public Vector2? OffsetAfterMouse(EntityUid uid, EyeCursorOffsetComponent? component) { - var localPlayer = _player.LocalPlayer?.ControlledEntity; + var localPlayer = _player.LocalEntity; var mousePos = _inputManager.MouseScreenPosition; var screenSize = _clyde.MainWindow.Size; var minValue = MathF.Min(screenSize.X / 2, screenSize.Y / 2) * _edgeOffset; diff --git a/Content.Server/Movement/RotateEyesCommand.cs b/Content.Server/Movement/RotateEyesCommand.cs index 6395b93cab..733d341820 100644 --- a/Content.Server/Movement/RotateEyesCommand.cs +++ b/Content.Server/Movement/RotateEyesCommand.cs @@ -28,14 +28,15 @@ public sealed class RotateEyesCommand : IConsoleCommand } var count = 0; - - foreach (var mover in entManager.EntityQuery(true)) + var query = entManager.EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var mover)) { if (mover.TargetRelativeRotation.Equals(rotation)) continue; mover.TargetRelativeRotation = rotation; - entManager.Dirty(mover); + + entManager.Dirty(uid, mover); count++; } diff --git a/Content.Server/Movement/Systems/BoundarySystem.cs b/Content.Server/Movement/Systems/BoundarySystem.cs index a798f1052d..a82348926e 100644 --- a/Content.Server/Movement/Systems/BoundarySystem.cs +++ b/Content.Server/Movement/Systems/BoundarySystem.cs @@ -27,6 +27,6 @@ public sealed class BoundarySystem : EntitySystem // If for whatever reason you want to yeet them to the other side. // offset = new Angle(MathF.PI).RotateVec(offset); - _xform.SetWorldPosition(otherXform, center + offset); + _xform.SetWorldPosition((args.OtherEntity, otherXform), center + offset); } } diff --git a/Content.Server/Movement/Systems/PullController.cs b/Content.Server/Movement/Systems/PullController.cs index f28ea952c8..40345a5867 100644 --- a/Content.Server/Movement/Systems/PullController.cs +++ b/Content.Server/Movement/Systems/PullController.cs @@ -139,7 +139,7 @@ public sealed class PullController : VirtualController // Cap the distance var range = 2f; - var fromUserCoords = coords.WithEntityId(player, EntityManager); + var fromUserCoords = _transformSystem.WithEntityId(coords, player); var userCoords = new EntityCoordinates(player, Vector2.Zero); if (!_transformSystem.InRange(coords, userCoords, range)) @@ -157,7 +157,7 @@ public sealed class PullController : VirtualController } fromUserCoords = new EntityCoordinates(player, direction.Normalized() * (range - 0.01f)); - coords = fromUserCoords.WithEntityId(coords.EntityId); + coords = _transformSystem.WithEntityId(fromUserCoords, coords.EntityId); } var moving = EnsureComp(pulled!.Value); @@ -248,7 +248,7 @@ public sealed class PullController : VirtualController var pullerXform = _xformQuery.Get(puller); var pullerPosition = TransformSystem.GetMapCoordinates(pullerXform); - var movingTo = mover.MovingTo.ToMap(EntityManager, TransformSystem); + var movingTo = TransformSystem.ToMapCoordinates(mover.MovingTo); if (movingTo.MapId != pullerPosition.MapId) { From 47f8aefc255bfadf839aaf8add82921727bc3407 Mon Sep 17 00:00:00 2001 From: J Date: Sun, 30 Mar 2025 13:16:46 +0000 Subject: [PATCH 19/45] Anomaly warnings cleanup (#36188) --- Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs | 6 +++--- Content.Server/Anomaly/Effects/TechAnomalySystem.cs | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs b/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs index 23e0e472f0..7983493961 100644 --- a/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/ProjectileAnomalySystem.cs @@ -81,14 +81,14 @@ public sealed class ProjectileAnomalySystem : EntitySystem EntityCoordinates targetCoords, float severity) { - var mapPos = coords.ToMap(EntityManager, _xform); + var mapPos = _xform.ToMapCoordinates(coords); var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out _) - ? coords.WithEntityId(gridUid, EntityManager) + ? _xform.WithEntityId(coords, gridUid) : new(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position); var ent = Spawn(component.ProjectilePrototype, spawnCoords); - var direction = targetCoords.ToMapPos(EntityManager, _xform) - mapPos.Position; + var direction = _xform.ToMapCoordinates(targetCoords).Position - mapPos.Position; if (!TryComp(ent, out var comp)) return; diff --git a/Content.Server/Anomaly/Effects/TechAnomalySystem.cs b/Content.Server/Anomaly/Effects/TechAnomalySystem.cs index 983cf2c8f4..1f3a6520d4 100644 --- a/Content.Server/Anomaly/Effects/TechAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/TechAnomalySystem.cs @@ -16,7 +16,6 @@ public sealed class TechAnomalySystem : EntitySystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly BeamSystem _beam = default!; [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly EmagSystem _emag = default!; public override void Initialize() { From 89e59b391d840003e2c3f71859b10267ff9fc7bf Mon Sep 17 00:00:00 2001 From: Milon Date: Sun, 30 Mar 2025 15:41:11 +0200 Subject: [PATCH 20/45] use manual component state for BaseEmitSoundComponent (#35030) * why * cursed --- .../Components/BaseEmitSoundComponent.cs | 21 ++++++++-- .../EmitSoundOnActivateComponent.cs | 2 +- .../Components/EmitSoundOnCollideComponent.cs | 5 +-- .../Components/EmitSoundOnDropComponent.cs | 4 +- .../EmitSoundOnInteractUsingComponent.cs | 4 +- .../Components/EmitSoundOnLandComponent.cs | 4 +- .../Components/EmitSoundOnPickupComponent.cs | 4 +- .../Components/EmitSoundOnSpawnComponent.cs | 4 +- .../Components/EmitSoundOnThrowComponent.cs | 4 +- .../Components/EmitSoundOnUseComponent.cs | 4 +- .../Components/SpamEmitSoundComponent.cs | 3 +- .../SpamEmitSoundRequirePowerComponent.cs | 4 +- Content.Shared/Sound/SharedEmitSoundSystem.cs | 42 +++++++++++++++++++ 13 files changed, 75 insertions(+), 30 deletions(-) diff --git a/Content.Shared/Sound/Components/BaseEmitSoundComponent.cs b/Content.Shared/Sound/Components/BaseEmitSoundComponent.cs index 870d20457e..7011f72ef0 100644 --- a/Content.Shared/Sound/Components/BaseEmitSoundComponent.cs +++ b/Content.Shared/Sound/Components/BaseEmitSoundComponent.cs @@ -1,4 +1,6 @@ using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; namespace Content.Shared.Sound.Components; @@ -8,10 +10,9 @@ namespace Content.Shared.Sound.Components; /// public abstract partial class BaseEmitSoundComponent : Component { - public static readonly AudioParams DefaultParams = AudioParams.Default.WithVolume(-2f); - - [AutoNetworkedField] - [ViewVariables(VVAccess.ReadWrite)] + /// + /// The to play. + /// [DataField(required: true)] public SoundSpecifier? Sound; @@ -22,3 +23,15 @@ public abstract partial class BaseEmitSoundComponent : Component [DataField] public bool Positional; } + +/// +/// Represents the state of . +/// +/// This is obviously very cursed, but since the BaseEmitSoundComponent is abstract, we cannot network it. +/// AutoGenerateComponentState attribute won't work here, and since everything revolves around inheritance for some fucking reason, +/// there's no better way of doing this. +[Serializable, NetSerializable] +public struct EmitSoundComponentState(SoundSpecifier? sound) : IComponentState +{ + public SoundSpecifier? Sound { get; } = sound; +} diff --git a/Content.Shared/Sound/Components/EmitSoundOnActivateComponent.cs b/Content.Shared/Sound/Components/EmitSoundOnActivateComponent.cs index 810f132d83..d6aa42177e 100644 --- a/Content.Shared/Sound/Components/EmitSoundOnActivateComponent.cs +++ b/Content.Shared/Sound/Components/EmitSoundOnActivateComponent.cs @@ -17,6 +17,6 @@ public sealed partial class EmitSoundOnActivateComponent : BaseEmitSoundComponen /// otherwise this might enable sound spamming, as use-delays are only initiated if the interaction was /// handled. /// - [DataField("handle")] + [DataField] public bool Handle = true; } diff --git a/Content.Shared/Sound/Components/EmitSoundOnCollideComponent.cs b/Content.Shared/Sound/Components/EmitSoundOnCollideComponent.cs index a2cdd63ab7..4cdea05220 100644 --- a/Content.Shared/Sound/Components/EmitSoundOnCollideComponent.cs +++ b/Content.Shared/Sound/Components/EmitSoundOnCollideComponent.cs @@ -11,13 +11,12 @@ public sealed partial class EmitSoundOnCollideComponent : BaseEmitSoundComponent /// /// Minimum velocity required for the sound to play. /// - [ViewVariables(VVAccess.ReadWrite), DataField("minVelocity")] + [DataField("minVelocity")] public float MinimumVelocity = 3f; /// /// To avoid sound spam add a cooldown to it. /// - [ViewVariables(VVAccess.ReadWrite), DataField("nextSound", customTypeSerializer: typeof(TimeOffsetSerializer))] - [AutoPausedField] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField] public TimeSpan NextSound; } diff --git a/Content.Shared/Sound/Components/EmitSoundOnDropComponent.cs b/Content.Shared/Sound/Components/EmitSoundOnDropComponent.cs index 5e04295607..64ed5e60dc 100644 --- a/Content.Shared/Sound/Components/EmitSoundOnDropComponent.cs +++ b/Content.Shared/Sound/Components/EmitSoundOnDropComponent.cs @@ -6,6 +6,4 @@ namespace Content.Shared.Sound.Components; /// Simple sound emitter that emits sound on entity drop /// [RegisterComponent, NetworkedComponent] -public sealed partial class EmitSoundOnDropComponent : BaseEmitSoundComponent -{ -} +public sealed partial class EmitSoundOnDropComponent : BaseEmitSoundComponent; diff --git a/Content.Shared/Sound/Components/EmitSoundOnInteractUsingComponent.cs b/Content.Shared/Sound/Components/EmitSoundOnInteractUsingComponent.cs index 49118d9799..d0b16fcec8 100644 --- a/Content.Shared/Sound/Components/EmitSoundOnInteractUsingComponent.cs +++ b/Content.Shared/Sound/Components/EmitSoundOnInteractUsingComponent.cs @@ -1,5 +1,4 @@ using Content.Shared.Whitelist; -using Robust.Shared.Prototypes; using Robust.Shared.GameStates; namespace Content.Shared.Sound.Components; @@ -10,6 +9,9 @@ namespace Content.Shared.Sound.Components; [RegisterComponent, NetworkedComponent] public sealed partial class EmitSoundOnInteractUsingComponent : BaseEmitSoundComponent { + /// + /// The for the entities that can use this item. + /// [DataField(required: true)] public EntityWhitelist Whitelist = new(); } diff --git a/Content.Shared/Sound/Components/EmitSoundOnLandComponent.cs b/Content.Shared/Sound/Components/EmitSoundOnLandComponent.cs index 2d33a7f5f2..d3fceb85dd 100644 --- a/Content.Shared/Sound/Components/EmitSoundOnLandComponent.cs +++ b/Content.Shared/Sound/Components/EmitSoundOnLandComponent.cs @@ -6,6 +6,4 @@ namespace Content.Shared.Sound.Components; /// Simple sound emitter that emits sound on LandEvent /// [RegisterComponent, NetworkedComponent] -public sealed partial class EmitSoundOnLandComponent : BaseEmitSoundComponent -{ -} +public sealed partial class EmitSoundOnLandComponent : BaseEmitSoundComponent; diff --git a/Content.Shared/Sound/Components/EmitSoundOnPickupComponent.cs b/Content.Shared/Sound/Components/EmitSoundOnPickupComponent.cs index ee4b4b1688..dcf73b7ac2 100644 --- a/Content.Shared/Sound/Components/EmitSoundOnPickupComponent.cs +++ b/Content.Shared/Sound/Components/EmitSoundOnPickupComponent.cs @@ -6,6 +6,4 @@ namespace Content.Shared.Sound.Components; /// Simple sound emitter that emits sound on entity pickup /// [RegisterComponent, NetworkedComponent] -public sealed partial class EmitSoundOnPickupComponent : BaseEmitSoundComponent -{ -} +public sealed partial class EmitSoundOnPickupComponent : BaseEmitSoundComponent; diff --git a/Content.Shared/Sound/Components/EmitSoundOnSpawnComponent.cs b/Content.Shared/Sound/Components/EmitSoundOnSpawnComponent.cs index 49d40ce185..20d39b3460 100644 --- a/Content.Shared/Sound/Components/EmitSoundOnSpawnComponent.cs +++ b/Content.Shared/Sound/Components/EmitSoundOnSpawnComponent.cs @@ -6,6 +6,4 @@ namespace Content.Shared.Sound.Components; /// Simple sound emitter that emits sound on entity spawn. /// [RegisterComponent, NetworkedComponent] -public sealed partial class EmitSoundOnSpawnComponent : BaseEmitSoundComponent -{ -} +public sealed partial class EmitSoundOnSpawnComponent : BaseEmitSoundComponent; diff --git a/Content.Shared/Sound/Components/EmitSoundOnThrowComponent.cs b/Content.Shared/Sound/Components/EmitSoundOnThrowComponent.cs index 5e3650a4a3..f8c0d1181b 100644 --- a/Content.Shared/Sound/Components/EmitSoundOnThrowComponent.cs +++ b/Content.Shared/Sound/Components/EmitSoundOnThrowComponent.cs @@ -6,6 +6,4 @@ namespace Content.Shared.Sound.Components; /// Simple sound emitter that emits sound on ThrowEvent /// [RegisterComponent, NetworkedComponent] -public sealed partial class EmitSoundOnThrowComponent : BaseEmitSoundComponent -{ -} +public sealed partial class EmitSoundOnThrowComponent : BaseEmitSoundComponent; diff --git a/Content.Shared/Sound/Components/EmitSoundOnUseComponent.cs b/Content.Shared/Sound/Components/EmitSoundOnUseComponent.cs index a99a01cec4..ec7a277e92 100644 --- a/Content.Shared/Sound/Components/EmitSoundOnUseComponent.cs +++ b/Content.Shared/Sound/Components/EmitSoundOnUseComponent.cs @@ -5,7 +5,7 @@ namespace Content.Shared.Sound.Components; /// /// Simple sound emitter that emits sound on UseInHand /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent] public sealed partial class EmitSoundOnUseComponent : BaseEmitSoundComponent { /// @@ -17,6 +17,6 @@ public sealed partial class EmitSoundOnUseComponent : BaseEmitSoundComponent /// otherwise this might enable sound spamming, as use-delays are only initiated if the interaction was /// handled. /// - [DataField("handle")] + [DataField] public bool Handle = true; } diff --git a/Content.Shared/Sound/Components/SpamEmitSoundComponent.cs b/Content.Shared/Sound/Components/SpamEmitSoundComponent.cs index 149728a5ba..7c1428798c 100644 --- a/Content.Shared/Sound/Components/SpamEmitSoundComponent.cs +++ b/Content.Shared/Sound/Components/SpamEmitSoundComponent.cs @@ -1,4 +1,5 @@ using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Sound.Components; @@ -12,7 +13,7 @@ public sealed partial class SpamEmitSoundComponent : BaseEmitSoundComponent /// /// The time at which the next sound will play. /// - [DataField, AutoPausedField, AutoNetworkedField] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField, AutoNetworkedField] public TimeSpan NextSound; /// diff --git a/Content.Shared/Sound/Components/SpamEmitSoundRequirePowerComponent.cs b/Content.Shared/Sound/Components/SpamEmitSoundRequirePowerComponent.cs index b0547ea398..bf5e925e0d 100644 --- a/Content.Shared/Sound/Components/SpamEmitSoundRequirePowerComponent.cs +++ b/Content.Shared/Sound/Components/SpamEmitSoundRequirePowerComponent.cs @@ -5,6 +5,4 @@ namespace Content.Shared.Sound.Components; /// on the powered state of the entity. /// [RegisterComponent] -public sealed partial class SpamEmitSoundRequirePowerComponent : Component -{ -} +public sealed partial class SpamEmitSoundRequirePowerComponent : Component; diff --git a/Content.Shared/Sound/SharedEmitSoundSystem.cs b/Content.Shared/Sound/SharedEmitSoundSystem.cs index 67aabbb74d..58d541e363 100644 --- a/Content.Shared/Sound/SharedEmitSoundSystem.cs +++ b/Content.Shared/Sound/SharedEmitSoundSystem.cs @@ -12,6 +12,7 @@ using Content.Shared.Whitelist; using JetBrains.Annotations; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; +using Robust.Shared.GameStates; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Network; @@ -54,6 +55,47 @@ public abstract class SharedEmitSoundSystem : EntitySystem SubscribeLocalEvent(OnEmitSoundOnCollide); SubscribeLocalEvent(OnMobState); + + // We need to handle state manually here + // BaseEmitSoundComponent isn't registered so we have to subscribe to each one + // TODO: Make it use autonetworking instead of relying on inheritance + SubscribeEmitComponent(); + SubscribeEmitComponent(); + SubscribeEmitComponent(); + SubscribeEmitComponent(); + SubscribeEmitComponent(); + SubscribeEmitComponent(); + SubscribeEmitComponent(); + SubscribeEmitComponent(); + SubscribeEmitComponent(); + SubscribeEmitComponent(); + + // Helper method so it's a little less ugly + void SubscribeEmitComponent() where T : BaseEmitSoundComponent + { + SubscribeLocalEvent(GetBaseEmitState); + SubscribeLocalEvent(HandleBaseEmitState); + } + } + + private static void GetBaseEmitState(Entity ent, ref ComponentGetState args) where T : BaseEmitSoundComponent + { + args.State = new EmitSoundComponentState(ent.Comp.Sound); + } + + private static void HandleBaseEmitState(Entity ent, ref ComponentHandleState args) where T : BaseEmitSoundComponent + { + if (args.Current is not EmitSoundComponentState state) + return; + + ent.Comp.Sound = state.Sound switch + { + SoundPathSpecifier pathSpec => new SoundPathSpecifier(pathSpec.Path, pathSpec.Params), + SoundCollectionSpecifier collectionSpec => collectionSpec.Collection != null + ? new SoundCollectionSpecifier(collectionSpec.Collection, collectionSpec.Params) + : null, + _ => null, + }; } private void HandleEmitSoundOnUIOpen(EntityUid uid, EmitSoundOnUIOpenComponent component, AfterActivatableUIOpenEvent args) From 66e2b0ab64a33e3511ac415cadfcda43dd6ac6f0 Mon Sep 17 00:00:00 2001 From: J Date: Sun, 30 Mar 2025 16:29:32 +0000 Subject: [PATCH 21/45] Gameticking warnings cleanup (#36193) --- Content.Server/GameTicking/GameTicker.Player.cs | 2 +- Content.Server/GameTicking/GameTicker.RoundFlow.cs | 2 +- Content.Server/GameTicking/GameTicker.Spawning.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Server/GameTicking/GameTicker.Player.cs b/Content.Server/GameTicking/GameTicker.Player.cs index f376408130..2c3dcaba43 100644 --- a/Content.Server/GameTicking/GameTicker.Player.cs +++ b/Content.Server/GameTicking/GameTicker.Player.cs @@ -33,7 +33,7 @@ namespace Content.Server.GameTicking if (args.NewStatus != SessionStatus.Disconnected) { mind.Session = session; - _pvsOverride.AddSessionOverride(GetNetEntity(mindId.Value), session); + _pvsOverride.AddSessionOverride(mindId.Value, session); } DebugTools.Assert(mind.Session == session); diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index ce60aae3d9..0e8f8bda1e 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -557,7 +557,7 @@ namespace Content.Server.GameTicking if (TryGetEntity(mind.OriginalOwnedEntity, out var entity) && pvsOverride) { - _pvsOverride.AddGlobalOverride(GetNetEntity(entity.Value), recursive: true); + _pvsOverride.AddGlobalOverride(entity.Value); } var roles = _roles.MindGetAllRoleInfo(mindId); diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 561e1cb787..26242925aa 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -427,7 +427,7 @@ namespace Content.Server.GameTicking // Ideally engine would just spawn them on grid directly I guess? Right now grid traversal is handling it during // update which means we need to add a hack somewhere around it. var spawn = _robustRandom.Pick(_possiblePositions); - var toMap = spawn.ToMap(EntityManager, _transform); + var toMap = _transform.ToMapCoordinates(spawn); if (_mapManager.TryFindGridAt(toMap, out var gridUid, out _)) { From fc0a6dfdb3644584aa961023f60e55aa8402a697 Mon Sep 17 00:00:00 2001 From: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Date: Sun, 30 Mar 2025 19:27:08 -0700 Subject: [PATCH 22/45] Cleanup and small update to the stethoscope! (#36210) * First commit * Address most of the review! --- .../Components/StethoscopeComponent.cs | 22 --- .../Components/WearingStethoscopeComponent.cs | 18 --- .../Medical/Stethoscope/StethoscopeSystem.cs | 153 ------------------ .../Inventory/InventorySystem.Relay.cs | 13 ++ .../Components/StethoscopeComponent.cs | 31 ++++ .../Stethoscope/StethoscopeActionEvent.cs | 7 - .../Medical/Stethoscope/StethoscopeSystem.cs | 148 +++++++++++++++++ .../Medical/StethoscopeDoAfterEvent.cs | 4 +- Content.Shared/Verbs/Verb.cs | 5 +- .../en-US/health-examinable/stethoscope.ftl | 11 +- .../Entities/Clothing/Neck/misc.yml | 33 ++-- 11 files changed, 225 insertions(+), 220 deletions(-) delete mode 100644 Content.Server/Medical/Stethoscope/Components/StethoscopeComponent.cs delete mode 100644 Content.Server/Medical/Stethoscope/Components/WearingStethoscopeComponent.cs delete mode 100644 Content.Server/Medical/Stethoscope/StethoscopeSystem.cs create mode 100644 Content.Shared/Medical/Stethoscope/Components/StethoscopeComponent.cs delete mode 100644 Content.Shared/Medical/Stethoscope/StethoscopeActionEvent.cs create mode 100644 Content.Shared/Medical/Stethoscope/StethoscopeSystem.cs diff --git a/Content.Server/Medical/Stethoscope/Components/StethoscopeComponent.cs b/Content.Server/Medical/Stethoscope/Components/StethoscopeComponent.cs deleted file mode 100644 index d7e971e953..0000000000 --- a/Content.Server/Medical/Stethoscope/Components/StethoscopeComponent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Medical.Stethoscope.Components -{ - /// - /// Adds an innate verb when equipped to use a stethoscope. - /// - [RegisterComponent] - public sealed partial class StethoscopeComponent : Component - { - public bool IsActive = false; - - [DataField("delay")] - public float Delay = 2.5f; - - [DataField("action", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Action = "ActionStethoscope"; - - [DataField("actionEntity")] public EntityUid? ActionEntity; - } -} diff --git a/Content.Server/Medical/Stethoscope/Components/WearingStethoscopeComponent.cs b/Content.Server/Medical/Stethoscope/Components/WearingStethoscopeComponent.cs deleted file mode 100644 index dfce294a73..0000000000 --- a/Content.Server/Medical/Stethoscope/Components/WearingStethoscopeComponent.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Threading; - -namespace Content.Server.Medical.Components -{ - /// - /// Used to let doctors use the stethoscope on people. - /// - [RegisterComponent] - public sealed partial class WearingStethoscopeComponent : Component - { - public CancellationTokenSource? CancelToken; - - [DataField("delay")] - public float Delay = 2.5f; - - public EntityUid Stethoscope = default!; - } -} diff --git a/Content.Server/Medical/Stethoscope/StethoscopeSystem.cs b/Content.Server/Medical/Stethoscope/StethoscopeSystem.cs deleted file mode 100644 index b8304c562a..0000000000 --- a/Content.Server/Medical/Stethoscope/StethoscopeSystem.cs +++ /dev/null @@ -1,153 +0,0 @@ -using Content.Server.Body.Components; -using Content.Server.Medical.Components; -using Content.Server.Medical.Stethoscope.Components; -using Content.Server.Popups; -using Content.Shared.Actions; -using Content.Shared.Clothing; -using Content.Shared.Damage; -using Content.Shared.DoAfter; -using Content.Shared.FixedPoint; -using Content.Shared.Medical; -using Content.Shared.Medical.Stethoscope; -using Content.Shared.Mobs.Components; -using Content.Shared.Mobs.Systems; -using Content.Shared.Verbs; -using Robust.Shared.Utility; - -namespace Content.Server.Medical.Stethoscope -{ - public sealed class StethoscopeSystem : EntitySystem - { - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly MobStateSystem _mobStateSystem = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnEquipped); - SubscribeLocalEvent(OnUnequipped); - SubscribeLocalEvent>(AddStethoscopeVerb); - SubscribeLocalEvent(OnGetActions); - SubscribeLocalEvent(OnStethoscopeAction); - SubscribeLocalEvent(OnDoAfter); - } - - /// - /// Add the component the verb event subs to if the equippee is wearing the stethoscope. - /// - private void OnEquipped(EntityUid uid, StethoscopeComponent component, ref ClothingGotEquippedEvent args) - { - component.IsActive = true; - - var wearingComp = EnsureComp(args.Wearer); - wearingComp.Stethoscope = uid; - } - - private void OnUnequipped(EntityUid uid, StethoscopeComponent component, ref ClothingGotUnequippedEvent args) - { - if (!component.IsActive) - return; - - RemComp(args.Wearer); - component.IsActive = false; - } - - /// - /// This is raised when someone with WearingStethoscopeComponent requests verbs on an item. - /// It returns if the target is not a mob. - /// - private void AddStethoscopeVerb(EntityUid uid, WearingStethoscopeComponent component, GetVerbsEvent args) - { - if (!args.CanInteract || !args.CanAccess) - return; - - if (!HasComp(args.Target)) - return; - - if (component.CancelToken != null) - return; - - if (!TryComp(component.Stethoscope, out var stetho)) - return; - - InnateVerb verb = new() - { - Act = () => - { - StartListening(component.Stethoscope, uid, args.Target, stetho); // start doafter - }, - Text = Loc.GetString("stethoscope-verb"), - Icon = new SpriteSpecifier.Rsi(new ("Clothing/Neck/Misc/stethoscope.rsi"), "icon"), - Priority = 2 - }; - args.Verbs.Add(verb); - } - - - private void OnStethoscopeAction(EntityUid uid, StethoscopeComponent component, StethoscopeActionEvent args) - { - StartListening(uid, args.Performer, args.Target, component); - } - - private void OnGetActions(EntityUid uid, StethoscopeComponent component, GetItemActionsEvent args) - { - args.AddAction(ref component.ActionEntity, component.Action); - } - - // construct the doafter and start it - private void StartListening(EntityUid scope, EntityUid user, EntityUid target, StethoscopeComponent comp) - { - _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, comp.Delay, new StethoscopeDoAfterEvent(), scope, target: target, used: scope) - { - NeedHand = true, - BreakOnMove = true, - }); - } - - private void OnDoAfter(EntityUid uid, StethoscopeComponent component, DoAfterEvent args) - { - if (args.Handled || args.Cancelled || args.Args.Target == null) - return; - - ExamineWithStethoscope(args.Args.User, args.Args.Target.Value); - } - - /// - /// Return a value based on the total oxyloss of the target. - /// Could be expanded in the future with reagent effects etc. - /// The loc lines are taken from the goon wiki. - /// - public void ExamineWithStethoscope(EntityUid user, EntityUid target) - { - // The mob check seems a bit redundant but (1) they could conceivably have lost it since when the doafter started and (2) I need it for .IsDead() - if (!HasComp(target) || !TryComp(target, out var mobState) || _mobStateSystem.IsDead(target, mobState)) - { - _popupSystem.PopupEntity(Loc.GetString("stethoscope-dead"), target, user); - return; - } - - if (!TryComp(target, out var damage)) - return; - // these should probably get loc'd at some point before a non-english fork accidentally breaks a bunch of stuff that does this - if (!damage.Damage.DamageDict.TryGetValue("Asphyxiation", out var value)) - return; - - var message = GetDamageMessage(value); - - _popupSystem.PopupEntity(Loc.GetString(message), target, user); - } - - private string GetDamageMessage(FixedPoint2 totalOxyloss) - { - var msg = (int) totalOxyloss switch - { - < 20 => "stethoscope-normal", - < 60 => "stethoscope-hyper", - < 80 => "stethoscope-irregular", - _ => "stethoscope-fucked" - }; - return msg; - } - } -} diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index 94a32f5ef3..fada9822a3 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -67,6 +67,8 @@ public partial class InventorySystem SubscribeLocalEvent>(RefRelayInventoryEvent); SubscribeLocalEvent>(OnGetEquipmentVerbs); + SubscribeLocalEvent>(OnGetInnateVerbs); + } protected void RefRelayInventoryEvent(EntityUid uid, InventoryComponent component, ref T args) where T : IInventoryRelayEvent @@ -121,6 +123,17 @@ public partial class InventorySystem } } + private void OnGetInnateVerbs(EntityUid uid, InventoryComponent component, GetVerbsEvent args) + { + // Automatically relay stripping related verbs to all equipped clothing. + var ev = new InventoryRelayedEvent>(args); + var enumerator = new InventorySlotEnumerator(component, SlotFlags.WITHOUT_POCKET); + while (enumerator.NextItem(out var item)) + { + RaiseLocalEvent(item, ev); + } + } + } /// diff --git a/Content.Shared/Medical/Stethoscope/Components/StethoscopeComponent.cs b/Content.Shared/Medical/Stethoscope/Components/StethoscopeComponent.cs new file mode 100644 index 0000000000..7f740ef39c --- /dev/null +++ b/Content.Shared/Medical/Stethoscope/Components/StethoscopeComponent.cs @@ -0,0 +1,31 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Medical.Stethoscope.Components; + +/// +/// Adds a verb and action that allows the user to listen to the entity's breathing. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class StethoscopeComponent : Component +{ + /// + /// Time between each use of the stethoscope. + /// + [DataField] + public TimeSpan Delay = TimeSpan.FromSeconds(1.75); + + /// + /// Last damage that was measured. Used to indicate if breathing is improving or getting worse. + /// + [DataField] + public FixedPoint2? LastMeasuredDamage; + + [DataField] + public EntProtoId Action = "ActionStethoscope"; + + [DataField] + public EntityUid? ActionEntity; +} + diff --git a/Content.Shared/Medical/Stethoscope/StethoscopeActionEvent.cs b/Content.Shared/Medical/Stethoscope/StethoscopeActionEvent.cs deleted file mode 100644 index 11ac8a2684..0000000000 --- a/Content.Shared/Medical/Stethoscope/StethoscopeActionEvent.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Content.Shared.Actions; - -namespace Content.Shared.Medical.Stethoscope; - -public sealed partial class StethoscopeActionEvent : EntityTargetActionEvent -{ -} diff --git a/Content.Shared/Medical/Stethoscope/StethoscopeSystem.cs b/Content.Shared/Medical/Stethoscope/StethoscopeSystem.cs new file mode 100644 index 0000000000..01d61aa06e --- /dev/null +++ b/Content.Shared/Medical/Stethoscope/StethoscopeSystem.cs @@ -0,0 +1,148 @@ +using Content.Shared.Actions; +using Content.Shared.Damage; +using Content.Shared.DoAfter; +using Content.Shared.FixedPoint; +using Content.Shared.Inventory; +using Content.Shared.Medical.Stethoscope.Components; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.Popups; +using Content.Shared.Verbs; +using Robust.Shared.Containers; + +namespace Content.Shared.Medical.Stethoscope; + +public sealed class StethoscopeSystem : EntitySystem +{ + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + + // The damage type to "listen" for with the stethoscope. + private const string DamageToListenFor = "Asphyxiation"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>>(AddStethoscopeVerb); + SubscribeLocalEvent(OnGetActions); + SubscribeLocalEvent(OnStethoscopeAction); + SubscribeLocalEvent(OnDoAfter); + } + + private void OnGetActions(Entity ent, ref GetItemActionsEvent args) + { + args.AddAction(ref ent.Comp.ActionEntity, ent.Comp.Action); + } + + private void OnStethoscopeAction(Entity ent, ref StethoscopeActionEvent args) + { + StartListening(ent, args.Target); + } + + private void AddStethoscopeVerb(Entity ent, ref InventoryRelayedEvent> args) + { + if (!args.Args.CanInteract || !args.Args.CanAccess) + return; + + if (!HasComp(args.Args.Target)) + return; + + var target = args.Args.Target; + + InnateVerb verb = new() + { + Act = () => StartListening(ent, target), + Text = Loc.GetString("stethoscope-verb"), + IconEntity = GetNetEntity(ent), + Priority = 2, + }; + args.Args.Verbs.Add(verb); + } + + private void StartListening(Entity ent, EntityUid target) + { + if (!_container.TryGetContainingContainer((ent, null, null), out var container)) + return; + + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, container.Owner, ent.Comp.Delay, new StethoscopeDoAfterEvent(), ent, target: target, used: ent) + { + DuplicateCondition = DuplicateConditions.SameEvent, + BreakOnMove = true, + Hidden = true, + BreakOnHandChange = false, + }); + } + + private void OnDoAfter(Entity ent, ref StethoscopeDoAfterEvent args) + { + var target = args.Target; + + if (args.Handled || target == null || args.Cancelled) + { + ent.Comp.LastMeasuredDamage = null; + return; + } + + ExamineWithStethoscope(ent, args.Args.User, target.Value); + + args.Repeat = true; + } + + private void ExamineWithStethoscope(Entity stethoscope, EntityUid user, EntityUid target) + { + // TODO: Add check for respirator component when it gets moved to shared. + // If the mob is dead or cannot asphyxiation damage, the popup shows nothing. + if (!TryComp(target, out var mobState) || + !TryComp(target, out var damageComp) || + _mobState.IsDead(target, mobState) || + !damageComp.Damage.DamageDict.TryGetValue(DamageToListenFor, out var asphyxDmg)) + { + _popup.PopupPredicted(Loc.GetString("stethoscope-nothing"), target, user); + stethoscope.Comp.LastMeasuredDamage = null; + return; + } + + var absString = GetAbsoluteDamageString(asphyxDmg); + + // Don't show the change if this is the first time listening. + if (stethoscope.Comp.LastMeasuredDamage == null) + { + _popup.PopupPredicted(absString, target, user); + } + else + { + var deltaString = GetDeltaDamageString(stethoscope.Comp.LastMeasuredDamage.Value, asphyxDmg); + _popup.PopupPredicted(Loc.GetString("stethoscope-combined-status", ("absolute", absString), ("delta", deltaString)), target, user); + } + + stethoscope.Comp.LastMeasuredDamage = asphyxDmg; + } + + private string GetAbsoluteDamageString(FixedPoint2 asphyxDmg) + { + var msg = (int) asphyxDmg switch + { + < 10 => "stethoscope-normal", + < 30 => "stethoscope-raggedy", + < 60 => "stethoscope-hyper", + < 80 => "stethoscope-irregular", + _ => "stethoscope-fucked", + }; + return Loc.GetString(msg); + } + + private string GetDeltaDamageString(FixedPoint2 lastDamage, FixedPoint2 currentDamage) + { + if (lastDamage > currentDamage) + return Loc.GetString("stethoscope-delta-improving"); + if (lastDamage < currentDamage) + return Loc.GetString("stethoscope-delta-worsening"); + return Loc.GetString("stethoscope-delta-steady"); + } + +} + +public sealed partial class StethoscopeActionEvent : EntityTargetActionEvent; diff --git a/Content.Shared/Medical/StethoscopeDoAfterEvent.cs b/Content.Shared/Medical/StethoscopeDoAfterEvent.cs index aeb1c133cf..d3f3962958 100644 --- a/Content.Shared/Medical/StethoscopeDoAfterEvent.cs +++ b/Content.Shared/Medical/StethoscopeDoAfterEvent.cs @@ -4,6 +4,4 @@ using Robust.Shared.Serialization; namespace Content.Shared.Medical; [Serializable, NetSerializable] -public sealed partial class StethoscopeDoAfterEvent : SimpleDoAfterEvent -{ -} +public sealed partial class StethoscopeDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/Verbs/Verb.cs b/Content.Shared/Verbs/Verb.cs index 5faca9bb06..207c739466 100644 --- a/Content.Shared/Verbs/Verb.cs +++ b/Content.Shared/Verbs/Verb.cs @@ -281,12 +281,11 @@ namespace Content.Shared.Verbs } /// - /// This is for verbs facilitated by components on the user. + /// This is for verbs facilitated by components on the user or their clothing. /// Verbs from clothing, species, etc. rather than a held item. /// /// - /// Add a component to the user's entity and sub to the get verbs event - /// and it'll appear in the verbs menu on any target. + /// This will get relayed to all clothing (Not pockets) through an inventory relay event. /// [Serializable, NetSerializable] public sealed class InnateVerb : Verb diff --git a/Resources/Locale/en-US/health-examinable/stethoscope.ftl b/Resources/Locale/en-US/health-examinable/stethoscope.ftl index decfd7795b..d4baf4cc93 100644 --- a/Resources/Locale/en-US/health-examinable/stethoscope.ftl +++ b/Resources/Locale/en-US/health-examinable/stethoscope.ftl @@ -1,6 +1,15 @@ stethoscope-verb = Listen with stethoscope -stethoscope-dead = You hear nothing. + +stethoscope-nothing = You don't hear anything. + stethoscope-normal = You hear normal breathing. +stethoscope-raggedy = You hear raggedy breathing. stethoscope-hyper = You hear hyperventilation. stethoscope-irregular = You hear hyperventilation with an irregular pattern. stethoscope-fucked = You hear twitchy, labored breathing interspersed with short gasps. + +stethoscope-delta-steady = It's steady. +stethoscope-delta-improving = It's improving. +stethoscope-delta-worsening = It's getting worse. + +stethoscope-combined-status = {$absolute} {$delta} diff --git a/Resources/Prototypes/Entities/Clothing/Neck/misc.yml b/Resources/Prototypes/Entities/Clothing/Neck/misc.yml index f712ec1b1d..26071b5146 100644 --- a/Resources/Prototypes/Entities/Clothing/Neck/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Neck/misc.yml @@ -32,17 +32,36 @@ path: /Audio/Items/flashlight_off.ogg - type: entity - parent: ClothingNeckBase + parent: Clothing id: ClothingNeckStethoscope name: stethoscope description: An outdated medical apparatus for listening to the sounds of the human body. It also makes you look like you know what you're doing. components: + - type: Item + size: Small - type: Sprite sprite: Clothing/Neck/Misc/stethoscope.rsi + state: icon - type: Clothing sprite: Clothing/Neck/Misc/stethoscope.rsi + quickEquip: true + slots: + - neck - type: Stethoscope +- type: entity + id: ActionStethoscope + name: Listen with stethoscope + components: + - type: EntityTargetAction + icon: + sprite: Clothing/Neck/Misc/stethoscope.rsi + state: icon + event: !type:StethoscopeActionEvent + checkCanInteract: false + priority: -1 + itemIconStyle: BigAction + - type: entity parent: ClothingNeckBase id: ClothingNeckBling @@ -69,18 +88,6 @@ - type: TypingIndicatorClothing proto: lawyer -- type: entity - id: ActionStethoscope - name: Listen with stethoscope - components: - - type: EntityTargetAction - icon: - sprite: Clothing/Neck/Misc/stethoscope.rsi - state: icon - event: !type:StethoscopeActionEvent - checkCanInteract: false - priority: -1 - - type: entity parent: ClothingNeckBase id: Dinkystar From 86e4365438658585fc22ab8525c98eae0ad08088 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 31 Mar 2025 02:28:17 +0000 Subject: [PATCH 23/45] Automatic changelog update --- Resources/Changelog/Changelog.yml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index fd524d1c7c..6e2758ba37 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Aquif - changes: - - message: There is now a button to view your admin remarks in the character editor, - right next to the stats button. - type: Tweak - id: 7618 - time: '2024-11-16T05:09:29.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/31761 - author: SpaceRox1244 changes: - message: Closets and lockers now have visuals for being labeled with papers. @@ -3884,3 +3876,15 @@ id: 8117 time: '2025-03-30T04:06:01.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/36093 +- author: beck-thompson + changes: + - message: Stethoscopes now automatically start doafters and also can tell if a + patient is losing oxygen damage or gaining it. + type: Add + - message: Moths can no longer eat stethoscopes. + type: Fix + - message: Stethoscopes action button now works properly. + type: Fix + id: 8118 + time: '2025-03-31T02:27:08.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36210 From 19f3497b35538e792cf884a2a6b9a95e20520cbc Mon Sep 17 00:00:00 2001 From: Fildrance Date: Mon, 31 Mar 2025 12:57:47 +0300 Subject: [PATCH 24/45] refactor: simple radial menu for easier creation (#34639) * it works! kinda * so it works now * minor cleanup * central button now is useful too * more cleanup * minor cleanup * more cleanup * refactor: migrated code from toolbox (as it was rejected as too specific) * feat: moved border drawing for radial menu into RadialMenuTextureButton. Radial menu position setting into was moved to OverrideArrange to not being called on every frame * refactor: major reworks! * renamed DrawBagleSector to DrawAnnulusSector * Remove strange indexing * Regularize math * refactor: re-orienting segment elements to be Y-mirrored * refactor: extracted radial menu radius multiplier property, changed color pallet for radial menu button * refactor: removed icon backgrounds on textures used in current radial menu buttons with sectors, RadialContainer Radius renamed and now actually changed control radius. * refactor: in RadialMenuTextureButtonWithSector all sector colors are converted to and from sRGB in property getter-setters * refactor: renamed srgb to include Srgb suffix so devs gonna see that its srgb clearly * fix: enabled any functional keys pressed when pushing radial menu buttons * fix: radial menu sector now scales with UIScale * fix: accept only one event when clicking on radial menu ContextualButton * fix: now radial menu buttons accepts only click/alt-click, now clicks outside menu closes menu always * feat: simple radial menu prototype for easier creation * refactor: cleanup, restored emote filtering, button models now have class hierarchy * refactor: remove usage of closure from 'outside code' * refactor: remove non existing type from UiControlTest * refactor: remove unused using * refactor: revert ability to declare radial menu layers in xaml, scale 32px sprites using scale in radial menu * refactor: whitespaces * refactor: subscribe for dispose on existing radial menus * feat: now simple radial menu button models can have custom color for each sector background (and hover background color). Also added OpenOverMouseScreenPosition inside SimpleRadialMenu * fix: AI door menu now can be closed by verb if it gets unpowered * refactor: simplify hiding border, extended xml-doc for simple radial menu settings * refactor: remove linq * fix: fix AI radial action serialization using invalid type * refactor: fix duplicate ShowDeviceNotRespondingPopup for AI by properly checking if it can interact * refactor: whitespaces, changed list to array in simple radial button preparing methods --------- Co-authored-by: pa.pecherskij Co-authored-by: Eoin Mcloughlin --- Content.Client/Chat/UI/EmotesMenu.xaml | 31 -- Content.Client/Chat/UI/EmotesMenu.xaml.cs | 111 ------- Content.Client/RCD/RCDMenu.xaml | 47 --- Content.Client/RCD/RCDMenu.xaml.cs | 172 ----------- .../RCD/RCDMenuBoundUserInterface.cs | 126 +++++++- .../StationAi/StationAiBoundUserInterface.cs | 44 ++- .../Silicons/StationAi/StationAiMenu.xaml | 13 - .../Silicons/StationAi/StationAiMenu.xaml.cs | 126 -------- .../UserInterface/Controls/RadialMenu.cs | 58 ++-- .../Controls/SimpleRadialMenu.xaml | 8 + .../Controls/SimpleRadialMenu.xaml.cs | 279 ++++++++++++++++++ .../Systems/Emotes/EmotesUIController.cs | 96 +++++- .../Tests/UserInterface/UiControlTest.cs | 2 - .../StationAi/SharedStationAiSystem.Held.cs | 19 +- 14 files changed, 559 insertions(+), 573 deletions(-) delete mode 100644 Content.Client/Chat/UI/EmotesMenu.xaml delete mode 100644 Content.Client/Chat/UI/EmotesMenu.xaml.cs delete mode 100644 Content.Client/RCD/RCDMenu.xaml delete mode 100644 Content.Client/RCD/RCDMenu.xaml.cs delete mode 100644 Content.Client/Silicons/StationAi/StationAiMenu.xaml delete mode 100644 Content.Client/Silicons/StationAi/StationAiMenu.xaml.cs create mode 100644 Content.Client/UserInterface/Controls/SimpleRadialMenu.xaml create mode 100644 Content.Client/UserInterface/Controls/SimpleRadialMenu.xaml.cs diff --git a/Content.Client/Chat/UI/EmotesMenu.xaml b/Content.Client/Chat/UI/EmotesMenu.xaml deleted file mode 100644 index 845b631617..0000000000 --- a/Content.Client/Chat/UI/EmotesMenu.xaml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Content.Client/Chat/UI/EmotesMenu.xaml.cs b/Content.Client/Chat/UI/EmotesMenu.xaml.cs deleted file mode 100644 index 80daa405a6..0000000000 --- a/Content.Client/Chat/UI/EmotesMenu.xaml.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System.Numerics; -using Content.Client.UserInterface.Controls; -using Content.Shared.Chat.Prototypes; -using Content.Shared.Speech; -using Content.Shared.Whitelist; -using Robust.Client.AutoGenerated; -using Robust.Client.GameObjects; -using Robust.Client.UserInterface.Controls; -using Robust.Client.UserInterface.XAML; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; - -namespace Content.Client.Chat.UI; - -[GenerateTypedNameReferences] -public sealed partial class EmotesMenu : RadialMenu -{ - [Dependency] private readonly EntityManager _entManager = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly ISharedPlayerManager _playerManager = default!; - - public event Action>? OnPlayEmote; - - public EmotesMenu() - { - IoCManager.InjectDependencies(this); - RobustXamlLoader.Load(this); - - var spriteSystem = _entManager.System(); - var whitelistSystem = _entManager.System(); - - var main = FindControl("Main"); - - var emotes = _prototypeManager.EnumeratePrototypes(); - foreach (var emote in emotes) - { - var player = _playerManager.LocalSession?.AttachedEntity; - if (emote.Category == EmoteCategory.Invalid || - emote.ChatTriggers.Count == 0 || - !(player.HasValue && whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player.Value)) || - whitelistSystem.IsBlacklistPass(emote.Blacklist, player.Value)) - continue; - - if (!emote.Available && - _entManager.TryGetComponent(player.Value, out var speech) && - !speech.AllowedEmotes.Contains(emote.ID)) - continue; - - var parent = FindControl(emote.Category.ToString()); - - var button = new EmoteMenuButton - { - SetSize = new Vector2(64f, 64f), - ToolTip = Loc.GetString(emote.Name), - ProtoId = emote.ID, - }; - - var tex = new TextureRect - { - VerticalAlignment = VAlignment.Center, - HorizontalAlignment = HAlignment.Center, - Texture = spriteSystem.Frame0(emote.Icon), - TextureScale = new Vector2(2f, 2f), - }; - - button.AddChild(tex); - parent.AddChild(button); - foreach (var child in main.Children) - { - if (child is not RadialMenuTextureButton castChild) - continue; - - if (castChild.TargetLayer == emote.Category.ToString()) - { - castChild.Visible = true; - break; - } - } - } - - - // Set up menu actions - foreach (var child in Children) - { - if (child is not RadialContainer container) - continue; - AddEmoteClickAction(container); - } - } - - private void AddEmoteClickAction(RadialContainer container) - { - foreach (var child in container.Children) - { - if (child is not EmoteMenuButton castChild) - continue; - - castChild.OnButtonUp += _ => - { - OnPlayEmote?.Invoke(castChild.ProtoId); - Close(); - }; - } - } -} - - -public sealed class EmoteMenuButton : RadialMenuTextureButtonWithSector -{ - public ProtoId ProtoId { get; set; } -} diff --git a/Content.Client/RCD/RCDMenu.xaml b/Content.Client/RCD/RCDMenu.xaml deleted file mode 100644 index d8ab0ac8f4..0000000000 --- a/Content.Client/RCD/RCDMenu.xaml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Content.Client/RCD/RCDMenu.xaml.cs b/Content.Client/RCD/RCDMenu.xaml.cs deleted file mode 100644 index 7ea9894e41..0000000000 --- a/Content.Client/RCD/RCDMenu.xaml.cs +++ /dev/null @@ -1,172 +0,0 @@ -using Content.Client.UserInterface.Controls; -using Content.Shared.Popups; -using Content.Shared.RCD; -using Content.Shared.RCD.Components; -using Robust.Client.AutoGenerated; -using Robust.Client.GameObjects; -using Robust.Client.Player; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controls; -using Robust.Client.UserInterface.XAML; -using Robust.Shared.Prototypes; -using System.Numerics; - -namespace Content.Client.RCD; - -[GenerateTypedNameReferences] -public sealed partial class RCDMenu : RadialMenu -{ - [Dependency] private readonly EntityManager _entManager = default!; - [Dependency] private readonly IPrototypeManager _protoManager = default!; - [Dependency] private readonly IPlayerManager _playerManager = default!; - - private SharedPopupSystem _popup; - private SpriteSystem _sprites; - - public event Action>? SendRCDSystemMessageAction; - - private EntityUid _owner; - - public RCDMenu() - { - IoCManager.InjectDependencies(this); - RobustXamlLoader.Load(this); - - _popup = _entManager.System(); - _sprites = _entManager.System(); - - OnChildAdded += AddRCDMenuButtonOnClickActions; - } - - public void SetEntity(EntityUid uid) - { - _owner = uid; - Refresh(); - } - - public void Refresh() - { - // Find the main radial container - var main = FindControl("Main"); - - // Populate secondary radial containers - if (!_entManager.TryGetComponent(_owner, out var rcd)) - return; - - foreach (var protoId in rcd.AvailablePrototypes) - { - if (!_protoManager.TryIndex(protoId, out var proto)) - continue; - - if (proto.Mode == RcdMode.Invalid) - continue; - - var parent = FindControl(proto.Category); - var tooltip = Loc.GetString(proto.SetName); - - if ((proto.Mode == RcdMode.ConstructTile || proto.Mode == RcdMode.ConstructObject) && - proto.Prototype != null && _protoManager.TryIndex(proto.Prototype, out var entProto, logError: false)) - { - tooltip = Loc.GetString(entProto.Name); - } - - tooltip = OopsConcat(char.ToUpper(tooltip[0]).ToString(), tooltip.Remove(0, 1)); - - var button = new RCDMenuButton() - { - SetSize = new Vector2(64f, 64f), - ToolTip = tooltip, - ProtoId = protoId, - }; - - if (proto.Sprite != null) - { - var tex = new TextureRect() - { - VerticalAlignment = VAlignment.Center, - HorizontalAlignment = HAlignment.Center, - Texture = _sprites.Frame0(proto.Sprite), - TextureScale = new Vector2(2f, 2f), - }; - - button.AddChild(tex); - } - - parent.AddChild(button); - - // Ensure that the button that transitions the menu to the associated category layer - // is visible in the main radial container (as these all start with Visible = false) - foreach (var child in main.Children) - { - if (child is not RadialMenuTextureButton castChild) - continue; - - if (castChild.TargetLayer == proto.Category) - { - castChild.Visible = true; - break; - } - } - } - - // Set up menu actions - foreach (var child in Children) - { - AddRCDMenuButtonOnClickActions(child); - } - } - - private static string OopsConcat(string a, string b) - { - // This exists to prevent Roslyn being clever and compiling something that fails sandbox checks. - return a + b; - } - - private void AddRCDMenuButtonOnClickActions(Control control) - { - var radialContainer = control as RadialContainer; - - if (radialContainer == null) - return; - - foreach (var child in radialContainer.Children) - { - var castChild = child as RCDMenuButton; - - if (castChild == null) - continue; - - castChild.OnButtonUp += _ => - { - SendRCDSystemMessageAction?.Invoke(castChild.ProtoId); - - if (_playerManager.LocalSession?.AttachedEntity != null && - _protoManager.TryIndex(castChild.ProtoId, out var proto)) - { - var msg = Loc.GetString("rcd-component-change-mode", ("mode", Loc.GetString(proto.SetName))); - - if (proto.Mode == RcdMode.ConstructTile || proto.Mode == RcdMode.ConstructObject) - { - var name = Loc.GetString(proto.SetName); - - if (proto.Prototype != null && - _protoManager.TryIndex(proto.Prototype, out var entProto, logError: false)) - name = entProto.Name; - - msg = Loc.GetString("rcd-component-change-build-mode", ("name", name)); - } - - // Popup message - _popup.PopupClient(msg, _owner, _playerManager.LocalSession.AttachedEntity); - } - - Close(); - }; - } - } -} - -public sealed class RCDMenuButton : RadialMenuTextureButtonWithSector -{ - public ProtoId ProtoId { get; set; } -} diff --git a/Content.Client/RCD/RCDMenuBoundUserInterface.cs b/Content.Client/RCD/RCDMenuBoundUserInterface.cs index 1dd03626ae..d599c324e1 100644 --- a/Content.Client/RCD/RCDMenuBoundUserInterface.cs +++ b/Content.Client/RCD/RCDMenuBoundUserInterface.cs @@ -1,20 +1,32 @@ +using Content.Client.Popups; +using Content.Client.UserInterface.Controls; using Content.Shared.RCD; using Content.Shared.RCD.Components; using JetBrains.Annotations; -using Robust.Client.Graphics; -using Robust.Client.Input; using Robust.Client.UserInterface; +using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Robust.Shared.Utility; namespace Content.Client.RCD; [UsedImplicitly] public sealed class RCDMenuBoundUserInterface : BoundUserInterface { - [Dependency] private readonly IClyde _displayManager = default!; - [Dependency] private readonly IInputManager _inputManager = default!; + private static readonly Dictionary PrototypesGroupingInfo + = new Dictionary + { + ["WallsAndFlooring"] = ("rcd-component-walls-and-flooring", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Radial/RCD/walls_and_flooring.png"))), + ["WindowsAndGrilles"] = ("rcd-component-windows-and-grilles", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Radial/RCD/windows_and_grilles.png"))), + ["Airlocks"] = ("rcd-component-airlocks", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Radial/RCD/airlocks.png"))), + ["Electrical"] = ("rcd-component-electrical", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Radial/RCD/multicoil.png"))), + ["Lighting"] = ("rcd-component-lighting", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Radial/RCD/lighting.png"))), + }; - private RCDMenu? _menu; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly ISharedPlayerManager _playerManager = default!; + + private SimpleRadialMenu? _menu; public RCDMenuBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { @@ -25,19 +37,107 @@ public sealed class RCDMenuBoundUserInterface : BoundUserInterface { base.Open(); - _menu = this.CreateWindow(); - _menu.SetEntity(Owner); - _menu.SendRCDSystemMessageAction += SendRCDSystemMessage; + if (!EntMan.TryGetComponent(Owner, out var rcd)) + return; - // Open the menu, centered on the mouse - var vpSize = _displayManager.ScreenSize; - _menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / vpSize); + _menu = this.CreateWindow(); + _menu.Track(Owner); + var models = ConvertToButtons(rcd.AvailablePrototypes); + _menu.SetButtons(models); + + _menu.OpenOverMouseScreenPosition(); } - public void SendRCDSystemMessage(ProtoId protoId) + private IEnumerable ConvertToButtons(HashSet> prototypes) + { + Dictionary> buttonsByCategory = new(); + foreach (var protoId in prototypes) + { + var prototype = _prototypeManager.Index(protoId); + if (!PrototypesGroupingInfo.TryGetValue(prototype.Category, out var groupInfo)) + continue; + + if (!buttonsByCategory.TryGetValue(prototype.Category, out var list)) + { + list = new List(); + buttonsByCategory.Add(prototype.Category, list); + } + + var actionOption = new RadialMenuActionOption(HandleMenuOptionClick, prototype) + { + Sprite = prototype.Sprite, + ToolTip = GetTooltip(prototype) + }; + list.Add(actionOption); + } + + var models = new RadialMenuNestedLayerOption[buttonsByCategory.Count]; + var i = 0; + foreach (var (key, list) in buttonsByCategory) + { + var groupInfo = PrototypesGroupingInfo[key]; + models[i] = new RadialMenuNestedLayerOption(list) + { + Sprite = groupInfo.Sprite, + ToolTip = Loc.GetString(groupInfo.Tooltip) + }; + i++; + } + + return models; + } + + private void HandleMenuOptionClick(RCDPrototype proto) { // A predicted message cannot be used here as the RCD UI is closed immediately // after this message is sent, which will stop the server from receiving it - SendMessage(new RCDSystemMessage(protoId)); + SendMessage(new RCDSystemMessage(proto.ID)); + + + if (_playerManager.LocalSession?.AttachedEntity == null) + return; + + var msg = Loc.GetString("rcd-component-change-mode", ("mode", Loc.GetString(proto.SetName))); + + if (proto.Mode is RcdMode.ConstructTile or RcdMode.ConstructObject) + { + var name = Loc.GetString(proto.SetName); + + if (proto.Prototype != null && + _prototypeManager.TryIndex(proto.Prototype, out var entProto, logError: false)) + name = entProto.Name; + + msg = Loc.GetString("rcd-component-change-build-mode", ("name", name)); + } + + // Popup message + var popup = EntMan.System(); + popup.PopupClient(msg, Owner, _playerManager.LocalSession.AttachedEntity); + } + + private string GetTooltip(RCDPrototype proto) + { + string tooltip; + + if (proto.Mode is RcdMode.ConstructTile or RcdMode.ConstructObject + && proto.Prototype != null + && _prototypeManager.TryIndex(proto.Prototype, out var entProto, logError: false)) + { + tooltip = Loc.GetString(entProto.Name); + } + else + { + tooltip = Loc.GetString(proto.SetName); + } + + tooltip = OopsConcat(char.ToUpper(tooltip[0]).ToString(), tooltip.Remove(0, 1)); + + return tooltip; + } + + private static string OopsConcat(string a, string b) + { + // This exists to prevent Roslyn being clever and compiling something that fails sandbox checks. + return a + b; } } diff --git a/Content.Client/Silicons/StationAi/StationAiBoundUserInterface.cs b/Content.Client/Silicons/StationAi/StationAiBoundUserInterface.cs index 68318305a0..77ac13c972 100644 --- a/Content.Client/Silicons/StationAi/StationAiBoundUserInterface.cs +++ b/Content.Client/Silicons/StationAi/StationAiBoundUserInterface.cs @@ -1,28 +1,46 @@ +using Content.Client.UserInterface.Controls; using Content.Shared.Silicons.StationAi; using Robust.Client.UserInterface; namespace Content.Client.Silicons.StationAi; -public sealed class StationAiBoundUserInterface : BoundUserInterface +public sealed class StationAiBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey) { - private StationAiMenu? _menu; - - public StationAiBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) - { - } + private SimpleRadialMenu? _menu; protected override void Open() { base.Open(); - _menu = this.CreateWindow(); - _menu.Track(Owner); - _menu.OnAiRadial += args => + var ev = new GetStationAiRadialEvent(); + EntMan.EventBus.RaiseLocalEvent(Owner, ref ev); + + _menu = this.CreateWindow(); + _menu.Track(Owner); + var buttonModels = ConvertToButtons(ev.Actions); + _menu.SetButtons(buttonModels); + + _menu.Open(); + } + + private IEnumerable ConvertToButtons(IReadOnlyList actions) + { + var models = new RadialMenuActionOption[actions.Count]; + for (int i = 0; i < actions.Count; i++) { - SendPredictedMessage(new StationAiRadialMessage() + var action = actions[i]; + models[i] = new RadialMenuActionOption(HandleRadialMenuClick, action.Event) { - Event = args, - }); - }; + Sprite = action.Sprite, + ToolTip = action.Tooltip + }; + } + + return models; + } + + private void HandleRadialMenuClick(BaseStationAiAction p) + { + SendPredictedMessage(new StationAiRadialMessage { Event = p }); } } diff --git a/Content.Client/Silicons/StationAi/StationAiMenu.xaml b/Content.Client/Silicons/StationAi/StationAiMenu.xaml deleted file mode 100644 index cfa0b93234..0000000000 --- a/Content.Client/Silicons/StationAi/StationAiMenu.xaml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - diff --git a/Content.Client/Silicons/StationAi/StationAiMenu.xaml.cs b/Content.Client/Silicons/StationAi/StationAiMenu.xaml.cs deleted file mode 100644 index a536d911f3..0000000000 --- a/Content.Client/Silicons/StationAi/StationAiMenu.xaml.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System.Numerics; -using Content.Client.UserInterface.Controls; -using Content.Shared.Silicons.StationAi; -using Robust.Client.AutoGenerated; -using Robust.Client.GameObjects; -using Robust.Client.Graphics; -using Robust.Client.UserInterface.Controls; -using Robust.Client.UserInterface.XAML; -using Robust.Shared.Timing; - -namespace Content.Client.Silicons.StationAi; - -[GenerateTypedNameReferences] -public sealed partial class StationAiMenu : RadialMenu -{ - [Dependency] private readonly IClyde _clyde = default!; - [Dependency] private readonly IEntityManager _entManager = default!; - - public event Action? OnAiRadial; - - private EntityUid _tracked; - - public StationAiMenu() - { - IoCManager.InjectDependencies(this); - RobustXamlLoader.Load(this); - } - - public void Track(EntityUid owner) - { - _tracked = owner; - - if (!_entManager.EntityExists(_tracked)) - { - Close(); - return; - } - - BuildButtons(); - UpdatePosition(); - } - - private void BuildButtons() - { - var ev = new GetStationAiRadialEvent(); - _entManager.EventBus.RaiseLocalEvent(_tracked, ref ev); - - var main = FindControl("Main"); - main.DisposeAllChildren(); - var sprites = _entManager.System(); - - foreach (var action in ev.Actions) - { - // TODO: This radial boilerplate is quite annoying - var button = new StationAiMenuButton(action.Event) - { - SetSize = new Vector2(64f, 64f), - ToolTip = action.Tooltip != null ? Loc.GetString(action.Tooltip) : null, - }; - - if (action.Sprite != null) - { - var texture = sprites.Frame0(action.Sprite); - var scale = Vector2.One; - - if (texture.Width <= 32) - { - scale *= 2; - } - - var tex = new TextureRect - { - VerticalAlignment = VAlignment.Center, - HorizontalAlignment = HAlignment.Center, - Texture = texture, - TextureScale = scale, - }; - - button.AddChild(tex); - } - - button.OnPressed += args => - { - OnAiRadial?.Invoke(action.Event); - Close(); - }; - main.AddChild(button); - } - } - - protected override void FrameUpdate(FrameEventArgs args) - { - base.FrameUpdate(args); - UpdatePosition(); - } - - private void UpdatePosition() - { - if (!_entManager.TryGetComponent(_tracked, out TransformComponent? xform)) - { - Close(); - return; - } - - if (!xform.Coordinates.IsValid(_entManager)) - { - Close(); - return; - } - - var coords = _entManager.System().GetSpriteScreenCoordinates((_tracked, null, xform)); - - if (!coords.IsValid) - { - Close(); - return; - } - - OpenScreenAt(coords.Position, _clyde); - } -} - -public sealed class StationAiMenuButton(BaseStationAiAction action) : RadialMenuTextureButtonWithSector -{ - public BaseStationAiAction Action = action; -} diff --git a/Content.Client/UserInterface/Controls/RadialMenu.cs b/Content.Client/UserInterface/Controls/RadialMenu.cs index 1b7f07aa2c..9734cf2960 100644 --- a/Content.Client/UserInterface/Controls/RadialMenu.cs +++ b/Content.Client/UserInterface/Controls/RadialMenu.cs @@ -1,10 +1,10 @@ -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controls; -using Robust.Client.UserInterface.CustomControls; using System.Linq; using System.Numerics; using Content.Shared.Input; using Robust.Client.Graphics; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; using Robust.Shared.Input; namespace Content.Client.UserInterface.Controls; @@ -143,11 +143,8 @@ public class RadialMenu : BaseWindow return children.First(x => x.Visible); } - public bool TryToMoveToNewLayer(string newLayer) + public bool TryToMoveToNewLayer(Control newLayer) { - if (newLayer == string.Empty) - return false; - var currentLayer = GetCurrentActiveLayer(); if (currentLayer == null) @@ -161,7 +158,7 @@ public class RadialMenu : BaseWindow continue; // Hide layers which are not of interest - if (result == true || child.Name != newLayer) + if (result == true || child != newLayer) { child.Visible = false; } @@ -186,6 +183,19 @@ public class RadialMenu : BaseWindow return result; } + public bool TryToMoveToNewLayer(string targetLayerControlName) + { + foreach (var child in Children) + { + if (child.Name == targetLayerControlName && child is RadialContainer) + { + return TryToMoveToNewLayer(child); + } + } + + return false; + } + public void ReturnToPreviousLayer() { // Close the menu if the traversal path is empty @@ -296,9 +306,15 @@ public sealed class RadialMenuOuterAreaButton : RadialMenuTextureButtonBase public class RadialMenuTextureButton : RadialMenuTextureButtonBase { /// - /// Upon clicking this button the radial menu will be moved to the named layer + /// Upon clicking this button the radial menu will be moved to the layer of this control. /// - public string TargetLayer { get; set; } = string.Empty; + public Control? TargetLayer { get; set; } + + /// + /// Other way to set navigation to other container, as , + /// but using property of target . + /// + public string? TargetLayerControlName { get; set; } /// /// A simple texture button that can move the user to a different layer within a radial menu @@ -311,7 +327,7 @@ public class RadialMenuTextureButton : RadialMenuTextureButtonBase private void OnClicked(ButtonEventArgs args) { - if (TargetLayer == string.Empty) + if (TargetLayer == null && TargetLayerControlName == null) return; var parent = FindParentMultiLayerContainer(this); @@ -319,7 +335,14 @@ public class RadialMenuTextureButton : RadialMenuTextureButtonBase if (parent == null) return; - parent.TryToMoveToNewLayer(TargetLayer); + if (TargetLayer != null) + { + parent.TryToMoveToNewLayer(TargetLayer); + } + else + { + parent.TryToMoveToNewLayer(TargetLayerControlName!); + } } private RadialMenu? FindParentMultiLayerContainer(Control control) @@ -387,7 +410,7 @@ public class RadialMenuTextureButtonWithSector : RadialMenuTextureButton, IRadia private Color _hoverBorderColorSrgb = Color.ToSrgb(new Color(87, 91, 127, 128)); /// - /// Marker, that control should render border of segment. Is false by default. + /// Marker, that controls if border of segment should be rendered. Is false by default. /// /// /// By default color of border is same as color of background. Use @@ -400,13 +423,6 @@ public class RadialMenuTextureButtonWithSector : RadialMenuTextureButton, IRadia /// public bool DrawBackground { get; set; } = true; - /// - /// Marker, that control should render separator lines. - /// Separator lines are used to visually separate sector of radial menu items. - /// Is true by default - /// - public bool DrawSeparators { get; set; } = true; - /// /// Color of background in non-hovered state. Accepts RGB color, works with sRGB for DrawPrimitive internally. /// @@ -520,7 +536,7 @@ public class RadialMenuTextureButtonWithSector : RadialMenuTextureButton, IRadia DrawAnnulusSector(handle, containerCenter, _innerRadius * UIScale, _outerRadius * UIScale, angleFrom, angleTo, borderColor, false); } - if (!_isWholeCircle && DrawSeparators) + if (!_isWholeCircle && DrawBorder) { DrawSeparatorLines(handle, containerCenter, _innerRadius * UIScale, _outerRadius * UIScale, angleFrom, angleTo, SeparatorColor); } diff --git a/Content.Client/UserInterface/Controls/SimpleRadialMenu.xaml b/Content.Client/UserInterface/Controls/SimpleRadialMenu.xaml new file mode 100644 index 0000000000..307064334d --- /dev/null +++ b/Content.Client/UserInterface/Controls/SimpleRadialMenu.xaml @@ -0,0 +1,8 @@ + + diff --git a/Content.Client/UserInterface/Controls/SimpleRadialMenu.xaml.cs b/Content.Client/UserInterface/Controls/SimpleRadialMenu.xaml.cs new file mode 100644 index 0000000000..15c8065a44 --- /dev/null +++ b/Content.Client/UserInterface/Controls/SimpleRadialMenu.xaml.cs @@ -0,0 +1,279 @@ +using Robust.Client.UserInterface; +using System.Numerics; +using Robust.Client.AutoGenerated; +using Robust.Client.Graphics; +using Robust.Shared.Utility; +using Robust.Client.GameObjects; +using Robust.Shared.Timing; +using Robust.Client.UserInterface.XAML; +using Robust.Client.Input; + +namespace Content.Client.UserInterface.Controls; + +[GenerateTypedNameReferences] +public partial class SimpleRadialMenu : RadialMenu +{ + private EntityUid? _attachMenuToEntity; + + [Dependency] private readonly IClyde _clyde = default!; + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IInputManager _inputManager = default!; + + public SimpleRadialMenu() + { + IoCManager.InjectDependencies(this); + RobustXamlLoader.Load(this); + } + + public void Track(EntityUid owner) + { + _attachMenuToEntity = owner; + } + + public void SetButtons(IEnumerable models, SimpleRadialMenuSettings? settings = null) + { + ClearExistingChildrenRadialButtons(); + + var sprites = _entManager.System(); + Fill(models, sprites, Children, settings ?? new SimpleRadialMenuSettings()); + } + + public void OpenOverMouseScreenPosition() + { + var vpSize = _clyde.ScreenSize; + OpenCenteredAt(_inputManager.MouseScreenPosition.Position / vpSize); + } + + private void Fill( + IEnumerable models, + SpriteSystem sprites, + ICollection rootControlChildren, + SimpleRadialMenuSettings settings + ) + { + var rootContainer = new RadialContainer + { + HorizontalExpand = true, + VerticalExpand = true, + InitialRadius = settings.DefaultContainerRadius, + ReserveSpaceForHiddenChildren = false, + Visible = true + }; + rootControlChildren.Add(rootContainer); + + foreach (var model in models) + { + if (model is RadialMenuNestedLayerOption nestedMenuModel) + { + var linkButton = RecursiveContainerExtraction(sprites, rootControlChildren, nestedMenuModel, settings); + linkButton.Visible = true; + rootContainer.AddChild(linkButton); + } + else + { + var rootButtons = ConvertToButton(model, sprites, settings, false); + rootContainer.AddChild(rootButtons); + } + } + } + + private RadialMenuTextureButton RecursiveContainerExtraction( + SpriteSystem sprites, + ICollection rootControlChildren, + RadialMenuNestedLayerOption model, + SimpleRadialMenuSettings settings + ) + { + var container = new RadialContainer + { + HorizontalExpand = true, + VerticalExpand = true, + InitialRadius = model.ContainerRadius!.Value, + ReserveSpaceForHiddenChildren = false, + Visible = false + }; + foreach (var nested in model.Nested) + { + if (nested is RadialMenuNestedLayerOption nestedMenuModel) + { + var linkButton = RecursiveContainerExtraction(sprites, rootControlChildren, nestedMenuModel, settings); + container.AddChild(linkButton); + } + else + { + var button = ConvertToButton(nested, sprites, settings, false); + container.AddChild(button); + } + } + rootControlChildren.Add(container); + + var thisLayerLinkButton = ConvertToButton(model, sprites, settings, true); + thisLayerLinkButton.TargetLayer = container; + return thisLayerLinkButton; + } + + private RadialMenuTextureButton ConvertToButton( + RadialMenuOption model, + SpriteSystem sprites, + SimpleRadialMenuSettings settings, + bool haveNested + ) + { + var button = settings.UseSectors + ? ConvertToButtonWithSector(model, settings) + : new RadialMenuTextureButton(); + button.SetSize = new Vector2(64f, 64f); + button.ToolTip = model.ToolTip; + if (model.Sprite != null) + { + var scale = Vector2.One; + + var texture = sprites.Frame0(model.Sprite); + if (texture.Width <= 32) + { + scale *= 2; + } + + button.TextureNormal = texture; + button.Scale = scale; + } + + if (model is RadialMenuActionOption actionOption) + { + button.OnPressed += _ => + { + actionOption.OnPressed?.Invoke(); + if(!haveNested) + Close(); + }; + } + + return button; + } + + private static RadialMenuTextureButtonWithSector ConvertToButtonWithSector(RadialMenuOption model, SimpleRadialMenuSettings settings) + { + var button = new RadialMenuTextureButtonWithSector + { + DrawBorder = settings.DisplayBorders, + DrawBackground = !settings.NoBackground + }; + if (model.BackgroundColor.HasValue) + { + button.BackgroundColor = model.BackgroundColor.Value; + } + + if (model.HoverBackgroundColor.HasValue) + { + button.HoverBackgroundColor = model.HoverBackgroundColor.Value; + } + + return button; + } + + private void ClearExistingChildrenRadialButtons() + { + var toRemove = new List(ChildCount); + foreach (var child in Children) + { + if (child != ContextualButton && child != MenuOuterAreaButton) + { + toRemove.Add(child); + } + } + + foreach (var control in toRemove) + { + Children.Remove(control); + } + } + + #region target entity tracking + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + if (_attachMenuToEntity != null) + { + UpdatePosition(); + } + } + + private void UpdatePosition() + { + if (!_entManager.TryGetComponent(_attachMenuToEntity, out TransformComponent? xform)) + { + Close(); + return; + } + + if (!xform.Coordinates.IsValid(_entManager)) + { + Close(); + return; + } + + var coords = _entManager.System().GetSpriteScreenCoordinates((_attachMenuToEntity.Value, null, xform)); + + if (!coords.IsValid) + { + Close(); + return; + } + + OpenScreenAt(coords.Position, _clyde); + } + + #endregion + +} + + +public abstract class RadialMenuOption +{ + public string? ToolTip { get; init; } + + public SpriteSpecifier? Sprite { get; init; } + public Color? BackgroundColor { get; set; } + public Color? HoverBackgroundColor { get; set; } +} + +public class RadialMenuActionOption(Action onPressed) : RadialMenuOption +{ + public Action OnPressed { get; } = onPressed; +} + +public class RadialMenuActionOption(Action onPressed, T data) + : RadialMenuActionOption(onPressed: () => onPressed(data)); + +public class RadialMenuNestedLayerOption(IReadOnlyCollection nested, float containerRadius = 100) + : RadialMenuOption +{ + public float? ContainerRadius { get; } = containerRadius; + + public IReadOnlyCollection Nested { get; } = nested; +} + +public class SimpleRadialMenuSettings +{ + /// + /// Default container draw radius. Is going to be further affected by per sector increment. + /// + public int DefaultContainerRadius = 100; + + /// + /// Marker, if sector-buttons should be used. + /// + public bool UseSectors = true; + + /// + /// Marker, if border of buttons should be rendered. Can only be used when = true. + /// + public bool DisplayBorders = true; + + /// + /// Marker, if sector background should not be rendered. Can only be used when = true. + /// + public bool NoBackground = false; +} + diff --git a/Content.Client/UserInterface/Systems/Emotes/EmotesUIController.cs b/Content.Client/UserInterface/Systems/Emotes/EmotesUIController.cs index 7b86859a1a..7652e39bfd 100644 --- a/Content.Client/UserInterface/Systems/Emotes/EmotesUIController.cs +++ b/Content.Client/UserInterface/Systems/Emotes/EmotesUIController.cs @@ -1,16 +1,17 @@ -using Content.Client.Chat.UI; using Content.Client.Gameplay; using Content.Client.UserInterface.Controls; using Content.Shared.Chat; using Content.Shared.Chat.Prototypes; using Content.Shared.Input; +using Content.Shared.Speech; +using Content.Shared.Whitelist; using JetBrains.Annotations; -using Robust.Client.Graphics; -using Robust.Client.Input; +using Robust.Client.Player; using Robust.Client.UserInterface.Controllers; using Robust.Client.UserInterface.Controls; using Robust.Shared.Input.Binding; using Robust.Shared.Prototypes; +using Robust.Shared.Utility; namespace Content.Client.UserInterface.Systems.Emotes; @@ -18,11 +19,19 @@ namespace Content.Client.UserInterface.Systems.Emotes; public sealed class EmotesUIController : UIController, IOnStateChanged { [Dependency] private readonly IEntityManager _entityManager = default!; - [Dependency] private readonly IClyde _displayManager = default!; - [Dependency] private readonly IInputManager _inputManager = default!; - + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + private MenuButton? EmotesButton => UIManager.GetActiveUIWidgetOrNull()?.EmotesButton; - private EmotesMenu? _menu; + private SimpleRadialMenu? _menu; + + private static readonly Dictionary EmoteGroupingInfo + = new Dictionary + { + [EmoteCategory.General] = ("emote-menu-category-general", new SpriteSpecifier.Texture(new ResPath("/Textures/Clothing/Head/Soft/mimesoft.rsi/icon.png"))), + [EmoteCategory.Hands] = ("emote-menu-category-hands", new SpriteSpecifier.Texture(new ResPath("/Textures/Clothing/Hands/Gloves/latex.rsi/icon.png"))), + [EmoteCategory.Vocal] = ("emote-menu-category-vocal", new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/Emotes/vocal.png"))), + }; public void OnStateEntered(GameplayState state) { @@ -42,10 +51,16 @@ public sealed class EmotesUIController : UIController, IOnStateChanged(); + var prototypes = _prototypeManager.EnumeratePrototypes(); + var models = ConvertToButtons(prototypes); + + _menu = new SimpleRadialMenu(); + _menu.SetButtons(models); + + _menu.Open(); + _menu.OnClose += OnWindowClosed; _menu.OnOpen += OnWindowOpen; - _menu.OnPlayEmote += OnPlayEmote; if (EmotesButton != null) EmotesButton.SetClickPressed(true); @@ -56,16 +71,13 @@ public sealed class EmotesUIController : UIController, IOnStateChanged protoId) + private IEnumerable ConvertToButtons(IEnumerable emotePrototypes) { - _entityManager.RaisePredictiveEvent(new PlayEmoteMessage(protoId)); + var whitelistSystem = EntitySystemManager.GetEntitySystem(); + var player = _playerManager.LocalSession?.AttachedEntity; + + Dictionary> emotesByCategory = new(); + foreach (var emote in emotePrototypes) + { + if(emote.Category == EmoteCategory.Invalid) + continue; + + // only valid emotes that have ways to be triggered by chat and player have access / no restriction on + if (emote.Category == EmoteCategory.Invalid + || emote.ChatTriggers.Count == 0 + || !(player.HasValue && whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player.Value)) + || whitelistSystem.IsBlacklistPass(emote.Blacklist, player.Value)) + continue; + + if (!emote.Available + && EntityManager.TryGetComponent(player.Value, out var speech) + && !speech.AllowedEmotes.Contains(emote.ID)) + continue; + + if (!emotesByCategory.TryGetValue(emote.Category, out var list)) + { + list = new List(); + emotesByCategory.Add(emote.Category, list); + } + + var actionOption = new RadialMenuActionOption(HandleRadialButtonClick, emote) + { + Sprite = emote.Icon, + ToolTip = Loc.GetString(emote.Name) + }; + list.Add(actionOption); + } + + var models = new RadialMenuOption[emotesByCategory.Count]; + var i = 0; + foreach (var (key, list) in emotesByCategory) + { + var tuple = EmoteGroupingInfo[key]; + + models[i] = new RadialMenuNestedLayerOption(list) + { + Sprite = tuple.Sprite, + ToolTip = Loc.GetString(tuple.Tooltip) + }; + i++; + } + + return models; + } + + private void HandleRadialButtonClick(EmotePrototype prototype) + { + _entityManager.RaisePredictiveEvent(new PlayEmoteMessage(prototype.ID)); } } diff --git a/Content.IntegrationTests/Tests/UserInterface/UiControlTest.cs b/Content.IntegrationTests/Tests/UserInterface/UiControlTest.cs index c8378bb661..5efa009ca7 100644 --- a/Content.IntegrationTests/Tests/UserInterface/UiControlTest.cs +++ b/Content.IntegrationTests/Tests/UserInterface/UiControlTest.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Client.Chat.UI; using Content.Client.LateJoin; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.ContentPack; @@ -14,7 +13,6 @@ public sealed class UiControlTest // You should not be adding to this. private Type[] _ignored = new Type[] { - typeof(EmotesMenu), typeof(LateJoinGui), }; diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs index 8acfb56376..afdf9c2b6d 100644 --- a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using Content.Shared.Actions.Events; using Content.Shared.IdentityManagement; using Content.Shared.Interaction.Events; @@ -122,6 +121,14 @@ public abstract partial class SharedStationAiSystem if (ev.Actor == ev.Target) return; + // no need to show menu if device is not powered. + if (!PowerReceiver.IsPowered(ev.Target)) + { + ShowDeviceNotRespondingPopup(ev.Actor); + ev.Cancel(); + return; + } + if (TryComp(ev.Actor, out StationAiHeldComponent? aiComp) && (!TryComp(ev.Target, out StationAiWhitelistComponent? whitelistComponent) || !ValidateAi((ev.Actor, aiComp)))) @@ -150,7 +157,8 @@ public abstract partial class SharedStationAiSystem private void OnTargetVerbs(Entity ent, ref GetVerbsEvent args) { if (!args.CanComplexInteract - || !HasComp(args.User)) + || !HasComp(args.User) + || !args.CanInteract) { return; } @@ -166,13 +174,6 @@ public abstract partial class SharedStationAiSystem Text = isOpen ? Loc.GetString("ai-close") : Loc.GetString("ai-open"), Act = () => { - // no need to show menu if device is not powered. - if (!PowerReceiver.IsPowered(ent.Owner)) - { - ShowDeviceNotRespondingPopup(user); - return; - } - if (isOpen) { _uiSystem.CloseUi(ent.Owner, AiUi.Key, user); From b980c509f9c059bb942a132b9f677641decd245d Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Mon, 31 Mar 2025 17:45:18 +0200 Subject: [PATCH 25/45] delete PolymorphOnCollideComponent (#36227) delete component --- .../Components/PolymorphOnCollideComponent.cs | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 Content.Server/Polymorph/Components/PolymorphOnCollideComponent.cs diff --git a/Content.Server/Polymorph/Components/PolymorphOnCollideComponent.cs b/Content.Server/Polymorph/Components/PolymorphOnCollideComponent.cs deleted file mode 100644 index 577dadb5c8..0000000000 --- a/Content.Server/Polymorph/Components/PolymorphOnCollideComponent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Content.Server.Polymorph.Systems; -using Content.Shared.Polymorph; -using Content.Shared.Whitelist; -using Robust.Shared.Audio; -using Robust.Shared.Prototypes; - -namespace Content.Server.Polymorph.Components; - -[RegisterComponent] -[Access(typeof(PolymorphSystem))] -public sealed partial class PolymorphOnCollideComponent : Component -{ - [DataField(required: true)] - public ProtoId Polymorph; - - [DataField(required: true)] - public EntityWhitelist Whitelist = default!; - - [DataField] - public EntityWhitelist? Blacklist; - - [DataField] - public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Magic/forcewall.ogg"); -} From 3c3cf1d86759362148e698bdad22c92541b9577e Mon Sep 17 00:00:00 2001 From: J Date: Mon, 31 Mar 2025 18:25:00 +0000 Subject: [PATCH 26/45] Light warnings cleanup (#36195) * Light warnings cleanup * Using EntitySystem Proxy overrides * New TryComp guards for light animations * Reverting guards when not wanted --- Content.Client/Light/EntitySystems/RotatingLightSystem.cs | 2 +- .../Light/Visualizers/PoweredLightVisualizerSystem.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Content.Client/Light/EntitySystems/RotatingLightSystem.cs b/Content.Client/Light/EntitySystems/RotatingLightSystem.cs index 5c2c4e4c87..1e20d7041a 100644 --- a/Content.Client/Light/EntitySystems/RotatingLightSystem.cs +++ b/Content.Client/Light/EntitySystems/RotatingLightSystem.cs @@ -85,7 +85,7 @@ public sealed class RotatingLightSystem : SharedRotatingLightSystem if (!_animations.HasRunningAnimation(uid, player, AnimKey)) { - _animations.Play(uid, player, GetAnimation(comp.Speed), AnimKey); + _animations.Play((uid, player), GetAnimation(comp.Speed), AnimKey); } } } diff --git a/Content.Client/Light/Visualizers/PoweredLightVisualizerSystem.cs b/Content.Client/Light/Visualizers/PoweredLightVisualizerSystem.cs index ee81641d26..c07742462b 100644 --- a/Content.Client/Light/Visualizers/PoweredLightVisualizerSystem.cs +++ b/Content.Client/Light/Visualizers/PoweredLightVisualizerSystem.cs @@ -2,7 +2,6 @@ using Content.Shared.Light; using Robust.Client.Animations; using Robust.Client.GameObjects; using Robust.Shared.Animations; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Random; @@ -53,13 +52,14 @@ public sealed class PoweredLightVisualizerSystem : VisualizerSystem private void OnAnimationCompleted(EntityUid uid, PoweredLightVisualsComponent comp, AnimationCompletedEvent args) { + if (!TryComp(uid, out var animationPlayer)) + return; if (args.Key != PoweredLightVisualsComponent.BlinkingAnimationKey) return; - if(!comp.IsBlinking) return; - AnimationSystem.Play(uid, Comp(uid), BlinkingAnimation(comp), PoweredLightVisualsComponent.BlinkingAnimationKey); + AnimationSystem.Play((uid, animationPlayer), BlinkingAnimation(comp), PoweredLightVisualsComponent.BlinkingAnimationKey); } /// @@ -76,7 +76,7 @@ public sealed class PoweredLightVisualizerSystem : VisualizerSystem(uid); if (shouldBeBlinking) { - AnimationSystem.Play(uid, animationPlayer, BlinkingAnimation(comp), PoweredLightVisualsComponent.BlinkingAnimationKey); + AnimationSystem.Play((uid, animationPlayer), BlinkingAnimation(comp), PoweredLightVisualsComponent.BlinkingAnimationKey); } else if (AnimationSystem.HasRunningAnimation(uid, animationPlayer, PoweredLightVisualsComponent.BlinkingAnimationKey)) { From f01c6e9b37e9ccc60608cf21370dad43ccd50a7f Mon Sep 17 00:00:00 2001 From: YoungThug Date: Mon, 31 Mar 2025 14:18:21 -0700 Subject: [PATCH 27/45] Holoparasite injector fix (#36109) * HoloParaTextFix * PleaseSpeedMergeLmao * ThankYouOrks * Update Resources/Locale/en-US/guardian/guardian.ftl Co-authored-by: Tayrtahn * Update Content.Server/Guardian/GuardianSystem.cs Co-authored-by: Tayrtahn * Update Content.Server/Guardian/GuardianSystem.cs Co-authored-by: Tayrtahn --------- Co-authored-by: Tayrtahn --- Content.Server/Guardian/GuardianSystem.cs | 5 ++++- Resources/Locale/en-US/guardian/guardian.ftl | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Content.Server/Guardian/GuardianSystem.cs b/Content.Server/Guardian/GuardianSystem.cs index 341993ce2f..e8c3fe7028 100644 --- a/Content.Server/Guardian/GuardianSystem.cs +++ b/Content.Server/Guardian/GuardianSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Examine; using Content.Shared.Guardian; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; +using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Mobs; @@ -188,7 +189,9 @@ namespace Content.Server.Guardian // Can only inject things with the component... if (!HasComp(target)) { - _popupSystem.PopupEntity(Loc.GetString("guardian-activator-invalid-target"), user, user); + var msg = Loc.GetString("guardian-activator-invalid-target", ("entity", Identity.Entity(target, EntityManager, user))); + + _popupSystem.PopupEntity(msg, user, user); return; } diff --git a/Resources/Locale/en-US/guardian/guardian.ftl b/Resources/Locale/en-US/guardian/guardian.ftl index 141646087d..13cb9ad9da 100644 --- a/Resources/Locale/en-US/guardian/guardian.ftl +++ b/Resources/Locale/en-US/guardian/guardian.ftl @@ -6,8 +6,7 @@ guardian-already-present-invalid-creation = You are NOT re-living that haunting guardian-no-actions-invalid-creation = You don't have the ability to host a guardian! guardian-activator-empty-invalid-creation = The injector is spent. guardian-activator-empty-examine = [color=#ba1919]The injector is spent.[/color] -# TODO: Change this once other species can inject it? -guardian-activator-invalid-target = Only humans can be injected! +guardian-activator-invalid-target = {CAPITALIZE(THE($entity))} cannot be injected! guardian-no-soul = Your guardian has no soul. guardian-available = Your guardian now has a soul. guardian-inside-container = There's no room to release your guardian! From d6dad24db81b4ec6cf78de2c12458e3f861d1113 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 31 Mar 2025 17:56:06 -0400 Subject: [PATCH 28/45] Localize and colorize grill temperature settings (#36236) * Make it easier to localize grill heat level settings * Change examine text color based on setting * Trailing periods * Use Fluent terms to reduce duplication --- .../en-US/temperature/entity-heater.ftl | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Resources/Locale/en-US/temperature/entity-heater.ftl b/Resources/Locale/en-US/temperature/entity-heater.ftl index a809d508e7..391e84e512 100644 --- a/Resources/Locale/en-US/temperature/entity-heater.ftl +++ b/Resources/Locale/en-US/temperature/entity-heater.ftl @@ -1,3 +1,18 @@ -entity-heater-examined = It is set to [color=gray]{$setting}[/color] -entity-heater-switch-setting = Switch to {$setting} -entity-heater-switched-setting = Switched to {$setting} +-entity-heater-setting-name = + { $setting -> + [off] off + [low] low + [medium] medium + [high] high + *[other] unknown + } + +entity-heater-examined = It is set to { $setting -> + [off] [color=gray]{ -entity-heater-setting-name(setting: "off") }[/color] + [low] [color=yellow]{ -entity-heater-setting-name(setting: "low") }[/color] + [medium] [color=orange]{ -entity-heater-setting-name(setting: "medium") }[/color] + [high] [color=red]{ -entity-heater-setting-name(setting: "high") }[/color] + *[other] [color=purple]{ -entity-heater-setting-name(setting: "other") }[/color] +}. +entity-heater-switch-setting = Switch to { -entity-heater-setting-name(setting: $setting) } +entity-heater-switched-setting = Switched to { -entity-heater-setting-name(setting: $setting) }. From d2ad6cdcaa21b1d5000d8a43f42f612b03ebc469 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 31 Mar 2025 18:00:04 -0400 Subject: [PATCH 29/45] Rework the way held items scatter when holder is knocked down (#36232) * Redo drop held items math * Don't assume the holder has a PhysicsComponent * Assume infinite mass for held items with no PhysicsComponent * Switch to EntityQuery for PhysicsComponent * The micro-est of optimizations * use NextAngle * Might as well do that outside the loop --- Content.Server/Hands/Systems/HandsSystem.cs | 39 +++++++++++++++++---- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index 41f582cde8..1e8e012c52 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -39,6 +39,15 @@ namespace Content.Server.Hands.Systems [Dependency] private readonly PullingSystem _pullingSystem = default!; [Dependency] private readonly ThrowingSystem _throwingSystem = default!; + private EntityQuery _physicsQuery; + + /// + /// Items dropped when the holder falls down will be launched in + /// a direction offset by up to this many degrees from the holder's + /// movement direction. + /// + private const float DropHeldItemsSpread = 45; + public override void Initialize() { base.Initialize(); @@ -60,6 +69,8 @@ namespace Content.Server.Hands.Systems CommandBinds.Builder .Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem)) .Register(); + + _physicsQuery = GetEntityQuery(); } public override void Shutdown() @@ -234,13 +245,13 @@ namespace Content.Server.Hands.Systems private void OnDropHandItems(Entity entity, ref DropHandItemsEvent args) { - var direction = EntityManager.TryGetComponent(entity, out PhysicsComponent? comp) ? comp.LinearVelocity / 50 : Vector2.Zero; - var dropAngle = _random.NextFloat(0.8f, 1.2f); + // If the holder doesn't have a physics component, they ain't moving + var holderVelocity = _physicsQuery.TryComp(entity, out var physics) ? physics.LinearVelocity : Vector2.Zero; + var spreadMaxAngle = Angle.FromDegrees(DropHeldItemsSpread); var fellEvent = new FellDownEvent(entity); RaiseLocalEvent(entity, fellEvent, false); - var worldRotation = TransformSystem.GetWorldRotation(entity).ToVec(); foreach (var hand in entity.Comp.Hands.Values) { if (hand.HeldEntity is not EntityUid held) @@ -255,10 +266,26 @@ namespace Content.Server.Hands.Systems if (!TryDrop(entity, hand, null, checkActionBlocker: false, handsComp: entity.Comp)) continue; + // Rotate the item's throw vector a bit for each item + var angleOffset = _random.NextAngle(-spreadMaxAngle, spreadMaxAngle); + // Rotate the holder's velocity vector by the angle offset to get the item's velocity vector + var itemVelocity = angleOffset.RotateVec(holderVelocity); + // Decrease the distance of the throw by a random amount + itemVelocity *= _random.NextFloat(1f); + // Heavier objects don't get thrown as far + // If the item doesn't have a physics component, it isn't going to get thrown anyway, but we'll assume infinite mass + itemVelocity *= _physicsQuery.TryComp(held, out var heldPhysics) ? heldPhysics.InvMass : 0; + // Throw at half the holder's intentional throw speed and + // vary the speed a little to make it look more interesting + var throwSpeed = entity.Comp.BaseThrowspeed * _random.NextFloat(0.45f, 0.55f); + _throwingSystem.TryThrow(held, - _random.NextAngle().RotateVec(direction / dropAngle + worldRotation / 50), - 0.5f * dropAngle * _random.NextFloat(-0.9f, 1.1f), - entity, 0); + itemVelocity, + throwSpeed, + entity, + pushbackRatio: 0, + compensateFriction: false + ); } } From 2688a867df235108109a66bf126fe074e86abb7e Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 31 Mar 2025 22:01:13 +0000 Subject: [PATCH 30/45] 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 6e2758ba37..5714221bf6 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: SpaceRox1244 - changes: - - message: Closets and lockers now have visuals for being labeled with papers. - type: Add - id: 7619 - time: '2024-11-17T03:27:29.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33318 - author: Ubaser changes: - message: You can now craft dim light bulbs at an autolathe. @@ -3888,3 +3881,11 @@ id: 8118 time: '2025-03-31T02:27:08.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/36210 +- author: Tayrtahn + changes: + - message: Items thrown when someone slips now tend to scatter in the direction + they are moving, and respect the item's mass. + type: Tweak + id: 8119 + time: '2025-03-31T22:00:04.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36232 From 115313ddedf3da0077f4a5ea2191af9485ba6abd Mon Sep 17 00:00:00 2001 From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Tue, 1 Apr 2025 00:32:31 +0200 Subject: [PATCH 31/45] Undetermined thieving satchel (#36201) * yippee! * no toolboxes allowed * sprite, descriptions --- .../ThiefUndeterminedBackpackComponent.cs | 9 +- .../ThiefUndeterminedBackpackSystem.cs | 22 ++++- .../game-presets/preset-thief.ftl | 2 +- Resources/Locale/en-US/thief/backpack.ftl | 2 +- .../Prototypes/Catalog/thief_toolbox_sets.yml | 11 +-- .../Entities/Clothing/Back/smuggler.yml | 4 +- .../Entities/Objects/Tools/thief.yml | 80 ++++++++++++++++++ .../Entities/Objects/Tools/thief_beacon.yml | 40 --------- .../Entities/Objects/Tools/toolbox.yml | 26 ------ Resources/Prototypes/Roles/Antags/thief.yml | 2 +- .../Guidebook/Antagonist/Thieves.xml | 15 ++-- .../Back/Satchels/smuggler.rsi/folded.png | Bin 0 -> 5979 bytes .../Back/Satchels/smuggler.rsi/meta.json | 5 +- 13 files changed, 125 insertions(+), 93 deletions(-) create mode 100644 Resources/Prototypes/Entities/Objects/Tools/thief.yml delete mode 100644 Resources/Prototypes/Entities/Objects/Tools/thief_beacon.yml create mode 100644 Resources/Textures/Clothing/Back/Satchels/smuggler.rsi/folded.png diff --git a/Content.Server/Thief/Components/ThiefUndeterminedBackpackComponent.cs b/Content.Server/Thief/Components/ThiefUndeterminedBackpackComponent.cs index 64f88df657..9080caa245 100644 --- a/Content.Server/Thief/Components/ThiefUndeterminedBackpackComponent.cs +++ b/Content.Server/Thief/Components/ThiefUndeterminedBackpackComponent.cs @@ -22,11 +22,18 @@ public sealed partial class ThiefUndeterminedBackpackComponent : Component public List SelectedSets = new(); [DataField] - public SoundSpecifier ApproveSound = new SoundPathSpecifier("/Audio/Effects/rustle1.ogg"); + public SoundCollectionSpecifier ApproveSound = new SoundCollectionSpecifier("storageRustle"); /// /// Max number of sets you can select. /// [DataField] public int MaxSelectedSets = 2; + + /// + /// What entity all the spawned items will appear inside of + /// If null, will instead drop on the ground. + /// + [DataField] + public EntProtoId? SpawnedStoragePrototype; } diff --git a/Content.Server/Thief/Systems/ThiefUndeterminedBackpackSystem.cs b/Content.Server/Thief/Systems/ThiefUndeterminedBackpackSystem.cs index 3248a6b9c8..23f845a2e7 100644 --- a/Content.Server/Thief/Systems/ThiefUndeterminedBackpackSystem.cs +++ b/Content.Server/Thief/Systems/ThiefUndeterminedBackpackSystem.cs @@ -1,5 +1,7 @@ using Content.Server.Thief.Components; +using Content.Shared.Hands.EntitySystems; using Content.Shared.Item; +using Content.Shared.Storage.EntitySystems; using Content.Shared.Thief; using Robust.Server.GameObjects; using Robust.Server.Audio; @@ -17,6 +19,8 @@ public sealed class ThiefUndeterminedBackpackSystem : EntitySystem [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; + [Dependency] private readonly SharedStorageSystem _storage = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; public override void Initialize() { @@ -37,6 +41,10 @@ public sealed class ThiefUndeterminedBackpackSystem : EntitySystem if (backpack.Comp.SelectedSets.Count != backpack.Comp.MaxSelectedSets) return; + EntityUid? spawnedStorage = null; + if (backpack.Comp.SpawnedStoragePrototype != null) + spawnedStorage = Spawn(backpack.Comp.SpawnedStoragePrototype, _transform.GetMapCoordinates(backpack.Owner)); + foreach (var i in backpack.Comp.SelectedSets) { var set = _proto.Index(backpack.Comp.PossibleSets[i]); @@ -44,10 +52,20 @@ public sealed class ThiefUndeterminedBackpackSystem : EntitySystem { var ent = Spawn(item, _transform.GetMapCoordinates(backpack.Owner)); if (TryComp(ent, out var itemComponent)) - _transform.DropNextTo(ent, backpack.Owner); + { + if (spawnedStorage != null) + _storage.Insert(spawnedStorage.Value, ent, out _, playSound: false); + else + _transform.DropNextTo(ent, backpack.Owner); + } } } - _audio.PlayPvs(backpack.Comp.ApproveSound, backpack.Owner); + + if (spawnedStorage != null) + _hands.TryPickupAnyHand(args.Actor, spawnedStorage.Value); + + // Play the sound on coordinates of the backpack/toolbox. The reason being, since we immediately delete it, the sound gets deleted alongside it. + _audio.PlayPvs(backpack.Comp.ApproveSound, Transform(backpack.Owner).Coordinates); QueueDel(backpack); } private void OnChangeSet(Entity backpack, ref ThiefBackpackChangeSetMessage args) diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-thief.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-thief.ftl index ab2b8f88d7..46eab5fee3 100644 --- a/Resources/Locale/en-US/game-ticking/game-presets/preset-thief.ftl +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-thief.ftl @@ -10,7 +10,7 @@ thief-role-greeting-animal = Steal things that you like. thief-role-greeting-equipment = - You have a toolbox of thieves' + You have a satchel of thieves' tools and chameleon thieves' gloves. Choose your starting equipment, and do your work stealthily. diff --git a/Resources/Locale/en-US/thief/backpack.ftl b/Resources/Locale/en-US/thief/backpack.ftl index 6d3baa2c0d..962480e2e2 100644 --- a/Resources/Locale/en-US/thief/backpack.ftl +++ b/Resources/Locale/en-US/thief/backpack.ftl @@ -1,4 +1,4 @@ -thief-backpack-window-title = thief toolbox +thief-backpack-window-title = thieving kit thief-backpack-window-description = Inside are your tools of the trade, which will dissolve when you're ready. diff --git a/Resources/Prototypes/Catalog/thief_toolbox_sets.yml b/Resources/Prototypes/Catalog/thief_toolbox_sets.yml index 7826c1db97..a17cb128a7 100644 --- a/Resources/Prototypes/Catalog/thief_toolbox_sets.yml +++ b/Resources/Prototypes/Catalog/thief_toolbox_sets.yml @@ -6,16 +6,7 @@ sprite: /Textures/Clothing/OuterClothing/Misc/black_hoodie.rsi state: icon content: - - ChameleonPDA - - ClothingUniformJumpsuitChameleon - - ClothingOuterChameleon - - ClothingNeckChameleon - - ClothingMaskGasChameleon - - ClothingHeadHatChameleon - - ClothingEyesChameleon - - ClothingHeadsetChameleon - - ClothingShoesChameleon - - BarberScissors + - ClothingBackpackChameleonFill - ChameleonProjector - FakeMindShieldImplanter - AgentIDCard diff --git a/Resources/Prototypes/Entities/Clothing/Back/smuggler.yml b/Resources/Prototypes/Entities/Clothing/Back/smuggler.yml index c9d7f61890..f5ec4fcd3a 100644 --- a/Resources/Prototypes/Entities/Clothing/Back/smuggler.yml +++ b/Resources/Prototypes/Entities/Clothing/Back/smuggler.yml @@ -37,7 +37,7 @@ id: ClothingBackpackSatchelSmuggler name: smuggler's satchel suffix: Empty - description: A dingy, suspicious looking satchel. + description: A handy, suspicious looking satchel. Just flat enough to fit underneath floor tiles. components: - type: Sprite sprite: Clothing/Back/Satchels/smuggler.rsi @@ -48,7 +48,7 @@ id: ClothingBackpackSatchelSmugglerUnanchored name: smuggler's satchel suffix: Empty, Unanchored - description: A dingy, suspicious looking satchel. + description: A handy, suspicious looking satchel. Just flat enough to fit underneath floor tiles. components: - type: Sprite sprite: Clothing/Back/Satchels/smuggler.rsi diff --git a/Resources/Prototypes/Entities/Objects/Tools/thief.yml b/Resources/Prototypes/Entities/Objects/Tools/thief.yml new file mode 100644 index 0000000000..7200c8c06d --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Tools/thief.yml @@ -0,0 +1,80 @@ +- type: entity + parent: BaseMinorContraband + id: ThiefBeacon + name: thieving beacon + description: A device that will teleport everything around it to the thief's vault at the end of the shift. + components: + - type: ThiefBeacon + - type: StealArea + range: 2 # Slightly larger than fulton beacon's random offset + - type: Item + size: Normal + - type: Physics + bodyType: Dynamic + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.25,-0.4,0.25,0.1" + density: 20 + mask: + - Impassable + - type: Foldable + folded: true + - type: Clickable + - type: InteractionOutline + - type: Appearance + - type: GenericVisualizer + visuals: + enum.FoldedVisuals.State: + foldedLayer: + True: { state: folded_extraction } + False: { state: extraction_point } + - type: Sprite + sprite: Objects/Tools/thief_beacon.rsi + drawdepth: SmallObjects + noRot: true + layers: + - state: extraction_point + map: [ "foldedLayer" ] + +- type: entity + id: ToolboxThief + name: undetermined thieving toolbox + description: This is where your favorite thief's supplies lie. Try to remember which ones. + parent: [ BaseItem, BaseMinorContraband ] + components: + - type: Sprite + sprite: Objects/Tools/Toolboxes/toolbox_thief.rsi + state: icon + - type: ThiefUndeterminedBackpack + possibleSets: + # TODO Thief pinpointer needed + - ChemistrySet + - ToolsSet + - ChameleonSet # TODO Chameleon stump PR needed + - SyndieSet + - SleeperSet + - CommunicatorSet + - SmugglerSet + - type: ActivatableUI + key: enum.ThiefBackpackUIKey.Key + - type: UserInterface + interfaces: + enum.ThiefBackpackUIKey.Key: + type: ThiefBackpackBoundUserInterface + +- type: entity + id: SatchelThief + name: undetermined thieving satchel + description: This is where your favorite thief's supplies lie. Folded for your convenience. + parent: ToolboxThief + components: + - type: Sprite + sprite: Clothing/Back/Satchels/smuggler.rsi + state: folded + - type: Item + storedRotation: 90 + - type: ThiefUndeterminedBackpack + spawnedStoragePrototype: ClothingBackpackSatchelSmugglerUnanchored diff --git a/Resources/Prototypes/Entities/Objects/Tools/thief_beacon.yml b/Resources/Prototypes/Entities/Objects/Tools/thief_beacon.yml deleted file mode 100644 index f0f3737417..0000000000 --- a/Resources/Prototypes/Entities/Objects/Tools/thief_beacon.yml +++ /dev/null @@ -1,40 +0,0 @@ -- type: entity - parent: BaseMinorContraband - id: ThiefBeacon - name: thieving beacon - description: A device that will teleport everything around it to the thief's vault at the end of the shift. - components: - - type: ThiefBeacon - - type: StealArea - range: 2 # Slightly larger than fulton beacon's random offset - - type: Item - size: Normal - - type: Physics - bodyType: Dynamic - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.25,-0.4,0.25,0.1" - density: 20 - mask: - - Impassable - - type: Foldable - folded: true - - type: Clickable - - type: InteractionOutline - - type: Appearance - - type: GenericVisualizer - visuals: - enum.FoldedVisuals.State: - foldedLayer: - True: { state: folded_extraction } - False: { state: extraction_point } - - type: Sprite - sprite: Objects/Tools/thief_beacon.rsi - drawdepth: SmallObjects - noRot: true - layers: - - state: extraction_point - map: [ "foldedLayer" ] diff --git a/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml b/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml index dd1f41e571..d0f42e405f 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml @@ -143,29 +143,3 @@ state: icon - type: Item sprite: Objects/Tools/Toolboxes/toolbox_gold.rsi - -- type: entity - id: ToolboxThief - name: thief undetermined toolbox - description: This is where your favorite thief's supplies lie. Try to remember which ones. - parent: [ BaseItem, BaseMinorContraband ] - components: - - type: Sprite - sprite: Objects/Tools/Toolboxes/toolbox_thief.rsi - state: icon - - type: ThiefUndeterminedBackpack - possibleSets: - # TODO Thief pinpointer needed - - ChemistrySet - - ToolsSet - - ChameleonSet # TODO Chameleon stump PR needed - - SyndieSet - - SleeperSet - - CommunicatorSet - - SmugglerSet - - type: ActivatableUI - key: enum.ThiefBackpackUIKey.Key - - type: UserInterface - interfaces: - enum.ThiefBackpackUIKey.Key: - type: ThiefBackpackBoundUserInterface diff --git a/Resources/Prototypes/Roles/Antags/thief.yml b/Resources/Prototypes/Roles/Antags/thief.yml index 0309b00b50..12fdefba2b 100644 --- a/Resources/Prototypes/Roles/Antags/thief.yml +++ b/Resources/Prototypes/Roles/Antags/thief.yml @@ -11,5 +11,5 @@ storage: back: - ThiefBeacon - - ToolboxThief + - SatchelThief - ClothingHandsChameleonThief diff --git a/Resources/ServerInfo/Guidebook/Antagonist/Thieves.xml b/Resources/ServerInfo/Guidebook/Antagonist/Thieves.xml index 4c11fc0d99..ec871851c6 100644 --- a/Resources/ServerInfo/Guidebook/Antagonist/Thieves.xml +++ b/Resources/ServerInfo/Guidebook/Antagonist/Thieves.xml @@ -1,6 +1,6 @@ # Thieves - + [color=#999999][italic]"Yoink! I'll be taking that! And that! Ooh, don't mind if I do!"[/italic][/color] @@ -25,7 +25,7 @@ ## Tools of the Trade - You've got two more aces up your stolen sleeves: your [color=cyan]beacon[/color] and your [color=cyan]toolbox.[/color] + You've got two more aces up your stolen sleeves: your [color=cyan]beacon[/color] and your [color=cyan]satchel.[/color] Your [color=cyan]beacon[/color] provides safe passage home for trinkets that may not be easy to carry with you on the evac shuttle. Simply find a secluded part of the station to unfold the beacon, then set its coordinates to your hideout. Any shinies near it will be [bold]teleported to your vault when the shift ends,[/bold] fulfilling your objectives. @@ -36,11 +36,10 @@ - Your [color=cyan]toolbox[/color] contains... well, whatever you remembered to pack. [bold]You can select two pre-made kits[/bold] to help you complete grander heists. - Approve your choices in a safe place, as the toolbox will dissolve and the gear will drop at your feet. + Your [color=cyan]satchel[/color] contains... well, whatever you remembered to pack. [bold]You can select two pre-made kits[/bold] to help you complete grander heists. - + @@ -56,7 +55,7 @@ ## Centerpiece of the Collection Your kleptomania will take you places. One day, you'll feel like stealing a few figurines. Another day, you'll feel like stealing an industrial machine. - + No matter. They'll all be a part of your collection within a matter of time. You can steal items by [bold]having them on your person[/bold] when you get to CentComm. Failing this, you can steal larger items by [bold]leaving them by your beacon.[/bold] @@ -64,12 +63,12 @@ Some of the more [italic]animate[/italic] objectives may not cooperate with you. Make sure they're alive and with you or your beacon when the shift ends. Things that you may desire include but are not limited to: - + - + diff --git a/Resources/Textures/Clothing/Back/Satchels/smuggler.rsi/folded.png b/Resources/Textures/Clothing/Back/Satchels/smuggler.rsi/folded.png new file mode 100644 index 0000000000000000000000000000000000000000..0773c0b94dd573b9b33529f2c89b2a9a63d37e79 GIT binary patch literal 5979 zcmeHKdo+|=8z0mVa!tu?rjd%weTm1=S=AJd0#GEseVz-Bb&!pS%HM~JaD{q^w6xWJrWyq**{oEufa3j z{ugx~Wy@1nRZ!l_=>QgUpKZ$)mByD+3zKo@gt`?Q8NVZ81yf}LAdFwwT)#DXggLdltX6Cm6 z;Y4?%(n|+4Gz-zoE0yzf@SOY+^7);`MoO^}i6u`A&w}!PRvFf3p5=BH7^@fiY%*a; z;@~6mN#2`Dlvhhfp1QRpF21$UNO7!w^tK72Y)G%)ndqE1-_ZOTV4`AozhZ^$=62dE8NkU)SC=@j_dGq57K6*d zC9&WOW0n$bMD0~5Wy}~kbVb7+WA5m1y!ZfVq@|L1ta5W=;qw;XeevFFEP`KL^s2xU1{vjTmj98xnRcfWE;UHqb{ z)T^kKy!G!`Pharbao3C!Ra=bjl8ZJ}4c3M};6Ly$rf>Z4`Xj7za#^Apqs-I47+}7= zhFU>QxuhDk+H8JY;IgQwqn8HD{gh{lw%_kiTDM?^i3u*7s-dt01o!10>@um@0MepduM#Gx@GEFneHlge!u4gm3h*+C4Nr~ zf_SkYu#^ASxyB(C<(ueYaNpjJkkvW+9d0ac8niyTtk1k*^-aA5%~{mbytG+}R+ZXi ztDnc1T@5{AVpM;1hG}Y_UFm^pb;BfO^?I$ee9vr>dFG#0c2;{VI`UUuc2q%C-0Ui- z+1`9%Jhx@=bX4?4!}!0tUX?WObeL20$Sb55uXo3M_6Mqk*`JL^p6#F)v$n>1b!`V9 z)L_Kt^5>WLF}OHX4+$8|L2 ztHaXEgu;4wXm#9SmlKa$PM;>_3~7hp_WEBIH>z744yW&W?3kkGX`7asO1d@CQ5S8r zvgGjG(d?Xe`}JXZNT=rwX|C=@kB!S;*xPQM)YT-{%(ztlaN}W9$3o3Mc zLva!P2olek;b$&0PLUv2lZtAxY61c2!2R&cBi(p7n^O!#2w{ zF%N7GbAuh{&Oatyb>pe`l}k$}b$6-*39dKpYWH7JwH_GAqaAx$Xnp}&}%8aWS16}I7i!* zmK^b}A<5PSlj#TIMCSR*I{Tg|ev0lrW1-@e%4gu0Y-5DZEHq%=5)FQ!T9s3}|;)R#?TQOGG3Ngy5NTrT5GrDWj$t5QV*EV>hyB%F7%B>y z3WvkSf20(il~DJzKVA2cOA?l-c2@J%L}3g>%AAoH)d zKWKl6eM%Y9a&x0O2-u-A_goz4DB1cnj)2YM(5AXnHkWO|#*xrOOR5E$L?N@#02j|i z11w9N1qII{T5#~+LAmh75`fPJWl#_}h6mvgi6lH8AcJU-z$KwcY%UJXf~?RGt_2BC zq!PG9i|-&jL_8=ffuQfbl0k7GC=!=Q1pyKPZ3(ccXcC??1w}!FmQ*&&0?(y_c*+zM zhfQ-5h=KuVJ9)uC0EiXx1EvHr!fDGrUFawR2KQ~oGYF7yAp__P@c0}-xcHlh$qNR( zB!G-hyd{Z9q>xA!BnpX4Bon?Vd4nP`l#4P{JPt#gmdG}S289Dr3&=7R0+_OZqM_M~ zKtLi8F$IDkI!fjgQfB#8>;@eu4j=&>00{_z;s_Qr9Fa!EG4T`{!4jerjkBcTzS9?Q zc-)BprY$=@NbBiMcjk$q^&_UHrq7fY7&1MY9tQEI4igeNbu4HAdpZO$5C(Fl{DiQk zMeI!gKLCX4$LECoGSB-D$w0BB0(eVHHkv@d;3<|CEFyt4P109% zv4ATH2SniV0EkD3D=0yyxI!-Zyj7+@dK110lpZS7({eQO4`|$r<0*U;!$WQV6i>_aE{S*T~rTi6<7zn~=5-r@Ff^E;~E>nB(*0-NP?m$_uw` zb1(C0B(-+s4GjhFKza^DUO3r!FrJJCbm5GZ)@=<{!L>d7!O>H{A(j;ItF0D18hEQ= zmp?#@wcDPUnAo1GA~Xo7sH$=*=_qX9)E8lNKParQEgb~17F}WC(+A#0K790ur3<%< zTVZ&hTfTSv7}E?^;fs#Z2s)WAeKST^iOASihY+Qm=A2E$up}+JID&7o9c8 zIvNrlzUwIOxK7@)R-2uG?!H!ThqZ$Bbq^bIQBhIZ+~@DxJd&7ryw<>NlOdLrWMFIMozJc}2zHoLix} z0|NtfzsDou1K_i_@% literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Back/Satchels/smuggler.rsi/meta.json b/Resources/Textures/Clothing/Back/Satchels/smuggler.rsi/meta.json index d3b44ffaa2..66c0f6df16 100644 --- a/Resources/Textures/Clothing/Back/Satchels/smuggler.rsi/meta.json +++ b/Resources/Textures/Clothing/Back/Satchels/smuggler.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/a8056c6ba7f5367934ef829116e57d743226e1f0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/a8056c6ba7f5367934ef829116e57d743226e1f0, folded by princesscheeseballs (Discord)(https://github.com/Pronana).", "size": { "x": 32, "y": 32 @@ -10,6 +10,9 @@ { "name": "icon" }, + { + "name": "folded" + }, { "name": "equipped-BACKPACK", "directions": 4 From 3c24f216073159f85ae7bd3106ba896837921080 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 31 Mar 2025 22:33:39 +0000 Subject: [PATCH 32/45] Automatic changelog update --- Resources/Changelog/Changelog.yml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 5714221bf6..4065ead5af 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Ubaser - changes: - - message: You can now craft dim light bulbs at an autolathe. - type: Add - id: 7620 - time: '2024-11-18T06:32:08.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33383 - author: Ilya246 changes: - message: Multiple people using one shuttle console will no longer cause the shuttle @@ -3889,3 +3882,19 @@ id: 8119 time: '2025-03-31T22:00:04.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/36232 +- author: ScarKy0 + changes: + - message: Thieves now start with the thieving satchel instead of their toolbox. + The satchel will get all the selected kits spawned inside of it. + type: Add + - message: Thief Chameleon kit now comes with a backpack and a bonus pair of chameleon + gloves. Be careful, they aren't thieving gloves and can be tough to tell apart! + type: Tweak + - message: Updated smuggler stachel's description to reflect what it's used for. + type: Tweak + - message: Thieving satchel and toolbox now correctly play a sound when their kits + are selected. + type: Fix + id: 8120 + time: '2025-03-31T22:32:31.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36201 From c8fe3651e5b924f93619d7bfcec64e31380a2856 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Tue, 1 Apr 2025 12:43:19 -0400 Subject: [PATCH 33/45] Add prediction to electric grills (#36241) * Prediction for EntityHeaterSystem * Switch to Entity * meh * Move popup inside ChangeSetting * Fix grill visually turning on when changing setting while power is off * Add note about my failed quest * Why isn't this an IDE warning? * Move comment above switch expression in SettingPower --- .../Temperature/Systems/EntityHeaterSystem.cs | 5 + .../Temperature/Systems/EntityHeaterSystem.cs | 99 +++++-------------- .../Components/EntityHeaterComponent.cs | 13 +-- .../Systems/SharedEntityHeaterSystem.cs | 97 ++++++++++++++++++ 4 files changed, 132 insertions(+), 82 deletions(-) create mode 100644 Content.Client/Temperature/Systems/EntityHeaterSystem.cs rename {Content.Server => Content.Shared}/Temperature/Components/EntityHeaterComponent.cs (73%) create mode 100644 Content.Shared/Temperature/Systems/SharedEntityHeaterSystem.cs diff --git a/Content.Client/Temperature/Systems/EntityHeaterSystem.cs b/Content.Client/Temperature/Systems/EntityHeaterSystem.cs new file mode 100644 index 0000000000..300cfa3d44 --- /dev/null +++ b/Content.Client/Temperature/Systems/EntityHeaterSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Temperature.Systems; + +namespace Content.Client.Temperature.Systems; + +public sealed partial class EntityHeaterSystem : SharedEntityHeaterSystem; diff --git a/Content.Server/Temperature/Systems/EntityHeaterSystem.cs b/Content.Server/Temperature/Systems/EntityHeaterSystem.cs index c4b5b72a9c..8452edf8e9 100644 --- a/Content.Server/Temperature/Systems/EntityHeaterSystem.cs +++ b/Content.Server/Temperature/Systems/EntityHeaterSystem.cs @@ -1,45 +1,41 @@ using Content.Server.Power.Components; -using Content.Server.Temperature.Components; -using Content.Shared.Examine; using Content.Shared.Placeable; -using Content.Shared.Popups; -using Content.Shared.Power; using Content.Shared.Temperature; -using Content.Shared.Verbs; -using Robust.Server.Audio; +using Content.Shared.Temperature.Components; +using Content.Shared.Temperature.Systems; namespace Content.Server.Temperature.Systems; /// -/// Handles updating and events. +/// Handles the server-only parts of /// -public sealed class EntityHeaterSystem : EntitySystem +public sealed class EntityHeaterSystem : SharedEntityHeaterSystem { - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly TemperatureSystem _temperature = default!; - [Dependency] private readonly AudioSystem _audio = default!; - - private readonly int SettingCount = Enum.GetValues(typeof(EntityHeaterSetting)).Length; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnExamined); - SubscribeLocalEvent>(OnGetVerbs); - SubscribeLocalEvent(OnPowerChanged); + SubscribeLocalEvent(OnMapInit); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + // Set initial power level + if (TryComp(ent, out var power)) + power.Load = SettingPower(ent.Comp.Setting, ent.Comp.Power); } public override void Update(float deltaTime) { var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp, out var placer, out var power)) + while (query.MoveNext(out _, out _, out var placer, out var power)) { if (!power.Powered) continue; - // don't divide by total entities since its a big grill + // don't divide by total entities since it's a big grill // excess would just be wasted in the air but that's not worth simulating // if you want a heater thermomachine just use that... var energy = power.PowerReceived * deltaTime; @@ -50,66 +46,17 @@ public sealed class EntityHeaterSystem : EntitySystem } } - private void OnExamined(EntityUid uid, EntityHeaterComponent comp, ExaminedEvent args) + /// + /// doesn't exist on the client, so we need + /// this server-only override to handle setting the network load. + /// + protected override void ChangeSetting(Entity ent, EntityHeaterSetting setting, EntityUid? user = null) { - if (!args.IsInDetailsRange) + base.ChangeSetting(ent, setting, user); + + if (!TryComp(ent, out var power)) return; - args.PushMarkup(Loc.GetString("entity-heater-examined", ("setting", comp.Setting))); - } - - private void OnGetVerbs(EntityUid uid, EntityHeaterComponent comp, GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - var setting = (int) comp.Setting; - setting++; - setting %= SettingCount; - var nextSetting = (EntityHeaterSetting) setting; - - args.Verbs.Add(new AlternativeVerb() - { - Text = Loc.GetString("entity-heater-switch-setting", ("setting", nextSetting)), - Act = () => - { - ChangeSetting(uid, nextSetting, comp); - _popup.PopupEntity(Loc.GetString("entity-heater-switched-setting", ("setting", nextSetting)), uid, args.User); - } - }); - } - - private void OnPowerChanged(EntityUid uid, EntityHeaterComponent comp, ref PowerChangedEvent args) - { - // disable heating element glowing layer if theres no power - // doesn't actually turn it off since that would be annoying - var setting = args.Powered ? comp.Setting : EntityHeaterSetting.Off; - _appearance.SetData(uid, EntityHeaterVisuals.Setting, setting); - } - - private void ChangeSetting(EntityUid uid, EntityHeaterSetting setting, EntityHeaterComponent? comp = null, ApcPowerReceiverComponent? power = null) - { - if (!Resolve(uid, ref comp, ref power)) - return; - - comp.Setting = setting; - power.Load = SettingPower(setting, comp.Power); - _appearance.SetData(uid, EntityHeaterVisuals.Setting, setting); - _audio.PlayPvs(comp.SettingSound, uid); - } - - private float SettingPower(EntityHeaterSetting setting, float max) - { - switch (setting) - { - case EntityHeaterSetting.Low: - return max / 3f; - case EntityHeaterSetting.Medium: - return max * 2f / 3f; - case EntityHeaterSetting.High: - return max; - default: - return 0f; - } + power.Load = SettingPower(setting, ent.Comp.Power); } } diff --git a/Content.Server/Temperature/Components/EntityHeaterComponent.cs b/Content.Shared/Temperature/Components/EntityHeaterComponent.cs similarity index 73% rename from Content.Server/Temperature/Components/EntityHeaterComponent.cs rename to Content.Shared/Temperature/Components/EntityHeaterComponent.cs index 0b5acb421a..6cf97a0534 100644 --- a/Content.Server/Temperature/Components/EntityHeaterComponent.cs +++ b/Content.Shared/Temperature/Components/EntityHeaterComponent.cs @@ -1,26 +1,27 @@ -using Content.Server.Temperature.Systems; -using Content.Shared.Temperature; +using Content.Shared.Temperature.Systems; using Robust.Shared.Audio; +using Robust.Shared.GameStates; -namespace Content.Server.Temperature.Components; +namespace Content.Shared.Temperature.Components; /// /// Adds thermal energy to entities with placed on it. /// -[RegisterComponent, Access(typeof(EntityHeaterSystem))] +[RegisterComponent, Access(typeof(SharedEntityHeaterSystem))] +[NetworkedComponent, AutoGenerateComponentState] public sealed partial class EntityHeaterComponent : Component { /// /// Power used when heating at the high setting. /// Low and medium are 33% and 66% respectively. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public float Power = 2400f; /// /// Current setting of the heater. If it is off or unpowered it won't heat anything. /// - [DataField] + [DataField, AutoNetworkedField] public EntityHeaterSetting Setting = EntityHeaterSetting.Off; /// diff --git a/Content.Shared/Temperature/Systems/SharedEntityHeaterSystem.cs b/Content.Shared/Temperature/Systems/SharedEntityHeaterSystem.cs new file mode 100644 index 0000000000..887047bfa1 --- /dev/null +++ b/Content.Shared/Temperature/Systems/SharedEntityHeaterSystem.cs @@ -0,0 +1,97 @@ +using Content.Shared.Examine; +using Content.Shared.Popups; +using Content.Shared.Power; +using Content.Shared.Power.EntitySystems; +using Content.Shared.Temperature.Components; +using Content.Shared.Verbs; +using Robust.Shared.Audio.Systems; + +namespace Content.Shared.Temperature.Systems; + +/// +/// Handles events. +/// +public abstract partial class SharedEntityHeaterSystem : EntitySystem +{ + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedPowerReceiverSystem _receiver = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + + private readonly int _settingCount = Enum.GetValues().Length; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent>(OnGetVerbs); + SubscribeLocalEvent(OnPowerChanged); + } + + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + args.PushMarkup(Loc.GetString("entity-heater-examined", ("setting", ent.Comp.Setting))); + } + + private void OnGetVerbs(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + var nextSettingIndex = ((int)ent.Comp.Setting + 1) % _settingCount; + var nextSetting = (EntityHeaterSetting)nextSettingIndex; + + var user = args.User; + args.Verbs.Add(new AlternativeVerb() + { + Text = Loc.GetString("entity-heater-switch-setting", ("setting", nextSetting)), + Act = () => + { + ChangeSetting(ent, nextSetting, user); + } + }); + } + + private void OnPowerChanged(Entity ent, ref PowerChangedEvent args) + { + // disable heating element glowing layer if theres no power + // doesn't actually change the setting since that would be annoying + var setting = args.Powered ? ent.Comp.Setting : EntityHeaterSetting.Off; + _appearance.SetData(ent, EntityHeaterVisuals.Setting, setting); + } + + protected virtual void ChangeSetting(Entity ent, EntityHeaterSetting setting, EntityUid? user = null) + { + // Still allow changing the setting without power + ent.Comp.Setting = setting; + _audio.PlayPredicted(ent.Comp.SettingSound, ent, user); + _popup.PopupClient(Loc.GetString("entity-heater-switched-setting", ("setting", setting)), ent, user); + Dirty(ent); + + // Only show the glowing heating element layer if there's power + if (_receiver.IsPowered(ent.Owner)) + _appearance.SetData(ent, EntityHeaterVisuals.Setting, setting); + } + + protected float SettingPower(EntityHeaterSetting setting, float max) + { + // Power use while off needs to be non-zero so powernet doesn't consider the device powered + // by an unpowered network while in the off state. Otherwise, when we increase the load, + // the clientside APC receiver will think the device is powered until it gets the next + // update from the server, which will cause the heating element to glow for a moment. + // I spent several hours trying to figure out a better way to do this using PowerDisabled + // or something, but nothing worked as well as this. + // Just think of the load as a little LED, or bad wiring, or something. + return setting switch + { + EntityHeaterSetting.Low => max / 3f, + EntityHeaterSetting.Medium => max * 2f / 3f, + EntityHeaterSetting.High => max, + _ => 0.01f, + }; + } +} From 2cc93e23e24bef9a80849ba9bde72189fb745d41 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 1 Apr 2025 16:44:28 +0000 Subject: [PATCH 34/45] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 4065ead5af..45ca1f71b9 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Ilya246 - changes: - - message: Multiple people using one shuttle console will no longer cause the shuttle - to slow down. - type: Fix - id: 7621 - time: '2024-11-19T02:59:42.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32381 - author: ScarKy0 changes: - message: Secret doors no longer tell you if they're welded shut on examine. @@ -3898,3 +3890,13 @@ id: 8120 time: '2025-03-31T22:32:31.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/36201 +- author: Tayrtahn + changes: + - message: Electric grills no longer appear powered when cycled while disconnected + from power. + type: Fix + - message: Interactions with electric grills are now predicted. + type: Tweak + id: 8121 + time: '2025-04-01T16:43:19.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36241 From 4cf14211f9c6caa38b943acedcb98e12e1f2a5eb Mon Sep 17 00:00:00 2001 From: Radezolid Date: Tue, 1 Apr 2025 18:55:43 -0300 Subject: [PATCH 35/45] Move medical locker fills to entityTables (#36249) * Added tables + moved things to EntityTableContainerFill * YAML convention --- .../Catalog/Fills/Lockers/medical.yml | 233 +++++++++--------- 1 file changed, 115 insertions(+), 118 deletions(-) diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/medical.yml b/Resources/Prototypes/Catalog/Fills/Lockers/medical.yml index 65c8d5ccea..7d9fe7fb59 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/medical.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/medical.yml @@ -1,74 +1,79 @@ -- type: entity - id: LockerMedicineFilled - suffix: Filled - parent: LockerMedicine - components: - - type: StorageFill - contents: - - id: BoxSyringe - - id: ChemistryBottleEpinephrine - amount: 1 - - id: Brutepack - amount: 2 - - id: Ointment - amount: 2 - - id: Bloodpack - amount: 2 - - id: Gauze +- type: entityTable + id: LockerFillMedicine + table: !type:AllSelector + children: + - id: BoxSyringe + - id: ChemistryBottleEpinephrine + - id: Brutepack + amount: !type:ConstantNumberSelector + value: 2 + - id: Ointment + amount: !type:ConstantNumberSelector + value: 2 + - id: Bloodpack + amount: !type:ConstantNumberSelector + value: 2 + - id: Gauze - type: entity + parent: LockerMedicine + id: LockerMedicineFilled + suffix: Filled + components: + - type: EntityTableContainerFill + containers: + entity_storage: !type:NestedSelector + tableId: LockerFillMedicine + +- type: entity + parent: LockerWallMedical id: LockerWallMedicalFilled name: medicine wall locker suffix: Filled - parent: LockerWallMedical components: - - type: StorageFill - contents: - - id: BoxSyringe - - id: ChemistryBottleEpinephrine - amount: 1 - - id: Brutepack - amount: 2 - - id: Ointment - amount: 2 - - id: Bloodpack - amount: 2 - - id: Gauze + - type: EntityTableContainerFill + containers: + entity_storage: !type:NestedSelector + tableId: LockerFillMedicine +- type: entityTable + id: LockerFillMedicalDoctor + table: !type:AllSelector + children: + - id: HandheldHealthAnalyzer + prob: 0.6 + - id: ClothingHeadMirror + prob: 0.1 + - id: ClothingHandsGlovesLatex + - id: ClothingHeadsetMedical + - id: ClothingEyesHudMedical + - !type:GroupSelector + children: + - id: ClothingHeadHatSurgcapGreen + weight: 0.1 + - id: ClothingHeadHatSurgcapPurple + weight: 0.05 + - id: ClothingHeadHatSurgcapBlue + weight: 0.90 + - !type:GroupSelector + children: + - id: UniformScrubsColorBlue + weight: 0.5 + - id: UniformScrubsColorGreen + weight: 0.1 + - id: UniformScrubsColorPurple + weight: 0.05 + - id: ClothingMaskSterile - type: entity + parent: LockerMedical id: LockerMedicalFilled suffix: Filled - parent: LockerMedical components: - - type: StorageFill - contents: - - id: HandheldHealthAnalyzer - prob: 0.6 - - id: ClothingHeadMirror - prob: 0.1 - - id: ClothingHandsGlovesLatex - - id: ClothingHeadsetMedical - - id: ClothingEyesHudMedical - - id: ClothingHeadHatSurgcapGreen - prob: 0.1 - orGroup: Surgcaps - - id: ClothingHeadHatSurgcapPurple - prob: 0.05 - orGroup: Surgcaps - - id: ClothingHeadHatSurgcapBlue - prob: 0.90 - orGroup: Surgcaps - - id: UniformScrubsColorBlue - prob: 0.5 - orGroup: Surgshrubs - - id: UniformScrubsColorGreen - prob: 0.1 - orGroup: Surgshrubs - - id: UniformScrubsColorPurple - prob: 0.05 - orGroup: Surgshrubs - - id: ClothingMaskSterile + - type: EntityTableContainerFill + containers: + entity_storage: !type:NestedSelector + tableId: LockerFillMedicalDoctor - type: entity parent: LockerWallMedical @@ -76,72 +81,64 @@ name: medical doctor's wall locker suffix: Filled components: - - type: StorageFill - contents: - - id: HandheldHealthAnalyzer - prob: 0.6 - - id: ClothingHandsGlovesLatex - - id: ClothingHeadsetMedical - - id: ClothingEyesHudMedical - - id: ClothingHeadHatSurgcapGreen - prob: 0.1 - orGroup: Surgcaps - - id: ClothingHeadHatSurgcapPurple - prob: 0.05 - orGroup: Surgcaps - - id: ClothingHeadHatSurgcapBlue - prob: 0.90 - orGroup: Surgcaps - - id: UniformScrubsColorBlue - prob: 0.5 - orGroup: Surgshrubs - - id: UniformScrubsColorGreen - prob: 0.1 - orGroup: Surgshrubs - - id: UniformScrubsColorPurple - prob: 0.05 - orGroup: Surgshrubs - - id: ClothingMaskSterile + - type: EntityTableContainerFill + containers: + entity_storage: !type:NestedSelector + tableId: LockerFillMedicalDoctor + +- type: entityTable + id: LockerFillChemistry + table: !type:AllSelector + children: + - id: BoxSyringe + - id: BoxBeaker + - id: BoxBeaker + prob: 0.3 + - id: BoxPillCanister + - id: BoxBottle + - id: BoxVial + - id: PlasmaChemistryVial + - id: ChemBag + - id: ClothingHandsGlovesLatex + - id: ClothingHeadsetMedical + - id: ClothingMaskSterile + - id: HandLabeler + prob: 0.5 - type: entity + parent: LockerChemistry id: LockerChemistryFilled suffix: Filled - parent: LockerChemistry components: - - type: StorageFill - contents: - - id: BoxSyringe - - id: BoxBeaker - - id: BoxBeaker - prob: 0.3 - - id: BoxPillCanister - - id: BoxBottle - - id: BoxVial - - id: PlasmaChemistryVial - - id: ChemBag - - id: ClothingHandsGlovesLatex - - id: ClothingHeadsetMedical - - id: ClothingMaskSterile - - id: HandLabeler - prob: 0.5 + - type: EntityTableContainerFill + containers: + entity_storage: !type:NestedSelector + tableId: LockerFillChemistry + +- type: entityTable + id: LockerFillParamedic + table: !type:AllSelector + children: + - id: ClothingOuterHardsuitVoidParamed + - id: ClothingOuterCoatParamedicWB + - id: ClothingHeadHatParamedicsoft + - id: ClothingOuterWinterPara + - id: ClothingUniformJumpsuitParamedic + - id: ClothingUniformJumpskirtParamedic + - id: ClothingEyesHudMedical + - id: ClothingHandsGlovesLatex + - id: ClothingHeadsetMedical + - id: ClothingMaskSterile + - id: HandheldGPSBasic + - id: MedkitFilled + prob: 0.3 - type: entity + parent: LockerParamedic id: LockerParamedicFilled suffix: Filled - parent: LockerParamedic components: - - type: StorageFill - contents: - - id: ClothingOuterHardsuitVoidParamed - - id: ClothingOuterCoatParamedicWB - - id: ClothingHeadHatParamedicsoft - - id: ClothingOuterWinterPara - - id: ClothingUniformJumpsuitParamedic - - id: ClothingUniformJumpskirtParamedic - - id: ClothingEyesHudMedical - - id: ClothingHandsGlovesLatex - - id: ClothingHeadsetMedical - - id: ClothingMaskSterile - - id: HandheldGPSBasic - - id: MedkitFilled - prob: 0.3 + - type: EntityTableContainerFill + containers: + entity_storage: !type:NestedSelector + tableId: LockerFillParamedic From 3192a7fde52c435f443cc6c49426d96c39c59f0a Mon Sep 17 00:00:00 2001 From: J Date: Tue, 1 Apr 2025 21:56:37 +0000 Subject: [PATCH 36/45] Rotation warnings cleanup (#36197) * Rotation warnings cleanup * Naming convention fix * Adding component that we already have --- Content.Client/Rotation/RotationVisualizerSystem.cs | 2 +- Content.Server/Rotatable/RotatableSystem.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Content.Client/Rotation/RotationVisualizerSystem.cs b/Content.Client/Rotation/RotationVisualizerSystem.cs index 6d3be4d1c0..8dbcf97320 100644 --- a/Content.Client/Rotation/RotationVisualizerSystem.cs +++ b/Content.Client/Rotation/RotationVisualizerSystem.cs @@ -52,7 +52,7 @@ public sealed class RotationVisualizerSystem : SharedRotationVisualsSystem // Stop the current rotate animation and then start a new one if (_animation.HasRunningAnimation(animationComp, animationKey)) { - _animation.Stop(animationComp, animationKey); + _animation.Stop((uid, animationComp), animationKey); } var animation = new Animation diff --git a/Content.Server/Rotatable/RotatableSystem.cs b/Content.Server/Rotatable/RotatableSystem.cs index 63b5e44c3d..85681535ca 100644 --- a/Content.Server/Rotatable/RotatableSystem.cs +++ b/Content.Server/Rotatable/RotatableSystem.cs @@ -21,6 +21,7 @@ namespace Content.Server.Rotatable [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; public override void Initialize() { @@ -112,7 +113,7 @@ namespace Content.Server.Rotatable var entity = EntityManager.SpawnEntity(component.MirrorEntity, oldTransform.Coordinates); var newTransform = EntityManager.GetComponent(entity); newTransform.LocalRotation = oldTransform.LocalRotation; - newTransform.Anchored = false; + _transform.Unanchor(entity, newTransform); EntityManager.DeleteEntity(uid); } From 899d318f012c4b67c4123676c2b321bbaa48b1ce Mon Sep 17 00:00:00 2001 From: MisterImp <101299120+MisterImp@users.noreply.github.com> Date: Tue, 1 Apr 2025 19:26:53 -0400 Subject: [PATCH 37/45] New food recipe: World Peazza (#35191) * added world peazza * fixed a comma in the pizza sprite json * changed attribution comment on ArtisticRoomba's suggestion * restored accidentally deleted line, thanks Tayrtahn --- .../Random/Food_Drinks/food_single.yml | 1 + .../Objects/Consumable/Food/Baked/pizza.yml | 52 ++++++++++++++++++ .../Consumable/Food/Containers/box.yml | 3 + .../Recipes/Cooking/meal_recipes.yml | 9 +++ .../Consumable/Food/Baked/pizza.rsi/meta.json | 8 ++- .../Food/Baked/pizza.rsi/worldpeas-slice.png | Bin 0 -> 284 bytes .../Food/Baked/pizza.rsi/worldpeas.png | Bin 0 -> 422 bytes 7 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 Resources/Textures/Objects/Consumable/Food/Baked/pizza.rsi/worldpeas-slice.png create mode 100644 Resources/Textures/Objects/Consumable/Food/Baked/pizza.rsi/worldpeas.png diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_single.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_single.yml index 4b7805c3d3..fda7b85b75 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_single.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_single.yml @@ -67,4 +67,5 @@ - FoodBurgerCrazy - FoodPizzaArnoldSlice - FoodPizzaUraniumSlice + - FoodPizzaWorldpeasSlice rareChance: 0.05 diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml index 8f476f9263..9fe96a18a1 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pizza.yml @@ -681,3 +681,55 @@ Quantity: 0.8 - ReagentId: Fiber Quantity: 1.5 + +- type: entity + name: world peazza + parent: FoodPizzaBase + id: FoodPizzaWorldpeas + description: Modern diplomacy in the shape of a disc. + components: + - type: FlavorProfile + flavors: + - bread + - numbingtranquility + - type: Sprite + layers: + - state: worldpeas + - type: SliceableFood + slice: FoodPizzaWorldpeasSlice + - type: SolutionContainerManager + solutions: + food: + maxVol: 45 + reagents: + - ReagentId: Nutriment + Quantity: 20 + - ReagentId: Happiness + Quantity: 12 + - ReagentId: Pax + Quantity: 8 + +- type: entity + name: slice of world peazza + parent: FoodPizzaSliceBase + id: FoodPizzaWorldpeasSlice + description: Dividing the world up is a small price to pay for harmony. + components: + - type: FlavorProfile + flavors: + - bread + - numbingtranquility + - type: Sprite + layers: + - state: worldpeas-slice + - type: SolutionContainerManager + solutions: + food: + maxVol: 10 + reagents: + - ReagentId: Nutriment + Quantity: 3.5 + - ReagentId: Happiness + Quantity: 2 + - ReagentId: Pax + Quantity: 1.5 diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/box.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/box.yml index fe690d8bd1..7dd402ae6d 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/box.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/box.yml @@ -273,6 +273,9 @@ - id: FoodPizzaCotton prob: 0.10 orGroup: Pizza + - id: FoodPizzaWorldpeas + prob: 0.05 + orGroup: Pizza - id: KnifePlastic - type: entity diff --git a/Resources/Prototypes/Recipes/Cooking/meal_recipes.yml b/Resources/Prototypes/Recipes/Cooking/meal_recipes.yml index 2eeb3c392b..28a47fdc21 100644 --- a/Resources/Prototypes/Recipes/Cooking/meal_recipes.yml +++ b/Resources/Prototypes/Recipes/Cooking/meal_recipes.yml @@ -718,6 +718,15 @@ FoodDoughCottonFlat: 1 CottonBol: 4 +- type: microwaveMealRecipe + id: RecipeWorldpeasPizza + name: world peazza recipe + result: FoodPizzaWorldpeas + time: 30 + solids: + FoodDoughFlat: 1 + FoodWorldPeas: 3 + #Italian - type: microwaveMealRecipe id: RecipeBoiledSpaghetti diff --git a/Resources/Textures/Objects/Consumable/Food/Baked/pizza.rsi/meta.json b/Resources/Textures/Objects/Consumable/Food/Baked/pizza.rsi/meta.json index 607a9cf8f3..1d7e8a01a5 100644 --- a/Resources/Textures/Objects/Consumable/Food/Baked/pizza.rsi/meta.json +++ b/Resources/Textures/Objects/Consumable/Food/Baked/pizza.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation and modified by Swept at https://github.com/tgstation/tgstation/commit/40d75cc340c63582fb66ce15bf75a36115f6bdaa, Spicy Rock Pizza modified from margherita pizza by mkanke, cotton made by mlexf (discord 1143460554963427380)", + "copyright": "Taken from tgstation and modified by Swept at https://github.com/tgstation/tgstation/commit/40d75cc340c63582fb66ce15bf75a36115f6bdaa, Spicy Rock Pizza modified from margherita pizza by mkanke, cotton made by mlexf (discord 1143460554963427380), world peazza modified from margherita by MisterImp (GitHub)", "size": { "x": 32, "y": 32 @@ -149,6 +149,12 @@ }, { "name": "uranium-slice" + }, + { + "name": "worldpeas" + }, + { + "name": "worldpeas-slice" } ] } diff --git a/Resources/Textures/Objects/Consumable/Food/Baked/pizza.rsi/worldpeas-slice.png b/Resources/Textures/Objects/Consumable/Food/Baked/pizza.rsi/worldpeas-slice.png new file mode 100644 index 0000000000000000000000000000000000000000..52216714bfcb7a1a950218af58d1236796fd659f GIT binary patch literal 284 zcmV+%0ptFOP)FswUW2#i7EAV*Tgau7H@%@FPYn86?nvJAwB zS&VEs$YKya*sDXCuvCq*R(cz77#I8N(Fwy}{ zEJk-kojt?<2P>Wb|G!-IzrQjUj6n{fN?a4`2#_y97-TUBuPrg9kLB1LLDL||2SDWl iRZ{pU7zLwXFaQAFfZsUr|2|#-0000d literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Food/Baked/pizza.rsi/worldpeas.png b/Resources/Textures/Objects/Consumable/Food/Baked/pizza.rsi/worldpeas.png new file mode 100644 index 0000000000000000000000000000000000000000..d521aa5c49866a8f2586a8fdeec5dc394ad0fad5 GIT binary patch literal 422 zcmV;X0a^ZuP)E!2b>4;qmt%A8XPNf6w%MMJgk! zGCH=YoQkU1?>wRwJ$;9ZY?BsIUC%cilyuAvP^qL%gS z-`+iwtLw*f8&Z&ilgDwaO*9(c82TYfb2~j~YiSNbvvd;-@ciM1c%9y{+@ zk6!BZWSqB+c5^{qn@fpEsDiD%8HtZ;a(^=MjX1-*)HKu{^!7ZphsU7s1+DB|dPnXt QjsO4v07*qoM6N<$f Date: Tue, 1 Apr 2025 23:28:01 +0000 Subject: [PATCH 38/45] 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 45ca1f71b9..3e4a0be541 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: ScarKy0 - changes: - - message: Secret doors no longer tell you if they're welded shut on examine. - type: Tweak - id: 7622 - time: '2024-11-19T05:07:02.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33365 - author: ArZarLordOfMango changes: - message: Most toggleable clothing must now be equipped to toggle their actions. @@ -3900,3 +3893,10 @@ id: 8121 time: '2025-04-01T16:43:19.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/36241 +- author: MisterImp + changes: + - message: A new recipe has been added for pizza made with world peas, world peazza. + type: Add + id: 8122 + time: '2025-04-01T23:26:53.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/35191 From 593a8fe86993cfd9e2f07bd4593e178374a62f55 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Tue, 1 Apr 2025 23:11:15 -0400 Subject: [PATCH 39/45] Fix KeyNotFoundException that sometimes happens on server shutdown (#36221) --- Content.Server/Mind/MindSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/Mind/MindSystem.cs b/Content.Server/Mind/MindSystem.cs index 2447d88641..1b55a533e3 100644 --- a/Content.Server/Mind/MindSystem.cs +++ b/Content.Server/Mind/MindSystem.cs @@ -85,11 +85,11 @@ public sealed class MindSystem : SharedMindSystem { if (base.TryGetMind(user, out mindId, out mind)) { - DebugTools.Assert(_players.GetPlayerData(user).ContentData() is not { } data || data.Mind == mindId); + DebugTools.Assert(!_players.TryGetPlayerData(user, out var playerData) || playerData.ContentData() is not { } data || data.Mind == mindId); return true; } - DebugTools.Assert(_players.GetPlayerData(user).ContentData()?.Mind == null); + DebugTools.Assert(!_players.TryGetPlayerData(user, out var pData) || pData.ContentData()?.Mind == null); return false; } From 93df56ac164a5e21c1464e244329888361135e04 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Wed, 2 Apr 2025 01:40:49 -0400 Subject: [PATCH 40/45] Fix "other player points at you" message formatting (#36253) Fix "other player points at you" message's Fluent functions --- .../Locale/en-US/entity-systems/pointing/pointing-system.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Locale/en-US/entity-systems/pointing/pointing-system.ftl b/Resources/Locale/en-US/entity-systems/pointing/pointing-system.ftl index 29f0fa27e2..be7b6196b2 100644 --- a/Resources/Locale/en-US/entity-systems/pointing/pointing-system.ftl +++ b/Resources/Locale/en-US/entity-systems/pointing/pointing-system.ftl @@ -5,6 +5,6 @@ pointing-system-point-at-self = You point at yourself. pointing-system-point-at-other = You point at {THE($other)}. pointing-system-point-at-self-others = {CAPITALIZE(THE($otherName))} points at {REFLEXIVE($other)}. pointing-system-point-at-other-others = {CAPITALIZE(THE($otherName))} points at {THE($other)}. -pointing-system-point-at-you-other = {$otherName} points at you. +pointing-system-point-at-you-other = {CAPITALIZE(THE($otherName))} points at you. pointing-system-point-at-tile = You point at the {$tileName}. pointing-system-other-point-at-tile = {CAPITALIZE(THE($otherName))} points at the {$tileName}. From 8548e062edd23a3dfd63de447a0e9ed8ebe4d937 Mon Sep 17 00:00:00 2001 From: Kirby <205904127+154942@users.noreply.github.com> Date: Wed, 2 Apr 2025 06:42:05 -0400 Subject: [PATCH 41/45] Light replacer description typo fix (#36256) Replacer description typo fix --- Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml b/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml index 646f6a6378..34dcd66b71 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml @@ -2,7 +2,7 @@ parent: BaseItem name: light replacer id: LightReplacer - description: An item which uses magnets to easily replace broken lights. Refill By adding more lights into the replacer. + description: An item which uses magnets to easily replace broken lights. Refill by adding more lights into the replacer. components: - type: Sprite sprite: Objects/Specific/Janitorial/light_replacer.rsi From 3f8deb7aa4cb2d0b838ef5550b3e4da124438417 Mon Sep 17 00:00:00 2001 From: Fildrance Date: Wed, 2 Apr 2025 19:11:34 +0300 Subject: [PATCH 42/45] fix: re-add missing RCD deconstruct action #36243 (#36255) Co-authored-by: pa.pecherskij --- Resources/Prototypes/RCD/rcd.yml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Resources/Prototypes/RCD/rcd.yml b/Resources/Prototypes/RCD/rcd.yml index 88a99451cf..f476f06dc4 100644 --- a/Resources/Prototypes/RCD/rcd.yml +++ b/Resources/Prototypes/RCD/rcd.yml @@ -2,25 +2,25 @@ - type: rcd id: Invalid # Hidden prototype - do not add to RCDs mode: Invalid - + - type: rcd id: Deconstruct name: rcd-component-deconstruct - category: Main + category: WallsAndFlooring sprite: /Textures/Interface/Radial/RCD/deconstruct.png mode: Deconstruct prototype: EffectRCDDeconstructPreview rotation: Camera - type: rcd - id: DeconstructLattice # Hidden prototype - do not add to RCDs + id: DeconstructLattice # Hidden prototype - do not add to RCDs name: rcd-component-deconstruct mode: Deconstruct cost: 2 delay: 0 rotation: Camera fx: EffectRCDConstruct0 - + - type: rcd id: DeconstructTile # Hidden prototype - do not add to RCDs name: rcd-component-deconstruct @@ -30,7 +30,7 @@ rotation: Camera fx: EffectRCDDeconstruct4 -# Flooring +# Flooring - type: rcd id: Plating name: rcd-component-plating @@ -44,7 +44,7 @@ rules: - CanBuildOnEmptyTile fx: EffectRCDConstruct1 - + - type: rcd id: FloorSteel name: rcd-component-floor-steel @@ -80,7 +80,7 @@ category: WallsAndFlooring sprite: /Textures/Interface/Radial/RCD/solid_wall.png mode: ConstructObject - prototype: WallSolid + prototype: WallSolid cost: 4 delay: 2 collisionMask: FullTileMask @@ -113,7 +113,7 @@ - IsWindow rotation: Fixed fx: EffectRCDConstruct2 - + - type: rcd id: WindowDirectional category: WindowsAndGrilles @@ -128,7 +128,7 @@ - IsWindow rotation: User fx: EffectRCDConstruct1 - + - type: rcd id: ReinforcedWindow category: WindowsAndGrilles @@ -142,7 +142,7 @@ - IsWindow rotation: User fx: EffectRCDConstruct3 - + - type: rcd id: WindowReinforcedDirectional category: WindowsAndGrilles @@ -170,7 +170,7 @@ collisionMask: FullTileMask rotation: Camera fx: EffectRCDConstruct4 - + - type: rcd id: AirlockGlass category: Airlocks @@ -182,7 +182,7 @@ collisionMask: FullTileMask rotation: Camera fx: EffectRCDConstruct4 - + - type: rcd id: Firelock category: Airlocks @@ -208,7 +208,7 @@ collisionBounds: "-0.23,-0.49,0.23,-0.36" rotation: User fx: EffectRCDConstruct1 - + - type: rcd id: BulbLight category: Lighting @@ -235,7 +235,7 @@ - MustBuildOnSubfloor rotation: Fixed fx: EffectRCDConstruct0 - + - type: rcd id: MVCable category: Electrical @@ -248,7 +248,7 @@ - MustBuildOnSubfloor rotation: Fixed fx: EffectRCDConstruct0 - + - type: rcd id: HVCable category: Electrical @@ -261,7 +261,7 @@ - MustBuildOnSubfloor rotation: Fixed fx: EffectRCDConstruct0 - + - type: rcd id: CableTerminal category: Electrical From e920558bb6953ad08df54e9b96ae2dcdb58b79dc Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 2 Apr 2025 16:12:41 +0000 Subject: [PATCH 43/45] 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 3e4a0be541..0676e638cc 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: ArZarLordOfMango - changes: - - message: Most toggleable clothing must now be equipped to toggle their actions. - type: Fix - id: 7623 - time: '2024-11-19T20:31:38.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32826 - author: Plykiya changes: - message: The SWAT crate from cargo now requires armory access to open. @@ -3900,3 +3893,10 @@ id: 8122 time: '2025-04-01T23:26:53.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/35191 +- author: Fildrance + changes: + - message: fixed missing deconstruct on RCD + type: Fix + id: 8123 + time: '2025-04-02T16:11:35.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36255 From b204fd9b0e2e0264e532d7d20887f32dd24d83fe Mon Sep 17 00:00:00 2001 From: qwerltaz Date: Wed, 2 Apr 2025 20:37:34 +0200 Subject: [PATCH 44/45] add: Dragon rift color changes based on charge (#36216) * use dragon rift sprite colours * Entity --- Content.Server/Dragon/DragonRiftSystem.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Content.Server/Dragon/DragonRiftSystem.cs b/Content.Server/Dragon/DragonRiftSystem.cs index 998834835e..9cab018fd7 100644 --- a/Content.Server/Dragon/DragonRiftSystem.cs +++ b/Content.Server/Dragon/DragonRiftSystem.cs @@ -13,6 +13,7 @@ using Robust.Shared.Serialization.Manager; using System.Numerics; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; +using Robust.Shared.GameStates; using Robust.Shared.Utility; namespace Content.Server.Dragon; @@ -33,11 +34,20 @@ public sealed class DragonRiftSystem : EntitySystem { base.Initialize(); + SubscribeLocalEvent(OnGetState); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnAnchorChange); SubscribeLocalEvent(OnShutdown); } + private void OnGetState(Entity ent, ref ComponentGetState args) + { + args.State = new DragonRiftComponentState + { + State = ent.Comp.State, + }; + } + public override void Update(float frameTime) { base.Update(frameTime); From b72a9170b0774116c931a39ecb1e6575ec27422b Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 2 Apr 2025 18:38:43 +0000 Subject: [PATCH 45/45] 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 0676e638cc..17ef3994ca 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Plykiya - changes: - - message: The SWAT crate from cargo now requires armory access to open. - type: Fix - id: 7624 - time: '2024-11-20T00:57:01.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33415 - author: SlamBamActionman changes: - message: It's no longer possible to drag an item out of a container's UI to drop @@ -3900,3 +3893,10 @@ id: 8123 time: '2025-04-02T16:11:35.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/36255 +- author: qwerltaz + changes: + - message: Dragon rifts now shine a different color depending on charge progress. + type: Add + id: 8124 + time: '2025-04-02T18:37:35.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36216