From be2eeb3cb14795cf24862263ff868a53a14ffe71 Mon Sep 17 00:00:00 2001 From: Winkarst-cpu <74284083+Winkarst-cpu@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:40:21 +0300 Subject: [PATCH 001/108] Cleanup: Un-hardcode reagents standout (#39752) --- .../Chemistry/Reagent/ReagentPrototype.cs | 6 +++++ Content.Shared/Fluids/SharedPuddleSystem.cs | 27 ++++++++++++++----- Resources/Prototypes/Reagents/biological.yml | 2 ++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs index 1a3d17c340..7689a27cd0 100644 --- a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs +++ b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs @@ -63,6 +63,12 @@ namespace Content.Shared.Chemistry.Reagent [DataField] public bool Recognizable; + /// + /// Whether this reagent stands out (blood, slime). + /// + [DataField] + public bool Standsout; + [DataField] public ProtoId? Flavor; diff --git a/Content.Shared/Fluids/SharedPuddleSystem.cs b/Content.Shared/Fluids/SharedPuddleSystem.cs index a2f0764ce0..2d0fffa37b 100644 --- a/Content.Shared/Fluids/SharedPuddleSystem.cs +++ b/Content.Shared/Fluids/SharedPuddleSystem.cs @@ -22,11 +22,7 @@ public abstract partial class SharedPuddleSystem : EntitySystem [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - private static readonly ProtoId Blood = "Blood"; - private static readonly ProtoId Slime = "Slime"; - private static readonly ProtoId CopperBlood = "CopperBlood"; - - private static readonly string[] StandoutReagents = [Blood, Slime, CopperBlood]; + private string[] _standoutReagents = []; /// /// The lowest threshold to be considered for puddle sprite states as well as slipperiness of a puddle. @@ -48,9 +44,26 @@ public abstract partial class SharedPuddleSystem : EntitySystem SubscribeLocalEvent(HandlePuddleExamined); SubscribeLocalEvent(OnEntRemoved); + SubscribeLocalEvent(OnPrototypesReloaded); + + CacheStandsout(); InitializeSpillable(); } + private void OnPrototypesReloaded(PrototypesReloadedEventArgs ev) + { + if (ev.WasModified()) + CacheStandsout(); + } + + /// + /// Used to cache standout reagents for future use. + /// + private void CacheStandsout() + { + _standoutReagents = [.. _prototypeManager.EnumeratePrototypes().Where(x => x.Standsout).Select(x => x.ID)]; + } + protected virtual void OnSolutionUpdate(Entity entity, ref SolutionContainerChangedEvent args) { if (args.SolutionId != entity.Comp.SolutionName) @@ -158,10 +171,10 @@ public abstract partial class SharedPuddleSystem : EntitySystem // Kinda EH // Could potentially do alpha per-solution but future problem. - color = solution.GetColorWithout(_prototypeManager, StandoutReagents); + color = solution.GetColorWithout(_prototypeManager, _standoutReagents); color = color.WithAlpha(0.7f); - foreach (var standout in StandoutReagents) + foreach (var standout in _standoutReagents) { var quantity = solution.GetTotalPrototypeQuantity(standout); if (quantity <= FixedPoint2.Zero) diff --git a/Resources/Prototypes/Reagents/biological.yml b/Resources/Prototypes/Reagents/biological.yml index 1a4fc93426..d6063cd9a7 100644 --- a/Resources/Prototypes/Reagents/biological.yml +++ b/Resources/Prototypes/Reagents/biological.yml @@ -12,6 +12,7 @@ metamorphicFillBaseName: fill- metamorphicChangeColor: false recognizable: true + standsout: true physicalDesc: reagent-physical-desc-ferrous metabolisms: Drink: @@ -64,6 +65,7 @@ flavor: slimy color: "#2cf274" recognizable: true + standsout: true physicalDesc: reagent-physical-desc-viscous viscosity: 0.25 tileReactions: From de240e1739b09940a318b53a1eb067383b4b83ce Mon Sep 17 00:00:00 2001 From: Samuka-C <47865393+Samuka-C@users.noreply.github.com> Date: Tue, 19 Aug 2025 13:23:47 -0300 Subject: [PATCH 002/108] Xenoborgs part 5 (#37068) Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Co-authored-by: Quantum-cross <7065792+Quantum-cross@users.noreply.github.com> Co-authored-by: pathetic meowmeow Co-authored-by: WarPigeon --- .../Audio/Voice/Xenoborg/xenoborg_scream.ogg | Bin 18684 -> 21785 bytes Resources/Locale/en-US/stack/stacks.ftl | 1 + Resources/Locale/en-US/tiles/tiles.ftl | 1 + .../Entities/Mobs/Player/mothershipcore.yml | 48 ++++- .../Circuitboards/Machine/production.yml | 14 ++ .../Devices/Circuitboards/computer.yml | 22 ++ .../Devices/Electronics/door_access.yml | 8 + .../Entities/Objects/Misc/tiles.yml | 14 ++ .../Structures/Doors/Airlocks/access.yml | 22 ++ .../Structures/Doors/Airlocks/airlocks.yml | 10 + .../Structures/Doors/Airlocks/shuttle.yml | 10 + .../Machines/Computers/computers.yml | 50 ++++- .../Machines/surveillance_camera_routers.yml | 16 ++ .../Entities/Structures/Power/apc.yml | 11 + .../Entities/Structures/Walls/walls.yml | 31 ++- .../Entities/Structures/Windows/xenoborg.yml | 14 ++ .../Prototypes/Stacks/floor_tile_stacks.yml | 6 + Resources/Prototypes/Tiles/floors.yml | 12 ++ .../Textures/Objects/Tiles/tile.rsi/meta.json | 3 + .../Objects/Tiles/tile.rsi/xenoborg-floor.png | Bin 0 -> 233 bytes .../shuttle_xenoborg.rsi/assembly.png | Bin 0 -> 352 bytes .../shuttle_xenoborg.rsi/bolted_unlit.png | Bin 0 -> 4972 bytes .../Standard/shuttle_xenoborg.rsi/closed.png | Bin 0 -> 629 bytes .../shuttle_xenoborg.rsi/closed_unlit.png | Bin 0 -> 5035 bytes .../Standard/shuttle_xenoborg.rsi/closing.png | Bin 0 -> 1771 bytes .../shuttle_xenoborg.rsi/closing_unlit.png | Bin 0 -> 6610 bytes .../shuttle_xenoborg.rsi/deny_unlit.png | Bin 0 -> 6524 bytes .../shuttle_xenoborg.rsi/emergency_unlit.png | Bin 0 -> 5301 bytes .../Standard/shuttle_xenoborg.rsi/meta.json | 146 +++++++++++++ .../Standard/shuttle_xenoborg.rsi/open.png | Bin 0 -> 428 bytes .../Standard/shuttle_xenoborg.rsi/opening.png | Bin 0 -> 1811 bytes .../shuttle_xenoborg.rsi/opening_unlit.png | Bin 0 -> 6610 bytes .../shuttle_xenoborg.rsi/panel_closing.png | Bin 0 -> 6400 bytes .../shuttle_xenoborg.rsi/panel_open.png | Bin 0 -> 5073 bytes .../shuttle_xenoborg.rsi/panel_opening.png | Bin 0 -> 6183 bytes .../Standard/shuttle_xenoborg.rsi/welded.png | Bin 0 -> 5638 bytes .../Standard/xenoborg.rsi/assembly.png | Bin 0 -> 401 bytes .../Standard/xenoborg.rsi/bolted_unlit.png | Bin 0 -> 259 bytes .../Airlocks/Standard/xenoborg.rsi/closed.png | Bin 0 -> 395 bytes .../Standard/xenoborg.rsi/closed_unlit.png | Bin 0 -> 259 bytes .../Standard/xenoborg.rsi/closing.png | Bin 0 -> 1541 bytes .../Standard/xenoborg.rsi/closing_unlit.png | Bin 0 -> 521 bytes .../Standard/xenoborg.rsi/deny_unlit.png | Bin 0 -> 496 bytes .../Standard/xenoborg.rsi/emergency_unlit.png | Bin 0 -> 407 bytes .../Airlocks/Standard/xenoborg.rsi/meta.json | 198 ++++++++++++++++++ .../Airlocks/Standard/xenoborg.rsi/open.png | Bin 0 -> 311 bytes .../Standard/xenoborg.rsi/opening.png | Bin 0 -> 1064 bytes .../Standard/xenoborg.rsi/opening_unlit.png | Bin 0 -> 521 bytes .../Standard/xenoborg.rsi/panel_closing.png | Bin 0 -> 494 bytes .../Standard/xenoborg.rsi/panel_open.png | Bin 0 -> 240 bytes .../Standard/xenoborg.rsi/panel_opening.png | Bin 0 -> 488 bytes .../Airlocks/Standard/xenoborg.rsi/sparks.png | Bin 0 -> 697 bytes .../Standard/xenoborg.rsi/sparks_broken.png | Bin 0 -> 315 bytes .../Standard/xenoborg.rsi/sparks_damaged.png | Bin 0 -> 184 bytes .../Standard/xenoborg.rsi/sparks_open.png | Bin 0 -> 281 bytes .../Airlocks/Standard/xenoborg.rsi/welded.png | Bin 0 -> 300 bytes .../Machines/computers.rsi/meta.json | 6 +- .../Machines/computers.rsi/xenorobot.png | Bin 0 -> 646 bytes .../Structures/Walls/xenoborg.rsi/full.png | Bin 0 -> 354 bytes .../Structures/Walls/xenoborg.rsi/meta.json | 46 ++++ .../Walls/xenoborg.rsi/xenoborg0.png | Bin 0 -> 471 bytes .../Walls/xenoborg.rsi/xenoborg1.png | Bin 0 -> 470 bytes .../Walls/xenoborg.rsi/xenoborg2.png | Bin 0 -> 471 bytes .../Walls/xenoborg.rsi/xenoborg3.png | Bin 0 -> 470 bytes .../Walls/xenoborg.rsi/xenoborg4.png | Bin 0 -> 471 bytes .../Walls/xenoborg.rsi/xenoborg5.png | Bin 0 -> 525 bytes .../Walls/xenoborg.rsi/xenoborg6.png | Bin 0 -> 471 bytes .../Walls/xenoborg.rsi/xenoborg7.png | Bin 0 -> 304 bytes .../Walls/xenoborg_diagonal.rsi/meta.json | 19 ++ .../Walls/xenoborg_diagonal.rsi/state0.png | Bin 0 -> 3706 bytes .../Walls/xenoborg_diagonal.rsi/state1.png | Bin 0 -> 3817 bytes .../Structures/Windows/xenoborg.rsi/full.png | Bin 0 -> 401 bytes .../Structures/Windows/xenoborg.rsi/meta.json | 46 ++++ .../Windows/xenoborg.rsi/xenoborg0.png | Bin 0 -> 497 bytes .../Windows/xenoborg.rsi/xenoborg1.png | Bin 0 -> 469 bytes .../Windows/xenoborg.rsi/xenoborg2.png | Bin 0 -> 497 bytes .../Windows/xenoborg.rsi/xenoborg3.png | Bin 0 -> 469 bytes .../Windows/xenoborg.rsi/xenoborg4.png | Bin 0 -> 462 bytes .../Windows/xenoborg.rsi/xenoborg5.png | Bin 0 -> 531 bytes .../Windows/xenoborg.rsi/xenoborg6.png | Bin 0 -> 462 bytes .../Windows/xenoborg.rsi/xenoborg7.png | Bin 0 -> 315 bytes Resources/Textures/Tiles/attributions.yml | 5 + Resources/Textures/Tiles/exoborg.png | Bin 0 -> 235 bytes 83 files changed, 753 insertions(+), 6 deletions(-) create mode 100644 Resources/Prototypes/Entities/Structures/Windows/xenoborg.yml create mode 100644 Resources/Textures/Objects/Tiles/tile.rsi/xenoborg-floor.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/assembly.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/bolted_unlit.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/closed.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/closed_unlit.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/closing.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/closing_unlit.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/deny_unlit.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/emergency_unlit.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/meta.json create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/open.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/opening.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/opening_unlit.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/panel_closing.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/panel_open.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/panel_opening.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/welded.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/assembly.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/bolted_unlit.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/closed.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/closed_unlit.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/closing.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/closing_unlit.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/deny_unlit.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/emergency_unlit.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/meta.json create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/open.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/opening.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/opening_unlit.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/panel_closing.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/panel_open.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/panel_opening.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/sparks.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/sparks_broken.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/sparks_damaged.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/sparks_open.png create mode 100644 Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/welded.png create mode 100644 Resources/Textures/Structures/Machines/computers.rsi/xenorobot.png create mode 100644 Resources/Textures/Structures/Walls/xenoborg.rsi/full.png create mode 100644 Resources/Textures/Structures/Walls/xenoborg.rsi/meta.json create mode 100644 Resources/Textures/Structures/Walls/xenoborg.rsi/xenoborg0.png create mode 100644 Resources/Textures/Structures/Walls/xenoborg.rsi/xenoborg1.png create mode 100644 Resources/Textures/Structures/Walls/xenoborg.rsi/xenoborg2.png create mode 100644 Resources/Textures/Structures/Walls/xenoborg.rsi/xenoborg3.png create mode 100644 Resources/Textures/Structures/Walls/xenoborg.rsi/xenoborg4.png create mode 100644 Resources/Textures/Structures/Walls/xenoborg.rsi/xenoborg5.png create mode 100644 Resources/Textures/Structures/Walls/xenoborg.rsi/xenoborg6.png create mode 100644 Resources/Textures/Structures/Walls/xenoborg.rsi/xenoborg7.png create mode 100644 Resources/Textures/Structures/Walls/xenoborg_diagonal.rsi/meta.json create mode 100644 Resources/Textures/Structures/Walls/xenoborg_diagonal.rsi/state0.png create mode 100644 Resources/Textures/Structures/Walls/xenoborg_diagonal.rsi/state1.png create mode 100644 Resources/Textures/Structures/Windows/xenoborg.rsi/full.png create mode 100644 Resources/Textures/Structures/Windows/xenoborg.rsi/meta.json create mode 100644 Resources/Textures/Structures/Windows/xenoborg.rsi/xenoborg0.png create mode 100644 Resources/Textures/Structures/Windows/xenoborg.rsi/xenoborg1.png create mode 100644 Resources/Textures/Structures/Windows/xenoborg.rsi/xenoborg2.png create mode 100644 Resources/Textures/Structures/Windows/xenoborg.rsi/xenoborg3.png create mode 100644 Resources/Textures/Structures/Windows/xenoborg.rsi/xenoborg4.png create mode 100644 Resources/Textures/Structures/Windows/xenoborg.rsi/xenoborg5.png create mode 100644 Resources/Textures/Structures/Windows/xenoborg.rsi/xenoborg6.png create mode 100644 Resources/Textures/Structures/Windows/xenoborg.rsi/xenoborg7.png create mode 100644 Resources/Textures/Tiles/exoborg.png diff --git a/Resources/Audio/Voice/Xenoborg/xenoborg_scream.ogg b/Resources/Audio/Voice/Xenoborg/xenoborg_scream.ogg index bc83ec46ff4aa5e22f427a1f517f8b7d5231a20a..9ac12637fb38372c83b86feda7fdf84028d3c62f 100644 GIT binary patch literal 21785 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 diff --git a/Resources/Locale/en-US/stack/stacks.ftl b/Resources/Locale/en-US/stack/stacks.ftl index d83825b614..818ac954c5 100644 --- a/Resources/Locale/en-US/stack/stacks.ftl +++ b/Resources/Locale/en-US/stack/stacks.ftl @@ -234,6 +234,7 @@ stack-asteroid-astro-sand-floor = asteroid astro-sand floor stack-xeno-floor = xeno floor stack-xeno-steel = xeno steel tile stack-xeno-steel-corner = xeno steel corner tile +stack-xenoborg = xenoborg tile stack-xeno-maint = xeno techmaint stack-dark-squiggly = dark steel squiggly tile stack-white-marble-floor = white marble floor diff --git a/Resources/Locale/en-US/tiles/tiles.ftl b/Resources/Locale/en-US/tiles/tiles.ftl index 448e8dbc5f..6295712722 100644 --- a/Resources/Locale/en-US/tiles/tiles.ftl +++ b/Resources/Locale/en-US/tiles/tiles.ftl @@ -139,6 +139,7 @@ tiles-xeno-floor = xeno floor tiles-xeno-steel = xeno steel tile tiles-xeno-steel-corner = xeno steel corner tile tiles-xeno-maint = xeno techmaint +tiles-xenoborg-floor = xenoborg tile tiles-dark-squiggly = dark steel squiggly tile tiles-white-marble = white marble tile tiles-dark-marble = dark marble tile diff --git a/Resources/Prototypes/Entities/Mobs/Player/mothershipcore.yml b/Resources/Prototypes/Entities/Mobs/Player/mothershipcore.yml index 4d729889c2..3535a55e7f 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/mothershipcore.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/mothershipcore.yml @@ -90,6 +90,17 @@ maxIntensity: 100 intensitySlope: 2 totalIntensity: 200 + - type: SurveillanceCameraSpeaker + - type: SurveillanceCameraMonitor + - type: RoboticsConsole + allowBorgControl: false + radioChannel: Xenoborg + - type: DeviceNetwork + deviceNetId: Wireless + receiveFrequencyId: Mothership + transmitFrequencyId: Mothership + - type: WirelessNetworkConnection + range: 10000 # mothership can see them from very far - type: WiresPanel - type: ActivatableUI key: enum.LatheUiKey.Key @@ -101,6 +112,10 @@ requireInputValidation: false enum.RadarConsoleUiKey.Key: type: RadarConsoleBoundUserInterface + enum.RoboticsConsoleUiKey.Key: + type: RoboticsConsoleBoundUserInterface + enum.SurveillanceCameraMonitorUiKey.Key: + type: SurveillanceCameraMonitorBoundUserInterface enum.LatheUiKey.Key: type: LatheBoundUserInterface enum.ResearchClientUiKey.Key: @@ -184,9 +199,38 @@ uis: enum.RadarConsoleUiKey.Key: toggleAction: ActionAGhostShowRadar + enum.RoboticsConsoleUiKey.Key: + toggleAction: ActionXenoborgControlMonitor + enum.SurveillanceCameraMonitorUiKey.Key: + toggleAction: ActionXenoborgCameraMonitor - type: ShowElectrocutionHUD - type: PowerMonitoringCableNetworks - type: RadarConsole -# TODO: add xenoborg control interface action (part 5) -# TODO: add xenoborg camera monitor interface action (part 5) +- type: entity + parent: BaseAGhostAction + id: ActionXenoborgControlMonitor + name: Xenoborgs Control Console + description: View the Xenoborgs Control Console + components: + - type: Action + icon: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-basic-module } + iconOn: Interface/Actions/actions_borg.rsi/xenoborg-basic-module.png + keywords: [ "Mothership Core", "console", "interface" ] + priority: -6 + - type: InstantAction + event: !type:ToggleIntrinsicUIEvent { key: enum.RoboticsConsoleUiKey.Key } + +- type: entity + parent: BaseAGhostAction + id: ActionXenoborgCameraMonitor + name: Xenoborgs Camera Monitor + description: View the Xenoborgs Camera Monitor + components: + - type: Action + icon: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-eye-module } + iconOn: Interface/Actions/actions_borg.rsi/xenoborg-eye-module.png + keywords: [ "Mothership Core", "console", "interface" ] + priority: -6 + - type: InstantAction + event: !type:ToggleIntrinsicUIEvent { key: enum.SurveillanceCameraMonitorUiKey.Key } diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml index f645539035..eec282acda 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml @@ -1131,6 +1131,20 @@ Cable: 2 Glass: 1 +- type: entity + parent: BaseMachineCircuitboard + id: SurveillanceCameraWirelessRouterXenoborgCircuitboard + name: xenoborg camera wireless router + description: A machine printed circuit board for a xenoborg camera wireless router. + components: + - type: Sprite + state: generic + - type: MachineBoard + prototype: SurveillanceCameraWirelessRouterXenoborg + stackRequirements: + Cable: 2 + Glass: 1 + - type: entity id: SurveillanceWirelessCameraMovableCircuitboard parent: BaseMachineCircuitboard diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml index 82a149bf27..459030d8a9 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml @@ -260,6 +260,17 @@ state: cpu_service - type: ComputerBoard prototype: ComputerSurveillanceWirelessCameraMonitor + +- type: entity + parent: BaseComputerCircuitboard + id: XenoborgCameraMonitorCircuitboard + name: xenoborg camera monitor board + description: A computer printed circuit board for a xenoborg camera monitor. + components: + - type: Sprite + state: cpu_science + - type: ComputerBoard + prototype: ComputerSurveillanceWirelessXenoborgCameraMonitor - type: entity parent: BaseComputerCircuitboard @@ -528,6 +539,17 @@ - type: ComputerBoard prototype: ComputerRoboticsControl +- type: entity + parent: BaseComputerCircuitboard + id: ComputerXenoborgsControlCircuitboard + name: xenoborg control console board + description: A computer printed circuit board for a xenoborg control console. + components: + - type: Sprite + state: cpu_science + - type: ComputerBoard + prototype: ComputerXenoborgsControl + - type: entity parent: BaseComputerCircuitboard id: StationAiUploadCircuitboard diff --git a/Resources/Prototypes/Entities/Objects/Devices/Electronics/door_access.yml b/Resources/Prototypes/Entities/Objects/Devices/Electronics/door_access.yml index 6176b6b2ea..1e5cf63b74 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Electronics/door_access.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Electronics/door_access.yml @@ -318,3 +318,11 @@ components: - type: AccessReader access: [["Security"], ["Command"]] + +- type: entity + parent: DoorElectronics + id: DoorElectronicsXenoborg + suffix: Xenoborg, Locked + components: + - type: AccessReader + access: [["Xenoborg"]] diff --git a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml index fcd25b80bf..6670114bdf 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml @@ -1768,6 +1768,20 @@ - type: Stack stackType: FloorTileXenoMaint +- type: entity + id: FloorTileItemXenoborg + parent: FloorTileItemBase + name: xenoborg floor + components: + - type: Sprite + state: xenoborg-floor + - type: FloorTile + outputs: + - Plating + - FloorXenoborg + - type: Stack + stackType: FloorTileXenoborg + - type: entity parent: FloorTileItemDark id: FloorTileItemDarkSquiggly diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml index 47bc6c96d0..879018d038 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml @@ -1133,6 +1133,17 @@ containers: board: [ DoorElectronicsNukeop ] +- type: entity + parent: AirlockXenoborg + id: AirlockXenoborgLocked + suffix: Xenoborg, Locked + components: + - type: StationAiWhitelist + enabled: false + - type: ContainerFill + containers: + board: [ DoorElectronicsXenoborg ] + # Shuttle airlocks - type: entity parent: AirlockShuttle @@ -1161,6 +1172,17 @@ containers: board: [ DoorElectronicsNukeop ] +- type: entity + parent: AirlockShuttleXenoborg + id: AirlockGlassShuttleXenoborgLocked + suffix: External, Docking, Xenoborg, Locked + components: + - type: StationAiWhitelist + enabled: false + - type: ContainerFill + containers: + board: [ DoorElectronicsXenoborg ] + - type: entity parent: AirlockGlassShuttle id: AirlockExternalGlassShuttleLocked diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml index 67e46649ef..76d98f96d2 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml @@ -160,6 +160,16 @@ - type: Paintable group: null +- type: entity + parent: Airlock + id: AirlockXenoborg + name: xenoborg airlock + components: + - type: Sprite + sprite: Structures/Doors/Airlocks/Standard/xenoborg.rsi + - type: Paintable + group: null + - type: entity parent: Airlock id: AirlockHatchMaintenance diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml index cad40324c8..5f80b94250 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml @@ -117,3 +117,13 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/shuttle_syndicate.rsi + +- type: entity + parent: AirlockShuttle + id: AirlockShuttleXenoborg + suffix: Docking + name: external airlock + description: Necessary for connecting two space craft together. + components: + - type: Sprite + sprite: Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index 6635e5698f..f6538ba64e 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -665,7 +665,7 @@ - type: CommunicationsConsole title: comms-console-announcement-title-station - type: DeviceNetwork - deviceNetId: Wireless + deviceNetId: Wireless transmitFrequencyId: ShuttleTimer - type: ActivatableUI key: enum.CommunicationsConsoleUiKey.Key @@ -1374,6 +1374,21 @@ enum.WiresUiKey.Key: type: WiresBoundUserInterface +- type: entity + parent: ComputerSurveillanceWirelessCameraMonitor + id: ComputerSurveillanceWirelessXenoborgCameraMonitor + name: xenoborg camera monitor + description: A wireless xenoborg camera monitor. You're watching them. Maybe. + components: + - type: Computer + board: XenoborgCameraMonitorCircuitboard + - type: DeviceNetwork + deviceNetId: Wireless + receiveFrequencyId: Mothership + transmitFrequencyId: Mothership + - type: WirelessNetworkConnection + range: 2000 + - type: entity id: ComputerPalletConsole parent: BaseComputerAiAccess @@ -1541,6 +1556,39 @@ - type: Lock unlockOnClick: false +- type: entity + parent: ComputerRoboticsControl + id: ComputerXenoborgsControl + name: xenoborgs control console + description: Used to remotely monitor all xenoborgs. + components: + - type: Sprite + layers: + - map: ["computerLayerBody"] + state: computer + - map: ["computerLayerKeyboard"] + state: generic_keyboard + - map: ["computerLayerScreen"] + state: xenorobot + - map: ["computerLayerKeys"] + state: rd_key + - map: [ "enum.WiresVisualLayers.MaintenancePanel" ] + state: generic_panel_open + - type: RoboticsConsole + allowBorgControl: false + radioChannel: Xenoborg + - type: ActiveRadio + channels: + - Xenoborg + - type: DeviceNetwork + deviceNetId: Wireless + receiveFrequencyId: Mothership + transmitFrequencyId: Xenoborg + - type: Computer + board: ComputerXenoborgsControlCircuitboard + - type: AccessReader # only used for dangerous things + access: [["Xenoborg"]] + - type: entity id: StationAiUploadComputer parent: BaseComputer diff --git a/Resources/Prototypes/Entities/Structures/Machines/surveillance_camera_routers.yml b/Resources/Prototypes/Entities/Structures/Machines/surveillance_camera_routers.yml index 977adfd79c..a14ae6cb7c 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/surveillance_camera_routers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/surveillance_camera_routers.yml @@ -195,3 +195,19 @@ components: - type: SurveillanceCameraRouter subnetFrequency: SurveillanceCameraEntertainment + +- type: entity + parent: SurveillanceCameraWirelessRouterBase + id: SurveillanceCameraWirelessRouterXenoborg + name: xenoborg camera wireless router + components: + - type: DeviceNetwork + deviceNetId: Wireless + receiveFrequencyId: Mothership + transmitFrequencyId: Mothership + - type: WirelessNetworkConnection + range: 2000 # longer range to get xenoborgs even when the mothership is far away + - type: SurveillanceCameraRouter + subnetFrequency: Xenoborg + - type: Machine + board: SurveillanceCameraWirelessRouterXenoborgCircuitboard diff --git a/Resources/Prototypes/Entities/Structures/Power/apc.yml b/Resources/Prototypes/Entities/Structures/Power/apc.yml index 75f61e7534..0addb34704 100644 --- a/Resources/Prototypes/Entities/Structures/Power/apc.yml +++ b/Resources/Prototypes/Entities/Structures/Power/apc.yml @@ -235,3 +235,14 @@ - type: Battery maxCharge: 200000 startingCharge: 200000 + +- type: entity + parent: BaseAPC + id: APCXenoborg + suffix: Basic, 50kJ, Xenoborg + components: + - type: Battery + maxCharge: 50000 + startingCharge: 50000 + - type: AccessReader + access: [["Xenoborg"]] diff --git a/Resources/Prototypes/Entities/Structures/Walls/walls.yml b/Resources/Prototypes/Entities/Structures/Walls/walls.yml index f3c2eaafe4..0063b0ad5a 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/walls.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/walls.yml @@ -1192,6 +1192,33 @@ graph: Girder node: reinforcedWallChitin +- type: entity + parent: WallPlastitanium + id: WallXenoborg + name: xenoborg wall + components: + - type: Sprite + sprite: Structures/Walls/xenoborg.rsi + - type: Icon + sprite: Structures/Walls/xenoborg.rsi + - type: IconSmooth + key: walls + base: xenoborg + +- type: entity + parent: WallPlastitaniumDiagonal + id: WallXenoborgDiagonal + name: xenoborg wall + suffix: diagonal + components: + - type: Sprite + drawdepth: Walls + sprite: Structures/Walls/xenoborg_diagonal.rsi + state: state0 + - type: Icon + sprite: Structures/Walls/xenoborg_diagonal.rsi + state: state0 + - type: entity parent: BaseWall id: WallUranium @@ -1696,6 +1723,6 @@ - type: IconSmooth key: cards base: card - - type: Occluder + - type: Occluder - type: BlockWeather - - type: SunShadowCast \ No newline at end of file + - type: SunShadowCast diff --git a/Resources/Prototypes/Entities/Structures/Windows/xenoborg.yml b/Resources/Prototypes/Entities/Structures/Windows/xenoborg.yml new file mode 100644 index 0000000000..9704ffee91 --- /dev/null +++ b/Resources/Prototypes/Entities/Structures/Windows/xenoborg.yml @@ -0,0 +1,14 @@ +- type: entity + id: XenoborgWindow + parent: PlastitaniumWindow + name: xenoborg window + components: + - type: Sprite + drawdepth: WallTops + sprite: Structures/Windows/xenoborg.rsi + - type: Icon + sprite: Structures/Windows/xenoborg.rsi + state: full + - type: IconSmooth + key: windows + base: xenoborg diff --git a/Resources/Prototypes/Stacks/floor_tile_stacks.yml b/Resources/Prototypes/Stacks/floor_tile_stacks.yml index dab0faded2..6e1ccd45bc 100644 --- a/Resources/Prototypes/Stacks/floor_tile_stacks.yml +++ b/Resources/Prototypes/Stacks/floor_tile_stacks.yml @@ -665,6 +665,12 @@ spawn: FloorTileItemXenoSteelCorner maxCount: 30 +- type: stack + id: FloorTileXenoborg + name: stack-xenoborg + spawn: FloorTileItemXenoborg + maxCount: 30 + - type: stack id: FloorTileDarkSquiggly name: stack-dark-squiggly diff --git a/Resources/Prototypes/Tiles/floors.yml b/Resources/Prototypes/Tiles/floors.yml index 6e3a588ab4..7c1f39aa54 100644 --- a/Resources/Prototypes/Tiles/floors.yml +++ b/Resources/Prototypes/Tiles/floors.yml @@ -2063,6 +2063,18 @@ itemDrop: FloorTileItemWoodLarge heatCapacity: 10000 +- type: tile + id: FloorXenoborg + name: tiles-xenoborg-floor + sprite: /Textures/Tiles/exoborg.png + baseTurf: Plating + isSubfloor: false + deconstructTools: [ Prying ] + footstepSounds: + collection: FootstepHull + itemDrop: FloorTileItemXenoborg + heatCapacity: 10000 + - type: tile id: FloorXeno name: tiles-xeno-floor diff --git a/Resources/Textures/Objects/Tiles/tile.rsi/meta.json b/Resources/Textures/Objects/Tiles/tile.rsi/meta.json index 749093b344..2b01db5ab5 100644 --- a/Resources/Textures/Objects/Tiles/tile.rsi/meta.json +++ b/Resources/Textures/Objects/Tiles/tile.rsi/meta.json @@ -579,6 +579,9 @@ { "name": "xeno-techmaint" }, + { + "name": "xenoborg-floor" + }, { "name": "dark-squiggly" }, diff --git a/Resources/Textures/Objects/Tiles/tile.rsi/xenoborg-floor.png b/Resources/Textures/Objects/Tiles/tile.rsi/xenoborg-floor.png new file mode 100644 index 0000000000000000000000000000000000000000..65ef33aab357868807bdb14033194d302178390c GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|j01c^T!FNZ zkc6_Ly0M|Do3ndJP)I~rL{>&teSJL_m#ZLOrVw|(BJWLC-Ct*qpMGmo8aQP87S%Q>Eak-(VLtg!K%?9D4K9;vq3Wh_ncz^2hN;fX*p9M70f6s zaVYbRnt|cr%r$BTj=l%hF{tnvGVW$-+ULNoY$C(4tUZyB;Uf!E*|LD*TR2 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/assembly.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/assembly.png new file mode 100644 index 0000000000000000000000000000000000000000..9629d9f2a99fd0b2fbefd7b08257303dc527a7d7 GIT binary patch literal 352 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|_Qf2KS2dyX-# zsXOiPE!ugKE@RI6EsW_7ft!7%$n04aDq5kYw84an!}q|MH%bBhJU3n{DaSZH;l1;J zdh<_B7u{(;g&0;o{l>PN@lUXn8uPt`olk`t8nQ35F+7}q>mS>j1IjPy* ol_9%-zDZSKMW)@M5BpwlPxKIyKCxh~9MG2xp00i_>zopr0O19PB>(^b literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/bolted_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/bolted_unlit.png new file mode 100644 index 0000000000000000000000000000000000000000..844bd201f1004c8edc847d6c63f14162e048f7c3 GIT binary patch literal 4972 zcmeHKc~}!?6JHPk5fqfe52%Kq)`Lwp2f0j81SC-+DWKfSX0w5nT;w2tP!GIXX)Ac) z2`G319%!keSPN1W#RIj1rCJo!s(^x}MFQU@pyKoOAD`#ze{P=L?9My$o0;FtJIQ_} znjdIq<7xu{fSrKvAA)}qjHk6Fe$}L?p7`}&SS!=QKPe4C!9Od z^vvHb;)pX0dL`(&XX!zg)l2)>wbc!S0CpPjzA0?q#PlPbPKk_0S!v?&#YSl)2LRYT5H!u();G zT@l3%GxL`}_1fk(j<8@nlvBnxW7bt9_KLNK zj#H~gsAbZ)*~cnve+?V4VRM$ijyKNnJhygm<|E^S>1 z#&9drk~H0ad~wQ#AuK71dL5N$(PK-n>Kir1pRTy6uD><6thMC(C%Ha6{fnaL?nO&& z$5W3k%c9EuI^a237*w=ve;rlcQ%rN-*O+tS)y)N4z{R=?8&B@ILw@R3eB-KXUXVOs z>2b@04Z~LcnY?EQ&Aw*p*m31Tv&nR2-KhOLQ)%Vw8PkvjQ>yq)JM9{?R-EU4rMrRe`jT# zYEct_n8DJ=SZ zjKkjYNr~>Y&ve(Xd4#^X{~c(ceO?@bL}3}tWoujS?u{z&aTf1Ew>V$QPsLOh0ty4a zIKm^Z;;buZ))J?$MIN_oN;tVOJ;Q~rIgT!Y=TAO(@U!^#`3sTZ zR^h((y+D#i6Qa!?)4$a*syapW>v#I@@%naP+1V>$Wr(}TyOuwxdG_RrX%>qeW;1&z ztJYNqWz2Wn*x|moyW{0+Qcc;JYX)}O=AD;-yjk2@Z58o1!tUQrXzSio!wWY#Hfx$G znIwMiMO{M+vu^lW1D7T%4DGCj#Rb=E)NW;a3dRIG1UfC)o4%|DUES#%e=-KWy7ZOH z8g=7k>Mz(R&lLe)zN>)$EZ8JkvwB>K>(t31Cnj`v1iGK&leWc7g4AC5=1w^et@<(g@~MM=Q=^*&D>dy3PEX?eb+4?j=98u;va6kE&gKa2PGG@Rz8lF6$@vEJ>x& z+s|0Vv;mpHrzQMv9rK@Fd%U8X*0awGQjP2?K6Tc+JE;dKJb3t#ol0e$hF25#P*7FL;@z^5yQQOkWl4^CSd$DH5!^WKMYArMA#DIOdlI=3I_*}qgt4dB9|#N zoD?3>gv-J2jbbv9U{cX0@`w>a5y4NXMhSEhodkgaDOfU<=wn0RswGlRi2s~H2>gjh zOwejo95Pw2*OT-#l2RQ{rm)#;GDIa)sUWTaYEl(iI0aN_JdF?o82+dRQDZ7Crc@A& zn6Ox>)AEQ!e4X$%KDkOLdeL7tkc=v{oNLEkth*N!ZF=4 zSah+J%A|lyI$jPs1@#8mkhd6Qv&2*hn?{2e63P%LfkLB&6$omC!of)xj)TAwIs>M% zKstlL0O?dZ6@(>-6l6g(s+bO=EDD=G1R_|C;av&KhDK$ClHgEGgvFphcnmO%Kp>sP zpn+nRm=3a0Dus=Ty~R|P!~|um1IJe+;1Q`L=)FWFgSAqnTFxT|VG3Q!`-Lz}j)rPs zBWV-{jSVpwOeVyjQy8ov)(cU!25&ecCIuou zyxDlYVFaY%1!vK{8FZAvFs1PhU89t0^{^WCjmI;^v%wq4lnr5uiQ}msYU>kFBY_Z= z0#c|T#15l)b07wX0)Y^n13^UcV8UeMu6~;_m;67Ra7_wB#sF?NAj8KCKCQ^_#?>Ha zMjHRcW3U$gq6ZxMlar6)_Y+;8==vxIK1%shcYUJkqZs%o%AxPZtTFaD!B~*V+7BAo^$PtPqQfYr^8J z+e3rTRP1EgI~b4bbI+pIKchQ+Z?`;Gqf@+iW4^rGzieMq>Fec`IVwHxaa#PiQhW2< qmw%XP<{oXowhKM=Q&Re4tNGEOQ;&z$=;OF^KoBtBzjSuo%Krk1BzZ&t literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/closed.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/closed.png new file mode 100644 index 0000000000000000000000000000000000000000..06c3cf5c6c4b2fa0c2a23dd30954ff5f486666fc GIT binary patch literal 629 zcmV-*0*d{KP)Px%FG)l}R9J<@S8a;hFccgIH)~}>?B-95VOjPBrO^AIpip{(QWi{H3XVb&E6GCo z!#q`zCHrZi=|CWs^fY7XeK}dL%Nzb9QULh!`4j58xhrv1ZSeKm_g(;z%fsm|XR-GU1)?R0jaKyz$*? zHnqSd3z2izl1R1yA0Dy~`bWlA=)?8E=0z)sJ0v)-?1nOox zn4c8j-eU|*TOxmXe!{Qck}m-0-=dXrdZAOn-9}wFUj0zxUGs#0$&WwzylFb(2i`7t_H%stT$z}`iC5q6- zoD<-{azct#jw5mU01m7Z;B>KL;$qekq8bv4@5|u?rCL2cOJp5{fA;*H+q62z`QPz3A9qO?KNk!?N5Mh^$+j|pA}qviKoxG P00000NkvXXu0mjf)TR~C literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/closed_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/closed_unlit.png new file mode 100644 index 0000000000000000000000000000000000000000..7c80bc210c483696c4f2080e80390e9c71960329 GIT binary patch literal 5035 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/closing.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/closing.png new file mode 100644 index 0000000000000000000000000000000000000000..8c45050ba39429a90af59dca66d34e7348da0cd8 GIT binary patch literal 1771 zcmV;P)Px*rAb6VRCt{2oZ)WUHWY=A4NJ1)z;d##$+BfZo?sZTC)tl-4?SSm6LeiSBN>V~ zGOXB^0%Skjt5Gy1l9EWumOdb8?1(xPFRv|%=XP*;dHseSXN&;gryqa7V)1BI_~zyc zzy1CPx~@M0kcmg@7c1i7`C0CQyR1K=H>33n!t*n1wi_g`Z;0a+F2WF#@dUxx!)}{u ziBOxx<3nL5Sh}h|V&4>r7oETaUDVG7KqUV3^f0I+pz|f)#Kn7Ffb+94)-PM6=}xO{ zQ-kk$fwK0HebMyb1JF806Z&bo!!nAr2VbPGisuFJya3bb4AbchzQ2F1LM{WE9(>~I z>0zLf2_k))8OZCawxwXF=`PE_BvI4MWx#wsYkcqtm2;ZV*EtujZzBV98SvrbCnJMT z$Yzx(ebw14(l?!fxePEl_$nHvgWtq4P3sQJ{&;HGm|(iPoeBWZz94ii04@;)p$~v% z_#pHFa7-Tn$MgYkOdkLS7rTRf061(1p;ZBB%l@>@ezYn8K=*=>3xEw#6%CX9&^fn> ze(>iUi4HKeT)(`$e!IWB#lN2;rDL1!hmJnmrj90i4I+KvN06z=-&|ee`FW+)#-%>t zc>ylMPpS->y`Qkw1>8kz+*c(L`i^sxF@Wk;d69<6Fc!FgVDYwrwzj$xD ztgq~S=`lm=9z}GGzCOUQeqSHpSU>v+B7b~%eg=QEUwg&z3h&>Ah-3ZbJGG^A#_=lq z5nX7JKD7k^e>BLBDf$t$jpJ3;R#%_BemckdcVYHJuT{p5`Von4L(8}bLo6fp2UygG zXwqZ9*={QJqpID!s4!!)LAr$U6MYZ;geK%EK^ z0Py|%^K#uFbTI=cflC-foqjEte z`cY*do9Ks3*Qzs42bY<^_oJ>RtLw}eSB19pqX}mmuyVyGgU~_9g<|1ZSJJ$PHUav)EU?I8K{agXB<%9o5>nvtIk07UtQ`9evvayhPE?K z#;MLgS=(Uj;rV%0>2WkI?AXEQQ3}8*&%iQWqDsG)QH1$i-bOhCPIU&B^_hnp4>IWb zR3=SjceIRSnSml*>N9|Ypvi)$yJuV)fG*5HmixQg>{_SOS)m__wW5$tl}QSe1VAuY%ah$60_xU0cH;to z_7^PKxHEfrhxPmV0LS`$eSl-EZpgHeUw`@8=>x~UKEN@3eSl-6^#M)z^Vzn%KVGKq zj|P?N6WF$F-)#k2g-YK6?ES6IB7~f0RiVHXE$CO$(U1P-P08UnTfJ{R}jLGyr8KIzV{_6qV>SfvpTQO{I?@ zwnRr%uEWEZ=;+YK3~UnZFX2-wl5>qT0H|)?F-51!RIA!zOwxFn zs?0+qn}Mb>u&yn}6pfUrvXm9+$!4Ib3~cM*1Kc{mtj9Ip6l`)GQd>6(n7gRQ8$v75k zplgHy=#n+iH3tE3IRka9fou?DkF48FZXIxccdK-}$nHex?n;#*x>qlH&d+Cnh-1iz zTmY)fz@lTy-Y-IRp`@yR4CDgfdIsv;tMj{Z$1vMP*9Wx1oZDr8zH2&PAK*Fz{sU$9A`G`Qv{nEB N002ovPDHLkV1hkkX_5c{ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/closing_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/closing_unlit.png new file mode 100644 index 0000000000000000000000000000000000000000..51ae8ad362371005930db55fd3917cc175d3bc7d GIT binary patch literal 6610 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/deny_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/deny_unlit.png new file mode 100644 index 0000000000000000000000000000000000000000..dfe4d406edae31f1130011576be62680489d511b GIT binary patch literal 6524 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 Bd?x?^ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/emergency_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/emergency_unlit.png new file mode 100644 index 0000000000000000000000000000000000000000..31f7a5f9f06cb09551eadf4c25a7a0ac4b86c2fc GIT binary patch literal 5301 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/meta.json b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/meta.json new file mode 100644 index 0000000000..5e40ab7363 --- /dev/null +++ b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/meta.json @@ -0,0 +1,146 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from CEV-Eris at commit https://github.com/discordia-space/CEV-Eris/commit/14517938186858388656a6aee14bf47af9e9649f - then modified by Samuka-C (github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "assembly" + }, + { + "name": "bolted_unlit" + }, + { + "name": "closed" + }, + { + "name": "closed_unlit" + }, + { + "name": "closing", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "closing_unlit", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "deny_unlit", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "open" + }, + { + "name": "opening", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "opening_unlit", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "panel_closing", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "panel_open" + }, + { + "name": "panel_opening", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "welded" + }, + { + "name": "emergency_unlit", + "delays": [ + [ + 0.4, + 0.4 + ] + ] + } + ] +} diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/open.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/open.png new file mode 100644 index 0000000000000000000000000000000000000000..a47d6aa446cd23407e99f8c90a4de220c74af5e1 GIT binary patch literal 428 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/panel_closing.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/panel_closing.png new file mode 100644 index 0000000000000000000000000000000000000000..6afe206992a4a5f8c1b8881316a259f3b09488d2 GIT binary patch literal 6400 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/panel_opening.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/panel_opening.png new file mode 100644 index 0000000000000000000000000000000000000000..e6c87d740e2af2651c01d77129428b36a0fcc384 GIT binary patch literal 6183 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 OkdRF*jdKlM!~O#m;QPJ+ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/welded.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle_xenoborg.rsi/welded.png new file mode 100644 index 0000000000000000000000000000000000000000..eed2758c7955fd7fce7a0d156810bf5b795ffe37 GIT binary patch literal 5638 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 As{jB1 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/assembly.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/assembly.png new file mode 100644 index 0000000000000000000000000000000000000000..fd765e671e87e9b55e54282cc924caf4c5c417ea GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|>;rs4T!FNZ zkc7CnysWI6vZA_arCv?RzcnBhMfm^N)1!-=K6SwIE%JzX3_EP87vzZPQ=;Am-m_E)}g>FH^ye~Z%W zL|0ynO8#8y?9%lBEBXp@QoW_k_zf`V7g6+yS%m4|u5@*!G|C41WTf z!Sl6joH?2er*%~{%oq&h8^U-Otys9MVE5L#y=gbtUYJf~*skm}jpKeTL*$|@<_xZ< zs+m`XTxYwtZLjmxPxs=rR9btmtBYaQ#km^=d}5s_F`ThMJ1nKl}%d|7WgO W=E}R4=9dHXDubu1pUXO@geCx(OQ28y literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/bolted_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/bolted_unlit.png new file mode 100644 index 0000000000000000000000000000000000000000..afcc4809e53bbf4d6cdfe3b1443f3469cf464a09 GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|qyv0HT!Hj| z7in~4XZQdA|Es}2+kn!XB|(0{3?dpfQ56gR|KHEaavLbv=;`7ZV$nNya-h%w0Uj6E z75Dxhxyn_y>1JPz;rs4T!FNZ zkc7CnysWI6vZA_arCv?RzcnBhMfm<9wFPwtimD!A$C;uvDl+d4T=sKtQ8IoEr~|B2g0G&gSAU1!l( zsoJR=KKr`FUgz0P3z+tQw`p)Ctx`oc3Y1^*7o~3wSIL>r?&my(CY#-MBoZ@iTPY z{C{t7XWQQKkh>!7!-R)#D>a$LZ{I)Dw4C+iOmp2$GwN3{pF literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/closed_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/closed_unlit.png new file mode 100644 index 0000000000000000000000000000000000000000..7df73f082cea0291aa93181aef714ab15f655b26 GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|qyv0HT!Hle zjr>^9|Np0@7cB=$bCv}81v7|f*hEz<`2T-DC(CW1V56stV~9oX+{uAL2LyOrTvy!t zf8;7x-KLv;HIl#IsB}+RD4|JLXbd)Dsg zUWR85)d|5DWPaIs7Kt;~R4PW;S$^KWQlO(r`qOk-rm$Z(4!q-+IwRh|Sna%=uaLil kYu;hMOVeNao2BT<*S%(1dC7`pI?yo;p00i_>zopr08qGM=l}o! literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/closing.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/closing.png new file mode 100644 index 0000000000000000000000000000000000000000..a41c0818b1ecaf472d76369d78d360df9cd6a6f2 GIT binary patch literal 1541 zcmV+g2KxDlP)Px)zez+vRCt{2TU}2ZI}klc3E8D6q>)+>Y82(MQXl&N{|rJ@sPsx)sM3%$54jI? z7RPw(u|4+YR-C6QZ1;@Mj6c>sKAKK1b};bn-53Dy`}YO_@L~EMqh^S!>l@tPe?LMb z&d=vN060D#0RVpe`ssZ>mw!6F*e&KWY}Q*mt)H-5tx=NS=4+c4qh^SQho5Mh7XRG; z>*XH+z~%V`9+#_E!~igAhM_ilad&rp1iU1I8hCbkg4?_A;P_8Znk-%d3Ha^y2V(d? zU4F#FgFjhPezOp4{s90yE?2DZSJyWHkW~F9w%ZLB^I0dv@UB42iXeUxezV^0#i0$3pW6SlegXirO)CrUBCsXH6(KHwfVIc0bGyc_^Tx#Bxo(+L&#YKNQAdheF);9y$H|}K6WUI zP5i+mKocqZR=JCS)E@WOB_Ovar3#rkg#L92NJY*fAa$o0cuPQs)C$h+ac>FugIYTQ z_W8?O0un(&N)t!VFXl4<=(eTb5)cVfNE)`J&jYVN`2IHuh^@F%{3K-T^S1DWgHJ$g z#g*g-WP3$)PHa9;uyc1KiShL)OK zW8j+vYr(q=Jw6^`xmshnTJKo{bHE92npL;kP4{o&Cx@CQOz}B>`dzyR*uqoKC;kEO zY!oH0;i^*i%@wZLe4J(WJ4DZj*b~*g){Q#{lXuE;?xF6v2=X3nM-4CFK zYfnaYAe=wHB)`j_Z}azdKcG~@LxI%hw|gY<_kKU1rZ3)AUA2ArHhRAwU)%fW55=Qe(6?6?E>f$plQ$c5pwS>!B6P>egM7SdqG3{j#kK~ zZGUZR^2hk|34Ij+I|t-+y%>MIHh-)?pU_tUyqW`28ZIHEhI8iuwGtmXk0@OLtTC%} z#f8oTY9-!x9+4UcK_Yz6m=&`MND)4?FE8a!O}yrrucQFP#;nv;fVS`w{t}6|<8X1= z&!tp6dmqQx3(o_(Q^T{8|NI+IpglS+T1CuHj}PVh+Wn{1V|~1R&%V0B7qF z8?)^H;2RUo8zCV>SL~s9h(A>Esr;!0AaU@Cj9JoKg3^Xd1Bl`05)Y&cAdSO}!Nu!? z$e1O)C79yf^aY^B?}LP2S^y~yz9?gs_NpQjfChh#0QT=(9I2jBu`#Pe!zBS|@aG5+ zibMPRfgOUh8!inXh2KQ30EC!|Px)vIE<I?g#t_h|pSuj(h{u00000NkvXXu0mjf_uJsH literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/closing_unlit.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/closing_unlit.png new file mode 100644 index 0000000000000000000000000000000000000000..f7700a06185315c54143fc431130f54a140925e2 GIT binary patch literal 521 zcmeAS@N?(olHy`uVBq!ia0vp^2|()Yw4r!_0^ zI5=)O_rLz*0tem+;vBQ(Vz%C!(cLsjVzJGeQiDGQw~x6fC|IZFs-E~aXZ~Ihhkc1@ z+2#jce`J35P5Ie&iSIoEJN}-2&2eJx=bzo`U!Gbqo_~M%oamKelc0o+t_E=q9|D;s z^$Qx*a864ApWZH|6>7j zxhdy*2bM4L+l2al+NaMAs{SywMnI-Fe5HlC#+q$851sxygdW$GS?&8+f91aLf|WJP zt)@(Ic$CEa=(@Cy355>oaNXDPL+SzxjHvC&o(b*?XNb|J;5}qa#m4L0Lz!c zb$VJ%QI(69EZ}+Zpz@(AtI4Ma`}f!HSiqI>eBp;r@daKJ*h@ZcU^iqExN!9Oy@Piw zXYA{G^?kvb|MM9xADu5QIM12sjP^Ov+e~7WVWuaq;Bul_K$`_5oJ&=RaD(TW3|0Rkj|; zK5X)K+9iQ!zWEG%bI+OPpYoX4|gdpxhkr*P+a2R{E@$`n$0Xu_B0 z(h-~*pSLLfZ+pS0|1*EhfinIx{uh5#2ZSDetY#Hu&%nF)ly&4}Rpq%gw$_*R@(;57 zuGi=P_CHSUt?gc$J9pR^HoW87{+yBF&HuytYYx-^bx-_j+wjl+z~6eAhRf^>6|!ts zR%tqfynpyFpQU2|wF7&PGbv=%6?`{gIC0DT&j0^~tlO#+>bjnMXL$dA@1DazPx$QAtEWRA_&OzIq-GJ+u3f+ippjrKk3Kof zq~OeLY~>_*Pb1A|qQlqo%^|-}kDqJr`{y!^2=9JmXym#gEXB=tE%}LnB3!aDVikoe z@qQ@YOG7PlCI+O?4BVxsD%`pwe=(57?}4ZElqSrhphaK?jy!w-9Mjqj&A4sW`^AN5Yuw-O2E(S*WL^g8P9k38CF*Q)3nbKwkl-+C5*cCdh8Cftq+?Yh5arSw> zn5py@j|j-&r8V5R8IW{`8*c;M;l?8kb6f8XH-J$D8ogpoqHmfw_!Xkz;l{KVRMK%% z3Cn0*q7<)CKPLBX=s8OSK@bE%5ClOG1VQjW_y&G(Xxjnn7ytkO002ovPDHLkV1mwh BtB?Qy literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/meta.json b/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/meta.json new file mode 100644 index 0000000000..cd619f7948 --- /dev/null +++ b/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/meta.json @@ -0,0 +1,198 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Created by Samuka-C (github).", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "assembly" + }, + { + "name": "bolted_unlit" + }, + { + "name": "closed" + }, + { + "name": "closed_unlit" + }, + { + "name": "closing", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "closing_unlit", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "deny_unlit", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "open", + "delays": [ + [ + 1 + ] + ] + }, + { + "name": "opening", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "opening_unlit", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "panel_closing", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "panel_open", + "delays": [ + [ + 1 + ] + ] + }, + { + "name": "panel_opening", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "sparks", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "sparks_broken", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "sparks_damaged", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 1.7 + ] + ] + }, + { + "name": "sparks_open", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "welded" + }, + { + "name": "emergency_unlit", + "delays": [ + [ + 0.4, + 0.4 + ] + ] + } + ] +} diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/open.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/open.png new file mode 100644 index 0000000000000000000000000000000000000000..3f21f58c8365edb98dad6a307463fa48ec31d9d1 GIT binary patch literal 311 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|>;rs4T!FNZ zkc7CnysWI6vZA_arCv?RzcnBhMPVE8``2uyhet^*acdb&7G4a1QpTaqfx}>RE z+rEp4?P2*;Px#KTu3mMF0Q*5)v2}7atuRCLg?ro(yVDQ&>q4fUe84L9NG;6+z#)(;0B- z@@#M%ttEQj^x<>_=&|YFOPs?-~=ng`at=lyK zDpSCZ9qbHx>Oz^i+bx9#V4cepqHnE*GWEB&Sf)Xj+(7MXr81@4lQIpuWCr@*{yCK? z-L3&(nW`?JOEl=Kj?3Q8KzF$8T>x07stf4C1{baa+!^Q@&FDFRRi*085vN7tjR_jsu>#e()0FPwHI& z5l>}`B1Q;hdK*B*bD4%$J(cNG00GZsx&@2~9|DMYD$||8`Z9Y8U<=R`U5#s&f@8IY zy8u?}m;&l(9kqE)s>C(7NdTy4XE2-J*?yBjXwRQ?0$BSF{k>c-qtP@b{r|lBnmfH; z*?iytS^tlK=2AG{2aNFZ0RgQ4pD%R}xOS;UFut(;RSupH0-*ZdGyqz~_dx`V_T4(O z9MBr>FamOW0E~w&0-*ml-K}Z!eXs#g<3#}L|Lvs?fTid50I<%?M~CgZPlNS<_W+&` zC^~E3Im^}q-UE0(py+1*9}m%*$Xh^|4=B3+SAK}A2fYQTE_{d<-QJ))=mhYGXwlsO z9`pb}%i=QrZ14g=C0P7|e*o;B#D4?q<@-AS6Tm0y{z?4<;F?ar{{Fk>*MKpdfV~G~ zXY~ufnoh1iaEgq(`vU-`69u^T-0#k=H$@=6|Hp4Uo(~vcMX)dQFn|Dn=K}&*5#-4O z08J+o;O7JXQg%gq9I$0@l^8vRNPy{h~4Di#*0Kk0UwWZrn2|K`iqxr@a0Atcm z0agTc1F+wVHh>x724FuWqyYQ9h&yA5aJ7aRpxe*ZGI>HS`#2w9nY!jSNfGMoKkVat iU;u8<8NfOLmi_~!d_zgW-5^x}0000)YwklUfvb zT!k-`ecS&iGh|ie6W0s&KZ5*U-P7RUJYXvzxTI0TT|;Ig_li8H8IKeL7$v;wTzC8s z+u5+=uFbIu)`}gD3VGG17M(kAhL`E~^B;E@`SJvA^zTnDFK#~YI{(8jnLYcuuB7S| zEoS(td~1g*!^*WsPQ7Jf(908LC@7lk;LUC!J3%MjWQW~M6@vr68*HBLIUtbEbLmnl z%N7=?d#ovyHxHcCY-ZWNn_YK->V;2c4<0@LcbqGOGh~i|$FJvnew-PcC3DPnoKtXf z@LurDj#0O<$=dGV9qEk87PjouQZ2T1?OQ7Uc`?Judd>(-lc_qTzXUjYK9mU@ch)vI zv-?nXK&WKM!>cE~<9ko6*zt^Oea*?aH(AwrPW^Mx-m~mnj`u^;TD8}$*OxE`P5!Z` zG5A2%1F;XHai8n=Ef2@cMtem|OF* zFWOcj3o80HPVajj`$!J#-ehE+&jBsN&4Pm;Ey~RwD9Kq6B9w4)yI_i7v8+R+PTth#)*GLD}V7W zl4@u_w)hJFghlIK$L+40JbAkM*PE;Cd-gLvdRYE?>$g8ClRpNE+c!M5-Ew8}@7KM| bN=lzN<#(T+UUXjW6UhCZu6{1-oD!M<^ljbm literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/panel_open.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/panel_open.png new file mode 100644 index 0000000000000000000000000000000000000000..3b98c91e747a5400988a3a0dc61d392216820715 GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}t2|vCLn2z= zUf#%i$Uva=;r$6ril=lMGz7X{>^acue3U_?qp4M*>69$1@RBZXZ6}TtF0&5{TORHE zH+TNL6oX?D$vuYl3QCbKMV{L(+3{RBeqztM?mQn8R)(_Q+6?!8Kjf}>Z#n;-&8q5; zykD8xA5XcNZ5-FQKO&(}`~2!U#v1ViZpD^Gd$Jk@nyMI=Ybq%1?4LMeo=NO$Jx$(d mhJ>v#&zEJ(I(RY~@8Y*hG~8*u^ISF1)eN4lelF{r5}E*u`a<@sF#^1UjR`+k-ZAlyF=8H&?%Z_$0f4Ap7!5(?7Brap;|Wc0pZ$#kfsr z(Gf?cqeo4?zmHvY|NOl>FYh`s|Lp`C4h274ITms$hTN;oS-kJr;ji|!ks_PyC62uG zxadAD)pomD)UAtl4<3CAbaA<45^BDA-LFs6TodD}x9)o;xr%W~rdIj(`Jb-EXUHvn zw2{G(ef_?M)w5%7{4?;xDu#RF4?t4#PnjmEm&&iL)OA>M(0yk^kSbW0sN_D*h$&5*V@4n)m5i5W4^g|E6XT_i2KbXfYko{p5V@l*T?NjBJ zm9=HnjGrPJW;Ea5Wqy3V+u_1c{R6d;v#zYU&-m);z*_K=!ln3*6F1wUf5nSm|G~6ovBSKV-rl|J36iX@ zoPJ$86(yb^+48|BJ}T-plg?s?m;+p|H7=A`y{$|?Z>8E2FxOA*YKFzSbCZwk*_P>; Y96!C`^z^EVfOe3zp00i_>zopr0J)geH~;_u literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/sparks.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/sparks.png new file mode 100644 index 0000000000000000000000000000000000000000..dd67e88a315f674a061fb3b27660b8c761c88f38 GIT binary patch literal 697 zcmV;q0!ICbP)7E0M--(oM3v-T6o|50rvB z?(X|$_9K~q!b>Jme`5^XKVF&j%`2QtMsN-@c!MQ;uYX{&xPXV-MV4h*a7^*H)mTo% z^duKBlw8QrI7#Az6a!+!k42gRM*{jVXw(2^)BtAG0A@rO5W6r+TTo*dR0c>itn&u= ziYhf*#29dhKzXbye)sb`x`T#9jF|@skRQx^J~1)&r}u;k?=m8#VH_qLszE1c7I|*U z-vi17+kNaSpCz%9yQ#rS&sN2x0`#dRVZ$Zo3 z(YozxD(tDJ*Nd^#9ngM11*`#hZL|qqUX-aA#5S}G^!n=WLLU@GQ4~c{6dSK|xP5Tx z!-H@vnhtn%y49-Lc3NyR!WG{&^FjpmIbAy+o5nPrx)V}}E%fz`bB+BtcI@^)8^rTf zBx((Ky3EXU1aLM2IGxVm^KA+58-UdY;QHA(FXNaPN!aD?zV8}<6&DioA6qwm%&S`B zG(?rf)v;@BC^IYu=$f%ZGhhqXw5SF!qXsa;V!%*uXSl{qZh&O*G5ePMUQntINQ83i z$upoRb}H)fv5TKDu*ZP$-hvmY3!Atq6ENBh(WYFFM67|7l05p#?E1nmayZKasmvEs z4%uF#@2=U$hCc&#<3^>qaoRUjD5t(Q@!@5>eLn%YPa&y@Wk6F&Jf;Amr=S&p`TF~H zKQmyzZBbK@-3j_NfaCqFrZteLVgLD`ViBgsl+FQ!832*%0DhkaTq4i015y-4Q4~c{ f6h%=KHvsSpwHV1KmpgiI00000NkvXXu0mjf++aJ5 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/sparks_broken.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/sparks_broken.png new file mode 100644 index 0000000000000000000000000000000000000000..c41fa18ca1a5201b580b0194415c120f2980ad94 GIT binary patch literal 315 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGojKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sDEfH31!Z9ZwB-~~??$B>G+x3?U*4jBlrUYO^z=YQuNR}pT- zBWy>`xct87wfm=W{jN74OBfgq^lLtiinBjCrM4q_XUp^LGankKO#7F2v&Y%y%Ff{Q z3~%|r^L0N9BrdH_`lfiRRhipD%PysF@10#wUYOUjy0=@j2J7*HO#%`BwVxio(s#yR z& dN}IerjGt7Q1>7HnEC$-e;OXk;vd$@?2>`@EK-2&L literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/sparks_open.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/sparks_open.png new file mode 100644 index 0000000000000000000000000000000000000000..40d559f7a3312504e49f41ccd899850ca59b3980 GIT binary patch literal 281 zcmeAS@N?(olHy`uVBq!ia0vp^2|(Nn{1`IRkt`T>t-f`0?Kk$m_OstpifbB|(0{|Nk>wKgA1F%fMOS5n0T@z;_sg z8IR|$NC66VdAc};M7Y15X2^BGK;UrE&;N_7IVHPIx5Rc$QDkj)wW>VRAK0MbAfmt} zz}ms&!nlax=iUc9=SyjmInTllg8=T*qkh)`CG2`A1pH*)B{#SEUB%$(>gTe~ HDWM4fZCPKD literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/welded.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/xenoborg.rsi/welded.png new file mode 100644 index 0000000000000000000000000000000000000000..08a09d48e31bbc7f819aeff0353fc762b698f93b GIT binary patch literal 300 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|@f2C){`9ecO-f2ietm%y-BKL4cbmcQ@#)-o(fZW9un5G7j` nBK0YD&4eJ8g$#G*KVX_wp?Ku@oPvu$7c+Re`njxgN@xNAeExPM literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Machines/computers.rsi/meta.json b/Resources/Textures/Structures/Machines/computers.rsi/meta.json index 65cba1211e..28b6b7fb79 100644 --- a/Resources/Textures/Structures/Machines/computers.rsi/meta.json +++ b/Resources/Textures/Structures/Machines/computers.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/bd6873fd4dd6a61d7e46f1d75cd4d90f64c40894. comm_syndie made by Veritius, based on comm. generic_panel_open made by Errant, commit https://github.com/space-wizards/space-station-14/pull/32273, comms_wizard and wizard_key by ScarKy0, request- variants transfer made by EmoGarbage404 (github)", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/bd6873fd4dd6a61d7e46f1d75cd4d90f64c40894. comm_syndie made by Veritius, based on comm. generic_panel_open made by Errant, commit https://github.com/space-wizards/space-station-14/pull/32273, comms_wizard and wizard_key by ScarKy0, request- variants transfer made by EmoGarbage404 (github), xenorobot by Samuka-C (github)", "size": { "x": 32, "y": 32 @@ -2027,6 +2027,10 @@ "name": "service_keys", "directions": 4 }, + { + "name": "xenorobot", + "directions": 4 + }, { "name": "wizard_key", "directions": 4 diff --git a/Resources/Textures/Structures/Machines/computers.rsi/xenorobot.png b/Resources/Textures/Structures/Machines/computers.rsi/xenorobot.png new file mode 100644 index 0000000000000000000000000000000000000000..15004ef02426ad45ced8a66cc2ad2f3aa984da9e GIT binary patch literal 646 zcmV;10(t$3P)Px%KuJVFRCt{2nlVemKorOSR&fXv+`1YqIAstW1aXpcarHAemCk;I&f?V1(6tUa zi#SLJr=p^3HbHO*wC5mKFX2*?yEIAh|C^Fa$b0v{_g->$0SF<45b|GHF*SOgkge6L zLJftJ4!*u_jrOX<=#bwW%l4)Z0O<8cv1=FSyHDFOhyI*?a@!|h?_)Vg~;#o_i4Ks-YA<23toQ{du!H?;`rMopuh zbUY904U{#tlpl(;fiqzgAe?m2#_vOQMNK4~GC3JeI+>kPsPst_IB6$q&C*GYfVcFnA%qY@2qAQ?2CjkJUz3EpHf}SU4JJ>E* zFB2!juFPd;wQR0)`?PLl+bIInI(>4zz6^@8?VJM3j*|d&_y@q%he9G??|Hw7$K;|Y z@vQ~u^+&UPxMV>p6WtFRoFd@tWW&k0E4}_`wi9fNlK^V_xe5`K&B4uw^`C1_c%G0V zh~(q_gA_p|%lsIs(sm3HL^DUq7C?30Wgbun%84o>pbnEU06z|uEkK>7;{eK?lh>~E ggb+dqA%rX+-^vIn;p{0AQvd(}07*qoM6N<$g8KC){Qv*} literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Walls/xenoborg.rsi/full.png b/Resources/Textures/Structures/Walls/xenoborg.rsi/full.png new file mode 100644 index 0000000000000000000000000000000000000000..d85d8587c3a43eed7068ea7939fc3254e8f3ec93 GIT binary patch literal 354 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|ECYN(T!FNZ zkc7CnysWI6vZA_OWP!0}aWAv!fdLG~e}2h*zDl^8K+Q zG0xCw%}=A1<(ruk=k0v>S}svgrX)@;<=u~xf`*cW#~Zw!@a0Y1dHBw|GG3F$D+{%4 z*_rcl&nIfww;$Uae5sg0(A$MwIVxgFxy1fzQ|-b659zl3+wW}5RDD@lmRrBW;`r+F deS6;jVl?>4?OQOhwF&4422WQ%mvv4FO#n!CiKqYo literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Walls/xenoborg.rsi/meta.json b/Resources/Textures/Structures/Walls/xenoborg.rsi/meta.json new file mode 100644 index 0000000000..fdebe0d8d6 --- /dev/null +++ b/Resources/Textures/Structures/Walls/xenoborg.rsi/meta.json @@ -0,0 +1,46 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Created by Samuka-C (github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "full" + }, + { + "name": "xenoborg0", + "directions": 4 + }, + { + "name": "xenoborg1", + "directions": 4 + }, + { + "name": "xenoborg2", + "directions": 4 + }, + { + "name": "xenoborg3", + "directions": 4 + }, + { + "name": "xenoborg4", + "directions": 4 + }, + { + "name": "xenoborg5", + "directions": 4 + }, + { + "name": "xenoborg6", + "directions": 4 + }, + { + "name": "xenoborg7", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Structures/Walls/xenoborg.rsi/xenoborg0.png b/Resources/Textures/Structures/Walls/xenoborg.rsi/xenoborg0.png new file mode 100644 index 0000000000000000000000000000000000000000..fd6951dda95ea93ac93fb805a401c044c7fbab5f GIT binary patch literal 471 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`SO)lnxB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^GrVRPyy zg#$GUmjw9*GyF#b)28jqyJ`=#PuJ7MF(jh(>U2+`Lk2u7j@l{D%H-8f5|Ep(vax?6=TtU4Y!>a&2H-=IjrQ__3t`cnw7g-Ln*EX8S9NXvA z^7djwL*c!>9084Ae{vkR(`ayy>pCE+(O@6E;dyjwPw1}+8!DrP*Iedj*d~6NWz~aA zVJvGBR!dnoXhy6%Afn@!!j&eWyPzr1e4CPj#)h?Bh3eZ%O#~9Iy>DpP+BxF^^X!Wk z7_LU{XM6EBhV?}Fgt;2+hW$Q^X0BMa>CcR5&wXAW3=CM%WXS$tmc?_1IAM4GCtO?# zUt6DY8E~1aU$U{dWgYy%>7u{7h`8v5#A>EfEUa=B4?AkFlk44ofy`glX(f`SO)lnxB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^GrVRPyy zg#$GUmjw9*GyF#b)28jqyJ`=#Psh{6F(jh(=(O8>hYWa}x0~JmUvI=Y`}S6ko*Nv8 zgoSK(tSpY4HalYZj^BU1r)bvk-&l5nIp@z3Ml)9_h35`+$4dA9vuD;=x~;%TO5pj# z0}4+V>+0;4Iam(N>YQ_c@!MlIE(N0%j9x!QCfFIc%QBksI&7M-$K=5L-Hc}cC$Sx= zw$}~kXI{d%K}Tl}+k%V$mrRCJVySc3{?zlG@GW`u=!i@J`^o?552w#Fta*}pa?8iO z)b*-t4M~ec7yQ4>b>V*|Yewc4CX>)8$CZnk7HO?m)#NRdDWAb*ATA<*;M5xaD8q?% zFP@&!oN@ShxBAiiBk#BwH}IY4-xbJcsJ@PCMeRauh3~D)8t#fOXt)Q&_r(`DFwUrI w_4Qpe_xz4*JA=Gk44ofy`glX(f`SO)lnxB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^GrVRPyy zg#$GUmjw9*GyF#b)28jqyJ`=#PuJ7MF(jh(>U2+`Lk2u7j@l{D%H-8f5|Ep(vax?6=TtU4Y!>a&2H-=IjrQ__3t`cnw7g-Ln*EX8S9NXvA z^7djwL*c!>9084Ae{vkR(`ayy>pCE+(O@6E;dyjwPw1}+8!DrP*Iedj*d~6NWz~aA zVJvGBR!dnoXhy6%Afn@!!j&eWyPzr1e4CPj#)h?Bh3eZ%O#~9Iy>DpP+BxF^^X!Wk z7_LU{XM6EBhV?}Fgt;2+hW$Q^X0BMa>CcR5&wXAW3=CM%WXS$tmc?_1IAM4GCtO?# zUt6DY8E~1aU$U{dWgYy%>7u{7h`8v5#A>EfEUa=B4?AkFlk44ofy`glX(f`SO)lnxB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^GrVRPyy zg#$GUmjw9*GyF#b)28jqyJ`=#Psh{6F(jh(=(O8>hYWa}x0~JmUvI=Y`}S6ko*Nv8 zgoSK(tSpY4HalYZj^BU1r)bvk-&l5nIp@z3Ml)9_h35`+$4dA9vuD;=x~;%TO5pj# z0}4+V>+0;4Iam(N>YQ_c@!MlIE(N0%j9x!QCfFIc%QBksI&7M-$K=5L-Hc}cC$Sx= zw$}~kXI{d%K}Tl}+k%V$mrRCJVySc3{?zlG@GW`u=!i@J`^o?552w#Fta*}pa?8iO z)b*-t4M~ec7yQ4>b>V*|Yewc4CX>)8$CZnk7HO?m)#NRdDWAb*ATA<*;M5xaD8q?% zFP@&!oN@ShxBAiiBk#BwH}IY4-xbJcsJ@PCMeRauh3~D)8t#fOXt)Q&_r(`DFwUrI w_4Qpe_xz4*JA=Gk44ofy`glX(f`SO)lnxB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^GrVRPyy zg#$GUmjw9*GyF#b)28jqyJ`=#PuJ7MF(jh(==9rshYWa}x5xbYKmSb7tf`?M!8u0` zar3`;pqlqM_qOj_rFUw(S5z8L-F?5F^#+Ru%M7kF8m#xCltow$%7zQDd=YeVRM)t1 zBtW1=BZiUL;G5u_+(o^HX6M-NvbFF_Xj@m$SW`6T{^h=nvwwbE`lrA4hU0|AnH&mf zcR5!y%_tQ}U^RT5A-3j0MH5Q`S4eNLf|r&+SmP!xyICB+8-fMwB;1;6Cxp4x7Raex zVE=59%=K-l1MB90{2!LSV%S}GiXnOx&w^@)e#2r1sa1RumI}JDY$<#voI}}rWG4J( zWVn&3q#>QocVaa!iwnDD!-0?go;&PidL^hno3nk*9pMkZo!)W&s8!VEb-%k){lJFW xcbpdQ?Dwg^@Gjf=w9)pU-mK^4Pwz^^KUmA-`R9bpabUPIc)I$ztaD0e0st6Kx+VYs literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Walls/xenoborg.rsi/xenoborg5.png b/Resources/Textures/Structures/Walls/xenoborg.rsi/xenoborg5.png new file mode 100644 index 0000000000000000000000000000000000000000..4e1df9aa5763aa2cb52bd9dc11e0c3f32876912c GIT binary patch literal 525 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`SO)lnxB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^GrVRPyy zg#$GUmjw9*GyF#b)28jqyJ`=#FWJ+@F(jh(?sU(xBL*BU(F!I1mCc(cKK-)Dd17c* z?!Uu5kG}p=QWTnL(y@Ha$uDnz^Y8r2`@qV<@4%eYh8cPdYqq{>SlO1upxiNq@zvKW z4tz#TpSYh$9p>a=Da>KzVz}U3df;i*DxMP$mcC-x8sE;)u)Lm|Y0U>Y?WZ|PZL?oy zJ&xaAeDlHbADk<8%N+D#5HRum%9ikVJr~2h|BMd5?VCBi*DvFL_g`8-i+RUuN4CJX ztXxUg4tDeg`7eGiXm;Spulskxs+oVdX)@dV+{Pi(u%u95Xgj;x4c-|74}P#V)-9BE znf-UI$upY{r%6?++q`$)^{HO7`0bp>Hm?`*JlK0`k=&_k46J`FC;U5Jeb9STvA#?8!S}5)n_g5a9=EB}H{tL5cJi{_ zu98jfH}mfjmupuq(J%3jSF&9Hy-MJyc?gr;k44ofy`glX(f`SO)lnxB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^GrVRPyy zg#$GUmjw9*GyF#b)28jqyJ`=#PuJ7MF(jh(==9rshYWa}x5xbYKmSb7tf`?M!8u0` zar3`;pqlqM_qOj_rFUw(S5z8L-F?5F^#+Ru%M7kF8m#xCltow$%7zQDd=YeVRM)t1 zBtW1=BZiUL;G5u_+(o^HX6M-NvbFF_Xj@m$SW`6T{^h=nvwwbE`lrA4hU0|AnH&mf zcR5!y%_tQ}U^RT5A-3j0MH5Q`S4eNLf|r&+SmP!xyICB+8-fMwB;1;6Cxp4x7Raex zVE=59%=K-l1MB90{2!LSV%S}GiXnOx&w^@)e#2r1sa1RumI}JDY$<#voI}}rWG4J( zWVn&3q#>QocVaa!iwnDD!-0?go;&PidL^hno3nk*9pMkZo!)W&s8!VEb-%k){lJFW xcbpdQ?Dwg^@Gjf=w9)pU-mK^4Pwz^^KUmA-`R9bpabUPIc)I$ztaD0e0st6Kx+VYs literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Walls/xenoborg.rsi/xenoborg7.png b/Resources/Textures/Structures/Walls/xenoborg.rsi/xenoborg7.png new file mode 100644 index 0000000000000000000000000000000000000000..231c5fda379415c4b56741c087326207a0b8b423 GIT binary patch literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`SO)lnxB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^GrVRPyy zg#$GUmjw9*GyF#b)28jqyJ`=VZu4|;42fucJLN3b0R^7sqyP5&SLS~nBGDuCDmA+3 zrK|l0HBHqC9kW(nsXF`OkFdkB7RFU?9|`dkJscX e$nU$&&pt0qkk?$}+5(`97(8A5T-G@yGywpY^l`NS literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Walls/xenoborg_diagonal.rsi/meta.json b/Resources/Textures/Structures/Walls/xenoborg_diagonal.rsi/meta.json new file mode 100644 index 0000000000..f680988301 --- /dev/null +++ b/Resources/Textures/Structures/Walls/xenoborg_diagonal.rsi/meta.json @@ -0,0 +1,19 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Created by Samuka-C (github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "state0", + "directions": 4 + }, + { + "name": "state1", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Structures/Walls/xenoborg_diagonal.rsi/state0.png b/Resources/Textures/Structures/Walls/xenoborg_diagonal.rsi/state0.png new file mode 100644 index 0000000000000000000000000000000000000000..b83968aa6b2f277bf731d79f0a80aab80888eac3 GIT binary patch literal 3706 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 Yf0K5*x!U3XJ^%m!07*qoM6N<$f)|7^0RR91 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Walls/xenoborg_diagonal.rsi/state1.png b/Resources/Textures/Structures/Walls/xenoborg_diagonal.rsi/state1.png new file mode 100644 index 0000000000000000000000000000000000000000..60832b5cd0e21e6d4f9d40558b2ddd0ec0e3d8fc GIT binary patch literal 3817 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 T=+Ec`dX>S`)z4*}Q$iB}MxdWj literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Windows/xenoborg.rsi/meta.json b/Resources/Textures/Structures/Windows/xenoborg.rsi/meta.json new file mode 100644 index 0000000000..80f4c3f703 --- /dev/null +++ b/Resources/Textures/Structures/Windows/xenoborg.rsi/meta.json @@ -0,0 +1,46 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Created by Samuka-C (github).", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "full" + }, + { + "name": "xenoborg0", + "directions": 4 + }, + { + "name": "xenoborg1", + "directions": 4 + }, + { + "name": "xenoborg2", + "directions": 4 + }, + { + "name": "xenoborg3", + "directions": 4 + }, + { + "name": "xenoborg4", + "directions": 4 + }, + { + "name": "xenoborg5", + "directions": 4 + }, + { + "name": "xenoborg6", + "directions": 4 + }, + { + "name": "xenoborg7", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Structures/Windows/xenoborg.rsi/xenoborg0.png b/Resources/Textures/Structures/Windows/xenoborg.rsi/xenoborg0.png new file mode 100644 index 0000000000000000000000000000000000000000..7d733f13a4505fd5d58a0d507795331f00ced02e GIT binary patch literal 497 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`*a!H8xB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^IB%&qkGky{uplTf zP3&uv=w}I@kLkDLlIoA#{(HZn*#21r`;LEel4J#b6-^UlI3WM}5c4@l^MW^~%*;#T zbeR}Sn59@+8Dif9iL(vep)4f=Ror_|)HB#Ndp=<3V!Wkpa9)9x<9}VHsD~RzR@U_S z8eYs_E{Sspw4AVD?r;{a61c!&;2^!=rC@{j+=)(S9DSXP8Pq=-HCU}U%)s>G>TXE~ zhH!RIW`=~kqEeZwk44ofy`glX(f`*a!H8xB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^IB%&qsz%l3~qU-P=Vt)0W$=I?*qZ>o0=>}-*?m{!1C!n;oOPQ$8WGG#qn4411IW?cWx zD0uPD7i``hU?k44ofy`glX(f`*a!H8xB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^IB%&qkGky{uplTf zP3&uv=w}I@kLkDLlIoA#{(HZn*#21r`;LEel4J#b6-^UlI3WM}5c4@l^MW^~%*;#T zbeR}Sn59@+8Dif9iL(vep)4f=Ror_|)HB#Ndp=<3V!Wkpa9)9x<9}VHsD~RzR@U_S z8eYs_E{Sspw4AVD?r;{a61c!&;2^!=rC@{j+=)(S9DSXP8Pq=-HCU}U%)s>G>TXE~ zhH!RIW`=~kqEeZwk44ofy`glX(f`*a!H8xB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^IB%&qsz%l3~qU-P=Vt)0W$=I?*qZ>o0=>}-*?m{!1C!n;oOPQ$8WGG#qn4411IW?cWx zD0uPD7i``hU?k44ofy`glX(f`*a!H8xB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^IB%&qfFbk&Lvcg=K|Tl8B`T+)4?K48R}wkE z$|_)W@ifOi;r_5=i&Ot>pL%w6DA$C`yOw^vVtbF3HHeLk17A(gc)_GqK$f`)x{TI|z{56k44ofy`glX(f`*a!H8xB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^IB%&qidAEo`M`Nqp7 zF@I0`+q#Bax&C_L(sI8eE=LaCnpgM#YFfRSBF_`;i3Q9XII{jL9pYhL$>==6!Xd;f zQqV(U#s9e0W<`?+D(s7xxcSb!S?F1EG1>EpZwK>i1_tdv!3#JgHiUgLJ`l}s@?@qZ zgT=XBxBvd{`6(^Ybih(TUxA@}&O1A14{0_FMn;|LnAZZ0&Kr0#OxcwtC}tQgpYlkM z=U{_2-MEUMlrV&<$vqRd}jJhI~uKIBmQ ztyBEafvI5}7rf2@4LSCBc2zGI^S3|$W9980?R53}6K%T{7#j?pu6{1-oD!M<#|73p literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Windows/xenoborg.rsi/xenoborg6.png b/Resources/Textures/Structures/Windows/xenoborg.rsi/xenoborg6.png new file mode 100644 index 0000000000000000000000000000000000000000..96aa56b31be2f70948efd99d2aacaaa8015da35c GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`*a!H8xB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^IB%&qfFbk&Lvcg=K|Tl8B`T+)4?K48R}wkE z$|_)W@ifOi;r_5=i&Ot>pL%w6DA$C`yOw^vVtbF3HHeLk17A(gc)_GqK$f`)x{TI|z{56k44ofy`glX(f`*a!H8xB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^IB%&qO?JGUe&D{EHI) z|0S|(%hr_GZ7$_y|F|c#HZUB~TinnZz-}W{!20*&17}HtFV&)@tha2{uQ2K}F-UCn zXX2W%m%*1ue&ITWKTH=~>f;@R*S6mE^l(&o!f?+1c|7Bs|H{wn*%Rap_-C-6X|`AR h-1zfAjfIkY-sPztR}UF+{RcXV!PC{xWt~$(698^9b_M_d literal 0 HcmV?d00001 diff --git a/Resources/Textures/Tiles/attributions.yml b/Resources/Textures/Tiles/attributions.yml index 4999508701..e1318a223e 100644 --- a/Resources/Textures/Tiles/attributions.yml +++ b/Resources/Textures/Tiles/attributions.yml @@ -155,3 +155,8 @@ license: "CC0-1.0" copyright: "Created by SeaWyrm" source: "https://github.com/space-wizards/space-station-14/pull/38007" + +- files: ["exoborg.png"] + license: "CC0-1.0" + copyright: "Created by Samuka-C (github) for space-station-14." + source: "https://github.com/space-wizards/space-station-14/pull/37068" diff --git a/Resources/Textures/Tiles/exoborg.png b/Resources/Textures/Tiles/exoborg.png new file mode 100644 index 0000000000000000000000000000000000000000..6f086e185cb93b60f52bebaa84be0e0f6987e4b4 GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|j01c^T!FNZ zkc6_Ly0M|Do3ndJP)I~rL{>&teSJL_m#ZLOrVw|(BJWLC-Ct*qpMGmo8aQP87S%L>Eak-(fjt2As^5M&VfHF{u{sUi1kQsUCCL=-5XjPmer?q z3rJ`e^QlxbDJ5`qG-xU?EoLm50>{an^L HB{Ts5*f3BN literal 0 HcmV?d00001 From 021adbe1e1e5df2146ec51fc2fdaa5bd0d9e08d7 Mon Sep 17 00:00:00 2001 From: Winkarst-cpu <74284083+Winkarst-cpu@users.noreply.github.com> Date: Tue, 19 Aug 2025 20:56:36 +0300 Subject: [PATCH 003/108] New Feature: Kitchen spike rework (#38723) * Start * Wow, text * Ultra raw * More stuff * Wow, DOT and gibbing!!! * More stuff * More * Update * Yes * Almost there * Done? * I forgot * Update * Update * Update * Update * Update * Update * Update * Update * Update * Beck * Unhardcode --- Content.Client/Kitchen/KitchenSpikeSystem.cs | 8 - .../Botany/Systems/BotanySystem.Seed.cs | 2 +- Content.Server/Botany/Systems/LogSystem.cs | 2 +- .../Botany/Systems/PlantHolderSystem.cs | 2 +- .../Kitchen/Components/SharpComponent.cs | 15 - .../EntitySystems/KitchenSpikeSystem.cs | 292 ------------ .../Kitchen/EntitySystems/SharpSystem.cs | 2 +- .../Components/KitchenSpikeComponent.cs | 145 +++++- .../Components/KitchenSpikeHookedComponent.cs | 10 + .../Components/KitchenSpikeVictimComponent.cs | 10 + .../Kitchen/Components/SharpComponent.cs | 26 ++ .../Kitchen/SharedKitchenSpikeSystem.cs | 438 +++++++++++++++++- .../Components/ButcherableComponent.cs | 67 ++- .../components/kitchen-spike-component.ftl | 43 +- .../Entities/Structures/meat_spike.yml | 5 +- .../SoundCollections/kitchenspike.yml | 9 + 16 files changed, 688 insertions(+), 388 deletions(-) delete mode 100644 Content.Client/Kitchen/KitchenSpikeSystem.cs delete mode 100644 Content.Server/Kitchen/Components/SharpComponent.cs delete mode 100644 Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs create mode 100644 Content.Shared/Kitchen/Components/KitchenSpikeHookedComponent.cs create mode 100644 Content.Shared/Kitchen/Components/KitchenSpikeVictimComponent.cs create mode 100644 Content.Shared/Kitchen/Components/SharpComponent.cs create mode 100644 Resources/Prototypes/SoundCollections/kitchenspike.yml diff --git a/Content.Client/Kitchen/KitchenSpikeSystem.cs b/Content.Client/Kitchen/KitchenSpikeSystem.cs deleted file mode 100644 index 3627a29fa9..0000000000 --- a/Content.Client/Kitchen/KitchenSpikeSystem.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Content.Shared.Kitchen; - -namespace Content.Client.Kitchen; - -public sealed class KitchenSpikeSystem : SharedKitchenSpikeSystem -{ - -} diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index fd65c141aa..6b26ce7119 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -1,5 +1,4 @@ using Content.Server.Botany.Components; -using Content.Server.Kitchen.Components; using Content.Server.Popups; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Botany; @@ -16,6 +15,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.Administration.Logs; using Content.Shared.Database; +using Content.Shared.Kitchen.Components; namespace Content.Server.Botany.Systems; diff --git a/Content.Server/Botany/Systems/LogSystem.cs b/Content.Server/Botany/Systems/LogSystem.cs index 3d415635be..08190af708 100644 --- a/Content.Server/Botany/Systems/LogSystem.cs +++ b/Content.Server/Botany/Systems/LogSystem.cs @@ -1,7 +1,7 @@ using Content.Server.Botany.Components; -using Content.Server.Kitchen.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; +using Content.Shared.Kitchen.Components; using Content.Shared.Random; using Robust.Shared.Containers; diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index d8381b2a79..e38c742fa2 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Botany.Components; using Content.Server.Hands.Systems; -using Content.Server.Kitchen.Components; using Content.Server.Popups; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Atmos; @@ -26,6 +25,7 @@ using Robust.Shared.Timing; using Content.Shared.Administration.Logs; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; +using Content.Shared.Kitchen.Components; using Content.Shared.Labels.Components; namespace Content.Server.Botany.Systems; diff --git a/Content.Server/Kitchen/Components/SharpComponent.cs b/Content.Server/Kitchen/Components/SharpComponent.cs deleted file mode 100644 index c67c3b8a4d..0000000000 --- a/Content.Server/Kitchen/Components/SharpComponent.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Content.Server.Kitchen.Components; - -/// -/// Applies to items that are capable of butchering entities, or -/// are otherwise sharp for some purpose. -/// -[RegisterComponent] -public sealed partial class SharpComponent : Component -{ - // TODO just make this a tool type. - public HashSet Butchering = new(); - - [DataField("butcherDelayModifier")] - public float ButcherDelayModifier = 1.0f; -} diff --git a/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs b/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs deleted file mode 100644 index 4ed05a3f16..0000000000 --- a/Content.Server/Kitchen/EntitySystems/KitchenSpikeSystem.cs +++ /dev/null @@ -1,292 +0,0 @@ -using Content.Server.Administration.Logs; -using Content.Server.Body.Systems; -using Content.Server.Kitchen.Components; -using Content.Server.Popups; -using Content.Shared.Chat; -using Content.Shared.Damage; -using Content.Shared.Database; -using Content.Shared.DoAfter; -using Content.Shared.DragDrop; -using Content.Shared.Humanoid; -using Content.Shared.IdentityManagement; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Kitchen; -using Content.Shared.Kitchen.Components; -using Content.Shared.Mobs.Components; -using Content.Shared.Mobs.Systems; -using Content.Shared.Nutrition.Components; -using Content.Shared.Popups; -using Content.Shared.Storage; -using Robust.Server.GameObjects; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; -using Robust.Shared.Random; -using static Content.Shared.Kitchen.Components.KitchenSpikeComponent; - -namespace Content.Server.Kitchen.EntitySystems -{ - public sealed class KitchenSpikeSystem : SharedKitchenSpikeSystem - { - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - [Dependency] private readonly IAdminLogManager _logger = default!; - [Dependency] private readonly MobStateSystem _mobStateSystem = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly TransformSystem _transform = default!; - [Dependency] private readonly BodySystem _bodySystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly MetaDataSystem _metaData = default!; - [Dependency] private readonly SharedSuicideSystem _suicide = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnInteractUsing); - SubscribeLocalEvent(OnInteractHand); - SubscribeLocalEvent(OnDragDrop); - - //DoAfter - SubscribeLocalEvent(OnDoAfter); - - SubscribeLocalEvent(OnSuicideByEnvironment); - - SubscribeLocalEvent(OnButcherableCanDrop); - } - - private void OnButcherableCanDrop(Entity entity, ref CanDropDraggedEvent args) - { - args.Handled = true; - args.CanDrop |= entity.Comp.Type != ButcheringType.Knife; - } - - /// - /// TODO: Update this so it actually meatspikes the user instead of applying lethal damage to them. - /// - private void OnSuicideByEnvironment(Entity entity, ref SuicideByEnvironmentEvent args) - { - if (args.Handled) - return; - - if (!TryComp(args.Victim, out var damageableComponent)) - return; - - _suicide.ApplyLethalDamage((args.Victim, damageableComponent), "Piercing"); - var othersMessage = Loc.GetString("comp-kitchen-spike-suicide-other", - ("victim", Identity.Entity(args.Victim, EntityManager)), - ("this", entity)); - _popupSystem.PopupEntity(othersMessage, args.Victim, Filter.PvsExcept(args.Victim), true); - - var selfMessage = Loc.GetString("comp-kitchen-spike-suicide-self", - ("this", entity)); - _popupSystem.PopupEntity(selfMessage, args.Victim, args.Victim); - args.Handled = true; - } - - private void OnDoAfter(Entity entity, ref SpikeDoAfterEvent args) - { - if (args.Args.Target == null) - return; - - if (TryComp(args.Args.Target.Value, out var butcherable)) - butcherable.BeingButchered = false; - - if (args.Cancelled) - { - entity.Comp.InUse = false; - return; - } - - if (args.Handled) - return; - - if (Spikeable(entity, args.Args.User, args.Args.Target.Value, entity.Comp, butcherable)) - Spike(entity, args.Args.User, args.Args.Target.Value, entity.Comp); - - entity.Comp.InUse = false; - args.Handled = true; - } - - private void OnDragDrop(Entity entity, ref DragDropTargetEvent args) - { - if (args.Handled) - return; - - args.Handled = true; - - if (Spikeable(entity, args.User, args.Dragged, entity.Comp)) - TrySpike(entity, args.User, args.Dragged, entity.Comp); - } - - private void OnInteractHand(Entity entity, ref InteractHandEvent args) - { - if (args.Handled) - return; - - if (entity.Comp.PrototypesToSpawn?.Count > 0) - { - _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-knife-needed"), entity, args.User); - args.Handled = true; - } - } - - private void OnInteractUsing(Entity entity, ref InteractUsingEvent args) - { - if (args.Handled) - return; - - if (TryGetPiece(entity, args.User, args.Used)) - args.Handled = true; - } - - private void Spike(EntityUid uid, EntityUid userUid, EntityUid victimUid, - KitchenSpikeComponent? component = null, ButcherableComponent? butcherable = null) - { - if (!Resolve(uid, ref component) || !Resolve(victimUid, ref butcherable)) - return; - - var logImpact = LogImpact.Medium; - if (HasComp(victimUid)) - logImpact = LogImpact.Extreme; - - _logger.Add(LogType.Gib, logImpact, $"{ToPrettyString(userUid):user} kitchen spiked {ToPrettyString(victimUid):target}"); - - // TODO VERY SUS - component.PrototypesToSpawn = EntitySpawnCollection.GetSpawns(butcherable.SpawnedEntities, _random); - - // This feels not okay, but entity is getting deleted on "Spike", for now... - component.MeatSource1p = Loc.GetString("comp-kitchen-spike-remove-meat", ("victim", victimUid)); - component.MeatSource0 = Loc.GetString("comp-kitchen-spike-remove-meat-last", ("victim", victimUid)); - component.Victim = Name(victimUid); - - UpdateAppearance(uid, null, component); - - _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-kill", - ("user", Identity.Entity(userUid, EntityManager)), - ("victim", Identity.Entity(victimUid, EntityManager)), - ("this", uid)), - uid, PopupType.LargeCaution); - - _transform.SetCoordinates(victimUid, Transform(uid).Coordinates); - // THE WHAT? - // TODO: Need to be able to leave them on the spike to do DoT, see ss13. - var gibs = _bodySystem.GibBody(victimUid); - foreach (var gib in gibs) { - QueueDel(gib); - } - - _audio.PlayPvs(component.SpikeSound, uid); - } - - private bool TryGetPiece(EntityUid uid, EntityUid user, EntityUid used, - KitchenSpikeComponent? component = null, SharpComponent? sharp = null) - { - if (!Resolve(uid, ref component) || component.PrototypesToSpawn == null || component.PrototypesToSpawn.Count == 0) - return false; - - // Is using knife - if (!Resolve(used, ref sharp, false) ) - { - return false; - } - - var item = _random.PickAndTake(component.PrototypesToSpawn); - - var ent = Spawn(item, Transform(uid).Coordinates); - _metaData.SetEntityName(ent, - Loc.GetString("comp-kitchen-spike-meat-name", ("name", Name(ent)), ("victim", component.Victim))); - - if (component.PrototypesToSpawn.Count != 0) - _popupSystem.PopupEntity(component.MeatSource1p, uid, user, PopupType.MediumCaution); - else - { - UpdateAppearance(uid, null, component); - _popupSystem.PopupEntity(component.MeatSource0, uid, user, PopupType.MediumCaution); - } - - return true; - } - - private void UpdateAppearance(EntityUid uid, AppearanceComponent? appearance = null, KitchenSpikeComponent? component = null) - { - if (!Resolve(uid, ref component, ref appearance, false)) - return; - - _appearance.SetData(uid, KitchenSpikeVisuals.Status, component.PrototypesToSpawn?.Count > 0 ? KitchenSpikeStatus.Bloody : KitchenSpikeStatus.Empty, appearance); - } - - private bool Spikeable(EntityUid uid, EntityUid userUid, EntityUid victimUid, - KitchenSpikeComponent? component = null, ButcherableComponent? butcherable = null) - { - if (!Resolve(uid, ref component)) - return false; - - if (component.PrototypesToSpawn?.Count > 0) - { - _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-deny-collect", ("this", uid)), uid, userUid); - return false; - } - - if (!Resolve(victimUid, ref butcherable, false)) - { - _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-deny-butcher", ("victim", Identity.Entity(victimUid, EntityManager)), ("this", uid)), victimUid, userUid); - return false; - } - - switch (butcherable.Type) - { - case ButcheringType.Spike: - return true; - case ButcheringType.Knife: - _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-deny-butcher-knife", ("victim", Identity.Entity(victimUid, EntityManager)), ("this", uid)), victimUid, userUid); - return false; - default: - _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-deny-butcher", ("victim", Identity.Entity(victimUid, EntityManager)), ("this", uid)), victimUid, userUid); - return false; - } - } - - public bool TrySpike(EntityUid uid, EntityUid userUid, EntityUid victimUid, KitchenSpikeComponent? component = null, - ButcherableComponent? butcherable = null, MobStateComponent? mobState = null) - { - if (!Resolve(uid, ref component) || component.InUse || - !Resolve(victimUid, ref butcherable) || butcherable.BeingButchered) - return false; - - // THE WHAT? (again) - // Prevent dead from being spiked TODO: Maybe remove when rounds can be played and DOT is implemented - if (Resolve(victimUid, ref mobState, false) && - _mobStateSystem.IsAlive(victimUid, mobState)) - { - _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-deny-not-dead", ("victim", Identity.Entity(victimUid, EntityManager))), - victimUid, userUid); - return true; - } - - if (userUid != victimUid) - { - _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-begin-hook-victim", ("user", Identity.Entity(userUid, EntityManager)), ("this", uid)), victimUid, victimUid, PopupType.LargeCaution); - } - // TODO: make it work when SuicideEvent is implemented - // else - // _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-begin-hook-self", ("this", uid)), victimUid, Filter.Pvs(uid)); // This is actually unreachable and should be in SuicideEvent - - butcherable.BeingButchered = true; - component.InUse = true; - - var doAfterArgs = new DoAfterArgs(EntityManager, userUid, component.SpikeDelay + butcherable.ButcherDelay, new SpikeDoAfterEvent(), uid, target: victimUid, used: uid) - { - BreakOnDamage = true, - BreakOnMove = true, - NeedHand = true, - BreakOnDropItem = false, - }; - - _doAfter.TryStartDoAfter(doAfterArgs); - - return true; - } - } -} diff --git a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs index 0275e4d1a7..ab6e1db494 100644 --- a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Body.Systems; -using Content.Server.Kitchen.Components; using Content.Shared.Administration.Logs; using Content.Shared.Body.Components; using Content.Shared.Database; @@ -8,6 +7,7 @@ using Content.Shared.DoAfter; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Kitchen; +using Content.Shared.Kitchen.Components; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Nutrition.Components; diff --git a/Content.Shared/Kitchen/Components/KitchenSpikeComponent.cs b/Content.Shared/Kitchen/Components/KitchenSpikeComponent.cs index 3057a75a4c..b4fdc5ed3c 100644 --- a/Content.Shared/Kitchen/Components/KitchenSpikeComponent.cs +++ b/Content.Shared/Kitchen/Components/KitchenSpikeComponent.cs @@ -1,40 +1,143 @@ +using Content.Shared.Damage; +using Content.Shared.FixedPoint; +using Content.Shared.Nutrition.Components; using Robust.Shared.Audio; +using Robust.Shared.Containers; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Kitchen.Components; +/// +/// Used to mark entity that should act as a spike. +/// [RegisterComponent, NetworkedComponent] +[AutoGenerateComponentState, AutoGenerateComponentPause] [Access(typeof(SharedKitchenSpikeSystem))] public sealed partial class KitchenSpikeComponent : Component { - [DataField("delay")] - public float SpikeDelay = 7.0f; + /// + /// Default sound to play when the victim is hooked or unhooked. + /// + private static readonly ProtoId DefaultSpike = new("Spike"); - [ViewVariables(VVAccess.ReadWrite)] - [DataField("sound")] - public SoundSpecifier SpikeSound = new SoundPathSpecifier("/Audio/Effects/Fluids/splat.ogg"); + /// + /// Default sound to play when the victim is butchered. + /// + private static readonly ProtoId DefaultSpikeButcher = new("SpikeButcher"); - public List? PrototypesToSpawn; + /// + /// ID of the container where the victim will be stored. + /// + [DataField, AutoNetworkedField] + public string ContainerId = "body"; - // TODO: Spiking alive mobs? (Replace with uid) (deal damage to their limbs on spiking, kill on first butcher attempt?) - public string MeatSource1p = "?"; - public string MeatSource0 = "?"; - public string Victim = "?"; + /// + /// Container where the victim will be stored. + /// + [ViewVariables] + public ContainerSlot BodyContainer = default!; - // Prevents simultaneous spiking of two bodies (could be replaced with CancellationToken, but I don't see any situation where Cancel could be called) - public bool InUse; + /// + /// Sound to play when the victim is hooked or unhooked. + /// + [DataField, AutoNetworkedField] + public SoundSpecifier SpikeSound = new SoundCollectionSpecifier(DefaultSpike); - [Serializable, NetSerializable] - public enum KitchenSpikeVisuals : byte + /// + /// Sound to play when the victim is butchered. + /// + [DataField, AutoNetworkedField] + public SoundSpecifier ButcherSound = new SoundCollectionSpecifier(DefaultSpikeButcher); + + /// + /// Damage that will be applied to the victim when they are hooked or unhooked. + /// + [DataField, AutoNetworkedField] + public DamageSpecifier SpikeDamage = new() { - Status - } + DamageDict = new Dictionary + { + { "Piercing", 10 }, + }, + }; - [Serializable, NetSerializable] - public enum KitchenSpikeStatus : byte + /// + /// Damage that will be applied to the victim when they are butchered. + /// + [DataField, AutoNetworkedField] + public DamageSpecifier ButcherDamage = new() { - Empty, - Bloody - } + DamageDict = new Dictionary + { + { "Slash", 20 }, + }, + }; + + /// + /// Damage that the victim will receive over time. + /// + [DataField, AutoNetworkedField] + public DamageSpecifier TimeDamage = new() + { + DamageDict = new Dictionary + { + { "Blunt", 1 }, // Mobs are only gibbed from blunt (at least for now). + }, + }; + + /// + /// The next time when the damage will be applied to the victim. + /// + [AutoPausedField, AutoNetworkedField] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan NextDamage; + + /// + /// How often the damage should be applied to the victim. + /// + [DataField, AutoNetworkedField] + public TimeSpan DamageInterval = TimeSpan.FromSeconds(10); + + /// + /// Time that it will take to put the victim on the spike. + /// + [DataField, AutoNetworkedField] + public TimeSpan HookDelay = TimeSpan.FromSeconds(7); + + /// + /// Time that it will take to put the victim off the spike. + /// + [DataField, AutoNetworkedField] + public TimeSpan UnhookDelay = TimeSpan.FromSeconds(10); + + /// + /// Time that it will take to butcher the victim while they are alive. + /// + /// + /// This is summed up with a 's butcher delay in butcher DoAfter. + /// + [DataField, AutoNetworkedField] + public TimeSpan ButcherDelayAlive = TimeSpan.FromSeconds(8); + + /// + /// Value by which the butchering delay will be multiplied if the victim is dead. + /// + [DataField, AutoNetworkedField] + public float ButcherModifierDead = 0.5f; +} + +[Serializable, NetSerializable] +public enum KitchenSpikeVisuals : byte +{ + Status, +} + +[Serializable, NetSerializable] +public enum KitchenSpikeStatus : byte +{ + Empty, + Bloody, // TODO: Add sprites for different species. } diff --git a/Content.Shared/Kitchen/Components/KitchenSpikeHookedComponent.cs b/Content.Shared/Kitchen/Components/KitchenSpikeHookedComponent.cs new file mode 100644 index 0000000000..c255db986e --- /dev/null +++ b/Content.Shared/Kitchen/Components/KitchenSpikeHookedComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Kitchen.Components; + +/// +/// Used to mark entities that are currently hooked on the spike. +/// +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedKitchenSpikeSystem))] +public sealed partial class KitchenSpikeHookedComponent : Component; diff --git a/Content.Shared/Kitchen/Components/KitchenSpikeVictimComponent.cs b/Content.Shared/Kitchen/Components/KitchenSpikeVictimComponent.cs new file mode 100644 index 0000000000..dc37592a87 --- /dev/null +++ b/Content.Shared/Kitchen/Components/KitchenSpikeVictimComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Kitchen.Components; + +/// +/// Used to mark entity that was butchered on the spike. +/// +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedKitchenSpikeSystem))] +public sealed partial class KitchenSpikeVictimComponent : Component; diff --git a/Content.Shared/Kitchen/Components/SharpComponent.cs b/Content.Shared/Kitchen/Components/SharpComponent.cs new file mode 100644 index 0000000000..3dd5e01af7 --- /dev/null +++ b/Content.Shared/Kitchen/Components/SharpComponent.cs @@ -0,0 +1,26 @@ +using Content.Shared.Nutrition.Components; +using Robust.Shared.GameStates; + +namespace Content.Shared.Kitchen.Components; + +/// +/// Applies to items that are capable of butchering entities, or +/// are otherwise sharp for some purpose. +/// +[RegisterComponent, NetworkedComponent] +[AutoGenerateComponentState] +public sealed partial class SharpComponent : Component +{ + /// + /// List of the entities that are currently being butchered. + /// + // TODO just make this a tool type. Move SharpSystem to shared. + [AutoNetworkedField] + public readonly HashSet Butchering = []; + + /// + /// Affects butcher delay of the . + /// + [DataField, AutoNetworkedField] + public float ButcherDelayModifier = 1.0f; +} diff --git a/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs b/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs index 2f740f99ad..57b08569f5 100644 --- a/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs +++ b/Content.Shared/Kitchen/SharedKitchenSpikeSystem.cs @@ -1,38 +1,456 @@ +using Content.Shared.Administration.Logs; +using Content.Shared.Body.Systems; +using Content.Shared.Damage; +using Content.Shared.Database; +using Content.Shared.Destructible; using Content.Shared.DoAfter; using Content.Shared.DragDrop; +using Content.Shared.Examine; +using Content.Shared.Hands; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Inventory.Events; +using Content.Shared.Item; using Content.Shared.Kitchen.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.Movement.Events; using Content.Shared.Nutrition.Components; +using Content.Shared.Popups; +using Content.Shared.Throwing; +using Content.Shared.Verbs; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Containers; +using Robust.Shared.Random; using Robust.Shared.Serialization; +using Robust.Shared.Timing; namespace Content.Shared.Kitchen; -public abstract class SharedKitchenSpikeSystem : EntitySystem +/// +/// Used to butcher some entities like monkeys. +/// +public sealed class SharedKitchenSpikeSystem : EntitySystem { + [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; + [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly ExamineSystemShared _examineSystem = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly MetaDataSystem _metaDataSystem = default!; + [Dependency] private readonly ISharedAdminLogManager _logger = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly SharedBodySystem _bodySystem = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IRobustRandom _random = default!; + public override void Initialize() { base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnInsertAttempt); + SubscribeLocalEvent(OnEntInsertedIntoContainer); + SubscribeLocalEvent(OnEntRemovedFromContainer); + SubscribeLocalEvent(OnInteractHand); + SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnCanDrop); + SubscribeLocalEvent(OnDragDrop); + SubscribeLocalEvent(OnSpikeHookDoAfter); + SubscribeLocalEvent(OnSpikeUnhookDoAfter); + SubscribeLocalEvent(OnSpikeButcherDoAfter); + SubscribeLocalEvent(OnSpikeExamined); + SubscribeLocalEvent>(OnGetVerbs); + SubscribeLocalEvent(OnDestruction); + + SubscribeLocalEvent(OnVictimExamined); + + // Prevent the victim from doing anything while on the spike. + SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnAttempt); } - private void OnCanDrop(EntityUid uid, KitchenSpikeComponent component, ref CanDropTargetEvent args) + private void OnInit(Entity ent, ref ComponentInit args) { - if (args.Handled) + ent.Comp.BodyContainer = _containerSystem.EnsureContainer(ent, ent.Comp.ContainerId); + } + + private void OnInsertAttempt(Entity ent, ref ContainerIsInsertingAttemptEvent args) + { + if (args.Cancelled || TryComp(args.EntityUid, out var butcherable) && butcherable.Type == ButcheringType.Spike) + return; + + args.Cancel(); + } + + private void OnEntInsertedIntoContainer(Entity ent, ref EntInsertedIntoContainerMessage args) + { + EnsureComp(args.Entity); + _damageableSystem.TryChangeDamage(args.Entity, ent.Comp.SpikeDamage, true); + + // TODO: Add sprites for different species. + _appearanceSystem.SetData(ent.Owner, KitchenSpikeVisuals.Status, KitchenSpikeStatus.Bloody); + } + + private void OnEntRemovedFromContainer(Entity ent, ref EntRemovedFromContainerMessage args) + { + RemComp(args.Entity); + _damageableSystem.TryChangeDamage(args.Entity, ent.Comp.SpikeDamage, true); + + _appearanceSystem.SetData(ent.Owner, KitchenSpikeVisuals.Status, KitchenSpikeStatus.Empty); + } + + private void OnInteractHand(Entity ent, ref InteractHandEvent args) + { + var victim = ent.Comp.BodyContainer.ContainedEntity; + + if (args.Handled || !victim.HasValue) + return; + + _popupSystem.PopupClient(Loc.GetString("butcherable-need-knife", + ("target", Identity.Entity(victim.Value, EntityManager))), + ent, + args.User, + PopupType.Medium); + + args.Handled = true; + } + + private void OnInteractUsing(Entity ent, ref InteractUsingEvent args) + { + var victim = ent.Comp.BodyContainer.ContainedEntity; + + if (args.Handled || !TryComp(victim, out var butcherable) || butcherable.SpawnedEntities.Count == 0) return; args.Handled = true; - if (!HasComp(args.Dragged)) + if (!TryComp(args.Used, out var sharp)) { - args.CanDrop = false; + _popupSystem.PopupClient(Loc.GetString("butcherable-need-knife", + ("target", Identity.Entity(victim.Value, EntityManager))), + ent, + args.User, + PopupType.Medium); + return; } - // TODO: Once we get silicons need to check organic - args.CanDrop = true; + var victimIdentity = Identity.Entity(victim.Value, EntityManager); + + _popupSystem.PopupPredicted(Loc.GetString("comp-kitchen-spike-begin-butcher-self", ("victim", victimIdentity)), + Loc.GetString("comp-kitchen-spike-begin-butcher", ("user", Identity.Entity(args.User, EntityManager)), ("victim", victimIdentity)), + ent, + args.User, + PopupType.MediumCaution); + + var delay = TimeSpan.FromSeconds(sharp.ButcherDelayModifier * butcherable.ButcherDelay); + + if (_mobStateSystem.IsAlive(victim.Value)) + delay += ent.Comp.ButcherDelayAlive; + else + delay *= ent.Comp.ButcherModifierDead; + + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, + args.User, + delay, + new SpikeButcherDoAfterEvent(), + ent, + target: victim, + used: args.Used) + { + BreakOnDamage = true, + BreakOnMove = true, + NeedHand = true, + }); + } + + private void OnCanDrop(Entity ent, ref CanDropTargetEvent args) + { + if (args.Handled) + return; + + args.CanDrop = _containerSystem.CanInsert(args.Dragged, ent.Comp.BodyContainer); + args.Handled = true; + } + + private void OnDragDrop(Entity ent, ref DragDropTargetEvent args) + { + if (args.Handled) + return; + + ShowPopups("comp-kitchen-spike-begin-hook-self", + "comp-kitchen-spike-begin-hook-self-other", + "comp-kitchen-spike-begin-hook-other-self", + "comp-kitchen-spike-begin-hook-other", + args.User, + args.Dragged, + ent); + + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, + args.User, + ent.Comp.HookDelay, + new SpikeHookDoAfterEvent(), + ent, + target: args.Dragged) + { + BreakOnDamage = true, + BreakOnMove = true, + NeedHand = true, + }); + + args.Handled = true; + } + + private void OnSpikeHookDoAfter(Entity ent, ref SpikeHookDoAfterEvent args) + { + if (args.Handled || args.Cancelled || !args.Target.HasValue) + return; + + if (_containerSystem.Insert(args.Target.Value, ent.Comp.BodyContainer)) + { + ShowPopups("comp-kitchen-spike-hook-self", + "comp-kitchen-spike-hook-self-other", + "comp-kitchen-spike-hook-other-self", + "comp-kitchen-spike-hook-other", + args.User, + args.Target.Value, + ent); + + _logger.Add(LogType.Action, + LogImpact.High, + $"{ToPrettyString(args.User):user} put {ToPrettyString(args.Target):target} on the {ToPrettyString(ent):spike}"); + + _audioSystem.PlayPredicted(ent.Comp.SpikeSound, ent, args.User); + } + + args.Handled = true; + } + + private void OnSpikeUnhookDoAfter(Entity ent, ref SpikeUnhookDoAfterEvent args) + { + if (args.Handled || args.Cancelled || !args.Target.HasValue) + return; + + if (_containerSystem.Remove(args.Target.Value, ent.Comp.BodyContainer)) + { + ShowPopups("comp-kitchen-spike-unhook-self", + "comp-kitchen-spike-unhook-self-other", + "comp-kitchen-spike-unhook-other-self", + "comp-kitchen-spike-unhook-other", + args.User, + args.Target.Value, + ent); + + _logger.Add(LogType.Action, + LogImpact.Medium, + $"{ToPrettyString(args.User):user} took {ToPrettyString(args.Target):target} off the {ToPrettyString(ent):spike}"); + + _audioSystem.PlayPredicted(ent.Comp.SpikeSound, ent, args.User); + } + + args.Handled = true; + } + + private void OnSpikeButcherDoAfter(Entity ent, ref SpikeButcherDoAfterEvent args) + { + if (args.Handled || args.Cancelled || !args.Target.HasValue || !args.Used.HasValue || !TryComp(args.Target, out var butcherable) ) + return; + + var victimIdentity = Identity.Entity(args.Target.Value, EntityManager); + + _popupSystem.PopupPredicted(Loc.GetString("comp-kitchen-spike-butcher-self", ("victim", victimIdentity)), + Loc.GetString("comp-kitchen-spike-butcher", ("user", Identity.Entity(args.User, EntityManager)), ("victim", victimIdentity)), + ent, + args.User, + PopupType.MediumCaution); + + // Get a random entry to spawn. + var index = _random.Next(butcherable.SpawnedEntities.Count); + var entry = butcherable.SpawnedEntities[index]; + + var uid = PredictedSpawnNextToOrDrop(entry.PrototypeId, ent); + _metaDataSystem.SetEntityName(uid, + Loc.GetString("comp-kitchen-spike-meat-name", + ("name", Name(uid)), + ("victim", args.Target))); + + // Decrease the amount since we spawned an entity from that entry. + entry.Amount--; + + // Remove the entry if its new amount is zero, or update it. + if (entry.Amount <= 0) + butcherable.SpawnedEntities.RemoveAt(index); + else + butcherable.SpawnedEntities[index] = entry; + + Dirty(args.Target.Value, butcherable); + + // Gib the victim if there is nothing else to butcher. + if (butcherable.SpawnedEntities.Count == 0) + { + _bodySystem.GibBody(args.Target.Value, true); + + _logger.Add(LogType.Gib, + LogImpact.Extreme, + $"{ToPrettyString(args.User):user} finished butchering {ToPrettyString(args.Target):target} on the {ToPrettyString(ent):spike}"); + } + else + { + EnsureComp(args.Target.Value); + + _damageableSystem.TryChangeDamage(args.Target, ent.Comp.ButcherDamage, true); + _logger.Add(LogType.Action, + LogImpact.Extreme, + $"{ToPrettyString(args.User):user} butchered {ToPrettyString(args.Target):target} on the {ToPrettyString(ent):spike}"); + } + + _audioSystem.PlayPredicted(ent.Comp.ButcherSound, ent, args.User); + + _popupSystem.PopupClient(Loc.GetString("butcherable-knife-butchered-success", + ("target", Identity.Entity(args.Target.Value, EntityManager)), + ("knife", args.Used.Value)), + ent, + args.User, + PopupType.Medium); + + args.Handled = true; + } + + private void OnSpikeExamined(Entity ent, ref ExaminedEvent args) + { + var victim = ent.Comp.BodyContainer.ContainedEntity; + + if (!victim.HasValue) + return; + + // Show it at the end of the examine so it looks good. + args.PushMarkup(Loc.GetString("comp-kitchen-spike-hooked", ("victim", Identity.Entity(victim.Value, EntityManager))), -1); + args.PushMessage(_examineSystem.GetExamineText(victim.Value, args.Examiner), -2); + } + + private void OnGetVerbs(Entity ent, ref GetVerbsEvent args) + { + var victim = ent.Comp.BodyContainer.ContainedEntity; + + if (!victim.HasValue || !_containerSystem.CanRemove(victim.Value, ent.Comp.BodyContainer)) + return; + + var user = args.User; + + args.Verbs.Add(new Verb() + { + Text = Loc.GetString("comp-kitchen-spike-unhook-verb"), + Act = () => TryUnhook(ent, user, victim.Value), + Impact = LogImpact.Medium, + }); + } + + private void OnDestruction(Entity ent, ref DestructionEventArgs args) + { + _containerSystem.EmptyContainer(ent.Comp.BodyContainer, destination: Transform(ent).Coordinates); + } + + private void OnVictimExamined(Entity ent, ref ExaminedEvent args) + { + args.PushMarkup(Loc.GetString("comp-kitchen-spike-victim-examine", ("target", Identity.Entity(ent, EntityManager)))); + } + + private static void OnAttempt(EntityUid uid, KitchenSpikeHookedComponent component, CancellableEntityEventArgs args) + { + args.Cancel(); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = AllEntityQuery(); + + while (query.MoveNext(out var uid, out var kitchenSpike)) + { + if (kitchenSpike.NextDamage > _gameTiming.CurTime) + continue; + + kitchenSpike.NextDamage += kitchenSpike.DamageInterval; + Dirty(uid, kitchenSpike); + + _damageableSystem.TryChangeDamage(kitchenSpike.BodyContainer.ContainedEntity, kitchenSpike.TimeDamage, true); + } + } + + /// + /// A helper method to show predicted popups that can be targeted towards yourself or somebody else. + /// + private void ShowPopups(string selfLocMessageSelf, + string selfLocMessageOthers, + string locMessageSelf, + string locMessageOthers, + EntityUid user, + EntityUid victim, + EntityUid hook) + { + string messageSelf, messageOthers; + + var victimIdentity = Identity.Entity(victim, EntityManager); + + if (user == victim) + { + messageSelf = Loc.GetString(selfLocMessageSelf, ("hook", hook)); + messageOthers = Loc.GetString(selfLocMessageOthers, ("victim", victimIdentity), ("hook", hook)); + } + else + { + messageSelf = Loc.GetString(locMessageSelf, ("victim", victimIdentity), ("hook", hook)); + messageOthers = Loc.GetString(locMessageOthers, + ("user", Identity.Entity(user, EntityManager)), + ("victim", victimIdentity), + ("hook", hook)); + } + + _popupSystem.PopupPredicted(messageSelf, messageOthers, hook, user, PopupType.MediumCaution); + } + + /// + /// Tries to unhook the victim. + /// + private void TryUnhook(Entity ent, EntityUid user, EntityUid target) + { + ShowPopups("comp-kitchen-spike-begin-unhook-self", + "comp-kitchen-spike-begin-unhook-self-other", + "comp-kitchen-spike-begin-unhook-other-self", + "comp-kitchen-spike-begin-unhook-other", + user, + target, + ent); + + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, + user, + ent.Comp.UnhookDelay, + new SpikeUnhookDoAfterEvent(), + ent, + target: target) + { + BreakOnDamage = user != target, + BreakOnMove = true, + }); } } [Serializable, NetSerializable] -public sealed partial class SpikeDoAfterEvent : SimpleDoAfterEvent -{ -} +public sealed partial class SpikeHookDoAfterEvent : SimpleDoAfterEvent; + +[Serializable, NetSerializable] +public sealed partial class SpikeUnhookDoAfterEvent : SimpleDoAfterEvent; + +[Serializable, NetSerializable] +public sealed partial class SpikeButcherDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/Nutrition/Components/ButcherableComponent.cs b/Content.Shared/Nutrition/Components/ButcherableComponent.cs index 4fce45422a..486026d259 100644 --- a/Content.Shared/Nutrition/Components/ButcherableComponent.cs +++ b/Content.Shared/Nutrition/Components/ButcherableComponent.cs @@ -1,34 +1,51 @@ +using Content.Shared.Kitchen; using Content.Shared.Storage; using Robust.Shared.GameStates; -namespace Content.Shared.Nutrition.Components +namespace Content.Shared.Nutrition.Components; + +/// +/// Indicates that the entity can be butchered. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ButcherableComponent : Component { /// - /// Indicates that the entity can be thrown on a kitchen spike for butchering. + /// List of the entities that this entity should spawn after being butchered. /// - [RegisterComponent, NetworkedComponent] - public sealed partial class ButcherableComponent : Component - { - [DataField("spawned", required: true)] - public List SpawnedEntities = new(); + /// + /// Note that spawns one item at a time and decreases the amount until it's zero and then removes the entry. + /// + [DataField("spawned", required: true), AutoNetworkedField] + public List SpawnedEntities = []; - [ViewVariables(VVAccess.ReadWrite), DataField("butcherDelay")] - public float ButcherDelay = 8.0f; + /// + /// Time required to butcher that entity. + /// + [DataField, AutoNetworkedField] + public float ButcherDelay = 8.0f; - [ViewVariables(VVAccess.ReadWrite), DataField("butcheringType")] - public ButcheringType Type = ButcheringType.Knife; - - /// - /// Prevents butchering same entity on two and more spikes simultaneously and multiple doAfters on the same Spike - /// - [ViewVariables] - public bool BeingButchered; - } - - public enum ButcheringType : byte - { - Knife, // e.g. goliaths - Spike, // e.g. monkeys - Gibber // e.g. humans. TODO - } + /// + /// Tool type used to butcher that entity. + /// + [DataField("butcheringType"), AutoNetworkedField] + public ButcheringType Type = ButcheringType.Knife; +} + +public enum ButcheringType : byte +{ + /// + /// E.g. goliaths. + /// + Knife, + + /// + /// E.g. monkeys. + /// + Spike, + + /// + /// E.g. humans. + /// + Gibber // TODO } diff --git a/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl b/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl index aaa1779f53..b620fdff8c 100644 --- a/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl +++ b/Resources/Locale/en-US/kitchen/components/kitchen-spike-component.ftl @@ -1,18 +1,37 @@ -comp-kitchen-spike-deny-collect = { CAPITALIZE(THE($this)) } already has something on it, finish collecting its meat first! -comp-kitchen-spike-deny-butcher = { CAPITALIZE(THE($victim)) } can't be butchered on { THE($this) }. -comp-kitchen-spike-deny-butcher-knife = { CAPITALIZE(THE($victim)) } can't be butchered on { THE($this) }, you need to butcher it using a knife. -comp-kitchen-spike-deny-not-dead = { CAPITALIZE(THE($victim)) } can't be butchered. { CAPITALIZE(SUBJECT($victim)) } { CONJUGATE-BE($victim) } not dead! +comp-kitchen-spike-begin-hook-self = You begin dragging yourself onto { THE($hook) }! +comp-kitchen-spike-begin-hook-self-other = { CAPITALIZE(THE($victim)) } begins dragging { REFLEXIVE($victim) } onto { THE($hook) }! -comp-kitchen-spike-begin-hook-victim = { CAPITALIZE(THE($user)) } begins dragging you onto { THE($this) }! -comp-kitchen-spike-begin-hook-self = You begin dragging yourself onto { THE($this) }! +comp-kitchen-spike-begin-hook-other-self = You begin dragging { CAPITALIZE(THE($victim)) } onto { THE($hook) }! +comp-kitchen-spike-begin-hook-other = { CAPITALIZE(THE($user)) } begins dragging { CAPITALIZE(THE($victim)) } onto { THE($hook) }!a -comp-kitchen-spike-kill = { CAPITALIZE(THE($user)) } has forced { THE($victim) } onto { THE($this) }, killing { OBJECT($victim) } instantly! +comp-kitchen-spike-hook-self = You threw yourself on { THE($hook) }! +comp-kitchen-spike-hook-self-other = { CAPITALIZE(THE($victim)) } threw { REFLEXIVE($victim) } on { THE($hook) }! -comp-kitchen-spike-suicide-other = { CAPITALIZE(THE($victim)) } threw { REFLEXIVE($victim) } on { THE($this) }! -comp-kitchen-spike-suicide-self = You throw yourself on { THE($this) }! +comp-kitchen-spike-hook-other-self = You threw { CAPITALIZE(THE($victim)) } on { THE($hook) }! +comp-kitchen-spike-hook-other = { CAPITALIZE(THE($user)) } threw { CAPITALIZE(THE($victim)) } on { THE($hook) }! -comp-kitchen-spike-knife-needed = You need a knife to do this. -comp-kitchen-spike-remove-meat = You remove some meat from { THE($victim) }. -comp-kitchen-spike-remove-meat-last = You remove the last piece of meat from { THE($victim) }! +comp-kitchen-spike-begin-unhook-self = You begin dragging yourself off { THE($hook) }! +comp-kitchen-spike-begin-unhook-self-other = { CAPITALIZE(THE($victim)) } begins dragging { REFLEXIVE($victim) } off { THE($hook) }! + +comp-kitchen-spike-begin-unhook-other-self = You begin dragging { CAPITALIZE(THE($victim)) } off { THE($hook) }! +comp-kitchen-spike-begin-unhook-other = { CAPITALIZE(THE($user)) } begins dragging { CAPITALIZE(THE($victim)) } off { THE($hook) }! + +comp-kitchen-spike-unhook-self = You got yourself off { THE($hook) }! +comp-kitchen-spike-unhook-self-other = { CAPITALIZE(THE($victim)) } got { REFLEXIVE($victim) } off { THE($hook) }! + +comp-kitchen-spike-unhook-other-self = You got { CAPITALIZE(THE($victim)) } off { THE($hook) }! +comp-kitchen-spike-unhook-other = { CAPITALIZE(THE($user)) } got { CAPITALIZE(THE($victim)) } off { THE($hook) }! + +comp-kitchen-spike-begin-butcher-self = You begin butchering { THE($victim) }! +comp-kitchen-spike-begin-butcher = { CAPITALIZE(THE($user)) } begins to butcher { THE($victim) }! + +comp-kitchen-spike-butcher-self = You butchered { THE($victim) }! +comp-kitchen-spike-butcher = { CAPITALIZE(THE($user)) } butchered { THE($victim) }! + +comp-kitchen-spike-unhook-verb = Unhook + +comp-kitchen-spike-hooked = [color=red]{ CAPITALIZE(THE($victim)) } is on this spike![/color] comp-kitchen-spike-meat-name = { $name } ({ $victim }) + +comp-kitchen-spike-victim-examine = [color=orange]{ CAPITALIZE(SUBJECT($target)) } looks quite lean.[/color] diff --git a/Resources/Prototypes/Entities/Structures/meat_spike.yml b/Resources/Prototypes/Entities/Structures/meat_spike.yml index 5825cec6ad..b8714d9d5e 100644 --- a/Resources/Prototypes/Entities/Structures/meat_spike.yml +++ b/Resources/Prototypes/Entities/Structures/meat_spike.yml @@ -50,7 +50,7 @@ enum.KitchenSpikeVisuals.Status: base: Empty: { state: spike } - Bloody: { state: spikebloody } + Bloody: { state: spikebloody } # TODO: Add sprites for different species. - type: Construction graph: MeatSpike node: MeatSpike @@ -58,3 +58,6 @@ guides: - Chef - FoodRecipes + - type: ContainerContainer + containers: + body: !type:ContainerSlot diff --git a/Resources/Prototypes/SoundCollections/kitchenspike.yml b/Resources/Prototypes/SoundCollections/kitchenspike.yml new file mode 100644 index 0000000000..e8d8dc79a6 --- /dev/null +++ b/Resources/Prototypes/SoundCollections/kitchenspike.yml @@ -0,0 +1,9 @@ +- type: soundCollection + id: Spike + files: + - /Audio/Effects/Fluids/splat.ogg + +- type: soundCollection + id: SpikeButcher + files: + - /Audio/Weapons/bladeslice.ogg From dcbbce52b6c8b64302cb8730fd53fb358f100bb4 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 19 Aug 2025 17:57:44 +0000 Subject: [PATCH 004/108] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f50bcb8a21..db2f74d203 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: RedBookcase - changes: - - message: Various changes to edged weapons, including new storage sprites and new - storage options. - type: Add - id: 8356 - time: '2025-04-26T20:45:08.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36564 - author: archee1, Beck changes: - message: Inflatable walls no longer instantly deconstruct if canceled. @@ -3952,3 +3944,10 @@ id: 8868 time: '2025-08-18T20:42:41.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/39735 +- author: Winkarst-cpu + changes: + - message: Meat spikes got an overhaul. + type: Tweak + id: 8869 + time: '2025-08-19T17:56:36.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38723 From 5cd9ba6016fb82bc7214fd9cb9e1a9014f5e44ca Mon Sep 17 00:00:00 2001 From: Samuka-C <47865393+Samuka-C@users.noreply.github.com> Date: Tue, 19 Aug 2025 15:28:52 -0300 Subject: [PATCH 005/108] Xenoborgs part 6 (#39595) --- Resources/Maps/Shuttles/mothership.yml | 2703 ++++++++++++++++++++++++ 1 file changed, 2703 insertions(+) create mode 100644 Resources/Maps/Shuttles/mothership.yml diff --git a/Resources/Maps/Shuttles/mothership.yml b/Resources/Maps/Shuttles/mothership.yml new file mode 100644 index 0000000000..b958c47401 --- /dev/null +++ b/Resources/Maps/Shuttles/mothership.yml @@ -0,0 +1,2703 @@ +meta: + format: 7 + category: Grid + engineVersion: 266.0.0 + forkId: "" + forkVersion: "" + time: 08/14/2025 23:46:23 + entityCount: 435 +maps: [] +grids: +- 1 +orphans: +- 1 +nullspace: [] +tilemap: + 1: Space + 3: FloorBlueCircuit + 4: FloorXenoborg + 2: Lattice + 0: Plating +entities: +- proto: "" + entities: + - uid: 1 + components: + - type: MetaData + name: mothership + - type: Transform + pos: -0.49999905,-0.4677186 + parent: invalid + - type: MapGrid + chunks: + 0,0: + ind: 0,0 + tiles: AwAAAAAAAAQAAAAAAAAEAAAAAAAAAwAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAMAAAAAAAAEAAAAAAAAAwAAAAAAAAAAAAAAAAAEAAAAAAAAAwAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAADAAAAAAAAAwAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAADAAAAAAAAAAAAAAAAAAIAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAA== + version: 7 + -1,0: + ind: -1,0 + tiles: AQAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAMAAAAAAAAEAAAAAAAABAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAwAAAAAAAAQAAAAAAAAAAAAAAAAAAwAAAAAAAAQAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAAAAADAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAA== + version: 7 + 0,-1: + ind: 0,-1 + tiles: AQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAADAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAwAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAMAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAADAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAwAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAMAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAADAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAwAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAMAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAAwAAAAAAAAAAAAAAAAACAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAADAAAAAAAAAwAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAwAAAAAAAAQAAAAAAAADAAAAAAAAAAAAAAAAAAQAAAAAAAADAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAA== + version: 7 + -1,-1: + ind: -1,-1 + tiles: AQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAAAAAAAAAAEAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAAAAAAAAAABAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAAAAAQAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAAAAAAAAAAEAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAAAAADAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAAAAAAAAAADAAAAAAAABAAAAAAAAAAAAAAAAAADAAAAAAAABAAAAAAAAA== + version: 7 + - type: Broadphase + - type: Physics + bodyStatus: InAir + fixedRotation: False + bodyType: Dynamic + - type: Fixtures + fixtures: {} + - type: OccluderTree + - type: SpreaderGrid + - type: Shuttle + dampingModifier: 0.25 + - type: ImplicitRoof + - type: GridPathfinding + - type: Gravity + gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + - type: DecalGrid + chunkCollection: + version: 2 + nodes: + - node: + color: '#0000FFFF' + id: WarnCornerSmallGreyscaleNE + decals: + 43: 4,0 + 50: 4,-4 + 51: -4,4 + 57: -4,-1 + - node: + color: '#0000FFFF' + id: WarnCornerSmallGreyscaleNW + decals: + 44: -4,0 + 49: -4,-4 + 52: 4,4 + 58: 4,-1 + - node: + color: '#0000FFFF' + id: WarnCornerSmallGreyscaleSE + decals: + 45: 4,0 + 48: 4,4 + 55: -4,1 + - node: + color: '#0000FFFF' + id: WarnCornerSmallGreyscaleSW + decals: + 46: -4,0 + 47: -4,4 + 56: 4,1 + - node: + color: '#0000FFFF' + id: WarnLineGreyscaleE + decals: + 1: 4,3 + 2: 4,2 + 3: 4,1 + 4: 4,-1 + 5: 4,-2 + 6: 4,-3 + 8: -1,-4 + 9: -1,-5 + 10: -1,-7 + 11: -1,-8 + 12: -1,-9 + 13: -1,-10 + 14: -1,-11 + 53: -4,0 + - node: + color: '#0000FFFF' + id: WarnLineGreyscaleN + decals: + 15: 5,0 + 16: -5,0 + 17: -3,4 + 18: -2,4 + 19: -1,4 + 20: 0,4 + 21: 1,4 + 22: 2,4 + 23: 3,4 + - node: + color: '#0000FFFF' + id: WarnLineGreyscaleS + decals: + 26: 5,0 + 27: -5,0 + - node: + color: '#0000FFFF' + id: WarnLineGreyscaleW + decals: + 29: -4,3 + 30: -4,2 + 31: -4,1 + 32: -4,-1 + 33: -4,-2 + 34: -4,-3 + 36: 1,-4 + 37: 1,-5 + 38: 1,-7 + 39: 1,-8 + 40: 1,-9 + 41: 1,-10 + 42: 1,-11 + 54: 4,0 + - type: GridAtmosphere + version: 2 + data: + chunkSize: 4 + - type: GasTileOverlay + - type: RadiationGridResistance +- proto: AirlockGlassShuttleXenoborgLocked + entities: + - uid: 107 + components: + - type: Transform + pos: -0.5,-11.5 + parent: 1 + - uid: 108 + components: + - type: Transform + pos: 1.5,-11.5 + parent: 1 +- proto: AirlockXenoborgLocked + entities: + - uid: 104 + components: + - type: Transform + pos: -0.5,-5.5 + parent: 1 + - uid: 105 + components: + - type: Transform + pos: 1.5,-5.5 + parent: 1 + - uid: 106 + components: + - type: Transform + pos: -5.5,0.5 + parent: 1 + - uid: 338 + components: + - type: Transform + pos: 0.5,-2.5 + parent: 1 + - uid: 365 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -2.5,0.5 + parent: 1 + - uid: 367 + components: + - type: Transform + pos: 3.5,0.5 + parent: 1 +- proto: APCXenoborg + entities: + - uid: 148 + components: + - type: MetaData + name: west APC + - type: Transform + pos: -8.5,2.5 + parent: 1 + - type: Fixtures + fixtures: {} + - uid: 240 + components: + - type: MetaData + name: docking APC + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,-6.5 + parent: 1 + - type: Fixtures + fixtures: {} + - uid: 422 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 6.5,2.5 + parent: 1 + - type: Fixtures + fixtures: {} + - uid: 423 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -5.5,-1.5 + parent: 1 + - type: Fixtures + fixtures: {} + - uid: 424 + components: + - type: Transform + pos: -1.5,2.5 + parent: 1 + - type: Fixtures + fixtures: {} +- proto: BlastDoor + entities: + - uid: 253 + components: + - type: Transform + pos: 0.5,-5.5 + parent: 1 +- proto: BorgCharger + entities: + - uid: 347 + components: + - type: Transform + pos: -4.5,2.5 + parent: 1 + - uid: 348 + components: + - type: Transform + pos: -4.5,-2.5 + parent: 1 + - uid: 350 + components: + - type: Transform + pos: -4.5,-1.5 + parent: 1 + - uid: 351 + components: + - type: Transform + pos: -4.5,3.5 + parent: 1 + - uid: 352 + components: + - type: Transform + pos: -4.5,1.5 + parent: 1 + - uid: 353 + components: + - type: Transform + pos: -4.5,-0.5 + parent: 1 + - uid: 389 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 5.5,-0.5 + parent: 1 + - uid: 390 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 5.5,-1.5 + parent: 1 + - uid: 391 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 5.5,-2.5 + parent: 1 + - uid: 392 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 5.5,1.5 + parent: 1 + - uid: 393 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 5.5,3.5 + parent: 1 + - uid: 394 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 5.5,2.5 + parent: 1 +- proto: CableApcExtension + entities: + - uid: 261 + components: + - type: Transform + pos: 1.5,4.5 + parent: 1 + - uid: 262 + components: + - type: Transform + pos: 3.5,4.5 + parent: 1 + - uid: 263 + components: + - type: Transform + pos: 4.5,3.5 + parent: 1 + - uid: 264 + components: + - type: Transform + pos: 5.5,-1.5 + parent: 1 + - uid: 265 + components: + - type: Transform + pos: 0.5,-0.5 + parent: 1 + - uid: 266 + components: + - type: Transform + pos: 2.5,1.5 + parent: 1 + - uid: 267 + components: + - type: Transform + pos: -4.5,2.5 + parent: 1 + - uid: 268 + components: + - type: Transform + pos: 0.5,-1.5 + parent: 1 + - uid: 269 + components: + - type: Transform + pos: 2.5,2.5 + parent: 1 + - uid: 275 + components: + - type: Transform + pos: 6.5,-1.5 + parent: 1 + - uid: 283 + components: + - type: Transform + pos: -3.5,3.5 + parent: 1 + - uid: 284 + components: + - type: Transform + pos: -2.5,3.5 + parent: 1 + - uid: 285 + components: + - type: Transform + pos: -3.5,-2.5 + parent: 1 + - uid: 286 + components: + - type: Transform + pos: -5.5,-1.5 + parent: 1 + - uid: 287 + components: + - type: Transform + pos: -2.5,4.5 + parent: 1 + - uid: 288 + components: + - type: Transform + pos: -2.5,-2.5 + parent: 1 + - uid: 289 + components: + - type: Transform + pos: -2.5,-3.5 + parent: 1 + - uid: 290 + components: + - type: Transform + pos: 3.5,-2.5 + parent: 1 + - uid: 294 + components: + - type: Transform + pos: 0.5,4.5 + parent: 1 + - uid: 295 + components: + - type: Transform + pos: 4.5,-2.5 + parent: 1 + - uid: 298 + components: + - type: Transform + pos: 4.5,-1.5 + parent: 1 + - uid: 299 + components: + - type: Transform + pos: 2.5,4.5 + parent: 1 + - uid: 300 + components: + - type: Transform + pos: 3.5,3.5 + parent: 1 + - uid: 301 + components: + - type: Transform + pos: 4.5,2.5 + parent: 1 + - uid: 302 + components: + - type: Transform + pos: 0.5,0.5 + parent: 1 + - uid: 303 + components: + - type: Transform + pos: -1.5,2.5 + parent: 1 + - uid: 304 + components: + - type: Transform + pos: -1.5,-3.5 + parent: 1 + - uid: 305 + components: + - type: Transform + pos: -0.5,-3.5 + parent: 1 + - uid: 306 + components: + - type: Transform + pos: 0.5,-3.5 + parent: 1 + - uid: 307 + components: + - type: Transform + pos: 1.5,-3.5 + parent: 1 + - uid: 308 + components: + - type: Transform + pos: 2.5,-3.5 + parent: 1 + - uid: 309 + components: + - type: Transform + pos: 3.5,-3.5 + parent: 1 + - uid: 310 + components: + - type: Transform + pos: -1.5,-6.5 + parent: 1 + - uid: 311 + components: + - type: Transform + pos: -0.5,-6.5 + parent: 1 + - uid: 312 + components: + - type: Transform + pos: 0.5,-6.5 + parent: 1 + - uid: 313 + components: + - type: Transform + pos: 0.5,-7.5 + parent: 1 + - uid: 314 + components: + - type: Transform + pos: 0.5,-8.5 + parent: 1 + - uid: 315 + components: + - type: Transform + pos: 0.5,-9.5 + parent: 1 + - uid: 316 + components: + - type: Transform + pos: 0.5,-10.5 + parent: 1 + - uid: 317 + components: + - type: Transform + pos: -1.5,1.5 + parent: 1 + - uid: 318 + components: + - type: Transform + pos: 1.5,1.5 + parent: 1 + - uid: 319 + components: + - type: Transform + pos: 0.5,1.5 + parent: 1 + - uid: 320 + components: + - type: Transform + pos: -0.5,1.5 + parent: 1 + - uid: 321 + components: + - type: Transform + pos: -5.5,2.5 + parent: 1 + - uid: 322 + components: + - type: Transform + pos: -3.5,2.5 + parent: 1 + - uid: 323 + components: + - type: Transform + pos: -0.5,4.5 + parent: 1 + - uid: 324 + components: + - type: Transform + pos: -1.5,4.5 + parent: 1 + - uid: 330 + components: + - type: Transform + pos: -8.5,2.5 + parent: 1 + - uid: 331 + components: + - type: Transform + pos: -8.5,1.5 + parent: 1 + - uid: 332 + components: + - type: Transform + pos: -8.5,0.5 + parent: 1 + - uid: 333 + components: + - type: Transform + pos: -10.5,0.5 + parent: 1 + - uid: 334 + components: + - type: Transform + pos: -9.5,0.5 + parent: 1 + - uid: 335 + components: + - type: Transform + pos: -8.5,0.5 + parent: 1 + - uid: 336 + components: + - type: Transform + pos: -7.5,0.5 + parent: 1 + - uid: 337 + components: + - type: Transform + pos: -6.5,0.5 + parent: 1 + - uid: 401 + components: + - type: Transform + pos: 5.5,2.5 + parent: 1 + - uid: 402 + components: + - type: Transform + pos: 6.5,2.5 + parent: 1 + - uid: 406 + components: + - type: Transform + pos: -3.5,-1.5 + parent: 1 + - uid: 407 + components: + - type: Transform + pos: -4.5,-1.5 + parent: 1 + - uid: 410 + components: + - type: Transform + pos: -8.5,-0.5 + parent: 1 + - uid: 411 + components: + - type: Transform + pos: -8.5,-1.5 + parent: 1 + - uid: 412 + components: + - type: Transform + pos: 1.5,-6.5 + parent: 1 + - uid: 413 + components: + - type: Transform + pos: 2.5,-6.5 + parent: 1 +- proto: CableApcStack + entities: + - uid: 345 + components: + - type: Transform + parent: 425 + - type: Physics + canCollide: False + - type: InsideEntityStorage +- proto: CableHV + entities: + - uid: 139 + components: + - type: Transform + pos: -7.5,-0.5 + parent: 1 + - uid: 140 + components: + - type: Transform + pos: -8.5,-0.5 + parent: 1 + - uid: 141 + components: + - type: Transform + pos: -9.5,-0.5 + parent: 1 + - uid: 142 + components: + - type: Transform + pos: -10.5,-0.5 + parent: 1 + - uid: 143 + components: + - type: Transform + pos: -10.5,0.5 + parent: 1 + - uid: 144 + components: + - type: Transform + pos: -10.5,1.5 + parent: 1 + - uid: 145 + components: + - type: Transform + pos: -9.5,1.5 + parent: 1 + - uid: 146 + components: + - type: Transform + pos: -8.5,1.5 + parent: 1 + - uid: 147 + components: + - type: Transform + pos: -7.5,1.5 + parent: 1 + - uid: 414 + components: + - type: Transform + pos: -9.5,0.5 + parent: 1 +- proto: CableHVStack + entities: + - uid: 343 + components: + - type: Transform + parent: 425 + - type: Physics + canCollide: False + - type: InsideEntityStorage +- proto: CableMV + entities: + - uid: 149 + components: + - type: Transform + pos: -7.5,1.5 + parent: 1 + - uid: 150 + components: + - type: Transform + pos: -9.5,-0.5 + parent: 1 + - uid: 151 + components: + - type: Transform + pos: -8.5,2.5 + parent: 1 + - uid: 152 + components: + - type: Transform + pos: -7.5,0.5 + parent: 1 + - uid: 153 + components: + - type: Transform + pos: -6.5,0.5 + parent: 1 + - uid: 154 + components: + - type: Transform + pos: -5.5,0.5 + parent: 1 + - uid: 155 + components: + - type: Transform + pos: -4.5,0.5 + parent: 1 + - uid: 156 + components: + - type: Transform + pos: -3.5,0.5 + parent: 1 + - uid: 157 + components: + - type: Transform + pos: -2.5,0.5 + parent: 1 + - uid: 158 + components: + - type: Transform + pos: -1.5,0.5 + parent: 1 + - uid: 159 + components: + - type: Transform + pos: 0.5,0.5 + parent: 1 + - uid: 160 + components: + - type: Transform + pos: 1.5,0.5 + parent: 1 + - uid: 161 + components: + - type: Transform + pos: 2.5,0.5 + parent: 1 + - uid: 162 + components: + - type: Transform + pos: 3.5,0.5 + parent: 1 + - uid: 163 + components: + - type: Transform + pos: -0.5,0.5 + parent: 1 + - uid: 164 + components: + - type: Transform + pos: 5.5,0.5 + parent: 1 + - uid: 165 + components: + - type: Transform + pos: 4.5,0.5 + parent: 1 + - uid: 166 + components: + - type: Transform + pos: 6.5,0.5 + parent: 1 + - uid: 167 + components: + - type: Transform + pos: 7.5,0.5 + parent: 1 + - uid: 168 + components: + - type: Transform + pos: 7.5,1.5 + parent: 1 + - uid: 169 + components: + - type: Transform + pos: 7.5,-0.5 + parent: 1 + - uid: 170 + components: + - type: Transform + pos: 0.5,7.5 + parent: 1 + - uid: 171 + components: + - type: Transform + pos: 0.5,6.5 + parent: 1 + - uid: 172 + components: + - type: Transform + pos: 0.5,4.5 + parent: 1 + - uid: 173 + components: + - type: Transform + pos: 0.5,3.5 + parent: 1 + - uid: 174 + components: + - type: Transform + pos: 0.5,2.5 + parent: 1 + - uid: 175 + components: + - type: Transform + pos: 0.5,1.5 + parent: 1 + - uid: 176 + components: + - type: Transform + pos: 0.5,5.5 + parent: 1 + - uid: 177 + components: + - type: Transform + pos: -0.5,6.5 + parent: 1 + - uid: 178 + components: + - type: Transform + pos: -0.5,7.5 + parent: 1 + - uid: 179 + components: + - type: Transform + pos: 1.5,7.5 + parent: 1 + - uid: 180 + components: + - type: Transform + pos: 1.5,6.5 + parent: 1 + - uid: 181 + components: + - type: Transform + pos: 0.5,-0.5 + parent: 1 + - uid: 182 + components: + - type: Transform + pos: 0.5,-1.5 + parent: 1 + - uid: 183 + components: + - type: Transform + pos: 0.5,-2.5 + parent: 1 + - uid: 184 + components: + - type: Transform + pos: 0.5,-3.5 + parent: 1 + - uid: 185 + components: + - type: Transform + pos: 0.5,-4.5 + parent: 1 + - uid: 186 + components: + - type: Transform + pos: 0.5,-5.5 + parent: 1 + - uid: 187 + components: + - type: Transform + pos: 0.5,-6.5 + parent: 1 + - uid: 188 + components: + - type: Transform + pos: 0.5,-7.5 + parent: 1 + - uid: 189 + components: + - type: Transform + pos: 0.5,-8.5 + parent: 1 + - uid: 190 + components: + - type: Transform + pos: -1.5,-8.5 + parent: 1 + - uid: 191 + components: + - type: Transform + pos: -1.5,-7.5 + parent: 1 + - uid: 192 + components: + - type: Transform + pos: -1.5,-9.5 + parent: 1 + - uid: 193 + components: + - type: Transform + pos: 1.5,-8.5 + parent: 1 + - uid: 194 + components: + - type: Transform + pos: 2.5,-8.5 + parent: 1 + - uid: 195 + components: + - type: Transform + pos: 2.5,-7.5 + parent: 1 + - uid: 196 + components: + - type: Transform + pos: 2.5,-9.5 + parent: 1 + - uid: 197 + components: + - type: Transform + pos: -7.5,-0.5 + parent: 1 + - uid: 198 + components: + - type: Transform + pos: -7.5,-1.5 + parent: 1 + - uid: 199 + components: + - type: Transform + pos: -6.5,-1.5 + parent: 1 + - uid: 200 + components: + - type: Transform + pos: -8.5,0.5 + parent: 1 + - uid: 201 + components: + - type: Transform + pos: -9.5,0.5 + parent: 1 + - uid: 202 + components: + - type: Transform + pos: -10.5,0.5 + parent: 1 + - uid: 203 + components: + - type: Transform + pos: -11.5,0.5 + parent: 1 + - uid: 204 + components: + - type: Transform + pos: -11.5,-0.5 + parent: 1 + - uid: 205 + components: + - type: Transform + pos: -11.5,1.5 + parent: 1 + - uid: 206 + components: + - type: Transform + pos: -9.5,-1.5 + parent: 1 + - uid: 207 + components: + - type: Transform + pos: -9.5,2.5 + parent: 1 + - uid: 208 + components: + - type: Transform + pos: -10.5,2.5 + parent: 1 + - uid: 209 + components: + - type: Transform + pos: -7.5,2.5 + parent: 1 + - uid: 210 + components: + - type: Transform + pos: -6.5,2.5 + parent: 1 + - uid: 211 + components: + - type: Transform + pos: -10.5,-1.5 + parent: 1 + - uid: 241 + components: + - type: Transform + pos: -1.5,-6.5 + parent: 1 + - uid: 242 + components: + - type: Transform + pos: -0.5,-6.5 + parent: 1 + - uid: 255 + components: + - type: Transform + pos: 1.5,3.5 + parent: 1 + - uid: 256 + components: + - type: Transform + pos: -0.5,3.5 + parent: 1 + - uid: 258 + components: + - type: Transform + pos: -5.5,-1.5 + parent: 1 + - uid: 259 + components: + - type: Transform + pos: 6.5,2.5 + parent: 1 + - uid: 281 + components: + - type: Transform + pos: -1.5,1.5 + parent: 1 + - uid: 293 + components: + - type: Transform + pos: -1.5,2.5 + parent: 1 + - uid: 359 + components: + - type: Transform + pos: 6.5,1.5 + parent: 1 + - uid: 360 + components: + - type: Transform + pos: 6.5,-0.5 + parent: 1 +- proto: CableMVStack + entities: + - uid: 339 + components: + - type: Transform + parent: 425 + - type: Physics + canCollide: False + - type: InsideEntityStorage +- proto: CableTerminal + entities: + - uid: 138 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -10.5,0.5 + parent: 1 + - uid: 371 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,0.5 + parent: 1 +- proto: ComputerIFFSyndicate + entities: + - uid: 278 + components: + - type: Transform + pos: 2.5,5.5 + parent: 1 +- proto: ComputerPowerMonitoring + entities: + - uid: 279 + components: + - type: Transform + pos: -1.5,5.5 + parent: 1 + - uid: 429 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,-0.5 + parent: 1 +- proto: ComputerShuttle + entities: + - uid: 273 + components: + - type: Transform + pos: 0.5,5.5 + parent: 1 + - uid: 291 + components: + - type: Transform + pos: 0.5,2.5 + parent: 1 +- proto: ComputerSurveillanceCameraMonitor + entities: + - uid: 426 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,-0.5 + parent: 1 +- proto: ComputerSurveillanceWirelessXenoborgCameraMonitor + entities: + - uid: 276 + components: + - type: Transform + pos: -0.5,5.5 + parent: 1 + - uid: 431 + components: + - type: Transform + pos: -0.5,2.5 + parent: 1 +- proto: ComputerXenoborgsControl + entities: + - uid: 277 + components: + - type: Transform + pos: 1.5,5.5 + parent: 1 + - uid: 282 + components: + - type: Transform + pos: 1.5,2.5 + parent: 1 +- proto: ConveyorBelt + entities: + - uid: 243 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-8.5 + parent: 1 + - uid: 244 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-7.5 + parent: 1 + - uid: 245 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-6.5 + parent: 1 + - uid: 246 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-5.5 + parent: 1 + - uid: 247 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-4.5 + parent: 1 + - uid: 248 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-3.5 + parent: 1 + - uid: 249 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-2.5 + parent: 1 + - uid: 250 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-1.5 + parent: 1 + - uid: 251 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-0.5 + parent: 1 +- proto: CrateGenericSteel + entities: + - uid: 425 + components: + - type: Transform + pos: -0.5,-1.5 + parent: 1 + - type: ContainerContainer + containers: + entity_storage: !type:Container + showEnts: False + occludes: True + ents: + - 339 + - 341 + - 343 + - 342 + - 345 + - 344 + - 340 + - 346 + paper_label: !type:ContainerSlot + showEnts: False + occludes: True + ent: null +- proto: DisposalBend + entities: + - uid: 377 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -4.5,-3.5 + parent: 1 + - uid: 386 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-4.5 + parent: 1 +- proto: DisposalJunctionFlipped + entities: + - uid: 376 + components: + - type: Transform + pos: -4.5,-4.5 + parent: 1 +- proto: DisposalPipe + entities: + - uid: 378 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-4.5 + parent: 1 + - uid: 379 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -2.5,-4.5 + parent: 1 + - uid: 380 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,-4.5 + parent: 1 + - uid: 381 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -0.5,-4.5 + parent: 1 + - uid: 382 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 0.5,-4.5 + parent: 1 + - uid: 384 + components: + - type: Transform + pos: 1.5,-2.5 + parent: 1 + - uid: 385 + components: + - type: Transform + pos: 1.5,-3.5 + parent: 1 + - uid: 387 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -4.5,-5.5 + parent: 1 +- proto: DisposalTrunk + entities: + - uid: 325 + components: + - type: Transform + pos: 1.5,-1.5 + parent: 1 + - uid: 374 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -3.5,-3.5 + parent: 1 + - uid: 388 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -4.5,-6.5 + parent: 1 +- proto: DisposalUnit + entities: + - uid: 326 + components: + - type: MetaData + name: external launch unit + - type: Transform + pos: 1.5,-1.5 + parent: 1 + - uid: 369 + components: + - type: MetaData + name: external launch unit + - type: Transform + pos: -3.5,-3.5 + parent: 1 +- proto: FloorDrain + entities: + - uid: 396 + components: + - type: Transform + pos: 3.5,-2.5 + parent: 1 + - type: Fixtures + fixtures: {} + - uid: 397 + components: + - type: Transform + pos: -2.5,-2.5 + parent: 1 + - type: Fixtures + fixtures: {} + - uid: 398 + components: + - type: Transform + pos: -2.5,3.5 + parent: 1 + - type: Fixtures + fixtures: {} + - uid: 399 + components: + - type: Transform + pos: 3.5,3.5 + parent: 1 + - type: Fixtures + fixtures: {} +- proto: GeneratorBasic15kW + entities: + - uid: 133 + components: + - type: Transform + pos: -10.5,-0.5 + parent: 1 + - uid: 134 + components: + - type: Transform + pos: -9.5,-0.5 + parent: 1 + - uid: 136 + components: + - type: Transform + pos: -7.5,-0.5 + parent: 1 + - uid: 137 + components: + - type: Transform + pos: -8.5,-0.5 + parent: 1 +- proto: GravityGeneratorMini + entities: + - uid: 132 + components: + - type: Transform + pos: -6.5,1.5 + parent: 1 +- proto: Grille + entities: + - uid: 212 + components: + - type: Transform + pos: -7.5,2.5 + parent: 1 + - uid: 213 + components: + - type: Transform + pos: -6.5,2.5 + parent: 1 + - uid: 214 + components: + - type: Transform + pos: -10.5,2.5 + parent: 1 + - uid: 215 + components: + - type: Transform + pos: -9.5,2.5 + parent: 1 + - uid: 216 + components: + - type: Transform + pos: -11.5,1.5 + parent: 1 + - uid: 217 + components: + - type: Transform + pos: -11.5,0.5 + parent: 1 + - uid: 218 + components: + - type: Transform + pos: -11.5,-0.5 + parent: 1 + - uid: 219 + components: + - type: Transform + pos: -10.5,-1.5 + parent: 1 + - uid: 220 + components: + - type: Transform + pos: -9.5,-1.5 + parent: 1 + - uid: 221 + components: + - type: Transform + pos: -7.5,-1.5 + parent: 1 + - uid: 222 + components: + - type: Transform + pos: -6.5,-1.5 + parent: 1 + - uid: 223 + components: + - type: Transform + pos: 7.5,-0.5 + parent: 1 + - uid: 224 + components: + - type: Transform + pos: 7.5,0.5 + parent: 1 + - uid: 225 + components: + - type: Transform + pos: 7.5,1.5 + parent: 1 + - uid: 226 + components: + - type: Transform + pos: -0.5,7.5 + parent: 1 + - uid: 227 + components: + - type: Transform + pos: -0.5,6.5 + parent: 1 + - uid: 228 + components: + - type: Transform + pos: 0.5,7.5 + parent: 1 + - uid: 229 + components: + - type: Transform + pos: 0.5,6.5 + parent: 1 + - uid: 230 + components: + - type: Transform + pos: 1.5,7.5 + parent: 1 + - uid: 231 + components: + - type: Transform + pos: 1.5,6.5 + parent: 1 + - uid: 232 + components: + - type: Transform + pos: -1.5,-7.5 + parent: 1 + - uid: 233 + components: + - type: Transform + pos: -1.5,-8.5 + parent: 1 + - uid: 234 + components: + - type: Transform + pos: -1.5,-9.5 + parent: 1 + - uid: 235 + components: + - type: Transform + pos: 2.5,-9.5 + parent: 1 + - uid: 236 + components: + - type: Transform + pos: 2.5,-8.5 + parent: 1 + - uid: 237 + components: + - type: Transform + pos: 2.5,-7.5 + parent: 1 + - uid: 254 + components: + - type: Transform + pos: 0.5,3.5 + parent: 1 + - uid: 260 + components: + - type: Transform + pos: 1.5,3.5 + parent: 1 + - uid: 296 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 6.5,0.5 + parent: 1 + - uid: 329 + components: + - type: Transform + pos: -0.5,3.5 + parent: 1 + - uid: 357 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 6.5,-0.5 + parent: 1 + - uid: 358 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 6.5,1.5 + parent: 1 +- proto: Gyroscope + entities: + - uid: 131 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,-0.5 + parent: 1 +- proto: MachineArtifactCrusher + entities: + - uid: 372 + components: + - type: MetaData + name: body crusher + - type: Transform + pos: 3.5,-4.5 + parent: 1 + - uid: 373 + components: + - type: MetaData + name: body crusher + - type: Transform + pos: 2.5,-4.5 + parent: 1 +- proto: PlasticFlapsAirtightClear + entities: + - uid: 252 + components: + - type: Transform + pos: 0.5,-5.5 + parent: 1 +- proto: PoweredlightBlue + entities: + - uid: 127 + components: + - type: Transform + pos: -8.5,1.5 + parent: 1 + - uid: 128 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-0.5 + parent: 1 + - uid: 238 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -0.5,-8.5 + parent: 1 + - uid: 239 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-8.5 + parent: 1 + - uid: 297 + components: + - type: Transform + pos: 0.5,5.5 + parent: 1 + - uid: 356 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 5.5,0.5 + parent: 1 + - uid: 432 + components: + - type: Transform + pos: 0.5,2.5 + parent: 1 + - uid: 433 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -4.5,-2.5 + parent: 1 + - uid: 434 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -2.5,-4.5 + parent: 1 +- proto: Recycler + entities: + - uid: 370 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -1.5,-4.5 + parent: 1 +- proto: SheetGlass + entities: + - uid: 341 + components: + - type: Transform + parent: 425 + - type: Physics + canCollide: False + - type: InsideEntityStorage +- proto: SheetPlasteel + entities: + - uid: 342 + components: + - type: Transform + parent: 425 + - type: Physics + canCollide: False + - type: InsideEntityStorage +- proto: SheetPlastic + entities: + - uid: 344 + components: + - type: Transform + parent: 425 + - type: Physics + canCollide: False + - type: InsideEntityStorage +- proto: SheetSteel + entities: + - uid: 340 + components: + - type: Transform + parent: 425 + - type: Physics + canCollide: False + - type: InsideEntityStorage + - uid: 346 + components: + - type: Transform + parent: 425 + - type: Physics + canCollide: False + - type: InsideEntityStorage +- proto: SignDoors + entities: + - uid: 428 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 2.5,-5.5 + parent: 1 + - type: Fixtures + fixtures: {} +- proto: SignEngine + entities: + - uid: 427 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -5.5,1.5 + parent: 1 + - type: Fixtures + fixtures: {} +- proto: SignSpace + entities: + - uid: 430 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -4.5,-3.5 + parent: 1 + - type: Fixtures + fixtures: {} +- proto: SMESAdvanced + entities: + - uid: 129 + components: + - type: Transform + pos: -10.5,1.5 + parent: 1 + - uid: 130 + components: + - type: Transform + pos: -9.5,1.5 + parent: 1 +- proto: SubstationBasic + entities: + - uid: 135 + components: + - type: Transform + pos: -7.5,1.5 + parent: 1 +- proto: SurveillanceCameraGeneral + entities: + - uid: 270 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,5.5 + parent: 1 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraGeneral + nameSet: True + id: front + - uid: 271 + components: + - type: Transform + pos: 0.5,-10.5 + parent: 1 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraGeneral + nameSet: True + id: docking + - uid: 272 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,2.5 + parent: 1 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraGeneral + nameSet: True + id: core + - uid: 274 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 5.5,0.5 + parent: 1 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraGeneral + nameSet: True + id: starboard + - uid: 280 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,0.5 + parent: 1 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraGeneral + nameSet: True + id: port + - uid: 292 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,0.5 + parent: 1 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraGeneral + nameSet: True + id: engineering + - uid: 435 + components: + - type: Transform + pos: 0.5,-4.5 + parent: 1 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraGeneral + nameSet: True + id: stern +- proto: SurveillanceCameraRouterGeneral + entities: + - uid: 327 + components: + - type: Transform + pos: 3.5,5.5 + parent: 1 +- proto: SurveillanceCameraWirelessRouterXenoborg + entities: + - uid: 395 + components: + - type: Transform + pos: -2.5,5.5 + parent: 1 +- proto: Thruster + entities: + - uid: 111 + components: + - type: Transform + pos: -10.5,3.5 + parent: 1 + - uid: 112 + components: + - type: Transform + pos: -9.5,3.5 + parent: 1 + - uid: 113 + components: + - type: Transform + pos: -7.5,3.5 + parent: 1 + - uid: 114 + components: + - type: Transform + pos: -6.5,3.5 + parent: 1 + - uid: 115 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -7.5,-2.5 + parent: 1 + - uid: 116 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,-2.5 + parent: 1 + - uid: 117 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -10.5,-2.5 + parent: 1 + - uid: 118 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,-2.5 + parent: 1 + - uid: 119 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,-0.5 + parent: 1 + - uid: 120 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,0.5 + parent: 1 + - uid: 121 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,1.5 + parent: 1 + - uid: 122 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 8.5,1.5 + parent: 1 + - uid: 123 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 8.5,0.5 + parent: 1 + - uid: 124 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 8.5,-0.5 + parent: 1 + - uid: 125 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -2.5,-6.5 + parent: 1 + - uid: 126 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 3.5,-6.5 + parent: 1 +- proto: TwoWayLever + entities: + - uid: 5 + components: + - type: Transform + pos: 0.5,1.5 + parent: 1 + - type: DeviceLinkSource + linkedPorts: + 251: + - - Left + - Reverse + - - Right + - Forward + - - Middle + - Off + 250: + - - Left + - Reverse + - - Right + - Forward + - - Middle + - Off + 249: + - - Left + - Reverse + - - Right + - Forward + - - Middle + - Off + 248: + - - Left + - Reverse + - - Right + - Forward + - - Middle + - Off + 247: + - - Left + - Reverse + - - Right + - Forward + - - Middle + - Off + 246: + - - Left + - Reverse + - - Right + - Forward + - - Middle + - Off + 245: + - - Left + - Reverse + - - Right + - Forward + - - Middle + - Off + 244: + - - Left + - Reverse + - - Right + - Forward + - - Middle + - Off + 243: + - - Left + - Reverse + - - Right + - Forward + - - Middle + - Off + - uid: 328 + components: + - type: Transform + pos: 1.5,-4.5 + parent: 1 + - type: DeviceLinkSource + linkedPorts: + 253: + - - Left + - Open + - - Right + - Open + - - Middle + - Close + - uid: 415 + components: + - type: Transform + pos: -2.5,-4.5 + parent: 1 + - type: DeviceLinkSource + linkedPorts: + 370: + - - Left + - Reverse + - - Right + - Forward + - - Middle + - Off +- proto: WallXenoborg + entities: + - uid: 2 + components: + - type: Transform + pos: -1.5,-11.5 + parent: 1 + - uid: 4 + components: + - type: Transform + pos: 0.5,-11.5 + parent: 1 + - uid: 6 + components: + - type: Transform + pos: 2.5,-11.5 + parent: 1 + - uid: 7 + components: + - type: Transform + pos: 2.5,-10.5 + parent: 1 + - uid: 11 + components: + - type: Transform + pos: 2.5,-6.5 + parent: 1 + - uid: 12 + components: + - type: Transform + pos: 2.5,-5.5 + parent: 1 + - uid: 13 + components: + - type: Transform + pos: 3.5,-5.5 + parent: 1 + - uid: 14 + components: + - type: Transform + pos: 4.5,-5.5 + parent: 1 + - uid: 15 + components: + - type: Transform + pos: 4.5,-4.5 + parent: 1 + - uid: 16 + components: + - type: Transform + pos: 5.5,-4.5 + parent: 1 + - uid: 17 + components: + - type: Transform + pos: 5.5,-3.5 + parent: 1 + - uid: 18 + components: + - type: Transform + pos: 6.5,-3.5 + parent: 1 + - uid: 19 + components: + - type: Transform + pos: 6.5,-2.5 + parent: 1 + - uid: 20 + components: + - type: Transform + pos: 6.5,-1.5 + parent: 1 + - uid: 21 + components: + - type: Transform + pos: 7.5,-1.5 + parent: 1 + - uid: 25 + components: + - type: Transform + pos: 7.5,2.5 + parent: 1 + - uid: 26 + components: + - type: Transform + pos: 6.5,2.5 + parent: 1 + - uid: 27 + components: + - type: Transform + pos: 6.5,3.5 + parent: 1 + - uid: 28 + components: + - type: Transform + pos: 6.5,4.5 + parent: 1 + - uid: 29 + components: + - type: Transform + pos: 5.5,4.5 + parent: 1 + - uid: 30 + components: + - type: Transform + pos: 5.5,5.5 + parent: 1 + - uid: 31 + components: + - type: Transform + pos: 4.5,5.5 + parent: 1 + - uid: 32 + components: + - type: Transform + pos: 4.5,6.5 + parent: 1 + - uid: 33 + components: + - type: Transform + pos: 3.5,6.5 + parent: 1 + - uid: 34 + components: + - type: Transform + pos: 2.5,6.5 + parent: 1 + - uid: 35 + components: + - type: Transform + pos: 2.5,7.5 + parent: 1 + - uid: 39 + components: + - type: Transform + pos: -1.5,7.5 + parent: 1 + - uid: 40 + components: + - type: Transform + pos: -1.5,6.5 + parent: 1 + - uid: 41 + components: + - type: Transform + pos: -2.5,6.5 + parent: 1 + - uid: 42 + components: + - type: Transform + pos: -3.5,6.5 + parent: 1 + - uid: 43 + components: + - type: Transform + pos: -3.5,5.5 + parent: 1 + - uid: 44 + components: + - type: Transform + pos: -4.5,5.5 + parent: 1 + - uid: 45 + components: + - type: Transform + pos: -4.5,4.5 + parent: 1 + - uid: 46 + components: + - type: Transform + pos: -5.5,4.5 + parent: 1 + - uid: 47 + components: + - type: Transform + pos: -5.5,3.5 + parent: 1 + - uid: 48 + components: + - type: Transform + pos: -5.5,2.5 + parent: 1 + - uid: 51 + components: + - type: Transform + pos: -8.5,2.5 + parent: 1 + - uid: 54 + components: + - type: Transform + pos: -11.5,2.5 + parent: 1 + - uid: 58 + components: + - type: Transform + pos: -11.5,-1.5 + parent: 1 + - uid: 61 + components: + - type: Transform + pos: -8.5,-1.5 + parent: 1 + - uid: 63 + components: + - type: Transform + pos: -5.5,-1.5 + parent: 1 + - uid: 65 + components: + - type: Transform + pos: -5.5,-2.5 + parent: 1 + - uid: 66 + components: + - type: Transform + pos: -5.5,-3.5 + parent: 1 + - uid: 67 + components: + - type: Transform + pos: -4.5,-3.5 + parent: 1 + - uid: 68 + components: + - type: Transform + pos: -4.5,-4.5 + parent: 1 + - uid: 69 + components: + - type: Transform + pos: -3.5,-4.5 + parent: 1 + - uid: 70 + components: + - type: Transform + pos: -3.5,-5.5 + parent: 1 + - uid: 71 + components: + - type: Transform + pos: -2.5,-5.5 + parent: 1 + - uid: 72 + components: + - type: Transform + pos: -1.5,-5.5 + parent: 1 + - uid: 73 + components: + - type: Transform + pos: -1.5,-6.5 + parent: 1 + - uid: 77 + components: + - type: Transform + pos: -1.5,-10.5 + parent: 1 + - uid: 78 + components: + - type: Transform + pos: -3.5,-6.5 + parent: 1 + - uid: 79 + components: + - type: Transform + pos: 4.5,-6.5 + parent: 1 + - uid: 80 + components: + - type: Transform + pos: 8.5,-1.5 + parent: 1 + - uid: 81 + components: + - type: Transform + pos: 8.5,2.5 + parent: 1 + - uid: 82 + components: + - type: Transform + pos: -12.5,2.5 + parent: 1 + - uid: 83 + components: + - type: Transform + pos: -12.5,-1.5 + parent: 1 + - uid: 84 + components: + - type: Transform + pos: -11.5,3.5 + parent: 1 + - uid: 85 + components: + - type: Transform + pos: -8.5,3.5 + parent: 1 + - uid: 86 + components: + - type: Transform + pos: -11.5,-2.5 + parent: 1 + - uid: 87 + components: + - type: Transform + pos: -8.5,-2.5 + parent: 1 + - uid: 109 + components: + - type: Transform + pos: -5.5,-0.5 + parent: 1 + - uid: 110 + components: + - type: Transform + pos: -5.5,1.5 + parent: 1 + - uid: 257 + components: + - type: Transform + pos: 1.5,-2.5 + parent: 1 + - uid: 361 + components: + - type: Transform + pos: 2.5,2.5 + parent: 1 + - uid: 362 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-0.5 + parent: 1 + - uid: 364 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -0.5,-2.5 + parent: 1 + - uid: 383 + components: + - type: Transform + pos: 3.5,-0.5 + parent: 1 + - uid: 400 + components: + - type: Transform + pos: 3.5,1.5 + parent: 1 + - uid: 403 + components: + - type: Transform + pos: -2.5,1.5 + parent: 1 + - uid: 404 + components: + - type: Transform + pos: -1.5,2.5 + parent: 1 + - uid: 405 + components: + - type: Transform + pos: 2.5,-1.5 + parent: 1 + - uid: 416 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-1.5 + parent: 1 +- proto: WallXenoborgDiagonal + entities: + - uid: 88 + components: + - type: Transform + pos: -5.5,5.5 + parent: 1 + - uid: 89 + components: + - type: Transform + pos: -4.5,6.5 + parent: 1 + - uid: 90 + components: + - type: Transform + pos: -2.5,7.5 + parent: 1 + - uid: 91 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 3.5,7.5 + parent: 1 + - uid: 92 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 5.5,6.5 + parent: 1 + - uid: 93 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 6.5,5.5 + parent: 1 + - uid: 94 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 7.5,3.5 + parent: 1 + - uid: 95 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 7.5,-2.5 + parent: 1 + - uid: 96 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 6.5,-4.5 + parent: 1 + - uid: 97 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 5.5,-5.5 + parent: 1 + - uid: 98 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -4.5,-5.5 + parent: 1 + - uid: 99 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -5.5,-4.5 + parent: 1 + - uid: 100 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,-2.5 + parent: 1 + - uid: 101 + components: + - type: Transform + pos: -12.5,3.5 + parent: 1 + - uid: 363 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -2.5,-1.5 + parent: 1 + - uid: 366 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 2.5,-2.5 + parent: 1 + - uid: 409 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 3.5,-1.5 + parent: 1 + - uid: 417 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,-2.5 + parent: 1 + - uid: 418 + components: + - type: Transform + pos: -2.5,2.5 + parent: 1 + - uid: 419 + components: + - type: Transform + pos: -1.5,3.5 + parent: 1 + - uid: 420 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,3.5 + parent: 1 + - uid: 421 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 3.5,2.5 + parent: 1 +- proto: XenoborgWindow + entities: + - uid: 3 + components: + - type: Transform + pos: 7.5,0.5 + parent: 1 + - uid: 8 + components: + - type: Transform + pos: -1.5,-7.5 + parent: 1 + - uid: 9 + components: + - type: Transform + pos: -1.5,-8.5 + parent: 1 + - uid: 10 + components: + - type: Transform + pos: -1.5,-9.5 + parent: 1 + - uid: 22 + components: + - type: Transform + pos: 7.5,-0.5 + parent: 1 + - uid: 23 + components: + - type: Transform + pos: 7.5,1.5 + parent: 1 + - uid: 24 + components: + - type: Transform + pos: -0.5,7.5 + parent: 1 + - uid: 36 + components: + - type: Transform + pos: 0.5,7.5 + parent: 1 + - uid: 37 + components: + - type: Transform + pos: 1.5,7.5 + parent: 1 + - uid: 38 + components: + - type: Transform + pos: -7.5,2.5 + parent: 1 + - uid: 49 + components: + - type: Transform + pos: -6.5,2.5 + parent: 1 + - uid: 50 + components: + - type: Transform + pos: -9.5,2.5 + parent: 1 + - uid: 52 + components: + - type: Transform + pos: -11.5,0.5 + parent: 1 + - uid: 53 + components: + - type: Transform + pos: -11.5,-0.5 + parent: 1 + - uid: 55 + components: + - type: Transform + pos: -6.5,-1.5 + parent: 1 + - uid: 56 + components: + - type: Transform + pos: -10.5,-1.5 + parent: 1 + - uid: 57 + components: + - type: Transform + pos: -0.5,6.5 + parent: 1 + - uid: 59 + components: + - type: Transform + pos: -7.5,-1.5 + parent: 1 + - uid: 60 + components: + - type: Transform + pos: -9.5,-1.5 + parent: 1 + - uid: 62 + components: + - type: Transform + pos: -11.5,1.5 + parent: 1 + - uid: 64 + components: + - type: Transform + pos: -10.5,2.5 + parent: 1 + - uid: 74 + components: + - type: Transform + pos: 2.5,-7.5 + parent: 1 + - uid: 75 + components: + - type: Transform + pos: 2.5,-8.5 + parent: 1 + - uid: 76 + components: + - type: Transform + pos: 2.5,-9.5 + parent: 1 + - uid: 102 + components: + - type: Transform + pos: 0.5,6.5 + parent: 1 + - uid: 103 + components: + - type: Transform + pos: 1.5,6.5 + parent: 1 + - uid: 349 + components: + - type: Transform + pos: 6.5,1.5 + parent: 1 + - uid: 354 + components: + - type: Transform + pos: 6.5,0.5 + parent: 1 + - uid: 355 + components: + - type: Transform + pos: 6.5,-0.5 + parent: 1 + - uid: 368 + components: + - type: Transform + pos: 0.5,3.5 + parent: 1 + - uid: 375 + components: + - type: Transform + pos: 1.5,3.5 + parent: 1 + - uid: 408 + components: + - type: Transform + pos: -0.5,3.5 + parent: 1 +... From 9de76e70c71097241b3b2a2720eef0c1d34aba89 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:35:09 -0700 Subject: [PATCH 006/108] EVENT BASED WEIGHTLESSNESS (#37971) * Init Commit * Typos * Commit 2 * Save Interaction Test Mob from failing * ssss * Confident I've gotten all the correct prototypes * Whoops forgot to edit those * aaaaa * Better solution * Test fail fixes * Yaml fix * THE FINAL TEST FIX * Final fix(?) * whoops * Added a WeightlessnessChangedEvent * Check out this diff * Wait I'm dumb * Final optimization and don't duplicate code * Death to IsWeightless * File scoped namespaces * REVIEW * Fix test fails * FIX TEST FAILS REAL * A * Commit of doom * borgar * We don't need to specify on map init apparently * Fuck it * LOAD BEARING COMMENT --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../Tests/Doors/AirlockTest.cs | 1 + .../Tests/Gravity/WeightlessStatusTests.cs | 1 + .../Tests/GravityGridTest.cs | 6 +- .../Tests/Interaction/InteractionTest.cs | 1 + .../Systems/AdminVerbSystem.Smites.cs | 6 + .../Fluids/EntitySystems/SpraySystem.cs | 2 +- Content.Server/Gravity/GravitySystem.cs | 4 +- .../AntiGravityClothingSystem.cs | 13 + Content.Shared/Clothing/MagbootsComponent.cs | 7 +- Content.Shared/Clothing/MagbootsSystem.cs | 10 +- .../DoAfter/SharedDoAfterSystem.Update.cs | 5 +- .../Friction/TileFrictionController.cs | 2 +- .../Gravity/FloatingVisualsComponent.cs | 7 +- .../Gravity/GravityAffectedComponent.cs | 17 + Content.Shared/Gravity/GravityComponent.cs | 22 +- .../Gravity/SharedFloatingVisualizerSystem.cs | 53 +-- Content.Shared/Gravity/SharedGravitySystem.cs | 367 +++++++++++------- .../MovementIgnoreGravityComponent.cs | 23 +- .../Systems/FrictionContactsSystem.cs | 2 +- .../Systems/MovementIgnoreGravitySystem.cs | 24 +- .../Movement/Systems/SharedMoverController.cs | 6 +- .../Systems/SpeedModifierContactsSystem.cs | 2 +- .../Controllers/SharedConveyorController.cs | 2 +- .../StepTrigger/Systems/StepTriggerSystem.cs | 2 +- Content.Shared/Throwing/ThrowingSystem.cs | 2 +- .../Weapons/Ranged/Systems/SharedGunSystem.cs | 2 +- Resources/Prototypes/Entities/Mobs/base.yml | 1 + .../Entities/Objects/Fun/immovable_rod.yml | 4 +- .../Entities/Objects/Specific/Mech/mechs.yml | 1 + .../Prototypes/Entities/Objects/base_item.yml | 1 + .../Entities/Structures/base_structure.yml | 1 + 31 files changed, 329 insertions(+), 268 deletions(-) create mode 100644 Content.Shared/Gravity/GravityAffectedComponent.cs diff --git a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs index e47c73611a..69fe66039b 100644 --- a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs +++ b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs @@ -21,6 +21,7 @@ namespace Content.IntegrationTests.Tests.Doors components: - type: Physics bodyType: Dynamic + - type: GravityAffected - type: Fixtures fixtures: fix1: diff --git a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs index 6aa2763888..0951e7e260 100644 --- a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs +++ b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs @@ -19,6 +19,7 @@ namespace Content.IntegrationTests.Tests.Gravity - type: Alerts - type: Physics bodyType: Dynamic + - type: GravityAffected - type: entity name: WeightlessGravityGeneratorDummy diff --git a/Content.IntegrationTests/Tests/GravityGridTest.cs b/Content.IntegrationTests/Tests/GravityGridTest.cs index b32d6c2b8d..8257035de6 100644 --- a/Content.IntegrationTests/Tests/GravityGridTest.cs +++ b/Content.IntegrationTests/Tests/GravityGridTest.cs @@ -76,8 +76,8 @@ namespace Content.IntegrationTests.Tests Assert.Multiple(() => { Assert.That(generatorComponent.GravityActive, Is.True); - Assert.That(!entityMan.GetComponent(grid1).EnabledVV); - Assert.That(entityMan.GetComponent(grid2).EnabledVV); + Assert.That(!entityMan.GetComponent(grid1).Enabled); + Assert.That(entityMan.GetComponent(grid2).Enabled); }); // Re-enable needs power so it turns off again. @@ -94,7 +94,7 @@ namespace Content.IntegrationTests.Tests Assert.Multiple(() => { Assert.That(generatorComponent.GravityActive, Is.False); - Assert.That(entityMan.GetComponent(grid2).EnabledVV, Is.False); + Assert.That(entityMan.GetComponent(grid2).Enabled, Is.False); }); }); diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs index 79756ea5b4..0ed42d3476 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs @@ -144,6 +144,7 @@ public abstract partial class InteractionTest - type: Stripping - type: Puller - type: Physics + - type: GravityAffected - type: Tag tags: - CanPilot diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index 1bc4b65999..3703c8c1ac 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -29,6 +29,7 @@ using Content.Shared.Damage; using Content.Shared.Damage.Systems; using Content.Shared.Database; using Content.Shared.Electrocution; +using Content.Shared.Gravity; using Content.Shared.Interaction.Components; using Content.Shared.Inventory; using Content.Shared.Mobs; @@ -675,6 +676,11 @@ public sealed partial class AdminVerbSystem grav.Weightless = true; Dirty(args.Target, grav); + + EnsureComp(args.Target, out var weightless); + weightless.Weightless = true; + + Dirty(args.Target, weightless); }, Impact = LogImpact.Extreme, Message = string.Join(": ", noGravityName, Loc.GetString("admin-smite-remove-gravity-description")) diff --git a/Content.Server/Fluids/EntitySystems/SpraySystem.cs b/Content.Server/Fluids/EntitySystems/SpraySystem.cs index d8da0cde3d..2a6b0644be 100644 --- a/Content.Server/Fluids/EntitySystems/SpraySystem.cs +++ b/Content.Server/Fluids/EntitySystems/SpraySystem.cs @@ -166,7 +166,7 @@ public sealed class SpraySystem : EntitySystem if (TryComp(user, out var body)) { - if (_gravity.IsWeightless(user, body)) + if (_gravity.IsWeightless(user)) { // push back the player _physics.ApplyLinearImpulse(user, -impulseDirection * entity.Comp.PushbackAmount, body: body); diff --git a/Content.Server/Gravity/GravitySystem.cs b/Content.Server/Gravity/GravitySystem.cs index 6807b9df4a..7958071a31 100644 --- a/Content.Server/Gravity/GravitySystem.cs +++ b/Content.Server/Gravity/GravitySystem.cs @@ -18,7 +18,7 @@ namespace Content.Server.Gravity /// public void RefreshGravity(EntityUid uid, GravityComponent? gravity = null) { - if (!Resolve(uid, ref gravity)) + if (!GravityQuery.Resolve(uid, ref gravity)) return; if (gravity.Inherent) @@ -61,7 +61,7 @@ namespace Content.Server.Gravity /// public void EnableGravity(EntityUid uid, GravityComponent? gravity = null) { - if (!Resolve(uid, ref gravity)) + if (!GravityQuery.Resolve(uid, ref gravity)) return; if (gravity.Enabled || gravity.Inherent) diff --git a/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs index c5b2ee3dfc..636a21533e 100644 --- a/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs @@ -6,10 +6,13 @@ namespace Content.Shared.Clothing.EntitySystems; public sealed class AntiGravityClothingSystem : EntitySystem { + [Dependency] SharedGravitySystem _gravity = default!; /// public override void Initialize() { SubscribeLocalEvent>(OnIsWeightless); + SubscribeLocalEvent(OnEquipped); + SubscribeLocalEvent(OnUnequipped); } private void OnIsWeightless(Entity ent, ref InventoryRelayedEvent args) @@ -20,4 +23,14 @@ public sealed class AntiGravityClothingSystem : EntitySystem args.Args.Handled = true; args.Args.IsWeightless = true; } + + private void OnEquipped(Entity entity, ref ClothingGotEquippedEvent args) + { + _gravity.RefreshWeightless(args.Wearer, true); + } + + private void OnUnequipped(Entity entity, ref ClothingGotUnequippedEvent args) + { + _gravity.RefreshWeightless(args.Wearer, false); + } } diff --git a/Content.Shared/Clothing/MagbootsComponent.cs b/Content.Shared/Clothing/MagbootsComponent.cs index 4bef74fd33..c0a8b40866 100644 --- a/Content.Shared/Clothing/MagbootsComponent.cs +++ b/Content.Shared/Clothing/MagbootsComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Alert; +using Content.Shared.Inventory; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -16,10 +17,4 @@ public sealed partial class MagbootsComponent : Component /// [DataField] public bool RequiresGrid = true; - - /// - /// Slot the clothing has to be worn in to work. - /// - [DataField] - public string Slot = "shoes"; } diff --git a/Content.Shared/Clothing/MagbootsSystem.cs b/Content.Shared/Clothing/MagbootsSystem.cs index fd5a2cc336..58bc2f0796 100644 --- a/Content.Shared/Clothing/MagbootsSystem.cs +++ b/Content.Shared/Clothing/MagbootsSystem.cs @@ -32,14 +32,8 @@ public sealed class SharedMagbootsSystem : EntitySystem private void OnToggled(Entity ent, ref ItemToggledEvent args) { - var (uid, comp) = ent; - // only stick to the floor if being worn in the correct slot - if (_container.TryGetContainingContainer((uid, null, null), out var container) && - _inventory.TryGetSlotEntity(container.Owner, comp.Slot, out var worn) - && uid == worn) - { + if (_container.TryGetContainingContainer((ent.Owner, null, null), out var container)) UpdateMagbootEffects(container.Owner, ent, args.Activated); - } } private void OnGotUnequipped(Entity ent, ref ClothingGotUnequippedEvent args) @@ -58,6 +52,8 @@ public sealed class SharedMagbootsSystem : EntitySystem if (TryComp(user, out var moved)) moved.Enabled = !state; + _gravity.RefreshWeightless(user, !state); + if (state) _alerts.ShowAlert(user, ent.Comp.MagbootsAlert); else diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs index 31ff034809..c70c7ab61e 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs @@ -164,12 +164,11 @@ public abstract partial class SharedDoAfterSystem : EntitySystem if (args.Target is { } target && !xformQuery.TryGetComponent(target, out targetXform)) return true; - TransformComponent? usedXform = null; - if (args.Used is { } @using && !xformQuery.TryGetComponent(@using, out usedXform)) + if (args.Used is { } @using && !xformQuery.HasComp(@using)) return true; // TODO: Re-use existing xform query for these calculations. - if (args.BreakOnMove && !(!args.BreakOnWeightlessMove && _gravity.IsWeightless(args.User, xform: userXform))) + if (args.BreakOnMove && !(!args.BreakOnWeightlessMove && _gravity.IsWeightless(args.User))) { // Whether the user has moved too much from their original position. if (!_transform.InRange(userXform.Coordinates, doAfter.UserPosition, args.MovementThreshold)) diff --git a/Content.Shared/Friction/TileFrictionController.cs b/Content.Shared/Friction/TileFrictionController.cs index 36e971862a..4b29b9a9de 100644 --- a/Content.Shared/Friction/TileFrictionController.cs +++ b/Content.Shared/Friction/TileFrictionController.cs @@ -73,7 +73,7 @@ namespace Content.Shared.Friction // If we're not touching the ground, don't use tileFriction. // TODO: Make IsWeightless event-based; we already have grid traversals tracked so just raise events - if (body.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(uid, body, xform) || !xform.Coordinates.IsValid(EntityManager)) + if (body.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(uid) || !xform.Coordinates.IsValid(EntityManager)) friction = xform.GridUid == null || !_gridQuery.HasComp(xform.GridUid) ? _offGridDamping : _airDamping; else friction = _frictionModifier * GetTileFriction(uid, body, xform); diff --git a/Content.Shared/Gravity/FloatingVisualsComponent.cs b/Content.Shared/Gravity/FloatingVisualsComponent.cs index 67650baecd..c0064ebf78 100644 --- a/Content.Shared/Gravity/FloatingVisualsComponent.cs +++ b/Content.Shared/Gravity/FloatingVisualsComponent.cs @@ -10,20 +10,17 @@ public sealed partial class FloatingVisualsComponent : Component /// /// How long it takes to go from the bottom of the animation to the top. /// - [ViewVariables(VVAccess.ReadWrite)] [DataField, AutoNetworkedField] public float AnimationTime = 2f; /// /// How far it goes in any direction. /// - [ViewVariables(VVAccess.ReadWrite)] [DataField, AutoNetworkedField] public Vector2 Offset = new(0, 0.2f); - [ViewVariables(VVAccess.ReadWrite)] - [AutoNetworkedField] - public bool CanFloat = false; + [DataField, AutoNetworkedField] + public bool CanFloat; public readonly string AnimationKey = "gravity"; } diff --git a/Content.Shared/Gravity/GravityAffectedComponent.cs b/Content.Shared/Gravity/GravityAffectedComponent.cs new file mode 100644 index 0000000000..2da7458b07 --- /dev/null +++ b/Content.Shared/Gravity/GravityAffectedComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Gravity; + +/// +/// This Component allows a target to be considered "weightless" when Weightless is true. Without this component, the +/// target will never be weightless. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class GravityAffectedComponent : Component +{ + /// + /// If true, this entity will be considered "weightless" + /// + [DataField, AutoNetworkedField] + public bool Weightless = true; +} diff --git a/Content.Shared/Gravity/GravityComponent.cs b/Content.Shared/Gravity/GravityComponent.cs index bbe3de69ea..ede8f74c7a 100644 --- a/Content.Shared/Gravity/GravityComponent.cs +++ b/Content.Shared/Gravity/GravityComponent.cs @@ -5,34 +5,20 @@ using Robust.Shared.Serialization; namespace Content.Shared.Gravity { [RegisterComponent] + [AutoGenerateComponentState] [NetworkedComponent] public sealed partial class GravityComponent : Component { - [DataField("gravityShakeSound")] + [DataField, AutoNetworkedField] public SoundSpecifier GravityShakeSound { get; set; } = new SoundPathSpecifier("/Audio/Effects/alert.ogg"); - [ViewVariables(VVAccess.ReadWrite)] - public bool EnabledVV - { - get => Enabled; - set - { - if (Enabled == value) return; - Enabled = value; - var ev = new GravityChangedEvent(Owner, value); - IoCManager.Resolve().EventBus.RaiseLocalEvent(Owner, ref ev); - Dirty(); - } - } - - [DataField("enabled")] + [DataField, AutoNetworkedField] public bool Enabled; /// /// Inherent gravity ensures GravitySystem won't change Enabled according to the gravity generators attached to this entity. /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("inherent")] + [DataField, AutoNetworkedField] public bool Inherent; } } diff --git a/Content.Shared/Gravity/SharedFloatingVisualizerSystem.cs b/Content.Shared/Gravity/SharedFloatingVisualizerSystem.cs index 6ca974f2ed..ae5c73b498 100644 --- a/Content.Shared/Gravity/SharedFloatingVisualizerSystem.cs +++ b/Content.Shared/Gravity/SharedFloatingVisualizerSystem.cs @@ -8,15 +8,14 @@ namespace Content.Shared.Gravity; /// public abstract class SharedFloatingVisualizerSystem : EntitySystem { - [Dependency] private readonly SharedGravitySystem GravitySystem = default!; + [Dependency] private readonly SharedGravitySystem _gravity = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnComponentStartup); - SubscribeLocalEvent(OnGravityChanged); - SubscribeLocalEvent(OnEntParentChanged); + SubscribeLocalEvent(OnWeightlessnessChanged); } /// @@ -24,48 +23,28 @@ public abstract class SharedFloatingVisualizerSystem : EntitySystem /// public virtual void FloatAnimation(EntityUid uid, Vector2 offset, string animationKey, float animationTime, bool stop = false) { } - protected bool CanFloat(EntityUid uid, FloatingVisualsComponent component, TransformComponent? transform = null) + protected bool CanFloat(Entity entity) { - if (!Resolve(uid, ref transform)) - return false; - - if (transform.MapID == MapId.Nullspace) - return false; - - component.CanFloat = GravitySystem.IsWeightless(uid, xform: transform); - Dirty(uid, component); - return component.CanFloat; + entity.Comp.CanFloat = _gravity.IsWeightless(entity.Owner); + Dirty(entity); + return entity.Comp.CanFloat; } - private void OnComponentStartup(EntityUid uid, FloatingVisualsComponent component, ComponentStartup args) + private void OnComponentStartup(Entity entity, ref ComponentStartup args) { - if (CanFloat(uid, component)) - FloatAnimation(uid, component.Offset, component.AnimationKey, component.AnimationTime); + if (CanFloat(entity)) + FloatAnimation(entity, entity.Comp.Offset, entity.Comp.AnimationKey, entity.Comp.AnimationTime); } - private void OnGravityChanged(ref GravityChangedEvent args) + private void OnWeightlessnessChanged(Entity entity, ref WeightlessnessChangedEvent args) { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var floating, out var transform)) - { - if (transform.MapID == MapId.Nullspace) - continue; + if (entity.Comp.CanFloat == args.Weightless) + return; - if (transform.GridUid != args.ChangedGridIndex) - continue; + entity.Comp.CanFloat = CanFloat(entity); + Dirty(entity); - floating.CanFloat = !args.HasGravity; - Dirty(uid, floating); - - if (!args.HasGravity) - FloatAnimation(uid, floating.Offset, floating.AnimationKey, floating.AnimationTime); - } - } - - private void OnEntParentChanged(EntityUid uid, FloatingVisualsComponent component, ref EntParentChangedMessage args) - { - var transform = args.Transform; - if (CanFloat(uid, component, transform)) - FloatAnimation(uid, component.Offset, component.AnimationKey, component.AnimationTime); + if (args.Weightless) + FloatAnimation(entity, entity.Comp.Offset, entity.Comp.AnimationKey, entity.Comp.AnimationTime); } } diff --git a/Content.Shared/Gravity/SharedGravitySystem.cs b/Content.Shared/Gravity/SharedGravitySystem.cs index e20771d603..b54f9b21c8 100644 --- a/Content.Shared/Gravity/SharedGravitySystem.cs +++ b/Content.Shared/Gravity/SharedGravitySystem.cs @@ -1,171 +1,254 @@ using Content.Shared.Alert; using Content.Shared.Inventory; -using Content.Shared.Movement.Components; -using Robust.Shared.GameStates; +using Content.Shared.Throwing; +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.Map; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Events; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Timing; -namespace Content.Shared.Gravity +namespace Content.Shared.Gravity; + +public abstract partial class SharedGravitySystem : EntitySystem { - public abstract partial class SharedGravitySystem : EntitySystem + [Dependency] protected readonly IGameTiming Timing = default!; + [Dependency] private readonly AlertsSystem _alerts = default!; + + public static readonly ProtoId WeightlessAlert = "Weightless"; + + protected EntityQuery GravityQuery; + private EntityQuery _weightlessQuery; + private EntityQuery _physicsQuery; + + public override void Initialize() { - [Dependency] protected readonly IGameTiming Timing = default!; - [Dependency] private readonly AlertsSystem _alerts = default!; + base.Initialize(); + // Grid Gravity + SubscribeLocalEvent(OnGridInit); + SubscribeLocalEvent(OnGravityChange); - public static readonly ProtoId WeightlessAlert = "Weightless"; + // Weightlessness + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnEntParentChanged); + SubscribeLocalEvent(OnBodyTypeChanged); - private EntityQuery _gravityQuery; + // Alerts + SubscribeLocalEvent(OnAlertsSync); + SubscribeLocalEvent(OnWeightlessnessChanged); + SubscribeLocalEvent(OnAlertsParentChange); - public bool IsWeightless(EntityUid uid, PhysicsComponent? body = null, TransformComponent? xform = null) - { - Resolve(uid, ref body, false); + // Impulse + SubscribeLocalEvent(OnShooterImpulse); + SubscribeLocalEvent(OnThrowerImpulse); - if ((body?.BodyType & (BodyType.Static | BodyType.Kinematic)) != 0) - return false; + GravityQuery = GetEntityQuery(); + _weightlessQuery = GetEntityQuery(); + _physicsQuery = GetEntityQuery(); + } - if (TryComp(uid, out var ignoreGravityComponent)) - return ignoreGravityComponent.Weightless; + public override void Update(float frameTime) + { + base.Update(frameTime); + UpdateShake(); + } - var ev = new IsWeightlessEvent(uid); - RaiseLocalEvent(uid, ref ev); - if (ev.Handled) - return ev.IsWeightless; + public bool IsWeightless(Entity entity) + { + // If we can be weightless and are weightless, return true, otherwise return false + return _weightlessQuery.Resolve(entity, ref entity.Comp, false) && entity.Comp.Weightless; + } - if (!Resolve(uid, ref xform)) - return true; + private bool GetWeightless(Entity entity) + { + if (!_physicsQuery.Resolve(entity, ref entity.Comp2, false)) + return false; - // If grid / map has gravity - if (EntityGridOrMapHaveGravity((uid, xform))) - return false; + if (entity.Comp2.BodyType is BodyType.Static or BodyType.Kinematic) + return false; + // Check if something other than the grid or map is overriding our gravity + var ev = new IsWeightlessEvent(); + RaiseLocalEvent(entity, ref ev); + if (ev.Handled) + return ev.IsWeightless; + + return !EntityGridOrMapHaveGravity(entity.Owner); + } + + /// + /// Refreshes weightlessness status, needs to be called anytime it would change. + /// + /// The entity we are updating the weightless status of + public void RefreshWeightless(Entity entity) + { + if (!_weightlessQuery.Resolve(entity, ref entity.Comp)) + return; + + UpdateWeightless(entity!); + } + + /// + /// Overload of which also takes a bool for the weightlessness value we want to change to. + /// This method is LOAD BEARING for UninitializedSaveTest. DO NOT REMOVE IT. + /// + /// The entity we are updating the weightless status of + /// The weightless value we are trying to change to, helps avoid needless networking + public void RefreshWeightless(Entity entity, bool weightless) + { + if (!_weightlessQuery.Resolve(entity, ref entity.Comp)) + return; + + // Only update if we're changing our weightless status + if (entity.Comp.Weightless == weightless) + return; + + UpdateWeightless(entity!); + } + + private void UpdateWeightless(Entity entity) + { + var newWeightless = GetWeightless(entity); + + // Don't network or raise events if it's not changing + if (newWeightless == entity.Comp.Weightless) + return; + + entity.Comp.Weightless = newWeightless; + Dirty(entity); + + var ev = new WeightlessnessChangedEvent(entity.Comp.Weightless); + RaiseLocalEvent(entity, ref ev); + } + + private void OnMapInit(Entity entity, ref MapInitEvent args) + { + RefreshWeightless((entity.Owner, entity.Comp)); + } + + private void OnWeightlessnessChanged(Entity entity, ref WeightlessnessChangedEvent args) + { + if (args.Weightless) + _alerts.ShowAlert(entity, WeightlessAlert); + else + _alerts.ClearAlert(entity, WeightlessAlert); + } + + private void OnEntParentChanged(Entity entity, ref EntParentChangedMessage args) + { + // If we've moved but are still on the same grid, then don't do anything. + if (args.OldParent == args.Transform.GridUid) + return; + + RefreshWeightless((entity.Owner, entity.Comp), !EntityGridOrMapHaveGravity((entity, args.Transform))); + } + + private void OnBodyTypeChanged(Entity entity, ref PhysicsBodyTypeChangedEvent args) + { + // No need to update weightlessness if we're not weightless and we're a body type that can't be weightless + if (args.New is BodyType.Static or BodyType.Kinematic && entity.Comp.Weightless == false) + return; + + RefreshWeightless((entity.Owner, entity.Comp)); + } + + /// + /// Checks if a given entity is currently standing on a grid or map that supports having gravity at all. + /// + public bool EntityOnGravitySupportingGridOrMap(Entity entity) + { + entity.Comp ??= Transform(entity); + + return GravityQuery.HasComp(entity.Comp.GridUid) || + GravityQuery.HasComp(entity.Comp.MapUid); + } + + /// + /// Checks if a given entity is currently standing on a grid or map that has gravity of some kind. + /// + public bool EntityGridOrMapHaveGravity(Entity entity) + { + entity.Comp ??= Transform(entity); + + // DO NOT SET TO WEIGHTLESS IF THEY'RE IN NULL-SPACE + // TODO: If entities actually properly pause when leaving PVS rather than entering null-space this can probably go. + if (entity.Comp.MapID == MapId.Nullspace) return true; - } - /// - /// Checks if a given entity is currently standing on a grid or map that supports having gravity at all. - /// - public bool EntityOnGravitySupportingGridOrMap(Entity entity) + return GravityQuery.TryComp(entity.Comp.GridUid, out var gravity) && gravity.Enabled || + GravityQuery.TryComp(entity.Comp.MapUid, out var mapGravity) && mapGravity.Enabled; + } + + private void OnGravityChange(ref GravityChangedEvent args) + { + var gravity = AllEntityQuery(); + while(gravity.MoveNext(out var uid, out var weightless, out var xform)) { - entity.Comp ??= Transform(entity); + if (xform.GridUid != args.ChangedGridIndex) + continue; - return _gravityQuery.HasComp(entity.Comp.GridUid) || - _gravityQuery.HasComp(entity.Comp.MapUid); - } - - - /// - /// Checks if a given entity is currently standing on a grid or map that has gravity of some kind. - /// - public bool EntityGridOrMapHaveGravity(Entity entity) - { - entity.Comp ??= Transform(entity); - - return _gravityQuery.TryComp(entity.Comp.GridUid, out var gravity) && gravity.Enabled || - _gravityQuery.TryComp(entity.Comp.MapUid, out var mapGravity) && mapGravity.Enabled; - } - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnGridInit); - SubscribeLocalEvent(OnAlertsSync); - SubscribeLocalEvent(OnAlertsParentChange); - SubscribeLocalEvent(OnGravityChange); - SubscribeLocalEvent(OnGetState); - SubscribeLocalEvent(OnHandleState); - - _gravityQuery = GetEntityQuery(); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - UpdateShake(); - } - - private void OnHandleState(EntityUid uid, GravityComponent component, ref ComponentHandleState args) - { - if (args.Current is not GravityComponentState state) - return; - - if (component.EnabledVV == state.Enabled) - return; - component.EnabledVV = state.Enabled; - var ev = new GravityChangedEvent(uid, component.EnabledVV); - RaiseLocalEvent(uid, ref ev, true); - } - - private void OnGetState(EntityUid uid, GravityComponent component, ref ComponentGetState args) - { - args.State = new GravityComponentState(component.EnabledVV); - } - - private void OnGravityChange(ref GravityChangedEvent ev) - { - var alerts = AllEntityQuery(); - while(alerts.MoveNext(out var uid, out _, out var xform)) - { - if (xform.GridUid != ev.ChangedGridIndex) - continue; - - if (!ev.HasGravity) - { - _alerts.ShowAlert(uid, WeightlessAlert); - } - else - { - _alerts.ClearAlert(uid, WeightlessAlert); - } - } - } - - private void OnAlertsSync(AlertSyncEvent ev) - { - if (IsWeightless(ev.Euid)) - { - _alerts.ShowAlert(ev.Euid, WeightlessAlert); - } - else - { - _alerts.ClearAlert(ev.Euid, WeightlessAlert); - } - } - - private void OnAlertsParentChange(EntityUid uid, AlertsComponent component, ref EntParentChangedMessage args) - { - if (IsWeightless(uid)) - { - _alerts.ShowAlert(uid, WeightlessAlert); - } - else - { - _alerts.ClearAlert(uid, WeightlessAlert); - } - } - - private void OnGridInit(GridInitializeEvent ev) - { - EnsureComp(ev.EntityUid); - } - - [Serializable, NetSerializable] - private sealed class GravityComponentState : ComponentState - { - public bool Enabled { get; } - - public GravityComponentState(bool enabled) - { - Enabled = enabled; - } + RefreshWeightless((uid, weightless), !args.HasGravity); } } - [ByRefEvent] - public record struct IsWeightlessEvent(EntityUid Entity, bool IsWeightless = false, bool Handled = false) : IInventoryRelayEvent + private void OnAlertsSync(AlertSyncEvent ev) { - SlotFlags IInventoryRelayEvent.TargetSlots => ~SlotFlags.POCKET; + if (IsWeightless(ev.Euid)) + _alerts.ShowAlert(ev.Euid, WeightlessAlert); + else + _alerts.ClearAlert(ev.Euid, WeightlessAlert); + } + + private void OnAlertsParentChange(EntityUid uid, AlertsComponent component, ref EntParentChangedMessage args) + { + if (IsWeightless(uid)) + _alerts.ShowAlert(uid, WeightlessAlert); + else + _alerts.ClearAlert(uid, WeightlessAlert); + } + + private void OnGridInit(GridInitializeEvent ev) + { + EnsureComp(ev.EntityUid); + } + + [Serializable, NetSerializable] + private sealed class GravityComponentState : ComponentState + { + public bool Enabled { get; } + + public GravityComponentState(bool enabled) + { + Enabled = enabled; + } + } + + private void OnThrowerImpulse(Entity entity, ref ThrowerImpulseEvent args) + { + args.Push = true; + } + + private void OnShooterImpulse(Entity entity, ref ShooterImpulseEvent args) + { + args.Push = true; } } + +/// +/// Raised to determine if an entity's weightlessness is being overwritten by a component or item with a component. +/// +/// Whether we should be weightless +/// Whether something is trying to override our weightlessness +[ByRefEvent] +public record struct IsWeightlessEvent(bool IsWeightless = false, bool Handled = false) : IInventoryRelayEvent +{ + SlotFlags IInventoryRelayEvent.TargetSlots => ~SlotFlags.POCKET; +} + +/// +/// Raised on an entity when their weightless status changes. +/// +[ByRefEvent] +public readonly record struct WeightlessnessChangedEvent(bool Weightless); diff --git a/Content.Shared/Movement/Components/MovementIgnoreGravityComponent.cs b/Content.Shared/Movement/Components/MovementIgnoreGravityComponent.cs index 77c468e871..1723600ad1 100644 --- a/Content.Shared/Movement/Components/MovementIgnoreGravityComponent.cs +++ b/Content.Shared/Movement/Components/MovementIgnoreGravityComponent.cs @@ -1,34 +1,17 @@ -using Content.Shared.Clothing; -using Content.Shared.Gravity; -using Content.Shared.Inventory; using Robust.Shared.GameStates; -using Robust.Shared.Map; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; -using Robust.Shared.Serialization; namespace Content.Shared.Movement.Components { /// /// Ignores gravity entirely. /// - [RegisterComponent, NetworkedComponent] + [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class MovementIgnoreGravityComponent : Component { /// - /// Whether or not gravity is on or off for this object. + /// Whether gravity is on or off for this object. This will always override the current Gravity State. /// - [DataField("gravityState")] public bool Weightless = false; - } - - [NetSerializable, Serializable] - public sealed class MovementIgnoreGravityComponentState : ComponentState - { + [DataField, AutoNetworkedField] public bool Weightless; - - public MovementIgnoreGravityComponentState(MovementIgnoreGravityComponent component) - { - Weightless = component.Weightless; - } } } diff --git a/Content.Shared/Movement/Systems/FrictionContactsSystem.cs b/Content.Shared/Movement/Systems/FrictionContactsSystem.cs index 44fc3933e3..3ffdd6ec26 100644 --- a/Content.Shared/Movement/Systems/FrictionContactsSystem.cs +++ b/Content.Shared/Movement/Systems/FrictionContactsSystem.cs @@ -84,7 +84,7 @@ public sealed class FrictionContactsSystem : EntitySystem var frictionNoInput = 0.0f; var acceleration = 0.0f; - var isAirborne = physicsComponent.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(entity, physicsComponent); + var isAirborne = physicsComponent.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(entity.Owner); var remove = true; var entries = 0; diff --git a/Content.Shared/Movement/Systems/MovementIgnoreGravitySystem.cs b/Content.Shared/Movement/Systems/MovementIgnoreGravitySystem.cs index 52cdf219e5..93f6650fa9 100644 --- a/Content.Shared/Movement/Systems/MovementIgnoreGravitySystem.cs +++ b/Content.Shared/Movement/Systems/MovementIgnoreGravitySystem.cs @@ -1,33 +1,35 @@ -using Content.Shared.Movement.Components; +using Content.Shared.Gravity; +using Content.Shared.Movement.Components; using Content.Shared.Movement.Events; -using Robust.Shared.GameStates; namespace Content.Shared.Movement.Systems; public sealed class MovementIgnoreGravitySystem : EntitySystem { + [Dependency] SharedGravitySystem _gravity = default!; public override void Initialize() { - SubscribeLocalEvent(GetState); - SubscribeLocalEvent(HandleState); SubscribeLocalEvent(OnWeightless); + SubscribeLocalEvent(OnIsWeightless); + SubscribeLocalEvent(OnComponentStartup); } - private void OnWeightless(EntityUid uid, MovementAlwaysTouchingComponent component, ref CanWeightlessMoveEvent args) + private void OnWeightless(Entity entity, ref CanWeightlessMoveEvent args) { args.CanMove = true; } - private void HandleState(EntityUid uid, MovementIgnoreGravityComponent component, ref ComponentHandleState args) + private void OnIsWeightless(Entity entity, ref IsWeightlessEvent args) { - if (args.Next is null) - return; + // We don't check if the event has been handled as this component takes precedent over other things. - component.Weightless = ((MovementIgnoreGravityComponentState) args.Next).Weightless; + args.IsWeightless = entity.Comp.Weightless; + args.Handled = true; } - private void GetState(EntityUid uid, MovementIgnoreGravityComponent component, ref ComponentGetState args) + private void OnComponentStartup(Entity entity, ref ComponentStartup args) { - args.State = new MovementIgnoreGravityComponentState(component); + EnsureComp(entity); + _gravity.RefreshWeightless(entity.Owner, entity.Comp.Weightless); } } diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index f8495fcd18..b3c84aed4d 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -195,8 +195,7 @@ public abstract partial class SharedMoverController : VirtualController } // If the body is in air but isn't weightless then it can't move - // TODO: MAKE ISWEIGHTLESS EVENT BASED - var weightless = _gravity.IsWeightless(uid, physicsComponent, xform); + var weightless = _gravity.IsWeightless(uid); var inAirHelpless = false; if (physicsComponent.BodyStatus != BodyStatus.OnGround && !CanMoveInAirQuery.HasComponent(uid)) @@ -624,8 +623,7 @@ public abstract partial class SharedMoverController : VirtualController if (!TryComp(ent, out var physicsComponent) || !XformQuery.TryComp(ent, out var xform)) return; - // TODO: Make IsWeightless event based!!! - if (physicsComponent.BodyStatus != BodyStatus.OnGround || _gravity.IsWeightless(ent, physicsComponent, xform)) + if (physicsComponent.BodyStatus != BodyStatus.OnGround || _gravity.IsWeightless(ent.Owner)) args.Modifier *= ent.Comp.BaseWeightlessFriction; else args.Modifier *= ent.Comp.BaseFriction; diff --git a/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs b/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs index 8a0b085a83..2dda244163 100644 --- a/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs +++ b/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs @@ -85,7 +85,7 @@ public sealed class SpeedModifierContactsSystem : EntitySystem var sprintSpeed = 0.0f; // Cache the result of the airborne check, as it's expensive and independent of contacting entities, hence need only be done once. - var isAirborne = physicsComponent.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(uid, physicsComponent); + var isAirborne = physicsComponent.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(uid); bool remove = true; var entries = 0; diff --git a/Content.Shared/Physics/Controllers/SharedConveyorController.cs b/Content.Shared/Physics/Controllers/SharedConveyorController.cs index c2c88b2742..b1ccb3be2a 100644 --- a/Content.Shared/Physics/Controllers/SharedConveyorController.cs +++ b/Content.Shared/Physics/Controllers/SharedConveyorController.cs @@ -216,7 +216,7 @@ public abstract class SharedConveyorController : VirtualController return true; if (physics.BodyStatus == BodyStatus.InAir || - _gravity.IsWeightless(entity, physics, xform)) + _gravity.IsWeightless(entity.Owner)) { return true; } diff --git a/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs b/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs index 14703f3177..d1443e5da2 100644 --- a/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs +++ b/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs @@ -139,7 +139,7 @@ public sealed class StepTriggerSystem : EntitySystem // and the entity is flying or currently weightless // Makes sense simulation wise to have this be part of steptrigger directly IMO if (!component.IgnoreWeightless && TryComp(otherUid, out var physics) && - (physics.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(otherUid, physics))) + (physics.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(otherUid))) return false; var msg = new StepTriggerAttemptEvent { Source = uid, Tripper = otherUid }; diff --git a/Content.Shared/Throwing/ThrowingSystem.cs b/Content.Shared/Throwing/ThrowingSystem.cs index 4e44901c57..db68c3517c 100644 --- a/Content.Shared/Throwing/ThrowingSystem.cs +++ b/Content.Shared/Throwing/ThrowingSystem.cs @@ -242,7 +242,7 @@ public sealed class ThrowingSystem : EntitySystem RaiseLocalEvent(user.Value, ref pushEv); const float massLimit = 5f; - if (pushEv.Push || _gravity.IsWeightless(user.Value)) + if (pushEv.Push) _physics.ApplyLinearImpulse(user.Value, -impulseVector / physics.Mass * pushbackRatio * MathF.Min(massLimit, physics.Mass), body: userPhysics); } } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index 61ee8cdada..31f86d0236 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -391,7 +391,7 @@ public abstract partial class SharedGunSystem : EntitySystem var shooterEv = new ShooterImpulseEvent(); RaiseLocalEvent(user, ref shooterEv); - if (shooterEv.Push || _gravity.IsWeightless(user, userPhysics)) + if (shooterEv.Push) CauseImpulse(fromCoordinates, toCoordinates.Value, user, userPhysics); } diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index cfd9c13631..90bb90e663 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -9,6 +9,7 @@ noRot: true drawdepth: Mobs - type: MobCollision + - type: GravityAffected - type: Physics bodyType: KinematicController - type: Fixtures diff --git a/Resources/Prototypes/Entities/Objects/Fun/immovable_rod.yml b/Resources/Prototypes/Entities/Objects/Fun/immovable_rod.yml index e57223e1b4..15f67245f1 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/immovable_rod.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/immovable_rod.yml @@ -6,11 +6,13 @@ - type: Clickable - type: InteractionOutline - type: MovementIgnoreGravity + weightless: true - type: Sprite sprite: Objects/Fun/immovable_rod.rsi state: icon noRot: false - type: ImmovableRod + - type: GravityAffected - type: Physics bodyType: Dynamic linearDamping: 0 @@ -78,8 +80,6 @@ damage: types: Blunt: 190 - - type: MovementIgnoreGravity - gravityState: true - type: InputMover - type: MovementSpeedModifier baseWeightlessAcceleration: 5 diff --git a/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml b/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml index 4c4e44c28c..bda5901378 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml @@ -64,6 +64,7 @@ - type: Pullable - type: Physics bodyType: KinematicController + - type: GravityAffected - type: Clickable - type: WiresPanel - type: Fixtures diff --git a/Resources/Prototypes/Entities/Objects/base_item.yml b/Resources/Prototypes/Entities/Objects/base_item.yml index bb67d3f4cf..8490ba0042 100644 --- a/Resources/Prototypes/Entities/Objects/base_item.yml +++ b/Resources/Prototypes/Entities/Objects/base_item.yml @@ -24,6 +24,7 @@ soundHit: collection: MetalThud - type: CollisionWake + - type: GravityAffected - type: Physics bodyType: Dynamic fixedRotation: false diff --git a/Resources/Prototypes/Entities/Structures/base_structure.yml b/Resources/Prototypes/Entities/Structures/base_structure.yml index 71971a6624..d4936c859c 100644 --- a/Resources/Prototypes/Entities/Structures/base_structure.yml +++ b/Resources/Prototypes/Entities/Structures/base_structure.yml @@ -7,6 +7,7 @@ - type: Transform anchored: true - type: Clickable + - type: GravityAffected - type: Physics bodyType: Static - type: Fixtures From da23bc9dcc0965b59d3ec8152c3f134952164a3d Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:18:05 -0700 Subject: [PATCH 007/108] Crawling Fixes Part 4: Can't crawl when weightless. (#39099) * Init Commit * Typos * Commit 2 * Save Interaction Test Mob from failing * ssss * Confident I've gotten all the correct prototypes * Whoops forgot to edit those * aaaaa * Better solution * Test fail fixes * Yaml fix * THE FINAL TEST FIX * Final fix(?) * whoops * Added a WeightlessnessChangedEvent * Check out this diff * Wait I'm dumb * Final optimization and don't duplicate code * Death to IsWeightless * Moth directed targeted attack * A * Bugfixes and such * Grrr * Death * Cleanup * Cleanup 2 --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../Stunnable/Systems/StunOnCollideSystem.cs | 2 +- .../Stunnable/SharedStunSystem.Knockdown.cs | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Content.Server/Stunnable/Systems/StunOnCollideSystem.cs b/Content.Server/Stunnable/Systems/StunOnCollideSystem.cs index 2257812da1..c1757b1c2d 100644 --- a/Content.Server/Stunnable/Systems/StunOnCollideSystem.cs +++ b/Content.Server/Stunnable/Systems/StunOnCollideSystem.cs @@ -22,7 +22,7 @@ internal sealed class StunOnCollideSystem : EntitySystem private void TryDoCollideStun(Entity ent, EntityUid target) { - _stunSystem.TryKnockdown(target, ent.Comp.KnockdownAmount, ent.Comp.Refresh, ent.Comp.AutoStand, ent.Comp.Drop); + _stunSystem.TryKnockdown(target, ent.Comp.KnockdownAmount, ent.Comp.Refresh, ent.Comp.AutoStand, ent.Comp.Drop, true); if (ent.Comp.Refresh) { diff --git a/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs b/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs index 7917f10bd5..098e3176d9 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs @@ -4,6 +4,7 @@ using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Database; using Content.Shared.DoAfter; +using Content.Shared.Gravity; using Content.Shared.Hands.EntitySystems; using Content.Shared.Input; using Content.Shared.Movement.Events; @@ -60,6 +61,9 @@ public abstract partial class SharedStunSystem // Crawling SubscribeLocalEvent(OnKnockdownRefresh); SubscribeLocalEvent(OnDamaged); + SubscribeLocalEvent(OnWeightlessnessChanged); + SubscribeLocalEvent(OnKnockdownAttempt); + SubscribeLocalEvent(OnGetStandUpTime); // Handling Alternative Inputs SubscribeAllEvent(OnForceStandup); @@ -488,6 +492,32 @@ public abstract partial class SharedStunSystem args.SpeedModifier *= entity.Comp.SpeedModifier; } + private void OnWeightlessnessChanged(Entity entity, ref WeightlessnessChangedEvent args) + { + // I probably don't need this check since weightless -> non-weightless you shouldn't be knocked down + // But you never know. + if (!args.Weightless) + return; + + // Targeted moth attack + CancelKnockdownDoAfter((entity, entity.Comp)); + RemComp(entity); + } + + private void OnKnockdownAttempt(Entity entity, ref KnockDownAttemptEvent args) + { + // Directed, targeted moth attack. + if (entity.Comp.Weightless) + args.Cancelled = true; + } + + private void OnGetStandUpTime(Entity entity, ref GetStandUpTimeEvent args) + { + // Get up instantly if weightless + if (entity.Comp.Weightless) + args.DoAfterTime = TimeSpan.Zero; + } + #endregion #region Action Blockers From 32ad429b8ff7174726350a5e73fbb5c1bdb6e550 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 19 Aug 2025 19:19:15 +0000 Subject: [PATCH 008/108] 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 db2f74d203..f63a2c7803 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: archee1, Beck - changes: - - message: Inflatable walls no longer instantly deconstruct if canceled. - type: Fix - id: 8357 - time: '2025-04-26T20:56:18.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36937 - author: ScarKy0 changes: - message: Deliveries now have a random multiplier added to them, slightly modifying @@ -3951,3 +3944,10 @@ id: 8869 time: '2025-08-19T17:56:36.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38723 +- author: Princess-Cheeseballs + changes: + - message: You can no longer crawl while weightless. + type: Fix + id: 8870 + time: '2025-08-19T19:18:06.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/39099 From 325f0e45fa44b100271e69d89c26e6d162a42dab Mon Sep 17 00:00:00 2001 From: Nox Date: Tue, 19 Aug 2025 13:31:02 -0700 Subject: [PATCH 009/108] Viable Canesword (#39586) * Buffed canesword damage to 16, cost to 3. Signed-off-by: Nox38 * Increased sale price from 1 to 2 TC, increased damage from 16 to 17 to match cap saber. Signed-off-by: Nox38 --------- Signed-off-by: Nox38 --- Resources/Prototypes/Catalog/uplink_catalog.yml | 2 +- Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index ebeae71d99..c499f54db6 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -2202,7 +2202,7 @@ discountDownTo: Telecrystal: 2 cost: - Telecrystal: 5 + Telecrystal: 3 categories: - UplinkJob conditions: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml index 2847b723d8..29ce5fa8c8 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml @@ -41,7 +41,7 @@ attackRate: 1.5 damage: types: - Slash: 14 + Slash: 17 soundHit: path: /Audio/Weapons/bladeslice.ogg - type: Item From f2d512e19a8f61ee7c2efb0bf0481b93751e3143 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 19 Aug 2025 20:32:09 +0000 Subject: [PATCH 010/108] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f63a2c7803..c851f1d086 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,14 +1,4 @@ Entries: -- author: ScarKy0 - changes: - - message: Deliveries now have a random multiplier added to them, slightly modifying - their earnings. - type: Add - - message: Deliveries now show how many spesos they grant on examine. - type: Tweak - id: 8358 - time: '2025-04-26T21:42:27.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36918 - author: sowelipililimute changes: - message: Medical and security techfab will no longer announce more than five recipes @@ -3951,3 +3941,11 @@ id: 8870 time: '2025-08-19T19:18:06.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/39099 +- author: Nox38 + changes: + - message: Buffed the Librarian cane sword's damage and price to be more competitive. + Damage 15 -> 17 slash, TC cost 5 -> 3 + type: Tweak + id: 8871 + time: '2025-08-19T20:31:02.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/39586 From e90fac14eb9d8ecd7267ea7d7381ad0f3abc8886 Mon Sep 17 00:00:00 2001 From: Winkarst-cpu <74284083+Winkarst-cpu@users.noreply.github.com> Date: Wed, 20 Aug 2025 01:30:55 +0300 Subject: [PATCH 011/108] Fix: Untoggle removed actions (#39526) * Fix * Update Content.Shared/Actions/SharedActionsSystem.cs Co-authored-by: LaCumbiaDelCoronavirus <90893484+LaCumbiaDelCoronavirus@users.noreply.github.com> --------- Co-authored-by: LaCumbiaDelCoronavirus <90893484+LaCumbiaDelCoronavirus@users.noreply.github.com> --- Content.Shared/Actions/SharedActionsSystem.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index c4581cfbff..c0a32e6038 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -832,7 +832,8 @@ public abstract class SharedActionsSystem : EntitySystem performer.Comp.Actions.Remove(ent.Owner); Dirty(performer, performer.Comp); ent.Comp.AttachedEntity = null; - DirtyField(ent, ent.Comp, nameof(ActionComponent.AttachedEntity)); + ent.Comp.Toggled = false; + DirtyFields(ent, ent.Comp, null, nameof(ActionComponent.AttachedEntity), nameof(ActionComponent.Toggled)); ActionRemoved((performer, performer.Comp), ent); if (ent.Comp.Temporary) From 2659e421c03f20a67765cf5152241433d89d89d5 Mon Sep 17 00:00:00 2001 From: M4rchy-S <89603088+M4rchy-S@users.noreply.github.com> Date: Wed, 20 Aug 2025 01:39:25 +0300 Subject: [PATCH 012/108] Fixing foam dart sprite in hands (#39758) --- .../Weapons/Guns/Ammunition/Projectiles/toy.yml | 2 +- .../Objects/Fun/Foam/foam_crossbow.rsi/meta.json | 3 --- .../foamdart.png | Bin .../Objects/Fun/Foam/foam_dart.rsi/meta.json | 14 ++++++++++++++ 4 files changed, 15 insertions(+), 4 deletions(-) rename Resources/Textures/Objects/Fun/Foam/{foam_crossbow.rsi => foam_dart.rsi}/foamdart.png (100%) create mode 100644 Resources/Textures/Objects/Fun/Foam/foam_dart.rsi/meta.json diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/toy.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/toy.yml index cfe080da6d..3e4235f677 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/toy.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/toy.yml @@ -24,7 +24,7 @@ - Trash - type: Ammo - type: Sprite - sprite: Objects/Fun/Foam/foam_crossbow.rsi + sprite: Objects/Fun/Foam/foam_dart.rsi layers: - state: foamdart - type: EmbeddableProjectile diff --git a/Resources/Textures/Objects/Fun/Foam/foam_crossbow.rsi/meta.json b/Resources/Textures/Objects/Fun/Foam/foam_crossbow.rsi/meta.json index b245d7e77d..79a6ec3509 100644 --- a/Resources/Textures/Objects/Fun/Foam/foam_crossbow.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/Foam/foam_crossbow.rsi/meta.json @@ -10,9 +10,6 @@ { "name": "icon" }, - { - "name": "foamdart" - }, { "name": "foambox" }, diff --git a/Resources/Textures/Objects/Fun/Foam/foam_crossbow.rsi/foamdart.png b/Resources/Textures/Objects/Fun/Foam/foam_dart.rsi/foamdart.png similarity index 100% rename from Resources/Textures/Objects/Fun/Foam/foam_crossbow.rsi/foamdart.png rename to Resources/Textures/Objects/Fun/Foam/foam_dart.rsi/foamdart.png diff --git a/Resources/Textures/Objects/Fun/Foam/foam_dart.rsi/meta.json b/Resources/Textures/Objects/Fun/Foam/foam_dart.rsi/meta.json new file mode 100644 index 0000000000..a7e64d408b --- /dev/null +++ b/Resources/Textures/Objects/Fun/Foam/foam_dart.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "foamdart" + } + ] +} From 3f14ceec0fa9aad8526bc4ced17fa6880bd81874 Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Wed, 20 Aug 2025 00:40:52 +0200 Subject: [PATCH 013/108] fix performer's suit sprites (#39722) --- .../Misc/performer-wig.rsi/equipped-HELMET.png | Bin 792 -> 1145 bytes .../Boots/performer.rsi/equipped-FEET.png | Bin 875 -> 854 bytes .../Shoes/Boots/performer.rsi/icon.png | Bin 396 -> 375 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/Textures/Clothing/Head/Misc/performer-wig.rsi/equipped-HELMET.png b/Resources/Textures/Clothing/Head/Misc/performer-wig.rsi/equipped-HELMET.png index 4d5e75107dbf77405642901544546fabbd6ba429..0f243264bd747a9f69a8c12db2bdc35b8dd77a99 100644 GIT binary patch delta 1135 zcmV-#1d#if2Kfk(8Gi-<0063Kaozv`010qNS#tmY4#5Bb4#5Gqk!$S$00b*ZL_t(| zUhP`FYZO5gpG}k)gyf9D{~*R90Sm=Wu+c_D#KJ<17A9ihN0lguh#;|wu@JQ~Y9WXq zSZSpeeqahqLyBM{LDXDM#wEU9ZsNn(jdk^3a2R zmKKK(o2$FJ)bHhAx;L8?KbC-~*;|~Q)d2(W!^>&)c;9{xJsQSwKiq7yAP(QmzP9#N z8jiBi20xYnu)kDZ)N7k&^3K43_haF!2JPL{U)N(C_kS@6dA(QE^lh^s3O`IPG9ao+ z@FNN6KRi_TD0@A2RtL6m_a62B#Z!e~m&@OD7NEy9#&I8wxnB)c5^)C}MR+AJ9fsc}C3*97E2 zydxlX^Qo=>JR0^9n`WyQ#!9o3m;qTfKsb_BA2w`Xthjoy1XSneZ6@v?9a}rLdSM6B z>_iff$L?x6BDH7ABZaxMNtlo~Q$GnmHUp$i$A6`ZtsPsvupeo5TKXwqrY|=OOu4l5 z2U#sm0A>$Cr0wKUJ3sW&W?w`A_l1Tl$K7Fx1oMA>k}l-9!E=3Za*hGVfMdWh;23ZW zI0hU8j)4`(0Q*xl#g0$S!;@Fly(<#|9jFnaEEO(D6vY=+jHx;eoCBv$tD7fJh`&OK z-hT$^J39bTut4BwbXI`-%9@3kaPaVfiYKT+NNbU70x0;Q3m4VY`E$YHi11l~EWvSg z!{-uEiH1+`O-cfr0Mj{oKR3??U?zG72gRSSxIse>vkwfK^}2HkmS3OQ1W>IW>+H#q zqjh@+O5=}q?D8gk-z#z-fNfv^i^}vwAb)&B)@CO$1rXD-mOGW?y002ovPDHLkV1mWx B6bAqR literal 792 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 diff --git a/Resources/Textures/Clothing/Shoes/Boots/performer.rsi/icon.png b/Resources/Textures/Clothing/Shoes/Boots/performer.rsi/icon.png index 7619c7fd2e5c152838bd699b5b41cc2b1106620b..dddc65638738f41aad72e16f9d60d18ca1a7f44c 100644 GIT binary patch delta 327 zcmeBS{?0VPvR>2E#W5tpJvl*wb+Mv>6cda%pm@Ne>+ihJV*k=ZCNpRMtbfaR_}dm2 zx9j&h|3{wvs*to@&M?QnOZ}veSdh$zvVT!*l9jw|{qk`}H5I>w)_lD?>n#t{@AHNq zxI2t(Dl3{r9v{e>>2dMn>*MBa?-Q@_^z@wJ=?Pd{uOM}Vy|ej=C##C3L*(HZjn!uh z+3e~BMXDWYCRljP3vt-O%;OXLul~=6u7|7F8yt@>yiPIeCMW&aT*0of)6l@s*!huw&wuvr<|mG?JPKI?I|G#TrZffyNn}W0*!#ax zjwNZ~^!Jhy4t)7Z0$m?c69mLrSXo*A|30qp@sBR^;g_~cSdOf?bx}Zqw-HPT$}lmw W*@sG53m?D500f?{elF{r5}E)`*@<%i delta 329 zcmV-P0k;140*nKYIWj&;L_t(|oMT}aU@+={Q3s4VVAKJl4j6U7r~^hFFzSF&2aGy^ zBnNoTTlk+*O%BZdkzp5qXQY_}-p*gkAS18A@bSY-h7WIFg2mpg+0G!a{~QCqfH=dG zhc_5V1tdA(*N<-u-@~jK7-w%}U}Rtdi-BlHMrN@1ujX^Kb%3&lF$3e8;|%}!MHoK3 zc>x#a1M-=G;vo48r}xmt0S2Zn|37|s#lRZp#qekG4xq0gQ4OMhKYx15z!K}h@O#lV zusWb4h)xGYIRIp+sH8lY4YC}V+!&acnZbMr{U7QmCI*l?QXD~)10>=?85kvnfwlqT z{r`V37JB)ff#L8b2B8RF1_p6KP*6bdzkdu8&)+gI5E&IjIbcU_I!@ng&rT&N+mYn} bS^@ym>0xFIVT?lz00000NkvXXu0mjfZ=a9s From a26a18243f0bcbefdf75c830d38ec0183a38e43f Mon Sep 17 00:00:00 2001 From: Samuka-C <47865393+Samuka-C@users.noreply.github.com> Date: Tue, 19 Aug 2025 20:04:24 -0300 Subject: [PATCH 014/108] fix upload console (#39756) * fix upload console * make it smaller * even smaller * revert last commit * Update --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- Content.Server/Silicons/Laws/SiliconLawSystem.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Content.Server/Silicons/Laws/SiliconLawSystem.cs b/Content.Server/Silicons/Laws/SiliconLawSystem.cs index e672cb5005..653ed6bac2 100644 --- a/Content.Server/Silicons/Laws/SiliconLawSystem.cs +++ b/Content.Server/Silicons/Laws/SiliconLawSystem.cs @@ -294,15 +294,16 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem protected override void OnUpdaterInsert(Entity ent, ref EntInsertedIntoContainerMessage args) { // TODO: Prediction dump this - if (!TryComp(args.Entity, out SiliconLawProviderComponent? provider)) + if (!TryComp(args.Entity, out var provider)) return; - var lawset = GetLawset(provider.Laws).Laws; + var lawset = provider.Lawset ?? GetLawset(provider.Laws); + var query = EntityManager.CompRegistryQueryEnumerator(ent.Comp.Components); while (query.MoveNext(out var update)) { - SetLaws(lawset, update, provider.LawUploadSound); + SetLaws(lawset.Laws, update, provider.LawUploadSound); } } } From d0b0a4a92611663bdb76150488e4908cc27cffaf Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 19 Aug 2025 23:05:31 +0000 Subject: [PATCH 015/108] Automatic changelog update --- Resources/Changelog/Admin.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index 60e38dc6e3..d2a9c7b436 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -1338,5 +1338,12 @@ Entries: id: 163 time: '2025-08-15T14:06:51.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/37783 +- author: Samuka + changes: + - message: edited law boards now work correctly + type: Fix + id: 164 + time: '2025-08-19T23:04:24.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/39756 Name: Admin Order: 2 From b317d7514f34c56a989c661668290857fdef6f57 Mon Sep 17 00:00:00 2001 From: Perry Fraser Date: Wed, 20 Aug 2025 02:52:52 -0400 Subject: [PATCH 016/108] fix: don't do emergency shuttle stuff in lobby (#38732) --- Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index 53714c846a..e0bbc9d090 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -7,6 +7,7 @@ using Content.Server.Administration.Managers; using Content.Server.Chat.Systems; using Content.Server.Communications; using Content.Server.DeviceNetwork.Systems; +using Content.Server.GameTicking; using Content.Server.GameTicking.Events; using Content.Server.Pinpointer; using Content.Server.Popups; @@ -57,6 +58,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem [Dependency] private readonly CommunicationsConsoleSystem _commsConsole = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!; [Dependency] private readonly DockingSystem _dock = default!; + [Dependency] private readonly GameTicker _ticker = default!; [Dependency] private readonly IdCardSystem _idSystem = default!; [Dependency] private readonly NavMapSystem _navMap = default!; [Dependency] private readonly MapLoaderSystem _loader = default!; @@ -157,7 +159,9 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem public override void Update(float frameTime) { base.Update(frameTime); - UpdateEmergencyConsole(frameTime); + // Don't handle any of this logic if in lobby + if (_ticker.RunLevel != GameRunLevel.PreRoundLobby) + UpdateEmergencyConsole(frameTime); } /// From 95b0df9a8948a7dd7d80b082981e25a2d9dc5afa Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Wed, 20 Aug 2025 03:02:41 -0400 Subject: [PATCH 017/108] Fix nuke disk getting lost when polymorphed holder is deleted (#36058) * Delete original entity when polymorph is deleted * Switch to EntityTerminatingEvent * Add RevertOnDelete option to PolymorphPrototype * Fix error on server shutdown while polymorphed * Set RevertOnDelete to false by default * AsNullable * Revert "Set RevertOnDelete to false by default" This reverts commit 087c43fbb923c9369c61c9d001e18814b3de3aca. * Use pattern matching instead of .Value --- .../Components/PolymorphedEntityComponent.cs | 2 +- .../Polymorph/Systems/PolymorphSystem.cs | 21 ++++++++++++++++++- .../Polymorph/PolymorphPrototype.cs | 6 ++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Content.Server/Polymorph/Components/PolymorphedEntityComponent.cs b/Content.Server/Polymorph/Components/PolymorphedEntityComponent.cs index 7620203a86..7e3437a908 100644 --- a/Content.Server/Polymorph/Components/PolymorphedEntityComponent.cs +++ b/Content.Server/Polymorph/Components/PolymorphedEntityComponent.cs @@ -18,7 +18,7 @@ public sealed partial class PolymorphedEntityComponent : Component /// The original entity that the player will revert back into /// [DataField(required: true)] - public EntityUid Parent; + public EntityUid? Parent; /// /// The amount of time that has passed since the entity was created diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.cs index d0f8b4e8fc..aabc0366cd 100644 --- a/Content.Server/Polymorph/Systems/PolymorphSystem.cs +++ b/Content.Server/Polymorph/Systems/PolymorphSystem.cs @@ -55,6 +55,7 @@ public sealed partial class PolymorphSystem : EntitySystem SubscribeLocalEvent(OnBeforeFullySliced); SubscribeLocalEvent(OnDestruction); + SubscribeLocalEvent(OnPolymorphedTerminating); InitializeMap(); } @@ -147,6 +148,16 @@ public sealed partial class PolymorphSystem : EntitySystem } } + private void OnPolymorphedTerminating(Entity ent, ref EntityTerminatingEvent args) + { + if (ent.Comp.Configuration.RevertOnDelete) + Revert(ent.AsNullable()); + + // Remove our original entity too + // Note that Revert will set Parent to null, so reverted entities will not be deleted + QueueDel(ent.Comp.Parent); + } + /// /// Polymorphs the target entity into the specific polymorph prototype /// @@ -284,13 +295,21 @@ public sealed partial class PolymorphSystem : EntitySystem if (Deleted(uid)) return null; - var parent = component.Parent; + if (component.Parent is not { } parent) + return null; + + // Clear our reference to the original entity + component.Parent = null; if (Deleted(parent)) return null; var uidXform = Transform(uid); var parentXform = Transform(parent); + // Don't swap back onto a terminating grid + if (TerminatingOrDeleted(uidXform.ParentUid)) + return null; + if (component.Configuration.ExitPolymorphSound != null) _audio.PlayPvs(component.Configuration.ExitPolymorphSound, uidXform.Coordinates); diff --git a/Content.Shared/Polymorph/PolymorphPrototype.cs b/Content.Shared/Polymorph/PolymorphPrototype.cs index 0978d7119a..26637431f0 100644 --- a/Content.Shared/Polymorph/PolymorphPrototype.cs +++ b/Content.Shared/Polymorph/PolymorphPrototype.cs @@ -105,6 +105,12 @@ public sealed partial record PolymorphConfiguration [DataField(serverOnly: true)] public bool RevertOnDeath = true; + /// + /// Whether or not the polymorph reverts when the entity is deleted. + /// + [DataField(serverOnly: true)] + public bool RevertOnDelete = true; + /// /// Whether or not the polymorph reverts when the entity is eaten or fully sliced. /// From 47dd036ef2414df7cdeab6ed89ae610c3a1abf79 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Wed, 20 Aug 2025 04:08:31 -0700 Subject: [PATCH 018/108] Prevent shoe buffs while crawling (#39648) Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../ClothingSpeedModifierComponent.cs | 7 ++++ .../Clothing/ClothingSpeedModifierSystem.cs | 15 ++++++--- .../AntiGravityClothingSystem.cs | 32 +++++++++++++++++-- .../Inventory/InventorySystem.Relay.cs | 16 +++++++--- .../EntityStorageLayingDownOverrideSystem.cs | 2 +- .../Standing/StandingStateSystem.cs | 28 +++++++++------- .../Stunnable/SharedStunSystem.Knockdown.cs | 2 +- .../Entities/Clothing/Shoes/misc.yml | 5 +-- 8 files changed, 80 insertions(+), 27 deletions(-) diff --git a/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs b/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs index 866ce38a57..8dc496ec25 100644 --- a/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs +++ b/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs @@ -15,6 +15,13 @@ public sealed partial class ClothingSpeedModifierComponent : Component [DataField] public float SprintModifier = 1.0f; + + /// + /// An optional required standing state. + /// Set to true if you need to be standing, false if you need to not be standing, null if you don't care. + /// + [DataField] + public bool? Standing; } [Serializable, NetSerializable] diff --git a/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs b/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs index 56758654ed..29f063c7fa 100644 --- a/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs +++ b/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Inventory; using Content.Shared.Item.ItemToggle; using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Movement.Systems; +using Content.Shared.Standing; using Content.Shared.Verbs; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -12,10 +13,11 @@ namespace Content.Shared.Clothing; public sealed class ClothingSpeedModifierSystem : EntitySystem { - [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly ExamineSystemShared _examine = default!; - [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!; [Dependency] private readonly ItemToggleSystem _toggle = default!; + [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly StandingStateSystem _standing = default!; public override void Initialize() { @@ -54,8 +56,13 @@ public sealed class ClothingSpeedModifierSystem : EntitySystem private void OnRefreshMoveSpeed(EntityUid uid, ClothingSpeedModifierComponent component, InventoryRelayedEvent args) { - if (_toggle.IsActivated(uid)) - args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier); + if (!_toggle.IsActivated(uid)) + return; + + if (component.Standing != null && !_standing.IsMatchingState(args.Owner, component.Standing.Value)) + return; + + args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier); } private void OnClothingVerbExamine(EntityUid uid, ClothingSpeedModifierComponent component, GetVerbsEvent args) diff --git a/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs index 636a21533e..554db51a23 100644 --- a/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs @@ -1,23 +1,33 @@ using Content.Shared.Clothing.Components; using Content.Shared.Gravity; using Content.Shared.Inventory; +using Content.Shared.Standing; namespace Content.Shared.Clothing.EntitySystems; +/// +/// We check standing state on all clothing because we don't want you to have anti-gravity unless you're standing. +/// This is for balance reasons as it prevents you from wearing anti-grav clothing to cheese being stun cuffed, as +/// well as other worse things. +/// public sealed class AntiGravityClothingSystem : EntitySystem { - [Dependency] SharedGravitySystem _gravity = default!; + [Dependency] private readonly StandingStateSystem _standing = default!; + [Dependency] private readonly SharedGravitySystem _gravity = default!; + /// public override void Initialize() { SubscribeLocalEvent>(OnIsWeightless); SubscribeLocalEvent(OnEquipped); SubscribeLocalEvent(OnUnequipped); + SubscribeLocalEvent>(OnDowned); + SubscribeLocalEvent>(OnStood); } private void OnIsWeightless(Entity ent, ref InventoryRelayedEvent args) { - if (args.Args.Handled) + if (args.Args.Handled || _standing.IsDown(args.Owner)) return; args.Args.Handled = true; @@ -26,11 +36,29 @@ public sealed class AntiGravityClothingSystem : EntitySystem private void OnEquipped(Entity entity, ref ClothingGotEquippedEvent args) { + // This clothing item does nothing if we're not standing + if (_standing.IsDown(args.Wearer)) + return; + _gravity.RefreshWeightless(args.Wearer, true); } private void OnUnequipped(Entity entity, ref ClothingGotUnequippedEvent args) { + // This clothing item does nothing if we're not standing + if (_standing.IsDown(args.Wearer)) + return; + _gravity.RefreshWeightless(args.Wearer, false); } + + private void OnDowned(Entity entity, ref InventoryRelayedEvent args) + { + _gravity.RefreshWeightless(args.Owner, false); + } + + private void OnStood(Entity entity, ref InventoryRelayedEvent args) + { + _gravity.RefreshWeightless(args.Owner, true); + } } diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index f4a0ccb5de..5109930a2d 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -24,6 +24,7 @@ using Content.Shared.Overlays; using Content.Shared.Projectiles; using Content.Shared.Radio; using Content.Shared.Slippery; +using Content.Shared.Standing; using Content.Shared.Strip.Components; using Content.Shared.Temperature; using Content.Shared.Verbs; @@ -57,6 +58,8 @@ public partial class InventorySystem SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); + SubscribeLocalEvent(RelayInventoryEvent); + SubscribeLocalEvent(RelayInventoryEvent); // by-ref events SubscribeLocalEvent(RefRelayInventoryEvent); @@ -114,7 +117,7 @@ public partial class InventorySystem return; // this copies the by-ref event if it is a struct - var ev = new InventoryRelayedEvent(args); + var ev = new InventoryRelayedEvent(args, inventory.Owner); var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots); while (enumerator.NextItem(out var item)) { @@ -130,7 +133,7 @@ public partial class InventorySystem if (args.TargetSlots == SlotFlags.NONE) return; - var ev = new InventoryRelayedEvent(args); + var ev = new InventoryRelayedEvent(args, inventory.Owner); var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots); while (enumerator.NextItem(out var item)) { @@ -141,7 +144,7 @@ public partial class InventorySystem private void OnGetEquipmentVerbs(EntityUid uid, InventoryComponent component, GetVerbsEvent args) { // Automatically relay stripping related verbs to all equipped clothing. - var ev = new InventoryRelayedEvent>(args); + var ev = new InventoryRelayedEvent>(args, uid); var enumerator = new InventorySlotEnumerator(component); while (enumerator.NextItem(out var item, out var slotDef)) { @@ -153,7 +156,7 @@ 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 ev = new InventoryRelayedEvent>(args, uid); var enumerator = new InventorySlotEnumerator(component, SlotFlags.WITHOUT_POCKET); while (enumerator.NextItem(out var item)) { @@ -176,9 +179,12 @@ public sealed class InventoryRelayedEvent : EntityEventArgs { public TEvent Args; - public InventoryRelayedEvent(TEvent args) + public EntityUid Owner; + + public InventoryRelayedEvent(TEvent args, EntityUid owner) { Args = args; + Owner = owner; } } diff --git a/Content.Shared/Morgue/EntityStorageLayingDownOverrideSystem.cs b/Content.Shared/Morgue/EntityStorageLayingDownOverrideSystem.cs index 630135f36a..d11695321b 100644 --- a/Content.Shared/Morgue/EntityStorageLayingDownOverrideSystem.cs +++ b/Content.Shared/Morgue/EntityStorageLayingDownOverrideSystem.cs @@ -21,7 +21,7 @@ public sealed class EntityStorageLayingDownOverrideSystem : EntitySystem { // Explicitly check for standing state component, as entities without it will return false for IsDown() // which prevents inserting any kind of non-mobs into this container (which is unintended) - if (TryComp(ent, out var standingState) && !_standing.IsDown(ent, standingState)) + if (TryComp(ent, out var standingState) && !_standing.IsDown((ent, standingState))) args.Contents.Remove(ent); } } diff --git a/Content.Shared/Standing/StandingStateSystem.cs b/Content.Shared/Standing/StandingStateSystem.cs index 7f3b8c9aac..ffe8f2c156 100644 --- a/Content.Shared/Standing/StandingStateSystem.cs +++ b/Content.Shared/Standing/StandingStateSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Climbing.Events; using Content.Shared.Hands.Components; +using Content.Shared.Inventory; using Content.Shared.Movement.Events; using Content.Shared.Movement.Systems; using Content.Shared.Physics; @@ -69,12 +70,17 @@ public sealed class StandingStateSystem : EntitySystem ChangeLayers(entity); } - public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null) + public bool IsMatchingState(Entity entity, bool standing) { - if (!Resolve(uid, ref standingState, false)) + return standing != IsDown(entity); + } + + public bool IsDown(Entity entity) + { + if (!Resolve(entity, ref entity.Comp, false)) return false; - return !standingState.Standing; + return !entity.Comp.Standing; } public bool Down(EntityUid uid, @@ -213,29 +219,27 @@ public record struct DropHandItemsEvent(); /// /// Subscribe if you can potentially block a down attempt. /// -public sealed class DownAttemptEvent : CancellableEntityEventArgs -{ -} +public sealed class DownAttemptEvent : CancellableEntityEventArgs; /// /// Subscribe if you can potentially block a stand attempt. /// -public sealed class StandAttemptEvent : CancellableEntityEventArgs -{ -} +public sealed class StandAttemptEvent : CancellableEntityEventArgs; /// /// Raised when an entity becomes standing /// -public sealed class StoodEvent : EntityEventArgs +public sealed class StoodEvent : EntityEventArgs, IInventoryRelayEvent { -} + public SlotFlags TargetSlots { get; } = SlotFlags.FEET; +}; /// /// Raised when an entity is not standing /// -public sealed class DownedEvent : EntityEventArgs +public sealed class DownedEvent : EntityEventArgs, IInventoryRelayEvent { + public SlotFlags TargetSlots { get; } = SlotFlags.FEET; } /// diff --git a/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs b/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs index 098e3176d9..1a7bc88ec3 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs @@ -501,7 +501,7 @@ public abstract partial class SharedStunSystem // Targeted moth attack CancelKnockdownDoAfter((entity, entity.Comp)); - RemComp(entity); + RemCompDeferred(entity); } private void OnKnockdownAttempt(Entity entity, ref KnockDownAttemptEvent args) diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml index 26fbc0c7ae..69c167051b 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml @@ -135,6 +135,7 @@ - type: ClothingSpeedModifier walkModifier: 1.5 sprintModifier: 1.5 + standing: true - type: Appearance - type: GenericVisualizer visuals: @@ -182,7 +183,7 @@ price: 75 - type: Tag tags: [ ] - + - type: entity parent: ClothingShoesBase id: ClothingShoesBootsJump @@ -200,7 +201,7 @@ jumpSound: /Audio/Effects/stealthoff.ogg - type: ActionGrant actions: - - ActionGravityJump + - ActionGravityJump - type: ItemActionGrant actions: - ActionGravityJump From db84d766e9fa6bd28e7da5ccd0dbb7da74c1be42 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 20 Aug 2025 11:09:38 +0000 Subject: [PATCH 019/108] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c851f1d086..10438ead6a 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: sowelipililimute - changes: - - message: Medical and security techfab will no longer announce more than five recipes - at once, avoiding chat flooding - type: Tweak - id: 8359 - time: '2025-04-26T21:45:53.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36945 - author: Nox38 changes: - message: Added descriptions for the .30 caliber and weapons. @@ -3949,3 +3941,12 @@ id: 8871 time: '2025-08-19T20:31:02.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/39586 +- author: Princess-Cheeseballs + changes: + - message: Speed boots no longer affect crawling speed. + type: Remove + - message: Moon boots don't work unless you're standing + type: Tweak + id: 8872 + time: '2025-08-20T11:08:31.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/39648 From 2bbff7f8c0c492c418d3330276b977fe72b7ad44 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:52:03 +0200 Subject: [PATCH 020/108] Cleanup subdermal implant code (#39755) --- Content.Client/Implants/ImplanterSystem.cs | 2 + .../Implants/SubdermalImplantSystem.cs | 5 + .../Implants/ChameleonControllerSystem.cs | 8 +- Content.Server/Implants/ImplanterSystem.cs | 1 + Content.Server/Implants/RadioImplantSystem.cs | 22 +- .../Implants/SubdermalImplantSystem.cs | 1 + Content.Server/Mindshield/MindShieldSystem.cs | 10 +- .../Components/ReplacementImplantComponent.cs | 18 ++ .../Components/StorageImplantComponent.cs | 11 + .../Components/SubdermalImplantComponent.cs | 36 ++- .../Implants/ReplacementImplantSystem.cs | 34 +++ .../Implants/SharedImplanterSystem.cs | 4 +- .../SharedSubdermalImplantSystem.Relays.cs | 50 ++++ .../Implants/SharedSubdermalImplantSystem.cs | 195 ++++++------- .../Implants/StorageImplantSystem.cs | 36 +++ ...hieldSystem.cs => FakeMindshieldSystem.cs} | 6 +- .../SharedFakeMindShieldImplantSystem.cs | 45 --- Resources/Prototypes/Actions/types.yml | 1 + .../Entities/Objects/Misc/implanters.yml | 274 +++++++++--------- .../Objects/Misc/subdermal_implants.yml | 71 +++-- 20 files changed, 462 insertions(+), 368 deletions(-) create mode 100644 Content.Client/Implants/SubdermalImplantSystem.cs create mode 100644 Content.Shared/Implants/Components/ReplacementImplantComponent.cs create mode 100644 Content.Shared/Implants/Components/StorageImplantComponent.cs create mode 100644 Content.Shared/Implants/ReplacementImplantSystem.cs create mode 100644 Content.Shared/Implants/SharedSubdermalImplantSystem.Relays.cs create mode 100644 Content.Shared/Implants/StorageImplantSystem.cs rename Content.Shared/Mindshield/FakeMindShield/{SharedFakeMindshieldSystem.cs => FakeMindshieldSystem.cs} (94%) delete mode 100644 Content.Shared/Mindshield/FakeMindShield/SharedFakeMindShieldImplantSystem.cs diff --git a/Content.Client/Implants/ImplanterSystem.cs b/Content.Client/Implants/ImplanterSystem.cs index cca09f5dad..4ba4d015ca 100644 --- a/Content.Client/Implants/ImplanterSystem.cs +++ b/Content.Client/Implants/ImplanterSystem.cs @@ -23,6 +23,8 @@ public sealed class ImplanterSystem : SharedImplanterSystem { if (_uiSystem.TryGetOpenUi(uid, DeimplantUiKey.Key, out var bui)) { + // TODO: Don't use protoId for deimplanting + // and especially not raw strings! Dictionary implants = new(); foreach (var implant in component.DeimplantWhitelist) { diff --git a/Content.Client/Implants/SubdermalImplantSystem.cs b/Content.Client/Implants/SubdermalImplantSystem.cs new file mode 100644 index 0000000000..5d814eddf8 --- /dev/null +++ b/Content.Client/Implants/SubdermalImplantSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Implants; + +namespace Content.Client.Implants; + +public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem; diff --git a/Content.Server/Implants/ChameleonControllerSystem.cs b/Content.Server/Implants/ChameleonControllerSystem.cs index 9e876f9399..930f2e3156 100644 --- a/Content.Server/Implants/ChameleonControllerSystem.cs +++ b/Content.Server/Implants/ChameleonControllerSystem.cs @@ -29,17 +29,17 @@ public sealed class ChameleonControllerSystem : SharedChameleonControllerSystem { base.Initialize(); - SubscribeLocalEvent(OnSelected); + SubscribeLocalEvent(OnSelected); SubscribeLocalEvent>(ChameleonControllerOutfitItemSelected); } - private void OnSelected(Entity ent, ref ChameleonControllerSelectedOutfitMessage args) + private void OnSelected(Entity ent, ref ChameleonControllerSelectedOutfitMessage args) { - if (!_delay.TryResetDelay(ent.Owner, true) || ent.Comp.ImplantedEntity == null || !HasComp(ent)) + if (!TryComp(ent, out var implantComp) || implantComp.ImplantedEntity == null || !_delay.TryResetDelay(ent.Owner, true)) return; - ChangeChameleonClothingToOutfit(ent.Comp.ImplantedEntity.Value, args.SelectedChameleonOutfit); + ChangeChameleonClothingToOutfit(implantComp.ImplantedEntity.Value, args.SelectedChameleonOutfit); } /// diff --git a/Content.Server/Implants/ImplanterSystem.cs b/Content.Server/Implants/ImplanterSystem.cs index 5023b1b3e4..48e83e4878 100644 --- a/Content.Server/Implants/ImplanterSystem.cs +++ b/Content.Server/Implants/ImplanterSystem.cs @@ -27,6 +27,7 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem SubscribeLocalEvent(OnDraw); } + // TODO: This all needs to be moved to shared and predicted. private void OnImplanterAfterInteract(EntityUid uid, ImplanterComponent component, AfterInteractEvent args) { if (args.Target == null || !args.CanReach || args.Handled) diff --git a/Content.Server/Implants/RadioImplantSystem.cs b/Content.Server/Implants/RadioImplantSystem.cs index d9ba229057..c5ae1ce494 100644 --- a/Content.Server/Implants/RadioImplantSystem.cs +++ b/Content.Server/Implants/RadioImplantSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Radio.Components; using Content.Shared.Implants; using Content.Shared.Implants.Components; -using Robust.Shared.Containers; namespace Content.Server.Implants; @@ -12,7 +11,7 @@ public sealed class RadioImplantSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnImplantImplanted); - SubscribeLocalEvent(OnRemove); + SubscribeLocalEvent(OnImplantRemoved); } /// @@ -20,19 +19,16 @@ public sealed class RadioImplantSystem : EntitySystem /// private void OnImplantImplanted(Entity ent, ref ImplantImplantedEvent args) { - if (args.Implanted == null) - return; - - var activeRadio = EnsureComp(args.Implanted.Value); + var activeRadio = EnsureComp(args.Implanted); foreach (var channel in ent.Comp.RadioChannels) { if (activeRadio.Channels.Add(channel)) ent.Comp.ActiveAddedChannels.Add(channel); } - EnsureComp(args.Implanted.Value); + EnsureComp(args.Implanted); - var intrinsicRadioTransmitter = EnsureComp(args.Implanted.Value); + var intrinsicRadioTransmitter = EnsureComp(args.Implanted); foreach (var channel in ent.Comp.RadioChannels) { if (intrinsicRadioTransmitter.Channels.Add(channel)) @@ -43,9 +39,9 @@ public sealed class RadioImplantSystem : EntitySystem /// /// Removes intrinsic radio components once the Radio Implant is removed /// - private void OnRemove(Entity ent, ref EntGotRemovedFromContainerMessage args) + private void OnImplantRemoved(Entity ent, ref ImplantRemovedEvent args) { - if (TryComp(args.Container.Owner, out var activeRadioComponent)) + if (TryComp(args.Implanted, out var activeRadioComponent)) { foreach (var channel in ent.Comp.ActiveAddedChannels) { @@ -55,11 +51,11 @@ public sealed class RadioImplantSystem : EntitySystem if (activeRadioComponent.Channels.Count == 0) { - RemCompDeferred(args.Container.Owner); + RemCompDeferred(args.Implanted); } } - if (!TryComp(args.Container.Owner, out var radioTransmitterComponent)) + if (!TryComp(args.Implanted, out var radioTransmitterComponent)) return; foreach (var channel in ent.Comp.TransmitterAddedChannels) @@ -70,7 +66,7 @@ public sealed class RadioImplantSystem : EntitySystem if (radioTransmitterComponent.Channels.Count == 0 || activeRadioComponent?.Channels.Count == 0) { - RemCompDeferred(args.Container.Owner); + RemCompDeferred(args.Implanted); } } } diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index f0530358a6..582b9cb2ac 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -18,6 +18,7 @@ public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem SubscribeLocalEvent>(OnStoreRelay); } + // TODO: This shouldn't be in the SubdermalImplantSystem private void OnStoreRelay(EntityUid uid, StoreComponent store, ImplantRelayEvent implantRelay) { var args = implantRelay.Event; diff --git a/Content.Server/Mindshield/MindShieldSystem.cs b/Content.Server/Mindshield/MindShieldSystem.cs index c04fb12027..bc5b65159b 100644 --- a/Content.Server/Mindshield/MindShieldSystem.cs +++ b/Content.Server/Mindshield/MindShieldSystem.cs @@ -27,7 +27,7 @@ public sealed class MindShieldSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnImplantImplanted); - SubscribeLocalEvent(OnImplantDraw); + SubscribeLocalEvent(OnImplantRemoved); } private void OnImplantImplanted(Entity ent, ref ImplantImplantedEvent ev) @@ -35,8 +35,8 @@ public sealed class MindShieldSystem : EntitySystem if (ev.Implanted == null) return; - EnsureComp(ev.Implanted.Value); - MindShieldRemovalCheck(ev.Implanted.Value, ev.Implant); + EnsureComp(ev.Implanted); + MindShieldRemovalCheck(ev.Implanted, ev.Implant); } /// @@ -58,9 +58,9 @@ public sealed class MindShieldSystem : EntitySystem } } - private void OnImplantDraw(Entity ent, ref EntGotRemovedFromContainerMessage args) + private void OnImplantRemoved(Entity ent, ref ImplantRemovedEvent args) { - RemComp(args.Container.Owner); + RemComp(args.Implanted); } } diff --git a/Content.Shared/Implants/Components/ReplacementImplantComponent.cs b/Content.Shared/Implants/Components/ReplacementImplantComponent.cs new file mode 100644 index 0000000000..73a6d4d8b4 --- /dev/null +++ b/Content.Shared/Implants/Components/ReplacementImplantComponent.cs @@ -0,0 +1,18 @@ +using Content.Shared.Whitelist; +using Robust.Shared.GameStates; + +namespace Content.Shared.Implants.Components; + +/// +/// Added to implants with the see . +/// When implanted it will cause other implants in the whitelist to be deleted and thus replaced. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class ReplacementImplantComponent : Component +{ + /// + /// Whitelist for which implants to delete. + /// + [DataField(required: true)] + public EntityWhitelist Whitelist = new(); +} diff --git a/Content.Shared/Implants/Components/StorageImplantComponent.cs b/Content.Shared/Implants/Components/StorageImplantComponent.cs new file mode 100644 index 0000000000..289f01bfce --- /dev/null +++ b/Content.Shared/Implants/Components/StorageImplantComponent.cs @@ -0,0 +1,11 @@ +using Content.Shared.Storage; +using Robust.Shared.GameStates; + +namespace Content.Shared.Implants.Components; + +/// +/// Handles emptying the implant's when the implant is removed. +/// Without this the contents would be deleted. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class StorageImplantComponent : Component; diff --git a/Content.Shared/Implants/Components/SubdermalImplantComponent.cs b/Content.Shared/Implants/Components/SubdermalImplantComponent.cs index 390d113dfb..85f2672603 100644 --- a/Content.Shared/Implants/Components/SubdermalImplantComponent.cs +++ b/Content.Shared/Implants/Components/SubdermalImplantComponent.cs @@ -16,13 +16,21 @@ public sealed partial class SubdermalImplantComponent : Component /// /// Used where you want the implant to grant the owner an instant action. /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("implantAction")] + [DataField] public EntProtoId? ImplantAction; + /// + /// The provided action entity. + /// [DataField, AutoNetworkedField] public EntityUid? Action; + /// + /// Components to add/remove to the implantee when the implant is injected/extracted. + /// + [DataField] + public ComponentRegistry ImplantComponents = new(); + /// /// The entity this implant is inside /// @@ -32,8 +40,7 @@ public sealed partial class SubdermalImplantComponent : Component /// /// Should this implant be removeable? /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("permanent"), AutoNetworkedField] + [DataField, AutoNetworkedField] public bool Permanent = false; /// @@ -61,23 +68,20 @@ public sealed partial class SubdermalImplantComponent : Component /// /// Used for opening the storage implant via action. /// -public sealed partial class OpenStorageImplantEvent : InstantActionEvent -{ - -} +/// +/// TODO: Delete this and just add a ToggleUIOnTriggerComponent +/// +public sealed partial class OpenStorageImplantEvent : InstantActionEvent; /// /// Used for triggering trigger events on the implant via action /// -public sealed partial class ActivateImplantEvent : InstantActionEvent -{ - -} +public sealed partial class ActivateImplantEvent : InstantActionEvent; /// /// Used for opening the uplink implant via action. /// -public sealed partial class OpenUplinkImplantEvent : InstantActionEvent -{ - -} +/// +/// TODO: Delete this and just add a ToggleUIOnTriggerComponent +/// +public sealed partial class OpenUplinkImplantEvent : InstantActionEvent; diff --git a/Content.Shared/Implants/ReplacementImplantSystem.cs b/Content.Shared/Implants/ReplacementImplantSystem.cs new file mode 100644 index 0000000000..b206091fe3 --- /dev/null +++ b/Content.Shared/Implants/ReplacementImplantSystem.cs @@ -0,0 +1,34 @@ +using Content.Shared.Implants.Components; +using Content.Shared.Whitelist; +using Robust.Shared.Containers; + +namespace Content.Shared.Implants; + +public sealed class ReplacementImplantSystem : EntitySystem +{ + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnImplantImplanted); + } + + private void OnImplantImplanted(Entity ent, ref ImplantImplantedEvent args) + { + if (!_container.TryGetContainer(args.Implanted, ImplanterComponent.ImplantSlotId, out var implantContainer)) + return; + + foreach (var implant in implantContainer.ContainedEntities) + { + if (implant == ent.Owner) + continue; // don't delete the replacement + + if (_whitelist.IsWhitelistPass(ent.Comp.Whitelist, implant)) + PredictedQueueDel(implant); + } + + } +} diff --git a/Content.Shared/Implants/SharedImplanterSystem.cs b/Content.Shared/Implants/SharedImplanterSystem.cs index 6e806384a0..6ff6d42d3b 100644 --- a/Content.Shared/Implants/SharedImplanterSystem.cs +++ b/Content.Shared/Implants/SharedImplanterSystem.cs @@ -118,7 +118,7 @@ public abstract class SharedImplanterSystem : EntitySystem //Set to draw mode if not implant only public void Implant(EntityUid user, EntityUid target, EntityUid implanter, ImplanterComponent component) { - if (!CanImplant(user, target, implanter, component, out var implant, out var implantComp)) + if (!CanImplant(user, target, implanter, component, out var implant, out _)) return; // Check if we are trying to implant a implant which is already implanted @@ -137,7 +137,6 @@ public abstract class SharedImplanterSystem : EntitySystem if (component.ImplanterSlot.ContainerSlot != null) _container.Remove(implant.Value, component.ImplanterSlot.ContainerSlot); - implantComp.ImplantedEntity = target; implantContainer.OccludesLight = false; _container.Insert(implant.Value, implantContainer); @@ -280,7 +279,6 @@ public abstract class SharedImplanterSystem : EntitySystem private void DrawImplantIntoImplanter(EntityUid implanter, EntityUid target, EntityUid implant, BaseContainer implantContainer, ContainerSlot implanterContainer, SubdermalImplantComponent implantComp) { _container.Remove(implant, implantContainer); - implantComp.ImplantedEntity = null; _container.Insert(implant, implanterContainer); var ev = new TransferDnaEvent { Donor = target, Recipient = implanter }; diff --git a/Content.Shared/Implants/SharedSubdermalImplantSystem.Relays.cs b/Content.Shared/Implants/SharedSubdermalImplantSystem.Relays.cs new file mode 100644 index 0000000000..4c0b2c2361 --- /dev/null +++ b/Content.Shared/Implants/SharedSubdermalImplantSystem.Relays.cs @@ -0,0 +1,50 @@ +using Content.Shared.Implants.Components; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Mobs; + +namespace Content.Shared.Implants; + +public abstract partial class SharedSubdermalImplantSystem +{ + public void InitializeRelay() + { + SubscribeLocalEvent(RelayToImplantEvent); + SubscribeLocalEvent(RelayToImplantEvent); + SubscribeLocalEvent(RelayToImplantEvent); + } + + /// + /// Relays events from the implanted to the implant. + /// + private void RelayToImplantEvent(EntityUid uid, ImplantedComponent component, T args) where T : notnull + { + if (!_container.TryGetContainer(uid, ImplanterComponent.ImplantSlotId, out var implantContainer)) + return; + + var relayEv = new ImplantRelayEvent(args, uid); + foreach (var implant in implantContainer.ContainedEntities) + { + if (args is HandledEntityEventArgs { Handled: true }) + return; + + RaiseLocalEvent(implant, relayEv); + } + } +} + +/// +/// Wrapper for relaying events from an implanted entity to their implants. +/// +public sealed class ImplantRelayEvent where T : notnull +{ + public readonly T Event; + + public readonly EntityUid ImplantedEntity; + + public ImplantRelayEvent(T ev, EntityUid implantedEntity) + { + Event = ev; + ImplantedEntity = implantedEntity; + } +} diff --git a/Content.Shared/Implants/SharedSubdermalImplantSystem.cs b/Content.Shared/Implants/SharedSubdermalImplantSystem.cs index 4c015f1209..630416b598 100644 --- a/Content.Shared/Implants/SharedSubdermalImplantSystem.cs +++ b/Content.Shared/Implants/SharedSubdermalImplantSystem.cs @@ -1,90 +1,76 @@ -using System.Linq; using Content.Shared.Actions; using Content.Shared.Implants.Components; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Mobs; -using Content.Shared.Tag; -using JetBrains.Annotations; using Robust.Shared.Containers; +using Robust.Shared.Network; using Robust.Shared.Prototypes; +using Robust.Shared.Timing; namespace Content.Shared.Implants; -public abstract class SharedSubdermalImplantSystem : EntitySystem +public abstract partial class SharedSubdermalImplantSystem : EntitySystem { - [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly SharedContainerSystem _container = default!; - [Dependency] private readonly TagSystem _tag = default!; - [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - - public const string BaseStorageId = "storagebase"; - - private static readonly ProtoId MicroBombTag = "MicroBomb"; - private static readonly ProtoId MacroBombTag = "MacroBomb"; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly IGameTiming _timing = default!; public override void Initialize() { + base.Initialize(); + InitializeRelay(); + SubscribeLocalEvent(OnInsert); SubscribeLocalEvent(OnRemoveAttempt); SubscribeLocalEvent(OnRemove); - - SubscribeLocalEvent(RelayToImplantEvent); - SubscribeLocalEvent(RelayToImplantEvent); - SubscribeLocalEvent(RelayToImplantEvent); } - private void OnInsert(EntityUid uid, SubdermalImplantComponent component, EntGotInsertedIntoContainerMessage args) + private void OnInsert(Entity ent, ref EntGotInsertedIntoContainerMessage args) { - if (component.ImplantedEntity == null) + // The results of the container change are already networked on their own + if (_timing.ApplyingState) return; - if (!string.IsNullOrWhiteSpace(component.ImplantAction)) - { - _actionsSystem.AddAction(component.ImplantedEntity.Value, ref component.Action, component.ImplantAction, uid); - } + if (args.Container.ID != ImplanterComponent.ImplantSlotId) + return; - // replace micro bomb with macro bomb - // TODO: this shouldn't be hardcoded here - if (_container.TryGetContainer(component.ImplantedEntity.Value, ImplanterComponent.ImplantSlotId, out var implantContainer) && _tag.HasTag(uid, MacroBombTag)) - { - foreach (var implant in implantContainer.ContainedEntities) - { - if (_tag.HasTag(implant, MicroBombTag)) - { - _container.Remove(implant, implantContainer); - PredictedQueueDel(implant); - } - } - } + ent.Comp.ImplantedEntity = args.Container.Owner; + Dirty(ent); - var ev = new ImplantImplantedEvent(uid, component.ImplantedEntity.Value); - RaiseLocalEvent(uid, ref ev); + EntityManager.AddComponents(ent.Comp.ImplantedEntity.Value, ent.Comp.ImplantComponents); + if (ent.Comp.ImplantAction != null) + _actions.AddAction(ent.Comp.ImplantedEntity.Value, ref ent.Comp.Action, ent.Comp.ImplantAction, ent.Owner); + + var ev = new ImplantImplantedEvent(ent.Owner, ent.Comp.ImplantedEntity.Value); + RaiseLocalEvent(ent.Owner, ref ev); } - private void OnRemoveAttempt(EntityUid uid, SubdermalImplantComponent component, ContainerGettingRemovedAttemptEvent args) + private void OnRemoveAttempt(Entity ent, ref ContainerGettingRemovedAttemptEvent args) { - if (component.Permanent && component.ImplantedEntity != null) + if (ent.Comp.Permanent && ent.Comp.ImplantedEntity != null) args.Cancel(); } - private void OnRemove(EntityUid uid, SubdermalImplantComponent component, EntGotRemovedFromContainerMessage args) + private void OnRemove(Entity ent, ref EntGotRemovedFromContainerMessage args) { - if (component.ImplantedEntity == null || Terminating(component.ImplantedEntity.Value)) + // The results of the container change are already networked on their own + if (_timing.ApplyingState) return; - if (component.ImplantAction != null) - _actionsSystem.RemoveProvidedActions(component.ImplantedEntity.Value, uid); - - if (!_container.TryGetContainer(uid, BaseStorageId, out var storageImplant)) + if (args.Container.ID != ImplanterComponent.ImplantSlotId) return; - var containedEntites = storageImplant.ContainedEntities.ToArray(); + if (ent.Comp.ImplantedEntity == null || Terminating(ent.Comp.ImplantedEntity.Value)) + return; - foreach (var entity in containedEntites) - { - _transformSystem.DropNextTo(entity, uid); - } + EntityManager.RemoveComponents(ent.Comp.ImplantedEntity.Value, ent.Comp.ImplantComponents); + _actions.RemoveAction(ent.Comp.ImplantedEntity.Value, ent.Comp.Action); + ent.Comp.Action = null; + + var ev = new ImplantRemovedEvent(ent.Owner, ent.Comp.ImplantedEntity.Value); + RaiseLocalEvent(ent.Owner, ref ev); + + ent.Comp.ImplantedEntity = null; + Dirty(ent); } /// @@ -106,23 +92,26 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem /// /// The implant, if it was successfully created. Otherwise, null. /// > - public EntityUid? AddImplant(EntityUid uid, String implantId) + public EntityUid? AddImplant(EntityUid target, EntProtoId implantId) { - var coords = Transform(uid).Coordinates; - var ent = Spawn(implantId, coords); + if (_net.IsClient) + return null; // can't interact with predicted spawns yet - if (TryComp(ent, out var implant)) + var coords = Transform(target).Coordinates; + var implant = Spawn(implantId, coords); + + if (TryComp(implant, out var implantComp)) { - ForceImplant(uid, ent, implant); + ForceImplant(target, (implant, implantComp)); } else { - Log.Warning($"Found invalid starting implant '{implantId}' on {uid} {ToPrettyString(uid):implanted}"); - Del(ent); + Log.Warning($"Tried to inject implant '{implantId}' without SubdermalImplantComponent into {ToPrettyString(target):implanted}"); + Del(implant); return null; } - return ent; + return implant; } /// @@ -131,15 +120,16 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem /// /// The entity to be implanted /// The implant - /// The implant component - public void ForceImplant(EntityUid target, EntityUid implant, SubdermalImplantComponent component) + public void ForceImplant(EntityUid target, Entity implant) { + if (!Resolve(implant, ref implant.Comp)) + return; + //If the target doesn't have the implanted component, add it. var implantedComp = EnsureComp(target); - var implantContainer = implantedComp.ImplantContainer; - component.ImplantedEntity = target; - _container.Insert(implant, implantContainer); + implant.Comp.ImplantedEntity = target; + _container.Insert(implant.Owner, implantedComp.ImplantContainer); } /// @@ -147,60 +137,25 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem /// /// the implanted entity /// the implant - [PublicAPI] - public void ForceRemove(EntityUid target, EntityUid implant) + public void ForceRemove(Entity target, EntityUid implant) { - if (!TryComp(target, out var implanted)) + if (!Resolve(target, ref target.Comp)) return; - var implantContainer = implanted.ImplantContainer; - - _container.Remove(implant, implantContainer); - QueueDel(implant); + _container.Remove(implant, target.Comp.ImplantContainer); + PredictedQueueDel(implant); } /// /// Removes and deletes implants by force /// /// The entity to have implants removed - [PublicAPI] - public void WipeImplants(EntityUid target) + public void WipeImplants(Entity target) { - if (!TryComp(target, out var implanted)) + if (!Resolve(target, ref target.Comp, false)) return; - var implantContainer = implanted.ImplantContainer; - - _container.CleanContainer(implantContainer); - } - - //Relays from the implanted to the implant - private void RelayToImplantEvent(EntityUid uid, ImplantedComponent component, T args) where T : notnull - { - if (!_container.TryGetContainer(uid, ImplanterComponent.ImplantSlotId, out var implantContainer)) - return; - - var relayEv = new ImplantRelayEvent(args, uid); - foreach (var implant in implantContainer.ContainedEntities) - { - if (args is HandledEntityEventArgs { Handled : true }) - return; - - RaiseLocalEvent(implant, relayEv); - } - } -} - -public sealed class ImplantRelayEvent where T : notnull -{ - public readonly T Event; - - public readonly EntityUid ImplantedEntity; - - public ImplantRelayEvent(T ev, EntityUid implantedEntity) - { - Event = ev; - ImplantedEntity = implantedEntity; + _container.CleanContainer(target.Comp.ImplantContainer); } } @@ -212,12 +167,30 @@ public sealed class ImplantRelayEvent where T : notnull /// implant implant implant implant /// [ByRefEvent] -public readonly struct ImplantImplantedEvent +public readonly record struct ImplantImplantedEvent { public readonly EntityUid Implant; - public readonly EntityUid? Implanted; + public readonly EntityUid Implanted; - public ImplantImplantedEvent(EntityUid implant, EntityUid? implanted) + public ImplantImplantedEvent(EntityUid implant, EntityUid implanted) + { + Implant = implant; + Implanted = implanted; + } +} + +/// +/// Event that is raised whenever an implant is removed from someone. +/// Raised on the the implant entity. +/// + +[ByRefEvent] +public readonly record struct ImplantRemovedEvent +{ + public readonly EntityUid Implant; + public readonly EntityUid Implanted; + + public ImplantRemovedEvent(EntityUid implant, EntityUid implanted) { Implant = implant; Implanted = implanted; diff --git a/Content.Shared/Implants/StorageImplantSystem.cs b/Content.Shared/Implants/StorageImplantSystem.cs new file mode 100644 index 0000000000..748f9f3ad2 --- /dev/null +++ b/Content.Shared/Implants/StorageImplantSystem.cs @@ -0,0 +1,36 @@ +using System.Linq; +using Content.Shared.Implants.Components; +using Content.Shared.Storage; +using Robust.Shared.Containers; +using Robust.Shared.Network; + +namespace Content.Shared.Implants; + +public sealed class StorageImplantSystem : EntitySystem +{ + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly INetManager _net = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnImplantRemoved); + } + + private void OnImplantRemoved(Entity ent, ref ImplantRemovedEvent args) + { + if (_net.IsClient) + return; // TODO: RandomPredicted and DropNextToPredicted + + if (!_container.TryGetContainer(ent.Owner, StorageComponent.ContainerId, out var storageImplant)) + return; + + var contained = storageImplant.ContainedEntities.ToArray(); + foreach (var entity in contained) + { + _transform.DropNextTo(entity, ent.Owner); + } + } +} diff --git a/Content.Shared/Mindshield/FakeMindShield/SharedFakeMindshieldSystem.cs b/Content.Shared/Mindshield/FakeMindShield/FakeMindshieldSystem.cs similarity index 94% rename from Content.Shared/Mindshield/FakeMindShield/SharedFakeMindshieldSystem.cs rename to Content.Shared/Mindshield/FakeMindShield/FakeMindshieldSystem.cs index c82f2b2863..4d7d457321 100644 --- a/Content.Shared/Mindshield/FakeMindShield/SharedFakeMindshieldSystem.cs +++ b/Content.Shared/Mindshield/FakeMindShield/FakeMindshieldSystem.cs @@ -8,7 +8,7 @@ using Robust.Shared.Timing; namespace Content.Shared.Mindshield.FakeMindShield; -public sealed class SharedFakeMindShieldSystem : EntitySystem +public sealed class FakeMindShieldSystem : EntitySystem { [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly TagSystem _tag = default!; @@ -24,9 +24,11 @@ public sealed class SharedFakeMindShieldSystem : EntitySystem SubscribeLocalEvent(OnChameleonControllerOutfitSelected); } - private void OnToggleMindshield(EntityUid uid, FakeMindShieldComponent comp, FakeMindShieldToggleEvent toggleEvent) + private void OnToggleMindshield(EntityUid uid, FakeMindShieldComponent comp, FakeMindShieldToggleEvent args) { comp.IsEnabled = !comp.IsEnabled; + args.Toggle = true; + args.Handled = true; Dirty(uid, comp); } diff --git a/Content.Shared/Mindshield/FakeMindShield/SharedFakeMindShieldImplantSystem.cs b/Content.Shared/Mindshield/FakeMindShield/SharedFakeMindShieldImplantSystem.cs deleted file mode 100644 index a597e03406..0000000000 --- a/Content.Shared/Mindshield/FakeMindShield/SharedFakeMindShieldImplantSystem.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Implants; -using Content.Shared.Implants.Components; -using Content.Shared.Mindshield.Components; -using Robust.Shared.Containers; - -namespace Content.Shared.Mindshield.FakeMindShield; - -public sealed class SharedFakeMindShieldImplantSystem : EntitySystem -{ - [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnFakeMindShieldToggle); - SubscribeLocalEvent(ImplantCheck); - SubscribeLocalEvent(ImplantDraw); - } - - /// - /// Raise the Action of a Implanted user toggling their implant to the FakeMindshieldComponent on their entity - /// - private void OnFakeMindShieldToggle(Entity entity, ref FakeMindShieldToggleEvent ev) - { - ev.Handled = true; - if (entity.Comp.ImplantedEntity is not { } ent) - return; - - if (!TryComp(ent, out var comp)) - return; - // TODO: is there a reason this cant set ev.Toggle = true; - _actionsSystem.SetToggled((ev.Action, ev.Action), !comp.IsEnabled); // Set it to what the Mindshield component WILL be after this - RaiseLocalEvent(ent, ev); //this reraises the action event to support an eventual future Changeling Antag which will also be using this component for it's "mindshield" ability - } - private void ImplantCheck(EntityUid uid, FakeMindShieldImplantComponent component ,ref ImplantImplantedEvent ev) - { - if (ev.Implanted != null) - EnsureComp(ev.Implanted.Value); - } - - private void ImplantDraw(Entity ent, ref EntGotRemovedFromContainerMessage ev) - { - RemComp(ev.Container.Owner); - } -} diff --git a/Resources/Prototypes/Actions/types.yml b/Resources/Prototypes/Actions/types.yml index 97435c229d..c8ea03502a 100644 --- a/Resources/Prototypes/Actions/types.yml +++ b/Resources/Prototypes/Actions/types.yml @@ -376,6 +376,7 @@ iconOn: { sprite: Interface/Actions/actions_fakemindshield.rsi, state: icon-on } itemIconStyle: NoItem useDelay: 1 + raiseOnUser: true - type: InstantAction event: !type:FakeMindShieldToggleEvent - type: Tag diff --git a/Resources/Prototypes/Entities/Objects/Misc/implanters.yml b/Resources/Prototypes/Entities/Objects/Misc/implanters.yml index e1918ef5e6..316acba6fc 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/implanters.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/implanters.yml @@ -6,89 +6,89 @@ parent: BaseItem abstract: true components: - - type: ItemSlots - - type: ContainerContainer - containers: - implanter_slot: !type:ContainerSlot { } - - type: Implanter + - type: ItemSlots + - type: ContainerContainer + containers: + implanter_slot: !type:ContainerSlot { } + - type: Implanter + whitelist: + components: + - MobState # no chair microbomb + blacklist: + components: + - Guardian # no holoparasite macrobomb wombo combo + tags: + - Unimplantable + currentMode: Draw + implanterSlot: + name: Implant + locked: True + priority: 0 whitelist: - components: - - MobState # no chair microbomb - blacklist: - components: - - Guardian # no holoparasite macrobomb wombo combo tags: - - Unimplantable - currentMode: Draw - implanterSlot: - name: Implant - locked: True - priority: 0 - whitelist: - tags: - - SubdermalImplant - allowDeimplantAll: false - deimplantWhitelist: - - SadTromboneImplant - - LightImplant - - BikeHornImplant - - TrackingImplant - - StorageImplant - - FreedomImplant - - UplinkImplant - - EmpImplant - - ScramImplant - - DnaScramblerImplant - - MicroBombImplant - - MacroBombImplant - - DeathAcidifierImplant - - DeathRattleImplant - - MindShieldImplant - - FakeMindShieldImplant - - RadioImplant - - ChameleonControllerImplant - deimplantFailureDamage: - types: - Cellular: 50 - Heat: 10 - - type: Sprite - sprite: Objects/Specific/Medical/implanter.rsi - state: implanter0 - layers: - - state: implanter0 - map: [ "implantOnly" ] - visible: true - - state: implanter1 - map: [ "implantFull" ] - visible: false - - type: Item - sprite: Objects/Specific/Medical/implanter.rsi - heldPrefix: implanter - size: Small - - type: Appearance - - type: GenericVisualizer - visuals: - enum.ImplanterVisuals.Full: - implantFull: - True: {visible: true} - False: {visible: false} - enum.ImplanterImplantOnlyVisuals.ImplantOnly: - implantOnly: - True: {state: broken} - False: {state: implanter0} - - type: UserInterface - interfaces: - enum.DeimplantUiKey.Key: - type: DeimplantBoundUserInterface + - SubdermalImplant + allowDeimplantAll: false + deimplantWhitelist: + - SadTromboneImplant + - LightImplant + - BikeHornImplant + - TrackingImplant + - StorageImplant + - FreedomImplant + - UplinkImplant + - EmpImplant + - ScramImplant + - DnaScramblerImplant + - MicroBombImplant + - MacroBombImplant + - DeathAcidifierImplant + - DeathRattleImplant + - MindShieldImplant + - FakeMindShieldImplant + - RadioImplant + - ChameleonControllerImplant + deimplantFailureDamage: + types: + Cellular: 50 + Heat: 10 + - type: Sprite + sprite: Objects/Specific/Medical/implanter.rsi + state: implanter0 + layers: + - state: implanter0 + map: [ "implantOnly" ] + visible: true + - state: implanter1 + map: [ "implantFull" ] + visible: false + - type: Item + sprite: Objects/Specific/Medical/implanter.rsi + heldPrefix: implanter + size: Small + - type: Appearance + - type: GenericVisualizer + visuals: + enum.ImplanterVisuals.Full: + implantFull: + True: {visible: true} + False: {visible: false} + enum.ImplanterImplantOnlyVisuals.ImplantOnly: + implantOnly: + True: {state: broken} + False: {state: implanter0} + - type: UserInterface + interfaces: + enum.DeimplantUiKey.Key: + type: DeimplantBoundUserInterface - type: entity id: Implanter parent: BaseImplanter description: A disposable syringe exclusively designed for the injection and extraction of subdermal implants. components: - - type: Tag - tags: - - Trash + - type: Tag + tags: + - Trash - type: entity parent: Implanter @@ -108,18 +108,18 @@ description: A disposable syringe exclusively designed for the injection of subdermal implants. abstract: true components: - - type: Sprite - sprite: Objects/Specific/Medical/implanter.rsi - state: implanter0 - layers: - - state: implanter1 - map: [ "implantFull" ] - visible: true - - state: implanter0 - map: [ "implantOnly" ] - - type: Implanter - currentMode: Inject - implantOnly: true + - type: Sprite + sprite: Objects/Specific/Medical/implanter.rsi + state: implanter0 + layers: + - state: implanter1 + map: [ "implantFull" ] + visible: true + - state: implanter0 + map: [ "implantOnly" ] + - type: Implanter + currentMode: Inject + implantOnly: true - type: entity id: BaseImplantOnlyImplanterSyndi @@ -128,30 +128,30 @@ description: A compact disposable syringe exclusively designed for the injection of subdermal implants. Make sure to scrub it with soap or a rag to remove residual DNA after use! abstract: true components: - - type: Item - sprite: Objects/Specific/Medical/syndi_implanter.rsi - heldPrefix: implanter - - type: Sprite - sprite: Objects/Specific/Medical/syndi_implanter.rsi - state: implanter1 - layers: - - state: implanter0 - map: [ "implantFull" ] - visible: true - - state: implanter1 - map: [ "implantOnly" ] - - type: GenericVisualizer - visuals: - enum.ImplanterVisuals.Full: - implantFull: - True: {visible: true} - False: {visible: false} - enum.ImplanterImplantOnlyVisuals.ImplantOnly: - implantOnly: - True: {state: broken} - False: {state: implanter1} - - type: Tag - tags: [] + - type: Item + sprite: Objects/Specific/Medical/syndi_implanter.rsi + heldPrefix: implanter + - type: Sprite + sprite: Objects/Specific/Medical/syndi_implanter.rsi + state: implanter1 + layers: + - state: implanter0 + map: [ "implantFull" ] + visible: true + - state: implanter1 + map: [ "implantOnly" ] + - type: GenericVisualizer + visuals: + enum.ImplanterVisuals.Full: + implantFull: + True: {visible: true} + False: {visible: false} + enum.ImplanterImplantOnlyVisuals.ImplantOnly: + implantOnly: + True: {state: broken} + False: {state: implanter1} + - type: Tag + tags: [] #Fun implanters @@ -160,24 +160,24 @@ name: sad trombone implanter parent: BaseImplantOnlyImplanter components: - - type: Implanter - implant: SadTromboneImplant + - type: Implanter + implant: SadTromboneImplant - type: entity id: LightImplanter name: light implanter parent: BaseImplantOnlyImplanter components: - - type: Implanter - implant: LightImplant + - type: Implanter + implant: LightImplant - type: entity id: BikeHornImplanter name: bike horn implanter parent: BaseImplantOnlyImplanter components: - - type: Implanter - implant: BikeHornImplant + - type: Implanter + implant: BikeHornImplant #Security implanters @@ -186,8 +186,8 @@ name: tracking implanter parent: BaseImplantOnlyImplanter components: - - type: Implanter - implant: TrackingImplant + - type: Implanter + implant: TrackingImplant #Traitor implanters @@ -196,16 +196,16 @@ name: storage implanter parent: BaseImplantOnlyImplanterSyndi components: - - type: Implanter - implant: StorageImplant + - type: Implanter + implant: StorageImplant - type: entity id: FreedomImplanter name: freedom implanter parent: BaseImplantOnlyImplanterSyndi components: - - type: Implanter - implant: FreedomImplant + - type: Implanter + implant: FreedomImplant - type: entity id: RadioImplanter @@ -228,24 +228,24 @@ name: EMP implanter parent: BaseImplantOnlyImplanterSyndi components: - - type: Implanter - implant: EmpImplant + - type: Implanter + implant: EmpImplant - type: entity id: ScramImplanter name: scram implanter parent: BaseImplantOnlyImplanterSyndi components: - - type: Implanter - implant: ScramImplant + - type: Implanter + implant: ScramImplant - type: entity id: DnaScramblerImplanter name: DNA scrambler implanter parent: BaseImplantOnlyImplanterSyndi components: - - type: Implanter - implant: DnaScramblerImplant + - type: Implanter + implant: DnaScramblerImplant - type: entity id: ChameleonControllerImplanter @@ -262,24 +262,24 @@ name: micro-bomb implanter parent: BaseImplantOnlyImplanterSyndi components: - - type: Implanter - implant: MicroBombImplant + - type: Implanter + implant: MicroBombImplant - type: entity id: MacroBombImplanter name: macro-bomb implanter parent: BaseImplantOnlyImplanterSyndi components: - - type: Implanter - implant: MacroBombImplant + - type: Implanter + implant: MacroBombImplant - type: entity id: DeathRattleImplanter name: death rattle implanter parent: BaseImplantOnlyImplanterSyndi components: - - type: Implanter - implant: DeathRattleImplant + - type: Implanter + implant: DeathRattleImplant - type: entity id: DeathAcidifierImplanter @@ -304,8 +304,8 @@ name: mindshield implanter parent: BaseImplantOnlyImplanter components: - - type: Implanter - implant: MindShieldImplant + - type: Implanter + implant: MindShieldImplant # Centcomm implanters diff --git a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml index 6a4ad24664..5ff8c79fb4 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml @@ -19,18 +19,18 @@ description: This implant plays a sad tune when the user dies. categories: [ HideSpawnMenu ] components: - - type: SubdermalImplant - whitelist: - components: - - MobState # admeme implanting a chair with trombone implant needs to give the chair mobstate so it can die first - - type: TriggerOnMobstateChange - mobState: - - Dead - - type: EmitSoundOnTrigger - sound: - collection: SadTrombone - params: - variation: 0.125 + - type: SubdermalImplant + whitelist: + components: + - MobState # admeme implanting a chair with trombone implant needs to give the chair mobstate so it can die first + - type: TriggerOnMobstateChange + mobState: + - Dead + - type: EmitSoundOnTrigger + sound: + collection: SadTrombone + params: + variation: 0.125 - type: entity parent: BaseSubdermalImplant @@ -39,20 +39,20 @@ description: This implant emits light from the user's skin on activation. categories: [ HideSpawnMenu ] components: - - type: SubdermalImplant - implantAction: ActionToggleLight - - type: PointLight - enabled: false - radius: 2.5 - softness: 5 - mask: /Textures/Effects/LightMasks/cone.png - autoRot: true - - type: Tag - tags: - - SubdermalImplant - - HideContextMenu - - Flashlight - - type: UnpoweredFlashlight + - type: SubdermalImplant + implantAction: ActionToggleLight + - type: PointLight + enabled: false + radius: 2.5 + softness: 5 + mask: /Textures/Effects/LightMasks/cone.png + autoRot: true + - type: Tag + tags: + - SubdermalImplant + - HideContextMenu + - Flashlight + - type: UnpoweredFlashlight - type: entity parent: BaseSubdermalImplant @@ -108,6 +108,7 @@ description: This implant grants hidden storage within a person's body using bluespace technology. categories: [ HideSpawnMenu ] components: + - type: StorageImplant - type: SubdermalImplant implantAction: ActionOpenStorageImplant whitelist: @@ -280,6 +281,10 @@ components: - type: SubdermalImplant permanent: true + - type: ReplacementImplant + whitelist: + tags: + - MicroBomb # replace microbomb implant with macrobomb - type: TriggerOnMobstateChange #activates the timer mobState: - Dead @@ -306,9 +311,9 @@ canCreateVacuum: true - type: Tag tags: - - SubdermalImplant - - HideContextMenu - - MacroBomb + - SubdermalImplant + - HideContextMenu + - MacroBomb - type: entity parent: BaseSubdermalImplant @@ -362,9 +367,11 @@ description: This implant allows the implanter to produce a fake signal that NT security huds use to identify individuals implanted with a mindshield. categories: [ HideSpawnMenu ] components: - - type: SubdermalImplant - implantAction: FakeMindShieldToggleAction - - type: FakeMindShieldImplant + - type: SubdermalImplant + implantAction: FakeMindShieldToggleAction + implantComponents: + - type: FakeMindShield # TODO: put the component on the implant and use implant relay events for the status icon + - type: FakeMindShieldImplant # Sec and Command implants From d1916fa4d3c6835df5004791d79a29867c42b486 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Wed, 20 Aug 2025 18:33:14 +0200 Subject: [PATCH 021/108] Revert "Fix: Untoggle removed actions" (#39776) Revert "Fix: Untoggle removed actions (#39526)" This reverts commit e90fac14eb9d8ecd7267ea7d7381ad0f3abc8886. --- Content.Shared/Actions/SharedActionsSystem.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index c0a32e6038..c4581cfbff 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -832,8 +832,7 @@ public abstract class SharedActionsSystem : EntitySystem performer.Comp.Actions.Remove(ent.Owner); Dirty(performer, performer.Comp); ent.Comp.AttachedEntity = null; - ent.Comp.Toggled = false; - DirtyFields(ent, ent.Comp, null, nameof(ActionComponent.AttachedEntity), nameof(ActionComponent.Toggled)); + DirtyField(ent, ent.Comp, nameof(ActionComponent.AttachedEntity)); ActionRemoved((performer, performer.Comp), ent); if (ent.Comp.Temporary) From dd74bfc083052bace7eaee20f04c0d9aa68f9faa Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Wed, 20 Aug 2025 15:25:53 -0400 Subject: [PATCH 022/108] Remove BodyComponent check from MobPrice test (#39786) --- Content.IntegrationTests/Tests/CargoTest.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Content.IntegrationTests/Tests/CargoTest.cs b/Content.IntegrationTests/Tests/CargoTest.cs index 5830ea59da..aad87b711a 100644 --- a/Content.IntegrationTests/Tests/CargoTest.cs +++ b/Content.IntegrationTests/Tests/CargoTest.cs @@ -5,7 +5,6 @@ using Content.Server.Cargo.Components; using Content.Server.Cargo.Systems; using Content.Server.Nutrition.Components; using Content.Server.Nutrition.EntitySystems; -using Content.Shared.Body.Components; using Content.Shared.Cargo.Prototypes; using Content.Shared.Mobs.Components; using Content.Shared.Prototypes; @@ -266,7 +265,6 @@ public sealed class CargoTest { foreach (var (proto, comp) in pair.GetPrototypesWithComponent()) { - Assert.That(proto.TryGetComponent(out _, componentFactory), $"Found MobPriceComponent on {proto.ID}, but no BodyComponent!"); Assert.That(proto.TryGetComponent(out _, componentFactory), $"Found MobPriceComponent on {proto.ID}, but no MobStateComponent!"); } }); From 9b8fa1af6f126cfa962e31d481accd03fafceb16 Mon Sep 17 00:00:00 2001 From: Perry Fraser Date: Wed, 20 Aug 2025 18:47:31 -0400 Subject: [PATCH 023/108] =?UTF-8?q?fix:=20spellbooks=20can=20have=20charge?= =?UTF-8?q?s=20=E2=89=A0=203=20(#38769)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: spellbooks can have charges ≠ 3 * refactor: just make setting MaxCharges an api thing * refactor: don't auto-add LimitedCharges * Small stuff --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../Components/LimitedChargesComponent.cs | 2 +- .../Charges/Systems/SharedChargesSystem.cs | 45 ++++++++++++++++++- Content.Shared/Magic/SpellbookSystem.cs | 26 +++++++---- 3 files changed, 63 insertions(+), 10 deletions(-) diff --git a/Content.Shared/Charges/Components/LimitedChargesComponent.cs b/Content.Shared/Charges/Components/LimitedChargesComponent.cs index e879e0f1df..7891857187 100644 --- a/Content.Shared/Charges/Components/LimitedChargesComponent.cs +++ b/Content.Shared/Charges/Components/LimitedChargesComponent.cs @@ -16,7 +16,7 @@ public sealed partial class LimitedChargesComponent : Component /// /// The max charges this action has. /// - [DataField, AutoNetworkedField, Access(Other = AccessPermissions.Read)] + [DataField, AutoNetworkedField] public int MaxCharges = 3; /// diff --git a/Content.Shared/Charges/Systems/SharedChargesSystem.cs b/Content.Shared/Charges/Systems/SharedChargesSystem.cs index e915580bae..504648c41d 100644 --- a/Content.Shared/Charges/Systems/SharedChargesSystem.cs +++ b/Content.Shared/Charges/Systems/SharedChargesSystem.cs @@ -94,6 +94,12 @@ public abstract class SharedChargesSystem : EntitySystem /// /// Adds the specified charges. Does not reset the accumulator. /// + /// + /// The action to add charges to. If it doesn't have , it will be added. + /// + /// + /// The number of charges to add. Can be negative. Resulting charge count is clamped to [0, MaxCharges]. + /// public void AddCharges(Entity action, int addCharges) { if (addCharges == 0) @@ -170,9 +176,21 @@ public abstract class SharedChargesSystem : EntitySystem Dirty(action); } + /// + /// Set the number of charges an action has. + /// + /// The action in question + /// + /// The number of charges. Clamped to [0, MaxCharges]. + /// + /// + /// This method doesn't implicitly add + /// unlike some other methods in this system. + /// public void SetCharges(Entity action, int value) { - action.Comp ??= EnsureComp(action.Owner); + if (!Resolve(action, ref action.Comp)) + return; var adjusted = Math.Clamp(value, 0, action.Comp.MaxCharges); @@ -186,6 +204,31 @@ public abstract class SharedChargesSystem : EntitySystem Dirty(action); } + /// + /// Sets the maximum charges of a given action. + /// + /// The action being modified. + /// The new maximum charges of the action. Clamped to zero. + /// + /// Does not change the current charge count, or adjust the + /// accumulator for auto-recharge. It also doesn't implicitly add + /// unlike some other methods + /// in this system. + /// + public void SetMaxCharges(Entity action, int value) + { + if (!Resolve(action, ref action.Comp)) + return; + + // You can't have negative max charges (even zero is a bit goofy but eh) + var adjusted = Math.Max(0, value); + if (action.Comp.MaxCharges == adjusted) + return; + + action.Comp.MaxCharges = adjusted; + Dirty(action); + } + /// /// The next time a charge will be considered to be filled. /// diff --git a/Content.Shared/Magic/SpellbookSystem.cs b/Content.Shared/Magic/SpellbookSystem.cs index ca01819bce..90038fa9ba 100644 --- a/Content.Shared/Magic/SpellbookSystem.cs +++ b/Content.Shared/Magic/SpellbookSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Actions; -using Content.Shared.Actions.Components; +using Content.Shared.Actions.Components; +using Content.Shared.Charges.Components; using Content.Shared.Charges.Systems; using Content.Shared.DoAfter; using Content.Shared.Interaction.Events; @@ -29,14 +30,19 @@ public sealed class SpellbookSystem : EntitySystem { foreach (var (id, charges) in ent.Comp.SpellActions) { - var spell = _actionContainer.AddAction(ent, id); - if (spell == null) + var action = _actionContainer.AddAction(ent, id); + if (action is not { } spell) continue; // Null means infinite charges. if (charges is { } count) - _sharedCharges.SetCharges(spell.Value, count); - ent.Comp.Spells.Add(spell.Value); + { + EnsureComp(spell, out var chargeComp); + _sharedCharges.SetMaxCharges((spell, chargeComp), count); + _sharedCharges.SetCharges((spell, chargeComp), count); + } + + ent.Comp.Spells.Add(spell); } } @@ -75,9 +81,13 @@ public sealed class SpellbookSystem : EntitySystem foreach (var (id, charges) in ent.Comp.SpellActions) { EntityUid? actionId = null; - if (_actions.AddAction(args.Args.User, ref actionId, id) - && charges is { } count) // Null means infinite charges - _sharedCharges.SetCharges(actionId.Value, count); + if (!_actions.AddAction(args.Args.User, ref actionId, id) + || charges is not { } count // Null means infinite charges + || !TryComp(actionId, out var chargeComp)) + continue; + + _sharedCharges.SetMaxCharges((actionId.Value, chargeComp), count); + _sharedCharges.SetCharges((actionId.Value, chargeComp), count); } } From def514bb3b293293dab242d7741a839d3ae67ec9 Mon Sep 17 00:00:00 2001 From: Kowlin <10947836+Kowlin@users.noreply.github.com> Date: Thu, 21 Aug 2025 01:34:40 +0200 Subject: [PATCH 024/108] Fix tricky nades not emitting their sounds. (#39792) --- .../Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml index d843463886..60ce9bd3ec 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml @@ -601,6 +601,7 @@ - timer sound: path: /Audio/Effects/Emotes/parp1.ogg + positional: true - type: Appearance - type: TimerTriggerVisuals primingSound: @@ -621,6 +622,7 @@ - timer sound: path: /Audio/Effects/Emotes/parp1.ogg + positional: true - type: Appearance - type: TimerTriggerVisuals primingSound: From f78280501a1757e7c6f124d1efc1bc7c2e470767 Mon Sep 17 00:00:00 2001 From: Prole <172158352+Prole0@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:35:38 -0700 Subject: [PATCH 025/108] Moving Zombie Components to Shared (#39791) * Moving Comps to Shared * Requested Changes --- Content.Server/Zombies/ZombifyOnDeathComponent.cs | 8 -------- .../Zombies/ZombieImmuneComponent.cs | 6 ++++-- Content.Shared/Zombies/ZombifyOnDeathComponent.cs | 12 ++++++++++++ 3 files changed, 16 insertions(+), 10 deletions(-) delete mode 100644 Content.Server/Zombies/ZombifyOnDeathComponent.cs rename {Content.Server => Content.Shared}/Zombies/ZombieImmuneComponent.cs (60%) create mode 100644 Content.Shared/Zombies/ZombifyOnDeathComponent.cs diff --git a/Content.Server/Zombies/ZombifyOnDeathComponent.cs b/Content.Server/Zombies/ZombifyOnDeathComponent.cs deleted file mode 100644 index dff17c4613..0000000000 --- a/Content.Server/Zombies/ZombifyOnDeathComponent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Content.Server.Zombies -{ - [RegisterComponent] - public sealed partial class ZombifyOnDeathComponent : Component - { - //this is not the component you are looking for - } -} diff --git a/Content.Server/Zombies/ZombieImmuneComponent.cs b/Content.Shared/Zombies/ZombieImmuneComponent.cs similarity index 60% rename from Content.Server/Zombies/ZombieImmuneComponent.cs rename to Content.Shared/Zombies/ZombieImmuneComponent.cs index 76a8ab9e67..18ad228363 100644 --- a/Content.Server/Zombies/ZombieImmuneComponent.cs +++ b/Content.Shared/Zombies/ZombieImmuneComponent.cs @@ -1,9 +1,11 @@ -namespace Content.Server.Zombies; +using Robust.Shared.GameStates; + +namespace Content.Shared.Zombies; /// /// Entities with this component cannot be zombified. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent] public sealed partial class ZombieImmuneComponent : Component { //still no diff --git a/Content.Shared/Zombies/ZombifyOnDeathComponent.cs b/Content.Shared/Zombies/ZombifyOnDeathComponent.cs new file mode 100644 index 0000000000..138ea6e409 --- /dev/null +++ b/Content.Shared/Zombies/ZombifyOnDeathComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Zombies; + +/// +/// Entities with this component zombify on death. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class ZombifyOnDeathComponent : Component +{ + //this is not the component you are looking for +} From e5011cb30515fe7368ef4e73e0c8160d1ada50dc Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 20 Aug 2025 23:35:50 +0000 Subject: [PATCH 026/108] 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 10438ead6a..422f819fcd 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Nox38 - changes: - - message: Added descriptions for the .30 caliber and weapons. - type: Add - id: 8360 - time: '2025-04-26T21:48:40.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36958 - author: UpAndLeaves changes: - message: The CMO's hardsuit has been redesigned with extra durathread to withstand @@ -3950,3 +3943,10 @@ id: 8872 time: '2025-08-20T11:08:31.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/39648 +- author: Kowlin + changes: + - message: Fixed Syndicate tricky grenades not playing sounds on explosion. + type: Fix + id: 8873 + time: '2025-08-20T23:34:40.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/39792 From d286a70d47bca6ea5c4835bd147f9b0bd97987e4 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Thu, 21 Aug 2025 02:42:42 +0200 Subject: [PATCH 027/108] cleanup changeling namespaces and prototypes (#39794) --- .../ChangelingTransformBoundUserInterface.cs | 2 +- .../Transform/ChangelingTransformMenu.xaml.cs | 2 +- ...em.Events.cs => ChangelingDevourEvents.cs} | 2 +- ...Events.cs => ChangelingTransformEvents.cs} | 2 +- .../ChangelingDevourComponent.cs | 3 +- .../ChangelingIdentityComponent.cs | 3 +- .../ChangelingStoredIdentityComponent.cs | 2 +- .../ChangelingTransformComponent.cs | 3 +- .../ChangelingDevourSystem.cs | 3 +- .../{ => Systems}/ChangelingIdentitySystem.cs | 3 +- .../ChangelingTransformSystem.UI.cs | 2 +- .../ChangelingTransformSystem.cs | 9 ++--- Resources/Prototypes/Actions/changeling.yml | 22 ++++++++++++ Resources/Prototypes/Antag/changeling.yml | 35 ------------------- .../Entities/Mobs/Player/changeling.yml | 12 +++++++ 15 files changed, 55 insertions(+), 50 deletions(-) rename Content.Shared/Changeling/{Devour/ChangelingDevourSystem.Events.cs => ChangelingDevourEvents.cs} (94%) rename Content.Shared/Changeling/{Transform/ChangelingTransformSystem.Events.cs => ChangelingTransformEvents.cs} (91%) rename Content.Shared/Changeling/{Devour => Components}/ChangelingDevourComponent.cs (98%) rename Content.Shared/Changeling/{ => Components}/ChangelingIdentityComponent.cs (95%) rename Content.Shared/Changeling/{ => Components}/ChangelingStoredIdentityComponent.cs (95%) rename Content.Shared/Changeling/{Transform => Components}/ChangelingTransformComponent.cs (95%) rename Content.Shared/Changeling/{Devour => Systems}/ChangelingDevourSystem.cs (99%) rename Content.Shared/Changeling/{ => Systems}/ChangelingIdentitySystem.cs (98%) rename Content.Shared/Changeling/{Transform => Systems}/ChangelingTransformSystem.UI.cs (95%) rename Content.Shared/Changeling/{Transform => Systems}/ChangelingTransformSystem.cs (95%) delete mode 100644 Resources/Prototypes/Antag/changeling.yml create mode 100644 Resources/Prototypes/Entities/Mobs/Player/changeling.yml diff --git a/Content.Client/Changeling/Transform/ChangelingTransformBoundUserInterface.cs b/Content.Client/Changeling/Transform/ChangelingTransformBoundUserInterface.cs index 8e383bc967..9401231303 100644 --- a/Content.Client/Changeling/Transform/ChangelingTransformBoundUserInterface.cs +++ b/Content.Client/Changeling/Transform/ChangelingTransformBoundUserInterface.cs @@ -1,4 +1,4 @@ -using Content.Shared.Changeling.Transform; +using Content.Shared.Changeling.Systems; using JetBrains.Annotations; using Robust.Client.UserInterface; diff --git a/Content.Client/Changeling/Transform/ChangelingTransformMenu.xaml.cs b/Content.Client/Changeling/Transform/ChangelingTransformMenu.xaml.cs index beef9ae427..fa2deaf431 100644 --- a/Content.Client/Changeling/Transform/ChangelingTransformMenu.xaml.cs +++ b/Content.Client/Changeling/Transform/ChangelingTransformMenu.xaml.cs @@ -1,6 +1,6 @@ using System.Numerics; using Content.Client.UserInterface.Controls; -using Content.Shared.Changeling.Transform; +using Content.Shared.Changeling.Systems; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; diff --git a/Content.Shared/Changeling/Devour/ChangelingDevourSystem.Events.cs b/Content.Shared/Changeling/ChangelingDevourEvents.cs similarity index 94% rename from Content.Shared/Changeling/Devour/ChangelingDevourSystem.Events.cs rename to Content.Shared/Changeling/ChangelingDevourEvents.cs index d063737e5c..ba8d25f30c 100644 --- a/Content.Shared/Changeling/Devour/ChangelingDevourSystem.Events.cs +++ b/Content.Shared/Changeling/ChangelingDevourEvents.cs @@ -2,7 +2,7 @@ using Content.Shared.DoAfter; using Robust.Shared.Serialization; -namespace Content.Shared.Changeling.Devour; +namespace Content.Shared.Changeling; /// /// Action event for Devour, someone has initiated a devour on someone, begin to windup. diff --git a/Content.Shared/Changeling/Transform/ChangelingTransformSystem.Events.cs b/Content.Shared/Changeling/ChangelingTransformEvents.cs similarity index 91% rename from Content.Shared/Changeling/Transform/ChangelingTransformSystem.Events.cs rename to Content.Shared/Changeling/ChangelingTransformEvents.cs index cfe2a56933..9940a60705 100644 --- a/Content.Shared/Changeling/Transform/ChangelingTransformSystem.Events.cs +++ b/Content.Shared/Changeling/ChangelingTransformEvents.cs @@ -2,7 +2,7 @@ using Content.Shared.DoAfter; using Robust.Shared.Serialization; -namespace Content.Shared.Changeling.Transform; +namespace Content.Shared.Changeling; /// /// Action event for opening the changeling transformation radial menu. diff --git a/Content.Shared/Changeling/Devour/ChangelingDevourComponent.cs b/Content.Shared/Changeling/Components/ChangelingDevourComponent.cs similarity index 98% rename from Content.Shared/Changeling/Devour/ChangelingDevourComponent.cs rename to Content.Shared/Changeling/Components/ChangelingDevourComponent.cs index 7798c6fec9..a874afe9ce 100644 --- a/Content.Shared/Changeling/Devour/ChangelingDevourComponent.cs +++ b/Content.Shared/Changeling/Components/ChangelingDevourComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Changeling.Systems; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; @@ -7,7 +8,7 @@ using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Shared.Changeling.Devour; +namespace Content.Shared.Changeling.Components; /// /// Component responsible for Changelings Devour attack. Including the amount of damage diff --git a/Content.Shared/Changeling/ChangelingIdentityComponent.cs b/Content.Shared/Changeling/Components/ChangelingIdentityComponent.cs similarity index 95% rename from Content.Shared/Changeling/ChangelingIdentityComponent.cs rename to Content.Shared/Changeling/Components/ChangelingIdentityComponent.cs index 461315f4ce..2779164e4e 100644 --- a/Content.Shared/Changeling/ChangelingIdentityComponent.cs +++ b/Content.Shared/Changeling/Components/ChangelingIdentityComponent.cs @@ -2,7 +2,7 @@ using Content.Shared.Cloning; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -namespace Content.Shared.Changeling; +namespace Content.Shared.Changeling.Components; /// /// The storage component for Changelings, it handles the link between a changeling and its consumed identities @@ -29,6 +29,7 @@ public sealed partial class ChangelingIdentityComponent : Component /// The cloning settings passed to the CloningSystem, contains a list of all components to copy or have handled by their /// respective systems. /// + [DataField] public ProtoId IdentityCloningSettings = "ChangelingCloningSettings"; public override bool SendOnlyToOwner => true; diff --git a/Content.Shared/Changeling/ChangelingStoredIdentityComponent.cs b/Content.Shared/Changeling/Components/ChangelingStoredIdentityComponent.cs similarity index 95% rename from Content.Shared/Changeling/ChangelingStoredIdentityComponent.cs rename to Content.Shared/Changeling/Components/ChangelingStoredIdentityComponent.cs index 44583190e6..010196bfd0 100644 --- a/Content.Shared/Changeling/ChangelingStoredIdentityComponent.cs +++ b/Content.Shared/Changeling/Components/ChangelingStoredIdentityComponent.cs @@ -1,7 +1,7 @@ using Robust.Shared.GameStates; using Robust.Shared.Player; -namespace Content.Shared.Changeling; +namespace Content.Shared.Changeling.Components; /// /// Marker component for cloned identities devoured by a changeling. diff --git a/Content.Shared/Changeling/Transform/ChangelingTransformComponent.cs b/Content.Shared/Changeling/Components/ChangelingTransformComponent.cs similarity index 95% rename from Content.Shared/Changeling/Transform/ChangelingTransformComponent.cs rename to Content.Shared/Changeling/Components/ChangelingTransformComponent.cs index 0a3b3f1985..7b465ea3b2 100644 --- a/Content.Shared/Changeling/Transform/ChangelingTransformComponent.cs +++ b/Content.Shared/Changeling/Components/ChangelingTransformComponent.cs @@ -1,9 +1,10 @@ +using Content.Shared.Changeling.Systems; using Content.Shared.Cloning; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -namespace Content.Shared.Changeling.Transform; +namespace Content.Shared.Changeling.Components; /// /// The component containing information about Changelings Transformation action diff --git a/Content.Shared/Changeling/Devour/ChangelingDevourSystem.cs b/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs similarity index 99% rename from Content.Shared/Changeling/Devour/ChangelingDevourSystem.cs rename to Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs index 83a589a8e3..a064858d43 100644 --- a/Content.Shared/Changeling/Devour/ChangelingDevourSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Administration.Logs; using Content.Shared.Armor; using Content.Shared.Atmos.Rotting; using Content.Shared.Body.Components; +using Content.Shared.Changeling.Components; using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.DoAfter; @@ -19,7 +20,7 @@ using Robust.Shared.Network; using Robust.Shared.Random; using Robust.Shared.Timing; -namespace Content.Shared.Changeling.Devour; +namespace Content.Shared.Changeling.Systems; public sealed class ChangelingDevourSystem : EntitySystem { diff --git a/Content.Shared/Changeling/ChangelingIdentitySystem.cs b/Content.Shared/Changeling/Systems/ChangelingIdentitySystem.cs similarity index 98% rename from Content.Shared/Changeling/ChangelingIdentitySystem.cs rename to Content.Shared/Changeling/Systems/ChangelingIdentitySystem.cs index f68f72f853..8467cc5702 100644 --- a/Content.Shared/Changeling/ChangelingIdentitySystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingIdentitySystem.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.Shared.Changeling.Components; using Content.Shared.Cloning; using Content.Shared.Humanoid; using Content.Shared.Mind.Components; @@ -9,7 +10,7 @@ using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Prototypes; -namespace Content.Shared.Changeling; +namespace Content.Shared.Changeling.Systems; public sealed class ChangelingIdentitySystem : EntitySystem { diff --git a/Content.Shared/Changeling/Transform/ChangelingTransformSystem.UI.cs b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.UI.cs similarity index 95% rename from Content.Shared/Changeling/Transform/ChangelingTransformSystem.UI.cs rename to Content.Shared/Changeling/Systems/ChangelingTransformSystem.UI.cs index 0383867698..98926631dc 100644 --- a/Content.Shared/Changeling/Transform/ChangelingTransformSystem.UI.cs +++ b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.UI.cs @@ -1,6 +1,6 @@ using Robust.Shared.Serialization; -namespace Content.Shared.Changeling.Transform; +namespace Content.Shared.Changeling.Systems; /// /// Send when a player selects an intentity to transform into in the radial menu. diff --git a/Content.Shared/Changeling/Transform/ChangelingTransformSystem.cs b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs similarity index 95% rename from Content.Shared/Changeling/Transform/ChangelingTransformSystem.cs rename to Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs index dbc5356448..31f22b9294 100644 --- a/Content.Shared/Changeling/Transform/ChangelingTransformSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Actions; using Content.Shared.Administration.Logs; +using Content.Shared.Changeling.Components; using Content.Shared.Cloning; using Content.Shared.Database; using Content.Shared.DoAfter; @@ -10,7 +11,7 @@ using Robust.Shared.Audio.Systems; using Robust.Shared.Network; using Robust.Shared.Prototypes; -namespace Content.Shared.Changeling.Transform; +namespace Content.Shared.Changeling.Systems; public sealed partial class ChangelingTransformSystem : EntitySystem { @@ -102,7 +103,7 @@ public sealed partial class ChangelingTransformSystem : EntitySystem if (_net.IsServer) ent.Comp.CurrentTransformSound = _audio.PlayPvs(ent.Comp.TransformAttemptNoise, ent)?.Entity; - if(TryComp(targetIdentity, out var storedIdentity) && storedIdentity.OriginalSession != null) + if (TryComp(targetIdentity, out var storedIdentity) && storedIdentity.OriginalSession != null) _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} begun an attempt to transform into \"{Name(targetIdentity)}\" ({storedIdentity.OriginalSession:player}) "); else _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} begun an attempt to transform into \"{Name(targetIdentity)}\""); @@ -162,8 +163,8 @@ public sealed partial class ChangelingTransformSystem : EntitySystem _humanoidAppearanceSystem.CloneAppearance(targetIdentity, args.User); _cloningSystem.CloneComponents(targetIdentity, args.User, settings); - - if(TryComp(targetIdentity, out var storedIdentity) && storedIdentity.OriginalSession != null) + + if (TryComp(targetIdentity, out var storedIdentity) && storedIdentity.OriginalSession != null) _adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(ent.Owner):player} successfully transformed into \"{Name(targetIdentity)}\" ({storedIdentity.OriginalSession:player})"); else _adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(ent.Owner):player} successfully transformed into \"{Name(targetIdentity)}\""); diff --git a/Resources/Prototypes/Actions/changeling.yml b/Resources/Prototypes/Actions/changeling.yml index 273bb8ed6b..6e4ec6db6c 100644 --- a/Resources/Prototypes/Actions/changeling.yml +++ b/Resources/Prototypes/Actions/changeling.yml @@ -20,3 +20,25 @@ retractSounds: collection: gib # Placeholder +- type: entity + id: ActionChangelingDevour + name: "[color=red]Devour[/color]" + description: Consume the essence of your victims and subsume their identity and mind into your own. + components: + - type: Action + icon: { sprite : Interface/Actions/changeling.rsi, state: "devour" } + iconOn: { sprite : Interface/Actions/changeling.rsi, state: "devour_on" } + priority: 1 + - type: TargetAction + - type: EntityTargetAction + event: !type:ChangelingDevourActionEvent + +- type: entity + id: ActionChangelingTransform + name: "[color=red]Transform[/color]" + description: Transform and assume the identities of those you have devoured. + components: + - type: Action + icon: { sprite : Interface/Actions/changeling.rsi, state: "transform" } + - type: InstantAction + event: !type:ChangelingTransformActionEvent diff --git a/Resources/Prototypes/Antag/changeling.yml b/Resources/Prototypes/Antag/changeling.yml deleted file mode 100644 index b4c8b2e3dc..0000000000 --- a/Resources/Prototypes/Antag/changeling.yml +++ /dev/null @@ -1,35 +0,0 @@ -- type: entity - parent: MobHuman - id: MobLing - name: Urist McLing - suffix: Non-Antag - components: - - type: ChangelingDevour - - type: ChangelingIdentity - - type: ChangelingTransform - - type: ActionGrant - actions: - - ActionRetractableItemArmBlade # Temporary addition, will inevitably be a purchasable in the bio-store - -- type: entity - id: ActionChangelingDevour - name: "[color=red]Devour[/color]" - description: Consume the essence of your victims and subsume their identity and mind into your own. - components: - - type: Action - icon: { sprite : Interface/Actions/changeling.rsi, state: "devour" } - iconOn: { sprite : Interface/Actions/changeling.rsi, state: "devour_on" } - priority: 1 - - type: TargetAction - - type: EntityTargetAction - event: !type:ChangelingDevourActionEvent - -- type: entity - id: ActionChangelingTransform - name: "[color=red]Transform[/color]" - description: Transform and assume the identities of those you have devoured. - components: - - type: Action - icon: { sprite : Interface/Actions/changeling.rsi, state: "transform" } - - type: InstantAction - event: !type:ChangelingTransformActionEvent diff --git a/Resources/Prototypes/Entities/Mobs/Player/changeling.yml b/Resources/Prototypes/Entities/Mobs/Player/changeling.yml new file mode 100644 index 0000000000..d8d4aac742 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Player/changeling.yml @@ -0,0 +1,12 @@ +- type: entity + parent: MobHuman + id: MobLing + name: Urist McLing + suffix: Non-Antag + components: + - type: ChangelingDevour + - type: ChangelingIdentity + - type: ChangelingTransform + - type: ActionGrant + actions: + - ActionRetractableItemArmBlade # Temporary addition, will inevitably be a purchasable in the bio-store From cd0cc721576a6db7905b1c25709c002f9f1bc7ef Mon Sep 17 00:00:00 2001 From: Mora <46364955+TrixxedHeart@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:43:06 -0800 Subject: [PATCH 028/108] Impaired Mobility Disability (#39398) * Implements mobility impairment * Implements mobility impairment * Implements mobility impairment * Removed white cane related stuff (impaired cane replacement and removed mobility aid component) * fix development.toml * Implements slower standing * Prevent speed stacking by checking if the entity is already holding a mobility aid * Move all speed handling into ImpairedMobilitySystem, added comments, made it so wielding a mobility aid doesn't grant the recovery benefit * Move all speed handling into ImpairedMobilitySystem, added comments, made it so wielding a mobility aid doesn't grant the recovery benefit * remove unused file * Shorten description * Apply suggestions Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> * Suggestion cleanup * formatting fix and removed extra datafield stuff * added comment, fixed slashes, yadda yadda * summary comments * removed a word * Add trait to clone whitelist * Fix clone.yml * my own review --------- Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Co-authored-by: ScarKy0 --- .../Assorted/ImpairedMobilityComponent.cs | 25 +++++++ .../Traits/Assorted/ImpairedMobilitySystem.cs | 70 +++++++++++++++++++ .../Traits/Assorted/MobilityAidComponent.cs | 11 +++ .../Traits/Assorted/MobilityAidSystem.cs | 42 +++++++++++ Resources/Locale/en-US/traits/traits.ftl | 3 + .../Prototypes/Entities/Mobs/Player/clone.yml | 1 + .../Entities/Objects/Weapons/Melee/cane.yml | 1 + .../Objects/Weapons/Melee/offset_cane.yml | 1 + .../Objects/Weapons/Melee/white_cane.yml | 1 - Resources/Prototypes/Traits/disabilities.yml | 9 +++ 10 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 Content.Shared/Traits/Assorted/ImpairedMobilityComponent.cs create mode 100644 Content.Shared/Traits/Assorted/ImpairedMobilitySystem.cs create mode 100644 Content.Shared/Traits/Assorted/MobilityAidComponent.cs create mode 100644 Content.Shared/Traits/Assorted/MobilityAidSystem.cs diff --git a/Content.Shared/Traits/Assorted/ImpairedMobilityComponent.cs b/Content.Shared/Traits/Assorted/ImpairedMobilityComponent.cs new file mode 100644 index 0000000000..50f7281ff8 --- /dev/null +++ b/Content.Shared/Traits/Assorted/ImpairedMobilityComponent.cs @@ -0,0 +1,25 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Traits.Assorted; + +/// +/// Used for the Impaired Mobility disability trait. +/// Applies a base movement speed reduction as determined by the SpeedModifier field. +/// Also increases the time it takes to stand up after falling, as determined by the StandUpTimeModifier field. +/// When an entity holds an item with the MobilityAidComponent, the speed penalty is nullified. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ImpairedMobilityComponent : Component +{ + /// + /// The movement speed modifier applied to the player (0.4 is 40% slower) + /// + [DataField, AutoNetworkedField] + public float SpeedModifier = 0.4f; + + /// + /// The doAfter modifier when getting up after falling (1.4 is 40% slower) + /// + [DataField, AutoNetworkedField] + public float StandUpTimeModifier = 1.4f; +} diff --git a/Content.Shared/Traits/Assorted/ImpairedMobilitySystem.cs b/Content.Shared/Traits/Assorted/ImpairedMobilitySystem.cs new file mode 100644 index 0000000000..678fc85344 --- /dev/null +++ b/Content.Shared/Traits/Assorted/ImpairedMobilitySystem.cs @@ -0,0 +1,70 @@ +using Content.Shared.Movement.Systems; +using Content.Shared.Stunnable; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Hands.Components; +using Content.Shared.Wieldable.Components; + +namespace Content.Shared.Traits.Assorted; + +/// +/// Handles +/// +public sealed class ImpairedMobilitySystem : EntitySystem +{ + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly MovementSpeedModifierSystem _speedModifier = default!; + public override void Initialize() + { + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnRefreshMovementSpeed); + SubscribeLocalEvent(OnGetStandUpTime); + } + + private void OnInit(Entity ent, ref ComponentInit args) + { + _speedModifier.RefreshMovementSpeedModifiers(ent); + } + + private void OnShutdown(Entity ent, ref ComponentShutdown args) + { + _speedModifier.RefreshMovementSpeedModifiers(ent); + } + + // Handles movement speed for entities with impaired mobility. + // Applies a speed penalty, but counteracts it if the entity is holding a non-wielded mobility aid. + private void OnRefreshMovementSpeed(Entity ent, ref RefreshMovementSpeedModifiersEvent args) + { + if (HasMobilityAid(ent.Owner)) + return; + + args.ModifySpeed(ent.Comp.SpeedModifier); + } + + // Increases the time it takes for entities to stand up from being knocked down. + private void OnGetStandUpTime(Entity ent, ref GetStandUpTimeEvent args) + { + args.DoAfterTime *= ent.Comp.StandUpTimeModifier; + } + + // Checks if the entity is holding any non-wielded mobility aids. + private bool HasMobilityAid(Entity entity) + { + if (!Resolve(entity, ref entity.Comp, false)) + return false; + + foreach (var held in _hands.EnumerateHeld(entity)) + { + if (!HasComp(held)) + continue; + + // Makes sure it's not wielded yet + if (TryComp(held, out var wieldable) && wieldable.Wielded) + continue; + + return true; + } + + return false; + } +} diff --git a/Content.Shared/Traits/Assorted/MobilityAidComponent.cs b/Content.Shared/Traits/Assorted/MobilityAidComponent.cs new file mode 100644 index 0000000000..6623c8217a --- /dev/null +++ b/Content.Shared/Traits/Assorted/MobilityAidComponent.cs @@ -0,0 +1,11 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Traits.Assorted; + +/// +/// Component to mark items that restore normal movement speed when held in-hand for entities with the impaired mobility trait. +/// The speed is automatically calculated to nullify the entity's speed penalty. +/// Should be used on items that act as mobility aids, such as canes. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class MobilityAidComponent : Component; diff --git a/Content.Shared/Traits/Assorted/MobilityAidSystem.cs b/Content.Shared/Traits/Assorted/MobilityAidSystem.cs new file mode 100644 index 0000000000..7df47ca750 --- /dev/null +++ b/Content.Shared/Traits/Assorted/MobilityAidSystem.cs @@ -0,0 +1,42 @@ +using Content.Shared.Hands; +using Content.Shared.Movement.Systems; +using Content.Shared.Wieldable; + +namespace Content.Shared.Traits.Assorted; + +/// +/// Handles +/// +public sealed class MobilityAidSystem : EntitySystem +{ + [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnGotEquippedHand); + SubscribeLocalEvent(OnGotUnequippedHand); + SubscribeLocalEvent(OnMobilityAidWielded); + SubscribeLocalEvent(OnMobilityAidUnwielded); + } + + private void OnGotEquippedHand(Entity ent, ref GotEquippedHandEvent args) + { + _movementSpeedModifier.RefreshMovementSpeedModifiers(args.User); + } + + private void OnGotUnequippedHand(Entity ent, ref GotUnequippedHandEvent args) + { + _movementSpeedModifier.RefreshMovementSpeedModifiers(args.User); + } + + private void OnMobilityAidWielded(Entity ent, ref ItemWieldedEvent args) + { + _movementSpeedModifier.RefreshMovementSpeedModifiers(args.User); + } + + private void OnMobilityAidUnwielded(Entity ent, ref ItemUnwieldedEvent args) + { + _movementSpeedModifier.RefreshMovementSpeedModifiers(args.User); + } +} diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index d7ab6ca76a..f4c51d863e 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -65,3 +65,6 @@ trait-spanish-desc = Hola señor, donde esta la biblioteca. trait-painnumbness-name = Numb trait-painnumbness-desc = You lack any sense of feeling pain, being unaware of how hurt you may be. + +trait-impaired-mobility-name = Impaired Mobility +trait-impaired-mobility-desc = You have difficulty moving without a mobility aid. diff --git a/Resources/Prototypes/Entities/Mobs/Player/clone.yml b/Resources/Prototypes/Entities/Mobs/Player/clone.yml index 43174cfffe..98d99d7321 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/clone.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/clone.yml @@ -16,6 +16,7 @@ # traits - BlackAndWhiteOverlay - Clumsy + - ImpairedMobility # - LegsParalyzed (you get healed) - LightweightDrunk - Muted diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml index 29ce5fa8c8..96ad8b233d 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml @@ -11,6 +11,7 @@ size: Normal sprite: Objects/Weapons/Melee/cane.rsi - type: Appearance + - type: MobilityAid - type: MeleeWeapon wideAnimationRotation: 45 damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/offset_cane.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/offset_cane.yml index 1d2081fe93..12e2e9ae70 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/offset_cane.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/offset_cane.yml @@ -10,6 +10,7 @@ - type: Item size: Normal sprite: Objects/Weapons/Melee/offset_canes/standard.rsi + - type: MobilityAid # - type: RandomSprite # Ideally I'd rather these be their own selectable item instead of randomly picked. # available: # - color: "#91949C" # standard gray diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/white_cane.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/white_cane.yml index 997c3771d5..bba4085295 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/white_cane.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/white_cane.yml @@ -24,4 +24,3 @@ Blunt: 3 - type: UseDelay delay: 1 - diff --git a/Resources/Prototypes/Traits/disabilities.yml b/Resources/Prototypes/Traits/disabilities.yml index a5c1f54d6b..0bb6f0da3c 100644 --- a/Resources/Prototypes/Traits/disabilities.yml +++ b/Resources/Prototypes/Traits/disabilities.yml @@ -83,3 +83,12 @@ category: Disabilities components: - type: PainNumbness + +- type: trait + id: ImpairedMobility + name: trait-impaired-mobility-name + description: trait-impaired-mobility-desc + traitGear: OffsetCane + category: Disabilities + components: + - type: ImpairedMobility From 96d3b4bb29b60d85bb9f9a1e31dcfb7c23bad259 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 21 Aug 2025 00:44:13 +0000 Subject: [PATCH 029/108] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 422f819fcd..044a743ef0 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: UpAndLeaves - changes: - - message: The CMO's hardsuit has been redesigned with extra durathread to withstand - zombie bites more effectively. - type: Tweak - id: 8361 - time: '2025-04-26T21:49:01.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36957 - author: Centronias changes: - message: Parcel wrap, purchasable from cargo, which allows you to wrap items in @@ -3950,3 +3942,11 @@ id: 8873 time: '2025-08-20T23:34:40.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/39792 +- author: TrixxedHeart + changes: + - message: Added the Impaired Mobility disability trait which reduces movement speed + unless using a mobility aid such as a cane and increases stand-up time. + type: Add + id: 8874 + time: '2025-08-21T00:43:06.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/39398 From 3d0573e7c8cb7eafa2e5df64d21d906a9e22ed0b Mon Sep 17 00:00:00 2001 From: TheFlyingSentry Date: Wed, 20 Aug 2025 21:02:54 -0400 Subject: [PATCH 030/108] Added Hemophilia Trait (#38224) Co-authored-by: ScarKy0 Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Content.Shared/Body/Events/BleedModifierEvent.cs | 9 +++++++++ .../Body/Systems/SharedBloodstreamSystem.cs | 8 ++++++-- .../Traits/Assorted/HemophiliaComponent.cs | 16 ++++++++++++++++ .../Traits/Assorted/HemophiliaSystem.cs | 16 ++++++++++++++++ Resources/Locale/en-US/traits/traits.ftl | 3 +++ .../Prototypes/Entities/Mobs/Player/clone.yml | 1 + Resources/Prototypes/Traits/disabilities.yml | 8 ++++++++ 7 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 Content.Shared/Body/Events/BleedModifierEvent.cs create mode 100644 Content.Shared/Traits/Assorted/HemophiliaComponent.cs create mode 100644 Content.Shared/Traits/Assorted/HemophiliaSystem.cs diff --git a/Content.Shared/Body/Events/BleedModifierEvent.cs b/Content.Shared/Body/Events/BleedModifierEvent.cs new file mode 100644 index 0000000000..c836738d63 --- /dev/null +++ b/Content.Shared/Body/Events/BleedModifierEvent.cs @@ -0,0 +1,9 @@ +namespace Content.Shared.Body.Events; + +/// +/// Raised on an entity before they bleed to modify the amount. +/// +/// The amount of blood the entity will lose. +/// The amount of bleed reduction that will happen. +[ByRefEvent] +public record struct BleedModifierEvent(float BleedAmount, float BleedReductionAmount); diff --git a/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs index 182cdb47d3..7db9f42280 100644 --- a/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs +++ b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs @@ -81,10 +81,14 @@ public abstract class SharedBloodstreamSystem : EntitySystem // as well as stop their bleeding to a certain extent. if (bloodstream.BleedAmount > 0) { + var ev = new BleedModifierEvent(bloodstream.BleedAmount, bloodstream.BleedReductionAmount); + RaiseLocalEvent(uid, ref ev); + // Blood is removed from the bloodstream at a 1-1 rate with the bleed amount - TryModifyBloodLevel((uid, bloodstream), -bloodstream.BleedAmount); + TryModifyBloodLevel((uid, bloodstream), -ev.BleedAmount); + // Bleed rate is reduced by the bleed reduction amount in the bloodstream component. - TryModifyBleedAmount((uid, bloodstream), -bloodstream.BleedReductionAmount); + TryModifyBleedAmount((uid, bloodstream), -ev.BleedReductionAmount); } // deal bloodloss damage if their blood level is below a threshold. diff --git a/Content.Shared/Traits/Assorted/HemophiliaComponent.cs b/Content.Shared/Traits/Assorted/HemophiliaComponent.cs new file mode 100644 index 0000000000..208883f11c --- /dev/null +++ b/Content.Shared/Traits/Assorted/HemophiliaComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Traits.Assorted; + +/// +/// This component is used for the Hemophilia Trait, it reduces the passive bleed stack reduction amount so entities with it bleed for longer. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class HemophiliaComponent : Component +{ + /// + /// What multiplier should be applied to the BleedReduction when an entity bleeds? + /// + [DataField, AutoNetworkedField] + public float HemophiliaBleedReductionMultiplier = 0.33f; +} diff --git a/Content.Shared/Traits/Assorted/HemophiliaSystem.cs b/Content.Shared/Traits/Assorted/HemophiliaSystem.cs new file mode 100644 index 0000000000..53f6609575 --- /dev/null +++ b/Content.Shared/Traits/Assorted/HemophiliaSystem.cs @@ -0,0 +1,16 @@ +using Content.Shared.Body.Events; + +namespace Content.Shared.Traits.Assorted; + +public sealed class HemophiliaSystem : EntitySystem +{ + public override void Initialize() + { + SubscribeLocalEvent(OnBleedModifier); + } + + private void OnBleedModifier(Entity ent, ref BleedModifierEvent args) + { + args.BleedReductionAmount *= ent.Comp.HemophiliaBleedReductionMultiplier; + } +} diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index f4c51d863e..3895ce162d 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -66,5 +66,8 @@ trait-spanish-desc = Hola señor, donde esta la biblioteca. trait-painnumbness-name = Numb trait-painnumbness-desc = You lack any sense of feeling pain, being unaware of how hurt you may be. +trait-hemophilia-name = Hemophilia +trait-hemophilia-desc = Your body fails to make blood clots. + trait-impaired-mobility-name = Impaired Mobility trait-impaired-mobility-desc = You have difficulty moving without a mobility aid. diff --git a/Resources/Prototypes/Entities/Mobs/Player/clone.yml b/Resources/Prototypes/Entities/Mobs/Player/clone.yml index 98d99d7321..5c59f82744 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/clone.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/clone.yml @@ -16,6 +16,7 @@ # traits - BlackAndWhiteOverlay - Clumsy + - Hemophilia - ImpairedMobility # - LegsParalyzed (you get healed) - LightweightDrunk diff --git a/Resources/Prototypes/Traits/disabilities.yml b/Resources/Prototypes/Traits/disabilities.yml index 0bb6f0da3c..c5356149f2 100644 --- a/Resources/Prototypes/Traits/disabilities.yml +++ b/Resources/Prototypes/Traits/disabilities.yml @@ -84,6 +84,14 @@ components: - type: PainNumbness +- type: trait + id: Hemophilia + name: trait-hemophilia-name + description: trait-hemophilia-desc + category: Disabilities + components: + - type: Hemophilia + - type: trait id: ImpairedMobility name: trait-impaired-mobility-name From ac09e16765a7fe1bdb5e6d1a4494ec6e40166d59 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 21 Aug 2025 01:04:01 +0000 Subject: [PATCH 031/108] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 044a743ef0..3dfb1196c9 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Centronias - changes: - - message: Parcel wrap, purchasable from cargo, which allows you to wrap items in - paper so you can give people surprise gifts... like bombs. - type: Add - id: 8362 - time: '2025-04-26T23:24:25.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/34471 - author: TiniestShark changes: - message: Inhand sprites for (most) ammo mags and speedloaders. @@ -3950,3 +3942,10 @@ id: 8874 time: '2025-08-21T00:43:06.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/39398 +- author: TheFlyingSentry + changes: + - message: 'Added a new trait: Hemophilia!' + type: Add + id: 8875 + time: '2025-08-21T01:02:54.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38224 From b124d0def58aea3fa16489c6b5bc85c3b1351095 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Wed, 20 Aug 2025 18:09:06 -0700 Subject: [PATCH 032/108] Cane Sword Priority Fix (#39795) GOTTA GO FAST Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml index 96ad8b233d..8b891a7533 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml @@ -84,6 +84,7 @@ - CaneBlade insertSound: /Audio/Items/sheath.ogg ejectSound: /Audio/Items/unsheath.ogg + priority: 3 - type: ItemMapper mapLayers: cane: From 9c546a0072eaf62255ce0fe25b22067260b58a8c Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Thu, 21 Aug 2025 03:10:07 +0200 Subject: [PATCH 033/108] Predict Mind Roles (#39611) --- .../Roles/ParadoxCloneRoleSystem.cs | 6 +- Content.Server/Roles/RoleSystem.cs | 2 +- .../EffectConditions/JobCondition.cs | 8 +-- Content.Shared/Mind/MindComponent.cs | 15 ++-- Content.Shared/Mind/SharedMindSystem.Relay.cs | 4 +- Content.Shared/Mind/SharedMindSystem.cs | 5 +- .../Roles/Components/MindRoleComponent.cs | 8 --- Content.Shared/Roles/SharedRoleSystem.cs | 68 ++++++++----------- 8 files changed, 53 insertions(+), 63 deletions(-) diff --git a/Content.Server/Roles/ParadoxCloneRoleSystem.cs b/Content.Server/Roles/ParadoxCloneRoleSystem.cs index c957692b70..85332ef4a4 100644 --- a/Content.Server/Roles/ParadoxCloneRoleSystem.cs +++ b/Content.Server/Roles/ParadoxCloneRoleSystem.cs @@ -19,11 +19,13 @@ public sealed class ParadoxCloneRoleSystem : EntitySystem private void OnRefreshNameModifiers(Entity ent, ref MindRelayedEvent args) { - if (!TryComp(ent.Owner, out var roleComp)) + var mindId = Transform(ent).ParentUid; // the mind role entity is in a container in the mind entity + + if (!TryComp(mindId, out var mindComp)) return; // only show for ghosts - if (!HasComp(roleComp.Mind.Comp.OwnedEntity)) + if (!HasComp(mindComp.OwnedEntity)) return; if (ent.Comp.NameModifier != null) diff --git a/Content.Server/Roles/RoleSystem.cs b/Content.Server/Roles/RoleSystem.cs index 6cbd039c73..346e13bd07 100644 --- a/Content.Server/Roles/RoleSystem.cs +++ b/Content.Server/Roles/RoleSystem.cs @@ -36,7 +36,7 @@ public sealed class RoleSystem : SharedRoleSystem // Briefing is no longer raised on the mind entity itself // because all the components that briefings subscribe to should be on Mind Role Entities - foreach(var role in mindComp.MindRoles) + foreach (var role in mindComp.MindRoleContainer.ContainedEntities) { RaiseLocalEvent(role, ref ev); } diff --git a/Content.Shared/EntityEffects/EffectConditions/JobCondition.cs b/Content.Shared/EntityEffects/EffectConditions/JobCondition.cs index 0b942a3e09..96f3be64c6 100644 --- a/Content.Shared/EntityEffects/EffectConditions/JobCondition.cs +++ b/Content.Shared/EntityEffects/EffectConditions/JobCondition.cs @@ -16,13 +16,13 @@ public sealed partial class JobCondition : EntityEffectCondition { args.EntityManager.TryGetComponent(args.TargetEntity, out var mindContainer); - if ( mindContainer is null - || !args.EntityManager.TryGetComponent(mindContainer.Mind, out var mind)) + if (mindContainer is null + || !args.EntityManager.TryGetComponent(mindContainer.Mind, out var mind)) return false; - foreach (var roleId in mind.MindRoles) + foreach (var roleId in mind.MindRoleContainer.ContainedEntities) { - if(!args.EntityManager.HasComponent(roleId)) + if (!args.EntityManager.HasComponent(roleId)) continue; if (!args.EntityManager.TryGetComponent(roleId, out var mindRole)) diff --git a/Content.Shared/Mind/MindComponent.cs b/Content.Shared/Mind/MindComponent.cs index 57cd628f90..efb8e45b94 100644 --- a/Content.Shared/Mind/MindComponent.cs +++ b/Content.Shared/Mind/MindComponent.cs @@ -1,8 +1,7 @@ -using Content.Shared.GameTicking; using Content.Shared.Mind.Components; +using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Network; -using Robust.Shared.Player; using Robust.Shared.Prototypes; namespace Content.Shared.Mind; @@ -100,10 +99,16 @@ public sealed partial class MindComponent : Component public bool PreventSuicide { get; set; } /// - /// Mind Role Entities belonging to this Mind + /// Mind Role Entities belonging to this Mind are stored in this container. /// - [DataField, AutoNetworkedField] - public List MindRoles = new List(); + [ViewVariables] + public Container MindRoleContainer = default!; + + /// + /// The id for the MindRoleContainer. + /// + [ViewVariables] + public const string MindRoleContainerId = "mind_roles"; /// /// The mind's current antagonist/special role, or lack thereof; diff --git a/Content.Shared/Mind/SharedMindSystem.Relay.cs b/Content.Shared/Mind/SharedMindSystem.Relay.cs index 60ad2fc30a..0ad18e2e08 100644 --- a/Content.Shared/Mind/SharedMindSystem.Relay.cs +++ b/Content.Shared/Mind/SharedMindSystem.Relay.cs @@ -23,7 +23,7 @@ public abstract partial class SharedMindSystem : EntitySystem { RaiseLocalEvent(mindId, ref ev); - foreach (var role in mindComp.MindRoles) + foreach (var role in mindComp.MindRoleContainer.ContainedEntities) RaiseLocalEvent(role, ref ev); } } @@ -36,7 +36,7 @@ public abstract partial class SharedMindSystem : EntitySystem { RaiseLocalEvent(mindId, ref ev); - foreach (var role in mindComp.MindRoles) + foreach (var role in mindComp.MindRoleContainer.ContainedEntities) RaiseLocalEvent(role, ref ev); } diff --git a/Content.Shared/Mind/SharedMindSystem.cs b/Content.Shared/Mind/SharedMindSystem.cs index 98ff77810c..8906e73248 100644 --- a/Content.Shared/Mind/SharedMindSystem.cs +++ b/Content.Shared/Mind/SharedMindSystem.cs @@ -15,8 +15,8 @@ using Content.Shared.Mobs.Systems; using Content.Shared.Objectives.Systems; using Content.Shared.Players; using Content.Shared.Speech; - using Content.Shared.Whitelist; +using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Player; @@ -36,6 +36,7 @@ public abstract partial class SharedMindSystem : EntitySystem [Dependency] private readonly ISharedPlayerManager _playerManager = default!; [Dependency] private readonly MetaDataSystem _metadata = default!; [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; [ViewVariables] protected readonly Dictionary UserMinds = new(); @@ -64,6 +65,8 @@ public abstract partial class SharedMindSystem : EntitySystem private void OnMindStartup(EntityUid uid, MindComponent component, ComponentStartup args) { + component.MindRoleContainer = _container.EnsureContainer(uid, MindComponent.MindRoleContainerId); + if (component.UserId == null) return; diff --git a/Content.Shared/Roles/Components/MindRoleComponent.cs b/Content.Shared/Roles/Components/MindRoleComponent.cs index 45ab808192..b85ee8f5e0 100644 --- a/Content.Shared/Roles/Components/MindRoleComponent.cs +++ b/Content.Shared/Roles/Components/MindRoleComponent.cs @@ -35,14 +35,6 @@ public sealed partial class MindRoleComponent : BaseMindRoleComponent [DataField] public bool ExclusiveAntag; - /// - /// The Mind that this role belongs to. - /// - /// - /// TODO: Make this a datafield. Also components should not store other components. - /// - public Entity Mind; - /// /// The Antagonist prototype of this role. /// diff --git a/Content.Shared/Roles/SharedRoleSystem.cs b/Content.Shared/Roles/SharedRoleSystem.cs index 3269680c32..d1afae9fd1 100644 --- a/Content.Shared/Roles/SharedRoleSystem.cs +++ b/Content.Shared/Roles/SharedRoleSystem.cs @@ -10,7 +10,6 @@ using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; -using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; @@ -35,7 +34,6 @@ public abstract class SharedRoleSystem : EntitySystem { Subs.CVar(_cfg, CCVars.GameRoleTimerOverride, SetRequirementOverride, true); - SubscribeLocalEvent(OnComponentShutdown); SubscribeLocalEvent(OnSpawn); } @@ -55,7 +53,7 @@ public abstract class SharedRoleSystem : EntitySystem return; } - if (!_prototypes.TryIndex(value, out _requirementOverride )) + if (!_prototypes.TryIndex(value, out _requirementOverride)) Log.Error($"Unknown JobRequirementOverridePrototype: {value}"); } @@ -152,22 +150,23 @@ public abstract class SharedRoleSystem : EntitySystem //If that was somehow to occur, a second mindrole for that comp would be created //Meaning any mind role checks could return wrong results, since they just return the first match they find - var mindRoleId = Spawn(protoId, MapCoordinates.Nullspace); - EnsureComp(mindRoleId); - var mindRoleComp = Comp(mindRoleId); + if (!PredictedTrySpawnInContainer(protoId, mindId, MindComponent.MindRoleContainerId, out var mindRoleId)) + { + Log.Error($"Failed to add role {protoId} to {ToPrettyString(mindId)} : Could not spawn the role entity inside the container"); + return; + } + + var mindRoleComp = EnsureComp(mindRoleId.Value); - mindRoleComp.Mind = (mindId,mind); if (jobPrototype is not null) { mindRoleComp.JobPrototype = jobPrototype; - EnsureComp(mindRoleId); + EnsureComp(mindRoleId.Value); DebugTools.AssertNull(mindRoleComp.AntagPrototype); DebugTools.Assert(!mindRoleComp.Antag); DebugTools.Assert(!mindRoleComp.ExclusiveAntag); } - mind.MindRoles.Add(mindRoleId); - var update = MindRolesUpdate((mindId, mind)); // RoleType refresh, Role time tracking, Update Admin playerlist @@ -201,13 +200,13 @@ public abstract class SharedRoleSystem : EntitySystem /// > private bool MindRolesUpdate(Entity ent) { - if(!Resolve(ent.Owner, ref ent.Comp)) + if (!Resolve(ent.Owner, ref ent.Comp)) return false; //get the most important/latest mind role var (roleType, subtype) = GetRoleTypeByTime(ent.Comp); - if (ent.Comp.RoleType == roleType && ent.Comp.Subtype == subtype) + if (ent.Comp.RoleType == roleType && ent.Comp.Subtype == subtype) return false; SetRoleType(ent.Owner, roleType, subtype); @@ -230,7 +229,7 @@ public abstract class SharedRoleSystem : EntitySystem { var roles = new List>(); - foreach (var role in mind.MindRoles) + foreach (var role in mind.MindRoleContainer.ContainedEntities) { var comp = Comp(role); if (comp.RoleType is not null) @@ -301,7 +300,7 @@ public abstract class SharedRoleSystem : EntitySystem var original = "'" + typeof(T).Name + "'"; var deleteName = original; - foreach (var role in mind.Comp.MindRoles) + foreach (var role in mind.Comp.MindRoleContainer.ContainedEntities) { if (!HasComp(role)) { @@ -351,7 +350,7 @@ public abstract class SharedRoleSystem : EntitySystem return MindRemoveRole((mindId, mind)); } - /// + /// /// Finds and removes all mind roles of a specific type /// /// The mind entity and component @@ -359,14 +358,14 @@ public abstract class SharedRoleSystem : EntitySystem /// True if the role existed and was removed public bool MindRemoveRole(Entity mind, EntProtoId protoId) { - if ( !Resolve(mind.Owner, ref mind.Comp)) + if (!Resolve(mind.Owner, ref mind.Comp)) return false; // If there were no matches and thus no mind role entity names, we'll need the protoId, to report what role failed to be removed var original = "'" + protoId + "'"; var deleteName = original; var delete = new List(); - foreach (var role in mind.Comp.MindRoles) + foreach (var role in mind.Comp.MindRoleContainer.ContainedEntities) { if (!HasComp(role)) { @@ -390,7 +389,7 @@ public abstract class SharedRoleSystem : EntitySystem /// private bool MindRemoveRoleDo(Entity mind, List delete, string? logName = "") { - if ( !Resolve(mind.Owner, ref mind.Comp)) + if (!Resolve(mind.Owner, ref mind.Comp)) return false; if (delete.Count <= 0) @@ -416,17 +415,6 @@ public abstract class SharedRoleSystem : EntitySystem return true; } - // Removing the mind role's reference on component shutdown - // to make sure the reference gets removed even if the mind role entity was deleted by outside code - private void OnComponentShutdown(Entity ent, ref ComponentShutdown args) - { - //TODO: Just ensure that the tests don't spawn unassociated mind role entities - if (ent.Comp.Mind.Comp is null) - return; - - ent.Comp.Mind.Comp.MindRoles.Remove(ent.Owner); - } - /// /// Finds the first mind role of a specific T type on a mind entity. /// Outputs entity components for the mind role's MindRoleComponent and for T @@ -442,7 +430,7 @@ public abstract class SharedRoleSystem : EntitySystem if (!Resolve(mind.Owner, ref mind.Comp)) return false; - foreach (var roleEnt in mind.Comp.MindRoles) + foreach (var roleEnt in mind.Comp.MindRoleContainer.ContainedEntities) { if (!TryComp(roleEnt, out T? tcomp)) continue; @@ -487,7 +475,7 @@ public abstract class SharedRoleSystem : EntitySystem var found = false; - foreach (var roleEnt in mind.MindRoles) + foreach (var roleEnt in mind.MindRoleContainer.ContainedEntities) { if (!HasComp(roleEnt, type)) continue; @@ -511,7 +499,7 @@ public abstract class SharedRoleSystem : EntitySystem /// public bool MindHasRole(Entity mind, EntityWhitelist whitelist) { - foreach (var roleEnt in mind.Comp.MindRoles) + foreach (var roleEnt in mind.Comp.MindRoleContainer.ContainedEntities) { if (_whitelist.IsWhitelistPass(whitelist, roleEnt)) return true; @@ -544,10 +532,10 @@ public abstract class SharedRoleSystem : EntitySystem var mind = Comp(mindId); - foreach (var uid in mind.MindRoles) + foreach (var uid in mind.MindRoleContainer.ContainedEntities) { if (HasComp(uid) && TryComp(uid, out var comp)) - result = (uid,comp); + result = (uid, comp); } return result; } @@ -564,7 +552,7 @@ public abstract class SharedRoleSystem : EntitySystem if (!Resolve(mind.Owner, ref mind.Comp)) return roleInfo; - foreach (var role in mind.Comp.MindRoles) + foreach (var role in mind.Comp.MindRoleContainer.ContainedEntities) { var valid = false; var name = "game-ticker-unknown-role"; @@ -644,14 +632,14 @@ public abstract class SharedRoleSystem : EntitySystem return CheckAntagonistStatus(mindId.Value).ExclusiveAntag; } - private (bool Antag, bool ExclusiveAntag) CheckAntagonistStatus(Entity mind) - { - if (!Resolve(mind.Owner, ref mind.Comp)) - return (false, false); + private (bool Antag, bool ExclusiveAntag) CheckAntagonistStatus(Entity mind) + { + if (!Resolve(mind.Owner, ref mind.Comp)) + return (false, false); var antagonist = false; var exclusiveAntag = false; - foreach (var role in mind.Comp.MindRoles) + foreach (var role in mind.Comp.MindRoleContainer.ContainedEntities) { if (!TryComp(role, out var roleComp)) { From 002d9272e6903a3c1240f5f092c362493e08a666 Mon Sep 17 00:00:00 2001 From: SolidSyn <220547106+SolidSyn@users.noreply.github.com> Date: Thu, 21 Aug 2025 11:41:36 -0700 Subject: [PATCH 034/108] Fixed a typo involving the Space Lizard Plushie (#39808) --- Resources/Prototypes/Entities/Objects/Fun/plushies.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Objects/Fun/plushies.yml b/Resources/Prototypes/Entities/Objects/Fun/plushies.yml index c0718d022b..fa028c038a 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/plushies.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/plushies.yml @@ -495,7 +495,7 @@ parent: PlushieLizard id: PlushieSpaceLizard #ᵂᵉʰ! name: space lizard plushie - description: An adorable stuffed toy that resembles a lizardperson in an EVA suit. Made by CentComm as a token initiative to combat speciesism in space environments. "Welcome your new colleges as you do this plush, with open arms!" + description: An adorable stuffed toy that resembles a lizardperson in an EVA suit. Made by CentComm as a token initiative to combat speciesism in space environments. "Welcome your new colleagues as you do this plush, with open arms!" components: - type: Sprite sprite: Objects/Fun/Plushies/lizard.rsi From f67cebf7a4ed451f1911aa5c8bf6f04c198b917f Mon Sep 17 00:00:00 2001 From: Southbridge <7013162+southbridge-fur@users.noreply.github.com> Date: Thu, 21 Aug 2025 16:12:16 -0400 Subject: [PATCH 035/108] Admin Log Browser Improvements (#39130) --- .../UI/CustomControls/AdminLogLabel.cs | 33 - .../CustomControls/PlayerListControl.xaml.cs | 18 +- .../UI/CustomControls/PlayerListEntry.xaml.cs | 2 - .../UI/Logs/AdminLogsControl.xaml | 10 +- .../UI/Logs/AdminLogsControl.xaml.cs | 89 +- .../Administration/UI/Logs/AdminLogsEui.cs | 11 +- .../UI/Logs/Entries/AdminLogEntry.xaml | 14 + .../UI/Logs/Entries/AdminLogEntry.xaml.cs | 79 + .../UI/Logs/Entries/AdminLogEntryDetails.xaml | 44 + .../Logs/Entries/AdminLogEntryDetails.xaml.cs | 98 + .../UI/Tabs/PlayerTab/PlayerTab.xaml.cs | 2 - .../Options/UI/Tabs/AdminOptionsTab.xaml | 3 + .../Options/UI/Tabs/AdminOptionsTab.xaml.cs | 2 + ...0250723055137_AdminLogsCurtime.Designer.cs | 2125 +++++++++++++++++ .../20250723055137_AdminLogsCurtime.cs | 29 + .../PostgresServerDbContextModelSnapshot.cs | 7 +- ...0250723055127_AdminLogsCurtime.Designer.cs | 2048 ++++++++++++++++ .../Sqlite/20250723055127_AdminLogsCurtime.cs | 29 + .../SqliteServerDbContextModelSnapshot.cs | 4 + Content.Server.Database/Model.cs | 5 + .../Logs/AdminLogManager.Cache.cs | 2 +- .../Administration/Logs/AdminLogManager.cs | 3 + Content.Server/Database/ServerDbBase.cs | 2 +- .../Administration/Logs/SharedAdminLog.cs | 1 + Content.Shared/CCVar/CCVars.Interface.cs | 6 + .../en-US/administration/ui/admin-logs.ftl | 15 + .../en-US/escape-menu/ui/options-menu.ftl | 4 + 27 files changed, 4608 insertions(+), 77 deletions(-) delete mode 100644 Content.Client/Administration/UI/CustomControls/AdminLogLabel.cs create mode 100644 Content.Client/Administration/UI/Logs/Entries/AdminLogEntry.xaml create mode 100644 Content.Client/Administration/UI/Logs/Entries/AdminLogEntry.xaml.cs create mode 100644 Content.Client/Administration/UI/Logs/Entries/AdminLogEntryDetails.xaml create mode 100644 Content.Client/Administration/UI/Logs/Entries/AdminLogEntryDetails.xaml.cs create mode 100644 Content.Server.Database/Migrations/Postgres/20250723055137_AdminLogsCurtime.Designer.cs create mode 100644 Content.Server.Database/Migrations/Postgres/20250723055137_AdminLogsCurtime.cs create mode 100644 Content.Server.Database/Migrations/Sqlite/20250723055127_AdminLogsCurtime.Designer.cs create mode 100644 Content.Server.Database/Migrations/Sqlite/20250723055127_AdminLogsCurtime.cs diff --git a/Content.Client/Administration/UI/CustomControls/AdminLogLabel.cs b/Content.Client/Administration/UI/CustomControls/AdminLogLabel.cs deleted file mode 100644 index 0de38ce234..0000000000 --- a/Content.Client/Administration/UI/CustomControls/AdminLogLabel.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Content.Shared.Administration.Logs; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controls; - -namespace Content.Client.Administration.UI.CustomControls; - -public sealed class AdminLogLabel : RichTextLabel -{ - public AdminLogLabel(ref SharedAdminLog log, HSeparator separator) - { - Log = log; - Separator = separator; - - SetMessage($"{log.Date:HH:mm:ss}: {log.Message}"); - OnVisibilityChanged += VisibilityChanged; - } - - public SharedAdminLog Log { get; } - - public HSeparator Separator { get; } - - private void VisibilityChanged(Control control) - { - Separator.Visible = Visible; - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - OnVisibilityChanged -= VisibilityChanged; - } -} diff --git a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs index c7fbf6c2dc..8027a00c54 100644 --- a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs +++ b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs @@ -1,16 +1,15 @@ using System.Linq; +using System.Text.RegularExpressions; using Content.Client.Administration.Systems; using Content.Client.UserInterface.Controls; using Content.Client.Verbs.UI; using Content.Shared.Administration; using Robust.Client.AutoGenerated; -using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Input; -using Robust.Shared.Utility; namespace Content.Client.Administration.UI.CustomControls; @@ -96,13 +95,26 @@ public sealed partial class PlayerListControl : BoxContainer private void FilterList() { _sortedPlayerList.Clear(); + + Regex filterRegex; + // There is no neat way to handle invalid regex being submitted other than + // catching and ignoring the exception which gets thrown when it's invalid. + try + { + filterRegex = new Regex(FilterLineEdit.Text, RegexOptions.IgnoreCase); + } + catch (ArgumentException) + { + return; + } + foreach (var info in _playerList) { var displayName = $"{info.CharacterName} ({info.Username})"; if (info.IdentityName != info.CharacterName) displayName += $" [{info.IdentityName}]"; if (!string.IsNullOrEmpty(FilterLineEdit.Text) - && !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant())) + && !filterRegex.IsMatch(displayName)) continue; _sortedPlayerList.Add(info); } diff --git a/Content.Client/Administration/UI/CustomControls/PlayerListEntry.xaml.cs b/Content.Client/Administration/UI/CustomControls/PlayerListEntry.xaml.cs index cd6a56ea71..f62a6c71e4 100644 --- a/Content.Client/Administration/UI/CustomControls/PlayerListEntry.xaml.cs +++ b/Content.Client/Administration/UI/CustomControls/PlayerListEntry.xaml.cs @@ -1,10 +1,8 @@ using Content.Client.Stylesheets; using Content.Shared.Administration; using Robust.Client.AutoGenerated; -using Robust.Client.GameObjects; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; -using Robust.Shared.Utility; namespace Content.Client.Administration.UI.CustomControls; diff --git a/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml b/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml index cd93ffeb0a..c646e380d4 100644 --- a/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml +++ b/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml @@ -1,5 +1,6 @@  + xmlns:aui="clr-namespace:Content.Client.Administration.UI.CustomControls" + xmlns:ui="clr-namespace:Content.Client.Options.UI"> @@ -52,6 +53,13 @@ diff --git a/Content.Client/Lathe/UI/LatheMenu.xaml.cs b/Content.Client/Lathe/UI/LatheMenu.xaml.cs index 66d875b0f2..ce190464d2 100644 --- a/Content.Client/Lathe/UI/LatheMenu.xaml.cs +++ b/Content.Client/Lathe/UI/LatheMenu.xaml.cs @@ -26,6 +26,10 @@ public sealed partial class LatheMenu : DefaultWindow public event Action? OnServerListButtonPressed; public event Action? RecipeQueueAction; + public event Action? QueueDeleteAction; + public event Action? QueueMoveUpAction; + public event Action? QueueMoveDownAction; + public event Action? DeleteFabricatingAction; public List> Recipes = new(); @@ -50,12 +54,21 @@ public sealed partial class LatheMenu : DefaultWindow }; AmountLineEdit.OnTextChanged += _ => { + if (int.TryParse(AmountLineEdit.Text, out var amount)) + { + if (amount > LatheSystem.MaxItemsPerRequest) + AmountLineEdit.Text = LatheSystem.MaxItemsPerRequest.ToString(); + else if (amount < 0) + AmountLineEdit.Text = "0"; + } + PopulateRecipes(); }; FilterOption.OnItemSelected += OnItemSelected; ServerListButton.OnPressed += a => OnServerListButtonPressed?.Invoke(a); + DeleteFabricating.OnPressed += _ => DeleteFabricatingAction?.Invoke(); } public void SetEntity(EntityUid uid) @@ -223,22 +236,27 @@ public sealed partial class LatheMenu : DefaultWindow /// Populates the build queue list with all queued items /// /// - public void PopulateQueueList(IReadOnlyCollection> queue) + public void PopulateQueueList(IReadOnlyCollection queue) { QueueList.DisposeAllChildren(); var idx = 1; - foreach (var recipeProto in queue) + foreach (var batch in queue) { - var recipe = _prototypeManager.Index(recipeProto); - var queuedRecipeBox = new BoxContainer(); - queuedRecipeBox.Orientation = BoxContainer.LayoutOrientation.Horizontal; + var recipe = _prototypeManager.Index(batch.Recipe); - queuedRecipeBox.AddChild(GetRecipeDisplayControl(recipe)); + var itemName = _lathe.GetRecipeName(batch.Recipe); + string displayText; + if (batch.ItemsRequested > 1) + displayText = Loc.GetString("lathe-menu-item-batch", ("index", idx), ("name", itemName), ("printed", batch.ItemsPrinted), ("total", batch.ItemsRequested)); + else + displayText = Loc.GetString("lathe-menu-item-single", ("index", idx), ("name", itemName)); + + var queuedRecipeBox = new QueuedRecipeControl(displayText, idx - 1, GetRecipeDisplayControl(recipe)); + queuedRecipeBox.OnDeletePressed += s => QueueDeleteAction?.Invoke(s); + queuedRecipeBox.OnMoveUpPressed += s => QueueMoveUpAction?.Invoke(s); + queuedRecipeBox.OnMoveDownPressed += s => QueueMoveDownAction?.Invoke(s); - var queuedRecipeLabel = new Label(); - queuedRecipeLabel.Text = $"{idx}. {_lathe.GetRecipeName(recipe)}"; - queuedRecipeBox.AddChild(queuedRecipeLabel); QueueList.AddChild(queuedRecipeBox); idx++; } diff --git a/Content.Client/Lathe/UI/QueuedRecipeControl.xaml b/Content.Client/Lathe/UI/QueuedRecipeControl.xaml new file mode 100644 index 0000000000..b1d4b496a1 --- /dev/null +++ b/Content.Client/Lathe/UI/QueuedRecipeControl.xaml @@ -0,0 +1,35 @@ + + + + + diff --git a/Content.Client/Lathe/UI/QueuedRecipeControl.xaml.cs b/Content.Client/Lathe/UI/QueuedRecipeControl.xaml.cs new file mode 100644 index 0000000000..c4ba9803b0 --- /dev/null +++ b/Content.Client/Lathe/UI/QueuedRecipeControl.xaml.cs @@ -0,0 +1,36 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.Lathe.UI; + +[GenerateTypedNameReferences] +public sealed partial class QueuedRecipeControl : Control +{ + public Action? OnDeletePressed; + public Action? OnMoveUpPressed; + public Action? OnMoveDownPressed; + + public QueuedRecipeControl(string displayText, int index, Control displayControl) + { + RobustXamlLoader.Load(this); + + RecipeName.Text = displayText; + RecipeDisplayContainer.AddChild(displayControl); + + MoveUp.OnPressed += (_) => + { + OnMoveUpPressed?.Invoke(index); + }; + + MoveDown.OnPressed += (_) => + { + OnMoveDownPressed?.Invoke(index); + }; + + Delete.OnPressed += (_) => + { + OnDeletePressed?.Invoke(index); + }; + } +} diff --git a/Content.Server/Lathe/LatheSystem.cs b/Content.Server/Lathe/LatheSystem.cs index 6ecd5bdf62..02abb07791 100644 --- a/Content.Server/Lathe/LatheSystem.cs +++ b/Content.Server/Lathe/LatheSystem.cs @@ -74,6 +74,9 @@ namespace Content.Server.Lathe SubscribeLocalEvent(OnLatheQueueRecipeMessage); SubscribeLocalEvent(OnLatheSyncRequestMessage); + SubscribeLocalEvent(OnLatheDeleteRequestMessage); + SubscribeLocalEvent(OnLatheMoveRequestMessage); + SubscribeLocalEvent(OnLatheAbortFabricationMessage); SubscribeLocalEvent((u, c, _) => UpdateUserInterfaceState(u, c)); SubscribeLocalEvent(OnMaterialAmountChanged); @@ -167,23 +170,32 @@ namespace Content.Server.Lathe return ev.Recipes.ToList(); } - public bool TryAddToQueue(EntityUid uid, LatheRecipePrototype recipe, LatheComponent? component = null) + public bool TryAddToQueue(EntityUid uid, LatheRecipePrototype recipe, int quantity, LatheComponent? component = null) { if (!Resolve(uid, ref component)) return false; - if (!CanProduce(uid, recipe, 1, component)) + if (quantity <= 0) + return false; + quantity = int.Min(quantity, MaxItemsPerRequest); + + if (!CanProduce(uid, recipe, quantity, component)) return false; foreach (var (mat, amount) in recipe.Materials) { var adjustedAmount = recipe.ApplyMaterialDiscount - ? (int) (-amount * component.MaterialUseMultiplier) + ? (int)(-amount * component.MaterialUseMultiplier) : -amount; + adjustedAmount *= quantity; _materialStorage.TryChangeMaterialAmount(uid, mat, adjustedAmount); } - component.Queue.Enqueue(recipe); + + if (component.Queue.Last is { } node && node.ValueRef.Recipe == recipe.ID) + node.ValueRef.ItemsRequested += quantity; + else + component.Queue.AddLast(new LatheRecipeBatch(recipe.ID, 0, quantity)); return true; } @@ -195,8 +207,11 @@ namespace Content.Server.Lathe if (component.CurrentRecipe != null || component.Queue.Count <= 0 || !this.IsPowered(uid, EntityManager)) return false; - var recipeProto = component.Queue.Dequeue(); - var recipe = _proto.Index(recipeProto); + var batch = component.Queue.First(); + batch.ItemsPrinted++; + if (batch.ItemsPrinted >= batch.ItemsRequested || batch.ItemsPrinted < 0) // Rollover sanity check + component.Queue.RemoveFirst(); + var recipe = _proto.Index(batch.Recipe); var time = _reagentSpeed.ApplySpeed(uid, recipe.CompleteTime) * component.TimeMultiplier; @@ -271,8 +286,8 @@ namespace Content.Server.Lathe return; var producing = component.CurrentRecipe; - if (producing == null && component.Queue.TryPeek(out var next)) - producing = next; + if (producing == null && component.Queue.First is { } node) + producing = node.Value.Recipe; var state = new LatheUpdateState(GetAvailableRecipes(uid, component), component.Queue.ToArray(), producing); _uiSys.SetUiState(uid, LatheUiKey.Key, state); @@ -349,12 +364,10 @@ namespace Content.Server.Lathe { if (!args.Powered) { - RemComp(uid); - UpdateRunningAppearance(uid, false); + AbortProduction(uid); } - else if (component.CurrentRecipe != null) + else { - EnsureComp(uid); TryStartProducing(uid, component); } } @@ -416,25 +429,46 @@ namespace Content.Server.Lathe return GetAvailableRecipes(uid, component).Contains(recipe.ID); } + public void AbortProduction(EntityUid uid, LatheComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + if (component.CurrentRecipe != null) + { + if (component.Queue.Count > 0) + { + // Batch abandoned while printing last item, need to create a one-item batch + var batch = component.Queue.First(); + if (batch.Recipe != component.CurrentRecipe) + { + var newBatch = new LatheRecipeBatch(component.CurrentRecipe.Value, 0, 1); + component.Queue.AddFirst(newBatch); + } + else if (batch.ItemsPrinted > 0) + { + batch.ItemsPrinted--; + } + } + + component.CurrentRecipe = null; + } + RemCompDeferred(uid); + UpdateUserInterfaceState(uid, component); + UpdateRunningAppearance(uid, false); + } + #region UI Messages private void OnLatheQueueRecipeMessage(EntityUid uid, LatheComponent component, LatheQueueRecipeMessage args) { if (_proto.TryIndex(args.ID, out LatheRecipePrototype? recipe)) { - var count = 0; - for (var i = 0; i < args.Quantity; i++) - { - if (TryAddToQueue(uid, recipe, component)) - count++; - else - break; - } - if (count > 0) + if (TryAddToQueue(uid, recipe, args.Quantity, component)) { _adminLogger.Add(LogType.Action, LogImpact.Low, - $"{ToPrettyString(args.Actor):player} queued {count} {GetRecipeName(recipe)} at {ToPrettyString(uid):lathe}"); + $"{ToPrettyString(args.Actor):player} queued {args.Quantity} {GetRecipeName(recipe)} at {ToPrettyString(uid):lathe}"); } } TryStartProducing(uid, component); @@ -445,6 +479,92 @@ namespace Content.Server.Lathe { UpdateUserInterfaceState(uid, component); } + + /// + /// Removes a batch from the batch queue by index. + /// If the index given does not exist or is outside of the bounds of the lathe's batch queue, nothing happens. + /// + /// The lathe whose queue is being altered. + /// + /// + public void OnLatheDeleteRequestMessage(EntityUid uid, LatheComponent component, ref LatheDeleteRequestMessage args) + { + if (args.Index < 0 || args.Index >= component.Queue.Count) + return; + + var node = component.Queue.First; + for (int i = 0; i < args.Index; i++) + node = node?.Next; + + if (node == null) // Shouldn't happen with checks above. + return; + + var batch = node.Value; + _adminLogger.Add(LogType.Action, + LogImpact.Low, + $"{ToPrettyString(args.Actor):player} deleted a lathe job for ({batch.ItemsPrinted}/{batch.ItemsRequested}) {GetRecipeName(batch.Recipe)} at {ToPrettyString(uid):lathe}"); + + component.Queue.Remove(node); + UpdateUserInterfaceState(uid, component); + } + + public void OnLatheMoveRequestMessage(EntityUid uid, LatheComponent component, ref LatheMoveRequestMessage args) + { + if (args.Change == 0 || args.Index < 0 || args.Index >= component.Queue.Count) + return; + + // New index must be within the bounds of the batch. + var newIndex = args.Index + args.Change; + if (newIndex < 0 || newIndex >= component.Queue.Count) + return; + + var node = component.Queue.First; + for (int i = 0; i < args.Index; i++) + node = node?.Next; + + if (node == null) // Something went wrong. + return; + + if (args.Change > 0) + { + var newRelativeNode = node.Next; + for (int i = 1; i < args.Change; i++) // 1-indexed: starting from Next + newRelativeNode = newRelativeNode?.Next; + + if (newRelativeNode == null) // Something went wrong. + return; + + component.Queue.Remove(node); + component.Queue.AddAfter(newRelativeNode, node); + } + else + { + var newRelativeNode = node.Previous; + for (int i = 1; i < -args.Change; i++) // 1-indexed: starting from Previous + newRelativeNode = newRelativeNode?.Previous; + + if (newRelativeNode == null) // Something went wrong. + return; + + component.Queue.Remove(node); + component.Queue.AddBefore(newRelativeNode, node); + } + + UpdateUserInterfaceState(uid, component); + } + + public void OnLatheAbortFabricationMessage(EntityUid uid, LatheComponent component, ref LatheAbortFabricationMessage args) + { + if (component.CurrentRecipe == null) + return; + + _adminLogger.Add(LogType.Action, + LogImpact.Low, + $"{ToPrettyString(args.Actor):player} aborted printing {GetRecipeName(component.CurrentRecipe.Value)} at {ToPrettyString(uid):lathe}"); + + component.CurrentRecipe = null; + FinishProducing(uid, component); + } #endregion } } diff --git a/Content.Shared/Lathe/LatheComponent.cs b/Content.Shared/Lathe/LatheComponent.cs index 8b701ff64e..7bd7764514 100644 --- a/Content.Shared/Lathe/LatheComponent.cs +++ b/Content.Shared/Lathe/LatheComponent.cs @@ -26,10 +26,14 @@ namespace Content.Shared.Lathe // Otherwise the material arbitrage test and/or LatheSystem.GetAllBaseRecipes needs to be updated /// - /// The lathe's construction queue + /// The lathe's construction queue. /// + /// + /// This is a LinkedList to allow for constant time insertion/deletion (vs a List), and more efficient + /// moves (vs a Queue). + /// [DataField] - public Queue> Queue = new(); + public LinkedList Queue = new(); /// /// The sound that plays when the lathe is producing an item, if any @@ -97,6 +101,21 @@ namespace Content.Shared.Lathe } } + [Serializable] + public sealed partial class LatheRecipeBatch + { + public ProtoId Recipe; + public int ItemsPrinted; + public int ItemsRequested; + + public LatheRecipeBatch(ProtoId recipe, int itemsPrinted, int itemsRequested) + { + Recipe = recipe; + ItemsPrinted = itemsPrinted; + ItemsRequested = itemsRequested; + } + } + /// /// Event raised on a lathe when it starts producing a recipe. /// diff --git a/Content.Shared/Lathe/LatheMessages.cs b/Content.Shared/Lathe/LatheMessages.cs index 1c1c6440f1..fe72eed367 100644 --- a/Content.Shared/Lathe/LatheMessages.cs +++ b/Content.Shared/Lathe/LatheMessages.cs @@ -10,11 +10,11 @@ public sealed class LatheUpdateState : BoundUserInterfaceState { public List> Recipes; - public ProtoId[] Queue; + public LatheRecipeBatch[] Queue; public ProtoId? CurrentlyProducing; - public LatheUpdateState(List> recipes, ProtoId[] queue, ProtoId? currentlyProducing = null) + public LatheUpdateState(List> recipes, LatheRecipeBatch[] queue, ProtoId? currentlyProducing = null) { Recipes = recipes; Queue = queue; @@ -46,6 +46,33 @@ public sealed class LatheQueueRecipeMessage : BoundUserInterfaceMessage } } +/// +/// Sent to the server to remove a batch from the queue. +/// +[Serializable, NetSerializable] +public sealed class LatheDeleteRequestMessage(int index) : BoundUserInterfaceMessage +{ + public int Index = index; +} + +/// +/// Sent to the server to move the position of a batch in the queue. +/// +[Serializable, NetSerializable] +public sealed class LatheMoveRequestMessage(int index, int change) : BoundUserInterfaceMessage +{ + public int Index = index; + public int Change = change; +} + +/// +/// Sent to the server to stop producing the current item. +/// +[Serializable, NetSerializable] +public sealed class LatheAbortFabricationMessage() : BoundUserInterfaceMessage +{ +} + [NetSerializable, Serializable] public enum LatheUiKey { diff --git a/Content.Shared/Lathe/PrototypeIdLinkedListSerializer.cs b/Content.Shared/Lathe/PrototypeIdLinkedListSerializer.cs new file mode 100644 index 0000000000..1c616ff105 --- /dev/null +++ b/Content.Shared/Lathe/PrototypeIdLinkedListSerializer.cs @@ -0,0 +1,81 @@ +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Serialization.Markdown; +using Robust.Shared.Serialization.Markdown.Sequence; +using Robust.Shared.Serialization.Markdown.Validation; +using Robust.Shared.Serialization.TypeSerializers.Interfaces; + +namespace Content.Shared.Lathe; + +/// +/// Handles reading, writing, and validation for linked lists of prototypes. +/// +/// The type of prototype this linked list represents +/// +/// This is in the Content.Shared.Lathe namespace as there are no other LinkedList ProtoId instances. +/// +[TypeSerializer] +public sealed class LinkedListSerializer : ITypeSerializer, SequenceDataNode>, ITypeCopier> where T : class +{ + public ValidationNode Validate(ISerializationManager serializationManager, SequenceDataNode node, + IDependencyCollection dependencies, ISerializationContext? context = null) + { + var list = new List(); + + foreach (var elem in node.Sequence) + { + list.Add(serializationManager.ValidateNode(elem, context)); + } + + return new ValidatedSequenceNode(list); + } + + public DataNode Write(ISerializationManager serializationManager, LinkedList value, + IDependencyCollection dependencies, + bool alwaysWrite = false, + ISerializationContext? context = null) + { + var sequence = new SequenceDataNode(); + + foreach (var elem in value) + { + sequence.Add(serializationManager.WriteValue(elem, alwaysWrite, context)); + } + + return sequence; + } + + LinkedList ITypeReader, SequenceDataNode>.Read(ISerializationManager serializationManager, + SequenceDataNode node, + IDependencyCollection dependencies, + SerializationHookContext hookCtx, + ISerializationContext? context, ISerializationManager.InstantiationDelegate>? instanceProvider) + { + var list = instanceProvider != null ? instanceProvider() : new LinkedList(); + + foreach (var dataNode in node.Sequence) + { + list.AddLast(serializationManager.Read(dataNode, hookCtx, context)); + } + + return list; + } + + public void CopyTo( + ISerializationManager serializationManager, + LinkedList source, + ref LinkedList target, + IDependencyCollection dependencies, + SerializationHookContext hookCtx, + ISerializationContext? context = null) + { + target.Clear(); + using var enumerator = source.GetEnumerator(); + + while (enumerator.MoveNext()) + { + var current = enumerator.Current; + target.AddLast(current); + } + } +} diff --git a/Content.Shared/Lathe/SharedLatheSystem.cs b/Content.Shared/Lathe/SharedLatheSystem.cs index 524d83fd84..5942f4bf6c 100644 --- a/Content.Shared/Lathe/SharedLatheSystem.cs +++ b/Content.Shared/Lathe/SharedLatheSystem.cs @@ -22,6 +22,7 @@ public abstract class SharedLatheSystem : EntitySystem [Dependency] private readonly EmagSystem _emag = default!; public readonly Dictionary> InverseRecipes = new(); + public const int MaxItemsPerRequest = 10_000; public override void Initialize() { @@ -86,6 +87,8 @@ public abstract class SharedLatheSystem : EntitySystem return false; if (!HasRecipe(uid, recipe, component)) return false; + if (amount <= 0) + return false; foreach (var (material, needed) in recipe.Materials) { diff --git a/Resources/Locale/en-US/lathe/ui/lathe-menu.ftl b/Resources/Locale/en-US/lathe/ui/lathe-menu.ftl index 076a70447c..c04c095162 100644 --- a/Resources/Locale/en-US/lathe/ui/lathe-menu.ftl +++ b/Resources/Locale/en-US/lathe/ui/lathe-menu.ftl @@ -29,3 +29,9 @@ lathe-menu-silo-linked-message = Silo Linked lathe-menu-fabricating-message = Fabricating... lathe-menu-materials-title = Materials lathe-menu-queue-title = Build Queue +lathe-menu-delete-fabricating-tooltip = Cancel printing the current item. +lathe-menu-delete-item-tooltip = Cancel printing this batch. +lathe-menu-move-up-tooltip = Move this batch ahead in the queue. +lathe-menu-move-down-tooltip = Move this batch back in the queue. +lathe-menu-item-single = {$index}. {$name} +lathe-menu-item-batch = {$index}. {$name} ({$printed}/{$total}) From b168f7be5a849863f6ce81048ca68fbd3eecefa9 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 24 Aug 2025 15:03:54 +0000 Subject: [PATCH 095/108] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 2d81e15d90..fc3f0e933b 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: ArtisticRoomba - changes: - - message: A new Mapping changelog has been added. You can find it in a different - "Mapping" tab under the changelog menu. - type: Add - id: 8369 - time: '2025-04-27T20:15:17.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/34848 - author: ScarKy0 changes: - message: Deliveries can now spawn as fragile-type! Deliver them intact to earn @@ -3948,3 +3940,11 @@ id: 8881 time: '2025-08-23T22:15:28.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/35100 +- author: whatston3 + changes: + - message: Lathes now batch items into jobs, which can be moved around in priority + or deleted. + type: Add + id: 8882 + time: '2025-08-24T15:02:47.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38624 From 1bca528c1747dcfc6954f4154c5f0975b2030495 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 Aug 2025 23:30:59 +0300 Subject: [PATCH 096/108] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BD=D0=B5=D0=B4=D0=BE=D1=81=D1=82=D0=B0=D1=8E?= =?UTF-8?q?=D1=89=D0=B8=D0=B5=20=D0=BC=D0=B8=D0=B3=D1=80=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=BF=D1=80=D0=BE=D1=82=D0=BE=D1=82=D0=B8=D0=BF=D0=BE?= =?UTF-8?q?=D0=B2=20MagazineP90Extended=20=D0=B8=20CrateArmoryWeaponXM1014?= =?UTF-8?q?=20(#3013)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: VigersRay <60344369+VigersRay@users.noreply.github.com> --- Resources/migration.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Resources/migration.yml b/Resources/migration.yml index 93502981c6..7590dc19a9 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -840,4 +840,6 @@ SpeedLoaderMagnum: SpeedLoaderMagnumSP MagazineBoxRifle: MagazineBoxRifleSP PrinterDoc: CopyMachineFilled PrinterDocFlatpack: CopyMachineFlatpack +MagazineP90Extended: null +CrateArmoryWeaponXM1014: null # Sunrise-End From 6c154fb79e467fff1191a4bb01c045e86cf7703f Mon Sep 17 00:00:00 2001 From: M4rchy-S <89603088+M4rchy-S@users.noreply.github.com> Date: Sun, 24 Aug 2025 23:46:23 +0300 Subject: [PATCH 097/108] Moving FlammableComponent to Shared (#39870) --- .../Administration/Systems/AdminVerbSystem.Smites.cs | 5 +---- Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs | 2 +- Content.Server/EntityEffects/EntityEffectSystem.cs | 2 +- Content.Server/Explosion/EntitySystems/ExplosionSystem.cs | 1 + Content.Server/NPC/Systems/NPCUtilitySystem.cs | 4 +--- .../Xenoarchaeology/Artifact/XAE/XAEIgniteSystem.cs | 2 +- .../Atmos/Components/FlammableComponent.cs | 5 +++-- 7 files changed, 9 insertions(+), 12 deletions(-) rename {Content.Server => Content.Shared}/Atmos/Components/FlammableComponent.cs (95%) diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index cd89b11e10..90e5e46d65 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -1,10 +1,8 @@ using System.Threading; using Content.Server.Administration.Components; -using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Components; using Content.Server.Body.Systems; -using Content.Server.Clothing.Systems; using Content.Server.Electrocution; using Content.Server.Explosion.EntitySystems; using Content.Server.GhostKick; @@ -19,6 +17,7 @@ using Content.Server.Tabletop; using Content.Server.Tabletop.Components; using Content.Shared.Administration; using Content.Shared.Administration.Components; +using Content.Shared.Atmos.Components; using Content.Shared.Body.Components; using Content.Shared.Body.Part; using Content.Shared.Clumsy; @@ -40,7 +39,6 @@ using Content.Shared.Nutrition.Components; using Content.Shared.Popups; using Content.Shared.Slippery; using Content.Shared.Storage.Components; -using Content.Shared.Stunnable; using Content.Shared.Tabletop.Components; using Content.Shared.Tools.Systems; using Content.Shared.Verbs; @@ -50,7 +48,6 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; using Robust.Shared.Player; using Robust.Shared.Random; -using Robust.Shared.Timing; using Robust.Shared.Utility; using Timer = Robust.Shared.Timing.Timer; diff --git a/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs b/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs index d38bda562b..5ceb9888f4 100644 --- a/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/PyroclasticAnomalySystem.cs @@ -1,7 +1,7 @@ -using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Shared.Anomaly.Components; using Content.Shared.Anomaly.Effects.Components; +using Content.Shared.Atmos.Components; using Robust.Shared.Map; namespace Content.Server.Anomaly.Effects; diff --git a/Content.Server/EntityEffects/EntityEffectSystem.cs b/Content.Server/EntityEffects/EntityEffectSystem.cs index b0b8ab5045..f423a43261 100644 --- a/Content.Server/EntityEffects/EntityEffectSystem.cs +++ b/Content.Server/EntityEffects/EntityEffectSystem.cs @@ -1,6 +1,5 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Components; using Content.Server.Body.Systems; @@ -22,6 +21,7 @@ using Content.Server.Temperature.Systems; using Content.Server.Traits.Assorted; using Content.Server.Zombies; using Content.Shared.Atmos; +using Content.Shared.Atmos.Components; using Content.Shared.Body.Components; using Content.Shared.Coordinates.Helpers; using Content.Shared.EntityEffects.EffectConditions; diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index c50b1e53fa..fc31a77041 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Administration.Logs; using Content.Server.Atmos.Components; using Content.Server.NodeContainer.EntitySystems; using Content.Server.NPC.Pathfinding; +using Content.Shared.Atmos.Components; using Content.Shared.Camera; using Content.Shared.CCVar; using Content.Shared.Damage; diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index 5f077a06bb..813626a1c4 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -1,4 +1,3 @@ -using Content.Server.Atmos.Components; using Content.Server.Fluids.EntitySystems; using Content.Server.Hands.Systems; using Content.Server.NPC.Queries; @@ -6,13 +5,11 @@ using Content.Server.NPC.Queries.Considerations; using Content.Server.NPC.Queries.Curves; using Content.Server.NPC.Queries.Queries; using Content.Server.Nutrition.Components; -using Content.Server.Nutrition.EntitySystems; using Content.Server.Temperature.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Damage; using Content.Shared.Examine; using Content.Shared.Fluids.Components; -using Content.Shared.Hands.Components; using Content.Shared.Inventory; using Content.Shared.Mobs; using Content.Shared.Mobs.Systems; @@ -31,6 +28,7 @@ using Microsoft.Extensions.ObjectPool; using Robust.Server.Containers; using Robust.Shared.Prototypes; using Robust.Shared.Utility; +using Content.Shared.Atmos.Components; using System.Linq; namespace Content.Server.NPC.Systems; diff --git a/Content.Server/Xenoarchaeology/Artifact/XAE/XAEIgniteSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XAE/XAEIgniteSystem.cs index 14270fb866..7e8fff73ad 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XAE/XAEIgniteSystem.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XAE/XAEIgniteSystem.cs @@ -1,6 +1,6 @@ -using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Xenoarchaeology.Artifact.XAE.Components; +using Content.Shared.Atmos.Components; using Content.Shared.Xenoarchaeology.Artifact; using Content.Shared.Xenoarchaeology.Artifact.XAE; using Robust.Shared.Random; diff --git a/Content.Server/Atmos/Components/FlammableComponent.cs b/Content.Shared/Atmos/Components/FlammableComponent.cs similarity index 95% rename from Content.Server/Atmos/Components/FlammableComponent.cs rename to Content.Shared/Atmos/Components/FlammableComponent.cs index 9ae99a1513..acfb9e2540 100644 --- a/Content.Server/Atmos/Components/FlammableComponent.cs +++ b/Content.Shared/Atmos/Components/FlammableComponent.cs @@ -1,11 +1,12 @@ using Content.Shared.Alert; using Content.Shared.Damage; +using Robust.Shared.GameStates; using Robust.Shared.Physics.Collision.Shapes; using Robust.Shared.Prototypes; -namespace Content.Server.Atmos.Components +namespace Content.Shared.Atmos.Components { - [RegisterComponent] + [RegisterComponent, NetworkedComponent] public sealed partial class FlammableComponent : Component { [DataField] From c39491fe62bef4953f842a9a34ba01351ef29d9c Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 Aug 2025 02:47:27 +0300 Subject: [PATCH 098/108] =?UTF-8?q?=D0=A0=D0=B5=D0=B2=D0=BE=D1=80=D0=BA=20?= =?UTF-8?q?=D1=81=D0=B8=D1=81=D1=82=D0=B5=D0=BC=D1=8B=20=D0=BE=D0=BF=D0=BE?= =?UTF-8?q?=D0=B2=D0=B5=D1=89=D0=B5=D0=BD=D0=B8=D0=B9:=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D1=80=D0=B0=D0=BD=D1=81=D1=82=D0=B2=D0=B5?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D0=B5=20=D0=B4=D0=B8=D0=BD=D0=B0=D0=BC=D0=B8?= =?UTF-8?q?=D0=BA=D0=B8=20=D1=81=20=D0=BE=D1=87=D0=B5=D1=80=D0=B5=D0=B4?= =?UTF-8?q?=D1=8C=D1=8E=20=D0=B8=20=D1=83=D0=BC=D0=BD=D0=BE=D0=B9=20=D1=84?= =?UTF-8?q?=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D0=B0=D1=86=D0=B8=D0=B5=D0=B9=20?= =?UTF-8?q?=D1=87=D0=B0=D1=82=D0=B0=20(#3000)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: VigersRay <60344369+VigersRay@users.noreply.github.com> Co-authored-by: Vigers Ray --- Content.Client/Options/UI/Tabs/ExtraTab.xaml | 1 - .../Options/UI/Tabs/ExtraTab.xaml.cs | 5 - Content.Client/_Sunrise/TTS/TTSSystem.cs | 29 +- Content.Server/Chat/Systems/ChatSystem.cs | 149 +++++-- .../CommunicationsConsoleComponent.cs | 2 +- .../CommunicationsConsoleSystem.cs | 16 +- .../AnnouncementSpeakerSystem.cs | 415 ++++++++++++++++++ Content.Server/_Sunrise/TTS/TTSManager.cs | 20 +- Content.Server/_Sunrise/TTS/TTSSystem.cs | 62 ++- .../AnnouncementSpeakerComponent.cs | 35 ++ .../Events/AnnouncementSpeakerEvents.cs | 30 ++ .../_Sunrise/SunriseCCVars/SunriseCCVars.cs | 7 +- .../_Sunrise/TTS/AnnounceTTSEvent.cs | 12 - .../Wallmounts/WallmountMachines/intercom.yml | 4 + .../WallmountMachines/surveillance_camera.yml | 4 + 15 files changed, 701 insertions(+), 90 deletions(-) create mode 100644 Content.Server/_Sunrise/AnnouncementSpeaker/AnnouncementSpeakerSystem.cs create mode 100644 Content.Shared/_Sunrise/AnnouncementSpeaker/Components/AnnouncementSpeakerComponent.cs create mode 100644 Content.Shared/_Sunrise/AnnouncementSpeaker/Events/AnnouncementSpeakerEvents.cs delete mode 100644 Content.Shared/_Sunrise/TTS/AnnounceTTSEvent.cs diff --git a/Content.Client/Options/UI/Tabs/ExtraTab.xaml b/Content.Client/Options/UI/Tabs/ExtraTab.xaml index 0113e528d6..b5e2807794 100644 --- a/Content.Client/Options/UI/Tabs/ExtraTab.xaml +++ b/Content.Client/Options/UI/Tabs/ExtraTab.xaml @@ -31,7 +31,6 @@ - diff --git a/Content.Client/Options/UI/Tabs/ExtraTab.xaml.cs b/Content.Client/Options/UI/Tabs/ExtraTab.xaml.cs index 40a3a29de9..3d65961233 100644 --- a/Content.Client/Options/UI/Tabs/ExtraTab.xaml.cs +++ b/Content.Client/Options/UI/Tabs/ExtraTab.xaml.cs @@ -32,11 +32,6 @@ public sealed partial class ExtraTab : Control SliderTtsRadio, scale: ContentAudioSystem.TtsMultiplier); - Control.AddOptionPercentSlider( - SunriseCCVars.TTSAnnounceVolume, - SliderTtsAnnounce, - scale: ContentAudioSystem.TtsMultiplier); - Control.AddOptionCheckBox(SunriseCCVars.TTSClientEnabled, TtsClientCheckBox); Control.AddOptionCheckBox(SunriseCCVars.TTSClientQueueEnabled, TtsClientCheckBoxQueue); Control.AddOptionCheckBox(SunriseCCVars.TTSRadioGhostEnabled, TtsRadioGhostCheckBox); diff --git a/Content.Client/_Sunrise/TTS/TTSSystem.cs b/Content.Client/_Sunrise/TTS/TTSSystem.cs index 6772cf7a97..e311f4d56d 100644 --- a/Content.Client/_Sunrise/TTS/TTSSystem.cs +++ b/Content.Client/_Sunrise/TTS/TTSSystem.cs @@ -36,25 +36,22 @@ public sealed class TTSSystem : EntitySystem private float _volume; private float _radioVolume; private int _fileIdx; - private float _volumeAnnounce; private bool _isQueueEnabled; private bool _ghostRadioEnabled; private readonly Queue _ttsQueue = new(); private (EntityUid Entity, AudioComponent Component)? _currentPlaying; private static readonly AudioResource EmptyAudioResource = new(); - public sealed class QueuedTts(byte[] data, TtsType ttsType, ResolvedSoundSpecifier? announcementSound = null) + public sealed class QueuedTts(byte[] data, TtsType ttsType) { public byte[] Data = data; - public ResolvedSoundSpecifier? AnnouncementSound = announcementSound; public TtsType TtsType = ttsType; } public enum TtsType { Voice, - Radio, - Announce + Radio } public override void Initialize() @@ -63,12 +60,10 @@ public sealed class TTSSystem : EntitySystem _res.AddRoot(Prefix, ContentRoot); _cfg.OnValueChanged(SunriseCCVars.TTSVolume, OnTtsVolumeChanged, true); _cfg.OnValueChanged(SunriseCCVars.TTSRadioVolume, OnTtsRadioVolumeChanged, true); - _cfg.OnValueChanged(SunriseCCVars.TTSAnnounceVolume, OnTtsAnnounceVolumeChanged, true); _cfg.OnValueChanged(SunriseCCVars.TTSClientEnabled, OnTtsClientOptionChanged, true); _cfg.OnValueChanged(SunriseCCVars.TTSClientQueueEnabled, OnTTSQueueOptionChanged, true); _cfg.OnValueChanged(SunriseCCVars.TTSRadioGhostEnabled, OnTtsRadioGhostChanged, true); SubscribeNetworkEvent(OnPlayTTS); - SubscribeNetworkEvent(OnAnnounceTTSPlay); } public override void Shutdown() @@ -76,7 +71,6 @@ public sealed class TTSSystem : EntitySystem base.Shutdown(); _cfg.UnsubValueChanged(SunriseCCVars.TTSVolume, OnTtsVolumeChanged); _cfg.UnsubValueChanged(SunriseCCVars.TTSRadioVolume, OnTtsRadioVolumeChanged); - _cfg.UnsubValueChanged(SunriseCCVars.TTSAnnounceVolume, OnTtsAnnounceVolumeChanged); _cfg.UnsubValueChanged(SunriseCCVars.TTSClientEnabled, OnTtsClientOptionChanged); _cfg.UnsubValueChanged(SunriseCCVars.TTSClientQueueEnabled, OnTTSQueueOptionChanged); _cfg.UnsubValueChanged(SunriseCCVars.TTSRadioGhostEnabled, OnTtsRadioGhostChanged); @@ -105,10 +99,6 @@ public sealed class TTSSystem : EntitySystem { _isQueueEnabled = option; } - private void OnTtsAnnounceVolumeChanged(float volume) - { - _volumeAnnounce = volume; - } private void OnTtsClientOptionChanged(bool option) { @@ -120,15 +110,7 @@ public sealed class TTSSystem : EntitySystem _ghostRadioEnabled = option; } - private void OnAnnounceTTSPlay(AnnounceTtsEvent ev) - { - if (_volumeAnnounce == 0) - return; - var entry = new QueuedTts(ev.Data, TtsType.Announce, ev.AnnouncementSound); - - _ttsQueue.Enqueue(entry); - } private void PlayNextInQueue() { @@ -145,9 +127,6 @@ public sealed class TTSSystem : EntitySystem case TtsType.Radio: volume = _radioVolume; break; - case TtsType.Announce: - volume = _volumeAnnounce; - break; case TtsType.Voice: volume = _volume; break; @@ -155,10 +134,6 @@ public sealed class TTSSystem : EntitySystem var finalParams = AudioParams.Default.WithVolume(SharedAudioSystem.GainToVolume(volume)); - if (entry.AnnouncementSound != null) - { - _currentPlaying = _audio.PlayGlobal(entry.AnnouncementSound, new EntityUid(), finalParams.AddVolume(-5f)); - } _currentPlaying = PlayTTSBytes(entry.Data, null, finalParams, true); } diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index 706d0e88a7..d787de369d 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using Content.Server._Sunrise.Chat; using Content.Server._Sunrise.Chat.Sanitization; +using Content.Server._Sunrise.AnnouncementSpeaker; using Content.Server.Administration.Logs; using Content.Server.Administration.Managers; using Content.Server.Chat.Managers; @@ -66,6 +67,7 @@ public sealed partial class ChatSystem : SharedChatSystem [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; [Dependency] private readonly ExamineSystemShared _examineSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly AnnouncementSpeakerSystem _announcementSpeaker = default!; public const string DefaultAnnouncementSound = "/Audio/_Sunrise/Announcements/announce_dig.ogg"; // Sunrise-edit @@ -350,7 +352,7 @@ public sealed partial class ChatSystem : SharedChatSystem #region Announcements /// - /// Dispatches an announcement to all. + /// Dispatches an announcement to all stations through their speaker networks. /// /// The contents of the message /// The sender (Communications Console in Communications Console Announcement) @@ -370,19 +372,25 @@ public sealed partial class ChatSystem : SharedChatSystem sender ??= Loc.GetString("chat-manager-sender-announcement"); var wrappedMessage = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender), ("message", FormattedMessage.EscapeText(message))); - _chatManager.ChatMessageToAll(ChatChannel.Radio, message, wrappedMessage, default, false, true, colorOverride); - - // Sunrise-start - if (playDefault && announcementSound == null) + + // Sunrise-start - Only show in chat for players with working speakers nearby + var filteredPlayers = GetPlayersWithWorkingSpeakers(); + if (filteredPlayers.Recipients.Any()) { - announcementSound ??= new SoundPathSpecifier(DefaultAnnouncementSound); + _chatManager.ChatMessageToManyFiltered(filteredPlayers, ChatChannel.Radio, message, wrappedMessage, default, false, true, colorOverride); } + // Sunrise-end - if (playTts && announcementSound != null) + // Sunrise-start - Use speaker network instead of global broadcast + if (playTts && (playDefault || announcementSound != null)) { - //_audio.PlayGlobal(announcementSound ?? DefaultAnnouncementSound, Filter.Broadcast(), true, AudioParams.Default.WithVolume(-2f)); - var announcementEv = new AnnouncementSpokeEvent(Filter.Broadcast(), message, _audio.ResolveSound(announcementSound), announceVoice); - RaiseLocalEvent(announcementEv); + if (playDefault && announcementSound == null) + { + announcementSound = new SoundPathSpecifier(DefaultAnnouncementSound); + } + + // Send announcement to all stations through their speaker networks + _announcementSpeaker.DispatchAnnouncementToAllStations(message, announcementSound, announceVoice); } // Sunrise-end @@ -413,17 +421,47 @@ public sealed partial class ChatSystem : SharedChatSystem sender ??= Loc.GetString("chat-manager-sender-announcement"); var wrappedMessage = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender), ("message", FormattedMessage.EscapeText(message))); - _chatManager.ChatMessageToManyFiltered(filter, ChatChannel.Radio, message, wrappedMessage, source ?? default, false, true, colorOverride); - // Sunrise-start - if (playDefault && announcementSound == null) - announcementSound = new SoundPathSpecifier(DefaultAnnouncementSound); - - if (playTts && announcementSound != null) + + // Sunrise-start - Filter chat recipients by working speakers + var filteredChatPlayers = FilterPlayersByWorkingSpeakers(filter); + if (filteredChatPlayers.Recipients.Any()) { - //_audio.PlayGlobal(announcementSound ?? DefaultAnnouncementSound, filter, true, AudioParams.Default.WithVolume(-2f)); - RaiseLocalEvent(new AnnouncementSpokeEvent(filter, message, _audio.ResolveSound(announcementSound), announceVoice)); + _chatManager.ChatMessageToManyFiltered(filteredChatPlayers, ChatChannel.Radio, message, wrappedMessage, source ?? default, false, true, colorOverride); } - // Sunrise-edit + // Sunrise-end + + // Sunrise-start - For filtered announcements, we may want to try speaker network if source is on a station + if (playTts && (playDefault || announcementSound != null)) + { + if (playDefault && announcementSound == null) + announcementSound = new SoundPathSpecifier(DefaultAnnouncementSound); + + var resolvedSound = announcementSound != null ? _audio.ResolveSound(announcementSound) : null; + + // If we have a source, try to use the station's speaker network + if (source != null) + { + var station = _stationSystem.GetOwningStation(source.Value); + if (station != null) + { + _announcementSpeaker.DispatchAnnouncementToSpeakers(station.Value, message, announcementSound, announceVoice); + } + else + { + // Fallback to old broadcast system for non-station sources + var announcementEv = new AnnouncementSpokeEvent(filter, message, resolvedSound, announceVoice); + RaiseLocalEvent(announcementEv); + } + } + else + { + // No source, fallback to old broadcast system + var announcementEv = new AnnouncementSpokeEvent(filter, message, resolvedSound, announceVoice); + RaiseLocalEvent(announcementEv); + } + } + // Sunrise-end + _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Station Announcement from {sender}: {message}"); } @@ -462,18 +500,24 @@ public sealed partial class ChatSystem : SharedChatSystem var filter = _stationSystem.GetInStation(stationDataComp); - _chatManager.ChatMessageToManyFiltered(filter, ChatChannel.Radio, message, wrappedMessage, source, false, true, colorOverride); - - // Sunrise-start - if (playDefault && announcementSound == null) - announcementSound = new SoundPathSpecifier(DefaultAnnouncementSound); - - if (playTts && announcementSound != null) + // Sunrise-start - Filter chat recipients by working speakers + var filteredChatPlayers = FilterPlayersByWorkingSpeakers(filter); + if (filteredChatPlayers.Recipients.Any()) { - //_audio.PlayGlobal(announcementSound ?? DefaultAnnouncementSound, filter, true, AudioParams.Default.WithVolume(-2f)); - RaiseLocalEvent(new AnnouncementSpokeEvent(filter, message, _audio.ResolveSound(announcementSound), announceVoice)); + _chatManager.ChatMessageToManyFiltered(filteredChatPlayers, ChatChannel.Radio, message, wrappedMessage, source, false, true, colorOverride); } - // Sunrise-edit + // Sunrise-end + + // Sunrise-start - Use speaker network for station announcements + if (playTts && (playDefault || announcementSound != null)) + { + if (playDefault && announcementSound == null) + announcementSound = new SoundPathSpecifier(DefaultAnnouncementSound); + + // Send announcement to this specific station's speaker network + _announcementSpeaker.DispatchAnnouncementToSpeakers(station.Value, message, announcementSound, announceVoice); + } + // Sunrise-end _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Station Announcement on {station} from {sender}: {message}"); } @@ -823,6 +867,53 @@ public sealed partial class ChatSystem : SharedChatSystem #region Utility + /// + /// Gets all players who have working announcement speakers nearby. + /// Used to filter chat recipients for announcements. + /// + private Filter GetPlayersWithWorkingSpeakers() + { + var filteredPlayers = Filter.Empty(); + + foreach (var player in _playerManager.Sessions) + { + if (player.AttachedEntity is not { Valid: true } playerEntity) + continue; + + if (_announcementSpeaker.HasWorkingSpeakersNearby(playerEntity)) + { + filteredPlayers = filteredPlayers.AddPlayer(player); + } + } + + return filteredPlayers; + } + + /// + /// Filters an existing filter to only include players with working speakers nearby. + /// + private Filter FilterPlayersByWorkingSpeakers(Filter originalFilter) + { + var filteredPlayers = Filter.Empty(); + + foreach (var player in originalFilter.Recipients) + { + if (player.AttachedEntity is not { Valid: true } playerEntity) + continue; + + if (_announcementSpeaker.HasWorkingSpeakersNearby(playerEntity)) + { + filteredPlayers = filteredPlayers.AddPlayer(player); + } + } + + return filteredPlayers; + } + + #endregion + + #region Utility + private enum MessageRangeCheckResult { Disallowed, diff --git a/Content.Server/Communications/CommunicationsConsoleComponent.cs b/Content.Server/Communications/CommunicationsConsoleComponent.cs index 45918de7cf..ffe76ad21a 100644 --- a/Content.Server/Communications/CommunicationsConsoleComponent.cs +++ b/Content.Server/Communications/CommunicationsConsoleComponent.cs @@ -75,7 +75,7 @@ namespace Content.Server.Communications /// In practise this removes the "Sent by ScugMcWawa (Slugcat Captain)" at the bottom of the announcement. /// [DataField] - public bool AnnounceSentBy = true; + public bool AnnounceSentBy = false; // Sunrise-Start [DataField("announceVoice", customTypeSerializer:typeof(PrototypeIdSerializer))] diff --git a/Content.Server/Communications/CommunicationsConsoleSystem.cs b/Content.Server/Communications/CommunicationsConsoleSystem.cs index 21ab244d66..98b7a2078c 100644 --- a/Content.Server/Communications/CommunicationsConsoleSystem.cs +++ b/Content.Server/Communications/CommunicationsConsoleSystem.cs @@ -262,23 +262,23 @@ namespace Content.Server.Communications if (comp.AnnounceSentBy) msg += "\n" + Loc.GetString("comms-console-announcement-sent-by") + " " + author; + // Sunrise-start + var voice = comp.AnnounceVoice; + if (TryComp(message.Actor, out var ttsComponent)) + { + voice = ttsComponent.VoicePrototypeId; + } + // Sunrise-end if (comp.Global) { - // Sunrise-start - var voice = comp.AnnounceVoice; - if (TryComp(message.Actor, out var ttsComponent)) - { - voice = ttsComponent.VoicePrototypeId; - } - // Sunrise-end _chatSystem.DispatchGlobalAnnouncement(msg, title, announcementSound: comp.Sound, colorOverride: comp.Color, announceVoice: voice); // Sunrise-edit _adminLogger.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(message.Actor):player} has sent the following global announcement: {msg}"); return; } - _chatSystem.DispatchStationAnnouncement(uid, msg, title, colorOverride: comp.Color, announceVoice: comp.AnnounceVoice); + _chatSystem.DispatchStationAnnouncement(uid, msg, title, colorOverride: comp.Color, announceVoice: voice); _adminLogger.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(message.Actor):player} has sent the following station announcement: {msg}"); diff --git a/Content.Server/_Sunrise/AnnouncementSpeaker/AnnouncementSpeakerSystem.cs b/Content.Server/_Sunrise/AnnouncementSpeaker/AnnouncementSpeakerSystem.cs new file mode 100644 index 0000000000..25afb80a39 --- /dev/null +++ b/Content.Server/_Sunrise/AnnouncementSpeaker/AnnouncementSpeakerSystem.cs @@ -0,0 +1,415 @@ +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Threading.Tasks; +using Content.Server.Station.Systems; +using Content.Server._Sunrise.TTS; +using Content.Server.Power.Components; +using Content.Shared._Sunrise.AnnouncementSpeaker.Components; +using Content.Shared._Sunrise.AnnouncementSpeaker.Events; +using Content.Shared._Sunrise.TTS; +using Content.Shared.Station.Components; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Configuration; +using Robust.Shared.Prototypes; +using Content.Shared._Sunrise.SunriseCCVars; +using Robust.Shared.Timing; + +namespace Content.Server._Sunrise.AnnouncementSpeaker; + +/// +/// Represents a queued announcement waiting to be played. +/// +public sealed class QueuedAnnouncement +{ + public EntityUid Station { get; set; } + public string Message { get; set; } = ""; + public ResolvedSoundSpecifier? AnnouncementSound { get; set; } + public string? AnnounceVoice { get; set; } + public byte[]? TtsData { get; set; } + public TimeSpan QueuedAt { get; set; } + + public QueuedAnnouncement(EntityUid station, string message, ResolvedSoundSpecifier? announcementSound, string? announceVoice, byte[]? ttsData) + { + Station = station; + Message = message; + AnnouncementSound = announcementSound; + AnnounceVoice = announceVoice; + TtsData = ttsData; + } +} + +/// +/// System that manages announcement speakers distributed across stations. +/// Replaces global announcements with spatial audio from speaker networks. +/// +public sealed class AnnouncementSpeakerSystem : EntitySystem +{ + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + private bool _isEnabled; + private string _defaultAnnounceVoice = "Hanson"; + + // Queue system for preventing overlapping announcements + private readonly Queue _announcementQueue = new(); + private bool _isPlayingAnnouncement = false; + private TimeSpan _currentAnnouncementEndTime; + private const float AnnouncementDurationEstimate = 5.0f; // Estimate 5 seconds per announcement + + public override void Initialize() + { + base.Initialize(); + _cfg.OnValueChanged(SunriseCCVars.TTSEnabled, v => _isEnabled = v, true); + SubscribeLocalEvent(OnAnnouncementSpeaker); + // Note: SpeakerPlayAnnouncementEvent is handled by TTSSystem for the component + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + ProcessAnnouncementQueue(); + } + + /// + /// Processes the announcement queue and plays the next announcement if none is currently playing. + /// + private void ProcessAnnouncementQueue() + { + // Check if current announcement has finished + if (_isPlayingAnnouncement && _timing.CurTime >= _currentAnnouncementEndTime) + { + _isPlayingAnnouncement = false; + } + + // If not playing and have queued announcements, play next one + if (!_isPlayingAnnouncement && _announcementQueue.Count > 0) + { + var announcement = _announcementQueue.Dequeue(); + PlayAnnouncementNow(announcement); + } + } + + /// + /// Immediately plays an announcement without queuing. + /// + private void PlayAnnouncementNow(QueuedAnnouncement announcement) + { + var duration = TimeSpan.FromSeconds(AnnouncementDurationEstimate); + if (announcement.TtsData != null) + duration = GetAudioDurationFromBytes(announcement.TtsData); + _isPlayingAnnouncement = true; + _currentAnnouncementEndTime = _timing.CurTime + duration; + + var ev = new AnnouncementSpeakerEvent( + announcement.Station, + announcement.Message, + announcement.AnnouncementSound, + announcement.AnnounceVoice, + announcement.TtsData + ); + + RaiseLocalEvent(ref ev); + } + + /// + /// Handles station-wide announcements by finding all speakers on the station and playing the announcement through them. + /// + private void OnAnnouncementSpeaker(ref AnnouncementSpeakerEvent ev) + { + // Find all speakers on the station + var speakers = GetStationSpeakers(ev.Station); + + if (speakers.Count == 0) + { + // Fallback: If no speakers are found, log a warning + // In the future, this could send to a single communications console or similar + Logger.Warning($"No announcement speakers found on station {ToPrettyString(ev.Station)}. Announcement not played: {ev.Message}"); + return; + } + + // Play announcement sound via PVS for each speaker on server side + if (ev.AnnouncementSound != null) + { + foreach (var speaker in speakers) + { + if (!TryComp(speaker, out var speakerComp)) + continue; + + // Check if speaker is enabled and has power + if (!speakerComp.Enabled) + continue; + + if (speakerComp.RequiresPower) + { + if (!TryComp(speaker, out var powerReceiver) || !powerReceiver.Powered) + continue; + } + + // Play announcement sound via PVS from this speaker + var audioParams = AudioParams.Default.WithVolume(-2f * speakerComp.VolumeModifier).WithMaxDistance(speakerComp.Range); + _audioSystem.PlayPvs(ev.AnnouncementSound, speaker, audioParams); + } + } + + // Передаём TTS сразу всем динамикам + var speakerEvent = new SpeakerPlayAnnouncementEvent(ev.Message, ev.AnnouncementSound, ev.AnnounceVoice, ev.TtsData); + foreach (var speaker in speakers) + { + RaiseLocalEvent(speaker, ref speakerEvent); + } + } + + /// + /// Gets all functional announcement speakers on a station. + /// + private List GetStationSpeakers(EntityUid station) + { + var speakers = new List(); + + if (!TryComp(station, out var stationData)) + return speakers; + + // Look through all grids on the station for speakers + foreach (var grid in stationData.Grids) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var speakerComp, out var xform)) + { + // Check if the speaker is on this grid + if (xform.GridUid == grid) + { + speakers.Add(uid); + } + } + } + + return speakers; + } + + /// + /// Dispatches an announcement to all speakers on a station. + /// This is the main entry point for the announcement speaker system. + /// Uses the queue system to prevent overlapping announcements. + /// + public async void DispatchAnnouncementToSpeakers(EntityUid station, string message, SoundSpecifier? announcementSound = null, string? announceVoice = null) + { + var resolvedSound = announcementSound != null ? _audioSystem.ResolveSound(announcementSound) : null; + if (!_isEnabled) + return; + if (!GetVoicePrototype(announceVoice ?? _defaultAnnounceVoice, out var protoVoice)) + return; + var generatedTts = await GenerateTtsForAnnouncement(message, protoVoice); + + var queuedAnnouncement = new QueuedAnnouncement(station, message, resolvedSound, announceVoice, generatedTts) + { + QueuedAt = _timing.CurTime + }; + + if (!_isPlayingAnnouncement) + { + // If no announcement is playing, play immediately + PlayAnnouncementNow(queuedAnnouncement); + } + else + { + // Queue the announcement to play after current one finishes + _announcementQueue.Enqueue(queuedAnnouncement); + } + } + + /// + /// Dispatches an announcement to speakers on all stations. + /// Used for server-wide announcements like round start/end. + /// Uses the queue system to prevent overlapping announcements. + /// + public async void DispatchAnnouncementToAllStations(string message, SoundSpecifier? announcementSound = null, string? announceVoice = null) + { + var resolvedSound = announcementSound != null ? _audioSystem.ResolveSound(announcementSound) : null; + if (!_isEnabled) + return; + if (!GetVoicePrototype(announceVoice ?? _defaultAnnounceVoice, out var protoVoice)) + return; + var generatedTts = await GenerateTtsForAnnouncement(message, protoVoice); + + var stationQuery = EntityQueryEnumerator(); + while (stationQuery.MoveNext(out var stationUid, out var stationData)) + { + var queuedAnnouncement = new QueuedAnnouncement(stationUid, message, resolvedSound, announceVoice, generatedTts) + { + QueuedAt = _timing.CurTime + }; + + if (!_isPlayingAnnouncement) + { + // If no announcement is playing, play immediately + PlayAnnouncementNow(queuedAnnouncement); + } + else + { + // Queue the announcement to play after current one finishes + _announcementQueue.Enqueue(queuedAnnouncement); + } + } + } + + /// + /// Gets a voice prototype by ID, with fallback to default voice. + /// + private bool GetVoicePrototype(string voiceId, [NotNullWhen(true)] out TTSVoicePrototype? voicePrototype) + { + if (!_prototypeManager.TryIndex(voiceId, out voicePrototype)) + { + return _prototypeManager.TryIndex("father_grigori", out voicePrototype); + } + return true; + } + + /// + /// Generates TTS audio for an announcement with the megaphone effect. + /// + private async Task GenerateTtsForAnnouncement(string text, TTSVoicePrototype voicePrototype) + { + try + { + var textSanitized = Sanitize(text); + if (textSanitized == "") return null; + if (char.IsLetter(textSanitized[^1])) + textSanitized += "."; + + // Use TTS manager directly to generate with megaphone effect + var ttsManager = IoCManager.Resolve(); + return await ttsManager.ConvertTextToSpeechAnnounce(voicePrototype, textSanitized); + } + catch (Exception e) + { + Logger.Error($"TTS System error in announcement generation: {e.Message}"); + } + return null; + } + + /// + /// Sanitizes text for TTS generation. + /// + private string Sanitize(string text) + { + return text.Trim(); + } + + /// + /// Checks if a player has any working announcement speakers within range. + /// Used to determine if they should receive announcement messages in chat. + /// + public bool HasWorkingSpeakersNearby(EntityUid playerEntity) + { + if (!TryComp(playerEntity, out var playerTransform)) + return false; + + var playerPos = playerTransform.Coordinates; + + // Find all speakers and check if any are in range and working + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var speakerUid, out var speakerComp, out var speakerTransform)) + { + // Check if speaker is enabled + if (!speakerComp.Enabled) + continue; + + // Check if speaker has power (if required) + if (speakerComp.RequiresPower) + { + if (!TryComp(speakerUid, out var powerReceiver) || !powerReceiver.Powered) + continue; + } + + // Check if player is within range of this speaker + if (playerPos.TryDistance(EntityManager, speakerTransform.Coordinates, out var distance) && + distance <= speakerComp.Range) + { + return true; + } + } + + return false; + } + + /// + /// Gets all speakers on all stations that are working. + /// Used to determine if any announcements can be played at all. + /// + public bool HasAnyWorkingSpeakers() + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var speakerUid, out var speakerComp)) + { + // Check if speaker is enabled + if (!speakerComp.Enabled) + continue; + + // Check if speaker has power (if required) + if (speakerComp.RequiresPower) + { + if (!TryComp(speakerUid, out var powerReceiver) || !powerReceiver.Powered) + continue; + } + + return true; // Found at least one working speaker + } + + return false; + } + + private static TimeSpan GetWavDurationFromBytes(byte[] wavData) + { + // WAV header is 44 bytes for PCM + if (wavData.Length < 44) + return TimeSpan.Zero; + int sampleRate = BitConverter.ToInt32(wavData, 24); + short channels = BitConverter.ToInt16(wavData, 22); + short bitsPerSample = BitConverter.ToInt16(wavData, 34); + int dataSize = BitConverter.ToInt32(wavData, 40); + int bytesPerSample = bitsPerSample / 8; + int totalSamples = dataSize / (bytesPerSample * channels); + if (sampleRate <= 0 || channels <= 0 || bitsPerSample <= 0) + return TimeSpan.Zero; + double durationSeconds = (double)totalSamples / sampleRate; + return TimeSpan.FromSeconds(durationSeconds); + } + + private static TimeSpan GetAudioDurationFromBytes(byte[] audioData, string? fileType = null) + { + if (fileType == "wav" || (audioData.Length > 12 && audioData[0] == 'R' && audioData[1] == 'I' && audioData[2] == 'F' && audioData[8] == 'W' && audioData[9] == 'A' && audioData[10] == 'V')) + { + return GetWavDurationFromBytes(audioData); + } + if (fileType == "ogg" || (audioData.Length > 4 && audioData[0] == 'O' && audioData[1] == 'g' && audioData[2] == 'g' && audioData[3] == 'S')) + { + try + { + var nvorbisType = Type.GetType("NVorbis.VorbisReader, NVorbis"); + if (nvorbisType != null) + { + using var stream = new MemoryStream(audioData); + using var reader = (IDisposable)Activator.CreateInstance(nvorbisType, stream, false)!; + var totalTimeProp = nvorbisType.GetProperty("TotalTime"); + if (totalTimeProp != null) + { + var totalTime = totalTimeProp.GetValue(reader); + if (totalTime is TimeSpan ts) + return ts; + } + } + } + catch + { + // NVorbis не доступен или ошибка — fallback + } + double fallbackBitrate = 128000.0; // 128 кбит/с + double duration = audioData.Length * 8.0 / fallbackBitrate; + return TimeSpan.FromSeconds(duration); + } + return TimeSpan.Zero; + } +} diff --git a/Content.Server/_Sunrise/TTS/TTSManager.cs b/Content.Server/_Sunrise/TTS/TTSManager.cs index ee9fa05057..41ae47be16 100644 --- a/Content.Server/_Sunrise/TTS/TTSManager.cs +++ b/Content.Server/_Sunrise/TTS/TTSManager.cs @@ -42,12 +42,16 @@ public sealed class TTSManager private ISawmill _sawmill = default!; private string _apiUrl = string.Empty; + private string _radioEffect = string.Empty; + private string _announceEffect = string.Empty; public void Initialize() { _sawmill = Logger.GetSawmill("tts"); _cfg.OnValueChanged(SunriseCCVars.TTSApiUrl, OnApiUrlChanged, true); _cfg.OnValueChanged(SunriseCCVars.TTSApiToken, OnApiTokenChanged, true); + _cfg.OnValueChanged(SunriseCCVars.TTSRadioEffect, OnRadioEffectChanged, true); + _cfg.OnValueChanged(SunriseCCVars.TTSAnnounceEffect, OnAnnounceEffectChanged, true); } private void OnApiUrlChanged(string value) @@ -60,6 +64,16 @@ public sealed class TTSManager _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", value); } + private void OnRadioEffectChanged(string value) + { + _radioEffect = value; + } + + private void OnAnnounceEffectChanged(string value) + { + _announceEffect = value; + } + public async Task ConvertTextToSpeech(TTSVoicePrototype voicePrototype, string text, string? effect = null) { WantedCount.Inc(); @@ -137,16 +151,14 @@ public sealed class TTSManager public async Task ConvertTextToSpeechRadio(TTSVoicePrototype voicePrototype, string text) { WantedRadioCount.Inc(); - var soundData = await ConvertTextToSpeech(voicePrototype, text, "radio"); - + var soundData = await ConvertTextToSpeech(voicePrototype, text, _radioEffect); return soundData; } public async Task ConvertTextToSpeechAnnounce(TTSVoicePrototype voicePrototype, string text) { WantedAnnounceCount.Inc(); - var soundData = await ConvertTextToSpeech(voicePrototype, text, "announce"); - + var soundData = await ConvertTextToSpeech(voicePrototype, text, _announceEffect); return soundData; } diff --git a/Content.Server/_Sunrise/TTS/TTSSystem.cs b/Content.Server/_Sunrise/TTS/TTSSystem.cs index 9d8b095c8d..ec2fe1f2ba 100644 --- a/Content.Server/_Sunrise/TTS/TTSSystem.cs +++ b/Content.Server/_Sunrise/TTS/TTSSystem.cs @@ -3,8 +3,13 @@ using System.Linq; using System.Threading.Tasks; using Content.Server.Chat.Systems; using Content.Server.GameTicking; +using Content.Server.Power.Components; using Content.Shared._Sunrise.SunriseCCVars; using Content.Shared._Sunrise.TTS; +using Content.Shared._Sunrise.AnnouncementSpeaker.Components; +using Content.Shared._Sunrise.AnnouncementSpeaker.Events; +using Robust.Server.GameObjects; +using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; using Robust.Shared.Player; @@ -58,6 +63,7 @@ public sealed partial class TTSSystem : EntitySystem SubscribeLocalEvent(OnEntitySpoke); SubscribeLocalEvent(OnRadioReceiveEvent); SubscribeLocalEvent(OnAnnouncementSpoke); + SubscribeLocalEvent(OnSpeakerPlayAnnouncement); SubscribeNetworkEvent(OnRequestPreviewTTS); SubscribeNetworkEvent(OnClientOptionTTS); @@ -132,6 +138,12 @@ public sealed partial class TTSSystem : EntitySystem return; } + // Play announcement sound first if available (for global announcements) + if (args.AnnouncementSound != null) + { + _audioSystem.PlayGlobal(args.AnnouncementSound, args.Source, true); + } + if (!_isEnabled || args.Message.Length > MaxMessageChars * 2 || !GetVoicePrototype(args.AnnounceVoice ?? _defaultAnnounceVoice, out var protoVoice)) @@ -139,7 +151,55 @@ public sealed partial class TTSSystem : EntitySystem var soundData = await GenerateTTS(args.Message, protoVoice, isAnnounce: true); soundData ??= []; - RaiseNetworkEvent(new AnnounceTtsEvent(soundData, args.AnnouncementSound), args.Source.RemovePlayers(_ignoredRecipients)); + RaiseNetworkEvent(new PlayTTSEvent(soundData, null, isRadio: false), args.Source.RemovePlayers(_ignoredRecipients)); + } + + /// + /// Handles TTS generation for speaker-based announcements. + /// This is the new system that replaces global broadcast announcements. + /// + private void OnSpeakerPlayAnnouncement(EntityUid speakerUid, AnnouncementSpeakerComponent component, ref SpeakerPlayAnnouncementEvent args) + { + // Check if speaker is enabled + if (!component.Enabled) + return; + + // Check if speaker has power (if required) + if (component.RequiresPower) + { + if (!TryComp(speakerUid, out var powerReceiver) || !powerReceiver.Powered) + return; + } + + if (!_isEnabled) + return; + + byte[]? soundData = args.GeneratedTts; + if (soundData == null || soundData.Length == 0) + { + return; + } + + var speakerPos = Transform(speakerUid).Coordinates; + var playersInRange = Filter.Empty(); + + var playerQuery = EntityQueryEnumerator(); + while (playerQuery.MoveNext(out var playerUid, out var actor, out var playerTransform)) + { + if (speakerPos.TryDistance(EntityManager, playerTransform.Coordinates, out var distance) && + distance <= component.Range) + { + playersInRange = playersInRange.AddPlayer(actor.PlayerSession); + } + } + + // Send TTS to players in range, excluding those who have disabled TTS + var filteredPlayers = playersInRange.RemovePlayers(_ignoredRecipients); + if (filteredPlayers.Recipients.Any()) + { + var speakerNetEntity = GetNetEntity(speakerUid); + RaiseNetworkEvent(new PlayTTSEvent(soundData, speakerNetEntity, isRadio: false), filteredPlayers); + } } private async void OnEntitySpoke(EntityUid uid, TTSComponent component, EntitySpokeEvent args) diff --git a/Content.Shared/_Sunrise/AnnouncementSpeaker/Components/AnnouncementSpeakerComponent.cs b/Content.Shared/_Sunrise/AnnouncementSpeaker/Components/AnnouncementSpeakerComponent.cs new file mode 100644 index 0000000000..fa48a3795c --- /dev/null +++ b/Content.Shared/_Sunrise/AnnouncementSpeaker/Components/AnnouncementSpeakerComponent.cs @@ -0,0 +1,35 @@ +using Robust.Shared.Audio; + +namespace Content.Shared._Sunrise.AnnouncementSpeaker.Components; + +/// +/// Marks an entity as a speaker that can broadcast station-wide announcements. +/// Announcements will be played spatially from this speaker with the configured range. +/// +[RegisterComponent] +public sealed partial class AnnouncementSpeakerComponent : Component +{ + /// + /// The range at which this speaker can be heard from. + /// + [DataField("range")] + public float Range = 20f; + + /// + /// Whether this speaker is currently enabled. + /// + [DataField("enabled")] + public bool Enabled = true; + + /// + /// Volume modifier for announcements played through this speaker. + /// + [DataField("volumeModifier")] + public float VolumeModifier = 1.0f; + + /// + /// Whether this speaker requires power to function. + /// + [DataField("requiresPower")] + public bool RequiresPower = true; +} \ No newline at end of file diff --git a/Content.Shared/_Sunrise/AnnouncementSpeaker/Events/AnnouncementSpeakerEvents.cs b/Content.Shared/_Sunrise/AnnouncementSpeaker/Events/AnnouncementSpeakerEvents.cs new file mode 100644 index 0000000000..da7bd313b0 --- /dev/null +++ b/Content.Shared/_Sunrise/AnnouncementSpeaker/Events/AnnouncementSpeakerEvents.cs @@ -0,0 +1,30 @@ +using Content.Shared.Station.Components; +using Robust.Shared.Audio; + +namespace Content.Shared._Sunrise.AnnouncementSpeaker.Events; + +/// +/// Event raised when a station-wide announcement should be played through speakers. +/// This replaces the global broadcast system with a speaker-based network. +/// +[ByRefEvent] +public readonly record struct AnnouncementSpeakerEvent( + EntityUid Station, + string Message, + ResolvedSoundSpecifier? AnnouncementSound, + string? AnnounceVoice, + byte[]? TtsData = null) +{ +} + +/// +/// Event raised on individual speakers to play an announcement. +/// +[ByRefEvent] +public readonly record struct SpeakerPlayAnnouncementEvent( + string Message, + ResolvedSoundSpecifier? AnnouncementSound, + string? AnnounceVoice, + byte[]? GeneratedTts = null) +{ +} diff --git a/Content.Shared/_Sunrise/SunriseCCVars/SunriseCCVars.cs b/Content.Shared/_Sunrise/SunriseCCVars/SunriseCCVars.cs index e881c9669f..92ba239810 100644 --- a/Content.Shared/_Sunrise/SunriseCCVars/SunriseCCVars.cs +++ b/Content.Shared/_Sunrise/SunriseCCVars/SunriseCCVars.cs @@ -57,8 +57,11 @@ public sealed partial class SunriseCCVars : CVars public static readonly CVarDef TTSRadioVolume = CVarDef.Create("tts.radio_volume", 0.50f, CVar.CLIENTONLY | CVar.ARCHIVE); - public static readonly CVarDef TTSAnnounceVolume = - CVarDef.Create("tts.announce_volume", 0.50f, CVar.CLIENTONLY | CVar.ARCHIVE); + public static readonly CVarDef TTSRadioEffect = + CVarDef.Create("tts.radio_effect", "radio", CVar.SERVERONLY | CVar.ARCHIVE); + + public static readonly CVarDef TTSAnnounceEffect = + CVarDef.Create("tts.announce_effect", "tiny_room", CVar.SERVERONLY | CVar.ARCHIVE); /** * Ban Webhook diff --git a/Content.Shared/_Sunrise/TTS/AnnounceTTSEvent.cs b/Content.Shared/_Sunrise/TTS/AnnounceTTSEvent.cs deleted file mode 100644 index 21c3955e79..0000000000 --- a/Content.Shared/_Sunrise/TTS/AnnounceTTSEvent.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Robust.Shared.Audio; -using Robust.Shared.Serialization; - -namespace Content.Shared._Sunrise.TTS; - -[Serializable, NetSerializable] -public sealed class AnnounceTtsEvent(byte[] data, ResolvedSoundSpecifier? announcementSound) - : EntityEventArgs -{ - public byte[] Data { get; } = data; - public ResolvedSoundSpecifier? AnnouncementSound = announcementSound; -} diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml index 6d04ada47c..92ca385de2 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml @@ -6,6 +6,10 @@ abstract: true components: - type: StationAiWhitelist + - type: AnnouncementSpeaker # Sunrise-Edit: Add announcement speaker capability + range: 15 + enabled: true + requiresPower: true - type: Electrified enabled: false usesApcPower: true diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml index 561991cd26..14c8afb05b 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml @@ -4,6 +4,10 @@ name: camera description: A surveillance camera. It's watching you. Kinda. components: + - type: AnnouncementSpeaker # Sunrise-Edit: Add announcement speaker capability to cameras + range: 12 + enabled: true + requiresPower: true - type: Physics bodyType: Static - type: Fixtures From cd911877edc7f2f38785e9d5f66dc9eaac5c651e Mon Sep 17 00:00:00 2001 From: Sunrise-Bot Date: Mon, 25 Aug 2025 02:49:15 +0300 Subject: [PATCH 099/108] Automatic changelog update --- Resources/Changelog/ChangelogSunrise.yml | 58 ++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/Resources/Changelog/ChangelogSunrise.yml b/Resources/Changelog/ChangelogSunrise.yml index e5f78df889..6d0e788bf3 100644 --- a/Resources/Changelog/ChangelogSunrise.yml +++ b/Resources/Changelog/ChangelogSunrise.yml @@ -21298,3 +21298,61 @@ id: 1394 time: '2025-08-24T04:02:54.0000000+00:00' url: https://github.com/space-sunrise/sunrise-station/pull/2984 +- author: Copilot AI, VigersRay + changes: + - message: "\u041F\u0435\u0440\u0435\u0440\u0430\u0431\u043E\u0442\u0430\u043D\u0430\ + \ \u0441\u0438\u0441\u0442\u0435\u043C\u0430 \u043E\u043F\u043E\u0432\u0435\u0449\ + \u0435\u043D\u0438\u0439 - \u0442\u0435\u043F\u0435\u0440\u044C \u0438\u0441\ + \u043F\u043E\u043B\u044C\u0437\u0443\u044E\u0442\u0441\u044F \u043F\u0440\u043E\ + \u0441\u0442\u0440\u0430\u043D\u0441\u0442\u0432\u0435\u043D\u043D\u044B\u0435\ + \ \u0434\u0438\u043D\u0430\u043C\u0438\u043A\u0438 \u0432\u043C\u0435\u0441\u0442\ + \u043E \u0433\u043B\u043E\u0431\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u0437\ + \u0432\u0443\u043A\u0430" + type: Tweak + - message: "\u0418\u0433\u0440\u043E\u043A\u0438 \u0432\u043D\u0435 \u0441\u0442\ + \u0430\u043D\u0446\u0438\u0438 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\ + \ \u0441\u043B\u044B\u0448\u0430\u0442 \u043E\u043F\u043E\u0432\u0435\u0449\u0435\ + \u043D\u0438\u044F \u0441\u0442\u0430\u043D\u0446\u0438\u0438." + type: Tweak + - message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D \u0440\u0435\u0430\u043B\ + \u0438\u0441\u0442\u0438\u0447\u043D\u044B\u0439 \u043F\u0440\u043E\u0441\u0442\ + \u0440\u0430\u043D\u0441\u0442\u0432\u0435\u043D\u043D\u044B\u0439 \u0437\u0432\ + \u0443\u043A \u0434\u043B\u044F \u043E\u043F\u043E\u0432\u0435\u0449\u0435\u043D\ + \u0438\u0439." + type: Add + - message: "\u0418\u043D\u0442\u0435\u0440\u043A\u043E\u043C\u044B \u0438 \u043A\ + \u0430\u043C\u0435\u0440\u044B \u0442\u0435\u043F\u0435\u0440\u044C \u0444\u0443\ + \u043D\u043A\u0446\u0438\u043E\u043D\u0438\u0440\u0443\u044E\u0442 \u043A\u0430\ + \u043A \u0434\u0438\u043D\u0430\u043C\u0438\u043A\u0438 \u043E\u043F\u043E\u0432\ + \u0435\u0449\u0435\u043D\u0438\u0439." + type: Add + - message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0430 \u0441\u0438\u0441\ + \u0442\u0435\u043C\u0430 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u0434\u043B\ + \u044F \u043F\u0440\u0435\u0434\u043E\u0442\u0432\u0440\u0430\u0449\u0435\u043D\ + \u0438\u044F \u043D\u0430\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u043E\u043F\ + \u043E\u0432\u0435\u0449\u0435\u043D\u0438\u0439." + type: Add + - message: "\u0423\u0434\u0430\u043B\u0435\u043D\u044B \u043D\u0430\u0441\u0442\u0440\ + \u043E\u0439\u043A\u0438 \u0433\u0440\u043E\u043C\u043A\u043E\u0441\u0442\u0438\ + \ \u043E\u043F\u043E\u0432\u0435\u0449\u0435\u043D\u0438\u0439 \u0438\u0437\ + \ \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043A." + type: Remove + - message: "\u041E\u043F\u043E\u0432\u0435\u0449\u0435\u043D\u0438\u044F \u0432\ + \ \u0447\u0430\u0442\u0435 \u0442\u0435\u043F\u0435\u0440\u044C \u043F\u043E\ + \u043A\u0430\u0437\u044B\u0432\u0430\u044E\u0442\u0441\u044F \u0442\u043E\u043B\ + \u044C\u043A\u043E \u0438\u0433\u0440\u043E\u043A\u0430\u043C \u0441 \u0440\u0430\ + \u0431\u043E\u0447\u0438\u043C\u0438 \u0434\u0438\u043D\u0430\u043C\u0438\u043A\ + \u0430\u043C\u0438 \u043F\u043E\u0431\u043B\u0438\u0437\u043E\u0441\u0442\u0438\ + ." + type: Add + - message: "\u0421\u0442\u0430\u043D\u0446\u0438\u043E\u043D\u043D\u044B\u0435 \u043E\ + \u043F\u043E\u0432\u0435\u0449\u0435\u043D\u0438\u044F \u0442\u0435\u043F\u0435\ + \u0440\u044C \u043F\u0435\u0440\u0435\u0434\u0430\u044E\u0442 \u0433\u043E\u043B\ + \u043E\u0441 \u0430\u0432\u0442\u043E\u0440\u0430 \u0438 \u0431\u043E\u043B\u044C\ + \u0448\u0435 \u043D\u0435 \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u044E\u0442\ + \ \u0432 \u043A\u043E\u043D\u0446\u0435 \u0438\u043C\u044F \u0430\u0432\u0442\ + \u043E\u0440\u0430." + type: Tweak + id: 1395 + time: '2025-08-24T23:47:27.0000000+00:00' + url: https://github.com/space-sunrise/sunrise-station/pull/3000 From 41cd012cbf57bca2fb55aec60aef475a407ceb8f Mon Sep 17 00:00:00 2001 From: Vigers Ray Date: Mon, 25 Aug 2025 03:17:21 +0300 Subject: [PATCH 100/108] =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D1=83=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D1=84=D0=B8=D0=BA=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_strings/_sunrise/guidebook/guides.ftl | 47 ------ .../_strings/_sunrise/guidebook/guides.ftl | 47 ------ .../_sunrise/tts/tts-voices-sunrise.ftl | 4 +- .../Catalog/sponsor_uplink_catalog.yml | 6 +- .../_Sunrise/Guidebook/rulesLust.yml | 149 ------------------ .../Prototypes/_Sunrise/game_preset_pools.yml | 21 +-- .../Prototypes/_Sunrise/secret_weights.yml | 18 --- .../Guidebook/ServerRules/LustRules.xml | 9 -- .../ServerRules/RulesLust/RuleLust0.xml | 9 -- .../ServerRules/RulesLust/RuleLust1.xml | 12 -- .../ServerRules/RulesLust/RuleLust2.xml | 10 -- .../ServerRules/RulesLust/RuleLust3.xml | 10 -- .../ServerRules/RulesLust/RuleLust4.xml | 17 -- .../RulesLust/Subrules/RuleLust11.xml | 4 - .../RulesLust/Subrules/RuleLust12.xml | 6 - .../RulesLust/Subrules/RuleLust13.xml | 8 - .../RulesLust/Subrules/RuleLust14.xml | 10 -- .../RulesLust/Subrules/RuleLust15.xml | 6 - .../RulesLust/Subrules/RuleLust21.xml | 6 - .../RulesLust/Subrules/RuleLust22.xml | 8 - .../RulesLust/Subrules/RuleLust23.xml | 4 - .../RulesLust/Subrules/RuleLust24.xml | 8 - .../RulesLust/Subrules/RuleLust25.xml | 6 - .../RulesLust/Subrules/RuleLust31.xml | 8 - .../RulesLust/Subrules/RuleLust32.xml | 4 - .../RulesLust/Subrules/RuleLust33.xml | 4 - .../RulesLust/Subrules/RuleLust41.xml | 7 - .../RulesLust/Subrules/RuleLust42.xml | 6 - .../RulesLust/Subrules/RuleLust43.xml | 9 -- .../RulesLust/Subrules/RuleLust44.xml | 8 - .../RulesLust/Subrules/RuleLust441.xml | 8 - .../RulesLust/Subrules/RuleLust45.xml | 8 - 32 files changed, 9 insertions(+), 478 deletions(-) delete mode 100644 Resources/Prototypes/_Sunrise/Guidebook/rulesLust.yml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/LustRules.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust0.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust1.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust2.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust3.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust4.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust11.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust12.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust13.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust14.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust15.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust21.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust22.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust23.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust24.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust25.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust31.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust32.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust33.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust41.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust42.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust43.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust44.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust441.xml delete mode 100644 Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust45.xml diff --git a/Resources/Locale/en-US/_strings/_sunrise/guidebook/guides.ftl b/Resources/Locale/en-US/_strings/_sunrise/guidebook/guides.ftl index 439e5bec9d..bef3ce20be 100644 --- a/Resources/Locale/en-US/_strings/_sunrise/guidebook/guides.ftl +++ b/Resources/Locale/en-US/_strings/_sunrise/guidebook/guides.ftl @@ -44,50 +44,3 @@ guide-entry-sr-rule-excep-10 = Исключение/прецедент 10 guide-entry-sr-rule-cep = Политика эскалации конфликтов guide-entry-sr-rule-ccp = Политика создания персонажей guide-entry-sr-rule-pana = Препятствование аресту неантагонистами - -## Frontier rules - -guide-entry-fr-rules = Правила сервера: Frontier -guide-entry-fr-rule-1 = Правило 1 -guide-entry-fr-rule-2 = Правило 2 -guide-entry-fr-rule-3 = Правило 3 -guide-entry-fr-rule-3-1 = Правило 3.1 -guide-entry-fr-rule-4 = Правило 4 -guide-entry-fr-rule-5 = Правило 5 -guide-entry-fr-rule-6 = Правило 6 -guide-entry-fr-rule-7 = Правило 7 -guide-entry-fr-rule-8 = Правило 8 -guide-entry-fr-rule-9 = Правило 9 -guide-entry-fr-rule-10 = Правило 10 -guide-entry-fr-rule-11 = Правило 11 -guide-entry-fr-rule-illegal-earnings = Запрещенные способы заработка -guide-entry-fr-rule-long-range-combat = Правила дальногобоя -guide-entry-fr-rule-c-i-technological-disks = Правила создания и реализации технологических дисков - -## Lust station rules - -guide-entry-ls-rules = Правила сервера: Lust Station -guide-entry-ls-rule-0 = Универсальное правило -guide-entry-lust-rule-1 = Основные правила RP -guide-entry-lust-rule-1-1 = Пренебрежение последствиями для персонажа -guide-entry-lust-rule-1-2 = NRP поведение -guide-entry-lust-rule-1-3 = Логика персонажа -guide-entry-lust-rule-1-4 = Память персонажей -guide-entry-lust-rule-1-5 = Отыгрыш своей роли -guide-entry-lust-rule-2 = Гриферство -guide-entry-lust-rule-2-1 = Применение силы -guide-entry-lust-rule-2-2 = Убийство -guide-entry-lust-rule-2-3 = Манчкинизм -guide-entry-lust-rule-2-4 = Пауэргейминг -guide-entry-lust-rule-2-5 = Метагейминг -guide-entry-lust-rule-3 = Самоантагонизм -guide-entry-lust-rule-3-1 = Валидхант -guide-entry-lust-rule-3-2 = Мультиаккаунт -guide-entry-lust-rule-3-3 = Сторонее ПО, читы -guide-entry-lust-rule-4 = Возраст -guide-entry-lust-rule-4-1 = Запрещённые фетиши -guide-entry-lust-rule-4-2 = Приоритеты -guide-entry-lust-rule-4-3 = ERP полиция -guide-entry-lust-rule-4-4 = Приватность -guide-entry-lust-rule-4-4-1 = Распространение -guide-entry-lust-rule-4-5 = ЕРП ради ЕРП diff --git a/Resources/Locale/ru-RU/_strings/_sunrise/guidebook/guides.ftl b/Resources/Locale/ru-RU/_strings/_sunrise/guidebook/guides.ftl index 5dcb9ea94f..1da5a94175 100644 --- a/Resources/Locale/ru-RU/_strings/_sunrise/guidebook/guides.ftl +++ b/Resources/Locale/ru-RU/_strings/_sunrise/guidebook/guides.ftl @@ -45,50 +45,3 @@ guide-entry-sr-rule-excep-10 = Исключение/прецедент 10 guide-entry-sr-rule-cep = Политика эскалации конфликтов guide-entry-sr-rule-ccp = Политика создания персонажей guide-entry-sr-rule-pana = Препятствование аресту неантагонистами - -## Frontier rules - -guide-entry-fr-rules = Правила сервера: Frontier -guide-entry-fr-rule-1 = Правило 1 -guide-entry-fr-rule-2 = Правило 2 -guide-entry-fr-rule-3 = Правило 3 -guide-entry-fr-rule-3-1 = Правило 3.1 -guide-entry-fr-rule-4 = Правило 4 -guide-entry-fr-rule-5 = Правило 5 -guide-entry-fr-rule-6 = Правило 6 -guide-entry-fr-rule-7 = Правило 7 -guide-entry-fr-rule-8 = Правило 8 -guide-entry-fr-rule-9 = Правило 9 -guide-entry-fr-rule-10 = Правило 10 -guide-entry-fr-rule-11 = Правило 11 -guide-entry-fr-rule-illegal-earnings = Запрещенные способы заработка -guide-entry-fr-rule-long-range-combat = Правила дальногобоя -guide-entry-fr-rule-c-i-technological-disks = Правила создания и реализации технологических дисков - -## Lust station rules - -guide-entry-ls-rules = Правила сервера: Lust Station -guide-entry-ls-rule-0 = Универсальное правило -guide-entry-lust-rule-1 = Основные правила RP -guide-entry-lust-rule-1-1 = Пренебрежение последствиями для персонажа -guide-entry-lust-rule-1-2 = NRP поведение -guide-entry-lust-rule-1-3 = Логика персонажа -guide-entry-lust-rule-1-4 = Память персонажей -guide-entry-lust-rule-1-5 = Отыгрыш своей роли -guide-entry-lust-rule-2 = Гриферство -guide-entry-lust-rule-2-1 = Применение силы -guide-entry-lust-rule-2-2 = Убийство -guide-entry-lust-rule-2-3 = Манчкинизм -guide-entry-lust-rule-2-4 = Пауэргейминг -guide-entry-lust-rule-2-5 = Метагейминг -guide-entry-lust-rule-3 = Самоантагонизм -guide-entry-lust-rule-3-1 = Валидхант -guide-entry-lust-rule-3-2 = Мультиаккаунт -guide-entry-lust-rule-3-3 = Сторонее ПО, читы -guide-entry-lust-rule-4 = Возраст -guide-entry-lust-rule-4-1 = Запрещённые фетиши -guide-entry-lust-rule-4-2 = Приоритеты -guide-entry-lust-rule-4-3 = ERP полиция -guide-entry-lust-rule-4-4 = Приватность -guide-entry-lust-rule-4-4-1 = Распространение -guide-entry-lust-rule-4-5 = ЕРП ради ЕРП \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_strings/_sunrise/tts/tts-voices-sunrise.ftl b/Resources/Locale/ru-RU/_strings/_sunrise/tts/tts-voices-sunrise.ftl index 8f80d8c1b6..13b8e834b1 100644 --- a/Resources/Locale/ru-RU/_strings/_sunrise/tts/tts-voices-sunrise.ftl +++ b/Resources/Locale/ru-RU/_strings/_sunrise/tts/tts-voices-sunrise.ftl @@ -16,7 +16,7 @@ tts-voice-name-stalker-sidorovich = [STALKER] Сидорович 2.0 tts-voice-name-scp-049 = SCP-049 tts-voice-name-scp-079 = SCP-079 tts-voice-name-ludvig = [Юбубер] Людвиг А. -tts-voice-name-icarus-revenge = [Lust-Модератор] IcarusRevenge +tts-voice-name-icarus-revenge = [Игрок SS14] IcarusRevenge tts-voice-name-hearthstone-thrud = [Hearthstone] Трад tts-voice-name-kendrick = [SS14] Kendrick tts-voice-name-splikzerys = [SUNRISE] SplikZerys @@ -99,4 +99,4 @@ tts-voice-name-crysis2-overlord-2 = (NEW) [Crysis 2] Overlord 2 tts-voice-name-dictor-vsevolod-kuznetsov = (NEW) [Диктор] Всеволод Кузнецов tts-voice-name-duke-nukem-forever = (NEW) Дюк Нюкем/Шрек tts-voice-name-crysis2-ai-uscaucasian-01 = (NEW) [Crysis 2] Оперативник 4 -tts-voice-name-crysis2-ai-blackops-04 = (NEW) [Crysis 2] Оперативник Blackops 04 \ No newline at end of file +tts-voice-name-crysis2-ai-blackops-04 = (NEW) [Crysis 2] Оперативник Blackops 04 diff --git a/Resources/Prototypes/_Sunrise/Catalog/sponsor_uplink_catalog.yml b/Resources/Prototypes/_Sunrise/Catalog/sponsor_uplink_catalog.yml index f7a61910f2..da165e8e16 100644 --- a/Resources/Prototypes/_Sunrise/Catalog/sponsor_uplink_catalog.yml +++ b/Resources/Prototypes/_Sunrise/Catalog/sponsor_uplink_catalog.yml @@ -2886,7 +2886,7 @@ stock: 1 - type: listing - id: UplinkLustPlushieAndrewNarvi + id: UplinkPlushieAndrewNarvi productEntity: PlushieAndrewNarvi cost: Suntick: 4 @@ -2897,7 +2897,7 @@ stock: 1 - type: listing - id: UplinkLustPlushieTimofeyTatarchuk + id: UplinkPlushieTimofeyTatarchuk productEntity: PlushieTimofeyTatarchuk cost: Suntick: 4 @@ -2908,7 +2908,7 @@ stock: 1 - type: listing - id: UplinkLustPlushieSansaMontanelli + id: UplinkPlushieSansaMontanelli productEntity: PlushieSansaMontanelli cost: Suntick: 4 diff --git a/Resources/Prototypes/_Sunrise/Guidebook/rulesLust.yml b/Resources/Prototypes/_Sunrise/Guidebook/rulesLust.yml deleted file mode 100644 index fab28cf7b6..0000000000 --- a/Resources/Prototypes/_Sunrise/Guidebook/rulesLust.yml +++ /dev/null @@ -1,149 +0,0 @@ -- type: guideEntry - id: LustRuleset - name: guide-entry-rules - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/LustRules.xml" - -- type: guideEntry - id: RuleLust0 - name: guide-entry-lust-rule-0 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust0.xml" - -- type: guideEntry - id: RuleLust1 - name: guide-entry-lust-rule-1 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust1.xml" - -- type: guideEntry - id: RuleLust11 - name: guide-entry-lust-rule-1-1 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust11.xml" - -- type: guideEntry - id: RuleLust12 - name: guide-entry-lust-rule-1-2 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust12.xml" - -- type: guideEntry - id: RuleLust13 - name: guide-entry-lust-rule-1-3 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust13.xml" - -- type: guideEntry - id: RuleLust14 - name: guide-entry-lust-rule-1-4 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust14.xml" - -- type: guideEntry - id: RuleLust15 - name: guide-entry-lust-rule-1-5 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust15.xml" - -- type: guideEntry - id: RuleLust2 - name: guide-entry-lust-rule-2 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust2.xml" - -- type: guideEntry - id: RuleLust21 - name: guide-entry-lust-rule-2-1 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust21.xml" - -- type: guideEntry - id: RuleLust22 - name: guide-entry-lust-rule-2-2 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust22.xml" - -- type: guideEntry - id: RuleLust23 - name: guide-entry-lust-rule-2-3 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust23.xml" - -- type: guideEntry - id: RuleLust24 - name: guide-entry-lust-rule-2-4 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust24.xml" - -- type: guideEntry - id: RuleLust25 - name: guide-entry-lust-rule-2-5 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust25.xml" - -- type: guideEntry - id: RuleLust3 - name: guide-entry-lust-rule-3 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust3.xml" - -- type: guideEntry - id: RuleLust31 - name: guide-entry-lust-rule-3-1 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust31.xml" - -- type: guideEntry - id: RuleLust32 - name: guide-entry-lust-rule-3-2 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust32.xml" - -- type: guideEntry - id: RuleLust33 - name: guide-entry-lust-rule-3-3 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust33.xml" - -- type: guideEntry - id: RuleLust4 - name: guide-entry-lust-rule-4 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust4.xml" - -- type: guideEntry - id: RuleLust41 - name: guide-entry-lust-rule-4-1 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust41.xml" - -- type: guideEntry - id: RuleLust42 - name: guide-entry-lust-rule-4-2 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust42.xml" - -- type: guideEntry - id: RuleLust43 - name: guide-entry-lust-rule-4-3 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust43.xml" - -- type: guideEntry - id: RuleLust44 - name: guide-entry-lust-rule-4-4 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust44.xml" - -- type: guideEntry - id: RuleLust441 - name: guide-entry-lust-rule-4-4-1 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust441.xml" - -- type: guideEntry - id: RuleLust45 - name: guide-entry-lust-rule-4-5 - ruleEntry: true - text: "/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust45.xml" diff --git a/Resources/Prototypes/_Sunrise/game_preset_pools.yml b/Resources/Prototypes/_Sunrise/game_preset_pools.yml index fc509d9827..5e77162fc4 100644 --- a/Resources/Prototypes/_Sunrise/game_preset_pools.yml +++ b/Resources/Prototypes/_Sunrise/game_preset_pools.yml @@ -2,32 +2,19 @@ id: DefaultLowPopPresetPool presets: Greenshift: [0, 200] - Secret: [30, 200] + Secret: [20, 200] - type: gamePresetPool id: DefaultHighPopPresetPool presets: Greenshift: [0, 200] - Secret: [50, 200] + Secret: [20, 200] - type: gamePresetPool id: DynamicPresetPool presets: - Nukeops: [30, 200] - Traitor: [5, 200] - Zombie: [30, 200] - Survival: [5, 200] - Revolutionary: [30, 200] - Wizard: [30, 200] - BloodCult: [30, 200] - AssaultOps: [10, 200] - FleshCult: [30, 200] - -- type: gamePresetPool - id: PeacePresetPool - presets: - Greenshift: [50, 100] - Extra: [50, 100] + Greenshift: [0, 200] + Dynamic: [20, 200] - type: gamePresetPool id: MiddleLowPopPresetPool diff --git a/Resources/Prototypes/_Sunrise/secret_weights.yml b/Resources/Prototypes/_Sunrise/secret_weights.yml index 9378e7e28a..540652ecb2 100644 --- a/Resources/Prototypes/_Sunrise/secret_weights.yml +++ b/Resources/Prototypes/_Sunrise/secret_weights.yml @@ -15,21 +15,3 @@ AssaultOps: 0.1 Revolutionary: 0.1 Nukeops: 0.1 - -- type: weightedRandom - id: LustSpectralSecret - weights: - Traitor: 0.6 - BloodCult: 0.1 - AssaultOps: 0.1 - Revolutionary: 0.1 - Nukeops: 0.1 - -- type: weightedRandom - id: LustVenusSecret - weights: - Traitor: 0.6 - BloodCult: 0.1 - AssaultOps: 0.1 - Revolutionary: 0.1 - Nukeops: 0.1 diff --git a/Resources/ServerInfo/Guidebook/ServerRules/LustRules.xml b/Resources/ServerInfo/Guidebook/ServerRules/LustRules.xml deleted file mode 100644 index 45e8385050..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/LustRules.xml +++ /dev/null @@ -1,9 +0,0 @@ - - # Правила сервера - - - [textlink="0. Универсальное правило" link="RuleLust0"] - - [textlink="1. Основные правила RP" link="RuleLust1"] - - [textlink="2. Гриферство" link="RuleLust2"] - - [textlink="3. Самоантагонизм" link="RuleLust3"] - - [textlink="4. Возраст" link="RuleLust4"] - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust0.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust0.xml deleted file mode 100644 index e1b6f20055..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust0.xml +++ /dev/null @@ -1,9 +0,0 @@ - - # 0. Универсальное правило - - Правила не могут покрыть абсолютно все ситуации, поэтому администрация вправе наказывать нарушителей порядка на своё усмотрение, если те продолжают действовать спорно после предупреждения. - - Всегда помните, что вы - обычный пользователь, который должен подчиняться правилам для сохранения дружелюбной атмосферы на сервере. Если администрация указывает вам на ваши неправильные действия - прислушайтесь к ним и прекратите, ведь вы всегда можете понести наказание, даже за то, что в правилах не прописано. - - Не пытайтесь искать лазейки в правилах. Они были написаны для безопасности наших пользователей, подобные действия будут строго караться. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust1.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust1.xml deleted file mode 100644 index 8af7043ee0..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust1.xml +++ /dev/null @@ -1,12 +0,0 @@ - - # 1. Основные правила RP - Для создания приятной RP атмосферы от всех игроков требуется соответствовать тематике игры и самым очевидным основам этого направления. Ваш персонаж - не вы, помните об этом и ответственно подходите к отыгрышу. Для большинства взаимодействий с игровым миром и другими персонажами должна быть своя внутриигровая причина, обоснованная нуждами и желаниями персонажа, подкреплёнными словами. - - Будьте грамотны, избегайте глупых сокращений или слов, которые бы взрослый человек, работающий на космической станции, ни за что бы не стал говорить. Если вы хотите отыграть безграмотного персонажа, рекомендуется перед этим предупредить администрацию о своём отыгрыше. - - - [textlink="1.1. Пренебрежение последствиями для персонажа" link="RuleLust11"] - - [textlink="1.2. NRP поведение" link="RuleLust12"] - - [textlink="1.3. Логика персонажа" link="RuleLust13"] - - [textlink="1.4. Память персонажей" link="RuleLust14"] - - [textlink="1.5. Отыгрыш своей роли" link="RuleLust15"] - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust2.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust2.xml deleted file mode 100644 index 663a48a5dd..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust2.xml +++ /dev/null @@ -1,10 +0,0 @@ - - # 2. Гриферство - Гриферство - намеренная порча игрового процесса другим игрокам без веской на то причины, помимо получения личного удовольствия. Гриферством может считаться: необоснованное нарушение целостности станции, застройка проходов или поломка/блокировка шлюзов, преследование и издевательство в IC, выходящее за рамки отыгрыша, и так далее. - - - [textlink="2.1. Применение силы" link="RuleLust21"] - - [textlink="2.2. Убийство" link="RuleLust22"] - - [textlink="2.3. Манчкинизм" link="RuleLust23"] - - [textlink="2.4. Пауэргейминг" link="RuleLust24"] - - [textlink="2.5. Метагейминг" link="RuleLust25"] - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust3.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust3.xml deleted file mode 100644 index e710ab383f..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust3.xml +++ /dev/null @@ -1,10 +0,0 @@ - - # 3. Самоантагонизм - Выполнение действий и проявление поведения, присущего антагонистам, при этом им не являясь, нарушает данное правило. Примеры нарушений: проведение революций; создание агрессивных культов или банд, несущих опасность для станции; хищение особо ценных предметов; причинение серьёзного вреда станции; добровольное содействие антагонистам в безопасных условиях и так далее. - - Мелкие нарушения или не летальное сопротивление аресту разрешены, но вы должны понимать грань и не отвлекать на себя внимание всего отдела службы безопасности. Проведение митингов или забастовок, создание мирных культов или банд, а также ведение неопасной криминальной деятельности, не запрещено. Освобождение кого-то из заключения не запрещено, но если вы хотите освободить кого-то из перманентной камеры, то вам следует спросить разрешения у администрации. - - - [textlink="3.1. Валидхант" link="RuleLust31"] - - [textlink="3.2. Мультиаккаунт" link="RuleLust32"] - - [textlink="3.3. Сторонее ПО, читы" link="RuleLust33"] - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust4.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust4.xml deleted file mode 100644 index 4c466ceb49..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/RuleLust4.xml +++ /dev/null @@ -1,17 +0,0 @@ - - # 4. Возраст - Одно из самых главных правил проекта - вам должно быть минимум 18 лет для игры на наших серверах. Иными словами, лишь совершеннолетние пользователи могут играть на Lust Station и находиться в нашем Discord сообществе. Нарушение данного правила карается баном со сроком до вашего совершеннолетия. - - В случае подозрения в несовершеннолетии вас могут вызвать на проверку возраста. Помните, что только создатель проекта (sshvedik) имеет право на проверку возраста. Не отправляйте личные данные и/или фотографии документов иным лицам. - - Даже шутки о вашем возрасте могут привести к блокировке. Администрация может понять любую шутку, кроме этой. - - Отыгрыш несовершеннолетних персонажей карается перманентной блокировкой без возможности обжалования. - - - [textlink="4.1. Зепрещённые фетиши" link="RuleLust41"] - - [textlink="4.2. Приоритеты" link="RuleLust42"] - - [textlink="4.3. ERP полиция" link="RuleLust43"] - - [textlink="4.4. Приватность" link="RuleLust44"] - - [textlink="4.4.1. Распространение" link="RuleLust441"] - - [textlink="4.5. ЕРП ради ЕРП" link="RuleLust45"] - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust11.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust11.xml deleted file mode 100644 index 46cba0fe78..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust11.xml +++ /dev/null @@ -1,4 +0,0 @@ - - # 1.1. Пренебрежение последствиями для персонажа - Мало кто бы хотел потерять свою работу, попасть в тюрьму или поссориться с руководством по какой-либо причине. Тоже самое применяется и ко всем персонажам. Не надо устраивать истерики на ровном месте или намеренно идти на конфликт с начальством из-за какой-то мелочи, ведь они всё ещё могут вас уволить, оставить без зарплаты и так далее. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust12.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust12.xml deleted file mode 100644 index 76bf28a4db..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust12.xml +++ /dev/null @@ -1,6 +0,0 @@ - - # 1.2. NRP поведение - Прежде всего, Space Station 14 - это игра, нацеленная на хотя бы минимальный RolePlay. Нарушение RP атмосферы, выход из роли и просто действия, несоответствующие вашей роли, являются NRP (NonRolePlay). Построить с десяток унитазов перед входом в бриг, обсуждать с клоуном отличную погоду за окном вашего реального дома или спрашивать в IC чате, на какую кнопку нажимать, чтобы поменять руки - яркий пример того, как не надо себя вести. - - Эмоции всегда являются важным аспектом отыгрыша персонажа, соблюдайте основные правила RP при написании каждой эмоции, без глупостей или других признаков NRP. Не эксплуатируйте эмоции для общения на персонажах, которые не могут говорить (неразумные животные/мим). - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust13.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust13.xml deleted file mode 100644 index 7c8821fced..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust13.xml +++ /dev/null @@ -1,8 +0,0 @@ - - # 1.3. Логика персонажа - Ко всем действиям персонажей должна применяться логика и правила игрового мира. Отыгрывайте персонажа так, как поступил бы он, а не вы, отталкиваясь от событий, происходящих в раунде. - - Крупная космическая корпорация вряд-ли станет набирать на важные должности людей с психическими отклонениями, серьёзными инвалидностями или заболеваниями, помните об этом, когда придумываете историю своего персонажа. - - При выборе имени для персонажа, соблюдайте рамки разумного. Очевидно, что в далёком будущем границы культур, народностей и тому подобного, размылись, но старайтесь соблюдать историю и лор выбранной вами расы при создании персонажа. Имена известных личностей и просто глупые имена будут считаться нарушением этого правила. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust14.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust14.xml deleted file mode 100644 index 31952abd94..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust14.xml +++ /dev/null @@ -1,10 +0,0 @@ - - # 1.4. Память персонажей - Память персонажей о разных сменах сохраняется, но все же есть некоторые условия. Каждый игрок обязан использовать только ту информацию, которую его персонаж мог запомнить. Персонажи не могут запомнить то, что произошло, пока они были мертвы, без сознания или во сне. После смерти персонаж, при воскрешении, забывает все детали своей смерти. - - Каждое существо имеет свою собственную память, при смене существ запрещено использовать память предыдущих взятых под контроль существ. - - Нельзя использовать информацию, полученную за смену, в которой вы окончательно умерли. Также нельзя использовать информацию для мета-ненависти к персонажам других игроков. - - Навыки персонажей распространяются только на время одной смены. Например, инженер не может изучать артефакты, если на предыдущей смене он был учёным. Как это обосновать? Отсутствие практики в течении длительного времени даёт о себе знать. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust15.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust15.xml deleted file mode 100644 index bd5d645a7a..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust15.xml +++ /dev/null @@ -1,6 +0,0 @@ - - # 1.5. Отыгрыш своей роли - Игроки должны выполнять обязанности своих ролей в первую очередь и отыгрывать соответственно. Чините станцию за инженера, лечите людей за врача, или разливайте напитки за бармена, отклонение от выполнения своих обязанностей без весомой причины будет считаться нарушением. - - Следование стандартным рабочим процедурам не обязательно на постоянной основе, только если вы не нарушаете какие-то серьёзные пункты, или не совершаете нарушения из смены в смену. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust21.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust21.xml deleted file mode 100644 index 8365b0c610..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust21.xml +++ /dev/null @@ -1,6 +0,0 @@ - - # 2.1. Применение силы - Вы должны прибегать к насилию только при наличии достаточных RP обоснований или как к последней мере решения проблемы. Простые драки и небольшие потасовки допустимы, но злоупотреблять этим не стоит. - - Напасть с кулаками или подручными средствами и забить до критического состояния вора или человека, проникшего к вам в отдел - неадекватная реакция, для решения подобных дел есть служба безопасности. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust22.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust22.xml deleted file mode 100644 index c8281e32c1..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust22.xml +++ /dev/null @@ -1,8 +0,0 @@ - - # 2.2. Убийство - Каждый персонаж не-антагонист должен иметь весомые обоснования для убийства. Например, месть за жестоко избитого друга. Помните, что такое убийство не должно привести к уничтожению тела другого игрока. Если дело было детально продумано, а мотив убийства достаточно весомый, то вы можете спокойно совершить задуманное, но, если вы не уверены в этом, то лучше обратитесь к администрации. - - Самооборона не должна приводить к убийству. Если вы подрались с другим игроком или на вас напали, вы можете ввести соперника в критическое состояние, но сразу после этого вы должны будете оказать ему помощь или отнести в медицинский отсек. Если враг начал убегать или перестал атаковать вас, драку следует прекратить. Если ваших противников несколько, то убийство разрешено, но вы также будете обязаны оказать им помощь. - - Под это правило также попадают случаи косвенного убийства: умышленное неоказание помощи умирающему игроку или приведение к смерти вашими действиями, например, заграждение выхода из зоны разгерметизации, будет наказуемо. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust23.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust23.xml deleted file mode 100644 index ef27775cde..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust23.xml +++ /dev/null @@ -1,4 +0,0 @@ - - # 2.3. Манчкинизм - Сбор множества различных вещей "на всякий случай" для получения явной выгоды в разных возможных ситуациях, в том числе и беспричинные кражи/грабежи, является нарушением данного правила. Всегда знайте меру - уборщик, который с самого начала смены роется по станции, собирая полезные вещи, словно завтра уже не наступит, явно не является хорошим примером RP отыгрыша. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust24.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust24.xml deleted file mode 100644 index ad7ca93ea1..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust24.xml +++ /dev/null @@ -1,8 +0,0 @@ - - # 2.4. Пауэргейминг - Каждый персонаж игрока - это живое существо, которое всё также ценит свою жизнь и здоровье. Нарушение психологических границ персонажа, принятие чрезмерных действий в безопасное время и несвойственное бесстрашие нарушают данное правило. - - Не создавайте себе угрозу жизни без причины. Если вы видите активированную бомбу, вам угрожают оружием, где-то у вашего отдела ползает огромный тарантул, и так далее, но при этом у вас нет нужных инструментов/вооружения, не надо геройствовать. - - Прятать особо важные предметы, такие как ядерный диск или продвинутые магнитные ботинки, и болтировать двери в свой отдел/кабинет, пока на станции нет никаких выявленных угроз, запрещено. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust25.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust25.xml deleted file mode 100644 index db961a1dc4..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust25.xml +++ /dev/null @@ -1,6 +0,0 @@ - - # 2.5. Метагейминг - Распространение и использование информации, полученной не через IC, запрещено. Будь-то общение во вне игровых или LOOC/OOC чатах или получение знаний, которые ваш персонаж никак не мог получить, и так далее. - - Метакооп между игроками также запрещён. Если вы хотите обучить своего друга игре, то лучше делайте это на локальном сервере, без метакоопа, посредством IC, или заранее предупредите администрацию об этом. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust31.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust31.xml deleted file mode 100644 index af1296265f..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust31.xml +++ /dev/null @@ -1,8 +0,0 @@ - - # 3.1. Валидхант - Намеренная охота или создание ситуаций для убийства антагонистов, не являясь сотрудником службы безопасности, запрещено. Если вы видите антагониста и он не причиняет никому вреда, то попытка атаковать его будет нарушением. - - Если антагонист напал на кого-то, то вы можете защищаться, атакуя его или содействуя его аресту, однако если антагонист начал убегать от вас, или скрылся, то нужно прекратить преследовать его. Если вы видите, как кто-то совершает кражу или похищение, вы можете попытаться остановить преступника в силу своих возможностей, но только для того, чтобы вернуть украденное. - - Это правило не влияет на крупных антагонистов, несущих угрозу уничтожения всей станции, например, ядерных оперативников или зомби. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust32.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust32.xml deleted file mode 100644 index 8382245261..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust32.xml +++ /dev/null @@ -1,4 +0,0 @@ - - # 3.2. Мультиаккаунт - Использование больше одного аккаунта Space Station 14 для игры на сервере запрещено и карается перманентной блокировкой всех аккаунтов. Обход наказания, посредством использования нового аккаунта, также считается нарушением этого правила. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust33.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust33.xml deleted file mode 100644 index 28b14fdaed..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust33.xml +++ /dev/null @@ -1,4 +0,0 @@ - - # 3.3. Сторонее ПО, читы - Применение сторонних программ для получения преимущества над другими игроками в игре запрещено. Багоюз и абуз игровых механик также попадают под это правило. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust41.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust41.xml deleted file mode 100644 index 3d39f2f99b..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust41.xml +++ /dev/null @@ -1,7 +0,0 @@ - - # 4.1. Зепрещённые фетиши - Не каждая тема или фетиш подойдёт для ERP отыгрыша на сервере. По тем или иным причинам на проекте запрещен отыгрыш с такими фетишами, как: - Педофилия, зоофилия, некрофилия, копрофилия, gore (очень жестокий секс с кровью, мясом и расчленением), vore (поедание разумных существ заживо), и другие отвратительные и/или спорные фетиши. - - Устраивать ERP процесс с ССД персонажами также запрещено. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust42.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust42.xml deleted file mode 100644 index 1c0668a6f8..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust42.xml +++ /dev/null @@ -1,6 +0,0 @@ - - # 4.2. Приоритеты - Мы за ERP отыгрыш, но не стоит ставить его на первое место, если вы играете важную роль в жизни станции. - - Очевидно, что если обычный интерн или бармен отлучится на час - ничего страшного не произойдёт. Но если вы являетесь главой или кем-то, без кого станция не может обойтись - позаботьтесь о работе своего отдела/станции. Например, попросите кого-нибудь подменить вас или сделайте свою работу наперёд. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust43.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust43.xml deleted file mode 100644 index 777e161141..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust43.xml +++ /dev/null @@ -1,9 +0,0 @@ - - # 4.3. ERP полиция - Запрещено намеренно прерывать ERP процесс, вмешиваться или мешать ему, не имея на то весомой причины. - - Взломать заблокированный шлюз в дормах смеха ради - является нарушением данного правила. - Ворваться в дормы за антагониста, если вашей целью является участник процесса - не является нарушением данного правила. - - Данное правило не распространяется на публичные ERP процессы. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust44.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust44.xml deleted file mode 100644 index 4a51c9583a..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust44.xml +++ /dev/null @@ -1,8 +0,0 @@ - - # 4.4. Приватность - На наших серверах вы можете устраивать публичные ERP процессы (хоть это и наказуемо по Космическому Закону), а также приватные ERP процессы. Для этого в каждой комнате дормиторий есть специальная кнопка для того, чтобы поставить шлюз на болты. - - В случае, если поставить шлюз на болты невозможно, но ваще место достаточно уединённое, то вы можете обратиться к администрации в Ahelp для того, чтобы вам поставили болты на шлюз. Если нужно будет их снять - вновь напишите администрации. - - Если ваше место отыгрыша является публичным (бар, коридор, тот или иной отдел), то очевидно, что ни о какой приватности речи идти не может, поэтому не стоит просить заболтировать шлюзы в подобные места. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust441.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust441.xml deleted file mode 100644 index 1dc33594dc..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust441.xml +++ /dev/null @@ -1,8 +0,0 @@ - - # 4.4.1. Распространение - Каждый игрок имеет на сохранение личной тайны и приватности. - - - Нельзя каким-либо образом распространять скрины и видео с ERP-процессом без разрешения всех участников этого процесса. - - В случае жалобы на вас, доказательства о соглашении/запрете на распространение будут запрашиваться у обоих игроков. - diff --git a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust45.xml b/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust45.xml deleted file mode 100644 index 4ce8c6ea66..0000000000 --- a/Resources/ServerInfo/Guidebook/ServerRules/RulesLust/Subrules/RuleLust45.xml +++ /dev/null @@ -1,8 +0,0 @@ - - # 4.5. ЕРП ради ЕРП - Эротический Ролевой Отыгрыш должен быть как-либо обоснован. Иными словами, ЕРП ради ЕРП запрещен. - - Вместо бездумного совокупления, вам нужно развивать отношения с другими персонажами. Не устраивайте ЕРП процессы бездумно, просим вас ответственнее подходить к отыгрышу. Вы не нимфоман, вы не должны быть помешаны на сексе просто из-за того, что это секс. - - Данное правило создано в целях предотвращения ЛРП ситуаций в стиле "секс ради секса", который заканчивается на диалоге "го секс". Пожалуйста, отнеситесь ответственно к отыгрышу вашего персонажа и его отношений с другими. - From 7b3f25573232dd3c6a15d62888f73965faa00129 Mon Sep 17 00:00:00 2001 From: Daniel <77834935+daniil7383@users.noreply.github.com> Date: Mon, 25 Aug 2025 02:19:03 +0200 Subject: [PATCH 101/108] CopyMachineFix (#2900) Co-authored-by: Vigers Ray --- Content.Client/Paper/UI/PaperWindow.xaml | 3 +- Content.Client/Paper/UI/PaperWindow.xaml.cs | 22 ++-- .../_Sunrise/CopyMachine/CopyMachineSystem.cs | 36 +----- .../Documents/DocumentFormatSystem.cs | 41 ++++++ .../_Sunrise/CopyMachine/DocTemplate.cs | 29 +++-- .../_Sunrise/SunriseCCVars/SunriseCCVars.cs | 42 +++--- .../_Sunrise/DocumentTemplates/templates.yml | 120 +++++++++--------- .../Centcom/OrderingSpecialEquipment.xml | 2 +- .../RequestCallMembersCentralCommitteeDSO.xml | 2 +- .../_Sunrise/Command/ApplicationAccess.xml | 2 +- .../Command/ApplicationAppointmentInterim.xml | 2 +- .../Command/ApplicationEmployment.xml | 2 +- .../_Sunrise/Command/ApplicationEquipment.xml | 2 +- .../_Sunrise/Command/Certificate.xml | 2 +- .../Command/CertificateAdvancedTraining.xml | 2 +- .../Command/EvacuationShuttleRequest.xml | 2 +- .../_Sunrise/Command/LetterResignation.xml | 4 +- .../Command/OrderDeprivationAccess.xml | 2 +- .../_Sunrise/Command/OrderDismissal.xml | 2 +- .../_Sunrise/Command/OrderEncouragement.xml | 2 +- .../_Sunrise/Command/OrderParolePrisoner.xml | 2 +- .../_Sunrise/Command/PermissionEquipment.xml | 2 +- .../Command/PermissionToCarryWeapons.xml | 2 +- .../Command/PermissionToExtendMarriage.xml | 2 +- .../PermissionToTravelInCaseOfThreat.xml | 2 +- .../_Sunrise/Command/ReportDepartment.xml | 2 +- .../Command/ReportEmployeePerformance.xml | 2 +- .../ReportOnEliminationOfViolations.xml | 2 +- .../Command/ReportOnTheChaptersMeeting.xml | 2 +- .../_Sunrise/Command/ReportStation.xml | 2 +- .../Command/RequestForNonlistedEmployment.xml | 2 +- .../_Sunrise/Command/RequestForPromotion.xml | 2 +- .../_Sunrise/Engineering/ConditionReport.xml | 2 +- .../Engineering/ConstructionPermit.xml | 2 +- .../Engineering/RequestConstructionWork.xml | 2 +- .../Documents/_Sunrise/General/Appeal.xml | 2 +- .../General/OrderMedicalIntervention.xml | 2 +- .../General/ProductManufacturingOrder.xml | 2 +- .../_Sunrise/General/RequestChangeSalary.xml | 2 +- .../Justice/ComplaintViolationLaborRules.xml | 2 +- .../_Sunrise/Justice/DecisionToStartTrial.xml | 2 +- .../Justice/InternalAffairsAgentsReport.xml | 2 +- .../Documents/_Sunrise/Justice/Judgment.xml | 6 +- .../_Sunrise/Justice/RequestDocuments.xml | 4 +- .../_Sunrise/Medical/DeathCertificate.xml | 2 +- .../Medical/PermissionDisposeBody.xml | 2 +- .../Medical/PrescriptionDrugAuthorization.xml | 2 +- .../_Sunrise/Medical/RequestEuthanasia.xml | 2 +- .../_Sunrise/Medical/StatementHealth.xml | 2 +- .../_Sunrise/Science/ExperimentReport.xml | 2 +- .../OrderRecognizingSentienceCreature.xml | 2 +- .../_Sunrise/Science/ReportStudyObject.xml | 2 +- .../_Sunrise/Science/RequestModernization.xml | 2 +- .../RequestRequestToEstablishThreatLevel.xml | 2 +- .../_Sunrise/Security/CertificateOffense.xml | 2 +- .../_Sunrise/Security/ClosingIndictment.xml | 2 +- .../_Sunrise/Security/ComplaintOffense.xml | 2 +- .../_Sunrise/Security/SearchPermission.xml | 2 +- .../Documents/_Sunrise/Security/Sentence.xml | 2 +- .../Security/ShuttleRegistrationRequest.xml | 2 +- .../_Sunrise/Service/DivorceCertificate.xml | 2 +- .../_Sunrise/Service/MarriageCertificate.xml | 2 +- .../_Sunrise/Supply/DisposalReport.xml | 2 +- .../OrderPurchaseResourcesEquipment.xml | 2 +- .../_Sunrise/Supply/OrderPurchaseWeapons.xml | 2 +- .../NoteBeginningMilitaryActions.xml | 2 +- 66 files changed, 223 insertions(+), 196 deletions(-) create mode 100644 Content.Server/_Sunrise/Documents/DocumentFormatSystem.cs diff --git a/Content.Client/Paper/UI/PaperWindow.xaml b/Content.Client/Paper/UI/PaperWindow.xaml index 9699e939a6..35eabf10e5 100644 --- a/Content.Client/Paper/UI/PaperWindow.xaml +++ b/Content.Client/Paper/UI/PaperWindow.xaml @@ -13,8 +13,7 @@ - + diff --git a/Content.Client/Paper/UI/PaperWindow.xaml.cs b/Content.Client/Paper/UI/PaperWindow.xaml.cs index 4f4cc01171..92e7dfb3f9 100644 --- a/Content.Client/Paper/UI/PaperWindow.xaml.cs +++ b/Content.Client/Paper/UI/PaperWindow.xaml.cs @@ -290,17 +290,17 @@ namespace Content.Client.Paper.UI BlankPaperIndicator.Visible = !isEditing && state.Text.Length == 0; // Sunrise-Start - var sprite = _entitySystemManager.GetEntitySystem(); - if (state.ImageContent != null) - { - ImageContent.Texture = sprite.Frame0(state.ImageContent); - if (state.ImageScale != null) - ImageContent.TextureScale = state.ImageScale.Value; - ImageContent.Visible = true; - BlankPaperIndicator.Visible = false; - } - else - ImageContent.Visible = false; + var spriteSys = _entitySystemManager.GetEntitySystem(); + + Robust.Client.Graphics.Texture? tex = null; + if (state.ImageContent is {} spec) + tex = spriteSys.Frame0(spec); + + ImageContent.Visible = tex != null; + ImageContent.Texture = tex; + + ImageContent.HorizontalExpand = false; + ImageContent.VerticalExpand = false; // Sunrise-End StampDisplay.RemoveAllChildren(); diff --git a/Content.Server/_Sunrise/CopyMachine/CopyMachineSystem.cs b/Content.Server/_Sunrise/CopyMachine/CopyMachineSystem.cs index 412df75e85..aa2a55a1c4 100644 --- a/Content.Server/_Sunrise/CopyMachine/CopyMachineSystem.cs +++ b/Content.Server/_Sunrise/CopyMachine/CopyMachineSystem.cs @@ -1,9 +1,8 @@ using System.Linq; using System.Numerics; using System.Text.RegularExpressions; -using Content.Server.GameTicking.Events; using Content.Server.Materials; -using Content.Server.Station.Systems; +using Content.Server._Sunrise.Documents; using Content.Shared._Sunrise.CopyMachine; using Content.Shared._Sunrise.SunriseCCVars; using Content.Shared.Buckle.Components; @@ -38,12 +37,11 @@ public sealed class CopyMachineSystem : EntitySystem [Dependency] private readonly IResourceManager _resourceManager = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IConfigurationManager _configManager = default!; - [Dependency] private readonly StationSystem _stationSystem = default!; [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly LabelSystem _labelSystem = default!; + [Dependency] private readonly DocumentFormatSystem _docFmt = default!; - private TimeSpan _roundStartTime; private readonly Dictionary _docCache = new(); private static readonly Regex DocRegex = @@ -53,7 +51,6 @@ public sealed class CopyMachineSystem : EntitySystem { base.Initialize(); - SubscribeLocalEvent(OnRoundStart); SubscribeLocalEvent(OnUiOpened); SubscribeLocalEvent(OnPrintMessage); SubscribeLocalEvent(OnCopyMessage); @@ -66,7 +63,7 @@ public sealed class CopyMachineSystem : EntitySystem SubscribeLocalEvent(OnStasisUnstrapped); SubscribeLocalEvent(OnEmagged); - _configManager.OnValueChanged(SunriseCCVars.CopyMachineTemplatePool, _ => + _configManager.OnValueChanged(SunriseCCVars.DocumentTemplatePool, _ => { CacheAllDocuments(); RebuildAllPrinters(); @@ -75,16 +72,11 @@ public sealed class CopyMachineSystem : EntitySystem CacheAllDocuments(); } - private void OnRoundStart(RoundStartingEvent ev) - { - _roundStartTime = _timing.CurTime; - } - private void CacheAllDocuments() { _docCache.Clear(); - var pool = _configManager.GetCVar(SunriseCCVars.CopyMachineTemplatePool); + var pool = _configManager.GetCVar(SunriseCCVars.DocumentTemplatePool); if (!_proto.TryIndex(pool, out var poolProto)) return; @@ -119,7 +111,7 @@ public sealed class CopyMachineSystem : EntitySystem var isEmagged = HasComp(uid); - var pool = _configManager.GetCVar(SunriseCCVars.CopyMachineTemplatePool); + var pool = _configManager.GetCVar(SunriseCCVars.DocumentTemplatePool); if (!_proto.TryIndex(pool, out var poolProto)) return; @@ -254,22 +246,8 @@ public sealed class CopyMachineSystem : EntitySystem !_proto.TryIndex(templateId, out var templateProto)) return false; - var offsetHours = _configManager.GetCVar(SunriseCCVars.CopyMachineTimeOffsetHours); - var offsetYears = _configManager.GetCVar(SunriseCCVars.CopyMachineYearOffset); - - var date = DateTime.UtcNow - .AddHours(offsetHours) - .AddYears(offsetYears) - .ToString("dd.MM.yyyy"); - - var shift = _timing.CurTime - _roundStartTime; - var timeString = $"{shift:hh\\:mm} {date}"; - - var station = _stationSystem.GetOwningStation(uid); - var stationName = station is null ? string.Empty : Name(station.Value); - - content = content.Replace("{timeString}", timeString); - content = content.Replace("{stationName}", stationName); + // ЕДИНАЯ система форматирования документов + content = _docFmt.Format(content, uid); _paperSystem.SetContent((paper, paperComp), content); if (templateProto.Header != null) diff --git a/Content.Server/_Sunrise/Documents/DocumentFormatSystem.cs b/Content.Server/_Sunrise/Documents/DocumentFormatSystem.cs new file mode 100644 index 0000000000..e8436d4b3d --- /dev/null +++ b/Content.Server/_Sunrise/Documents/DocumentFormatSystem.cs @@ -0,0 +1,41 @@ +using Content.Server.GameTicking.Events; +using Content.Server.Station.Systems; +using Content.Shared._Sunrise.SunriseCCVars; +using Robust.Shared.Configuration; +using Robust.Shared.Timing; + +namespace Content.Server._Sunrise.Documents; + +public sealed class DocumentFormatSystem : EntitySystem +{ + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly StationSystem _station = default!; + + private TimeSpan _roundStart; + + public override void Initialize() + { + SubscribeLocalEvent(_ => _roundStart = _timing.CurTime); + } + + public string Format(string content, EntityUid context) + { + var offsH = _cfg.GetCVar(SunriseCCVars.DocumentTimeOffsetHours); + var offsY = _cfg.GetCVar(SunriseCCVars.DocumentYearOffset); + + var date = DateTime.UtcNow.AddHours(offsH).AddYears(offsY).ToString("dd.MM.yyyy"); + var shift = _timing.CurTime - _roundStart; + var timeString = $"{shift:hh\\:mm} {date}"; + + var st = _station.GetOwningStation(context); + var stationName = st is null ? string.Empty : Name(st.Value); + + var corp = _cfg.GetCVar(SunriseCCVars.DocumentCorporationName); + + return content + .Replace("{timeString}", timeString) + .Replace("{stationName}", stationName) + .Replace("{corpString}", corp); + } +} diff --git a/Content.Shared/_Sunrise/CopyMachine/DocTemplate.cs b/Content.Shared/_Sunrise/CopyMachine/DocTemplate.cs index 6020c64af8..b2da141fcf 100644 --- a/Content.Shared/_Sunrise/CopyMachine/DocTemplate.cs +++ b/Content.Shared/_Sunrise/CopyMachine/DocTemplate.cs @@ -1,25 +1,28 @@ using Robust.Shared.Prototypes; using Robust.Shared.Utility; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; namespace Content.Shared._Sunrise.CopyMachine; [Prototype] -public sealed partial class DocTemplatePrototype : IPrototype +public sealed partial class DocTemplatePrototype : IPrototype, IInheritingPrototype { - [IdDataField] - public string ID { get; private set; } = default!; + [IdDataField] public string ID { get; private set; } = default!; - [DataField] - public LocId Name; + [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] + public string[]? Parents { get; private set; } - [DataField(required: true)] - public ResPath Content; + [NeverPushInheritance] + [AbstractDataField] + public bool Abstract { get; private set; } - [DataField] - public SpriteSpecifier? Header; + [DataField] public LocId Name; - [DataField(required: true)] - public string Component { get; private set; } = default!; - [DataField] - public bool IsPublic { get; private set; } = true; + [DataField(required: true)] public ResPath Content; + + [DataField] public SpriteSpecifier? Header; + + [DataField(required: true)] public string Component { get; private set; } = default!; + + [DataField] public bool IsPublic { get; private set; } = true; } diff --git a/Content.Shared/_Sunrise/SunriseCCVars/SunriseCCVars.cs b/Content.Shared/_Sunrise/SunriseCCVars/SunriseCCVars.cs index 92ba239810..403e7ad82b 100644 --- a/Content.Shared/_Sunrise/SunriseCCVars/SunriseCCVars.cs +++ b/Content.Shared/_Sunrise/SunriseCCVars/SunriseCCVars.cs @@ -533,24 +533,30 @@ public sealed partial class SunriseCCVars : CVars public static readonly CVarDef TracesEnabled = CVarDef.Create("opt.traces_enabled", true, CVar.CLIENTONLY | CVar.ARCHIVE); - /// - /// Определяет, какие шаблоны будут доступны. (например, "sunrise" или "lust") - /// - public static readonly CVarDef CopyMachineTemplatePool = - CVarDef.Create("copy_machine.template_pool", "Sunrise", CVar.SERVERONLY | CVar.ARCHIVE); - - /// - /// Смещение автозаполнения времени (в часах) - /// - public static readonly CVarDef CopyMachineTimeOffsetHours = - CVarDef.Create("copy_machine.time_offset_hours", 3, CVar.SERVERONLY | CVar.ARCHIVE); - - /// - /// Смещение автозаполнения времени (в годах) - /// - public static readonly CVarDef CopyMachineYearOffset = - CVarDef.Create("copy_machine.year_offset", 1000, CVar.SERVERONLY | CVar.ARCHIVE); - public static readonly CVarDef HoldLookUp = CVarDef.Create("scope.hold_look_up", true, CVar.CLIENT | CVar.ARCHIVE); + + /// + /// Название корпорации для подстановки в документы + /// + public static readonly CVarDef DocumentCorporationName = + CVarDef.Create("doc.corp_name", "Nanotrasen", CVar.SERVER | CVar.ARCHIVE); + + /// + /// Смешение автозаполнения времени в часах + /// + public static readonly CVarDef DocumentTimeOffsetHours = + CVarDef.Create("doc.time_offset_hours", 3, CVar.SERVER | CVar.ARCHIVE); + + /// + /// Смешение автозаполнения времени в годах + /// + public static readonly CVarDef DocumentYearOffset = + CVarDef.Create("doc.year_offset", 1000, CVar.SERVER | CVar.ARCHIVE); + + /// + /// Пул шаблонов документов, используемых при создании документов на принтере + /// + public static readonly CVarDef DocumentTemplatePool = + CVarDef.Create("doc.template_pool", "Sunrise", CVar.SERVER | CVar.ARCHIVE); } diff --git a/Resources/Prototypes/_Sunrise/DocumentTemplates/templates.yml b/Resources/Prototypes/_Sunrise/DocumentTemplates/templates.yml index 4f709c0f1c..4d274666e1 100644 --- a/Resources/Prototypes/_Sunrise/DocumentTemplates/templates.yml +++ b/Resources/Prototypes/_Sunrise/DocumentTemplates/templates.yml @@ -14,7 +14,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: ApplicationAppointmentInterim @@ -23,7 +23,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: ApplicationEmployment @@ -32,7 +32,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: ApplicationEquipment @@ -41,7 +41,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: Certificate @@ -50,7 +50,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: CertificateAdvancedTraining @@ -59,7 +59,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: CertificateOffense @@ -68,7 +68,7 @@ component: Security header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_sec_investig - type: docTemplate id: ClosingIndictment @@ -77,7 +77,7 @@ component: Security header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_sec_investig - type: docTemplate id: ComplaintOffense @@ -86,7 +86,7 @@ component: Security header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_sec_investig - type: docTemplate id: ComplaintViolationLaborRules @@ -95,7 +95,7 @@ component: Justice header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_just - type: docTemplate id: ConditionReport @@ -104,7 +104,7 @@ component: Engineering header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_eng - type: docTemplate id: ConstructionPermit @@ -113,7 +113,7 @@ component: Engineering header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_eng - type: docTemplate id: DeathCertificate @@ -122,7 +122,7 @@ component: Medical header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_med - type: docTemplate id: DecisionToStartTrial @@ -131,7 +131,7 @@ component: Justice header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_just - type: docTemplate id: DisposalReport @@ -140,7 +140,7 @@ component: Supply header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_sup - type: docTemplate id: DivorceCertificate @@ -149,7 +149,7 @@ component: Service header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_serv - type: docTemplate id: EvacuationShuttleRequest @@ -158,7 +158,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: ExperimentReport @@ -167,7 +167,7 @@ component: Science header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_rnd - type: docTemplate id: InternalAffairsAgentsReport @@ -176,7 +176,7 @@ component: Justice header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_just - type: docTemplate id: Judgment @@ -185,7 +185,7 @@ component: Justice header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_just - type: docTemplate id: LetterResignation @@ -194,7 +194,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: MarriageCertificate @@ -203,7 +203,7 @@ component: Service header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_serv - type: docTemplate id: OrderDeprivationAccess @@ -212,7 +212,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: OrderDismissal @@ -221,7 +221,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: OrderEncouragement @@ -230,7 +230,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: OrderingSpecialEquipment @@ -239,7 +239,7 @@ component: Centcom header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_centcom - type: docTemplate id: OrderMedicalIntervention @@ -248,7 +248,7 @@ component: General header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_med - type: docTemplate id: OrderParolePrisoner @@ -257,7 +257,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: OrderPurchaseResourcesEquipment @@ -266,7 +266,7 @@ component: Supply header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_sup - type: docTemplate id: OrderPurchaseWeapons @@ -275,7 +275,7 @@ component: Supply header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_sup - type: docTemplate id: OrderRecognizingSentienceCreature @@ -284,7 +284,7 @@ component: Science header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_rnd - type: docTemplate id: PermissionDisposeBody @@ -293,7 +293,7 @@ component: Medical header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_med - type: docTemplate id: PermissionEquipment @@ -302,7 +302,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: PermissionToCarryWeapons @@ -311,7 +311,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: PermissionToExtendMarriage @@ -320,7 +320,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: PermissionToTravelInCaseOfThreat @@ -329,7 +329,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: PrescriptionDrugAuthorization @@ -338,7 +338,7 @@ component: Medical header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_med - type: docTemplate id: ProductManufacturingOrder @@ -356,7 +356,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: ReportEmployeePerformance @@ -365,7 +365,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: ReportOnEliminationOfViolations @@ -374,7 +374,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: ReportOnTheChaptersMeeting @@ -383,7 +383,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: ReportStation @@ -392,7 +392,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: ReportStudyObject @@ -401,7 +401,7 @@ component: Science header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_rnd - type: docTemplate id: RequestCallMembersCentralCommitteeDSO @@ -410,7 +410,7 @@ component: Centcom header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_centcom - type: docTemplate id: RequestChangeSalary @@ -428,7 +428,7 @@ component: Engineering header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_eng - type: docTemplate id: RequestDocuments @@ -437,7 +437,7 @@ component: Justice header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_just - type: docTemplate id: RequestEuthanasia @@ -446,7 +446,7 @@ component: Medical header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_med - type: docTemplate id: RequestForNonlistedEmployment @@ -455,7 +455,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: RequestForPromotion @@ -464,7 +464,7 @@ component: Command header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_cmd - type: docTemplate id: RequestModernization @@ -473,7 +473,7 @@ component: Science header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_rnd - type: docTemplate id: RequestRequestToEstablishThreatLevel @@ -482,7 +482,7 @@ component: Science header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_rnd - type: docTemplate id: SearchPermission @@ -491,7 +491,7 @@ component: Security header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_sec_investig - type: docTemplate id: Sentence @@ -500,7 +500,7 @@ component: Security header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_sec_investig - type: docTemplate id: ShuttleRegistrationRequest @@ -509,7 +509,7 @@ component: Security header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_sec_investig - type: docTemplate id: StatementHealth @@ -518,7 +518,7 @@ component: Medical header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: nanotrasen_form_header_med - type: docTemplate id: BusinessDeal @@ -528,7 +528,7 @@ isPublic: false header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: syndicate_form_header - type: docTemplate id: ErrorLoadingFormHeader @@ -538,7 +538,7 @@ isPublic: false header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: syndicate_form_header - type: docTemplate id: NoticeOfLiquidation @@ -548,7 +548,7 @@ isPublic: false header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: syndicate_form_header - type: docTemplate id: NoteBeginningMilitaryActions @@ -558,7 +558,7 @@ isPublic: false header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: syndicate_form_header - type: docTemplate id: ReportAccomplishmentGoals @@ -568,4 +568,4 @@ isPublic: false header: sprite: /Textures/_Sunrise/CopyMachine/paper_headers.rsi - state: nanotrasen_form_header + state: syndicate_form_header diff --git a/Resources/ServerInfo/Documents/_Sunrise/Centcom/OrderingSpecialEquipment.xml b/Resources/ServerInfo/Documents/_Sunrise/Centcom/OrderingSpecialEquipment.xml index aa36248673..ff2ca9c7ee 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Centcom/OrderingSpecialEquipment.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Centcom/OrderingSpecialEquipment.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ЦК-ЗАКССН[/bold] +[bold]Форма НТ/КОМ-ЦК-ЗАКССН[/bold] [head=3]Заказ специального снаряжения[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Centcom/RequestCallMembersCentralCommitteeDSO.xml b/Resources/ServerInfo/Documents/_Sunrise/Centcom/RequestCallMembersCentralCommitteeDSO.xml index 47085683a4..cdf41c7be0 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Centcom/RequestCallMembersCentralCommitteeDSO.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Centcom/RequestCallMembersCentralCommitteeDSO.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ЦК-ЗПЦК[/bold] +[bold]Форма НТ/КОМ-ЦК-ЗПЦК[/bold] [head=3]Запрос на вызов членов ЦК, ДСО [/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationAccess.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationAccess.xml index 090efa5133..17afd878d1 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationAccess.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationAccess.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ЗПД[/bold] +[bold]Форма {corpString}/КОМ-ЗПД[/bold] [head=3]Заявление на получение доступа[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationAppointmentInterim.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationAppointmentInterim.xml index 8a0f03b4b7..f364c729af 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationAppointmentInterim.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationAppointmentInterim.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ЗВРИО[/bold] +[bold]Форма {corpString}/КОМ-ЗВРИО[/bold] [head=3]Заявление о назначении ВрИО[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationEmployment.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationEmployment.xml index a839ae0fc5..72b8504c3c 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationEmployment.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationEmployment.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ЗТУ[/bold] +[bold]Форма {corpString}/КОМ-ЗТУ[/bold] [head=3]Заявление о трудоустройстве[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationEquipment.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationEquipment.xml index 643c2e915d..3130d235b1 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationEquipment.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/ApplicationEquipment.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ЗСН[/bold] +[bold]Форма {corpString}/КОМ-ЗСН[/bold] [head=3]Заявление на получение cнаряжения[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/Certificate.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/Certificate.xml index f0179a2678..98de9e3055 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/Certificate.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/Certificate.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ГР[/bold] +[bold]Форма {corpString}/КОМ-ГР[/bold] [head=2]Грамота[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/CertificateAdvancedTraining.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/CertificateAdvancedTraining.xml index d628f89270..8cda46383b 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/CertificateAdvancedTraining.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/CertificateAdvancedTraining.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-КВАЛ[/bold] +[bold]Форма {corpString}/КОМ-КВАЛ[/bold] [head=3]Свидетельство о повышении квалификации[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/EvacuationShuttleRequest.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/EvacuationShuttleRequest.xml index d58e16f635..eef5e8abcc 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/EvacuationShuttleRequest.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/EvacuationShuttleRequest.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ЦК-ЗЭВАК[/bold] +[bold]Форма {corpString}/КОМ-ЦК-ЗЭВАК[/bold] [head=3]Запрос эвакуационного шаттла[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/LetterResignation.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/LetterResignation.xml index cbdd63813d..9c647dec9c 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/LetterResignation.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/LetterResignation.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма СНД/КОМ-ЗУВАЛ[/bold] +[bold]Форма {corpString}/КОМ-ЗУВАЛ[/bold] [head=2]Заявление об увольнении[/head] [italic]Время от начала смены и дата: {timeString}[/italic] @@ -9,7 +9,7 @@ Должность составителя: Я, (ПОЛНОЕ ИМЯ), в текущей должности (ДОЛЖНОСТЬ), хочу уволится с отдела (название отдела) по причине (ПРИЧИНА). -Обязуюсь заплатить штраф, установленный условиями расторжения срочного/бессрочного контракта, по прибытию в распределительный хаб корпорации NanoTrasen. +Обязуюсь заплатить штраф, установленный условиями расторжения срочного/бессрочного контракта, по прибытию в распределительный хаб корпорации НаноТрейзен. [italic]Место для печатей[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/OrderDeprivationAccess.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/OrderDeprivationAccess.xml index cee390781a..1f01468bb3 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/OrderDeprivationAccess.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/OrderDeprivationAccess.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ПЛД[/bold] +[bold]Форма {corpString}/КОМ-ПЛД[/bold] [head=3]Приказ о лишении доступа[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/OrderDismissal.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/OrderDismissal.xml index c21ef02ff2..9636619dd0 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/OrderDismissal.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/OrderDismissal.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ПУВАЛ[/bold] +[bold]Форма {corpString}/КОМ-ПУВАЛ[/bold] [head=3]Приказ об увольнении[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/OrderEncouragement.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/OrderEncouragement.xml index 4ce92c5906..5ba4a35245 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/OrderEncouragement.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/OrderEncouragement.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ПЩРН[/bold] +[bold]Форма {corpString}/КОМ-ПЩРН[/bold] [head=2]Приказ о поощрении[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/OrderParolePrisoner.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/OrderParolePrisoner.xml index 565cccb7d7..033839178b 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/OrderParolePrisoner.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/OrderParolePrisoner.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ПУДО[/bold] +[bold]Форма {corpString}/КОМ-ПУДО[/bold] [head=3]Приказ об УДО заключенного[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionEquipment.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionEquipment.xml index 36d4307308..e5fa09deab 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionEquipment.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionEquipment.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОД-КОД[/bold] +[bold]Форма {corpString}/КОД-КОД[/bold] [head=3]Разрешение на использование снаряжения[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionToCarryWeapons.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionToCarryWeapons.xml index 2029041470..81b2ca75df 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionToCarryWeapons.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionToCarryWeapons.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-РСН[/bold] +[bold]Форма {corpString}/КОМ-РСН[/bold] [head=3]Разрешение на ношение оружия[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionToExtendMarriage.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionToExtendMarriage.xml index 1365b1a40f..60dd965f03 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionToExtendMarriage.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionToExtendMarriage.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-РСН[/bold] +[bold]Форма {corpString}/КОМ-РСН[/bold] [head=3]Разрешение на расширение брака[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionToTravelInCaseOfThreat.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionToTravelInCaseOfThreat.xml index e7c34e475d..13a30bfee1 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionToTravelInCaseOfThreat.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/PermissionToTravelInCaseOfThreat.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-РППУ[/bold] +[bold]Форма {corpString}/КОМ-РППУ[/bold] [head=3]Разрешение на передвижение при угрозе[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/ReportDepartment.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/ReportDepartment.xml index 59a37e7306..59838d3b34 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/ReportDepartment.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/ReportDepartment.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ОРО[/bold] +[bold]Форма {corpString}/КОМ-ОРО[/bold] [head=2]Отчет о работе отдела[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/ReportEmployeePerformance.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/ReportEmployeePerformance.xml index 2e3869fc39..1c89cf56ec 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/ReportEmployeePerformance.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/ReportEmployeePerformance.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ОРО[/bold] +[bold]Форма {corpString}/КОМ-ОРО[/bold] [head=2]Отчет о работе сотрудника[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/ReportOnEliminationOfViolations.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/ReportOnEliminationOfViolations.xml index c6aa3a3734..58e927a1fd 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/ReportOnEliminationOfViolations.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/ReportOnEliminationOfViolations.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ОУСН[/bold] +[bold]Форма {corpString}/КОМ-ОУСН[/bold] [head=2]Отчет об устранении нарушений[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/ReportOnTheChaptersMeeting.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/ReportOnTheChaptersMeeting.xml index f042f570d0..dcf18f3a14 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/ReportOnTheChaptersMeeting.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/ReportOnTheChaptersMeeting.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ОУСН[/bold] +[bold]Форма {corpString}/КОМ-ОУСН[/bold] [head=2]Отчет о Собрании Глав[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/ReportStation.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/ReportStation.xml index a96a221868..1083652ad5 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/ReportStation.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/ReportStation.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ЦК-СИТ[/bold] +[bold]Форма {corpString}/КОМ-ЦК-СИТ[/bold] [head=3]Отчет о ситуации на станции[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/RequestForNonlistedEmployment.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/RequestForNonlistedEmployment.xml index c90c92ce79..8410e912f3 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/RequestForNonlistedEmployment.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/RequestForNonlistedEmployment.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ЗВТУ[/bold] +[bold]Форма {corpString}/КОМ-ЗВТУ[/bold] [head=3]Запрос на внеперечневое трудоустройство[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Command/RequestForPromotion.xml b/Resources/ServerInfo/Documents/_Sunrise/Command/RequestForPromotion.xml index db7da42449..8d09482729 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Command/RequestForPromotion.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Command/RequestForPromotion.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ЗПОВ[/bold] +[bold]Форма {corpString}/КОМ-ЗПОВ[/bold] [head=2]Запрос на повышение[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Engineering/ConditionReport.xml b/Resources/ServerInfo/Documents/_Sunrise/Engineering/ConditionReport.xml index c4cb18bf3b..e5cb127d97 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Engineering/ConditionReport.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Engineering/ConditionReport.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/ИНЖ-ОТС[/bold] +[bold]Форма {corpString}/ИНЖ-ОТС[/bold] [head=3]Отчет о техническом состоянии[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Engineering/ConstructionPermit.xml b/Resources/ServerInfo/Documents/_Sunrise/Engineering/ConstructionPermit.xml index 0e4b3a2daa..5bde091692 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Engineering/ConstructionPermit.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Engineering/ConstructionPermit.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/ИНЖ-РС[/bold] +[bold]Форма {corpString}/ИНЖ-РС[/bold] [head=3]Разрешение на строительство[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Engineering/RequestConstructionWork.xml b/Resources/ServerInfo/Documents/_Sunrise/Engineering/RequestConstructionWork.xml index 20d06cf58a..1d3d99c5dc 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Engineering/RequestConstructionWork.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Engineering/RequestConstructionWork.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/ИНЖ-ЗСР[/bold] +[bold]Форма {corpString}/ИНЖ-ЗСР[/bold] [head=3]Запрос на проведение строительных работ[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/General/Appeal.xml b/Resources/ServerInfo/Documents/_Sunrise/General/Appeal.xml index f3fd532d7b..bfacbeee26 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/General/Appeal.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/General/Appeal.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОД-КОД[/bold] +[bold]Форма {corpString}/КОД-КОД[/bold] [head=2]Обращение[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/General/OrderMedicalIntervention.xml b/Resources/ServerInfo/Documents/_Sunrise/General/OrderMedicalIntervention.xml index 609d44d1bf..a8d8d20dae 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/General/OrderMedicalIntervention.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/General/OrderMedicalIntervention.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/МЕД[/bold] +[bold]Форма {corpString}/МЕД[/bold] [head=3]Приказ о медицинском вмешательстве[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/General/ProductManufacturingOrder.xml b/Resources/ServerInfo/Documents/_Sunrise/General/ProductManufacturingOrder.xml index c48bb8da3d..efcfe4475c 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/General/ProductManufacturingOrder.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/General/ProductManufacturingOrder.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОД-КОД[/bold] +[bold]Форма {corpString}/КОД-КОД[/bold] [head=3]Заказ на производство продукта[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/General/RequestChangeSalary.xml b/Resources/ServerInfo/Documents/_Sunrise/General/RequestChangeSalary.xml index 4ab0f1e86f..b361ea9c97 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/General/RequestChangeSalary.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/General/RequestChangeSalary.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ЦК-ЗЗП[/bold] +[bold]Форма {corpString}/КОМ-ЦК-ЗЗП[/bold] [head=3]Запрос на изменение заработной платы[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Justice/ComplaintViolationLaborRules.xml b/Resources/ServerInfo/Documents/_Sunrise/Justice/ComplaintViolationLaborRules.xml index 5d7a03364b..eed774a3d3 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Justice/ComplaintViolationLaborRules.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Justice/ComplaintViolationLaborRules.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/ЮДВН-ЖБТП[/bold] +[bold]Форма {corpString}/ЮДВН-ЖБТП[/bold] [head=3]Жалоба на нарушение трудового порядка[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Justice/DecisionToStartTrial.xml b/Resources/ServerInfo/Documents/_Sunrise/Justice/DecisionToStartTrial.xml index 105928ed15..acc5950d4b 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Justice/DecisionToStartTrial.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Justice/DecisionToStartTrial.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/СУД-РНСП[/bold] +[bold]Форма {corpString}/СУД-РНСП[/bold] [head=3]Решение о начале судебного процесса[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Justice/InternalAffairsAgentsReport.xml b/Resources/ServerInfo/Documents/_Sunrise/Justice/InternalAffairsAgentsReport.xml index 7e8937e51f..24f36ccac2 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Justice/InternalAffairsAgentsReport.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Justice/InternalAffairsAgentsReport.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОД-КОД[/bold] +[bold]Форма {corpString}/КОД-КОД[/bold] [head=2]Отчет о внутреннем расследовании[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Justice/Judgment.xml b/Resources/ServerInfo/Documents/_Sunrise/Justice/Judgment.xml index 948769d774..604f87d560 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Justice/Judgment.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Justice/Judgment.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/СУД-ЗКЛ[/bold] +[bold]Форма {corpString}/СУД-ЗКЛ[/bold] [head=2]Заключение суда[/head] [italic]Время от начала смены и дата: {timeString}[/italic] @@ -8,9 +8,9 @@ Представитель судебной власти: (ПОЛНОЕ ИМЯ) В должности: Магистрат -Я, (ПОЛНОЕ ИМЯ), как представитель Судебной системы корпорации NanoTrasen, именем корпорации NanoTrasen заключаю, что +Я, (ПОЛНОЕ ИМЯ), как представитель Судебной системы корпорации НаноТрейзен, именем корпорации НаноТрейзен заключаю, что -(ПОЛНОЕ ИМЯ) нарушил XXX статью корпоративного закона корпорации NanoTrasen. +(ПОЛНОЕ ИМЯ) нарушил XXX статью корпоративного закона корпорации НаноТрейзен. [bold]Краткое описание дела:[/bold] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Justice/RequestDocuments.xml b/Resources/ServerInfo/Documents/_Sunrise/Justice/RequestDocuments.xml index 1b05d33bb0..50f050829d 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Justice/RequestDocuments.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Justice/RequestDocuments.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/ИНЖ-ЗСР[/bold] +[bold]Форма {corpString}/ИНЖ-ЗСР[/bold] [head=3]Запрос на предоставление документов[/head] [italic]Время от начала смены и дата: {timeString}[/italic] @@ -10,7 +10,7 @@ Я, (ПОЛНОЕ ИМЯ), в должности Агента Внутренних Дел, прошу предоставить для проверки соответствия Корпоративному Закону копию/оригинал документов: -[italic]Предоставление документации по запросу представителя Департамента внутреннего надзора корпорации NanoTrasen является непосредственной обязанностью любого сотрудника на объекте.[/italic] +[italic]Предоставление документации по запросу представителя Департамента внутреннего надзора корпорации НаноТрейзен является непосредственной обязанностью любого сотрудника на объекте.[/italic] [italic]Место для печатей[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Medical/DeathCertificate.xml b/Resources/ServerInfo/Documents/_Sunrise/Medical/DeathCertificate.xml index a94a8c1446..85f3c3975b 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Medical/DeathCertificate.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Medical/DeathCertificate.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/МЕД-ССМ[/bold] +[bold]Форма {corpString}/МЕД-ССМ[/bold] [head=2]Свидетельство о смерти[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Medical/PermissionDisposeBody.xml b/Resources/ServerInfo/Documents/_Sunrise/Medical/PermissionDisposeBody.xml index 3c74de12b2..bf6941664e 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Medical/PermissionDisposeBody.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Medical/PermissionDisposeBody.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/МЕД-РУТ[/bold] +[bold]Форма {corpString}/МЕД-РУТ[/bold] [head=2]Разрешение на утилизацию тела[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Medical/PrescriptionDrugAuthorization.xml b/Resources/ServerInfo/Documents/_Sunrise/Medical/PrescriptionDrugAuthorization.xml index 9d04fdce00..b7c03c6f38 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Medical/PrescriptionDrugAuthorization.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Medical/PrescriptionDrugAuthorization.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/МЕД-РППУ[/bold] +[bold]Форма {corpString}/МЕД-РППУ[/bold] [head=3]Назначение рецептурного препарата[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Medical/RequestEuthanasia.xml b/Resources/ServerInfo/Documents/_Sunrise/Medical/RequestEuthanasia.xml index 96d7d14758..270a431c93 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Medical/RequestEuthanasia.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Medical/RequestEuthanasia.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/МЕД-ЗЭВТ[/bold] +[bold]Форма {corpString}/МЕД-ЗЭВТ[/bold] [head=3]Запрос на проведение эвтаназии[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Medical/StatementHealth.xml b/Resources/ServerInfo/Documents/_Sunrise/Medical/StatementHealth.xml index 5608fe28e2..dce67b6dd7 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Medical/StatementHealth.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Medical/StatementHealth.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/МЕД-ЗСЗ[/bold] +[bold]Форма {corpString}/МЕД-ЗСЗ[/bold] [head=2]Заключение о состоянии здоровья[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Science/ExperimentReport.xml b/Resources/ServerInfo/Documents/_Sunrise/Science/ExperimentReport.xml index 0c2e1c5096..ed7fce7654 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Science/ExperimentReport.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Science/ExperimentReport.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/НИО-ОЭКС[/bold] +[bold]Форма {corpString}/НИО-ОЭКС[/bold] [head=2]Отчет об эксперименте[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Science/OrderRecognizingSentienceCreature.xml b/Resources/ServerInfo/Documents/_Sunrise/Science/OrderRecognizingSentienceCreature.xml index e735cfabee..2b9d54607d 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Science/OrderRecognizingSentienceCreature.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Science/OrderRecognizingSentienceCreature.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/НИО-ПРРЗМ[/bold] +[bold]Форма {corpString}/НИО-ПРРЗМ[/bold] [head=3]Приказ о признании разумности существа[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Science/ReportStudyObject.xml b/Resources/ServerInfo/Documents/_Sunrise/Science/ReportStudyObject.xml index c1b1a54427..5bdb6b90f1 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Science/ReportStudyObject.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Science/ReportStudyObject.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/НИО-СИТ[/bold] +[bold]Форма {corpString}/НИО-СИТ[/bold] [head=3]Отчет об исследовании объекта[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Science/RequestModernization.xml b/Resources/ServerInfo/Documents/_Sunrise/Science/RequestModernization.xml index 8f1a6d3fb3..6615833858 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Science/RequestModernization.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Science/RequestModernization.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/НИО-ЗМОДЕР[/bold] +[bold]Форма {corpString}/НИО-ЗМОДЕР[/bold] [head=3]Запрос на проведение модернизации[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Science/RequestRequestToEstablishThreatLevel.xml b/Resources/ServerInfo/Documents/_Sunrise/Science/RequestRequestToEstablishThreatLevel.xml index 26d6275ab6..8f3315e866 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Science/RequestRequestToEstablishThreatLevel.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Science/RequestRequestToEstablishThreatLevel.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/НИО-ЗМОДЕР[/bold] +[bold]Форма {corpString}/НИО-ЗМОДЕР[/bold] [head=3]Запрос на установление уровня угрозы[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Security/CertificateOffense.xml b/Resources/ServerInfo/Documents/_Sunrise/Security/CertificateOffense.xml index 1a600026f5..4690334cce 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Security/CertificateOffense.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Security/CertificateOffense.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/СБ-СВП[/bold] +[bold]Форма {corpString}/СБ-СВП[/bold] [head=3]Свидетельство о правонарушении[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Security/ClosingIndictment.xml b/Resources/ServerInfo/Documents/_Sunrise/Security/ClosingIndictment.xml index 50a90ac4a6..c5ee9ace31 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Security/ClosingIndictment.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Security/ClosingIndictment.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/СБ-ОЗКЛ[/bold] +[bold]Форма {corpString}/СБ-ОЗКЛ[/bold] [head=2]Обвинительное заключение[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Security/ComplaintOffense.xml b/Resources/ServerInfo/Documents/_Sunrise/Security/ComplaintOffense.xml index 5ebad6e8ce..8d99c22a21 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Security/ComplaintOffense.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Security/ComplaintOffense.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/СБ-ЖБПВН[/bold] +[bold]Форма {corpString}/СБ-ЖБПВН[/bold] [head=2]Жалоба на правонарушение[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Security/SearchPermission.xml b/Resources/ServerInfo/Documents/_Sunrise/Security/SearchPermission.xml index f1221dc2f8..2f350eb0f5 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Security/SearchPermission.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Security/SearchPermission.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/СБ-РНО[/bold] +[bold]Форма {corpString}/СБ-РНО[/bold] [head=2]Разрешение на обыск[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Security/Sentence.xml b/Resources/ServerInfo/Documents/_Sunrise/Security/Sentence.xml index 4921baf9be..10bbf4c2ac 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Security/Sentence.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Security/Sentence.xml @@ -2,7 +2,7 @@ [tex path="/Textures/_Sunrise/Paper/nanotrasen_form_header_sec.png" scale=1] [bold]Станция: {stationName}[/bold] -[bold]Форма NT/СБ-ПРИГ[/bold] +[bold]Форма {corpString}/СБ-ПРИГ[/bold] [head=2]Приговор[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Security/ShuttleRegistrationRequest.xml b/Resources/ServerInfo/Documents/_Sunrise/Security/ShuttleRegistrationRequest.xml index 9a34fae35a..e371fedce1 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Security/ShuttleRegistrationRequest.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Security/ShuttleRegistrationRequest.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/СБ-ЗРЕГШ[/bold] +[bold]Форма {corpString}/СБ-ЗРЕГШ[/bold] [head=3]Запрос на регистрацию шаттла[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Service/DivorceCertificate.xml b/Resources/ServerInfo/Documents/_Sunrise/Service/DivorceCertificate.xml index f0b9aa8511..1e99521196 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Service/DivorceCertificate.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Service/DivorceCertificate.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/СРВ-CРБ[/bold] +[bold]Форма {corpString}/СРВ-CРБ[/bold] [head=3]Свидетельство о расторжении брака[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Service/MarriageCertificate.xml b/Resources/ServerInfo/Documents/_Sunrise/Service/MarriageCertificate.xml index 711e98dbe3..f87ca5af84 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Service/MarriageCertificate.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Service/MarriageCertificate.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма СНД/СРВ-СЗБ[/bold] +[bold]Форма {corpString}/СРВ-СЗБ[/bold] [head=2]Свидетельство о заключении брака[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Supply/DisposalReport.xml b/Resources/ServerInfo/Documents/_Sunrise/Supply/DisposalReport.xml index a7d712229d..63e8620be0 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Supply/DisposalReport.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Supply/DisposalReport.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОД-КОД[/bold] +[bold]Форма {corpString}/КОД-КОД[/bold] [head=2]Отчет об утилизации[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Supply/OrderPurchaseResourcesEquipment.xml b/Resources/ServerInfo/Documents/_Sunrise/Supply/OrderPurchaseResourcesEquipment.xml index 172b9078cb..99c4c962c2 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Supply/OrderPurchaseResourcesEquipment.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Supply/OrderPurchaseResourcesEquipment.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ЗАК[/bold] +[bold]Форма {corpString}/КОМ-ЗАК[/bold] [head=3]Приказ на закупку ресурсов, оборудования[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Supply/OrderPurchaseWeapons.xml b/Resources/ServerInfo/Documents/_Sunrise/Supply/OrderPurchaseWeapons.xml index 5ed27ae876..2cfcdef9e9 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Supply/OrderPurchaseWeapons.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Supply/OrderPurchaseWeapons.xml @@ -1,6 +1,6 @@ [bold]Станция: {stationName}[/bold] -[bold]Форма NT/КОМ-ЗАК[/bold] +[bold]Форма {corpString}/КОМ-ЗАК[/bold] [head=3]Заказ на закупку вооружения[/head] [italic]Время от начала смены и дата: {timeString}[/italic] diff --git a/Resources/ServerInfo/Documents/_Sunrise/Syndicate/NoteBeginningMilitaryActions.xml b/Resources/ServerInfo/Documents/_Sunrise/Syndicate/NoteBeginningMilitaryActions.xml index 63bdf6fb85..bfb84bcd33 100644 --- a/Resources/ServerInfo/Documents/_Sunrise/Syndicate/NoteBeginningMilitaryActions.xml +++ b/Resources/ServerInfo/Documents/_Sunrise/Syndicate/NoteBeginningMilitaryActions.xml @@ -7,7 +7,7 @@ Позывной Агента: -Неуважаемые корпоративные крысы NanoTrasen! Синдикат официально объявляет о начале военных действий с вами, а также о начале операции по вашему истреблению. +Неуважаемые корпоративные крысы НаноТрейзен! Синдикат официально объявляет о начале военных действий с вами, а также о начале операции по вашему истреблению. Причина предъявления ноты: [italic]Место для печатей[/italic] From 08f38784b5b1f85466cec91581784b762a900035 Mon Sep 17 00:00:00 2001 From: Sunrise-Bot Date: Mon, 25 Aug 2025 03:20:50 +0300 Subject: [PATCH 102/108] Automatic changelog update --- Resources/Changelog/ChangelogSunrise.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Resources/Changelog/ChangelogSunrise.yml b/Resources/Changelog/ChangelogSunrise.yml index 6d0e788bf3..fe9192814d 100644 --- a/Resources/Changelog/ChangelogSunrise.yml +++ b/Resources/Changelog/ChangelogSunrise.yml @@ -21356,3 +21356,13 @@ id: 1395 time: '2025-08-24T23:47:27.0000000+00:00' url: https://github.com/space-sunrise/sunrise-station/pull/3000 +- author: daniil7383 + changes: + - message: "\u0443\u0441\u0442\u0440\u0430\u043D\u0435\u043D\u0438\u0435 \u043C\u0435\ + \u043B\u043A\u0438\u0445 \u043F\u0440\u043E\u0431\u043B\u0435\u043C \u0443 \"\ + \u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u043B\u044C\u043D\u043E\u0433\ + \u043E \u0430\u043F\u043F\u0430\u0440\u0430\u0442\u0430\"" + type: Fix + id: 1396 + time: '2025-08-25T00:19:04.0000000+00:00' + url: https://github.com/space-sunrise/sunrise-station/pull/2900 From 4af5f1277ebfb545e9437f4d4b492550498ddc8e Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 Aug 2025 03:44:04 +0300 Subject: [PATCH 103/108] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BC=D0=B5=D1=81=D1=82=D0=BE=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B8=D0=B3=D1=80?= =?UTF-8?q?=D0=BE=D0=BA=D0=B0=20=D0=B2=20=D0=9D=D0=B0=D0=B2=D0=B8=D0=B3?= =?UTF-8?q?=D0=B0=D1=82=D0=BE=D1=80=D0=B5=20=D0=9A=D0=9F=D0=9A=20(#3017)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: VigersRay <60344369+VigersRay@users.noreply.github.com> Co-authored-by: Vigers Ray --- .../Cartridges/NavigatorUiFragment.xaml.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Content.Client/CartridgeLoader/Cartridges/NavigatorUiFragment.xaml.cs b/Content.Client/CartridgeLoader/Cartridges/NavigatorUiFragment.xaml.cs index 58ca071a45..65be869e0c 100644 --- a/Content.Client/CartridgeLoader/Cartridges/NavigatorUiFragment.xaml.cs +++ b/Content.Client/CartridgeLoader/Cartridges/NavigatorUiFragment.xaml.cs @@ -3,21 +3,29 @@ using Robust.Client.AutoGenerated; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; +using Robust.Shared.Map; +using Robust.Shared.Timing; +using System.Numerics; namespace Content.Client.CartridgeLoader.Cartridges; [GenerateTypedNameReferences] public sealed partial class NavigatorUiFragment : BoxContainer { + [Dependency] private readonly IEntityManager _entManager = default!; + public NavMapControl? NavMap; + private EntityUid? _owner; public NavigatorUiFragment() { RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); } public void Setup(EntityUid? owner) { + _owner = owner; NavMap = new NavMapControl(); NavMap.Owner = owner; NavMap.HorizontalExpand = true; @@ -34,5 +42,33 @@ public sealed partial class NavigatorUiFragment : BoxContainer { NavMap.MapUid = mapUid; } + + // Track owner position on the map + UpdateOwnerPosition(); + } + + private void UpdateOwnerPosition() + { + if (NavMap == null || _owner == null || !_entManager.EntityExists(_owner.Value)) + return; + + var transformSystem = _entManager.System(); + + var ownerCoords = transformSystem.GetMapCoordinates(_owner.Value); + var ownerEntityCoords = new EntityCoordinates(_owner.Value, Vector2.Zero); + + // Clear previous owner tracking + NavMap.TrackedCoordinates.Clear(); + + // Add owner position as a tracked coordinate + NavMap.TrackedCoordinates[ownerEntityCoords] = (true, Color.Red); + } + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + + // Periodically update owner position + UpdateOwnerPosition(); } } From 1df1298f45470b30014ea5b5b42dd5cfda7be151 Mon Sep 17 00:00:00 2001 From: Sunrise-Bot Date: Mon, 25 Aug 2025 03:45:50 +0300 Subject: [PATCH 104/108] Automatic changelog update --- Resources/Changelog/ChangelogSunrise.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Resources/Changelog/ChangelogSunrise.yml b/Resources/Changelog/ChangelogSunrise.yml index fe9192814d..d6311b2a01 100644 --- a/Resources/Changelog/ChangelogSunrise.yml +++ b/Resources/Changelog/ChangelogSunrise.yml @@ -21366,3 +21366,16 @@ id: 1396 time: '2025-08-25T00:19:04.0000000+00:00' url: https://github.com/space-sunrise/sunrise-station/pull/2900 +- author: Copilot AI + changes: + - message: "\u041D\u0430\u0432\u0438\u0433\u0430\u0442\u043E\u0440 \u0432 \u041A\ + \u041F\u041A \u0442\u0435\u043F\u0435\u0440\u044C \u043F\u043E\u043A\u0430\u0437\ + \u044B\u0432\u0430\u0435\u0442 \u0442\u0435\u043A\u0443\u0449\u0435\u0435 \u043C\ + \u0435\u0441\u0442\u043E\u043F\u043E\u043B\u043E\u0436\u0435\u043D\u0438\u0435\ + \ \u0438\u0433\u0440\u043E\u043A\u0430 \u043A\u0440\u0430\u0441\u043D\u043E\u0439\ + \ \u0442\u043E\u0447\u043A\u043E\u0439 \u043D\u0430 \u043A\u0430\u0440\u0442\ + \u0435 \u0441\u0442\u0430\u043D\u0446\u0438\u0438." + type: Add + id: 1397 + time: '2025-08-25T00:44:04.0000000+00:00' + url: https://github.com/space-sunrise/sunrise-station/pull/3017 From 923845021b43e03b64a4cab16b11bc73470f7863 Mon Sep 17 00:00:00 2001 From: Vigers Ray Date: Mon, 25 Aug 2025 04:03:10 +0300 Subject: [PATCH 105/108] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=20=D0=B0=D0=BF?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=B8=D0=BC=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Content.Server/Weapons/Ranged/Systems/GunSystem.cs | 1 + Content.Server/_Sunrise/AssaultOps/Icarus/IcarusBeamSystem.cs | 1 + .../_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.Rune.cs | 1 + Content.Server/_Sunrise/Boss/Systems/HellSpawnArenaSystem.cs | 1 + 4 files changed, 4 insertions(+) diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index 9dab5150aa..29363e6252 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -52,6 +52,7 @@ using Content.Shared.Pinpointer; using Robust.Server.GameObjects; using System.Collections.Generic; using Content.Server.PowerCell; +using Content.Shared.Atmos.Components; using Content.Shared.Body.Components; using Content.Shared.Cargo; diff --git a/Content.Server/_Sunrise/AssaultOps/Icarus/IcarusBeamSystem.cs b/Content.Server/_Sunrise/AssaultOps/Icarus/IcarusBeamSystem.cs index c15fc8ae61..6c9f4851c9 100644 --- a/Content.Server/_Sunrise/AssaultOps/Icarus/IcarusBeamSystem.cs +++ b/Content.Server/_Sunrise/AssaultOps/Icarus/IcarusBeamSystem.cs @@ -1,6 +1,7 @@ using System.Numerics; using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; +using Content.Shared.Atmos.Components; using Content.Shared.Ghost; using Robust.Server.GameObjects; using Robust.Shared.Map; diff --git a/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.Rune.cs b/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.Rune.cs index 6e45cf6a66..b85682605d 100644 --- a/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.Rune.cs +++ b/Content.Server/_Sunrise/BloodCult/Runes/Systems/BloodCultSystem.Rune.cs @@ -16,6 +16,7 @@ using Content.Shared._Sunrise.BloodCult.Components; using Content.Shared._Sunrise.BloodCult.Items; using Content.Shared._Sunrise.BloodCult.Runes; using Content.Shared._Sunrise.BloodCult.UI; +using Content.Shared.Atmos.Components; using Content.Shared.Body.Components; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Coordinates; diff --git a/Content.Server/_Sunrise/Boss/Systems/HellSpawnArenaSystem.cs b/Content.Server/_Sunrise/Boss/Systems/HellSpawnArenaSystem.cs index 19b54d51c0..9e177e3ee8 100644 --- a/Content.Server/_Sunrise/Boss/Systems/HellSpawnArenaSystem.cs +++ b/Content.Server/_Sunrise/Boss/Systems/HellSpawnArenaSystem.cs @@ -11,6 +11,7 @@ using Content.Server.Shuttles.Systems; using Content.Server.Sunrise.Paws; using Content.Shared._Sunrise.Boss.Components; using Content.Shared._Sunrise.Boss.Systems; +using Content.Shared.Atmos.Components; using Content.Shared.Body.Events; using Content.Shared.Construction; using Content.Shared.Damage.Components; From 48d11d810438f7d302490af2fbcd09ba2b19a0cb Mon Sep 17 00:00:00 2001 From: Vigers Ray Date: Mon, 25 Aug 2025 04:35:06 +0300 Subject: [PATCH 106/108] =?UTF-8?q?=D0=BF=D1=83-=D0=BF=D1=83-=D0=BF=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml index 286d4ab313..9e671de586 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml @@ -174,7 +174,7 @@ containers: ballistic-ammo: !type:Container - type: ProjectileBatteryAmmoProvider - proto: CartridgePistol + proto: CartridgePistolSP fireCost: 100 - type: Battery maxCharge: 3000 From 0e60ae1247b82e70b93ddac06a1a3fdeea6eb35a Mon Sep 17 00:00:00 2001 From: Gpxgji Date: Mon, 25 Aug 2025 04:41:01 +0300 Subject: [PATCH 107/108] =?UTF-8?q?=D0=9F=D0=BB=D1=8E=D1=88=D0=BA=D0=B8=20?= =?UTF-8?q?(#2916)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entities/objects/fun/plushie_boxes.ftl | 4 + .../_sunrise/entities/objects/fun/toys.ftl | 8 +- .../entities/objects/fun/plushie_boxes.ftl | 4 + .../_sunrise/entities/objects/fun/toys.ftl | 6 + .../Catalog/sponsor_uplink_catalog.yml | 902 +----------------- .../Entities/Objects/Fun/plushie_boxes.yml | 192 ++++ .../_Sunrise/Entities/Objects/Fun/toys.yml | 199 ++++ .../plushie_kalium.rsi/meta.json | 4 +- .../plushie_kalium_double.rsi/meta.json | 4 +- .../kalium-equipped-NECK.png | Bin 0 -> 1082 bytes .../kalium-inhand-left.png | Bin 0 -> 877 bytes .../kalium-inhand-right.png | Bin 0 -> 881 bytes .../plushie_kalium_juggernaut.rsi/meta.json | 45 + .../plushie-kalium.png | Bin 0 -> 1407 bytes .../potasium_jager_shaded.png | Bin 0 -> 1408 bytes .../potasium_jager_unshaded.png | Bin 0 -> 481 bytes .../kalium-equipped-NECK.png | Bin 0 -> 690 bytes .../kalium-inhand-left.png | Bin 0 -> 963 bytes .../kalium-inhand-right.png | Bin 0 -> 973 bytes .../meta.json | 45 + .../plushie-kalium.png | Bin 0 -> 1466 bytes .../potasium_nuclearcom_unshaded.png | Bin 0 -> 1901 bytes .../kalium-equipped-NECK.png | Bin 0 -> 617 bytes .../kalium-inhand-left.png | Bin 0 -> 837 bytes .../kalium-inhand-right.png | Bin 0 -> 850 bytes .../plushie_kalium_operative.rsi/meta.json | 44 + .../plushie-kalium.png | Bin 0 -> 1554 bytes .../potasium_nuclearop_shaded.png | Bin 0 -> 1975 bytes .../potasium_nuclearop_unshaded.png | Bin 0 -> 146 bytes .../plushie_sindy_box.rsi/fig_box.png | Bin 0 -> 499 bytes .../plushie_sindy_box.rsi/meta.json | 14 + 31 files changed, 573 insertions(+), 898 deletions(-) create mode 100644 Resources/Locale/en-US/_prototypes/_sunrise/entities/objects/fun/plushie_boxes.ftl create mode 100644 Resources/Locale/ru-RU/_prototypes/_sunrise/entities/objects/fun/plushie_boxes.ftl create mode 100644 Resources/Prototypes/_Sunrise/Entities/Objects/Fun/plushie_boxes.yml create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/kalium-equipped-NECK.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/kalium-inhand-left.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/kalium-inhand-right.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/meta.json create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/plushie-kalium.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/potasium_jager_shaded.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/potasium_jager_unshaded.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/kalium-equipped-NECK.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/kalium-inhand-left.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/kalium-inhand-right.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/meta.json create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/plushie-kalium.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/potasium_nuclearcom_unshaded.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/kalium-equipped-NECK.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/kalium-inhand-left.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/kalium-inhand-right.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/meta.json create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/plushie-kalium.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/potasium_nuclearop_shaded.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/potasium_nuclearop_unshaded.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_sindy_box.rsi/fig_box.png create mode 100644 Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_sindy_box.rsi/meta.json diff --git a/Resources/Locale/en-US/_prototypes/_sunrise/entities/objects/fun/plushie_boxes.ftl b/Resources/Locale/en-US/_prototypes/_sunrise/entities/objects/fun/plushie_boxes.ftl new file mode 100644 index 0000000000..2aae67fe7c --- /dev/null +++ b/Resources/Locale/en-US/_prototypes/_sunrise/entities/objects/fun/plushie_boxes.ftl @@ -0,0 +1,4 @@ +ent-PlushieVulpSyndiPresent = Sindy present + .desc = Sindy box with mistery plushie. +ent-PlushiePresent = Plushie present + .desc = Box with mistery plushie. diff --git a/Resources/Locale/en-US/_prototypes/_sunrise/entities/objects/fun/toys.ftl b/Resources/Locale/en-US/_prototypes/_sunrise/entities/objects/fun/toys.ftl index dbe83de89b..8061366584 100644 --- a/Resources/Locale/en-US/_prototypes/_sunrise/entities/objects/fun/toys.ftl +++ b/Resources/Locale/en-US/_prototypes/_sunrise/entities/objects/fun/toys.ftl @@ -170,5 +170,11 @@ ent-PlushieSansaMontanelli = Sansa Montanelli .desc = { ent-BasePlushie.desc } ent-PlushieKalium = Kalium Fon Dez .desc = { ent-BasePlushie.desc } -ent-PlushieKaliumDouble = Kalium Fon Dez +ent-PlushieKaliumDouble = Kalium Double + .desc = { ent-BasePlushie.desc } +ent-PlushieKaliumJuggernaut = Kalium Juggernaut + .desc = { ent-BasePlushie.desc } +ent-PlushieKaliumOperative = Kalium Fon Dez Operative + .desc = { ent-BasePlushie.desc } +ent-PlushieKaliumNukieCommander = Kalium Commander .desc = { ent-BasePlushie.desc } diff --git a/Resources/Locale/ru-RU/_prototypes/_sunrise/entities/objects/fun/plushie_boxes.ftl b/Resources/Locale/ru-RU/_prototypes/_sunrise/entities/objects/fun/plushie_boxes.ftl new file mode 100644 index 0000000000..4985505be9 --- /dev/null +++ b/Resources/Locale/ru-RU/_prototypes/_sunrise/entities/objects/fun/plushie_boxes.ftl @@ -0,0 +1,4 @@ +ent-PlushieVulpSyndiPresent = Коробка игрушек синдиката + .desc = Специальная одноразовая коробка для игрушек. Производилась в качестве рекламной продукции Синдиленда и рассылалась некоторым агентам. +ent-PlushiePresent = Коробка плюшевых игрушек + .desc = Коробка, содержащая загадочную плюшку. На боковой стороне коробки изображены несколько затемнённых "редких" плюшек. diff --git a/Resources/Locale/ru-RU/_prototypes/_sunrise/entities/objects/fun/toys.ftl b/Resources/Locale/ru-RU/_prototypes/_sunrise/entities/objects/fun/toys.ftl index af65f121ff..5518aec30d 100644 --- a/Resources/Locale/ru-RU/_prototypes/_sunrise/entities/objects/fun/toys.ftl +++ b/Resources/Locale/ru-RU/_prototypes/_sunrise/entities/objects/fun/toys.ftl @@ -172,3 +172,9 @@ ent-PlushieKalium = Калий Фон Дез .desc = Предмет гордости и радости синдиката. Профессиональный агент, жаль что он вульпа.. ent-PlushieKaliumDouble = Агент Виски .desc = Профессиональный агент компании CyberSun.Ind. К сожалению, всё еще вульпа.. +ent-PlushieKaliumJuggernaut = Плюшевый джаггернаут оперативников + .desc = Не игрушка, а плюшевый штурмовой комплекс. Прошел сквозь стену сладости в аттракционе "Синди-дом". Заверши свою коллекцию оперативников этой грозной единицей! Выпуск #04. +ent-PlushieKaliumOperative = Плюшевый оперативник + .desc = Плюшевая версия элитного оперативника Синдиката. Промо-игрушка к премьере фильма "Операция: Код Дельта". Единственный, кому разрешено сдаться. Выпуск #02. +ent-PlushieKaliumNukieCommander = Плюшевый командир оперативников + .desc = Плюшевый командир оперативников. Образец для воспитания юных агентов. Одобрено отделом кадров Синдиката. Смерть NanoTrasen и скучным игрушкам! Выпуск #01. diff --git a/Resources/Prototypes/_Sunrise/Catalog/sponsor_uplink_catalog.yml b/Resources/Prototypes/_Sunrise/Catalog/sponsor_uplink_catalog.yml index da165e8e16..d944252c89 100644 --- a/Resources/Prototypes/_Sunrise/Catalog/sponsor_uplink_catalog.yml +++ b/Resources/Prototypes/_Sunrise/Catalog/sponsor_uplink_catalog.yml @@ -972,871 +972,6 @@ whitelist: - IAA -# Plushies - -- type: listing - id: UplinkSunrisePlushieUunaKatsuhiro - productEntity: PlushieUunaKatsuhiro - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieOne - name: uplink-sunrise-plushie-one-name - productEntity: PlushieOne - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieTwo - name: uplink-sunrise-plushie-two-name - productEntity: PlushieTwo - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieThree - name: uplink-sunrise-plushie-three-name - productEntity: PlushieThree - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieFour - name: uplink-sunrise-plushie-four-name - productEntity: PlushieFour - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieFive - name: uplink-sunrise-plushie-five-name - productEntity: PlushieFive - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieSix - name: uplink-sunrise-plushie-six-name - productEntity: PlushieSix - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieSeven - name: uplink-sunrise-plushie-seven-name - productEntity: PlushieSeven - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieEight - name: uplink-sunrise-plushie-eight-name - productEntity: PlushieEight - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieNine - name: uplink-sunrise-plushie-nine-name - productEntity: PlushieNine - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieTen - name: uplink-sunrise-plushie-ten-name - productEntity: PlushieTen - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieEleven - name: uplink-sunrise-plushie-eleven-name - productEntity: PlushieEleven - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieTwelve - name: uplink-sunrise-plushie-twelve-name - productEntity: PlushieTwelve - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieThirteen - name: uplink-sunrise-plushie-thirteen-name - productEntity: PlushieThirteen - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieFourteen - name: uplink-sunrise-plushie-fourteen-name - productEntity: PlushieFourteen - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieFifteen - name: uplink-sunrise-plushie-fifteen-name - productEntity: PlushieFifteen - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieSixteen - name: uplink-sunrise-plushie-sixteen-name - productEntity: PlushieSixteen - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieSeventeen - name: uplink-sunrise-plushie-seventeen-name - productEntity: PlushieSeventeen - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieEighteen - name: uplink-sunrise-plushie-eighteen-name - productEntity: PlushieEighteen - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieNineteen - name: uplink-sunrise-plushie-nineteen-name - productEntity: PlushieNineteen - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieTwenty - name: uplink-sunrise-plushie-twenty-name - productEntity: PlushieTwenty - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieTwentyOne - name: uplink-sunrise-plushie-twenty-one-name - productEntity: PlushieTwentyOne - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieTwentyTwo - name: uplink-sunrise-plushie-twenty-two-name - productEntity: PlushieTwentyTwo - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieTwentyThree - name: uplink-sunrise-plushie-twenty-three-name - productEntity: PlushieTwentyThree - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieTwentyFour - name: uplink-sunrise-plushie-twenty-four-name - productEntity: PlushieTwentyFour - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieTwentyFive - name: uplink-sunrise-plushie-twenty-five-name - productEntity: PlushieTwentyFive - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieTwentySix - name: uplink-sunrise-plushie-twenty-six-name - productEntity: PlushieTwentySix - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieTwentySeven - name: uplink-sunrise-plushie-twenty-seven-name - productEntity: PlushieTwentySeven - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieTwentyEight - name: uplink-sunrise-plushie-twenty-eight-name - productEntity: PlushieTwentyEight - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieTwentyNine - name: uplink-sunrise-plushie-twenty-nine-name - productEntity: PlushieTwentyNine - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieThirty - name: uplink-sunrise-plushie-thirty-name - productEntity: PlushieThirty - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieThirtyOne - name: uplink-sunrise-plushie-thirty-one-name - productEntity: PlushieThirtyOne - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieThirtyTwo - name: uplink-sunrise-plushie-thirty-two-name - productEntity: PlushieThirtyTwo - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieThirtyThree - name: uplink-sunrise-plushie-thirty-three-name - productEntity: PlushieThirtyThree - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieThirtyFour - name: uplink-sunrise-plushie-thirty-four-name - productEntity: PlushieThirtyFour - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieThirtyFive - name: uplink-sunrise-plushie-thirty-five-name - productEntity: PlushieThirtyFive - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieThirtySixBig - name: uplink-sunrise-plushie-thirty-six-big-name - productEntity: PlushieThirtySixBig - cost: - Suntick: 12 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieThirtySeven - name: uplink-sunrise-plushie-thirty-seven-name - productEntity: PlushieThirtySeven - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieThirtyEight - name: uplink-sunrise-plushie-thirty-eight-name - productEntity: PlushieThirtyEight - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieMiraKovalskaya - productEntity: PlushieMiraKovalskaya - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieMechanixFaurHorseman - productEntity: PlushieMechanixFaurHorseman - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieMihailOsipov - productEntity: PlushieMihailOsipov - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieMadokaMori - productEntity: PlushieMadokaMori - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieMolnia - productEntity: PlushieMolnia - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieExperiencedSalvageSpecialist - productEntity: PlushieExperiencedSalvageSpecialist - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieAlisaKrokozyabka - productEntity: PlushieAlisaKrokozyabka - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieJamalDotsenko - productEntity: PlushieJamalDotsenko - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieRichardDetective - productEntity: PlushieRichardDetective - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieCaptainTartarus - productEntity: PlushieCaptainTartarus - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieCaptainEmily - productEntity: PlushieCaptainEmily - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieCrusader - productEntity: PlushieCrusader - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieGenok - productEntity: PlushieGenok - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieMegane - productEntity: PlushieMegane - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieFrizko - productEntity: PlushieFrizko - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieMiron - productEntity: PlushieMiron - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieBublegum - productEntity: PlushieBublegum - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieCikuus - productEntity: PlushieCikuus - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieSuvagesi - productEntity: PlushieSuvagesi - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushiePrototupus - productEntity: PlushiePrototupus - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieSmile - productEntity: PlushieSmile - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieCFB - productEntity: PlushieCFB - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieSebastian - productEntity: PlushieSebastian - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieNara - productEntity: PlushieNara - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushiePeacemaker - productEntity: PlushiePeacemaker - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieEblan - productEntity: PlushieEblan - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieKlim - productEntity: PlushieKlim - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieMelania - productEntity: PlushieMelania - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieOkobius - productEntity: PlushieOkobius - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieVyacheslav - productEntity: PlushieVyacheslav - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieLu - productEntity: PlushieLu - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieAgata - productEntity: PlushieAgata - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieMilosTanka - productEntity: PlushieMilosTanka - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieAlice - productEntity: PlushieAlice - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieKaioshi - productEntity: PlushieKaioshi - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieRaisya - productEntity: PlushieRaisya - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - - type: listing id: UplinkSunriseClothingHeadHatCapUSSPBlack name: uplink-sunrise-clothing-head-hat-cap-black-name @@ -2885,11 +2020,14 @@ - !type:ListingLimitedStockCondition stock: 1 + +#plushie + - type: listing - id: UplinkPlushieAndrewNarvi - productEntity: PlushieAndrewNarvi + id: UplinkSunrisePlushieVulpSyndiPresent + productEntity: PlushieVulpSyndiPresent cost: - Suntick: 4 + Suntick: 12 categories: - SpecialThings conditions: @@ -2897,34 +2035,12 @@ stock: 1 - type: listing - id: UplinkPlushieTimofeyTatarchuk - productEntity: PlushieTimofeyTatarchuk + id: UplinkSunrisePlushiePresent + productEntity: PlushiePresent cost: Suntick: 4 categories: - SpecialThings conditions: - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkPlushieSansaMontanelli - productEntity: PlushieSansaMontanelli - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: UplinkSunrisePlushieKalium - productEntity: PlushieKalium - cost: - Suntick: 4 - categories: - - SpecialThings - conditions: - - !type:ListingLimitedStockCondition - stock: 1 + stock: 3 diff --git a/Resources/Prototypes/_Sunrise/Entities/Objects/Fun/plushie_boxes.yml b/Resources/Prototypes/_Sunrise/Entities/Objects/Fun/plushie_boxes.yml new file mode 100644 index 0000000000..0793e4abcf --- /dev/null +++ b/Resources/Prototypes/_Sunrise/Entities/Objects/Fun/plushie_boxes.yml @@ -0,0 +1,192 @@ +- type: entity + parent: MysteryLighterBox + id: PlushieVulpSyndiPresent + name: mystery sindy plushie box + description: A box containing a mystery plushie. + components: + - type: Sprite + sprite: _Sunrise/Objects/Fun/advanced_toys/plushie_sindy_box.rsi + state: fig_box + - type: SpawnItemsOnUse + items: + - id: MysteryFigureBoxTrash + - id: PlushieKaliumNukieCommander + orGroup: PlushieKalium + - id: PlushieKaliumOperative + orGroup: PlushieKalium + - id: PlushieKaliumJuggernaut + orGroup: PlushieKalium + sound: + path: /Audio/Effects/unwrap.ogg + +- type: entity + parent: PresentRandom + id: PlushiePresent + name: mystery plushie box + description: A box containing a mystery plushie. + components: + - type: Sprite + sprite: Objects/Fun/figurines.rsi + scale: 1.2, 1.2 + layers: + - state: fig_box + - type: SpawnItemsOnUse + items: + - id: PlushieUunaKatsuhiro + orGroup: Plushie + - id: PlushieOne + orGroup: Plushie + - id: PlushieTwo + orGroup: Plushie + - id: PlushieThree + orGroup: Plushie + - id: PlushieFour + orGroup: Plushie + - id: PlushieFive + orGroup: Plushie + - id: PlushieSix + orGroup: Plushie + - id: PlushieSeven + orGroup: Plushie + - id: PlushieEight + orGroup: Plushie + - id: PlushieNine + orGroup: Plushie + - id: PlushieTen + orGroup: Plushie + - id: PlushieEleven + orGroup: Plushie + - id: PlushieTwelve + orGroup: Plushie + - id: PlushieThirteen + orGroup: Plushie + - id: PlushieFourteen + orGroup: Plushie + - id: PlushieFifteen + orGroup: Plushie + - id: PlushieSixteen + orGroup: Plushie + - id: PlushieSeventeen + orGroup: Plushie + - id: PlushieEighteen + orGroup: Plushie + - id: PlushieNineteen + orGroup: Plushie + - id: PlushieTwenty + orGroup: Plushie + - id: PlushieTwentyOne + orGroup: Plushie + - id: PlushieTwentyTwo + orGroup: Plushie + - id: PlushieTwentyThree + orGroup: Plushie + - id: PlushieTwentyFour + orGroup: Plushie + - id: PlushieTwentyFive + orGroup: Plushie + - id: PlushieTwentySix + orGroup: Plushie + - id: PlushieTwentySeven + orGroup: Plushie + - id: PlushieTwentyEight + orGroup: Plushie + - id: PlushieTwentyNine + orGroup: Plushie + - id: PlushieThirty + orGroup: Plushie + - id: PlushieThirtyOne + orGroup: Plushie + - id: PlushieThirtyTwo + orGroup: Plushie + - id: PlushieThirtyThree + orGroup: Plushie + - id: PlushieThirtyFour + orGroup: Plushie + - id: PlushieThirtyFive + orGroup: Plushie + - id: PlushieThirtySixBig + orGroup: Plushie + - id: PlushieThirtySeven + orGroup: Plushie + - id: PlushieThirtyEight + orGroup: Plushie + - id: PlushieMiraKovalskaya + orGroup: Plushie + - id: PlushieMechanixFaurHorseman + orGroup: Plushie + - id: PlushieMihailOsipov + orGroup: Plushie + - id: PlushieMadokaMori + orGroup: Plushie + - id: PlushieMolnia + orGroup: Plushie + - id: PlushieExperiencedSalvageSpecialist + orGroup: Plushie + - id: PlushieAlisaKrokozyabka + orGroup: Plushie + - id: PlushieJamalDotsenko + orGroup: Plushie + - id: PlushieRichardDetective + orGroup: Plushie + - id: PlushieCaptainTartarus + orGroup: Plushie + - id: PlushieCaptainEmily + orGroup: Plushie + - id: PlushieCrusader + orGroup: Plushie + - id: PlushieGenok + orGroup: Plushie + - id: PlushieMegane + orGroup: Plushie + - id: PlushieFrizko + orGroup: Plushie + - id: PlushieMiron + orGroup: Plushie + - id: PlushieBublegum + orGroup: Plushie + - id: PlushieCikuus + orGroup: Plushie + - id: PlushieSuvagesi + orGroup: Plushie + - id: PlushiePrototupus + orGroup: Plushie + - id: PlushieSmile + orGroup: Plushie + - id: PlushieCFB + orGroup: Plushie + - id: PlushieSebastian + orGroup: Plushie + - id: PlushieNara + orGroup: Plushie + - id: PlushiePeacemaker + orGroup: Plushie + - id: PlushieEblan + orGroup: Plushie + - id: PlushieKlim + orGroup: Plushie + - id: PlushieMelania + orGroup: Plushie + - id: PlushieOkobius + orGroup: Plushie + - id: PlushieVyacheslav + orGroup: Plushie + - id: PlushieLu + orGroup: Plushie + - id: PlushieAgata + orGroup: Plushie + - id: PlushieMilosTanka + orGroup: Plushie + - id: PlushieAlice + orGroup: Plushie + - id: PlushieKaioshi + orGroup: Plushie + - id: PlushieRaisya + orGroup: Plushie + - id: PlushieAndrewNarvi + orGroup: Plushie + - id: PlushieTimofeyTatarchuk + orGroup: Plushie + - id: PlushieSansaMontanelli + orGroup: Plushie + - id: PlushieKalium + orGroup: Plushie diff --git a/Resources/Prototypes/_Sunrise/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/_Sunrise/Entities/Objects/Fun/toys.yml index 71b82b8568..6485ef4e40 100644 --- a/Resources/Prototypes/_Sunrise/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/_Sunrise/Entities/Objects/Fun/toys.yml @@ -1126,6 +1126,8 @@ sprite: _Sunrise/Objects/Fun/advanced_toys/plushie_kalium.rsi layers: - state: plushie-kalium + - state: potasium-unshaded + visible: false - state: potasium-unshaded visible: false shader: unshaded @@ -1227,4 +1229,201 @@ - type: ToggleableVisuals spriteLayer: light +- type: entity + parent: BasePlushie + id: PlushieKaliumJuggernaut + components: + - type: Sprite + sprite: _Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi + layers: + - state: plushie-kalium + - state: potasium_jager_unshaded + visible: false + shader: unshaded + map: [ "light" ] + - type: Clothing + quickEquip: false + sprite: _Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi + equippedPrefix: kalium + slots: + - NECK + - type: ItemToggle + soundActivate: + path: /Audio/Weapons/Guns/Cock/lmg_cock.ogg + soundDeactivate: + path: /Audio/Weapons/Guns/EmptyAlarm/lmg_empty_alarm.ogg + params: + volume: -4 + - type: RandomMetadata + nameSegments: + - NamesSyndicateNormal + nameFormat: name-format-nukie-operator + - type: Item + size: Small + heldPrefix: kalium + - type: Tag + tags: + - PlushieKalium + - type: UseDelay + delay: 1.0 + - type: PointLight + enabled: true + radius: 1.2 + energy: 1.5 + color: "#FFD700" + netsync: false + - type: Appearance + - type: ToggleableVisuals + spriteLayer: light + - type: ItemTogglePointLight + - type: EmitSoundOnTrigger + sound: + path: /Audio/Weapons/Guns/Bolt/lmg_bolt_open.ogg + - type: EmitSoundOnLand + sound: + path: /Audio/Weapons/Guns/Bolt/lmg_bolt_closed.ogg + - type: EmitSoundOnActivate + sound: + path: /Audio/Weapons/Guns/Bolt/lmg_bolt_open.ogg + - type: EmitSoundOnUse + sound: + path: /Audio/Weapons/Guns/Empty/lmg_empty.ogg + - type: MeleeWeapon + soundHit: + collection: PlushieVulpkaninGrowls + params: + volume: 100 +- type: entity + parent: BasePlushie + id: PlushieKaliumOperative + components: + - type: Sprite + sprite: _Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi + layers: + - state: plushie-kalium + - state: potasium_nuclearop_shaded + visible: false + map: [ "light" ] + - type: Clothing + quickEquip: false + sprite: _Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi + equippedPrefix: kalium + slots: + - NECK + - type: ItemToggle + soundActivate: + path: /Audio/Weapons/Guns/Bolt/rifle_bolt_closed.ogg + soundDeactivate: + path: /Audio/Weapons/Guns/EmptyAlarm/smg_empty_alarm.ogg + params: + volume: -4 + - type: RandomMetadata + nameSegments: + - NamesSyndicateNormal + nameFormat: name-format-nukie-operator + - type: Item + size: Small + heldPrefix: kalium + - type: Tag + tags: + - PlushieKalium + - type: UseDelay + delay: 1.0 + - type: PointLight + enabled: true + radius: 1.2 + energy: 2 + color: "#FFD700" + netsync: false + - type: Appearance + - type: ToggleableVisuals + spriteLayer: light + - type: ItemTogglePointLight + - type: EmitSoundOnTrigger + sound: + collection: PlushieVulpkaninGrowls + - type: EmitSoundOnLand + sound: + collection: RiflesDrop + - type: EmitSoundOnActivate + sound: + collection: PlushieVulpkaninGrowls + - type: EmitSoundOnUse + sound: + path: /Audio/Weapons/Guns/Empty/lmg_empty.ogg + - type: MeleeWeapon + soundHit: + collection: PlushieVulpkaninGrowls + params: + volume: 100 + +- type: entity + parent: BasePlushie + id: PlushieKaliumNukieCommander + components: + - type: Sprite + sprite: _Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi + layers: + - state: plushie-kalium + - state: potasium_nuclearcom_unshaded + visible: false + shader: unshaded + map: [ "light" ] + - type: Clothing + quickEquip: false + sprite: _Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi + equippedPrefix: kalium + slots: + - NECK + - type: ItemToggle + soundActivate: + path: /Audio/Machines/energyshield_up.ogg + soundDeactivate: + path: /Audio/Machines/energyshield_down.ogg + params: + volume: -2 + - type: ItemToggleActiveSound + activeSound: + path: /Audio/Machines/energyshield_ambient.ogg + params: + volume: -2 + - type: RandomMetadata + nameSegments: + - NamesSyndicateElite + nameFormat: name-format-nukie-commander + - type: Item + size: Small + heldPrefix: kalium + - type: Tag + tags: + - PlushieKalium + - type: UseDelay + delay: 1.0 + - type: PointLight + enabled: true + radius: 2 + energy: 3 + color: "#ff0000" + netsync: false + - type: Appearance + - type: ToggleableVisuals + spriteLayer: light + - type: ItemTogglePointLight + - type: EmitSoundOnTrigger + sound: + collection: PlushieVulpkaninGrowls + - type: EmitSoundOnLand + sound: + path: /Audio/_Sunrise/Items/Handling/sabre_drop.ogg + - type: EmitSoundOnActivate + sound: + collection: VulpkaninBarks + - type: EmitSoundOnUse + sound: + path: /Audio/Machines/beep.ogg + - type: MeleeWeapon + soundHit: + collection: PlushieVulpkaninGrowls + params: + volume: 100 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium.rsi/meta.json b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium.rsi/meta.json index ec9ed1e8ac..1d4ae2f4aa 100644 --- a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium.rsi/meta.json +++ b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Created by tg @Salko_f", + "license": "CLA", + "copyright": "SUNRISE / Sokol9Bl", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_double.rsi/meta.json b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_double.rsi/meta.json index c20971fca3..bb7b1c12ea 100644 --- a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_double.rsi/meta.json +++ b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_double.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Created by tg @Salko_f", + "license": "CLA", + "copyright": "SUNRISE / Sokol9Bl", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/kalium-equipped-NECK.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/kalium-equipped-NECK.png new file mode 100644 index 0000000000000000000000000000000000000000..408dc80c22076b3ada1b345e75e99efc7aa4573b GIT binary patch literal 1082 zcmV-A1jYM_P)Px&+DSw~RCt{2+QCoTRvZWLZ-}W=mWC+i#n_M%U?g=ynuZpHHqrKkCRIhEM?}tC z*s1@(cHvNI+GUrWEA_Sm(~wZ7soL1If>yDOiHugP-6RSjAx)xmEOM0J4pE}ChbF5Z zo2$Lg%~symKR^4~R^9^yLEz!-vl_j;ZRpHWrlGSF$!#X_3VQVW7A~B-OozFn#&%FQ z2!P{tGfhb%)2tvKjx=&jvjTKVmU5eM>i~zN$FZ1966@_n=HLLjKZ>_bTZ=c%3cMbM zsfmlI=YF<&7eIwi(EvKQB?0*4_H73MY;66E$wVBo%WWyoea4Hi=(!j6O(x=SpYdAC zZN{|&0AS_IyIBA6Cp>)e7`bOfOIf*RMQr@C^};?Lxy`tCKr}Q$E?LHCXap0ZG0S;= zpMr_e82-qY@YOe8gw-qE4OR?_6Gpw>4to|6XjzQR&(DNhR$%y zC()&g6SSC2lEzfJ<&&s&%l2^}0?jn0S}yiur-Cl2_HA*B%J^brj7#pnBK2UyQlW;< zDD_~2D4!*7mx};^@>#N)`^KPrmV(Ut|2SDe5ClOG1VIo4K@bGNu3nz6x03;lom_3+ zE*A}GjEJ(f?Es=IX-sYWf!YpdJ3oqtBQ$nChD+x!;M$FOTTV!5Or__U5(auzC`u1x zN&X;xl=-k#yS7#^9OjNt_>3l&0L=)XZ0AHscj^Ti1ms&JIo#7-uivK zpG+bY97Mv1U^BH1XZ08}*JoRHLd)^2Ykl-|f6vj}qjsB^)R;cqX5UO@!fkV+e?Pt=CxpIa=)lNL@FtIii0{{w)hw$|Cy;rtv zIRuuMJn;Jz6bmJ+4aKlfD%q9)L-eWO!8o000hUSV?A0O#mtY000O800000007cclK=n!07*qoM6N<$g57up AHvj+t literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/kalium-inhand-left.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/kalium-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..c1183b8535e9f6ef25357964fc6821449dd63533 GIT binary patch literal 877 zcmV-z1CsoSP)Px&CrLy>RCt{2+RsZHK@pz|LO2?tE@ZBmn5Mi#RAAVlSVw^fz`` z6<88o6bUI%<1qk$o=yE;+-2O7NP0E}08ryGL_!KA(N%Lyt1Q_7WASNy8+s-KJ)45; z9;>h)jO+n9Okk+@`vI(XO`ec$s7fnT@L;x+$&y(U0G_(lCAE3qgc_M(O zqEV7W7X^K?WgB36e);54>J-ZGd4`clYIySa5#FzU#L4$lZ0_oH=kpGE=+^B{JiPw^ zZ{EIxagt8ShV(|t()+*^K6#Y(S0`!7VWT;F2T7ufyhENVeL?S#ha}NObM_8ea@c5p zb&`DYs3oVsk~<(YomDQkn8~M+D_5Y# zV^I77qN761#n4fK6n}u!cnrC61vB~dxp-EvSrOFU(*>Y}=OrfwL?`ZP8q|0U>zS>Z z&nuz=$sPsvo-VAcZ3w{GodBTbePEsR>tt<;X`&$O$a|EE^_5llM)?W*Trq?LXI)1JZW ztmm82kI=Gq)$=dYY(GLvDNtSSUnrJ^cGUAP(riBh&|-gp(09IEy-+L*Ubl^`tmj{( z*?t5|go849-Gf9_fA`HmR3NW=kix;UUP2=s8cBs8Nz$D#h!v%g00000NkvXXu0mjf D&mW~P literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/kalium-inhand-right.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/kalium-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d42740da7b07654438c441edbbcd2190f58694fa GIT binary patch literal 881 zcmV-%1CIQOP)Px&D@jB_RCt{2+RsbdU>FDRZ%bvHKZ>@CAq;FCO{?ujsK_Xc9Rw+ZLdBehUWWgI z=-u7sW&gqcfE{GJ?LG!&2fHv`U9}oLRBNkc#CGgCvM#p2vCPO11#)6-WsLNsY&l zD;{G!p9TQG2}Zo*`80CHW2o^Mlt7SZuTVA*hxuSBvkEmH!&gnis?>+=-5s>G zI^b+?tyk=pIEnWFZw}n(7TkEKX;9-aEM->9#+K|2W2u<;{6I*i{htR|)-~8{_Z$8_ zKvW>vtH9abiuuK58}RR)0O0cP1I8F*j4{R-V~jDz*sW62AB4B5XW*=gr7^Qc>KQO9 zzE)ndY62RiXTV(Zw^G#v06^QlR=k>ijkj}ir)6hXm($Wfs&REWkw|LrdwW5EWy!B8 z0l$79;N6G!m{3(Dk{Tjm1;9xu+NwYk+JHvs z88B-8)9b~OrElxJTFJg@0$z;01W>tWfT%$A=ov8A{H^vEZ_00000NkvXX Hu0mjfPfwrz literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/meta.json b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/meta.json new file mode 100644 index 0000000000..7edd6b16e6 --- /dev/null +++ b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/meta.json @@ -0,0 +1,45 @@ +{ + "version": 1, + "license": "CLA", + "copyright": "SUNRISE / Sokol9Bl", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "plushie-kalium" + }, + { + "name": "potasium_jager_unshaded", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "potasium_jager_shaded" + }, + { + "name": "kalium-inhand-left", + "directions": 4 + }, + { + "name": "kalium-inhand-right", + "directions": 4 + }, + { + "name": "kalium-equipped-NECK", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/plushie-kalium.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_juggernaut.rsi/plushie-kalium.png new file mode 100644 index 0000000000000000000000000000000000000000..1627c0e309d32288949f447d8c856dd5930ded0d GIT binary patch literal 1407 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 X14#Z|zcW@J00000NkvXXu0mjfJfPcj literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/kalium-equipped-NECK.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/kalium-equipped-NECK.png new file mode 100644 index 0000000000000000000000000000000000000000..e3bd2c744f46cb06e337833a4003a929e1f4c9be GIT binary patch literal 690 zcmV;j0!{siP)Px%Y)M2xRCt{2+P`lSVHgMSFQ!WQ(E?4tGl5_(B^VPdv646|NhcQ<7D!{_(7}JD zn}dr(V;!29G!CW?E>P-=17Idp&UV7kb6&{JfGz*x#YR$p1b$% zlIH;!V~mO8^|+{iYn1GlsDC^5a>_16!q+IL?2^$U$bN~u9v9uZu|!^vi_9p(jOc(n z8{4p2t=QSv#+`2`*fJ0+c2t7@!9!GPRRDlHvxfsQY4%(Y8EjLDQ96^yoXv)GCXahc z%y7&~t%__Rk5;QCAj7{2Ex>YM6)WpS*ljkftQWBySTz){1|VC=WA@?3bkYa+Z6l)LbkAb?)Njix$uj6W~_)8|7xDpiDDJRtZTpnLf?Z2I6H^-_|I_m)4m=HMxH;-X zMqyKlQC53Qi@^Xz!i&?{JV^kgcaEYVdHR;XR<~syowJi1^r6vIX^`HeY2D#b=$|PV zv^9UO3eZ1OFlcN3Topi{DJYif(8!eOpYeC~%4LcJlo)j)qcoMDLB<$kj4{R-V{CB0 Y0EL(Y+Xu`wr2qf`07*qoM6N<$f~zPp%>V!Z literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/kalium-inhand-left.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/kalium-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..b3f1a2ddd4b6f05b89118f69e9c3091937e06aca GIT binary patch literal 963 zcmV;!13dhRP)Px&eMv+?RCt{2+D&NNW*ETne-aJcHEoC!)w2`ViKk+4Ff4Q~SyI-ym@!yMAQwSd z*kzP4%ua*B20ic8Q()PthZP39ZqUoXkgn@KI%o-X?Amki9Kswogc_5oG~TmAl@dzQ zI#?22d4DGf^nU2kE4`rS0Yy<1MNt$*Q4~c{6h%>-ruX-Wo7Z=u+wB2NkQ=}MaG*9v z4j;knF^xtPJp z@P~YI_B*DMNxt|#&6g+MroMT9&nH^nyx)~v{fA=%gTxDYdZuS+G#cb$hG(yXBT^s> zAlG_*V%W#a*1TLPCfmA+M9h%$(=)OFV%YsGfTUasg5&R33gno5{3t!aD?k0pQZz!Y zTxol(cr+r3a^*>TfOR12!h9Wn1*$*_Sc*n?Xl-MiImKv5XCxSC>j{no12P)YvCf?0 zp|#CYG(yVm*A0$G)Brij;~;z$4Djk9A1A^Awp;(QvQ~fQN{B}zG8)p+db%lAYkad_ z<4)5e3;YR`Ui1}kdVEX(GQB>sK;O}R2K+j=?^ak@tF!gMdS?5r2No-9b#C9SFyPn8 z42_Vr``g{WlN=2|qq&8IgIsJ{h{Pc_@V#dDz{@r4NBw~gvTl12doRW_}n3H8|-rIS+fBH>{M4e~MoJ7dnKC&UDaMI?btUU)9d4ce~e78&((bX(>kq3a4Y}|XW!$q-+q3Q z&-YRm__SQ%&RPx&heGkM8>Vp`A}ePe)|bsWUv}G zcJ3C5}wb5KN~S9&!?1{e$AhgS{*IY9=hp z*1Rl$7$Dc}Hdz3P_DK?=APcp)joS8w=@|(HjF$1Y%tp}flTa?tqu3PBee)v#_khGM z`}dM4$Z&1D9{-ZxM>3ah8GF;Wz?j=5;~pLB>`5M4TP*o~M1jJyjJk~qKonT=`*>(= zVVyn6xJPHq?b^Qrf_|TjdvvrThshMyscc$&^!qvq;1?j<2Nhs|oZv|kChRue9d>ct z>td_=4=ZcMrb_r%1ynXIR@RE#zME&rp)>H}APFFDw*hcwa-yg21NHdgcH5Ju3VP$#rwE&dAuWD=4N9!(kYSwgG*%#@wzFME1g@-|4=Gd z5U-m{Ws79Mz>!WdHygt{;>DHAbG~?=YgaC}K8bNnll4irn6*28T+<|T%u~PnOE$A8 z)oP)qE1+5}NH()5)YgzW=8?FjNfg8cS)X)ET+>>w!K^KM`{RE3>f23y;q}LHO_P56 zO`|A^q9}@@D2k#eilQjpa?oZ5{dPZhrQ=L5l-JTiP-t!Ooee%YcS36&U zLBCH-klEOjn6-{eClg?9Yj3r)`u4mB)OCAipx*P3+ikq#m}qpX+hN}X1pPjlc-h;S z8MyOnfe&(d5^W3N=_G(VWs8eHeaFJN*W32F`zD~?GrvQ6VP;@w&;MB-tI0u}8QAGD*Eq!x3|ooi00000NkvXXu0mjfg(TPx literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/meta.json b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/meta.json new file mode 100644 index 0000000000..cf477417c4 --- /dev/null +++ b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/meta.json @@ -0,0 +1,45 @@ +{ + "version": 1, + "license": "CLA", + "copyright": "SUNRISE / Sokol9Bl", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "plushie-kalium" + }, + { + "name": "potasium_nuclearcom_unshaded", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "kalium-inhand-left", + "directions": 4 + }, + { + "name": "kalium-inhand-right", + "directions": 4 + }, + { + "name": "kalium-equipped-NECK", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/plushie-kalium.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/plushie-kalium.png new file mode 100644 index 0000000000000000000000000000000000000000..e4fdfe1f68d57675d1de017da5689cea22a0d62d GIT binary patch literal 1466 zcmV;r1x5OaP)Px)bV)=(R9Jta)R6*IW8x#2$;K+W#Aft6H>qdLmty{aTqg}h#Kek<2 zbnON7{Bv*aIrn*<_nh;d`(C(8Gn)(|Z>C;M=g*s|7iN=TTK{#K*<=v$xF;dOWK1HF z*KBz+^&%ek2&bb-)N3@N4#cXvmWtc0b3`48IuK4rQ&N#vnxvN8Y%&PP#XopXSAstl zL%VJR=iM#Xf7?Ka^WJ5+W#OD ziBM*}MIieE6eO2K0sS`n3G+t%-H`b zt+G53HO!u$4wF7Z$H?Uf0PXGVNwHPWfkI<}&}p@N(dWlCCU6P(DnFk{bi4s|S}g#P zXcV1S#;FopwSZz{fv|Yxe>`{pOKi+oFz)lpN%2*cplq&LEOYeo;{dF%~pA0BAI5$<4|HU|?u` z442L;Qr?Nh$Izsv&~o8o(k!SFkRzqivUCob)D(pv5*?-XloyW}rESGrh4e}hk?1H+ zk1wH!qyp+doG=!!(HEsqO2Z>Yf$=O%T7xXuzr0u>)}*E=0($!gNmPb}Jy5UFh>xr0 z(G%#Wl1akx>j^^&o0@~{F4I$!mCn6Q4%$}Cok9+OmuNbhb`ZU)*DK_qK8c*1Y%0An zpY#T(^vY~%4pJ*+v8g%8cDIK{gO+=axrxQc0MKdE5)vmBa4Z;9$bEqTC8lD|TP?g@ zx{&!>UKo#AyM)E)_$W3`Hd&rXbX3W}H48Wb(!*!G)TU?fe&sS;U2d|o^%T!8#Ow0` zV9d{_?d}Q|qr>7Gq|u;d?QfmR>YW}R4{m1OE_lNQAF#5KUTb z$}Eb?Oe~)`a(z1?+ybHprfgTYxC~Op_=^H+r7Qq3ort6-q9Y)s+3j5H?&2e}dCGTw z@;=lj@p@SW+pG%!I3&VS4{_YYl-cmV+A0vsY&KzmDgT4jQiiCOGX9@G<^Rh50?`wi U&rX~va{vGU07*qoM6N<$f}9+*{{R30 literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/potasium_nuclearcom_unshaded.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_nukiecommander.rsi/potasium_nuclearcom_unshaded.png new file mode 100644 index 0000000000000000000000000000000000000000..9738bb551b579c139bd700a9d29dfe617a69794b GIT binary patch literal 1901 zcmV-z2a@=SP)Px+CrLy>RCt{2ox5%uI}nCPMvx*^3I`CP;yl2XevkBikW=~zQl}3P7;Xbqsa&X6 z6$q|RAw)omq)i%~!di*u&X6%A}0iECZHS^gWkI)jUqw_mICEuF>&fNi> z-}$OsB^{mL`8oK`1h5CV)esQX2|K^@GxNRN#R9*+-Kywlj_=~0BR@n(rF?$^0HCvw zv>^(!VJW_d%WQlcZZmvagHKCCe6sUVZH+Qa3nK`W;)}Uv`M&m;;<=UiEFBcnr%#|G zH=j%Z&Md@jjE)W&?1;pC5m(X(J=`Yvln&b(M)>hah2kWxn}t{Hz%&| zUajX==CgFzK6Uv&P)8{~ngB2{%g1Z%PXPd6^=%K!FMm!KY{9mWfXc|hM=RlQudDnC z-@4bwt(t$Z<>NIORL_O)x_|lQ582UHpMSve<9~SYjY)*1`1U>X`7?jLK3%)HIq@<7 zD?N7+zFEe>r_0A{GT|KkkJV4C9}@E?34jVhZ{NL!`%k}KT)zMG3s&Fu@cZZA=L>d* zHsO&$I5poDd;#FZH_E^N`sO7cGlk?+jBnV)|MU6O|Bdo}zCZHso?cwLzk7n!=O0tP z5u7+0O7dp_59aH$05F|B?{?4puWwG**6S(X9*vHJuaQ3tupfro{V?3t!Uu5p-_RlQ zzb^=L>puhkxCby1L1hkXs$phk&bl3E9kNEgJ^jRNw>uBMX{Hv;|31Fmp)2EieeYuu zBh!f%q7ItjQyj`#=5zWQm+x7NFUh|`1t4~ug4(uT!|MBc=M-gP+;zy>_@h}Ba-4zz zoFCs@f+EUx^0CHO5uY1;j^*2Cw?E`wwCsb1Ci$-RcMg0c_>%tj@u?MwO8EFDFf#gl z`|kAwa)0*(tMBjO_s_rZ;opB=f*EC!RvUl2edb%8%$$Oq6(6~W^uLQA^5#NKe8Z2T z_D7V_EPuV0wZE46#K>FJ{zl`hOcdtN00O6=oatD~DR3@+%v7vZzO5mo6Jw39LjHaj zUhYS5xsv4wvmShJr`5)nF1xt+oHz4I^GzKw$%_iUTn(A{GuGO(EreSszV+B~^QTsR z>^hvUd*?lqi$&u&cyeB z2M`*3v3on?f;U3f2=jy{&@-DQX0Q$rGsxSzWotSEK6EHrTMwaZ=wCC3clR3O7mM_Df9ga zAm-shcHe8@n*o#hu*zkc!M_CGG#}qpKOx#{<7;&8nEEKezrdxCCRhphSCdb%&0wjK zkv%6fR>9Xa!5b*m@G1XGSe*K)kWc>qZkHzbx}L8zfBc(~AIl4U1Ev(;_J0Tqvx@oj z3?Ozbdbf)!BPZXt48d)Tp(DN0e4k-_U*ppledpj)zSkIh`08V`sh1zi<7jS~uaTT9 z;NuAZ3!IV;jRjxH$gU>7S*FW;o$Z|XA;Gs*eu-tD()`gReU5t`(K+x#Kf&wFup`@jvo_z zE9BFM!BK$(tB~F9)Hz4UVsRRId)8h{^HHNlj3a9_PVkNW5UduyYh-HcYMmdt5g&U5 zKn;9J5Dp2xt@5>kZ$*6iIA{Rb)DatX+feDxp=IO;Y2fTJr1U{fe(0V|BYc+cCC4(m zf^X;`C?P*eM`#e%@`D23%att4rFiZ{{No+~>xUUAQwYAL`C<*q!MAFDhTvZV-+RAn zmZ3HBuZhpmLA`YoGXk!KKi$wy1XZHUef2hNC zMhi8{#_TYn)?(`Q~4ngU+jM+ zZYlmG0kFhKOgioE{LU}T_j0KqM12XmElbbjaO;CmCmM376Av4N+3#6jnGehL1iCcw_W3_d#nprWRQ>7HxT n`JG=0pWP9aI)3MOeh&WsnE^BkJ9pQ900000NkvXXu0mjfH>1XK literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/kalium-equipped-NECK.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/kalium-equipped-NECK.png new file mode 100644 index 0000000000000000000000000000000000000000..9d7c82bdff1c3cf37144bc7dfd8a69fc280dbfd5 GIT binary patch literal 617 zcmV-v0+#)WP)Px%BS}O-RCt{2+OcaBaTEveZ|sqrArK-&erQyh(#4@m5FNTyZ}AV1TXK7lY*~xD z;2+>r$S9OLiHlP+h(d-!heA7f5l%FboN0)a0nt%UeJQA%qY@ z2qA3rtr1fn|Y2P7QMk)*Z|x3(=zis@6zmTE>@tezUW-e-SlColv>UL z0OJ!d@0;M#sV#dKc`{kDyq5az3n7FMLI@#*5dZcYe09|w)V_Iv00000NkvXXu0mjf DG7TJJ literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/kalium-inhand-left.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/kalium-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..f1d27d4a501d55cb7546fde47f9eb3a24d163d58 GIT binary patch literal 837 zcmV-L1G@Z)P)Px%|4BqaRCt{2+RsZ=Q53-O@6<(J({n;$BrPN2!IQ*=3kj*sK5>y?wct%9~hW{xj){! z_ntS*IiQqMN-3q3Qc5YMlyWWGc21J~emzblJ%9yy_x|M{wP`(j1`7?l35l>kUl_<_ zxuu+uH{hKk{UMyU6#rSJ~uze=l$6=AF*j=@D43Y#`m8JgIE55C*iDij3U4 zNv73j704PRQ{$rtuH@ACsAP?iRUp&qGji)D?WH0MVZf8hCeq!>dS%1qbluz9T9(D~ zj1UK{@}(|`jFH@)pm^9Z*t`96AdxXrzSLCd`=T)HNm}8@qQc5YMlu}A5rIb>QtS|WI*!UmhrVQSQeEwnn zwjRN0Vx$rI{3HBPJwlDMyuh9OkU(?t`F~rF;8e5n`SsQ#IMb+n{(kk=BRGoz&C2K3 zTaVyOqw@Lt)l-iE4tK!P=TAfzF7t++)qtIP0ayJWq}G4rdpqM^xXjY$PZYx*9?X4V zty(=c06Mz?vmZXB8lBItrye0ON$2zTs;3?yG0prXSiA7r=g1g2E*Sm-VDGlUfCZ1| P00000NkvXXu0mjfVo{zl literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/kalium-inhand-right.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/kalium-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..a0a655b100e6671295495806f62e18f36c1d944f GIT binary patch literal 850 zcmV-Y1FigtP)Px&3`s;mRCt{2+P_O1VHgMSZ_0~Y(~F`I@kbRY2PX{~GB}7X0jE-|2<3%}L!op! z`X@-&4iyRxZZ1*?x@ZR%p^y|xi{PXrB$E+aBeaml5G2Xd!MhO8BPv{yi#(qx7n1jV z-Z#(tUby!G7-Nhv#u#IaF~%5U!QLKfcMNW7G@b`eZ+`#z1roRylOO>KJbHNhFBn2l zLRDzf-$h|vBiX#zno0r)>l$tPyC|V5WC-E-gVr)2*xN&ybeyCja9XV*^k*4U6O%ya zW&%x3Ok$&WfzxUgQW1Fac!2E7!B>AF0eW9r!KXJrkk9Aw?BfYOJpYTQsSIMa&w7~z z=up=X)p}?wkr2M^A9!{%oQ{O(_x=IaR0S)A671su;EUP5Q(FQ%Ys(ZAf^q=511c8h zXV8~Q;!xMHR49Q)29YlmTREOP%NL5EkwGjKN;uRt^rezmoS#8=Kt&EfPzc&tTXxRm z`t*+iWC%g&(IIQ|qf9zZl}gsr%b-%pQYIZIm}5wf4v`@QMe8<2`!cAdvjiw81kTE3 zU(CGD0qA!~n@%7B;B3^*&7eed>vA*qk!=;Rp2@EE=VwO?U6 z5^CxsSnV6Ca@p_j7>-VkU2DJL1bCrl!0F6CvA5TnJAfBz2JC14&Ft$MX%1S}U7I^{ cTQsBJ|BdXw!Tt4GJ^%m!07*qoM6N<$f_uJ!c>n+a literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/meta.json b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/meta.json new file mode 100644 index 0000000000..93c014d8b1 --- /dev/null +++ b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/meta.json @@ -0,0 +1,44 @@ +{ + "version": 1, + "license": "CLA", + "copyright": "SUNRISE / Sokol9Bl", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "plushie-kalium" + }, + { + "name": "potasium_nuclearop_unshaded" + }, + { + "name": "potasium_nuclearop_shaded", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "kalium-inhand-left", + "directions": 4 + }, + { + "name": "kalium-inhand-right", + "directions": 4 + }, + { + "name": "kalium-equipped-NECK", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/plushie-kalium.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/plushie-kalium.png new file mode 100644 index 0000000000000000000000000000000000000000..aab6f76458f39643a1bdf087ff164a329763fc80 GIT binary patch literal 1554 zcmV+t2JQKYP)Px)%t=H+R9J<@mu+lQRT#&AWo0W{^{!(Z+tM=Hbrd@RO{XZLSnxF{kRZ-z(D*_8 z0O~X`zMv3Ij3Am|05xc$xF8Dn0gQM+RaK@UN@d#M0)ry~I&*p8pjpr>kR8?i2(gDDx>t+HFj`p)oQ2^LI zkl=tpqNgvMkuzaD@Xhn5({TSC0H~_UD%lMi-o*F9D#Foz?wRjkouUv)rHG_b7=Z;@ zMn5hAJ;@|rFMXW5UaMip(&YerbElKKr}RvH<^=0jKZX6H&xoW_gc3u1fA0glR)^@&Dpqdv?_G-Q0H5txT;la(n&FyuRyh8b0o4`R4U_E2j_`hAY)3o_k_B?G+VS>yEnxXTrNP zwxA$CAEnxaQf*?Au7FpbeHlrTXliQ8xEAER4wPxNY}@?}D|wI|v1Zz?8Q`a`R&?L3 zrPc4}{iZ!k(iNoh2jeOLvMjS`(V~oXa~7~Anbepfzlryo_HaY6hjT*$ML^TLTTv5< zQMqa&L4nUt=`FWln>-np%Z1%;zd8W`bA5h-z#D(MH|@Ma=U?(hd^mmjG?kT=I2?|Q zHFKT=Yb8mPX|+rSI)Q-AN++N?>H#RUS<|^D;6AOEMvVqpmZ__&%Qz2ma$v0_iKlA~ zbhuUA(Io9QEBgy$JmjdS%5|K_fVzuatg=~oM)op8ufsBERkKhNkwP7Z`( zF*@AcxTA44RGZje7~_nmkCNhIoIssk$MjkQ`wL@is5aq_#_4c(b16JPC>BGKBwk&S z|Bo)@B%mjmWJ@&8ni2y?OOlkA7znE=^hJ7%6DKmg)Dbe_+Lm6@Od!#%#gJCKL$u@$5+-XA(N71*ss9Xe^3UkOx2@)W;c5A8pY% zPT~ZB^Y!(-*I3ECmH!L}$jb&_D@o#=xn=^PJ|aO4TPvn;&KICGsPaiXL7=3B_VX90 zi6-gr#W)*Ja1i)%p`H7Swd_6aX6upuQSf8Oj*VhuZ9z*ooQ8LYLYR%yXt!B;FO}lq zE$1GKtNF&K}-`7R1=R?Nm}X|dih7&(0f2SL*+do@-Ny|oqwWiPGn zQ-8o^FDB&@vGP(LTC^Yyzv;gD|BeH}6V2kMU)oT70d~H+o?e@k-bjq1q9S6k7^T-1 z(c9NgLHFTm=3v@T-a60kT}sW1CrW9{>OV07*qoM6N<$ Ef+@%4y#N3J literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/potasium_nuclearop_shaded.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/potasium_nuclearop_shaded.png new file mode 100644 index 0000000000000000000000000000000000000000..b5fda02a5e86df5bc0c736fdd8be7436ee2675c6 GIT binary patch literal 1975 zcmV;o2T1sdP)Px+aY;l$RCt{2-G5A!_Z`6T_u-X0;pXEFLC%5v@X@PmV!NYh+*;j9v$$-NCT3x? zYtla&x2$G$O-ZzCG_l!+Yn#})wQHKRHci$U%o0h=BwKJw7t$uQn;Q@GCvp?i+ItA~J=QxSqZ$j?jo^PJl=kwv=oA38|9u7#ORhAS>p;MFD_7^%esVpg; zb^m;u@|CarEculs#WFqZpA(4pnJY-;D_{AG&!^C-$@H{ed_8Anx78~3Al|QhRW`WS zNIghBh_C1D9fMT9@|C~H{P@Tcho!bkYN|I%P4z~R+A3*t*rm;3zbObaMH!cbS%owC8b>PcxYQ5Woy()EH=#RSNiy%7Y46go)*kjE-=??+2i1e3DkD zMlfuo1A;Sd3@7yWr44`k#9SV|0YCZ}D)z?pS0OcY6z+fnh!heaf=E`{^GXMMcCWCjqeK<^m9#oW#~YXj<~h zSHAKydYOBPIAe66s6>tfTzDu#w%_&F_!S}o-m(Zz8o*`MTebrYn}Wl zZ;V4_rF4x>(Azb@Rb!mc*jUO5%kWb-1-;PzZ25PMP9*%zdd4O1w@?OrU96A_#zBFoR^nUP5UBsfEr2S{#TrqQ$)cuBrhCc&;nQeb_bMs5-)BvD!YU++pVKp=~ycpm9 z<=)^+>C_;ezpkg{FZ%rB{5D10&?kD{z72y9z{{yQVSxEnO?Ov~e%70<=Uub*IYXEJA zL0(=SE|-gw*Cr__D8S`%;Tatz91dd`2H|j+SNFIQcq9@b5DF6+ws6p4M^E9ep)3VQ zEf#H`#WwXjEZRQHLi?%I;GLhpnAI9&%2)YkH~(a8Gtd7y2Y}OHSwApdM=JL&~fyzI9-}38HK6rYa>9(2Ye^OHuFUYW4=UlhX z;?2pCe-#zs@pyRQ$y|2qX{S(g5S^HSir|AKbK!CyY4Yxw{44Q4?^6H{u6bNOL1}WExuk>l88b1%FmQ<#`&Mr=rH_oi+uF=PW=4=-hZKy zA-B#@WQ_d$e8$GcSg|ahq3{T{-1~U=azDO`ReWZ(vCNT2>B<$nf9lMk03UY#6OYfw>mRoe5A@I*?~-q~n^FYjr|xh4 z(rKDnqntc5eNX0p=#6)2`ORvYTBDrna^92q=iBZ4;ngCVTBGbcd@lV#$O|&~pi6pI zuaY*0U39Mh0|I^KEB+bf@jhOVLZ>EcN?lUDwnBV8XXiB)Qoiz)pBX>t4+Pnfvx>3@ zN_e#S1VG~HA{`_EiP32w6v7XQ&mwPWJw`=k#eDKtzVelyWgjodBOZ_3Us-v}XyOIw zS-nc0*j^`(cs!W5WD_{A_&#JGQYN{>5_FocoiZ@{dosIwi002ov JPDHLkV1h$K05Sjo literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/potasium_nuclearop_unshaded.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_kalium_operative.rsi/potasium_nuclearop_unshaded.png new file mode 100644 index 0000000000000000000000000000000000000000..0b400ea59defa8c485b75f855e24318b3292ea23 GIT binary patch literal 146 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}KAtX)ArY;~ z2@Yv* p;vn@sO@RhnZU;4hnj3C0Fl0H&{Bhv%%mtdu;OXk;vd$@?2>`wOG7JC! literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_sindy_box.rsi/fig_box.png b/Resources/Textures/_Sunrise/Objects/Fun/advanced_toys/plushie_sindy_box.rsi/fig_box.png new file mode 100644 index 0000000000000000000000000000000000000000..32d8e7d1da78a2fde0c5d2f19909a8782aa79b39 GIT binary patch literal 499 zcmVPx$lSxEDR9J=WmAz`iFc8OoDWMo)iU?^3K?@l}$2>z{qkHEpUeY0xm(1y{&&7RL4^tx{yS}t8%mD^XcV*wr^Go6Rg$DhGnEMHII-N{*^%USIAf*H#iXuD*9xuOaf*`2S7f8T-il?Faaqg zLWm7nk=}GeX#!Cc5r*N0%vPU=!a4BMuxdjyYWIa1)p@YafKn=(^-@ZdQY(+Ci9ypv zDP>L4x(l?{Op+MS0e{Q?HTS_Jj?s>Na~)IzN-28vG`kCW^>nYOOA~;vNq0RPFU^Br zBw(DEMJWOgjYZnKZ?6ZRvDYXL-&0^wkoS%R^G< Date: Mon, 25 Aug 2025 04:42:48 +0300 Subject: [PATCH 108/108] Automatic changelog update --- Resources/Changelog/ChangelogSunrise.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Resources/Changelog/ChangelogSunrise.yml b/Resources/Changelog/ChangelogSunrise.yml index d6311b2a01..75879fd6e2 100644 --- a/Resources/Changelog/ChangelogSunrise.yml +++ b/Resources/Changelog/ChangelogSunrise.yml @@ -21379,3 +21379,18 @@ id: 1397 time: '2025-08-25T00:44:04.0000000+00:00' url: https://github.com/space-sunrise/sunrise-station/pull/3017 +- author: Gpxgji + changes: + - message: "\u041E\u0431\u043D\u043E\u0432\u043B\u0451\u043D \u0434\u043E\u043D\u0430\ + \u0442 \u0448\u043E\u043F. \u0423\u0434\u0430\u043B\u0435\u043D\u044B \u0441\ + \u0442\u0430\u0440\u044B\u0435 \u043F\u043E\u0437\u0438\u0446\u0438\u0438 \u043F\ + \u043B\u044E\u0448\u0435\u043A. \u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\ + \u044B \u0434\u0432\u0435 \u0442\u0430\u0438\u043D\u0441\u0442\u0432\u0435\u043D\ + \u043D\u044B\u0435 \u043A\u043E\u0440\u043E\u0431\u043A\u0438 \u0441 \u043F\u043B\ + \u044E\u0448\u043A\u0430\u043C\u0438 (\u043E\u0431\u044B\u0447\u043D\u0430\u044F\ + \ \u0438 \u0441\u0438\u043D\u0434\u0438-\u0432\u0435\u0440\u0441\u0438\u044F\ + )." + type: Tweak + id: 1398 + time: '2025-08-25T01:41:01.0000000+00:00' + url: https://github.com/space-sunrise/sunrise-station/pull/2916