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 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 0000000000000..261e2179e325e Binary files /dev/null and b/Resources/Textures/Objects/Devices/camera.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Devices/camera.rsi/meta.json b/Resources/Textures/Objects/Devices/camera.rsi/meta.json new file mode 100644 index 0000000000000..932bffe6832bd --- /dev/null +++ b/Resources/Textures/Objects/Devices/camera.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": "icon" + } + ] +} diff --git a/Resources/Textures/Objects/Devices/photo.rsi/icon.png b/Resources/Textures/Objects/Devices/photo.rsi/icon.png new file mode 100644 index 0000000000000..3381a931a357c Binary files /dev/null and b/Resources/Textures/Objects/Devices/photo.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Devices/photo.rsi/meta.json b/Resources/Textures/Objects/Devices/photo.rsi/meta.json new file mode 100644 index 0000000000000..932bffe6832bd --- /dev/null +++ b/Resources/Textures/Objects/Devices/photo.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": "icon" + } + ] +} diff --git a/Resources/Textures/Structures/Wallmounts/photo_frames.rsi/frame.png b/Resources/Textures/Structures/Wallmounts/photo_frames.rsi/frame.png new file mode 100644 index 0000000000000..2b94a2856777d Binary files /dev/null and b/Resources/Textures/Structures/Wallmounts/photo_frames.rsi/frame.png differ 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" + } + ] +}