Files
Bubberstation/code/game/objects/items/devices/radio/headset.dm
SmArtKar 291c3142f7 Gives jammers a secondary ability that allows to disable someone's radio mic (#87149)
## About The Pull Request

Radio jammers can now disable someone's radio mic, both in AOE (right
self-click on cooldown) and for on a specific target. For this to work
headsets had to be adjusted to require broadcasting to be active in
order to use their mic (it is now active by default and doesn't pick up
nearby speech automatically).

## Why It's Good For The Game

Jammers aren't very strong and this buff will allow for better stealth
gameplay, as you cannot tell that you've been jammed this way until its
too late as you still can hear others' speech. Requested by Mothblocks
on discord (thus no GBP label).

## Changelog
🆑
add: Jammers can now disable players' radio microphones via a new
secondary ability, both for handhelds and headsets
/🆑
2024-10-21 22:11:19 +13:00

487 lines
17 KiB
Plaintext

// Used for translating channels to tokens on examination
GLOBAL_LIST_INIT(channel_tokens, list(
RADIO_CHANNEL_COMMON = RADIO_KEY_COMMON,
RADIO_CHANNEL_SCIENCE = RADIO_TOKEN_SCIENCE,
RADIO_CHANNEL_COMMAND = RADIO_TOKEN_COMMAND,
RADIO_CHANNEL_MEDICAL = RADIO_TOKEN_MEDICAL,
RADIO_CHANNEL_ENGINEERING = RADIO_TOKEN_ENGINEERING,
RADIO_CHANNEL_SECURITY = RADIO_TOKEN_SECURITY,
RADIO_CHANNEL_CENTCOM = RADIO_TOKEN_CENTCOM,
RADIO_CHANNEL_SYNDICATE = RADIO_TOKEN_SYNDICATE,
RADIO_CHANNEL_SUPPLY = RADIO_TOKEN_SUPPLY,
RADIO_CHANNEL_SERVICE = RADIO_TOKEN_SERVICE,
MODE_BINARY = MODE_TOKEN_BINARY,
RADIO_CHANNEL_AI_PRIVATE = RADIO_TOKEN_AI_PRIVATE,
RADIO_CHANNEL_ENTERTAINMENT = RADIO_TOKEN_ENTERTAINMENT,
))
/obj/item/radio/headset
name = "radio headset"
desc = "An updated, modular intercom that fits over the head. Takes encryption keys."
icon = 'icons/obj/clothing/headsets.dmi'
icon_state = "headset"
inhand_icon_state = "headset"
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
worn_icon_state = "headset"
custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT * 0.75)
subspace_transmission = TRUE
canhear_range = 0 // can't hear headsets from very far away
interaction_flags_mouse_drop = FORBID_TELEKINESIS_REACH
slot_flags = ITEM_SLOT_EARS
dog_fashion = null
var/obj/item/encryptionkey/keyslot2 = null
/// A list of all languages that this headset allows the user to understand. Populated by language encryption keys.
var/list/language_list
// headset is too small to display overlays
overlay_speaker_idle = null
overlay_speaker_active = null
overlay_mic_idle = null
overlay_mic_active = null
/obj/item/radio/headset/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] begins putting \the [src]'s antenna up [user.p_their()] nose! It looks like [user.p_theyre()] trying to give [user.p_them()]self cancer!"))
return TOXLOSS
/obj/item/radio/headset/examine(mob/user)
. = ..()
if(item_flags & IN_INVENTORY && loc == user)
// construction of frequency description
var/list/avail_chans = list("Use [RADIO_KEY_COMMON] for the currently tuned frequency")
if(special_channels & RADIO_SPECIAL_BINARY)
avail_chans += "use [MODE_TOKEN_BINARY] for [MODE_BINARY]"
if(length(channels))
for(var/i in 1 to length(channels))
if(i == 1)
avail_chans += "use [MODE_TOKEN_DEPARTMENT] or [GLOB.channel_tokens[channels[i]]] for [LOWER_TEXT(channels[i])]"
else
avail_chans += "use [GLOB.channel_tokens[channels[i]]] for [LOWER_TEXT(channels[i])]"
. += span_notice("A small screen on the headset displays the following available frequencies:\n[english_list(avail_chans)].")
if(command)
. += span_info("Alt-click to toggle the high-volume mode.")
else
. += span_notice("A small screen on the headset flashes, it's too small to read without holding or wearing the headset.")
/obj/item/radio/headset/Initialize(mapload)
. = ..()
if(ispath(keyslot2))
keyslot2 = new keyslot2()
set_listening(TRUE)
set_broadcasting(TRUE)
recalculateChannels()
possibly_deactivate_in_loc()
/obj/item/radio/headset/proc/possibly_deactivate_in_loc()
if(ismob(loc))
set_listening(should_be_listening)
else
set_listening(FALSE, actual_setting = FALSE)
/obj/item/radio/headset/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
. = ..()
possibly_deactivate_in_loc()
/obj/item/radio/headset/Destroy()
if(istype(keyslot2))
QDEL_NULL(keyslot2)
return ..()
/obj/item/radio/headset/ui_data(mob/user)
. = ..()
.["headset"] = TRUE
/obj/item/radio/headset/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
if(user == over)
return attack_self(user)
/// Grants all the languages this headset allows the mob to understand via installed chips.
/obj/item/radio/headset/proc/grant_headset_languages(mob/grant_to)
for(var/language in language_list)
grant_to.grant_language(language, language_flags = UNDERSTOOD_LANGUAGE, source = LANGUAGE_RADIOKEY)
/obj/item/radio/headset/equipped(mob/user, slot, initial)
. = ..()
if(!(slot_flags & slot))
return
grant_headset_languages(user)
/obj/item/radio/headset/dropped(mob/user, silent)
. = ..()
if(QDELETED(src)) //This can be called as a part of destroy
return
for(var/language in language_list)
user.remove_language(language, language_flags = UNDERSTOOD_LANGUAGE, source = LANGUAGE_RADIOKEY)
// Headsets do not become hearing sensitive as broadcasting instead controls their talk_into capabilities
/obj/item/radio/headset/set_broadcasting(new_broadcasting, actual_setting = TRUE)
broadcasting = new_broadcasting
if(actual_setting)
should_be_broadcasting = broadcasting
if (perform_update_icon && !isnull(overlay_mic_idle))
update_icon()
else if (!perform_update_icon)
should_update_icon = TRUE
/obj/item/radio/headset/talk_into_impl(atom/movable/talking_movable, message, channel, list/spans, datum/language/language, list/message_mods)
if (!broadcasting)
return
return ..()
/obj/item/radio/headset/syndicate //disguised to look like a normal headset for stealth ops
/obj/item/radio/headset/syndicate/Initialize(mapload)
. = ..()
make_syndie()
/obj/item/radio/headset/syndicate/alt //undisguised bowman with flash protection
name = "syndicate headset"
desc = "A syndicate headset that can be used to hear all radio frequencies. Protects ears from flashbangs."
icon_state = "syndie_headset"
worn_icon_state = "syndie_headset"
/obj/item/radio/headset/syndicate/alt/Initialize(mapload)
. = ..()
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS))
/obj/item/radio/headset/syndicate/alt/leader
name = "team leader headset"
command = TRUE
/obj/item/radio/headset/binary
keyslot = /obj/item/encryptionkey/binary
/obj/item/radio/headset/headset_sec
name = "security radio headset"
desc = "This is used by your elite security force."
icon_state = "sec_headset"
worn_icon_state = "sec_headset"
keyslot = /obj/item/encryptionkey/headset_sec
/obj/item/radio/headset/headset_sec/alt
name = "security bowman headset"
desc = "This is used by your elite security force. Protects ears from flashbangs."
icon_state = "sec_headset_alt"
worn_icon_state = "sec_headset_alt"
/obj/item/radio/headset/headset_sec/alt/Initialize(mapload)
. = ..()
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS))
/obj/item/radio/headset/headset_eng
name = "engineering radio headset"
desc = "When the engineers wish to chat like girls."
icon_state = "eng_headset"
worn_icon_state = "eng_headset"
keyslot = /obj/item/encryptionkey/headset_eng
/obj/item/radio/headset/headset_rob
name = "robotics radio headset"
desc = "Made specifically for the roboticists, who cannot decide between departments."
icon_state = "rob_headset"
worn_icon_state = "rob_headset"
keyslot = /obj/item/encryptionkey/headset_rob
/obj/item/radio/headset/headset_med
name = "medical radio headset"
desc = "A headset for the trained staff of the medbay."
icon_state = "med_headset"
worn_icon_state = "med_headset"
keyslot = /obj/item/encryptionkey/headset_med
/obj/item/radio/headset/headset_sci
name = "science radio headset"
desc = "A sciency headset. Like usual."
icon_state = "sci_headset"
worn_icon_state = "sci_headset"
keyslot = /obj/item/encryptionkey/headset_sci
/obj/item/radio/headset/headset_medsci
name = "medical research radio headset"
desc = "A headset that is a result of the mating between medical and science."
icon_state = "medsci_headset"
worn_icon_state = "medsci_headset"
keyslot = /obj/item/encryptionkey/headset_medsci
/obj/item/radio/headset/headset_srvsec
name = "law and order headset"
desc = "In the criminal justice headset, the encryption key represents two separate but equally important groups. Sec, who investigate crime, and Service, who provide services. These are their comms."
icon_state = "srvsec_headset"
worn_icon_state = "srvsec_headset"
keyslot = /obj/item/encryptionkey/headset_srvsec
/obj/item/radio/headset/headset_srvmed
name = "service medical headset"
desc = "A headset allowing the wearer to communicate with medbay and service."
icon_state = "srv_headset"
worn_icon_state = "srv_headset"
keyslot = /obj/item/encryptionkey/headset_srvmed
/obj/item/radio/headset/headset_srvent
name = "press headset"
desc = "A headset allowing the wearer to communicate with service and broadcast to entertainment channel."
icon_state = "srvent_headset"
worn_icon_state = "srv_headset"
keyslot = /obj/item/encryptionkey/headset_srvent
/obj/item/radio/headset/headset_com
name = "command radio headset"
desc = "A headset with a commanding channel."
icon_state = "com_headset"
worn_icon_state = "com_headset"
keyslot = /obj/item/encryptionkey/headset_com
/obj/item/radio/headset/heads
command = TRUE
/obj/item/radio/headset/heads/captain
name = "\proper the captain's headset"
desc = "The headset of the king."
icon_state = "com_headset"
worn_icon_state = "com_headset"
keyslot = /obj/item/encryptionkey/heads/captain
/obj/item/radio/headset/heads/captain/alt
name = "\proper the captain's bowman headset"
desc = "The headset of the boss. Protects ears from flashbangs."
icon_state = "com_headset_alt"
worn_icon_state = "com_headset_alt"
/obj/item/radio/headset/heads/captain/alt/Initialize(mapload)
. = ..()
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS))
/obj/item/radio/headset/heads/rd
name = "\proper the research director's headset"
desc = "Headset of the fellow who keeps society marching towards technological singularity."
icon_state = "com_headset"
worn_icon_state = "com_headset"
keyslot = /obj/item/encryptionkey/heads/rd
/obj/item/radio/headset/heads/hos
name = "\proper the head of security's headset"
desc = "The headset of the man in charge of keeping order and protecting the station."
icon_state = "com_headset"
worn_icon_state = "com_headset"
keyslot = /obj/item/encryptionkey/heads/hos
/obj/item/radio/headset/heads/hos/advisor
name = "\proper the veteran security advisor headset"
desc = "The headset of the man who was in charge of keeping order and protecting the station..."
icon_state = "com_headset"
worn_icon_state = "com_headset"
keyslot = /obj/item/encryptionkey/heads/hos
command = FALSE
/obj/item/radio/headset/heads/hos/alt
name = "\proper the head of security's bowman headset"
desc = "The headset of the man in charge of keeping order and protecting the station. Protects ears from flashbangs."
icon_state = "com_headset_alt"
worn_icon_state = "com_headset_alt"
/obj/item/radio/headset/heads/hos/alt/Initialize(mapload)
. = ..()
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS))
/obj/item/radio/headset/heads/ce
name = "\proper the chief engineer's headset"
desc = "The headset of the guy in charge of keeping the station powered and undamaged."
icon_state = "com_headset"
worn_icon_state = "com_headset"
keyslot = /obj/item/encryptionkey/heads/ce
/obj/item/radio/headset/heads/cmo
name = "\proper the chief medical officer's headset"
desc = "The headset of the highly trained medical chief."
icon_state = "com_headset"
worn_icon_state = "com_headset"
keyslot = /obj/item/encryptionkey/heads/cmo
/obj/item/radio/headset/heads/hop
name = "\proper the head of personnel's headset"
desc = "The headset of the guy who will one day be captain."
icon_state = "com_headset"
worn_icon_state = "com_headset"
keyslot = /obj/item/encryptionkey/heads/hop
/obj/item/radio/headset/heads/qm
name = "\proper the quartermaster's headset"
desc = "The headset of the guy who runs the cargo department."
icon_state = "com_headset"
worn_icon_state = "com_headset"
keyslot = /obj/item/encryptionkey/heads/qm
/obj/item/radio/headset/headset_cargo
name = "supply radio headset"
desc = "A headset used by the QM's slaves."
icon_state = "cargo_headset"
worn_icon_state = "cargo_headset"
keyslot = /obj/item/encryptionkey/headset_cargo
/obj/item/radio/headset/headset_cargo/mining
name = "mining radio headset"
desc = "Headset used by shaft miners. It has a mining network uplink which allows the user to quickly transmit commands to their comrades and amplifies their voice in low-pressure environments."
icon_state = "mine_headset"
worn_icon_state = "mine_headset"
// "puts the antenna down" while the headset is off
overlay_speaker_idle = "headset_up"
overlay_mic_idle = "headset_up"
keyslot = /obj/item/encryptionkey/headset_mining
/obj/item/radio/headset/headset_cargo/mining/Initialize(mapload)
. = ..()
AddComponent(/datum/component/callouts, ITEM_SLOT_EARS, examine_text = span_info("Use ctrl-click to enable or disable callouts."))
/obj/item/radio/headset/headset_cargo/mining/equipped(mob/living/carbon/human/user, slot)
. = ..()
if(slot & ITEM_SLOT_EARS)
ADD_TRAIT(user, TRAIT_SPEECH_BOOSTER, CLOTHING_TRAIT)
/obj/item/radio/headset/headset_cargo/mining/dropped(mob/living/carbon/human/user)
. = ..()
REMOVE_TRAIT(user, TRAIT_SPEECH_BOOSTER, CLOTHING_TRAIT)
/obj/item/radio/headset/headset_srv
name = "service radio headset"
desc = "Headset used by the service staff, tasked with keeping the station full, happy and clean."
icon_state = "srv_headset"
worn_icon_state = "srv_headset"
keyslot = /obj/item/encryptionkey/headset_service
/obj/item/radio/headset/headset_cent
name = "\improper CentCom headset"
desc = "A headset used by the upper echelons of Nanotrasen."
icon_state = "cent_headset"
worn_icon_state = "cent_headset"
keyslot = /obj/item/encryptionkey/headset_cent
keyslot2 = /obj/item/encryptionkey/headset_com
/obj/item/radio/headset/headset_cent/empty
keyslot = null
keyslot2 = null
/obj/item/radio/headset/headset_cent/commander
keyslot2 = /obj/item/encryptionkey/heads/captain
command = TRUE
/obj/item/radio/headset/headset_cent/alt
name = "\improper CentCom bowman headset"
desc = "A headset especially for emergency response personnel. Protects ears from flashbangs."
icon_state = "cent_headset_alt"
worn_icon_state = "cent_headset_alt"
keyslot2 = null
/obj/item/radio/headset/headset_cent/alt/Initialize(mapload)
. = ..()
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS))
/obj/item/radio/headset/silicon/pai
name = "\proper mini Integrated Subspace Transceiver"
subspace_transmission = FALSE
/obj/item/radio/headset/silicon/ai
name = "\proper Integrated Subspace Transceiver"
keyslot2 = new /obj/item/encryptionkey/ai
command = TRUE
/obj/item/radio/headset/silicon/human_ai
name = "\proper Disconnected Subspace Transceiver"
desc = "A headset that is rumored to be one day implanted into a brain in a jar directly."
icon_state = "rob_headset"
worn_icon_state = "rob_headset"
keyslot2 = new /obj/item/encryptionkey/ai_with_binary
command = TRUE
/obj/item/radio/headset/silicon/ai/evil
name = "\proper Evil Integrated Subspace Transceiver"
keyslot2 = new /obj/item/encryptionkey/ai/evil
command = FALSE
/obj/item/radio/headset/silicon/ai/evil/Initialize(mapload)
. = ..()
make_syndie()
/obj/item/radio/headset/screwdriver_act(mob/living/user, obj/item/tool)
if(keyslot || keyslot2)
for(var/ch_name in channels)
SSradio.remove_object(src, GLOB.radiochannels[ch_name])
secure_radio_connections[ch_name] = null
if(keyslot)
user.put_in_hands(keyslot)
keyslot = null
if(keyslot2)
user.put_in_hands(keyslot2)
keyslot2 = null
recalculateChannels()
to_chat(user, span_notice("You pop out the encryption keys in the headset."))
else
to_chat(user, span_warning("This headset doesn't have any unique encryption keys! How useless..."))
tool.play_tool_sound(src, 10)
return TRUE
/obj/item/radio/headset/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/encryptionkey))
if(keyslot && keyslot2)
to_chat(user, span_warning("The headset can't hold another key!"))
return
if(!keyslot)
if(!user.transferItemToLoc(W, src))
return
keyslot = W
else
if(!user.transferItemToLoc(W, src))
return
keyslot2 = W
recalculateChannels()
else
return ..()
/obj/item/radio/headset/recalculateChannels()
. = ..()
if(keyslot2)
for(var/ch_name in keyslot2.channels)
if(!(ch_name in src.channels))
LAZYSET(channels, ch_name, keyslot2.channels[ch_name])
special_channels |= keyslot2.special_channels
for(var/ch_name in channels)
secure_radio_connections[ch_name] = add_radio(src, GLOB.radiochannels[ch_name])
var/list/old_language_list = language_list?.Copy()
language_list = list()
if(keyslot?.translated_language)
language_list += keyslot.translated_language
if(keyslot2?.translated_language)
language_list += keyslot2.translated_language
// If we're equipped on a mob, we should make sure all the languages
// learned from our installed key chips are all still accurate
var/mob/mob_loc = loc
if(istype(mob_loc) && mob_loc.get_item_by_slot(slot_flags) == src)
// Remove all the languages we may not be able to know anymore
for(var/language in old_language_list)
mob_loc.remove_language(language, language_flags = UNDERSTOOD_LANGUAGE, source = LANGUAGE_RADIOKEY)
// And grant all the languages we definitely should know now
grant_headset_languages(mob_loc)
/obj/item/radio/headset/click_alt(mob/living/user)
if(!istype(user) || !command)
return CLICK_ACTION_BLOCKING
use_command = !use_command
to_chat(user, span_notice("You toggle high-volume mode [use_command ? "on" : "off"]."))
return CLICK_ACTION_SUCCESS