Bitrunners can broadcast their bitruns to the crew via Entertainment Monitors (#82218)

## About The Pull Request


![image](https://github.com/tgstation/tgstation/assets/51863163/72b90aba-863c-4776-b596-89f0dc0ee45f)

Bitrunners are now equipped with body cameras. 
The Quantum Console now holds a switch which you can toggle on to
broadcast your body camera footage to the station's Entertainment
Monitors.

I also cleaned up some balloon alerts

## Why It's Good For The Game

I did a gimmick with a bunch of bitrunners and thought it was lame I
couldn't watch them as they did the bitrunning part.
So here we are. I think it is pretty neat and fun, and also kinda
thematic, since it is VR after all.

## Changelog

🆑 Melbert
add: Bitrunners can now broadcast their Bitruns to the station's
Entertainment Monitors.
/🆑
This commit is contained in:
MrMelbert
2024-03-26 12:28:45 -06:00
committed by GitHub
parent ae8cfefa0e
commit c1047432c0
10 changed files with 162 additions and 7 deletions
+3
View File
@@ -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"
+56
View File
@@ -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)
+48 -2
View File
@@ -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)
@@ -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
@@ -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)
. = ..()
@@ -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
@@ -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.
+14
View File
@@ -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
+1
View File
@@ -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"
@@ -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) => {
<Section
buttons={
<>
<Button.Checkbox
checked={broadcasting}
disabled={broadcasting_on_cd}
onClick={() => act('broadcast')}
tooltip="Toggles whether you broadcast your
bitrun to station Entertainment Monitors."
>
Broadcast
</Button.Checkbox>
<Button
disabled={
!ready || occupants > 0 || points < 1 || !!generated_domain
@@ -146,7 +159,8 @@ const AccessView = (props) => {
icon="random"
onClick={() => act('random_domain')}
mr={1}
tooltip="Get a random domain for more rewards. Weighted towards your current points. Minimum: 1 point."
tooltip="Get a random domain for more rewards.
Weighted towards your current points. Minimum: 1 point."
>
Randomize
</Button>