Files
Bubberstation/code/game/communications.dm
Watermelon914 17d39f7af0 Upgrades bot voice lines (#83847)
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may
not be viewable. -->
<!-- You can view Contributing.MD for a detailed description of the pull
request process. -->

## About The Pull Request

<!-- Describe The Pull Request. Please be sure every change is
documented or this can delay review and even discourage maintainers from
merging your PR! -->
As the title says. Upgrades the menu and allows you to create action
button shortcuts.


![image](https://github.com/tgstation/tgstation/assets/37270891/902d5e2b-de50-463e-a566-062ee24cc138)

![image](https://github.com/tgstation/tgstation/assets/37270891/0ce025e3-2f38-4013-8cde-00af12584086)

![image](https://github.com/tgstation/tgstation/assets/37270891/88ad4a30-dd65-4275-912c-b341264d77fd)

Additionally, bots can now select the radio channel if they have one.

Lowers the cooldown for voice lines.

## Why It's Good For The Game

<!-- Argue for the merits of your changes and how they benefit the game,
especially if they are controversial and/or far reaching. If you can't
actually explain WHY what you are doing will improve the game, then it
probably isn't good for the game in the first place. -->
Being able to more conveniently send a voice line by pressing a button
without needing to open up an interface and navigate to the correct line
will make using these a lot easier in relevant situations.

Voice line cooldowns are lowered a bit just to make voice lines easier
to use in relevant situations.
I.e. 'Extinguishing fire' -> 5 seconds later -> 'Fire extinguished'

Abuse should generally just be punished by the admins, the cooldown
doesn't need to be too lengthy.

## Changelog

<!-- If your PR modifies aspects of the game that can be concretely
observed by players or admins you should add a changelog. If your change
does NOT meet this description, remove this section. Be sure to properly
mark your PRs to prevent unnecessary GBP loss. You can read up on GBP
and it's effects on PRs in the tgstation guides for contributors. Please
note that maintainers freely reserve the right to remove and add tags
should they deem it appropriate. You can attempt to finagle the system
all you want, but it's best to shoot for clear communication right off
the bat. -->

🆑
qol: Bots can now bind voice lines to an action slot. Lowers cooldown
for all bot voicelines to 5 seconds.
/🆑

<!-- Both 🆑's are required for the changelog to work! You can put
your name to the right of the first 🆑 if you want to overwrite your
GitHub username as author ingame. -->
<!-- You can use multiple of the same prefix (they're only used for the
icon ingame) and delete the unneeded ones. Despite some of the tags,
changelogs should generally represent how a player might be affected by
the changes rather than a summary of the PR's contents. -->

---------

Co-authored-by: Watermelon914 <3052169-Watermelon914@users.noreply.gitlab.com>
2024-06-13 13:06:49 +00:00

250 lines
8.9 KiB
Plaintext

/*
* HOW IT WORKS
*
*The SSradio is a global object maintaining all radio transmissions, think about it as about "ether".
*Note that walkie-talkie, intercoms and headsets handle transmission using nonstandard way.
*procs:
*
* add_object(obj/device as obj, new_frequency as num, filter as text|null = null)
* Adds listening object.
* parameters:
* device - device receiving signals, must have proc receive_signal (see description below).
* one device may listen several frequencies, but not same frequency twice.
* new_frequency - see possibly frequencies below;
* filter - thing for optimization. Optional, but recommended.
* All filters should be consolidated in this file, see defines later.
* Device without listening filter will receive all signals (on specified frequency).
* Device with filter will receive any signals sent without filter.
* Device with filter will not receive any signals sent with different filter.
* returns:
* Reference to frequency object.
*
* remove_object (obj/device, old_frequency)
* Obliviously, after calling this proc, device will not receive any signals on old_frequency.
* Other frequencies will left unaffected.
*
*return_frequency(var/frequency as num)
* returns:
* Reference to frequency object. Use it if you need to send and do not need to listen.
*
*radio_frequency is a global object maintaining list of devices that listening specific frequency.
*procs:
*
* post_signal(obj/source as obj|null, datum/signal/signal, filter as text|null = null, range as num|null = null)
* Sends signal to all devices that wants such signal.
* parameters:
* source - object, emitted signal. Usually, devices will not receive their own signals.
* signal - see description below.
* filter - described above.
* range - radius of regular byond's square circle on that z-level. null means everywhere, on all z-levels.
*
* obj/proc/receive_signal(datum/signal/signal, receive_method as num, receive_param)
* Handler from received signals. By default does nothing. Define your own for your object.
* Avoid of sending signals directly from this proc, use spawn(0). Do not use sleep() here please.
* parameters:
* signal - see description below. Extract all needed data from the signal before doing sleep(), spawn() or return!
* receive_method - may be TRANSMISSION_WIRE or TRANSMISSION_RADIO.
* TRANSMISSION_WIRE is currently unused.
* receive_param - for TRANSMISSION_RADIO here comes frequency.
*
* datum/signal
* vars:
* source
* an object that emitted signal. Used for debug and bearing.
* data
* list with transmitting data. Usual use pattern:
* data["msg"] = "hello world"
* encryption
* Some number symbolizing "encryption key".
* Note that game actually do not use any cryptography here.
* If receiving object don't know right key, it must ignore encrypted signal in its receive_signal.
*
*/
/* the radio controller is a confusing piece of shit and didnt work
so i made radios not use the radio controller.
*/
GLOBAL_LIST_EMPTY(all_radios)
/proc/add_radio(obj/item/radio, freq)
if(!freq || !radio)
return
if(!GLOB.all_radios["[freq]"])
GLOB.all_radios["[freq]"] = list(radio)
return freq
GLOB.all_radios["[freq]"] |= radio
return freq
/proc/remove_radio(obj/item/radio, freq)
if(!freq || !radio)
return
if(!GLOB.all_radios["[freq]"])
return
GLOB.all_radios["[freq]"] -= radio
/proc/remove_radio_all(obj/item/radio)
for(var/freq in GLOB.all_radios)
GLOB.all_radios["[freq]"] -= radio
// For information on what objects or departments use what frequencies,
// see __DEFINES/radio.dm. Mappers may also select additional frequencies for
// use in maps, such as in intercoms.
GLOBAL_LIST_INIT(radiochannels, list(
RADIO_CHANNEL_COMMON = FREQ_COMMON,
RADIO_CHANNEL_SCIENCE = FREQ_SCIENCE,
RADIO_CHANNEL_COMMAND = FREQ_COMMAND,
RADIO_CHANNEL_MEDICAL = FREQ_MEDICAL,
RADIO_CHANNEL_ENGINEERING = FREQ_ENGINEERING,
RADIO_CHANNEL_SECURITY = FREQ_SECURITY,
RADIO_CHANNEL_CENTCOM = FREQ_CENTCOM,
RADIO_CHANNEL_SYNDICATE = FREQ_SYNDICATE,
RADIO_CHANNEL_UPLINK = FREQ_UPLINK,
RADIO_CHANNEL_SUPPLY = FREQ_SUPPLY,
RADIO_CHANNEL_SERVICE = FREQ_SERVICE,
RADIO_CHANNEL_AI_PRIVATE = FREQ_AI_PRIVATE,
RADIO_CHANNEL_CTF_RED = FREQ_CTF_RED,
RADIO_CHANNEL_CTF_BLUE = FREQ_CTF_BLUE,
RADIO_CHANNEL_CTF_GREEN = FREQ_CTF_GREEN,
RADIO_CHANNEL_CTF_YELLOW = FREQ_CTF_YELLOW
))
GLOBAL_LIST_INIT(reverseradiochannels, list(
"[FREQ_COMMON]" = RADIO_CHANNEL_COMMON,
"[FREQ_SCIENCE]" = RADIO_CHANNEL_SCIENCE,
"[FREQ_COMMAND]" = RADIO_CHANNEL_COMMAND,
"[FREQ_MEDICAL]" = RADIO_CHANNEL_MEDICAL,
"[FREQ_ENGINEERING]" = RADIO_CHANNEL_ENGINEERING,
"[FREQ_SECURITY]" = RADIO_CHANNEL_SECURITY,
"[FREQ_CENTCOM]" = RADIO_CHANNEL_CENTCOM,
"[FREQ_SYNDICATE]" = RADIO_CHANNEL_SYNDICATE,
"[FREQ_UPLINK]" = RADIO_CHANNEL_UPLINK,
"[FREQ_SUPPLY]" = RADIO_CHANNEL_SUPPLY,
"[FREQ_SERVICE]" = RADIO_CHANNEL_SERVICE,
"[FREQ_AI_PRIVATE]" = RADIO_CHANNEL_AI_PRIVATE,
"[FREQ_CTF_RED]" = RADIO_CHANNEL_CTF_RED,
"[FREQ_CTF_BLUE]" = RADIO_CHANNEL_CTF_BLUE,
"[FREQ_CTF_GREEN]" = RADIO_CHANNEL_CTF_GREEN,
"[FREQ_CTF_YELLOW]" = RADIO_CHANNEL_CTF_YELLOW
))
GLOBAL_LIST_INIT(radiocolors, list(
RADIO_CHANNEL_COMMON = "#008000",
RADIO_CHANNEL_SCIENCE = "#993399",
RADIO_CHANNEL_COMMAND = "#948f02",
RADIO_CHANNEL_MEDICAL = "#337296",
RADIO_CHANNEL_ENGINEERING = "#fb5613",
RADIO_CHANNEL_SECURITY = "#a30000",
RADIO_CHANNEL_CENTCOM = "#686868",
RADIO_CHANNEL_SYNDICATE = "#6d3f40",
RADIO_CHANNEL_SUPPLY = "#a8732b",
RADIO_CHANNEL_SERVICE = "#6eaa2c",
RADIO_CHANNEL_AI_PRIVATE = "#ff00ff",
RADIO_CHANNEL_CTF_RED = "#ff0000",
RADIO_CHANNEL_CTF_BLUE = "#0000ff",
RADIO_CHANNEL_CTF_GREEN = "#00ff00",
RADIO_CHANNEL_CTF_YELLOW = "#d1ba22"
))
/datum/radio_frequency
/// The frequency of this radio frequency. Of course.
var/frequency
/// List of filters -> list of devices
var/list/list/datum/weakref/devices = list()
/datum/radio_frequency/New(freq)
frequency = freq
//If range > 0, only post to devices on the same z_level and within range
//Use range = -1, to restrain to the same z_level without limiting range
/datum/radio_frequency/proc/post_signal(obj/source as obj|null, datum/signal/signal, filter = null as text|null, range = null as num|null)
// Ensure the signal's data is fully filled
signal.source = source
signal.frequency = frequency
//Apply filter to the signal. If none supply, broadcast to every devices
//_default channel is always checked
var/list/filter_list
if(filter)
filter_list = list(filter,"_default")
else
filter_list = devices
//If checking range, find the source turf
var/turf/start_point
if(range)
start_point = get_turf(source)
if(!start_point)
return
//Send the data
for(var/current_filter in filter_list)
for(var/datum/weakref/device_ref as anything in devices[current_filter])
var/obj/device = device_ref.resolve()
if(!device)
devices[current_filter] -= device_ref
continue
if(device == source)
continue
if(range)
var/turf/end_point = get_turf(device)
if(!end_point)
continue
if(start_point.z != end_point.z || (range > 0 && get_dist(start_point, end_point) > range))
continue
device.receive_signal(signal)
CHECK_TICK
/// Handles adding a listener to the radio frequency.
/datum/radio_frequency/proc/add_listener(obj/device, filter as text|null)
if (!filter)
filter = "_default"
var/datum/weakref/new_listener = WEAKREF(device)
if(isnull(new_listener))
return stack_trace("null, non-datum, or qdeleted device")
var/list/devices_line = devices[filter]
if(!devices_line)
devices[filter] = devices_line = list()
devices_line += new_listener
/// Handles removing a listener from this radio frequency.
/datum/radio_frequency/proc/remove_listener(obj/device)
for(var/devices_filter in devices)
var/list/devices_line = devices[devices_filter]
if(!devices_line)
devices -= devices_filter
devices_line -= WEAKREF(device)
if(!devices_line.len)
devices -= devices_filter
/**
* Proc for reacting to a received `/datum/signal`. To be implemented as needed,
* does nothing by default.
*/
/obj/proc/receive_signal(datum/signal/signal)
set waitfor = FALSE
return
/datum/signal
/// The source of this signal.
var/obj/source
/// The frequency on which this signal was emitted.
var/frequency = 0
/// The method through which this signal was transmitted.
/// See all of the `TRANSMISSION_X` in `code/__DEFINES/radio.dm` for
/// all of the possible options.
var/transmission_method
/// The data carried through this signal. Defaults to `null`, otherwise it's
/// an associative list of (string, any).
var/list/data
/// Logging data, used for logging purposes. Makes sense, right?
var/logging_data
/datum/signal/New(data, transmission_method = TRANSMISSION_RADIO, logging_data = null)
src.data = data || list()
src.transmission_method = transmission_method
src.logging_data = logging_data