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
2 changes: 2 additions & 0 deletions code/_globalvars/misc.dm
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,5 @@ GLOBAL_VAR(obfs_z)

/// List of giant lizards that are alive.
GLOBAL_LIST_EMPTY(giant_lizards_alive)
/// List of F5CT Field Camera Tripods
GLOBAL_LIST_EMPTY_TYPED(deployed_tripod_cameras, /obj/structure/overwatch_camera_tripod)
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ GLOBAL_LIST_INIT(cm_vending_gear_engi, list(
list("Motion Detector", 8, /obj/item/device/motiondetector, null, VENDOR_ITEM_REGULAR),
list("Whistle", 3, /obj/item/clothing/accessory/device/whistle, null, VENDOR_ITEM_REGULAR),
list("Synthetic Reset Key", 10, /obj/item/device/defibrillator/synthetic, null, VENDOR_ITEM_REGULAR),
list("FCT - Field Camera Tripod", 5, /obj/item/device/overwatch_camera/tripod, null, VENDOR_ITEM_REGULAR),

list("BINOCULARS", 0, null, null, null),
list("Binoculars", 5, /obj/item/device/binoculars, null, VENDOR_ITEM_REGULAR),
Expand Down
217 changes: 217 additions & 0 deletions code/modules/cm_marines/equipment/gear.dm
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,220 @@

/obj/item/device/overwatch_camera/see_emote(mob/living/sourcemob, emote, audible)
SEND_SIGNAL(src, COMSIG_BROADCAST_SEE_EMOTE, sourcemob, emote, audible, loc == sourcemob && audible)

/obj/item/device/overwatch_camera/tripod
name = "FTC Tripod Camera"
desc = "A Motoca-430-T deployable tripod camera that connects to the overwatch network. It can be renamed and deployed."
icon = 'icons/overwatch.dmi' // ToDO: Get real sprites
icon_state = "undeployed"
desc_lore = "Following modernisation efforts in the Marine'70 program, USCM Platoons were shrunk and squads re-organised to emphasise individual firepower and mobility. The Motoca-430-T, the precursor to the Motoca-500 Helmet Camera, was commissioned by the Department of Defense to be utilised by Colonial Marine squads in establishing secure perimeters and watching rear areas remotely through the Overwatch system."
var/label
var/datum/squad/squad

/obj/item/device/overwatch_camera/tripod/Initialize(mapload, ...)
. = ..()
camera = new /obj/structure/machinery/camera/overwatch(src)
AddComponent(/datum/component/overwatch_console_control)

/obj/item/device/overwatch_camera/tripod/Destroy()
QDEL_NULL(camera)
return ..()

/obj/item/device/overwatch_camera/tripod/attack_self(mob/user)
..()
var/choice = tgui_alert(user, "What would you like to do with [src]?", "Tripod Camera", list("Rename", "Deploy", "Cancel"))
switch(choice)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check for deletion after tgui_alert - the item can have been deleted in the meantime the user selecting. This is important if you're gonna deploy it.

Ideally should check the user is still holding it and conscious and such aswell, but that's bonus, at least check for deletion

if("Cancel")
return
if("Rename")
var/new_name = tgui_input_text(user, "Enter new name for the camera:", "Rename Camera", label ? label : initial(name), MAX_NAME_LEN, ui_state=GLOB.not_incapacitated_state, encode=FALSE)
if(!new_name)
return
new_name = trim_right(replace_non_alphanumeric_plus(new_name))
if(!length(new_name))
to_chat(user, SPAN_WARNING("Invalid name."))
return
label = new_name
name = new_name
if(camera)
camera.c_tag = new_name
to_chat(user, SPAN_NOTICE("Camera renamed to [name]."))
return
if("Deploy")
deploy_tripod(user)

/obj/item/device/overwatch_camera/tripod/proc/deploy_tripod(mob/user)
var/datum/squad/user_squad = null // find squad for addition to label

if(ishuman(user)) // synths can place so not strict check
var/mob/living/carbon/human/human_user = user
user_squad = human_user.assigned_squad
if(isyautja(user))
to_chat(user, SPAN_WARNING("You can't think of a reason to interact with [src] and decide to leave it alone."))
return
if(user.is_mob_incapacitated())
return
// if(user. != src)
// to_chat(user, SPAN_WARNING("You need to hold [src] in your hand to deploy it!"))
// return

var/turf/deploy_turf = get_turf(user)
if(!deploy_turf)
return

var/area/deploy_area = get_area(deploy_turf)
if(!deploy_area.allow_construction)
to_chat(user, SPAN_WARNING("You cannot deploy [src] here!"))
return
if(istype(deploy_area, /area/shuttle))
to_chat(user, SPAN_WARNING("You cannot deploy [src] in a shuttle area.")) // i copied this from M2C so idk if this is necessary?
return
if(!istype(deploy_turf, /turf/open))
to_chat(user, SPAN_WARNING("[src] must be placed on a solid surface!"))
return

for(var/obj/blocking_object in deploy_turf)
if(blocking_object.density && blocking_object != src)
to_chat(user, SPAN_WARNING("[blocking_object] is blocking the deployment spot!"))
return

if(!do_after(user, 3 SECONDS, INTERRUPT_ALL, BUSY_ICON_BUILD))
to_chat(user, SPAN_WARNING("You must stand still while deploying the tripod."))
return

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, check conditions are still valid after do_after, at least that the item still exists and the user is valid


if(user.stat != CONCIOUS || user.is_mob_incapacitated()) //not sure if this is the same check or not :D

Check failure on line 441 in code/modules/cm_marines/equipment/gear.dm

View workflow job for this annotation

GitHub Actions / Run Linters

undefined var: "CONCIOUS"
return

if(user.get_active_hand() != src)
to_chat(user, SPAN_WARNING("You must hold [src] in your hand to deploy it!"))
return

var/base_label = label ? label : initial(name)
var/final_label = user_squad ? "[user_squad.name] - [base_label]" : base_label

var/obj/structure/overwatch_camera_tripod/deployed_structure = new(deploy_turf) // transform to new struc
deployed_structure.label = final_label
deployed_structure.name = final_label
deployed_structure.squad = user_squad
deployed_structure.icon_state = "deployed"

if(camera)
camera.forceMove(deployed_structure)
camera.c_tag = final_label
camera.status = TRUE
deployed_structure.camera = camera
src.camera = null

to_chat(user, SPAN_NOTICE("You deploy [src]."))
user.temp_drop_inv_item()
qdel(src)

/obj/structure/overwatch_camera_tripod
name = "FTC Tripod Camera"
desc = "A Motoca-430-T deployed tripod camera connected to the overwatch network."
icon = 'icons/overwatch.dmi' // ToDO: Get real sprites
icon_state = "deployed"
density = TRUE
anchored = TRUE
layer = OBJ_LAYER
desc_lore = "Following modernisation efforts in the Marine'70 program, USCM Platoons were shrunk and squads re-organised to emphasise individual firepower and mobility. The Motoca-430-T, the precursor to the Motoca-500 Helmet Camera, was commissioned by the Department of Defense to be utilised by Colonial Marine squads in establishing secure perimeters and watching rear areas remotely through the Overwatch system."
var/label = "Tripod Camera"
var/obj/structure/machinery/camera/camera
var/datum/squad/squad
var/slash_count = 0 // tracks xeno slashes 4 breaking

/obj/structure/overwatch_camera_tripod/Initialize(mapload)
. = ..()
icon_state = "deployed"
camera = new /obj/structure/machinery/camera/overwatch(src)
camera.c_tag = label
camera.status = TRUE
AddComponent(/datum/component/overwatch_console_control)
GLOB.deployed_tripod_cameras += src

/obj/structure/overwatch_camera_tripod/Destroy()
GLOB.deployed_tripod_cameras -= src
QDEL_NULL(camera)
return ..()

/obj/structure/overwatch_camera_tripod/examine(mob/user)
. = ..()
to_chat(user, SPAN_INFO("The label label reads: [label]")) // ToDO: This maybe should be in the description box I just don't know how to add it atm
if(squad)
to_chat(user, SPAN_INFO("It is currently assigned to squad: [squad.name]")) // ToDO: This maybe should be in the description box I just don't know how to add it atm

/obj/structure/overwatch_camera_tripod/attack_hand(mob/user)
if(user.a_intent != INTENT_HELP) // I've left this in just in case maints want me to change the tgui menu to intent handling or smth.
return ..()
var/choice = tgui_alert(user, "What would you like to do with [src]?", "Tripod Camera", list("Rename", "Pick Up", "Cancel"))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same goes here regarding checking after tgui_alert. This is even more important cause in this situation, if there's no error, it's possible for several people to click on it and all undeploy it, duplicating the item

switch(choice)
if("Cancel")
return
if("Rename")
if(isyautja(user))
to_chat(user, SPAN_WARNING("You can't think of a reason to interact with [src] and decide to leave it alone."))
return
var/new_name = tgui_input_text(user, "Enter new label for the camera:", "Rename Camera", label, MAX_NAME_LEN, ui_state=GLOB.not_incapacitated_state, encode=FALSE)
if(!new_name)
return
new_name = trim_right(replace_non_alphanumeric_plus(new_name))
if(!length(new_name))
to_chat(user, SPAN_WARNING("Invalid name."))
return
label = new_name
name = new_name
if(camera)
camera.c_tag = new_name
to_chat(user, SPAN_NOTICE("[src] renamed to [name]."))
return
if("Pick Up")
if(isyautja(user))
to_chat(user, SPAN_WARNING("You can't think of a reason to interact with [src] and decide to leave it alone."))
return
if(!user.Adjacent(src))
to_chat(user, SPAN_WARNING("You must be closer to pick up [src]."))
return
if(!do_after(user, 2 SECONDS, INTERRUPT_ALL, BUSY_ICON_GENERIC))
to_chat(user, SPAN_WARNING("You were interrupted while picking up the [src]."))
return
// Create a new tripod item from the structure
undeploy(user)
return // not sure if i need this here

/obj/structure/overwatch_camera_tripod/attack_alien(mob/living/carbon/xenomorph/Xeno)
if(islarva(Xeno))
return
slash_count++
Xeno.animation_attack_on(src)
Xeno.flick_attack_overlay(src, "slash")
playsound(loc, 'sound/weapons/slash.ogg', 25, 1)
if(slash_count >= 3)
Xeno.visible_message(SPAN_DANGER("[Xeno] slashes [src] apart!"),
SPAN_DANGER("You tear through [src]!"))
undeploy()
else
Xeno.visible_message(SPAN_DANGER("[Xeno] slashes [src]!"),
SPAN_DANGER("You slash [src]!"))
return XENO_ATTACK_ACTION

/obj/structure/overwatch_camera_tripod/proc/undeploy(mob/user)
var/obj/item/device/overwatch_camera/tripod/new_tripod = new(get_turf(src))
new_tripod.label = label
new_tripod.name = label
new_tripod.squad = squad
if(camera)
camera.forceMove(new_tripod)
camera.c_tag = label
camera.status = TRUE
new_tripod.camera = camera
src.camera = null
if(user && ishuman(user))
user.put_in_hands(new_tripod)
to_chat(user, SPAN_NOTICE("You disassemble [src]."))
else
new_tripod.visible_message(SPAN_WARNING("[new_tripod] falls to the floor."))
qdel(src)

/obj/structure/overwatch_camera_tripod/ex_act(severity)
if(severity >= EXPLOSION_THRESHOLD_LOW) // no idea if i need to add this or it's inherited from parent somewhere

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's ok here since undeploy is your behavior
it probably should get a bit more than undeployed when slashed/exploded though...

undeploy()
128 changes: 93 additions & 35 deletions code/modules/cm_marines/overwatch.dm
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,40 @@ GLOBAL_LIST_EMPTY_TYPED(active_overwatch_consoles, /obj/structure/machinery/comp
leader_count++
marine_count--

for(var/obj/structure/overwatch_camera_tripod/tripod_camera in GLOB.deployed_tripod_cameras) // add cameras to list o' marines

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for(var/obj/structure/overwatch_camera_tripod/tripod_camera in GLOB.deployed_tripod_cameras) // add cameras to list o' marines
for(var/obj/structure/overwatch_camera_tripod/tripod_camera as anything in GLOB.deployed_tripod_cameras) // add cameras to list o' marines

if(current_squad && current_squad.name != "Root")
if(!tripod_camera.squad || tripod_camera.squad != current_squad) // tldr: show cameras in root squad if placed by non-squad marines
continue
if(!tripod_camera.camera || !tripod_camera.camera.can_use()) // skip broken (code) or damaged (in-game) cameras
continue // ToDO: There should be an error log if camera is missing camera comp.
if(!tripod_camera.loc) // skip null location cameras
continue // ToDO: Error Log if camera has no LOC
var/turf/camera_turf = get_turf(tripod_camera)
if(!camera_turf)
continue // ToDO: Error Log if camera has no turf.
switch(z_hidden)
if(HIDE_ALMAYER)
if(is_mainship_level(camera_turf.z))
continue
if(HIDE_GROUND)
if(is_ground_level(camera_turf.z))
continue
var/area/camera_area = get_area(tripod_camera)
var/camera_area_name = camera_area ? sanitize_area(camera_area.name) : "Unknown"
var/list/camera_data = list(
"name" = tripod_camera.label,
"state" = "Active",
"has_helmet" = TRUE, // can't click the button in OW if set to false
"role" = "Tripod Camera",
"acting_sl" = "", // not sure if i need to null these or not
"fteam" = "",
"distance" = "N/A",
"area_name" = camera_area_name,
"ref" = REF(tripod_camera),
"rank" = "",
)
data["marines"] += list(camera_data)

data["total_deployed"] = leader_count + ftl_count + spec_count + medic_count + engi_count + smart_count + marine_count
data["living_count"] = leaders_alive + ftl_alive + spec_alive + medic_alive + engi_alive + smart_alive + marines_alive

Expand Down Expand Up @@ -788,47 +822,62 @@ GLOBAL_LIST_EMPTY_TYPED(active_overwatch_consoles, /obj/structure/machinery/comp
return
if(!params["target_ref"])
return
if(current_squad)
var/mob/living/carbon/human/cam_target = locate(params["target_ref"])
if(!current_squad)
return

if(!istype(cam_target))
return
var/atom/target_ref = locate(params["target_ref"])
var/obj/structure/machinery/camera/new_cam = null
var/obj/item/new_holder = null
var/atom/cam_target = null

var/obj/item/new_holder = cam_target.get_camera_holder()
var/obj/structure/machinery/camera/new_cam
if(ishuman(target_ref)) // not strict since synths can be placed in OW squads
var/mob/living/carbon/human/Human = target_ref
cam_target = Human
new_holder = Human.get_camera_holder()
if(new_holder)
new_cam = new_holder.get_camera()
if(user.interactee != src) //if we multitasking
user.set_interaction(src)
if(cam == new_cam) //if we switch to a console that is already watching this cam
return
if(!new_cam || !new_cam.can_use())
to_chat(user, "[icon2html(src, user)] [SPAN_WARNING("Searching for camera. No camera found for this marine! Tell your squad to put their cameras on!")]")
else if(cam && cam == new_cam)//click the camera you're watching a second time to stop watching.
visible_message("[icon2html(src, viewers(src))] [SPAN_BOLDNOTICE("Stopping camera view of [cam_target].")]")
for(var/datum/weakref/user_ref in concurrent_users)
var/mob/concurrent = user_ref.resolve()
if(!concurrent)
continue
stop_watching_camera(concurrent)
else if(istype(target_ref, /obj/structure/overwatch_camera_tripod))
var/obj/structure/overwatch_camera_tripod/tripod_camera = target_ref
if(tripod_camera.camera)
new_cam = tripod_camera.camera
cam_target = tripod_camera
else
to_chat(user, "[icon2html(src, user)] [SPAN_WARNING("Invalid target.")]")
return

if(user.interactee != src) //if we multitasking
user.set_interaction(src)
if(cam == new_cam) //if we switch to a console that is already watching this cam
return
if(!new_cam || !new_cam.can_use())
to_chat(user, "[icon2html(src, user)] [SPAN_WARNING("Searching for camera. No camera found for this target!")]")
else if(cam && cam == new_cam)//click the camera you're watching a second time to stop watching.
visible_message("[icon2html(src, viewers(src))] [SPAN_BOLDNOTICE("Stopping camera view.")]")
for(var/datum/weakref/user_ref in concurrent_users)
var/mob/concurrent = user_ref.resolve()
if(!concurrent)
continue
stop_watching_camera(concurrent)
concurrent.UnregisterSignal(cam, COMSIG_PARENT_QDELETING)
disconnect_holder()
cam = null
else if(user.client.view != GLOB.world_view_size)
to_chat(user, SPAN_WARNING("You're too busy peering through binoculars."))
else
for(var/datum/weakref/user_ref in concurrent_users)
var/mob/concurrent = user_ref.resolve()
if(!concurrent)
continue
if(cam)
concurrent.UnregisterSignal(cam, COMSIG_PARENT_QDELETING)
disconnect_holder()
cam = null
else if(user.client.view != GLOB.world_view_size)
to_chat(user, SPAN_WARNING("You're too busy peering through binoculars."))
else
for(var/datum/weakref/user_ref in concurrent_users)
var/mob/concurrent = user_ref.resolve()
if(!concurrent)
continue
if(cam)
concurrent.UnregisterSignal(cam, COMSIG_PARENT_QDELETING)
start_watching_camera(concurrent, new_cam)
start_watching_camera(concurrent, new_cam)
if(cam_target)
set_onscreen_text(concurrent, cam_target)
concurrent.RegisterSignal(new_cam, COMSIG_PARENT_QDELETING, TYPE_PROC_REF(/mob, reset_observer_view_on_deletion))
if(camera_holder)
disconnect_holder()
cam = new_cam
concurrent.RegisterSignal(new_cam, COMSIG_PARENT_QDELETING, TYPE_PROC_REF(/mob, reset_observer_view_on_deletion))
if(camera_holder)
disconnect_holder()
cam = new_cam
if(new_holder)
connect_holder(new_holder)

if("change_operator")
Expand Down Expand Up @@ -1580,6 +1629,15 @@ GLOBAL_LIST_EMPTY_TYPED(active_overwatch_consoles, /obj/structure/machinery/comp

watcher.hud_used.overwatch_text.maptext = name_part + location_part + job_part + living_part

else if(istype(target, /obj/structure/overwatch_camera_tripod)) // on-screen text - in theory you can't click on a downed camera
var/obj/structure/overwatch_camera_tripod/tripod = target
var/area/current_area = get_area(tripod)
var/area_name = current_area ? sanitize_area(current_area.name) : "Unknown"
var/name_part = "<span class='langchat langchat_yell'>[tripod.label]</span><br>"
var/location_part = "<span class='langchat' style='font-size: 7px;'>[area_name]</span><br>"
var/job_part = "<span class='langchat' style='font-size: 6px;'>Tripod Camera</span>"
watcher.hud_used.overwatch_text.maptext = name_part + location_part + job_part

/obj/structure/machinery/computer/overwatch/almayer
density = FALSE
icon = 'icons/obj/structures/machinery/computer.dmi'
Expand Down
Binary file added icons/overwatch.dmi
Binary file not shown.
Loading