Body camera (#585)

* Initial commit

* removed jumpsuit slot component, system

* sprite fixes

* Локализация для телевизора и роутера

* Removed separate router and tv for body cameras. Now they're public. Added localizations.

* +cargo buying, sprites finale
This commit is contained in:
Viktor
2024-01-03 21:10:08 +02:00
committed by Aviu00
parent 8011440c38
commit dfcfded62f
35 changed files with 397 additions and 2 deletions

View File

@@ -0,0 +1,10 @@
namespace Content.Server.SurveillanceCamera;
[RegisterComponent]
public sealed partial class SurveillanceBodyCameraComponent : Component
{
[DataField("wattage"), ViewVariables(VVAccess.ReadWrite)]
public float Wattage = 0.3f;
public bool lastState = false;
}

View File

@@ -4,7 +4,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
namespace Content.Server.SurveillanceCamera;
[RegisterComponent]
[Access(typeof(SurveillanceCameraSystem))]
[Access(typeof(SurveillanceCameraSystem), typeof(SurveillanceBodyCameraSystem))]
public sealed partial class SurveillanceCameraComponent : Component
{
// List of active viewers. This is for bookkeeping purposes,

View File

@@ -0,0 +1,116 @@
using Content.Server.Popups;
using Content.Server.PowerCell;
using Content.Shared.Clothing.EntitySystems;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Item;
using Content.Shared.PowerCell.Components;
using Content.Shared.Toggleable;
namespace Content.Server.SurveillanceCamera;
/// <summary>
/// This handles the bodycamera all itself. Activation, examine,init, powercell stuff.
/// </summary>
public sealed class SurveillanceBodyCameraSystem : EntitySystem
{
[Dependency] private readonly PowerCellSystem _powerCell = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly SurveillanceCameraSystem _surveillanceCameras = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedItemSystem _item = default!;
[Dependency] private readonly ClothingSystem _clothing = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SurveillanceBodyCameraComponent, ActivateInWorldEvent>(OnActivate);
SubscribeLocalEvent<SurveillanceBodyCameraComponent, PowerCellChangedEvent>(OnPowerCellChanged);
SubscribeLocalEvent<SurveillanceBodyCameraComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<SurveillanceBodyCameraComponent, ComponentInit>(OnInit);
}
public void OnInit(EntityUid uid, SurveillanceBodyCameraComponent comp, ComponentInit args)
{
if (!TryComp<SurveillanceCameraComponent>(uid, out var surComp))
return;
_surveillanceCameras.SetActive(uid, false, surComp);
surComp.NetworkSet = true;
AppearanceChange(uid, surComp.Active);
}
public override void Update(float frameTime)
{
var query = EntityQueryEnumerator<SurveillanceBodyCameraComponent>();
while (query.MoveNext(out var uid, out var cam))
{
if (!_powerCell.TryGetBatteryFromSlot(uid, out var battery))
continue;
if (!TryComp<SurveillanceCameraComponent>(uid, out var surComp))
continue;
if (!surComp.Active)
continue;
if (!battery.TryUseCharge(cam.Wattage * frameTime))
{
_surveillanceCameras.SetActive(uid, false, surComp);
AppearanceChange(uid, surComp.Active);
}
}
}
public void OnActivate(EntityUid uid, SurveillanceBodyCameraComponent comp, ActivateInWorldEvent args)
{
if (!TryComp<SurveillanceCameraComponent>(uid, out var surComp))
return;
if (!_powerCell.TryGetBatteryFromSlot(uid, out var battery))
return;
_surveillanceCameras.SetActive(uid, battery.CurrentCharge > comp.Wattage && !surComp.Active, surComp);
AppearanceChange(uid, surComp.Active);
var message = Loc.GetString(surComp.Active ? "surveillance-body-camera-on" : "surveillance-body-camera-off");
_popup.PopupEntity(message, args.User, args.User);
args.Handled = true;
}
public void OnPowerCellChanged(EntityUid uid, SurveillanceBodyCameraComponent comp, PowerCellChangedEvent args)
{
if (!TryComp<SurveillanceCameraComponent>(uid, out var surComp))
return;
if (args.Ejected)
{
_surveillanceCameras.SetActive(uid, false, surComp);
AppearanceChange(uid, surComp.Active);
}
}
public void OnExamine(EntityUid uid, SurveillanceBodyCameraComponent comp, ExaminedEvent args)
{
if (!TryComp<SurveillanceCameraComponent>(uid, out var surComp))
return;
if (args.IsInDetailsRange)
{
var message =
Loc.GetString(surComp.Active ? "surveillance-body-camera-on" : "surveillance-body-camera-off");
args.PushMarkup(message);
}
}
public void AppearanceChange(EntityUid uid, Boolean isActive)
{
if (TryComp<AppearanceComponent>(uid, out var appearance) &&
TryComp<ItemComponent>(uid, out var item))
{
_item.SetHeldPrefix(uid, isActive ? "on" : "off", false, item);
_clothing.SetEquippedPrefix(uid, isActive ? null : "off");
_appearance.SetData(uid, ToggleVisuals.Toggled, isActive, appearance);
}
}
}

View File

@@ -1,3 +1,4 @@
using Content.Shared.Clothing.EntitySystems;
using Content.Shared.Whitelist;
using Robust.Shared.Audio;
using Robust.Shared.Containers;

View File

@@ -14,3 +14,5 @@ ent-CrateSecurityBiosuit = ящик биокостюмов безопаснос
.desc = Содержит 2 костюма биологической защиты, гарантирующих, что никакая болезнь не отвлечет вас от ваших обязанностей. Для открытия требуется доступ СБ.
ent-CreateSecurityMindShield = ящик имплантов защиты разума
.desc = Содержит в себе 5 имплантов защиты разума. Чтобы открыть необходим уровень доступа Служба безопасности.
ent-CrateSecurityBodyCamera = ящик нагрудных камер
.desc = Содержит 10 нагрудных камер. Чтобы открыть необходим уровень доступа Служба безопасности.

View File

@@ -16,3 +16,6 @@ ent-BoxZiptie = коробка стяжек
ent-BoxForensicPad = коробка криминалистических пластинок
.desc = Коробка криминалистических пластинок.
.suffix = { "" }
ent-BoxBodyCamera = коробка бодикамер
.desc = Коробка нагрудных камер службы безопасности.
.suffix = { "" }

View File

@@ -10,3 +10,6 @@ ent-PortableFlasher = переносная вспышка
ent-DeskBell = настольный звонок
.desc = Краеугольный камень любой работы по обслуживанию клиентов. Вы испытываете непреодолимое желание позвонить в него.
.suffix = { "" }
ent-SurveillanceBodyCamera = бодикамера
.desc = Следи за собой! Или за другими. Или за всеми сразу. Или ни за кем. Как хочешь. Нательная камера, записывающая все, что происходит вокруг.
.suffix = { "" }

View File

@@ -31,6 +31,9 @@ ent-SurveillanceCameraRouterGeneral = { ent-SurveillanceCameraRouterBase }
ent-SurveillanceCameraWirelessRouterBase = маршрутизатор беспроводных камер
.desc = Маршрутизатор для беспроводных камер наблюдения. Он маршрутизирует. Возможно.
.suffix = { "" }
ent-SurveillanceCameraWirelessRouterSecurity = { ent-SurveillanceCameraWirelessRouterBase }
.suffix = Охранный
.desc = { ent-SurveillanceCameraWirelessRouterBase.desc }
ent-SurveillanceCameraWirelessRouterConstructed = { ent-SurveillanceCameraWirelessRouterBase }
.suffix = Построенный
.desc = { ent-SurveillanceCameraWirelessRouterBase.desc }

View File

@@ -9,3 +9,5 @@ surveillance-camera-monitor-ui-status-disconnected = Отключено
surveillance-camera-monitor-ui-no-subnets = Нет подсетей
surveillance-camera-setup = Настроить
surveillance-camera-setup-ui-set = Установить
surveillance-body-camera-on = Камера включена
surveillance-body-camera-off = Камера выключена

View File

@@ -77,3 +77,13 @@
cost: 1000
category: Security
group: market
- type: cargoProduct
id: SecurityBodycam
icon:
sprite: Objects/Specific/Security/body-camera.rsi
state: unpowered
product: CrateSecurityBodyCamera
cost: 2000
category: Security
group: market

View File

@@ -81,3 +81,20 @@
layers:
- state: box_security
- state: forensic
#White
- type: entity
name: body camera box
parent: BoxCardboard
id: BoxBodyCamera
description: A box full of body cameras.
components:
- type: StorageFill
contents:
- id: SurveillanceBodyCamera
amount: 5
- type: Sprite
layers:
- state: box_security
- state: bodycam

View File

@@ -97,5 +97,14 @@
amount: 4
- id: TrackingImplanter
amount: 4
# Cosmetic Crates
- type: entity
id: CrateSecurityBodyCamera
parent: CrateSecgear
components:
- type: StorageFill
contents:
- id: BoxBodyCamera
amount: 2

View File

@@ -21,6 +21,7 @@
- id: ClothingOuterHardsuitWarden
- id: HoloprojectorSecurity
- id: ClothingEyesHudSecurity
- id: BoxBodyCamera
- type: entity
id: LockerWardenFilled
@@ -45,6 +46,7 @@
- id: DoorRemoteArmory
- id: HoloprojectorSecurity
- id: ClothingEyesHudSecurity
- id: BoxBodyCamera
- type: entity
id: LockerSecurityFilled

View File

@@ -0,0 +1,73 @@
- type: entity
parent: BaseItem
id: SurveillanceBodyCamera
name: Security Body Camera
description: MP3 player sized pocket camera for security officers.
components:
- type: SurveillanceBodyCamera
- type: Sprite # Sprites section
sprite: Objects/Specific/Security/body-camera.rsi
layers:
- state: unpowered
- state: active-animated
map: ["active"]
visible: false
- type: Clothing
sprite: Objects/Specific/Security/body-camera.rsi
quickEquip: false
slots:
- neck
- type: ToggleableLightVisuals
spriteLayer: active
inhandVisuals:
left:
- state: inhand-left-active
shader: unshaded
right:
- state: inhand-left-active
shader: unshaded
- type: Item
heldPrefix: off
sprite: Objects/Specific/Security/body-camera.rsi
- type: Appearance
- type: GenericVisualizer
visuals:
enum.ToggleVisuals.Toggled:
enum.ToggleVisuals.Layer:
True: { state: active-animated }
False: { state: unpowered } # END
- type: Eye
- type: WirelessNetworkConnection
range: 300
- type: ActiveListener
range: 5
- type: SurveillanceCameraMicrophone
blacklist:
components:
- SurveillanceCamera
- SurveillanceCameraMonitor
- RadioSpeaker
- type: DeviceNetwork
deviceNetId: Wireless
receiveFrequencyId: SurveillanceCameraSecurity
transmitFrequencyId: SurveillanceCamera
- type: SurveillanceCamera
setupAvailableNetworks:
- SurveillanceCameraSecurity
- type: PowerCellSlot
cellSlotId: cell_slot
- type: ContainerContainer
containers:
cell_slot: !type:ContainerSlot
- type: ItemSlots
slots:
cell_slot:
name: power-cell-slot-component-slot-name-default
startingItem: PowerCellMedium
- type: UserInterface
interfaces:
- key: enum.SurveillanceCameraSetupUiKey.Camera
type: SurveillanceCameraSetupBoundUi
- type: Tag
tags:
- SurveillanceBodyCamera

View File

@@ -135,6 +135,7 @@
- type: SurveillanceCameraRouter
setupAvailableNetworks:
- SurveillanceCameraEntertainment
- SurveillanceCameraSecurity
- type: entity
parent: SurveillanceCameraWirelessRouterBase
@@ -143,3 +144,12 @@
components:
- type: SurveillanceCameraRouter
subnetFrequency: SurveillanceCameraEntertainment
# Body Camera Separate Router
- type: entity
parent: SurveillanceCameraWirelessRouterBase
id: SurveillanceCameraWirelessRouterSecurity
suffix: Security
components:
- type: SurveillanceCameraRouter
subnetFrequency: SurveillanceCameraSecurity

View File

@@ -42,3 +42,6 @@
- type: Tag
id: Telecrystal
- type: Tag
id: SurveillanceBodyCamera

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

View File

@@ -0,0 +1,14 @@
{
"version" : 1,
"license" : "CC-BY-SA-3.0",
"copyright" : "Copyright (c) White Dream Space Station 14",
"size" : {
"x" : 32,
"y" : 32
},
"states" : [
{
"name" : "camera"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,108 @@
{
"version" : 1,
"license" : "CC-BY-SA-3.0",
"copyright" : "Copyright (c) White Dream Space Station 14",
"size" : {
"x" : 32,
"y" : 32
},
"states" : [
{
"name" : "unpowered"
},
{
"name" : "active"
},
{
"name" : "active-animated",
"delays" : [
[
1,
2
]
]
},
{
"name" : "off-inhand-right",
"directions" : 4
},
{
"name" : "off-inhand-left",
"directions" : 4
},
{
"name" : "on-inhand-right",
"directions" : 4,
"delays" : [
[
1,
2
],
[
1,
2
],
[
1,
2
],
[
1,
2
]
]
},
{
"name" : "on-inhand-left",
"directions" : 4,
"delays" : [
[
1,
2
],
[
1,
2
],
[
1,
2
],
[
1,
2
]
]
},
{
"name": "equipped-NECK",
"directions": 4,
"delays" : [
[
1,
2
],
[
1,
2
],
[
1,
2
],
[
1,
2
]
]
},
{
"name" : "off-equipped-NECK",
"directions" : 4
},
{
"name": "equipped-INNERCLOTHING",
"directions" : 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -205,6 +205,9 @@
},
{
"name": "vials"
},
{
"name": "bodycam"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

View File

@@ -7,6 +7,12 @@
"y": 32
},
"states": [
{
"name" : "bodycammonitor"
},
{
"name" : "bodycamprogram"
},
{
"name": "ai-fixer",
"directions": 4