/
communications.dm
231 lines (210 loc) · 8.33 KB
/
communications.dm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/*
* 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
))
/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