From 3af390918ca49dbd0d1672a01d596d70c59b0503 Mon Sep 17 00:00:00 2001
From: NovaBot <154629622+NovaBot13@users.noreply.github.com>
Date: Fri, 5 Apr 2024 03:44:41 -0400
Subject: [PATCH] [MIRROR] New operative reinforcement option: Intelligence
Overwatch Agent (#1819)
* New operative reinforcement option: Intelligence Overwatch Agent (#82307)
## About The Pull Request
Introducing a new Nuclear Operative reinforcement option: The Overwatch
Intelligence Agent.
Equipped with multi-hudglasses, they have an advanced camera console,
station alerts, and bodycams of every operative! If something can be
known, they will know about it.
They can also remotely pilot your ship. Finally, everyone can ride in
the Steel Rain without getting stuck on the station!
This role spawns in the formerly unused outpost just north of the nukie
base. With a few shelves of supplies and some tools in the back room,
they can set up their workplace however they like. This also gives them
something to work on while they wait for the operatives to gear up.
![image](https://github.com/tgstation/tgstation/assets/28870487/4a39ec5f-0578-4825-8c6b-cc4db47bf726)
As you can see, it's rather cramped and the lights are quite dim in the
backroom. Set it up however you like, this is how I did mine:
![image](https://github.com/tgstation/tgstation/assets/28870487/f80b65fa-dcd1-425e-a6ce-c0ed94a8a3a5)
Total price? 12 TC per agent. It might get a bit cramped, but you could
buy a second to make sure the first guy doesn't get lonely!
This turned into a 30-commit ugly because the bodycams were originally
meant to be accomplished via a refactoring of the spyglass kit. Big
mistake that made me shelve the project -- until Melbert's simple
bodycam component conveniently did exactly what I needed in a much
simpler way.
## Why It's Good For The Game
Having a "guy in the chair" for your kickass murder operator squad
enables more brainy strategizing, and is thematically sound. Also,
nukies have the opportunity to bring in another player to participate in
the fun!
## Changelog
:cl: Rhials
add: Nuclear Operatives now purchase an Intelligence Agent, who can
watch cameras and bodycams, move the shuttle, and provide radio support.
Only 12 Telecrystals!
/:cl:
* New operative reinforcement option: Intelligence Overwatch Agent
---------
Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com>
---
_maps/templates/lazy_templates/nukie_base.dmm | 229 +++++++++++++++---
code/__DEFINES/cameranets.dm | 1 +
.../dcs/signals/signals_operatives.dm | 2 +
code/__DEFINES/role_preferences.dm | 1 +
code/_globalvars/lists/mapping.dm | 1 +
code/game/objects/effects/landmarks.dm | 10 +
.../antagonists/_common/antag_spawner.dm | 33 ++-
.../antagonists/nukeop/datums/operative.dm | 1 -
.../nukeop/datums/operative_support.dm | 54 +++++
.../nukeop/datums/operative_team.dm | 4 +
.../nukeop/equipment/overwatch_tools.dm | 46 ++++
code/modules/antagonists/nukeop/outfits.dm | 21 ++
.../carbon/human/species_types/plasmamen.dm | 1 +
code/modules/power/cable.dm | 3 +
code/modules/shuttle/syndicate.dm | 1 +
code/modules/uplink/uplink_items/nukeops.dm | 8 +
tgstation.dme | 3 +
17 files changed, 379 insertions(+), 40 deletions(-)
create mode 100644 code/__DEFINES/dcs/signals/signals_operatives.dm
create mode 100644 code/modules/antagonists/nukeop/datums/operative_support.dm
create mode 100644 code/modules/antagonists/nukeop/equipment/overwatch_tools.dm
diff --git a/_maps/templates/lazy_templates/nukie_base.dmm b/_maps/templates/lazy_templates/nukie_base.dmm
index 63c0fc2b168..059d27000d0 100644
--- a/_maps/templates/lazy_templates/nukie_base.dmm
+++ b/_maps/templates/lazy_templates/nukie_base.dmm
@@ -152,6 +152,10 @@
},
/turf/open/floor/plating,
/area/centcom/syndicate_mothership/control)
+"bZ" = (
+/obj/effect/decal/cleanable/fuel_pool,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"cl" = (
/turf/open/lava/plasma/ice_moon,
/area/centcom/syndicate_mothership/control)
@@ -349,6 +353,15 @@
},
/turf/open/floor/iron/dark/textured_large,
/area/centcom/syndicate_mothership/control)
+"dZ" = (
+/obj/machinery/light/small/directional/north,
+/obj/effect/decal/cleanable/cobweb/cobweb2,
+/obj/structure/closet/crate/bin,
+/obj/effect/decal/cleanable/fuel_pool,
+/obj/item/binoculars,
+/obj/item/stack/sheet/animalhide/carp/five,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"ec" = (
/obj/structure/sign/poster/contraband/cc64k_ad,
/turf/closed/indestructible/syndicate,
@@ -485,9 +498,27 @@
/turf/open/floor/iron/freezer,
/area/centcom/syndicate_mothership/control)
"fw" = (
-/obj/item/kirbyplants/random,
+/obj/structure/frame/computer,
+/obj/effect/decal/cleanable/cobweb,
+/obj/structure/sign/poster/contraband/syndiemoth/directional/north,
/turf/open/floor/mineral/plastitanium,
/area/centcom/syndicate_mothership)
+"fx" = (
+/obj/machinery/light/small/dim/directional/west,
+/obj/structure/rack,
+/obj/effect/turf_decal/box/white,
+/obj/item/stack/cable_coil/thirty{
+ pixel_x = 4;
+ pixel_y = -2
+ },
+/obj/item/circuitboard/computer/stationalert{
+ pixel_y = 1;
+ pixel_x = 3
+ },
+/obj/item/stack/cable_coil/thirty,
+/obj/item/circuitboard/computer/advanced_camera,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"fy" = (
/obj/structure/lattice/catwalk,
/obj/effect/turf_decal/stripes/line{
@@ -536,6 +567,8 @@
/obj/structure/frame/computer{
dir = 1
},
+/obj/effect/decal/cleanable/dirt/dust,
+/obj/machinery/computer/security/telescreen/entertainment/directional/south,
/turf/open/floor/mineral/plastitanium,
/area/centcom/syndicate_mothership)
"gf" = (
@@ -740,6 +773,20 @@
/obj/item/melee/powerfist,
/turf/open/floor/mineral/plastitanium/red,
/area/centcom/syndicate_mothership/expansion_bombthreat)
+"id" = (
+/obj/structure/extinguisher_cabinet/directional/south,
+/obj/item/storage/fancy/donut_box,
+/obj/structure/rack,
+/obj/item/food/syndicake{
+ pixel_x = -1;
+ pixel_y = -1
+ },
+/obj/item/food/syndicake{
+ pixel_x = 1;
+ pixel_y = 1
+ },
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"ig" = (
/obj/effect/turf_decal/siding/wideplating{
dir = 1
@@ -1033,6 +1080,10 @@
/obj/structure/sign/poster/contraband/power/directional/north,
/turf/open/floor/mineral/plastitanium,
/area/centcom/syndicate_mothership/expansion_chemicalwarfare)
+"lx" = (
+/obj/machinery/light/small/dim/directional/east,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"lA" = (
/obj/machinery/door/window/survival_pod/left/directional/west{
name = "Toilet Door"
@@ -1281,6 +1332,22 @@
dir = 8
},
/area/centcom/syndicate_mothership/control)
+"oN" = (
+/obj/structure/rack,
+/obj/item/poster/random_contraband{
+ pixel_x = 3;
+ pixel_y = 4
+ },
+/obj/item/poster/random_contraband{
+ pixel_x = 3;
+ pixel_y = 8
+ },
+/obj/item/poster/random_contraband{
+ pixel_x = 1
+ },
+/obj/effect/decal/cleanable/cobweb,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"oR" = (
/obj/structure/table/wood,
/obj/item/storage/box/donkpockets,
@@ -1367,6 +1434,7 @@
/obj/structure/chair/greyscale{
dir = 4
},
+/obj/effect/landmark/start/nukeop_overwatch,
/turf/open/floor/mineral/plastitanium,
/area/centcom/syndicate_mothership)
"pU" = (
@@ -1641,6 +1709,7 @@
/area/centcom/syndicate_mothership/control)
"sv" = (
/obj/machinery/light/small/directional/north,
+/obj/effect/decal/cleanable/dirt/dust,
/turf/open/floor/mineral/plastitanium,
/area/centcom/syndicate_mothership)
"sE" = (
@@ -1731,6 +1800,17 @@
/obj/machinery/portable_atmospherics/scrubber,
/turf/open/floor/mineral/plastitanium/red,
/area/centcom/syndicate_mothership/expansion_bombthreat)
+"tB" = (
+/obj/effect/turf_decal/siding/wideplating{
+ dir = 1
+ },
+/obj/effect/turf_decal/siding/wideplating,
+/obj/machinery/door/airlock/hatch{
+ name = "Workout Room"
+ },
+/obj/effect/mapping_helpers/airlock/access/all/syndicate/general,
+/turf/open/floor/mineral/plastitanium,
+/area/centcom/syndicate_mothership)
"tJ" = (
/obj/effect/light_emitter{
set_cap = 1;
@@ -2046,6 +2126,10 @@
dir = 8
},
/area/centcom/syndicate_mothership/control)
+"yA" = (
+/obj/effect/decal/cleanable/insectguts,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"yB" = (
/obj/machinery/light/small/directional/north,
/turf/open/misc/asteroid/snow/icemoon,
@@ -2100,12 +2184,12 @@
/area/centcom/syndicate_mothership/control)
"zy" = (
/obj/structure/table/reinforced/plasmarglass,
-/obj/item/pen{
- pixel_x = 6;
- pixel_y = 5
+/obj/item/flashlight/lamp/green{
+ pixel_y = 9;
+ pixel_x = 5
},
-/obj/item/folder/red{
- pixel_x = -5
+/obj/item/circuitboard/computer/overwatch{
+ pixel_x = -2
},
/turf/open/floor/mineral/plastitanium,
/area/centcom/syndicate_mothership)
@@ -3037,6 +3121,10 @@
},
/turf/open/floor/mineral/plastitanium/red,
/area/centcom/syndicate_mothership/expansion_bioterrorism)
+"Jx" = (
+/obj/effect/decal/cleanable/plastic,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"JJ" = (
/obj/effect/turf_decal/siding/thinplating_new/dark{
dir = 5
@@ -3318,6 +3406,19 @@
/obj/structure/noticeboard/directional/east,
/turf/open/floor/mineral/plastitanium,
/area/centcom/syndicate_mothership/expansion_bioterrorism)
+"MA" = (
+/obj/item/stack/sheet/iron/fifty{
+ pixel_x = -3;
+ pixel_y = 3
+ },
+/obj/structure/rack,
+/obj/item/stack/sheet/glass/fifty{
+ pixel_y = 3;
+ pixel_x = 5
+ },
+/obj/item/storage/toolbox/mechanical,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"MD" = (
/obj/machinery/door/airlock/maintenance/external{
name = "Bunk Room 1"
@@ -3483,6 +3584,14 @@
pixel_x = -2;
pixel_y = 6
},
+/obj/item/paper/fluff/overwatch{
+ pixel_y = 8;
+ pixel_x = -2
+ },
+/obj/item/toy/figure/syndie{
+ pixel_x = 5
+ },
+/obj/structure/sign/clock/directional/north,
/turf/open/floor/mineral/plastitanium,
/area/centcom/syndicate_mothership)
"Ov" = (
@@ -3706,6 +3815,14 @@
/obj/effect/turf_decal/tile/bar/opposingcorners,
/turf/open/floor/iron,
/area/centcom/syndicate_mothership/control)
+"QD" = (
+/obj/item/flashlight{
+ pixel_y = 6
+ },
+/obj/structure/rack,
+/obj/item/flashlight,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"QJ" = (
/obj/effect/turf_decal/stripes/line{
dir = 4
@@ -3728,10 +3845,39 @@
},
/turf/open/misc/asteroid/snow/icemoon,
/area/centcom/syndicate_mothership/control)
+"QS" = (
+/obj/item/soap/syndie,
+/obj/structure/rack,
+/obj/item/storage/fancy/cigarettes/cigpack_syndicate{
+ pixel_x = 5;
+ pixel_y = 2
+ },
+/obj/effect/spawner/random/entertainment/lighter,
+/obj/machinery/light/small/directional/north,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"Rd" = (
/obj/structure/cable,
/turf/open/floor/catwalk_floor/iron,
/area/centcom/syndicate_mothership/control)
+"Rn" = (
+/obj/item/circuitboard/computer/syndicate_shuttle{
+ pixel_x = 2;
+ pixel_y = 3
+ },
+/obj/structure/rack,
+/obj/item/stack/cable_coil/thirty{
+ pixel_x = -2;
+ pixel_y = -1
+ },
+/obj/effect/turf_decal/box/white,
+/obj/item/circuitboard/computer/syndicate_shuttle_docker,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
+"Rr" = (
+/obj/effect/decal/cleanable/dirt,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"Rs" = (
/obj/effect/turf_decal/siding/thinplating_new/dark{
dir = 1
@@ -3830,6 +3976,11 @@
},
/turf/open/floor/mineral/plastitanium/red,
/area/centcom/syndicate_mothership/expansion_bombthreat)
+"St" = (
+/obj/effect/decal/cleanable/fuel_pool,
+/obj/machinery/light_switch/directional/east,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"Su" = (
/obj/structure/chair/stool/bar/directional/west,
/obj/effect/turf_decal/siding/thinplating/dark{
@@ -4276,6 +4427,11 @@
/obj/machinery/light/cold/directional/east,
/turf/open/floor/catwalk_floor/iron_dark,
/area/centcom/syndicate_mothership/control)
+"XO" = (
+/obj/effect/decal/cleanable/dirt,
+/obj/effect/decal/cleanable/fuel_pool,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"XQ" = (
/obj/effect/turf_decal/siding/wideplating/dark,
/obj/effect/turf_decal/siding/wideplating/dark{
@@ -4375,6 +4531,14 @@
},
/turf/open/misc/asteroid/snow/icemoon,
/area/centcom/syndicate_mothership/control)
+"YT" = (
+/obj/structure/rack,
+/obj/effect/turf_decal/box/white,
+/obj/item/storage/medkit/emergency,
+/obj/item/tank/internals/emergency_oxygen/double,
+/obj/item/clothing/mask/gas/syndicate,
+/turf/open/floor/mineral/plastitanium/red,
+/area/centcom/syndicate_mothership)
"YX" = (
/obj/structure/railing{
dir = 6
@@ -4491,6 +4655,7 @@
/turf/open/floor/plating/snowed/icemoon,
/area/centcom/syndicate_mothership/control)
"ZX" = (
+/obj/effect/decal/cleanable/dirt/dust,
/turf/open/floor/mineral/plastitanium,
/area/centcom/syndicate_mothership)
"ZZ" = (
@@ -5254,9 +5419,9 @@ sU
sU
sU
sU
-sU
-sU
-sU
+Ye
+Ye
+Ye
Ye
Ye
Ye
@@ -5356,13 +5521,13 @@ sU
sU
sU
sU
-sU
-sU
Ye
Ye
Kq
Kq
Kq
+Kq
+Kq
Ye
KU
KU
@@ -5458,12 +5623,12 @@ sU
sU
sU
sU
-sU
-Ye
Ye
Kq
Kq
-Xk
+YT
+fx
+Rn
Kq
Kq
KU
@@ -5561,12 +5726,12 @@ sU
sU
sU
Ye
-Ye
Kq
-Kq
-Xk
-Xk
+oN
+Jx
Xk
+Rr
+MA
Kq
Kq
uT
@@ -5664,12 +5829,12 @@ sU
sU
Ye
Kq
-Kq
-Xk
-Xk
-Xk
+QS
Xk
Xk
+bZ
+Rr
+id
Kq
Kq
uT
@@ -5766,12 +5931,12 @@ sU
sU
Ye
Kq
-Xk
-Xk
-Xk
-Xk
-Xk
-Xk
+QD
+Rr
+lx
+bZ
+St
+XO
Xk
Kq
Kq
@@ -5871,12 +6036,12 @@ Kq
Kq
Kq
Kq
-lg
+tB
Kq
+dZ
Xk
-Xk
-Xk
-Kq
+yA
+tO
uT
ds
HW
diff --git a/code/__DEFINES/cameranets.dm b/code/__DEFINES/cameranets.dm
index 1dea0cd18ae..15b4493738e 100644
--- a/code/__DEFINES/cameranets.dm
+++ b/code/__DEFINES/cameranets.dm
@@ -26,3 +26,4 @@
#define CAMERANET_NETWORK_BAR "bar"
#define CAMERANET_NETWORK_INTERROGATION "interrogation"
#define CAMERANET_NETWORK_ABDUCTOR "abductor"
+#define OPERATIVE_CAMERA_NET "operative"
diff --git a/code/__DEFINES/dcs/signals/signals_operatives.dm b/code/__DEFINES/dcs/signals/signals_operatives.dm
new file mode 100644
index 00000000000..12a3bed4668
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_operatives.dm
@@ -0,0 +1,2 @@
+/// For when a new teammate is added to a nukie team
+#define COMSIG_NUKE_TEAM_ADDITION "nuke_team_addition"
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index abc272d0482..270b997e367 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -89,6 +89,7 @@
#define ROLE_WIZARD_APPRENTICE "apprentice"
#define ROLE_SYNDICATE_MONKEY "Syndicate Monkey Agent"
#define ROLE_CONTRACTOR_SUPPORT "Contractor Support Unit"
+#define ROLE_OPERATIVE_OVERWATCH "Operative Overwatch Agent"
#define ROLE_SYNDICATE_SABOBORG "Syndicate Sabotage Cyborg"
#define ROLE_SYNDICATE_MEDBORG "Syndicate Medical Cyborg"
#define ROLE_SYNDICATE_ASSAULTBORG "Syndicate Assault Cyborg"
diff --git a/code/_globalvars/lists/mapping.dm b/code/_globalvars/lists/mapping.dm
index 8719f45f18f..45f59fb4475 100644
--- a/code/_globalvars/lists/mapping.dm
+++ b/code/_globalvars/lists/mapping.dm
@@ -111,6 +111,7 @@ GLOBAL_LIST_EMPTY(gorilla_start)
GLOBAL_LIST_EMPTY(wizardstart)
GLOBAL_LIST_EMPTY(nukeop_start)
GLOBAL_LIST_EMPTY(nukeop_leader_start)
+GLOBAL_LIST_EMPTY(nukeop_overwatch_start)
GLOBAL_LIST_EMPTY(newplayer_start)
GLOBAL_LIST_EMPTY(prisonwarp) //admin prisoners go to these
GLOBAL_LIST_EMPTY(holdingfacility) //captured people go here (ninja energy net)
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index ac0d756e5fa..76522819d08 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -298,6 +298,16 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark)
GLOB.nukeop_leader_start += loc
return INITIALIZE_HINT_QDEL
+/obj/effect/landmark/start/nukeop_overwatch
+ name = "nukeop overwatch"
+ icon = 'icons/effects/landmarks_static.dmi'
+ icon_state = "snukeop_leader_spawn"
+
+/obj/effect/landmark/start/nukeop_overwatch/Initialize(mapload)
+ ..()
+ GLOB.nukeop_overwatch_start += loc
+ return INITIALIZE_HINT_QDEL
+
// Must be immediate because players will
// join before SSatom initializes everything.
INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm
index 2719c576a5e..8be6abe98ea 100644
--- a/code/modules/antagonists/_common/antag_spawner.dm
+++ b/code/modules/antagonists/_common/antag_spawner.dm
@@ -103,11 +103,13 @@
/// The applied outfit
var/datum/outfit/syndicate/outfit = /datum/outfit/syndicate/reinforcement
/// The antag datum applied
- var/datum/antagonist/nukeop/reinforcement/antag_datum = /datum/antagonist/nukeop/reinforcement
+ var/antag_datum = /datum/antagonist/nukeop/reinforcement
/// Style used by the droppod
var/pod_style = STYLE_SYNDICATE
/// Do we use a random subtype of the outfit?
var/use_subtypes = TRUE
+ /// Where do we land our pod?
+ var/turf/spawn_location
/obj/item/antag_spawner/nuke_ops/proc/check_usability(mob/user)
if(used)
@@ -143,7 +145,6 @@
/obj/item/antag_spawner/nuke_ops/spawn_antag(client/our_client, turf/T, kind, datum/mind/user)
var/mob/living/carbon/human/nukie = new()
- var/obj/structure/closet/supplypod/pod = setup_pod()
our_client.prefs.safe_transfer_prefs_to(nukie, is_antag = TRUE)
nukie.ckey = our_client.key
var/datum/mind/op_mind = nukie.mind
@@ -152,15 +153,33 @@
else
nukie.forceMove(locate(1,1,1))
- antag_datum = new()
-
- antag_datum.nukeop_outfit = use_subtypes ? pick(subtypesof(outfit)) : outfit
+ var/new_datum = new antag_datum()
var/datum/antagonist/nukeop/creator_op = user.has_antag_datum(/datum/antagonist/nukeop, TRUE)
- op_mind.add_antag_datum(antag_datum, creator_op ? creator_op.get_team() : null)
+ op_mind.add_antag_datum(new_datum, creator_op ? creator_op.get_team() : null)
op_mind.special_role = special_role_name
+
+ if(outfit)
+ var/datum/antagonist/nukeop/nukie_datum = op_mind.has_antag_datum(antag_datum)
+ nukie_datum.nukeop_outfit = use_subtypes ? pick(subtypesof(outfit)) : outfit
+
+ var/obj/structure/closet/supplypod/pod = setup_pod()
nukie.forceMove(pod)
- new /obj/effect/pod_landingzone(get_turf(src), pod)
+ new /obj/effect/pod_landingzone(spawn_location ? spawn_location : get_turf(src), pod)
+
+/obj/item/antag_spawner/nuke_ops/overwatch
+ name = "overwatch support beacon"
+ desc = "Assigns an Overwatch Intelligence Agent to your operation. Stationed at their own remote outpost, they can view station cameras, alarms, and even move the Infiltrator shuttle! \
+ Also, all members of your operation will recieve body cameras that they can view your progress from."
+ special_role_name = ROLE_OPERATIVE_OVERWATCH
+ outfit = /datum/outfit/syndicate/support
+ use_subtypes = FALSE
+ antag_datum = /datum/antagonist/nukeop/support
+
+/obj/item/antag_spawner/nuke_ops/overwatch/Initialize(mapload)
+ . = ..()
+ if(length(GLOB.nukeop_overwatch_start)) //Otherwise, it will default to the datum's spawn point anyways
+ spawn_location = pick(GLOB.nukeop_overwatch_start)
//////CLOWN OP
/obj/item/antag_spawner/nuke_ops/clown
diff --git a/code/modules/antagonists/nukeop/datums/operative.dm b/code/modules/antagonists/nukeop/datums/operative.dm
index 516108c5725..3b4c9fa4da7 100644
--- a/code/modules/antagonists/nukeop/datums/operative.dm
+++ b/code/modules/antagonists/nukeop/datums/operative.dm
@@ -46,7 +46,6 @@
var/datum/component/uplink/uplink = owner.find_syndicate_uplink()
if (uplink)
uplink.uplink_handler.add_telecrystals(extra_tc)
-
var/datum/component/uplink/uplink = owner.find_syndicate_uplink()
if(uplink)
var/datum/team/nuclear/nuke_team = get_team()
diff --git a/code/modules/antagonists/nukeop/datums/operative_support.dm b/code/modules/antagonists/nukeop/datums/operative_support.dm
new file mode 100644
index 00000000000..c9ea12b63c5
--- /dev/null
+++ b/code/modules/antagonists/nukeop/datums/operative_support.dm
@@ -0,0 +1,54 @@
+/datum/antagonist/nukeop/support
+ name = ROLE_OPERATIVE_OVERWATCH
+ show_to_ghosts = TRUE
+ send_to_spawnpoint = TRUE
+ nukeop_outfit = /datum/outfit/syndicate/support
+
+/datum/antagonist/nukeop/support/greet()
+ owner.current.playsound_local(get_turf(owner.current), 'sound/machines/printer.ogg', 100, 0, use_reverb = FALSE)
+ to_chat(owner, span_big("You are a [name]! You've been temporarily assigned to provide camera overwatch and manage communications for a nuclear operative team!"))
+ to_chat(owner, span_red("Use your tools to set up your equipment however you like, but do NOT attempt to leave your outpost."))
+ owner.announce_objectives()
+
+/datum/antagonist/nukeop/support/on_gain()
+ ..()
+ for(var/datum/mind/teammate_mind in nuke_team.members)
+ var/mob/living/our_teammate = teammate_mind.current
+ our_teammate.AddComponent( \
+ /datum/component/simple_bodycam, \
+ camera_name = "operative bodycam", \
+ c_tag = "[our_teammate.real_name]", \
+ network = OPERATIVE_CAMERA_NET, \
+ emp_proof = FALSE, \
+ )
+ our_teammate.playsound_local(get_turf(owner.current), 'sound/weapons/egloves.ogg', 100, 0)
+ to_chat(our_teammate, span_notice("A Syndicate Overwatch Intelligence Agent has been assigned to your team. Smile, you're on camera!"))
+
+ RegisterSignal(nuke_team, COMSIG_NUKE_TEAM_ADDITION, PROC_REF(late_bodycam))
+
+ owner.current.grant_language(/datum/language/codespeak)
+
+/datum/antagonist/nukeop/support/get_spawnpoint()
+ return pick(GLOB.nukeop_overwatch_start)
+
+/datum/antagonist/nukeop/support/forge_objectives()
+ var/datum/objective/overwatch/objective = new
+ objective.owner = owner
+ objectives += objective
+
+/datum/antagonist/nukeop/support/proc/late_bodycam(datum/source, mob/living/new_teammate)
+ SIGNAL_HANDLER
+ new_teammate.AddComponent( \
+ /datum/component/simple_bodycam, \
+ camera_name = "operative bodycam", \
+ c_tag = "[new_teammate.real_name]", \
+ network = OPERATIVE_CAMERA_NET, \
+ emp_proof = FALSE, \
+ )
+ to_chat(new_teammate, span_notice("You have been equipped with a bodycam, viewable by your Overwatch Intelligence Agent. Make sure to show them a good performance!"))
+
+/datum/objective/overwatch
+ explanation_text = "Provide intelligence support and overwatch to your operative team!"
+
+/datum/objective/overwatch/check_completion()
+ return GLOB.station_was_nuked
diff --git a/code/modules/antagonists/nukeop/datums/operative_team.dm b/code/modules/antagonists/nukeop/datums/operative_team.dm
index 9bec3b0fcf0..3345f3cf4d2 100644
--- a/code/modules/antagonists/nukeop/datums/operative_team.dm
+++ b/code/modules/antagonists/nukeop/datums/operative_team.dm
@@ -313,5 +313,9 @@
return TRUE
return FALSE
+/datum/team/nuclear/add_member(datum/mind/new_member)
+ ..()
+ SEND_SIGNAL(src, COMSIG_NUKE_TEAM_ADDITION, new_member.current)
+
#undef SPAWN_AT_BASE
#undef SPAWN_AT_INFILTRATOR
diff --git a/code/modules/antagonists/nukeop/equipment/overwatch_tools.dm b/code/modules/antagonists/nukeop/equipment/overwatch_tools.dm
new file mode 100644
index 00000000000..852c0d7d32f
--- /dev/null
+++ b/code/modules/antagonists/nukeop/equipment/overwatch_tools.dm
@@ -0,0 +1,46 @@
+///One of the special items that spawns in the overwatch agent's room.
+/obj/item/paper/fluff/overwatch
+ name = "OVERWATCH NOTES #1"
+ color = COLOR_RED
+ desc = "A note from Syndicate leadership regarding your new job. You should read this!"
+ default_raw_text = @{"
+Congratulations! You have been picked to be the Sole Survivor of an anti-Nanotrasen suicide mission!
+We're kidding of course, these types of missions tend to have abnormally high survival rates. I guess that says a lot about who your team will be going up against.
+
+You've been assigned to provide intelligence support to the ground-pounders carrying out the operation.
+Each operative has been equipped with a bodycam that can be accessed via your Overwatch Camera Console.
+Additionally, you have been given the boards to access station alerts, cameras, and remotely control the infiltrator shuttle.
+
+Feel free to set up your workplace however you like. We've provided the tools and boards in your backroom.
+
+Happy hunting!
+ "}
+
+/obj/machinery/computer/security/overwatch
+ name = "overwatch camera console"
+ desc = "Allows you to view members of your operative team via their bodycam feeds. We call them 'bodycams', but they're actually a swarm of tiny, near-imperceptible camera drones that follow each target. \
+ It is believed that adversaries either don't notice the drones, or avoid attacking them in hopes that they'll capture footage of their combat prowess against our operatives."
+ icon_screen = "commsyndie"
+ icon_keyboard = "syndie_key"
+ network = list(OPERATIVE_CAMERA_NET)
+ circuit = /obj/item/circuitboard/computer/overwatch
+
+/obj/item/circuitboard/computer/overwatch
+ name = "Overwatch Body Cameras"
+ build_path = /obj/machinery/computer/security/overwatch
+ greyscale_colors = CIRCUIT_COLOR_SECURITY
+
+/obj/item/circuitboard/computer/syndicate_shuttle_docker
+ name = "Shuttle Controller"
+ build_path = /obj/machinery/computer/camera_advanced/shuttle_docker/syndicate
+ greyscale_colors = CIRCUIT_COLOR_SECURITY
+
+/obj/item/clothing/glasses/overwatch
+ name = "intelligence glasses"
+ desc = "A set of incredibly advanced sunglasses, providing you with an array of different sensor scans and visual readouts for pretty much anything you look at. \
+ It's kind of overwhelming, actually. Wearing this for a few hours will probably give you a migrane."
+ icon_state = "sunhudmed"
+ flags_cover = GLASSESCOVERSEYES
+ flash_protect = FLASH_PROTECTION_WELDER
+ clothing_traits = list(TRAIT_REAGENT_SCANNER)
+ var/list/hudlist = list(DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC_ADVANCED, DATA_HUD_SECURITY_ADVANCED)
diff --git a/code/modules/antagonists/nukeop/outfits.dm b/code/modules/antagonists/nukeop/outfits.dm
index 80360f56360..eb251bcc363 100644
--- a/code/modules/antagonists/nukeop/outfits.dm
+++ b/code/modules/antagonists/nukeop/outfits.dm
@@ -114,6 +114,17 @@
r_hand = /obj/item/tank/internals/plasmaman/belt/full
tc = 0
+/datum/outfit/syndicate/support/plasmaman
+ name = "Nuclear Operative Overwatch Agent (Plasmaman)"
+ back = /obj/item/storage/backpack/satchel
+ head = /obj/item/clothing/head/helmet/space/plasmaman/syndie
+ uniform = /obj/item/clothing/under/plasmaman/syndicate
+ glasses = /obj/item/clothing/glasses/overwatch
+ suit = /obj/item/clothing/suit/jacket/letterman_syndie
+ r_hand = /obj/item/tank/internals/plasmaman/belt/full
+ command_radio = TRUE
+ tc = 0
+
/datum/outfit/syndicate/reinforcement/gorlex
name = "Syndicate Operative - Gorlex Reinforcement"
suit = /obj/item/clothing/suit/armor/vest/alt
@@ -191,3 +202,13 @@
var/obj/item/shield/energy/shield = locate() in H.held_items
shield.icon_state = "[shield.base_icon_state]1"
H.update_held_items()
+
+/datum/outfit/syndicate/support
+ name = "Nuclear Operative Overwatch Agent"
+ back = /obj/item/storage/backpack/satchel
+ uniform = /obj/item/clothing/under/syndicate/tacticool
+ glasses = /obj/item/clothing/glasses/overwatch
+ suit = /obj/item/clothing/suit/jacket/letterman_syndie
+ shoes = /obj/item/clothing/shoes/sandal
+ command_radio = TRUE
+ tc = 0
diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
index 1cf8b67db5d..ba6e9619a2f 100644
--- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
@@ -62,6 +62,7 @@
/datum/outfit/syndicate/reinforcement/interdyne = /datum/outfit/syndicate/reinforcement/plasmaman,
/datum/outfit/syndicate/reinforcement/mi13 = /datum/outfit/syndicate/reinforcement/plasmaman,
/datum/outfit/syndicate/reinforcement/waffle = /datum/outfit/syndicate/reinforcement/plasmaman,
+ /datum/outfit/syndicate/support = /datum/outfit/syndicate/support/plasmaman,
)
/// If the bones themselves are burning clothes won't help you much
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index ad8f8695a05..9d5fb6d241f 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -637,6 +637,9 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/gri
/obj/item/stack/cable_coil/five
amount = 5
+/obj/item/stack/cable_coil/thirty
+ amount = 30
+
/obj/item/stack/cable_coil/cut
amount = null
icon_state = "coil2"
diff --git a/code/modules/shuttle/syndicate.dm b/code/modules/shuttle/syndicate.dm
index f08446b650d..402a87bf6d4 100644
--- a/code/modules/shuttle/syndicate.dm
+++ b/code/modules/shuttle/syndicate.dm
@@ -64,5 +64,6 @@
y_offset = -1
whitelist_turfs = list(/turf/open/space, /turf/open/floor/plating, /turf/open/lava, /turf/closed/mineral, /turf/open/openspace, /turf/open/misc)
see_hidden = TRUE
+ circuit = /obj/item/circuitboard/computer/syndicate_shuttle_docker
#undef SYNDICATE_CHALLENGE_TIMER
diff --git a/code/modules/uplink/uplink_items/nukeops.dm b/code/modules/uplink/uplink_items/nukeops.dm
index 975424923cb..f663ea0dc9d 100644
--- a/code/modules/uplink/uplink_items/nukeops.dm
+++ b/code/modules/uplink/uplink_items/nukeops.dm
@@ -475,6 +475,14 @@
Its chameleon projector lets it disguise itself as a Nanotrasen cyborg, on top it has thermal vision and a pinpointer."
item = /obj/item/antag_spawner/nuke_ops/borg_tele/saboteur
+/datum/uplink_item/reinforcements/overwatch_agent
+ name = "Overwatch Intelligence Agent"
+ desc = "An Overwatch Intelligence Agent is assigned to your operation. They can view your progress and help coordinate using your \
+ operative team's body-cams. They can also pilot the shuttle remotely and view the station's camera net. \
+ If you're a meathead who's just here to kill people and don't care about strategising or intel, you'll still have someone to bear witness to your murder-spree!"
+ item = /obj/item/antag_spawner/nuke_ops/overwatch
+ cost = 12
+
// ~~ Disposable Sentry Gun ~~
// Technically not a spawn but it is a kind of reinforcement...I guess.
diff --git a/tgstation.dme b/tgstation.dme
index 395ef32c531..32bb9b44334 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -336,6 +336,7 @@
#include "code\__DEFINES\dcs\signals\signals_music.dm"
#include "code\__DEFINES\dcs\signals\signals_object.dm"
#include "code\__DEFINES\dcs\signals\signals_operating_computer.dm"
+#include "code\__DEFINES\dcs\signals\signals_operatives.dm"
#include "code\__DEFINES\dcs\signals\signals_painting.dm"
#include "code\__DEFINES\dcs\signals\signals_proxmonitor.dm"
#include "code\__DEFINES\dcs\signals\signals_radiation.dm"
@@ -3251,10 +3252,12 @@
#include "code\modules\antagonists\nukeop\datums\operative_leader.dm"
#include "code\modules\antagonists\nukeop\datums\operative_lone.dm"
#include "code\modules\antagonists\nukeop\datums\operative_reinforcement.dm"
+#include "code\modules\antagonists\nukeop\datums\operative_support.dm"
#include "code\modules\antagonists\nukeop\datums\operative_team.dm"
#include "code\modules\antagonists\nukeop\equipment\borgchameleon.dm"
#include "code\modules\antagonists\nukeop\equipment\nuclear_authentication_disk.dm"
#include "code\modules\antagonists\nukeop\equipment\nuclear_challenge.dm"
+#include "code\modules\antagonists\nukeop\equipment\overwatch_tools.dm"
#include "code\modules\antagonists\nukeop\equipment\pinpointer.dm"
#include "code\modules\antagonists\nukeop\equipment\nuclear_bomb\_nuclear_bomb.dm"
#include "code\modules\antagonists\nukeop\equipment\nuclear_bomb\beer_nuke.dm"