Skip to content

Commit

Permalink
Radio TGUI backend Tidy-up & Adds Listening Devices (#4866)
Browse files Browse the repository at this point in the history
# About the pull request
This PR adds listening devices, disguisable objects that can transmit
nearby speech over a radio channel, or in a shortwave-burst like
handheld radios. Four exist on the USS Almayer, two for the MPs and two
for the CL.

I have also gone through the TGUI files to fix missing or incorrect
radio frequency checks regarding the displayed active frequency on
radios, along with adding the channel colours to said UI.
<!-- Remove this text and explain what the purpose of your PR is.

Mention if you have tested your changes. If you changed a map, make sure
you used the mapmerge tool.
If this is an Issue Correction, you can type "Fixes Issue #169420" to
link the PR to the corresponding Issue number #169420.

Remember: something that is self-evident to you might not be to others.
Explain your rationale fully, even if you feel it goes without saying.
-->

# Explain why it's good for the game
# Testing Photographs and Procedure
<details>
<summary>Screenshots & Videos</summary>

Put screenshots and videos here with an empty line between the
screenshots and the `<details>` tags.

</details>


# Changelog
:cl:
add: Added disguisable listening bugs/devices. MPs have two outside CMP
office, the CL has two in their bedroom.
fix: Fixes incorrect frequencies being used to display channel names on
radios.
fix: Radios and tape recorders placed on tables or placed inside webbing
can now hear speech again.
/:cl:
  • Loading branch information
realforest2001 authored Dec 1, 2023
1 parent 9e64234 commit ee43d8d
Show file tree
Hide file tree
Showing 11 changed files with 493 additions and 78 deletions.
4 changes: 4 additions & 0 deletions code/__DEFINES/radio.dm
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@
#define RADIO_CHANNEL_PMC_CCT "PMC CCT"
#define RADIO_CHANNEL_WY_WO "SpecOps"

//Listening Devices
#define RADIO_CHANNEL_BUG_A "Listening Device A"
#define RADIO_CHANNEL_BUG_B "Listening Device B"

//1-Channel ERTs
#define RADIO_CHANNEL_DUTCH_DOZEN "DD"
#define RADIO_CHANNEL_VAI "VAI"
Expand Down
15 changes: 14 additions & 1 deletion code/controllers/subsystem/communications.dm
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ Radiochat range: 1441 to 1489 (most devices refuse to be tune to other frequency
//Misc channels
#define YAUT_FREQ 1205
#define DUT_FREQ 1210
#define CMB_FREQ 1220
#define VAI_FREQ 1215
#define RMC_FREQ 1216
#define CMB_FREQ 1220

//WY Channels (1230-1249)
#define WY_FREQ 1231
Expand All @@ -99,6 +99,11 @@ Radiochat range: 1441 to 1489 (most devices refuse to be tune to other frequency
#define CLF_MED_FREQ 1274
#define CLF_CCT_FREQ 1275

//Listening Bugs (1290-1291)
#define BUG_A_FREQ 1290
#define BUG_B_FREQ 1291

//General Radio
#define MIN_FREQ 1460 // ------------------------------------------------------
#define PUB_FREQ 1461
#define MAX_FREQ 1468 // ------------------------------------------------------
Expand Down Expand Up @@ -191,6 +196,9 @@ GLOBAL_LIST_INIT(radiochannels, list(
RADIO_CHANNEL_CLF_ENGI = CLF_ENGI_FREQ,
RADIO_CHANNEL_CLF_MED = CLF_MED_FREQ,
RADIO_CHANNEL_CLF_CCT = CLF_CCT_FREQ,

RADIO_CHANNEL_BUG_A = BUG_A_FREQ,
RADIO_CHANNEL_BUG_B = BUG_B_FREQ,
))

// Response Teams
Expand All @@ -205,6 +213,9 @@ GLOBAL_LIST_INIT(radiochannels, list(
// PMC Frequencies
#define PMC_FREQS list(PMC_FREQ, PMC_CMD_FREQ, PMC_ENGI_FREQ, PMC_MED_FREQ, PMC_CCT_FREQ, WY_WO_FREQ, WY_FREQ)

//Listening Device Frequencies
#define BUG_FREQS list(BUG_A_FREQ, BUG_B_FREQ)

//Depts - used for colors in headset.dm, as well as deciding what the marine comms tower can listen into
#define DEPT_FREQS list(COMM_FREQ, MED_FREQ, ENG_FREQ, SEC_FREQ, SENTRY_FREQ, ALPHA_FREQ, BRAVO_FREQ, CHARLIE_FREQ, DELTA_FREQ, ECHO_FREQ, CRYO_FREQ, REQ_FREQ, JTAC_FREQ, INTEL_FREQ, WY_FREQ)

Expand Down Expand Up @@ -269,6 +280,8 @@ SUBSYSTEM_DEF(radio)
"[HC_FREQ]" = "hcradio",
"[PVST_FREQ]" = "pvstradio",
"[COLONY_FREQ]" = "deptradio",
"[BUG_A_FREQ]" = "airadio",
"[BUG_B_FREQ]" = "aiprivradio",
)

/datum/controller/subsystem/radio/proc/add_object(obj/device as obj, new_frequency as num, filter = null as text|null)
Expand Down
276 changes: 276 additions & 0 deletions code/game/objects/items/devices/radio/listening_bugs.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
#define DISGUISE_REMOVE "remove disguise"
#define DISGUISE_RADIO "radio"
#define DISGUISE_PEN "pen"
#define DISGUISE_FOUNTAIN_PEN "fountain pen"
#define DISGUISE_ACCESS_TUNER "access tuner"
#define DISGUISE_WHISTLE "whistle"
#define DISGUISE_MASS_SPEC "mass-spectrometer"
#define DISGUISE_CAMERA "camera"
#define DISGUISE_ZIPPO "zippo lighter"
#define DISGUISE_TAPE_RECORDER "tape recorder"

/obj/item/device/radio/listening_bug
name = "listening device"
desc = "A small, and disguisable, listening device."

icon = 'icons/obj/items/devices.dmi'
icon_state = "voice0"
item_state = "analyzer"

w_class = SIZE_TINY
volume = RADIO_VOLUME_RAISED

broadcasting = FALSE
listening = FALSE
frequency = BUG_A_FREQ
canhear_range = 2
freqlock = TRUE
/// If the bug is disguised or not.
var/ready_to_disguise = FALSE
var/disguised = FALSE
/// Whether or not the bug can be used to listen to its own channel.
var/prevent_snooping = FALSE
/// The ID tag of the device, for identification.
var/nametag = "Device"

/obj/item/device/radio/listening_bug/ui_data(mob/user)
var/list/data = list()

data["broadcasting"] = broadcasting
data["listening"] = listening
data["frequency"] = frequency
data["freqlock"] = freqlock

var/list/radio_channels = list()

for(var/channel in channels)
var/channel_key = channel_to_prefix(channel)
radio_channels += list(list(
"name" = channel,
"status" = channels[channel] & FREQ_LISTENING,
"hotkey" = channel_key))

data["channels"] = radio_channels

data["command"] = volume
data["useCommand"] = use_volume
data["subspace"] = subspace_transmission
data["subspaceSwitchable"] = subspace_switchable
data["headset"] = FALSE

return data

/obj/item/device/radio/listening_bug/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
switch(action)
if("listen")
if(prevent_snooping)
to_chat(usr, SPAN_WARNING("This device cannot receive transmissions!"))
return
listening = !listening
return
if("subspace")
if(!ishuman(usr))
return
var/mob/living/carbon/human/user = usr
if(!check_access(user.wear_id) && !check_access(user.get_active_hand()))
to_chat(user, SPAN_WARNING("You need an authenticated ID card to change this function!"))
return
if(subspace_switchable)
subspace_transmission = !subspace_transmission
var/initial_prevent = initial(prevent_snooping)
if(initial_prevent)
prevent_snooping = TRUE
if(!subspace_transmission)
prevent_snooping = FALSE
channels = list()
return
..()

/obj/item/device/radio/listening_bug/hear_talk(mob/M as mob, msg, verb = "says", datum/language/speaking = null)
var/processed_verb = "[SPAN_RED("\[LSTN [nametag]\]")] [verb]"
if(broadcasting)
if(get_dist(src, M) <= 7)
talk_into(M, msg,null,processed_verb,speaking)

/obj/item/device/radio/listening_bug/afterattack(atom/target_atom, mob/user as mob, proximity)
if(!ready_to_disguise)
return ..()

var/obj/item/target_item = target_atom
if(!istype(target_item) || target_item.anchored || target_item.w_class >= SIZE_LARGE)
to_chat(user, SPAN_WARNING("You cannot disguise the listening device as this object."))
return FALSE

var/confirm = tgui_alert(user, "Are you sure you wish to disguise the listening device as '[target_item]'?", "Confirm Choice", list("Yes","No"), 20 SECONDS)
if(confirm != "Yes")
return FALSE

icon = target_item.icon
name = target_item.name
desc = target_item.desc
icon_state = target_item.icon_state
item_state = target_item.item_state
flags_equip_slot = target_item.flags_equip_slot
w_class = target_item.w_class
ready_to_disguise = FALSE
disguised = TRUE

/obj/item/device/radio/listening_bug/get_examine_text(mob/user)
if(disguised)
. = list()
var/size
switch(w_class)
if(SIZE_TINY)
size = "tiny"
if(SIZE_SMALL)
size = "small"
if(SIZE_MEDIUM)
size = "normal-sized"
. += "This is a [blood_color ? blood_color != "#030303" ? "bloody " : "oil-stained " : ""][icon2html(src, user)][src.name]. It is a [size] item."
if(desc)
. += desc
if(desc_lore)
. += SPAN_NOTICE("This has an <a href='byond://?src=\ref[src];desc_lore=1'>extended lore description</a>.")
else
. = ..()
. += SPAN_INFO("[src] is set to frequency [get_bug_letter()].")
if(nametag != initial(nametag))
. += SPAN_INFO("[src]'s nametag is set to '[nametag]'")

/obj/item/device/radio/listening_bug/verb/change_disguise()
set name = "Change Disguise"
set category = "Object"
set src in usr

if(usr.is_mob_incapacitated())
to_chat(usr, SPAN_WARNING("You cannot do this while incapacitated!"))
return FALSE

var/check = tgui_alert(usr, "Do you wish to change the disguise of this listening bug?", "Change Disguise?", list("Yes", "No"))
if(check != "Yes")
return FALSE
if(disguised)
var/remove_check = tgui_alert(usr, "Do you wish to remove the current disguise?", "Remove Disguise?", list("Yes","No"))
if(remove_check == "Yes")
icon = initial(icon)
name = initial(name)
desc = initial(desc)
icon_state = initial(icon_state)
item_state = initial(item_state)
flags_equip_slot = initial(flags_equip_slot)
w_class = initial(w_class)
disguised = FALSE
return TRUE

to_chat(usr, SPAN_HELPFUL("You can now change the disguise of the device by selecting a normal, or smaller, sized object."))
ready_to_disguise = TRUE
return TRUE

/obj/item/device/radio/listening_bug/proc/get_bug_letter()
switch(frequency)
if(BUG_A_FREQ)
return "A"
if(BUG_B_FREQ)
return "B"
if(SEC_FREQ)
return "MP"
if(PVST_FREQ)
return "PVST"
if(HC_FREQ)
return "HC"
if(WY_FREQ, PMC_CCT_FREQ)
return "WY"
if(PMC_CMD_FREQ)
return "WYC"
if(UPP_CCT_FREQ, UPP_KDO_FREQ)
return "UPP"
else
return "X"

#define OPTION_REMOVE "Remove Tag"
#define OPTION_NEW "New Tag"

/obj/item/device/radio/listening_bug/verb/set_nametag()
set name = "Set Nametag"
set category = "Object"
set src in usr

if(usr.is_mob_incapacitated())
to_chat(usr, SPAN_WARNING("You cannot do this while incapacitated!"))
return FALSE

var/check = tgui_alert(usr, "Do you wish to change the name tag of this listening bug?", "Change Name tag?", list("Yes", "No"))
if(check != "Yes")
return FALSE


var/new_nametag
var/remove
if(nametag != initial(nametag))
remove = tgui_alert(usr, "Do you wish to remove the current nametag?", "Remove Nametag", list("Yes", "No"))
if(remove == "Yes")
new_nametag = initial(nametag)
else
new_nametag = tgui_input_text(usr, "What new name tag do you wish to use?", "New Name", initial(nametag), 6)

if(!new_nametag || (new_nametag == nametag))
return FALSE

nametag = new_nametag
log_game("[key_name(usr)] set a listening device nametag to [new_nametag].")
return TRUE

#undef OPTION_REMOVE
#undef OPTION_NEW

/obj/item/device/radio/listening_bug/freq_a
frequency = BUG_A_FREQ

/obj/item/device/radio/listening_bug/freq_b
frequency = BUG_B_FREQ

/obj/item/device/radio/listening_bug/radio_linked
prevent_snooping = TRUE
subspace_transmission = TRUE
subspace_switchable = TRUE

/obj/item/device/radio/listening_bug/radio_linked/mp
frequency = SEC_FREQ
req_one_access = list(ACCESS_MARINE_BRIG)

/obj/item/device/radio/listening_bug/radio_linked/hc
frequency = HC_FREQ
req_one_access = list(ACCESS_MARINE_CO)
/obj/item/device/radio/listening_bug/radio_linked/hc/pvst
frequency = PVST_FREQ

/obj/item/device/radio/listening_bug/radio_linked/wy
frequency = WY_FREQ
req_one_access = list(ACCESS_WY_EXEC, ACCESS_WY_SECURITY)

/obj/item/device/radio/listening_bug/radio_linked/wy/pmc
frequency = PMC_CCT_FREQ
req_one_access = list(ACCESS_WY_EXEC, ACCESS_WY_SECURITY)

/obj/item/device/radio/listening_bug/radio_linked/upp
frequency = UPP_CCT_FREQ
req_one_access = list(ACCESS_UPP_COMMANDO, ACCESS_UPP_SECURITY)

/obj/item/device/radio/listening_bug/radio_linked/upp/commando
frequency = UPP_KDO_FREQ
req_one_access = list(ACCESS_UPP_COMMANDO)


// ENCRYPTION KEYS FOR LISTENING IN!
//REQURIES SUBSPACE ACTIVATION ON THE BUGS FIRST!
/obj/item/device/encryptionkey/listening_bug
desc = "A small encryption key for listening to a secret broadcasting device! Unlikely to work if the device is not using subspace communications!"
icon_state = "stripped_key"

/obj/item/device/encryptionkey/listening_bug/freq_a
name = "Listening Bug Encryption Key (A)"
channels = list(RADIO_CHANNEL_BUG_A = TRUE)

/obj/item/device/encryptionkey/listening_bug/freq_b
name = "Listening Bug Encryption Key (B)"
channels = list(RADIO_CHANNEL_BUG_B = TRUE)
5 changes: 5 additions & 0 deletions code/modules/clothing/clothing.dm
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@
return
..()

/obj/item/clothing/hear_talk(mob/M, msg)
for(var/obj/item/clothing/accessory/attached in accessories)
attached.hear_talk(M, msg)
..()

/obj/item/clothing/proc/get_armor(armortype)
var/armor_total = 0
var/armor_count = 0
Expand Down
3 changes: 3 additions & 0 deletions code/modules/gear_presets/uscm_event.dm
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,9 @@
new_human.equip_to_slot_or_del(new back_item(new_human), WEAR_BACK)
new_human.equip_to_slot_or_del(new /obj/item/device/taperecorder(new_human), WEAR_L_STORE)
new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/general/large(new_human), WEAR_R_STORE)
new_human.equip_to_slot_or_del(new /obj/item/device/radio/listening_bug/radio_linked/hc/pvst(new_human), WEAR_IN_R_STORE)
new_human.equip_to_slot_or_del(new /obj/item/device/radio/listening_bug/radio_linked/hc/pvst(new_human), WEAR_IN_R_STORE)
new_human.equip_to_slot_or_del(new /obj/item/device/radio/listening_bug/radio_linked/hc/pvst(new_human), WEAR_IN_R_STORE)
new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/MP/provost/light/flexi(new_human.back), WEAR_IN_BACK)

new_human.equip_to_slot_or_del(new /obj/item/device/flash(new_human), WEAR_IN_JACKET)
Expand Down
7 changes: 7 additions & 0 deletions code/modules/mob/living/say.dm
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
listening += M
hearturfs += M.locs[1]
for(var/obj/O in M.contents)
var/obj/item/clothing/worn_item = O
if((O.flags_atom & USES_HEARING) || ((istype(worn_item) && worn_item.accessories)))
listening_obj |= O
else if(istype(I, /obj/structure/surface))
var/obj/structure/surface/table = I
hearturfs += table.locs[1]
for(var/obj/O in table.contents)
if(O.flags_atom & USES_HEARING)
listening_obj |= O
else if(istype(I, /obj/))
Expand Down
1 change: 1 addition & 0 deletions colonialmarines.dme
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,7 @@
#include "code\game\objects\items\devices\radio\encryptionkey.dm"
#include "code\game\objects\items\devices\radio\headset.dm"
#include "code\game\objects\items\devices\radio\intercom.dm"
#include "code\game\objects\items\devices\radio\listening_bugs.dm"
#include "code\game\objects\items\devices\radio\radio.dm"
#include "code\game\objects\items\explosives\explosive.dm"
#include "code\game\objects\items\explosives\mine.dm"
Expand Down
Loading

0 comments on commit ee43d8d

Please sign in to comment.