-
Notifications
You must be signed in to change notification settings - Fork 4.9k
/
say.dm
231 lines (195 loc) · 7.97 KB
/
say.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
/*
Miauw's big Say() rewrite.
This file has the basic atom/movable level speech procs.
And the base of the send_speech() proc, which is the core of saycode.
*/
GLOBAL_LIST_INIT(freqtospan, list(
"[FREQ_SCIENCE]" = "sciradio",
"[FREQ_MEDICAL]" = "medradio",
"[FREQ_ENGINEERING]" = "engradio",
"[FREQ_SUPPLY]" = "suppradio",
"[FREQ_SERVICE]" = "servradio",
"[FREQ_SECURITY]" = "secradio",
"[FREQ_COMMAND]" = "comradio",
"[FREQ_AI_PRIVATE]" = "aiprivradio",
"[FREQ_SYNDICATE]" = "syndradio",
"[FREQ_CENTCOM]" = "centcomradio",
"[FREQ_CTF_RED]" = "redteamradio",
"[FREQ_CTF_BLUE]" = "blueteamradio",
"[FREQ_CTF_GREEN]" = "greenteamradio",
"[FREQ_CTF_YELLOW]" = "yellowteamradio"
))
/atom/movable/proc/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
if(!can_speak())
return
if(sanitize)
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
if(message == "" || !message)
return
spans |= speech_span
if(!language)
language = get_selected_language()
send_speech(message, 7, src, , spans, message_language=language)
/atom/movable/proc/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list())
SEND_SIGNAL(src, COMSIG_MOVABLE_HEAR, args)
/atom/movable/proc/can_speak()
//SHOULD_BE_PURE(TRUE)
return TRUE
/atom/movable/proc/send_speech(message, range = 7, obj/source = src, bubble_type, list/spans, datum/language/message_language = null, list/message_mods = list())
var/rendered = compose_message(src, message_language, message, , spans, message_mods)
for(var/atom/movable/AM as anything in get_hearers_in_view(range, source))
AM.Hear(rendered, src, message_language, message, , spans, message_mods)
/atom/movable/proc/compose_message(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), face_name = FALSE)
//This proc uses text() because it is faster than appending strings. Thanks BYOND.
//Basic span
var/spanpart1 = "<span class='[radio_freq ? get_radio_span(radio_freq) : "game say"]'>"
//Start name span.
var/spanpart2 = "<span class='name'>"
//Radio freq/name display
var/freqpart = radio_freq ? "\[[get_radio_name(radio_freq)]\] " : ""
//Speaker name
var/namepart = "[speaker.GetVoice()][speaker.get_alt_name()]"
if(face_name && ishuman(speaker))
var/mob/living/carbon/human/H = speaker
namepart = "[H.get_face_name()]" //So "fake" speaking like in hallucinations does not give the speaker away if disguised
//End name span.
var/endspanpart = "</span>"
//Message
var/messagepart = " <span class='message'>[say_emphasis(lang_treat(speaker, message_language, raw_message, spans, message_mods))]</span></span>"
var/languageicon = ""
var/datum/language/D = GLOB.language_datum_instances[message_language]
if(istype(D) && D.display_icon(src))
languageicon = "[D.get_icon()] "
return "[spanpart1][spanpart2][freqpart][languageicon][compose_track_href(speaker, namepart)][namepart][compose_job(speaker, message_language, raw_message, radio_freq)][endspanpart][messagepart]"
/atom/movable/proc/compose_track_href(atom/movable/speaker, message_langs, raw_message, radio_freq)
return ""
/atom/movable/proc/compose_job(atom/movable/speaker, message_langs, raw_message, radio_freq)
return ""
/atom/movable/proc/say_mod(input, list/message_mods = list())
var/ending = copytext_char(input, -1)
if(copytext_char(input, -2) == "!!")
return verb_yell
else if(message_mods[MODE_SING])
. = verb_sing
else if(ending == "?")
return verb_ask
else if(ending == "!")
return verb_exclaim
else
return verb_say
/atom/movable/proc/say_quote(input, list/spans=list(speech_span), list/message_mods = list())
if(!input)
input = "..."
if(copytext_char(input, -2) == "!!")
spans |= SPAN_YELL
var/spanned = attach_spans(input, spans)
return "[say_mod(input, message_mods)], \"[spanned]\""
/// Transforms the speech emphasis mods from [/atom/movable/proc/say_emphasis] into the appropriate HTML tags. Includes escaping.
#define ENCODE_HTML_EMPHASIS(input, char, html, varname) \
var/static/regex/##varname = regex("(?<!\\\\)[char](.+?)(?<!\\\\)[char]", "g");\
input = varname.Replace_char(input, "<[html]>$1</[html]>")
/// Scans the input sentence for speech emphasis modifiers, notably |italics|, +bold+, and _underline_ -mothblocks
/atom/movable/proc/say_emphasis(input)
ENCODE_HTML_EMPHASIS(input, "\\|", "i", italics)
ENCODE_HTML_EMPHASIS(input, "\\+", "b", bold)
ENCODE_HTML_EMPHASIS(input, "_", "u", underline)
var/static/regex/remove_escape_backlashes = regex("\\\\(_|\\+|\\|)", "g") // Removes backslashes used to escape text modification.
input = remove_escape_backlashes.Replace_char(input, "$1")
return input
#undef ENCODE_HTML_EMPHASIS
/atom/movable/proc/lang_treat(atom/movable/speaker, datum/language/language, raw_message, list/spans, list/message_mods = list(), no_quote = FALSE)
if(has_language(language))
var/atom/movable/AM = speaker.GetSource()
if(AM) //Basically means "if the speaker is virtual"
return no_quote ? raw_message : AM.say_quote(raw_message, spans, message_mods)
else
return no_quote ? raw_message : speaker.say_quote(raw_message, spans, message_mods)
else if(language)
var/atom/movable/AM = speaker.GetSource()
var/datum/language/D = GLOB.language_datum_instances[language]
raw_message = D.scramble(raw_message)
if(AM)
return no_quote ? raw_message : AM.say_quote(raw_message, spans, message_mods)
else
return no_quote ? raw_message : speaker.say_quote(raw_message, spans, message_mods)
else
return "makes a strange sound."
/proc/get_radio_span(freq)
var/returntext = GLOB.freqtospan["[freq]"]
if(returntext)
return returntext
return "radio"
/proc/get_radio_name(freq)
var/returntext = GLOB.reverseradiochannels["[freq]"]
if(returntext)
return returntext
return "[copytext_char("[freq]", 1, 4)].[copytext_char("[freq]", 4, 5)]"
/proc/attach_spans(input, list/spans)
return "[message_spans_start(spans)][input]</span>"
/proc/message_spans_start(list/spans)
var/output = "<span class='"
for(var/S in spans)
output = "[output][S] "
output = "[output]'>"
return output
/proc/say_test(text)
var/ending = copytext_char(text, -1)
if (ending == "?")
return "1"
else if (ending == "!")
return "2"
return "0"
/atom/movable/proc/GetVoice()
return "[src]" //Returns the atom's name, prepended with 'The' if it's not a proper noun
/atom/movable/proc/IsVocal()
return 1
/atom/movable/proc/get_alt_name()
//HACKY VIRTUALSPEAKER STUFF BEYOND THIS POINT
//these exist mostly to deal with the AIs hrefs and job stuff.
/atom/movable/proc/GetJob() //Get a job, you lazy butte
/atom/movable/proc/GetSource()
/atom/movable/proc/GetRadio()
//VIRTUALSPEAKERS
/atom/movable/virtualspeaker
var/job
var/atom/movable/source
var/obj/item/radio/radio
INITIALIZE_IMMEDIATE(/atom/movable/virtualspeaker)
/atom/movable/virtualspeaker/Initialize(mapload, atom/movable/M, _radio)
. = ..()
radio = _radio
source = M
if(istype(M))
name = radio.anonymize ? "Unknown" : M.GetVoice()
verb_say = M.verb_say
verb_ask = M.verb_ask
verb_exclaim = M.verb_exclaim
verb_yell = M.verb_yell
// The mob's job identity
if(ishuman(M))
// Humans use their job as seen on the crew manifest. This is so the AI
// can know their job even if they don't carry an ID.
var/datum/data/record/findjob = find_record("name", name, GLOB.data_core.general)
if(findjob)
job = findjob.fields["rank"]
else
job = "Unknown"
else if(iscarbon(M)) // Carbon nonhuman
job = "No ID"
else if(isAI(M)) // AI
job = "AI"
else if(iscyborg(M)) // Cyborg
var/mob/living/silicon/robot/B = M
job = "[B.designation] Cyborg"
else if(istype(M, /mob/living/silicon/pai)) // Personal AI (pAI)
job = "Personal AI"
else if(isobj(M)) // Cold, emotionless machines
job = "Machine"
else // Unidentifiable mob
job = "Unknown"
/atom/movable/virtualspeaker/GetJob()
return job
/atom/movable/virtualspeaker/GetSource()
return source
/atom/movable/virtualspeaker/GetRadio()
return radio