Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions CAMERA_USAGE.md
Original file line number Diff line number Diff line change
@@ -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
28 changes: 28 additions & 0 deletions Content.Server/Camera/HandheldCameraComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Robust.Shared.Audio;

namespace Content.Server.Camera;

/// <summary>
/// A handheld camera that can take photos and spawn photo items.
/// </summary>
[RegisterComponent]
public sealed partial class HandheldCameraComponent : Component
{
/// <summary>
/// The entity prototype to spawn when a photo is taken.
/// </summary>
[DataField("photoPrototype")]
public string PhotoPrototype = "Photo";

/// <summary>
/// Sound to play when taking a photo.
/// </summary>
[DataField("sound")]
public SoundSpecifier? Sound = new SoundPathSpecifier("/Audio/Machines/shutter.ogg");

/// <summary>
/// Use delay in seconds to prevent spam.
/// </summary>
[DataField("useDelay")]
public float UseDelay = 2f;
}
60 changes: 60 additions & 0 deletions Content.Server/Camera/HandheldCameraSystem.cs
Original file line number Diff line number Diff line change
@@ -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<EntityUid, TimeSpan> _useDelays = new();

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<HandheldCameraComponent, UseInHandEvent>(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;
}
}
19 changes: 19 additions & 0 deletions Resources/Prototypes/Entities/Objects/Devices/camera.yml
Original file line number Diff line number Diff line change
@@ -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
42 changes: 42 additions & 0 deletions Resources/Prototypes/Entities/Objects/Devices/photo.yml
Original file line number Diff line number Diff line change
@@ -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"]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions Resources/Textures/Objects/Devices/camera.rsi/meta.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions Resources/Textures/Objects/Devices/photo.rsi/meta.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
Loading