diff --git a/code/__DEFINES/bitrunning.dm b/code/__DEFINES/bitrunning.dm index bdb02b4d792..ba6c3b9b61c 100644 --- a/code/__DEFINES/bitrunning.dm +++ b/code/__DEFINES/bitrunning.dm @@ -23,3 +23,6 @@ #define BITRUNNER_DIFFICULTY_MEDIUM 2 /// Red with skull. I am trying to kill bitrunners. #define BITRUNNER_DIFFICULTY_HIGH 3 + +/// Camera network bitrunner bodycams are on +#define BITRUNNER_CAMERA_NET "bitrunner" diff --git a/code/datums/components/simple_bodycam.dm b/code/datums/components/simple_bodycam.dm new file mode 100644 index 00000000000..2a6b0188972 --- /dev/null +++ b/code/datums/components/simple_bodycam.dm @@ -0,0 +1,56 @@ +/// Simple component to integrate a bodycam into a mob +/datum/component/simple_bodycam + /// The actual camera, in our mob's contents + VAR_PRIVATE/obj/machinery/camera/bodycam + /// How fast we update + var/camera_update_time = 0.5 SECONDS + +/datum/component/simple_bodycam/Initialize( + camera_name = "bodycam", + c_tag = capitalize(camera_name), + network = "ss13", + emp_proof = FALSE, + camera_update_time = 0.5 SECONDS, +) + if(!isliving(parent)) + return COMPONENT_INCOMPATIBLE + + src.camera_update_time = camera_update_time + + bodycam = new(parent) + bodycam.network = list(network) + bodycam.name = camera_name + bodycam.c_tag = c_tag + if(emp_proof) + bodycam.AddElement(/datum/element/empprotection, EMP_PROTECT_ALL) + + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(update_cam)) + RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, PROC_REF(rotate_cam)) + RegisterSignals(bodycam, list(COMSIG_QDELETING, COMSIG_MOVABLE_MOVED), PROC_REF(camera_gone)) + + do_update_cam() + +/datum/component/simple_bodycam/Destroy() + if(QDELETED(bodycam)) + bodycam = null + else + QDEL_NULL(bodycam) + return ..() + +/datum/component/simple_bodycam/proc/update_cam(datum/source, atom/old_loc, ...) + SIGNAL_HANDLER + + if(get_turf(old_loc) != get_turf(parent)) + do_update_cam() + +/datum/component/simple_bodycam/proc/do_update_cam() + GLOB.cameranet.updatePortableCamera(bodycam, camera_update_time) + +/datum/component/simple_bodycam/proc/rotate_cam(datum/source, old_dir, new_dir) + SIGNAL_HANDLER + // I don't actually think cameras care about dir but just in case + bodycam.setDir(new_dir) + +/datum/component/simple_bodycam/proc/camera_gone(datum/source) + SIGNAL_HANDLER + qdel(src) diff --git a/code/game/machinery/computer/telescreen.dm b/code/game/machinery/computer/telescreen.dm index 7b1e62e2cb7..daba0b62f10 100644 --- a/code/game/machinery/computer/telescreen.dm +++ b/code/game/machinery/computer/telescreen.dm @@ -87,6 +87,53 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/security/telescreen/entertai notify(network.len, announcement) +/** + * Adds a camera network to all entertainment monitors. + * + * * camera_net - The camera network ID to add to the monitors. + * * announcement - Optional, what announcement to make when the show starts. + */ +/proc/start_broadcasting_network(camera_net, announcement) + for(var/obj/machinery/computer/security/telescreen/entertainment/tv as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/computer/security/telescreen/entertainment)) + tv.update_shows( + is_show_active = TRUE, + tv_show_id = camera_net, + announcement = announcement, + ) + +/** + * Removes a camera network from all entertainment monitors. + * + * * camera_net - The camera network ID to remove from the monitors. + * * announcement - Optional, what announcement to make when the show ends. + */ +/proc/stop_broadcasting_network(camera_net, announcement) + for(var/obj/machinery/computer/security/telescreen/entertainment/tv as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/computer/security/telescreen/entertainment)) + tv.update_shows( + is_show_active = FALSE, + tv_show_id = camera_net, + announcement = announcement, + ) + +/** + * Sets the camera network status on all entertainment monitors. + * A way to force a network to a status if you are unsure of the current state. + * + * * camera_net - The camera network ID to set on the monitors. + * * is_show_active - Whether the show is active or not. + * * announcement - Optional, what announcement to make. + * Note this announcement will be made regardless of the current state of the show: + * This means if it's currently on and you set it to on, the announcement will still be made. + * Likewise, there's no way to differentiate off -> on and on -> off, unless you handle that yourself. + */ +/proc/set_network_broadcast_status(camera_net, is_show_active, announcement) + for(var/obj/machinery/computer/security/telescreen/entertainment/tv as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/computer/security/telescreen/entertainment)) + tv.update_shows( + is_show_active = is_show_active, + tv_show_id = camera_net, + announcement = announcement, + ) + /obj/machinery/computer/security/telescreen/rd name = "\improper Research Director's telescreen" desc = "Used for watching the AI and the RD's goons from the safety of his office." @@ -270,5 +317,4 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/security/telescreen/entertai is_show_active = !is_show_active say("The [tv_show_name] show has [is_show_active ? "begun" : "ended"]") var/announcement = is_show_active ? pick(tv_starters) : pick(tv_enders) - for(var/obj/machinery/computer/security/telescreen/entertainment/tv as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/computer/security/telescreen/entertainment)) - tv.update_shows(is_show_active, tv_network_id, announcement) + set_network_broadcast_status(tv_network_id, is_show_active, announcement) diff --git a/code/modules/bitrunning/objects/quantum_console.dm b/code/modules/bitrunning/objects/quantum_console.dm index 6cc8aef6dae..9dc829c48cd 100644 --- a/code/modules/bitrunning/objects/quantum_console.dm +++ b/code/modules/bitrunning/objects/quantum_console.dm @@ -46,6 +46,8 @@ data["ready"] = server.is_ready && server.is_operational data["scanner_tier"] = server.scanner_tier data["retries_left"] = length(server.exit_turfs) - server.retries_spent + data["broadcasting"] = server.broadcasting + data["broadcasting_on_cd"] = !COOLDOWN_FINISHED(server, broadcast_toggle_cd) return data @@ -83,6 +85,9 @@ if("stop_domain") server.begin_shutdown(usr) return TRUE + if("broadcast") + server.toggle_broadcast() + return TRUE return FALSE diff --git a/code/modules/bitrunning/server/_parent.dm b/code/modules/bitrunning/server/_parent.dm index f3f71376897..b9d8808607e 100644 --- a/code/modules/bitrunning/server/_parent.dm +++ b/code/modules/bitrunning/server/_parent.dm @@ -44,6 +44,10 @@ var/threat = 0 /// The turfs we can place a hololadder on. var/turf/exit_turfs = list() + /// Determines if we broadcast to entertainment monitors or not + var/broadcasting = FALSE + /// Cooldown between being able to toggle broadcasting + COOLDOWN_DECLARE(broadcast_toggle_cd) /obj/machinery/quantum_server/Initialize(mapload) . = ..() diff --git a/code/modules/bitrunning/server/map_handling.dm b/code/modules/bitrunning/server/map_handling.dm index 2ddff04fc15..ed3adc3467e 100644 --- a/code/modules/bitrunning/server/map_handling.dm +++ b/code/modules/bitrunning/server/map_handling.dm @@ -30,11 +30,11 @@ return FALSE if(isnull(map_key)) - balloon_alert_to_viewers("no domain specified.") + balloon_alert_to_viewers("no domain specified!") return FALSE if(generated_domain) - balloon_alert_to_viewers("stop the current domain first.") + balloon_alert_to_viewers("stop the current domain first!") return FALSE if(length(avatar_connection_refs)) @@ -46,7 +46,7 @@ /// If any one of these fail, it reverts the entire process if(!load_domain(map_key) || !load_map_items() || !load_mob_segments()) - balloon_alert_to_viewers("initialization failed.") + balloon_alert_to_viewers("initialization failed!") scrub_vdom() is_ready = TRUE return FALSE @@ -63,6 +63,9 @@ update_use_power(ACTIVE_POWER_USE) update_appearance() + if(broadcasting) + start_broadcasting_network(BITRUNNER_CAMERA_NET) + return TRUE /// Initializes a new domain if the given key is valid and the user has enough points @@ -147,6 +150,8 @@ domain_randomized = FALSE retries_spent = 0 + stop_broadcasting_network(BITRUNNER_CAMERA_NET) + /// Tries to clean up everything in the domain /obj/machinery/quantum_server/proc/scrub_vdom() sever_connections() /// just in case someone's connected diff --git a/code/modules/bitrunning/server/obj_generation.dm b/code/modules/bitrunning/server/obj_generation.dm index a2acbf121e7..0cc923d6b3c 100644 --- a/code/modules/bitrunning/server/obj_generation.dm +++ b/code/modules/bitrunning/server/obj_generation.dm @@ -83,6 +83,13 @@ SSid_access.apply_trim_to_card(outfit_id, /datum/id_trim/bit_avatar) + avatar.AddComponent( \ + /datum/component/simple_bodycam, \ + camera_name = "bitrunner bodycam", \ + c_tag = "Avatar [avatar.real_name]", \ + network = BITRUNNER_CAMERA_NET, \ + emp_proof = TRUE, \ + ) return avatar /// Generates a new hololadder for the bitrunner. Effectively a respawn attempt. diff --git a/code/modules/bitrunning/server/util.dm b/code/modules/bitrunning/server/util.dm index 24ed78210cf..a657122082d 100644 --- a/code/modules/bitrunning/server/util.dm +++ b/code/modules/bitrunning/server/util.dm @@ -98,3 +98,17 @@ return chosen_turf #undef MAX_DISTANCE + +/// Toggles broadcast on and off +/obj/machinery/quantum_server/proc/toggle_broadcast() + if(!COOLDOWN_FINISHED(src, broadcast_toggle_cd)) + return FALSE + + broadcasting = !broadcasting + + if(generated_domain) + // The cooldown only really matter is we're flipping TVs + COOLDOWN_START(src, broadcast_toggle_cd, 5 SECONDS) + // And we only flip TVs when there's a domain, because otherwise there's no cams to watch + set_network_broadcast_status(BITRUNNER_CAMERA_NET, broadcasting) + return TRUE diff --git a/tgstation.dme b/tgstation.dme index c43a535e806..564e3165e28 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1173,6 +1173,7 @@ #include "code\datums\components\shy_in_room.dm" #include "code\datums\components\sign_language.dm" #include "code\datums\components\simple_access.dm" +#include "code\datums\components\simple_bodycam.dm" #include "code\datums\components\singularity.dm" #include "code\datums\components\sitcomlaughter.dm" #include "code\datums\components\sizzle.dm" diff --git a/tgui/packages/tgui/interfaces/QuantumConsole.tsx b/tgui/packages/tgui/interfaces/QuantumConsole.tsx index 7d64056307c..bba1d858019 100644 --- a/tgui/packages/tgui/interfaces/QuantumConsole.tsx +++ b/tgui/packages/tgui/interfaces/QuantumConsole.tsx @@ -29,6 +29,8 @@ type Data = ready: BooleanLike; retries_left: number; scanner_tier: number; + broadcasting: BooleanLike; + broadcasting_on_cd: BooleanLike; } | { connected: 0; @@ -111,11 +113,13 @@ const AccessView = (props) => { const { available_domains = [], + broadcasting, + broadcasting_on_cd, generated_domain, - ready, occupants, points, randomized, + ready, } = data; const sorted = available_domains.sort((a, b) => a.cost - b.cost); @@ -139,6 +143,15 @@ const AccessView = (props) => {
+ act('broadcast')} + tooltip="Toggles whether you broadcast your + bitrun to station Entertainment Monitors." + > + Broadcast +