From ed7bea8e019e947b54499b2ba0964a5a622ad2e2 Mon Sep 17 00:00:00 2001 From: FungiFellow <151778459+FungiFellow@users.noreply.github.com> Date: Sat, 23 Aug 2025 18:15:28 -0400 Subject: [PATCH] Inflatable Module (#35100) Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Co-authored-by: PJB3005 --- .../Engineering/InflatablesDeflateTest.cs | 20 ++++ .../Interaction/InteractionTest.Constants.cs | 8 ++ .../Melee/Balloon/BalloonPopperSystem.cs | 1 + .../InflatableSafeDisassemblyComponent.cs | 14 +++ .../Systems/DisassembleOnAltVerbSystem.cs | 20 ++-- .../InflatableSafeDisassemblySystem.cs | 39 ++++++++ .../Melee/Balloon/BalloonPopperComponent.cs | 2 +- .../Locale/en-US/engineering/inflatables.ftl | 1 + .../Locale/en-US/robotics/borg_modules.ftl | 2 + .../Entities/Objects/Misc/inflatable_wall.yml | 2 + .../Specific/Robotics/borg_modules.yml | 30 ++++++ .../Objects/Tools/inflatable_wall.yml | 90 ++++++++++-------- .../Recipes/Lathes/Packs/robotics.yml | 1 + .../Recipes/Lathes/robot_modules.yml | 5 + Resources/Prototypes/borg_types.yml | 1 + Resources/Prototypes/tags.yml | 3 + .../actions_borg.rsi/inflatable-module.png | Bin 0 -> 5579 bytes .../Actions/actions_borg.rsi/meta.json | 5 +- .../borgmodule.rsi/icon-inflatable.png | Bin 0 -> 4594 bytes .../Robotics/borgmodule.rsi/meta.json | 6 +- 20 files changed, 199 insertions(+), 51 deletions(-) create mode 100644 Content.IntegrationTests/Tests/Engineering/InflatablesDeflateTest.cs create mode 100644 Content.Shared/Engineering/Components/InflatableSafeDisassemblyComponent.cs create mode 100644 Content.Shared/Engineering/Systems/InflatableSafeDisassemblySystem.cs rename {Content.Server => Content.Shared}/Weapons/Melee/Balloon/BalloonPopperComponent.cs (94%) create mode 100644 Resources/Locale/en-US/engineering/inflatables.ftl create mode 100644 Resources/Textures/Interface/Actions/actions_borg.rsi/inflatable-module.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-inflatable.png diff --git a/Content.IntegrationTests/Tests/Engineering/InflatablesDeflateTest.cs b/Content.IntegrationTests/Tests/Engineering/InflatablesDeflateTest.cs new file mode 100644 index 0000000000..a7203d9259 --- /dev/null +++ b/Content.IntegrationTests/Tests/Engineering/InflatablesDeflateTest.cs @@ -0,0 +1,20 @@ +using Content.IntegrationTests.Tests.Interaction; +using Content.Shared.Engineering.Systems; + +namespace Content.IntegrationTests.Tests.Engineering; + +[TestFixture] +[TestOf(typeof(InflatableSafeDisassemblySystem))] +public sealed class InflatablesDeflateTest : InteractionTest +{ + [Test] + public async Task Test() + { + await SpawnTarget(InflatableWall); + + await InteractUsing(Needle); + + AssertDeleted(); + await AssertEntityLookup(new EntitySpecifier(InflatableWallStack.Id, 1)); + } +} diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Constants.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Constants.cs index 5db5d91d0d..8917ba7ead 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Constants.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Constants.cs @@ -1,3 +1,6 @@ +using Content.Shared.Stacks; +using Robust.Shared.Prototypes; + namespace Content.IntegrationTests.Tests.Interaction; // This partial class contains various constant prototype IDs common to interaction tests. @@ -32,4 +35,9 @@ public abstract partial class InteractionTest protected const string Manipulator1 = "MicroManipulatorStockPart"; protected const string Battery1 = "PowerCellSmall"; protected const string Battery4 = "PowerCellHyper"; + + // Inflatables & Needle used to pop them + protected static readonly EntProtoId InflatableWall = "InflatableWall"; + protected static readonly EntProtoId Needle = "WeaponMeleeNeedle"; + protected static readonly ProtoId InflatableWallStack = "InflatableWall"; } diff --git a/Content.Server/Weapons/Melee/Balloon/BalloonPopperSystem.cs b/Content.Server/Weapons/Melee/Balloon/BalloonPopperSystem.cs index a8460a8c66..d4ab81ae10 100644 --- a/Content.Server/Weapons/Melee/Balloon/BalloonPopperSystem.cs +++ b/Content.Server/Weapons/Melee/Balloon/BalloonPopperSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Popups; using Content.Shared.Tag; using Content.Shared.Weapons.Melee.Events; using Content.Shared.Throwing; +using Content.Shared.Weapons.Melee.Balloon; using Robust.Shared.Audio.Systems; namespace Content.Server.Weapons.Melee.Balloon; diff --git a/Content.Shared/Engineering/Components/InflatableSafeDisassemblyComponent.cs b/Content.Shared/Engineering/Components/InflatableSafeDisassemblyComponent.cs new file mode 100644 index 0000000000..47591b6eb9 --- /dev/null +++ b/Content.Shared/Engineering/Components/InflatableSafeDisassemblyComponent.cs @@ -0,0 +1,14 @@ +using Content.Shared.Engineering.Systems; +using Content.Shared.Weapons.Melee.Balloon; + +namespace Content.Shared.Engineering.Components; + +/// +/// Implements logic to allow inflatable objects to be safely deflated by items. +/// +/// +/// The owning entity must have to implement the logic. +/// +/// +[RegisterComponent] +public sealed partial class InflatableSafeDisassemblyComponent : Component; diff --git a/Content.Shared/Engineering/Systems/DisassembleOnAltVerbSystem.cs b/Content.Shared/Engineering/Systems/DisassembleOnAltVerbSystem.cs index 150688d3d4..0c2e5398fc 100644 --- a/Content.Shared/Engineering/Systems/DisassembleOnAltVerbSystem.cs +++ b/Content.Shared/Engineering/Systems/DisassembleOnAltVerbSystem.cs @@ -19,14 +19,12 @@ public sealed partial class DisassembleOnAltVerbSystem : EntitySystem SubscribeLocalEvent>(AddDisassembleVerb); SubscribeLocalEvent(OnDisassembleDoAfter); } - private void AddDisassembleVerb(Entity entity, ref GetVerbsEvent args) - { - if (!args.CanInteract || !args.CanAccess || args.Hands == null) - return; + public void StartDisassembly(Entity entity, EntityUid user) + { // Doafter setup var doAfterArgs = new DoAfterArgs(EntityManager, - args.User, + user, entity.Comp.DisassembleTime, new DisassembleDoAfterEvent(), entity, @@ -35,12 +33,22 @@ public sealed partial class DisassembleOnAltVerbSystem : EntitySystem BreakOnMove = true, }; + _doAfter.TryStartDoAfter(doAfterArgs); + } + + private void AddDisassembleVerb(Entity entity, ref GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess || args.Hands == null) + return; + + var user = args.User; + // Actual verb stuff AlternativeVerb verb = new() { Act = () => { - _doAfter.TryStartDoAfter(doAfterArgs); + StartDisassembly(entity, user); }, Text = Loc.GetString("disassemble-system-verb-disassemble"), Priority = 2 diff --git a/Content.Shared/Engineering/Systems/InflatableSafeDisassemblySystem.cs b/Content.Shared/Engineering/Systems/InflatableSafeDisassemblySystem.cs new file mode 100644 index 0000000000..7852036330 --- /dev/null +++ b/Content.Shared/Engineering/Systems/InflatableSafeDisassemblySystem.cs @@ -0,0 +1,39 @@ +using Content.Shared.Engineering.Components; +using Content.Shared.Interaction; +using Content.Shared.Popups; +using Content.Shared.Weapons.Melee.Balloon; + +namespace Content.Shared.Engineering.Systems; + +/// +/// Implements +/// +public sealed class InflatableSafeDisassemblySystem : EntitySystem +{ + [Dependency] private readonly DisassembleOnAltVerbSystem _disassembleOnAltVerbSystem = null!; + [Dependency] private readonly SharedPopupSystem _popupSystem = null!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(InteractHandler); + } + + private void InteractHandler(Entity ent, ref InteractUsingEvent args) + { + if (args.Handled) + return; + + if (!HasComp(args.Used)) + return; + + _popupSystem.PopupPredicted( + Loc.GetString("inflatable-safe-disassembly", ("item", args.Used), ("target", ent.Owner)), + ent, + args.User); + + _disassembleOnAltVerbSystem.StartDisassembly((ent, Comp(ent)), args.User); + args.Handled = true; + } +} diff --git a/Content.Server/Weapons/Melee/Balloon/BalloonPopperComponent.cs b/Content.Shared/Weapons/Melee/Balloon/BalloonPopperComponent.cs similarity index 94% rename from Content.Server/Weapons/Melee/Balloon/BalloonPopperComponent.cs rename to Content.Shared/Weapons/Melee/Balloon/BalloonPopperComponent.cs index e7f318d61c..7e90b2b4ea 100644 --- a/Content.Server/Weapons/Melee/Balloon/BalloonPopperComponent.cs +++ b/Content.Shared/Weapons/Melee/Balloon/BalloonPopperComponent.cs @@ -2,7 +2,7 @@ using Robust.Shared.Audio; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -namespace Content.Server.Weapons.Melee.Balloon; +namespace Content.Shared.Weapons.Melee.Balloon; /// /// This is used for weapons that pop balloons on attack. diff --git a/Resources/Locale/en-US/engineering/inflatables.ftl b/Resources/Locale/en-US/engineering/inflatables.ftl new file mode 100644 index 0000000000..0c4a2d44c0 --- /dev/null +++ b/Resources/Locale/en-US/engineering/inflatables.ftl @@ -0,0 +1 @@ +inflatable-safe-disassembly = You expertly use { THE($item) } to open the valve on { THE($target) }, and start deflating { OBJECT($target) } without causing damage. diff --git a/Resources/Locale/en-US/robotics/borg_modules.ftl b/Resources/Locale/en-US/robotics/borg_modules.ftl index b6c55447e7..ba5ee602a5 100644 --- a/Resources/Locale/en-US/robotics/borg_modules.ftl +++ b/Resources/Locale/en-US/robotics/borg_modules.ftl @@ -10,3 +10,5 @@ borg-slot-documents-empty = Books and papers borg-slot-soap-empty = Soap borg-slot-instruments-empty = Instruments borg-slot-beakers-empty = Beakers +borg-slot-inflatable-door-empty = Inflatable Door +borg-slot-inflatable-wall-empty = Inflatable Wall diff --git a/Resources/Prototypes/Entities/Objects/Misc/inflatable_wall.yml b/Resources/Prototypes/Entities/Objects/Misc/inflatable_wall.yml index 0641084847..577ab1dddd 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/inflatable_wall.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/inflatable_wall.yml @@ -33,6 +33,7 @@ - type: DisassembleOnAltVerb prototypeToSpawn: InflatableWallStack1 disassembleTime: 3 + - type: InflatableSafeDisassembly - type: Airtight - type: Transform anchored: true @@ -81,5 +82,6 @@ - type: DisassembleOnAltVerb prototypeToSpawn: InflatableDoorStack1 disassembleTime: 3 + - type: InflatableSafeDisassembly - type: Occluder enabled: false diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml index 2a971d71ea..675d9efa70 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml @@ -489,6 +489,36 @@ - type: BorgModuleIcon icon: { sprite: Interface/Actions/actions_borg.rsi, state: tool-module } +- type: entity + id: BorgModuleInflatable + parent: [ BaseBorgModule, BaseProviderBorgModule ] + name: inflatable cyborg module + components: + - type: Sprite + layers: + - state: generic + - state: icon-inflatable + - type: ItemBorgModule + hands: + - item: InflatableDoorStack + hand: + emptyRepresentative: InflatableDoorStack + emptyLabel: borg-slot-inflatable-door-empty + whitelist: + tags: + - Inflatable + - item: InflatableWallStack + hand: + emptyRepresentative: InflatableWallStack + emptyLabel: borg-slot-inflatable-wall-empty + whitelist: + tags: + - Inflatable + - item: BoxInflatable + - item: WeaponMeleeNeedle + - type: BorgModuleIcon + icon: { sprite: Interface/Actions/actions_borg.rsi, state: inflatable-module } + # cargo modules - type: entity id: BorgModuleAppraisal diff --git a/Resources/Prototypes/Entities/Objects/Tools/inflatable_wall.yml b/Resources/Prototypes/Entities/Objects/Tools/inflatable_wall.yml index f5e05b4e28..4f55d5c164 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/inflatable_wall.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/inflatable_wall.yml @@ -5,21 +5,24 @@ description: A folded membrane which rapidly expands into a large cubical shape on activation. suffix: Full components: - - type: Stack - stackType: InflatableWall - count: 10 - - type: Sprite - sprite: Objects/Misc/inflatable_wall.rsi - state: item_wall - - type: Item - sprite: Objects/Misc/inflatable_wall.rsi - size: Small - - type: SpawnAfterInteract - prototype: InflatableWall - doAfter: 1 - removeOnInteract: true - - type: Clickable - - type: PhysicalComposition + - type: Stack + stackType: InflatableWall + count: 10 + - type: Sprite + sprite: Objects/Misc/inflatable_wall.rsi + state: item_wall + - type: Item + sprite: Objects/Misc/inflatable_wall.rsi + size: Small + - type: SpawnAfterInteract + prototype: InflatableWall + doAfter: 1 + removeOnInteract: true + - type: Clickable + - type: PhysicalComposition + - type: Tag + tags: + - Inflatable # TODO: Add stack sprites + visuals. - type: entity @@ -29,21 +32,24 @@ description: A folded membrane which rapidly expands into a large cubical shape on activation. suffix: Full components: - - type: Stack - stackType: InflatableDoor - count: 4 - - type: Sprite - sprite: Objects/Misc/inflatable_door.rsi - state: item_door - - type: Item - sprite: Objects/Misc/inflatable_door.rsi - size: Small - - type: SpawnAfterInteract - prototype: InflatableDoor - doAfter: 1 - removeOnInteract: true - - type: Clickable - - type: PhysicalComposition + - type: Stack + stackType: InflatableDoor + count: 4 + - type: Sprite + sprite: Objects/Misc/inflatable_door.rsi + state: item_door + - type: Item + sprite: Objects/Misc/inflatable_door.rsi + size: Small + - type: SpawnAfterInteract + prototype: InflatableDoor + doAfter: 1 + removeOnInteract: true + - type: Clickable + - type: PhysicalComposition + - type: Tag + tags: + - Inflatable # TODO: Add stack sprites + visuals. - type: entity @@ -51,27 +57,27 @@ id: InflatableWallStack5 suffix: 5 components: - - type: Sprite - state: item_wall - - type: Stack - count: 5 + - type: Sprite + state: item_wall + - type: Stack + count: 5 - type: entity parent: InflatableWallStack id: InflatableWallStack1 suffix: 1 components: - - type: Sprite - state: item_wall - - type: Stack - count: 1 + - type: Sprite + state: item_wall + - type: Stack + count: 1 - type: entity parent: InflatableDoorStack id: InflatableDoorStack1 suffix: 1 components: - - type: Sprite - state: item_door - - type: Stack - count: 1 + - type: Sprite + state: item_door + - type: Stack + count: 1 diff --git a/Resources/Prototypes/Recipes/Lathes/Packs/robotics.yml b/Resources/Prototypes/Recipes/Lathes/Packs/robotics.yml index f22cde9c52..2db59977be 100644 --- a/Resources/Prototypes/Recipes/Lathes/Packs/robotics.yml +++ b/Resources/Prototypes/Recipes/Lathes/Packs/robotics.yml @@ -14,6 +14,7 @@ - BorgModuleTool - BorgModuleCable - BorgModuleFireExtinguisher + - BorgModuleInflatable - type: latheRecipePack id: BorgLimbsStatic diff --git a/Resources/Prototypes/Recipes/Lathes/robot_modules.yml b/Resources/Prototypes/Recipes/Lathes/robot_modules.yml index 9210529a1d..9465d7b87a 100644 --- a/Resources/Prototypes/Recipes/Lathes/robot_modules.yml +++ b/Resources/Prototypes/Recipes/Lathes/robot_modules.yml @@ -37,6 +37,11 @@ id: BorgModuleFireExtinguisher result: BorgModuleFireExtinguisher +- type: latheRecipe + parent: BaseBorgModuleRecipe + id: BorgModuleInflatable + result: BorgModuleInflatable + # Cargo Modules - type: latheRecipe diff --git a/Resources/Prototypes/borg_types.yml b/Resources/Prototypes/borg_types.yml index 1c49713e26..1d88af8455 100644 --- a/Resources/Prototypes/borg_types.yml +++ b/Resources/Prototypes/borg_types.yml @@ -14,6 +14,7 @@ defaultModules: - BorgModuleTool + - BorgModuleInflatable - BorgModuleArtifact - BorgModuleAnomaly radioChannels: diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 95b261466f..a9b927cdcf 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -801,6 +801,9 @@ - type: Tag id: Ingot +- type: Tag + id: Inflatable + - type: Tag id: InstantDoAfters diff --git a/Resources/Textures/Interface/Actions/actions_borg.rsi/inflatable-module.png b/Resources/Textures/Interface/Actions/actions_borg.rsi/inflatable-module.png new file mode 100644 index 0000000000000000000000000000000000000000..15e1c86629fb981cbcf5dc39ddfed19bf2efcf6a GIT binary patch literal 5579 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