From 3060e0f2ecbae328ce11d4bbef1b9c33f6fceca7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 12 Dec 2025 00:33:41 +0000 Subject: [PATCH 1/3] Initial plan From 07c3c15932c04c8ae1b2ec0eea35d09a83136a80 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 12 Dec 2025 00:49:44 +0000 Subject: [PATCH 2/3] Add handheld camera item with photo functionality Co-authored-by: MWG-Logan <2997336+MWG-Logan@users.noreply.github.com> --- .../Camera/HandheldCameraComponent.cs | 28 ++++++++ Content.Server/Camera/HandheldCameraSystem.cs | 60 ++++++++++++++++++ .../Entities/Objects/Devices/camera.yml | 19 ++++++ .../Entities/Objects/Devices/photo.yml | 42 ++++++++++++ .../Objects/Devices/camera.rsi/icon.png | Bin 0 -> 206 bytes .../Objects/Devices/camera.rsi/meta.json | 14 ++++ .../Objects/Devices/photo.rsi/icon.png | Bin 0 -> 168 bytes .../Objects/Devices/photo.rsi/meta.json | 14 ++++ .../Wallmounts/photo_frames.rsi/frame.png | Bin 0 -> 169 bytes .../Wallmounts/photo_frames.rsi/meta.json | 14 ++++ 10 files changed, 191 insertions(+) create mode 100644 Content.Server/Camera/HandheldCameraComponent.cs create mode 100644 Content.Server/Camera/HandheldCameraSystem.cs create mode 100644 Resources/Prototypes/Entities/Objects/Devices/camera.yml create mode 100644 Resources/Prototypes/Entities/Objects/Devices/photo.yml create mode 100644 Resources/Textures/Objects/Devices/camera.rsi/icon.png create mode 100644 Resources/Textures/Objects/Devices/camera.rsi/meta.json create mode 100644 Resources/Textures/Objects/Devices/photo.rsi/icon.png create mode 100644 Resources/Textures/Objects/Devices/photo.rsi/meta.json create mode 100644 Resources/Textures/Structures/Wallmounts/photo_frames.rsi/frame.png create mode 100644 Resources/Textures/Structures/Wallmounts/photo_frames.rsi/meta.json diff --git a/Content.Server/Camera/HandheldCameraComponent.cs b/Content.Server/Camera/HandheldCameraComponent.cs new file mode 100644 index 0000000000000..1d2041b858c3f --- /dev/null +++ b/Content.Server/Camera/HandheldCameraComponent.cs @@ -0,0 +1,28 @@ +using Robust.Shared.Audio; + +namespace Content.Server.Camera; + +/// +/// A handheld camera that can take photos and spawn photo items. +/// +[RegisterComponent] +public sealed partial class HandheldCameraComponent : Component +{ + /// + /// The entity prototype to spawn when a photo is taken. + /// + [DataField("photoPrototype")] + public string PhotoPrototype = "Photo"; + + /// + /// Sound to play when taking a photo. + /// + [DataField("sound")] + public SoundSpecifier? Sound = new SoundPathSpecifier("/Audio/Machines/shutter.ogg"); + + /// + /// Use delay in seconds to prevent spam. + /// + [DataField("useDelay")] + public float UseDelay = 2f; +} diff --git a/Content.Server/Camera/HandheldCameraSystem.cs b/Content.Server/Camera/HandheldCameraSystem.cs new file mode 100644 index 0000000000000..ba0b595fc73cf --- /dev/null +++ b/Content.Server/Camera/HandheldCameraSystem.cs @@ -0,0 +1,60 @@ +using Content.Server.Popups; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Interaction.Events; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Timing; + +namespace Content.Server.Camera; + +public sealed class HandheldCameraSystem : EntitySystem +{ + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + private readonly Dictionary _useDelays = new(); + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnUseInHand); + } + + private void OnUseInHand(EntityUid uid, HandheldCameraComponent component, UseInHandEvent args) + { + if (args.Handled) + return; + + // Check use delay + if (_useDelays.TryGetValue(uid, out var lastUse)) + { + var timeSinceUse = _timing.CurTime - lastUse; + if (timeSinceUse.TotalSeconds < component.UseDelay) + { + var remaining = component.UseDelay - timeSinceUse.TotalSeconds; + _popup.PopupEntity($"Camera is recharging! ({remaining:F1}s)", uid, args.User); + return; + } + } + + // Take the photo! + _useDelays[uid] = _timing.CurTime; + + // Play the camera shutter sound + if (component.Sound != null) + _audio.PlayPvs(component.Sound, uid); + + // Spawn the photo item + var coords = Transform(args.User).Coordinates; + var photo = Spawn(component.PhotoPrototype, coords); + + // Try to put it in the user's hands + _hands.PickupOrDrop(args.User, photo); + + _popup.PopupEntity("You take a photo!", uid, args.User); + + args.Handled = true; + } +} diff --git a/Resources/Prototypes/Entities/Objects/Devices/camera.yml b/Resources/Prototypes/Entities/Objects/Devices/camera.yml new file mode 100644 index 0000000000000..222471ee228aa --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Devices/camera.yml @@ -0,0 +1,19 @@ +- type: entity + parent: BaseItem + id: HandheldCamera + name: camera + description: A handheld camera that can take photos. The photos can be placed on walls. + components: + - type: Sprite + sprite: Objects/Devices/camera.rsi + state: icon + - type: Item + sprite: Objects/Devices/camera.rsi + size: Small + - type: HandheldCamera + photoPrototype: Photo + sound: + path: /Audio/Machines/shutter.ogg + useDelay: 2 + - type: UseDelay + delay: 2 diff --git a/Resources/Prototypes/Entities/Objects/Devices/photo.yml b/Resources/Prototypes/Entities/Objects/Devices/photo.yml new file mode 100644 index 0000000000000..89e478f46aba9 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Devices/photo.yml @@ -0,0 +1,42 @@ +- type: entity + parent: BaseItem + id: Photo + name: photo + description: A photo taken with a camera. It can be placed on a wall. + components: + - type: Sprite + sprite: Objects/Devices/photo.rsi + state: icon + - type: Item + sprite: Objects/Devices/photo.rsi + size: Tiny + - type: SpawnItemsOnUse + items: + - id: PhotoFrame + uses: 1 + sound: + path: /Audio/Effects/poster_being_set.ogg + +- type: entity + parent: BaseSign + id: PhotoFrame + name: framed photo + description: A photo in a frame, mounted on the wall. + components: + - type: Sprite + sprite: Structures/Wallmounts/photo_frames.rsi + state: frame + - type: Damageable + damageContainer: StructuralInorganic + damageModifierSet: Card + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:PlaySoundBehavior + sound: + path: /Audio/Effects/glass_break1.ogg + - !type:DoActsBehavior + acts: ["Destruction"] diff --git a/Resources/Textures/Objects/Devices/camera.rsi/icon.png b/Resources/Textures/Objects/Devices/camera.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..261e2179e325ed8734d6987b5ae0a669e619360c GIT binary patch literal 206 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJsh%#5Ar*6y6C_v{Cy4Zv9Pn6k z=>PxVf5{nXbDX!-#WZvVF}uk+XevmwGcqzt6#8r3*pSHVSIuU_60st&WA<$6IeW9O z>;1B1xS3R9m?Y~oRlvZ=sA=>0xz_HJ;x=7oIJ5l3H0945Tlqp|9dr#!WEs3uUKH{@ zzHBNuY1W4o&lqOtv#*g+xE_>Z@Sw9mq^Bv+-~j_e>~Zy@#iHwLov>|pNjYH67jx;R0ECzF9;QUG_${j!?P QK&u%%UHx3vIVCg!03k3q!TFITc-li_vn%Z(;1#<+4rbc8{Ym#@>qSAs>y#E9m8ec}gpOjOvY`c-8DYG2Y zVm^sdpgOiYj4t9XM?|!qG%)fv2D0~9H6-XAxISY6o5TkJpb8C%0tSZi4?HT|+1>kr Pb~AXo`njxgN@xNAuxB|M literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Wallmounts/photo_frames.rsi/meta.json b/Resources/Textures/Structures/Wallmounts/photo_frames.rsi/meta.json new file mode 100644 index 0000000000000..e7b90a3eb38c0 --- /dev/null +++ b/Resources/Textures/Structures/Wallmounts/photo_frames.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Created for Solaris-14", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "frame" + } + ] +} From 3ee4f385b6fe1136f6f7ab3f63507f0092fac9ec Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 12 Dec 2025 00:59:06 +0000 Subject: [PATCH 3/3] Add camera usage documentation Co-authored-by: MWG-Logan <2997336+MWG-Logan@users.noreply.github.com> --- CAMERA_USAGE.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 CAMERA_USAGE.md diff --git a/CAMERA_USAGE.md b/CAMERA_USAGE.md new file mode 100644 index 0000000000000..798810201e780 --- /dev/null +++ b/CAMERA_USAGE.md @@ -0,0 +1,48 @@ +# Handheld Camera Usage Guide + +## Overview +The handheld camera is a device that allows you to take photos in-game and place them on walls as decorations. + +## How to Use + +### Taking Photos +1. Get a camera (spawn with admin commands: `/spawn HandheldCamera`) +2. Hold the camera in your hand +3. Press the use key (default: Z) to take a photo +4. A photo item will appear in your hands (or drop if hands are full) +5. Wait 2 seconds before taking another photo (cooldown) + +### Placing Photos on Walls +1. Hold a photo item in your hand +2. Press the use key (default: Z) to convert it to a framed photo +3. The framed photo will spawn at your location +4. Pick up the framed photo +5. Aim at a wall and click to place it (like placing posters or signs) + +## Technical Details + +### Entity IDs +- `HandheldCamera` - The camera item +- `Photo` - The photo item (before framing) +- `PhotoFrame` - The framed photo (wallmount) + +### Spawning Items +Use these commands as an admin: +- `/spawn HandheldCamera` - Spawn a camera +- `/spawn Photo` - Spawn a photo item +- `/spawn PhotoFrame` - Spawn a framed photo + +### Audio +- Camera uses: `/Audio/Machines/shutter.ogg` +- Photo framing: `/Audio/Effects/poster_being_set.ogg` +- Photo destruction: `/Audio/Effects/glass_break1.ogg` + +### Sprites +- Camera: `Objects/Devices/camera.rsi` +- Photo: `Objects/Devices/photo.rsi` +- Frame: `Structures/Wallmounts/photo_frames.rsi` + +## Notes +- Photos are placeholders with simple colored sprites +- Future enhancements could include actual screenshot capture +- Photos can be destroyed by dealing 10+ damage to them