Saycode Overhaul -- Multilingualism (#6956)

* Port ParadiseSS13/Paradise#2100 - Saycode refactor

* Removed unused old carbon slimes code

* Port ParadiseSS13/Paradise#5099 - Saycode part 2

* Ported ParadiseSS13/Paradise#7170's /datum/browser Check Known Languages

* Port ParadiseSS13/Paradise#9240 - Get rid of alt_name in favor of GetAltName()

* Port ParadiseSS13/Paradise#10330 - You can now use multiple languages in one message

* Addressed Atermonera's review.

Translators now print the full message if they find any languages within the
message that the user doesn't understand, minus languages it cannot translate.

Additionally, the combine_message proc has been significantly simplified
by eliminating an ugly tree structure with the help of a little helper
proc.

The removal of the extra span inside each piece doesn't seem to have
visually changed the messages in any other way than changing where the
wordwrap happens, strangely enough. Must be something in IE's code being
picky about invisible elements. On the bright side, it splits *later*
than it did before, thus reducing the lines a message will take up by a
tiny amount.

Also, a bunch of things now have the 'filter_say' class from
PolarisSS13/Polaris#6998. Since span classes with no definition are
totally valid and just don't do anything, this PR does **not** depend on
that PR being merged first.

* Always gotta be one
This commit is contained in:
ShadowLarkens
2020-04-20 01:11:53 -07:00
committed by GitHub
parent 1ed5556b8f
commit 54a8a5823d
72 changed files with 1338 additions and 3284 deletions

View File

@@ -13,6 +13,8 @@
#define isitem(D) istype(D, /obj/item)
#define isradio(A) istype(A, /obj/item/device/radio)
#define isairlock(A) istype(A, /obj/machinery/door/airlock)
#define isorgan(A) istype(A, /obj/item/organ/external)

View File

@@ -368,3 +368,7 @@ var/global/list/##LIST_NAME = list();\
#define MOUSE_OPACITY_TRANSPARENT 0
#define MOUSE_OPACITY_ICON 1
#define MOUSE_OPACITY_OPAQUE 2
// Used by radios to indicate that they have sent a message via something other than subspace
#define RADIO_CONNECTION_FAIL 0
#define RADIO_CONNECTION_NON_SUBSPACE 1

View File

@@ -92,22 +92,11 @@ var/const/HOLOPAD_MODE = RANGE_BASED
/*This is the proc for special two-way communication between AI and holopad/people talking near holopad.
For the other part of the code, check silicon say.dm. Particularly robot talk.*/
/obj/machinery/hologram/holopad/hear_talk(mob/living/M, text, verb, datum/language/speaking)
if(M)
/obj/machinery/hologram/holopad/hear_talk(mob/M, list/message_pieces, verb)
if(M && LAZYLEN(masters))
for(var/mob/living/silicon/ai/master in masters)
if(!master.say_understands(M, speaking))//The AI will be able to understand most mobs talking through the holopad.
if(speaking)
text = speaking.scramble(text)
else
text = stars(text)
var/name_used = M.GetVoice()
//This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only.
var/rendered
if(speaking)
rendered = "<i><span class='game say'>Holopad received, <span class='name'>[name_used]</span> [speaking.format_message(text, verb)]</span></i>"
else
rendered = "<i><span class='game say'>Holopad received, <span class='name'>[name_used]</span> [verb], <span class='message'>\"[text]\"</span></span></i>"
master.show_message(rendered, 2)
if(masters[master] && M != master)
master.relay_speech(M, message_pieces, verb)
/obj/machinery/hologram/holopad/see_emote(mob/living/M, text)
if(M)

View File

@@ -71,7 +71,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
signal.data["name"], signal.data["job"],
signal.data["realname"], signal.data["vname"],,
signal.data["compression"], signal.data["level"], signal.frequency,
signal.data["verb"], signal.data["language"] )
signal.data["verb"])
/** #### - Simple Broadcast - #### **/
@@ -97,7 +97,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
signal.data["realname"], signal.data["vname"], 4, signal.data["compression"], signal.data["level"], signal.frequency,
signal.data["verb"], signal.data["language"])
signal.data["verb"])
if(!message_delay)
message_delay = 1
@@ -165,7 +165,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
signal.data["realname"], signal.data["vname"],, signal.data["compression"], list(0), connection.frequency,
signal.data["verb"], signal.data["language"])
signal.data["verb"])
else
if(intercept)
Broadcast_Message(signal.data["connection"], signal.data["mob"],
@@ -173,7 +173,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
signal.data["realname"], signal.data["vname"], 3, signal.data["compression"], list(0), connection.frequency,
signal.data["verb"], signal.data["language"])
signal.data["verb"])
@@ -235,9 +235,9 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
**/
/proc/Broadcast_Message(var/datum/radio_frequency/connection, var/mob/M,
var/vmask, var/vmessage, var/obj/item/device/radio/radio,
var/message, var/name, var/job, var/realname, var/vname,
var/data, var/compression, var/list/level, var/freq, var/verbage = "says", var/datum/language/speaking = null)
var/vmask, var/list/vmessage_pieces, var/obj/item/device/radio/radio,
var/list/message_pieces, var/name, var/job, var/realname, var/vname,
var/data, var/compression, var/list/level, var/freq, var/verbage = "says")
/* ###### Prepare the radio connection ###### */
@@ -331,7 +331,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
else
// - The speaker has a prespecified "voice message" to display if not understood -
if (vmessage)
if(vmessage_pieces)
heard_voice += R
// - Just display a garbled message -
@@ -356,12 +356,11 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
// --- Filter the message; place it in quotes apply a verb ---
var/quotedmsg = null
if(M)
quotedmsg = M.say_quote(message)
quotedmsg = "[M.say_quote(multilingual_to_message(message_pieces))], \"[multilingual_to_message(message_pieces)]\""
else
quotedmsg = "says, \"[message]\""
quotedmsg = "says, \"[multilingual_to_message(message_pieces)]\""
// --- This following recording is intended for research and feedback in the use of department radio channels ---
@@ -402,44 +401,36 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
//End of research and feedback code.
/* ###### Send the message ###### */
/* --- Process all the mobs that heard a masked voice (understood) --- */
if(length(heard_masked))
for (var/mob/R in heard_masked)
R.hear_radio(message,verbage, speaking, part_a, part_b, part_c, M, 0, name)
R.hear_radio(message_pieces, verbage, part_a, part_b, part_c, M, 0, name)
/* --- Process all the mobs that heard the voice normally (understood) --- */
if(length(heard_normal))
for (var/mob/R in heard_normal)
R.hear_radio(message, verbage, speaking, part_a, part_b, part_c, M, 0, realname)
R.hear_radio(message_pieces, verbage, part_a, part_b, part_c, M, 0, realname)
/* --- Process all the mobs that heard the voice normally (did not understand) --- */
if(length(heard_voice))
for (var/mob/R in heard_voice)
R.hear_radio(message,verbage, speaking, part_a, part_b, part_c, M,0, vname)
R.hear_radio(message_pieces, verbage, part_a, part_b, part_c, M,0, vname)
/* --- Process all the mobs that heard a garbled voice (did not understand) --- */
// Displays garbled message (ie "f*c* **u, **i*er!")
if(length(heard_garbled))
for (var/mob/R in heard_garbled)
R.hear_radio(message, verbage, speaking, part_a, part_b, part_c, M, 1, vname)
R.hear_radio(message_pieces, verbage, part_a, part_b, part_c, M, 1, vname)
/* --- Complete gibberish. Usually happens when there's a compressed message --- */
if(length(heard_gibberish))
for (var/mob/R in heard_gibberish)
R.hear_radio(message, verbage, speaking, part_a, part_b, part_c, M, 1)
R.hear_radio(message_pieces, verbage, part_a, part_b, part_c, M, 1)
return 1
/proc/Broadcast_SimpleMessage(var/source, var/frequency, var/text, var/data, var/mob/M, var/compression, var/level)
/proc/Broadcast_SimpleMessage(var/source, var/frequency, list/message_pieces, var/data, var/mob/M, var/compression, var/level)
var/text = multilingual_to_message(message_pieces)
/* ###### Prepare the radio connection ###### */
if(!M)

View File

@@ -574,7 +574,6 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
var/encryption = "null" // encryption key: ie "password"
var/salt = "null" // encryption salt: ie "123comsat"
// would add up to md5("password123comsat")
var/language = "human"
var/obj/item/device/radio/headset/server_radio = null
/obj/machinery/telecomms/server/New()
@@ -615,12 +614,11 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
log.parameters["mobtype"] = signal.data["mobtype"]
log.parameters["job"] = signal.data["job"]
log.parameters["key"] = signal.data["key"]
log.parameters["vmessage"] = signal.data["message"]
log.parameters["vmessage"] = multilingual_to_message(signal.data["message"])
log.parameters["vname"] = signal.data["vname"]
log.parameters["message"] = signal.data["message"]
log.parameters["message"] = multilingual_to_message(signal.data["message"])
log.parameters["name"] = signal.data["name"]
log.parameters["realname"] = signal.data["realname"]
log.parameters["language"] = signal.data["language"]
var/race = "unknown"
if(ishuman(M))
@@ -649,7 +647,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
// If the signal is still compressed, make the log entry gibberish
if(signal.data["compression"] > 0)
log.parameters["message"] = Gibberish(signal.data["message"], signal.data["compression"] + 50)
log.parameters["message"] = Gibberish(multilingual_to_message(signal.data["message"]), signal.data["compression"] + 50)
log.parameters["job"] = Gibberish(signal.data["job"], signal.data["compression"] + 50)
log.parameters["name"] = Gibberish(signal.data["name"], signal.data["compression"] + 50)
log.parameters["realname"] = Gibberish(signal.data["realname"], signal.data["compression"] + 50)

View File

@@ -119,134 +119,6 @@
melee_can_hit = 1
return
/*
/obj/mecha/combat/proc/mega_shake(target)
if(!istype(target, /obj) && !istype(target, /mob)) return
if(istype(target, /mob))
var/mob/M = target
M.make_dizzy(3)
M.adjustBruteLoss(1)
M.updatehealth()
for (var/mob/V in viewers(src))
V.show_message("[src.name] shakes [M] like a rag doll.")
return
*/
/*
if(energy>0 && can_move)
if(step(src,direction))
can_move = 0
spawn(step_in) can_move = 1
if(overload)
energy = energy-2
health--
else
energy--
return 1
return 0
*/
/*
/obj/mecha/combat/hear_talk(mob/M as mob, text)
..()
if(am && M==occupant)
if(findtext(text,""))
sam()
return
/obj/mecha/combat/proc/sam()
if(am)
var/window = {"<html>
<head>
<style>
body {background:#000;color: #00ff00;font-family:"Courier",monospace;font-size:12px;}
#target {word-wrap: break-word;width:100%;padding-right:2px;}
#form {display:none;padding:0;margin:0;}
#input {background:#000;color: #00ff00;font-family:"Courier",monospace;border:none;padding:0;margin:0;width:90%;font-size:12px;}
</style>
<script type="text/javascript">
var text = "SGNL RCVD\\nTAG ANL :: STTS ACCPTD \\nINITSOC{buff:{128,0,NIL};p:'-zxf';stddev;inenc:'bin';outenc:'plain'}\\nSOD ->\\n0010101100101011001000000101010001101000011010010111001100100000011011010110000101100011011010000110100101101110011001010010000001101001011100110010000001100100011010010111001101100011011010000110000101110010011001110110010101100100001000000110100101101110011101000110111100100000011110010110111101110101011100100010000001100011011000010111001001100101001000000010101100101011000011010000101000101011001010110010000001000110011010010110011101101000011101000010000001110111011010010111010001101000001000000111010001101000011010010111001100100000011011010110000101100011011010000110100101101110011001010010110000100000011000010110111001100100001000000110011101110101011000010111001001100100001000000110100101110100001000000110011001110010011011110110110100100000011101000110100001100101001000000111001101101000011000010110110101100101001000000110111101100110001000000110010001100101011001100110010101100001011101000010000000101011001010110000110100001010001010110010101100100000010100110110010101110010011101100110010100100000011101000110100001101001011100110010000001101101011000010110001101101000011010010110111001100101001011000010000001100001011100110010000001111001011011110111010100100000011101110110111101110101011011000110010000100000011010000110000101110110011001010010000001100110011010010110011101101000011101000010000001101001011101000010000001100110011011110111001000100000011110010110111101110101001000000010101100101011\\n<- EOD\\nSOCFLUSH\\n";
var target_id = "target";
var form_id = "form";
var input_id = "input";
var delay=5;
var currentChar=0;
var inter;
var cur_el;
var maiden_el;
function type()
{
maiden_el = cur_el = document.getElementById(target_id);
if(cur_el && typeof(cur_el)!='undefined'){
inter = setInterval(function(){appendText(cur_el)},delay);
}
}
function appendText(el){
if(currentChar>text.length){
maiden_el.style.border = 'none';
clearInterval(inter);
var form = document.getElementById(form_id);
var input = document.getElementById(input_id);
if((form && typeof(form)!='undefined') && (input && typeof(input)!='undefined')){
form.style.display = 'block';
input.focus();
}
return;
}
var tchar = text.substr(currentChar, 1);
if(tchar=='\\n'){
el = cur_el = document.createElement('div');
maiden_el.appendChild(cur_el);
currentChar++;
return;
}
if(!el.firstChild){
var tNode=document.createTextNode(tchar);
el.appendChild(tNode);
}
else {
el.firstChild.nodeValue = el.firstChild.nodeValue+tchar
}
currentChar++;
}
function addSubmitEvent(form, input) {
input.onkeydown = function(e) {
e = e || window.event;
if (e.keyCode == 13) {
form.submit();
return false;
}
};
}
window.onload = function(){
var form = document.getElementById(form_id);
var input = document.getElementById(input_id);
if((!form || typeof(form)=='undefined') || (!input || typeof(input)=='undefined')){
return false;
}
addSubmitEvent(form,input);
type();
}
</script>
</head>
<body>
<div id="wrapper"><div id="target"></div>
<form id="form" name="form" action="byond://" method="get">
<label for="input">&gt;</label><input name="saminput" type="text" id="input" value="" />
<input type=\"hidden\" name=\"src\" value=\"\ref[src]\">
</form>
</div>
</body>
</html>
"}
occupant << browse(window, "window=sam;size=800x600;")
onclose(occupant, "sam", src)
return
*/
/obj/mecha/combat/moved_inside(var/mob/living/carbon/human/H as mob)
if(..())
if(H.client)
@@ -275,10 +147,3 @@
if(top_filter.get("close"))
am = null
return
/*
if(top_filter.get("saminput"))
if(md5(top_filter.get("saminput")) == am)
occupant_message("From the lies of the Antipath, Circuit preserve us.")
am = null
return
*/

View File

@@ -290,10 +290,9 @@
/obj/mecha/proc/drop_item()//Derpfix, but may be useful in future for engineering exosuits.
return
/obj/mecha/hear_talk(mob/M as mob, text)
/obj/mecha/hear_talk(mob/M, list/message_pieces, verb)
if(M == occupant && radio.broadcasting)
radio.talk_into(M, text)
return
radio.talk_into(M, message_pieces)
////////////////////////////
///// Action processing ////

View File

@@ -220,30 +220,22 @@
..()
// Proc: hear_talk()
// Parameters: 4 (M - the mob the speech originated from, text - what is being said, verb - the word used to describe how text is being said, speaking - language
// being used)
// Parameters: 3 (M - the mob the speech originated from,
// list/message_pieces - what is being said w/ baked languages,
// verb - the word used to describe how text is being said)
// Description: Relays the speech to all linked communicators.
/obj/item/device/communicator/hear_talk(mob/living/M, text, verb, datum/language/speaking)
/obj/item/device/communicator/hear_talk(mob/M, list/message_pieces, verb)
for(var/obj/item/device/communicator/comm in communicating)
var/turf/T = get_turf(comm)
if(!T) return
var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0)
var/list/mobs_to_relay = in_range["mobs"]
for(var/mob/mob in mobs_to_relay)
//Can whoever is hearing us understand?
if(!mob.say_understands(M, speaking))
if(speaking)
text = speaking.scramble(text)
else
text = stars(text)
var/message = mob.combine_message(message_pieces, verb, M)
var/name_used = M.GetVoice()
var/rendered = null
if(speaking) //Language being used
rendered = "<span class='game say'>[bicon(src)] <span class='name'>[name_used]</span> [speaking.format_message(text, verb)]</span>"
else
rendered = "<span class='game say'>[bicon(src)] <span class='name'>[name_used]</span> [verb], <span class='message'>\"[text]\"</span></span>"
rendered = "<span class='game say'>[bicon(src)] <span class='name'>[name_used]</span> [message]</span>"
mob.show_message(rendered, 2)
// Proc: show_message()

View File

@@ -13,50 +13,49 @@
if(user.client)
if(user.client.prefs.muted & MUTE_IC)
to_chat(user, "<span class='warning'>You cannot speak in IC (muted).</span>")
return 0
return FALSE
if(!(ishuman(user) || user.isSynthetic()))
to_chat(user, "<span class='warning'>You don't know how to use this!</span>")
return 0
return FALSE
if(user.silent)
return 0
if(spamcheck)
to_chat(user, "<span class='warning'>\The [src] needs to recharge!</span>")
return 0
return 1
return FALSE
if(spamcheck > world.time)
to_chat(user, "<span class='warning'>[src] needs to recharge!</span>")
return FALSE
if(loc != user)
return FALSE
if(user.stat != CONSCIOUS)
return FALSE
return TRUE
/obj/item/device/megaphone/proc/do_broadcast(var/mob/living/user, var/message)
if ((src.loc == user && usr.stat == 0))
if(emagged)
if(insults)
user.audible_message("<B>[user]</B> broadcasts, <FONT size=3>\"[pick(insultmsg)]\"</FONT>")
user.audible_message("<B>[user.GetVoice()]</B>[user.GetAltName()] broadcasts, <FONT size=3>\"[pick(insultmsg)]\"</FONT>")
insults--
else
to_chat(user, "<span class='warning'>*BZZZZzzzzzt*</span>")
else
user.audible_message("<B>[user]</B> broadcasts, <FONT size=3>\"[message]\"</FONT>")
spamcheck = 1
spawn(20)
spamcheck = 0
return
/obj/item/device/megaphone/attack_self(mob/living/user as mob)
if(!can_broadcast(user))
return
user.audible_message("<B>[user.GetVoice()]</B>[user.GetAltName()] broadcasts, <FONT size=3>\"[message]\"</FONT>")
/obj/item/device/megaphone/attack_self(var/mob/living/user)
var/message = sanitize(input(user, "Shout a message?", "Megaphone", null) as text)
if(!message)
return
message = capitalize(message)
if(!can_broadcast(user))
return
spamcheck = world.time + 20
do_broadcast(user, message)
/obj/item/device/megaphone/emag_act(var/remaining_charges, var/mob/user)
if(!emagged)
to_chat(user, "<span class='warning'>You overload \the [src]'s voice synthesizer.</span>")
emagged = 1
to_chat(user, "<span class='warning'>You overload [src]'s voice synthesizer.</span>")
emagged = TRUE
insults = rand(1, 3)//to prevent caps spam.
return 1
return TRUE
/obj/item/device/megaphone/super
name = "gigaphone"
@@ -130,10 +129,9 @@
broadcast_color = new_color
/obj/item/device/megaphone/super/do_broadcast(var/mob/living/user, var/message)
if ((src.loc == user && usr.stat == 0))
if(emagged)
if(insults)
user.audible_message("<B>[user]</B> broadcasts, <FONT size=[broadcast_size] face='[broadcast_font]' color='[broadcast_color]'>\"[pick(insultmsg)]\"</FONT>")
user.audible_message("<B>[user.GetVoice()]</B>[user.GetAltName()] broadcasts, <FONT size=[broadcast_size] face='[broadcast_font]' color='[broadcast_color]'>\"[pick(insultmsg)]\"</FONT>")
if(broadcast_size >= 11)
var/turf/T = get_turf(user)
playsound(T, 'sound/items/AirHorn.ogg', 100, 1)
@@ -162,9 +160,4 @@
qdel(src)
return
else
user.audible_message("<B>[user]</B> broadcasts, <FONT size=[broadcast_size] face='[broadcast_font]' color='[broadcast_color]'>\"[message]\"</FONT>")
spamcheck = 1
spawn(20)
spamcheck = 0
return
user.audible_message("<B>[user.GetVoice()]</B>[user.GetAltName()] broadcasts, <FONT size=[broadcast_size] face='[broadcast_font]' color='[broadcast_color]'>\"[message]\"</FONT>")

View File

@@ -43,15 +43,17 @@
to_chat(user, "The following channels are available:")
to_chat(user, radio_desc)
/obj/item/device/radio/headset/handle_message_mode(mob/living/M as mob, message, channel)
/obj/item/device/radio/headset/handle_message_mode(mob/living/M as mob, list/message_pieces, channel)
if(channel == "special")
if(translate_binary)
var/datum/language/binary = GLOB.all_languages["Robot Talk"]
binary.broadcast(M, message)
binary.broadcast(M, M.strip_prefixes(multilingual_to_message(message_pieces)))
return RADIO_CONNECTION_NON_SUBSPACE
if(translate_hive)
var/datum/language/hivemind = GLOB.all_languages["Hivemind"]
hivemind.broadcast(M, message)
return null
hivemind.broadcast(M, M.strip_prefixes(multilingual_to_message(message_pieces)))
return RADIO_CONNECTION_NON_SUBSPACE
return RADIO_CONNECTION_FAIL
return ..()

View File

@@ -254,11 +254,11 @@ var/global/list/default_medbay_channels = list(
A.SetName(from)
Broadcast_Message(connection, A,
0, "*garbled automated announcement*", src,
message, from, "Automated Announcement", from, "synthesized voice",
message_to_multilingual(message), from, "Automated Announcement", from, "synthesized voice",
4, 0, list(0), connection.frequency, "states")
// Interprets the message mode when talking into a radio, possibly returning a connection datum
/obj/item/device/radio/proc/handle_message_mode(mob/living/M as mob, message, message_mode)
/obj/item/device/radio/proc/handle_message_mode(mob/living/M as mob, list/message_pieces, message_mode)
// If a channel isn't specified, send to common.
if(!message_mode || message_mode == "headset")
return radio_connection
@@ -272,14 +272,14 @@ var/global/list/default_medbay_channels = list(
return secure_radio_connections[message_mode]
// If we were to send to a channel we don't have, drop it.
return null
return RADIO_CONNECTION_FAIL
/obj/item/device/radio/talk_into(mob/living/M as mob, message, channel, var/verb = "says", var/datum/language/speaking = null)
if(!on) return FALSE // the device has to be on
/obj/item/device/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, var/verb = "says")
if(!on)
return FALSE // the device has to be on
// Fix for permacell radios, but kinda eh about actually fixing them.
if(!M || !message) return FALSE
if(speaking && (speaking.flags & (SIGNLANG|NONVERBAL))) return FALSE
if(!M || !message_pieces)
return FALSE
if(istype(M)) M.trigger_aiming(TARGET_CAN_RADIO)
@@ -303,10 +303,17 @@ var/global/list/default_medbay_channels = list(
*/
//#### Grab the connection datum ####//
var/datum/radio_frequency/connection = handle_message_mode(M, message, channel)
if (!istype(connection))
var/message_mode = handle_message_mode(M, message_pieces, channel)
switch(message_mode)
if(RADIO_CONNECTION_FAIL)
return FALSE
if(RADIO_CONNECTION_NON_SUBSPACE)
return TRUE
if(!istype(message_mode, /datum/radio_frequency)) //if not a special case, it should be returning a radio connection
return FALSE
var/datum/radio_frequency/connection = message_mode
var/turf/position = get_turf(src)
//#### Tagging the signal with all appropriate identity values ####//
@@ -390,7 +397,7 @@ var/global/list/default_medbay_channels = list(
// Other tags:
"compression" = rand(45,50), // compressed radio signal
"message" = message, // the actual sent message
"message" = message_pieces, // the actual sent message
"connection" = connection, // the radio connection to use
"radio" = src, // stores the radio used for transmission
"slow" = 0, // how much to sleep() before broadcasting - simulates net lag
@@ -399,7 +406,6 @@ var/global/list/default_medbay_channels = list(
"server" = null, // the last server to log this signal
"reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery
"level" = position.z, // The source's z level
"language" = speaking,
"verb" = verb
)
signal.frequency = connection.frequency // Quick frequency set
@@ -421,8 +427,8 @@ var/global/list/default_medbay_channels = list(
to_chat(loc, "<span class='warning'>\The [src] pings as it falls back to local radio transmission.</span>")
subspace_transmission = FALSE
return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote),
src, message, displayname, jobname, real_name, M.voice_name,
signal.transmission_method, signal.data["compression"], GetConnectedZlevels(position.z), connection.frequency,verb,speaking)
src, message_pieces, displayname, jobname, real_name, M.voice_name,
signal.transmission_method, signal.data["compression"], GetConnectedZlevels(position.z), connection.frequency,verb)
/* ###### Intercoms and station-bounced radios ###### */
@@ -452,7 +458,7 @@ var/global/list/default_medbay_channels = list(
"vmask" = voicemask, // 1 if the mob is using a voice gas mas
"compression" = 0, // uncompressed radio signal
"message" = message, // the actual sent message
"message" = message_pieces, // the actual sent message
"connection" = connection, // the radio connection to use
"radio" = src, // stores the radio used for transmission
"slow" = 0,
@@ -461,7 +467,6 @@ var/global/list/default_medbay_channels = list(
"server" = null,
"reject" = 0,
"level" = position.z,
"language" = speaking,
"verb" = verb
)
signal.frequency = connection.frequency // Quick frequency set
@@ -482,14 +487,14 @@ var/global/list/default_medbay_channels = list(
//THIS IS TEMPORARY. YEAH RIGHT
if(!connection) return FALSE //~Carn
return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote),
src, message, displayname, jobname, real_name, M.voice_name,
filter_type, signal.data["compression"], GetConnectedZlevels(position.z), connection.frequency,verb,speaking)
src, message_pieces, displayname, jobname, real_name, M.voice_name,
filter_type, signal.data["compression"], GetConnectedZlevels(position.z), connection.frequency, verb)
/obj/item/device/radio/hear_talk(mob/M as mob, msg, var/verb = "says", var/datum/language/speaking = null)
/obj/item/device/radio/hear_talk(mob/M, list/message_pieces, verb)
if(broadcasting)
if(get_dist(src, M) <= canhear_range)
talk_into(M, msg,null,verb,speaking)
talk_into(M, message_pieces, null, verb)
/*

View File

@@ -126,10 +126,7 @@
linkedmonitor.unpair(src)
linkedmonitor = null
..()
/*
/obj/item/device/camerabug/hear_talk(mob/M, var/msg, verb, datum/language/speaking)
radio.hear_talk(M, msg, speaking)
*/
/obj/item/device/bug_monitor
name = "mobile camera pod monitor"
desc = "A portable camera console designed to work with mobile camera pods."
@@ -208,10 +205,7 @@
return
return 1
/*
/obj/item/device/bug_monitor/hear_talk(mob/M, var/msg, verb, datum/language/speaking)
return radio.hear_talk(M, msg, speaking)
*/
/obj/item/device/bug_monitor/spy
name = "\improper PDA"
desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge."

View File

@@ -86,14 +86,9 @@
update_icon()
/obj/item/device/taperecorder/hear_talk(mob/living/M as mob, msg, var/verb="says", datum/language/speaking=null)
/obj/item/device/taperecorder/hear_talk(mob/M, list/message_pieces, verb)
var/msg = multilingual_to_message(message_pieces, requires_machine_understands = TRUE, with_capitalization = TRUE)
if(mytape && recording)
if(speaking)
if(!speaking.machine_understands)
msg = speaking.scramble(msg)
mytape.record_speech("[M.name] [speaking.format_message_plain(msg, verb)]")
else
mytape.record_speech("[M.name] [verb], \"[msg]\"")

View File

@@ -33,8 +33,8 @@
icon_state = "[initial(icon_state)]"
to_chat(user, "<span class='notice'>You disable \the [src].</span>")
/obj/item/device/universal_translator/hear_talk(var/mob/speaker, var/message, var/vrb, var/datum/language/language)
if(!listening || !istype(speaker))
/obj/item/device/universal_translator/hear_talk(mob/M, list/message_pieces, verb)
if(!listening || !istype(M))
return
//Show the "I heard something" animation.
@@ -46,34 +46,38 @@
return
var/mob/living/L = loc
if(!language)
return //Borgs were causing runtimes when passing language=null
if (language && (language.flags & NONVERBAL))
return //Not gonna translate sign language
if (!language.machine_understands)
return //Any other languages that it can't translate.
if(visual && ((L.sdisabilities & BLIND) || L.eye_blind))
return //Can't see the screen, don't get the message
return
if(audio && ((L.sdisabilities & DEAF) || L.ear_deaf))
return //Can't hear the translation, don't get the message
return
//Only translate if they can't understand, otherwise pointlessly spammy
//I'll just assume they don't look at the screen in that case
// Using two for loops kinda sucks, but I think it's more efficient
// to shortcut past string building if we're just going to discard the string
// anyways.
if(user_understands(M, L, message_pieces))
return
//They don't understand the spoken language we're translating FROM
if(!L.say_understands(speaker,language))
//They understand the output language
if(L.say_understands(null,langset))
to_chat(L, "<i><b>[src]</b> translates, </i>\"<span class='[langset.colour]'>[message]</span>\"")
var/new_message = ""
//They don't understand the output language
else
to_chat(L, "<i><b>[src]</b> translates, </i>\"<span class='[langset.colour]'>[langset.scramble(message)]</span>\"")
for(var/datum/multilingual_say_piece/S in message_pieces)
if(S.speaking.flags & NONVERBAL)
continue
if(!S.speaking.machine_understands)
new_message += stars(S.message) + " "
continue
new_message += (S.message + " ")
if(!L.say_understands(null, langset))
new_message = langset.scramble(new_message)
to_chat(L, "<span class='filter_say'><i><b>[src]</b> translates, </i>\"<span class='[langset.colour]'>[new_message]</span>\"</span>")
/obj/item/device/universal_translator/proc/user_understands(mob/M, mob/living/L, list/message_pieces)
for(var/datum/multilingual_say_piece/S in message_pieces)
if(S.speaking && !L.say_understands(M, S.speaking))
return FALSE
return TRUE
//Let's try an ear-worn version
/obj/item/device/universal_translator/ear

View File

@@ -38,9 +38,9 @@
radio.icon_state = src.icon_state
update_icon()
/obj/item/device/tvcamera/hear_talk(mob/living/M, msg, var/verb="says", datum/language/speaking=null)
radio.hear_talk(M,msg,verb,speaking)
..()
/obj/item/device/tvcamera/hear_talk(mob/M, list/message_pieces, verb)
radio.hear_talk(M, message_pieces, verb)
. = ..()
/obj/item/device/tvcamera/attack_self(mob/user)
add_fingerprint(user)

View File

@@ -227,7 +227,8 @@ Implant Specifics:<BR>"}
<b>Integrity:</b> Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."}
return dat
/obj/item/weapon/implant/explosive/hear_talk(mob/M as mob, msg)
/obj/item/weapon/implant/explosive/hear_talk(mob/M, list/message_pieces, verb)
var/msg = multilingual_to_message(message_pieces)
hear(msg)
return

View File

@@ -162,9 +162,9 @@
/obj/proc/hides_under_flooring()
return 0
/obj/proc/hear_talk(mob/M as mob, text, verb, datum/language/speaking)
/obj/proc/hear_talk(mob/M, list/message_pieces, verb)
if(talking_atom)
talking_atom.catchMessage(text, M)
talking_atom.catchMessage(multilingual_to_message(message_pieces), M)
/*
var/mob/mo = locate(/mob) in src
if(mo)

View File

@@ -155,11 +155,11 @@
master.receive_signal()
return 1
/obj/item/device/assembly_holder/hear_talk(mob/living/M as mob, msg, verb, datum/language/speaking)
/obj/item/device/assembly_holder/hear_talk(mob/M, list/message_pieces, verb)
if(a_right)
a_right.hear_talk(M,msg,verb,speaking)
a_right.hear_talk(M, message_pieces, verb)
if(a_left)
a_left.hear_talk(M,msg,verb,speaking)
a_left.hear_talk(M, message_pieces, verb)
/obj/item/device/assembly_holder/timer_igniter
name = "timer-igniter assembly"

View File

@@ -7,7 +7,8 @@
var/listening = 0
var/recorded //the activation message
/obj/item/device/assembly/voice/hear_talk(mob/living/M as mob, msg)
/obj/item/device/assembly/voice/hear_talk(mob/M, list/message_pieces, verb)
var/msg = multilingual_to_message(message_pieces)
if(listening)
recorded = msg
listening = 0

View File

@@ -527,13 +527,15 @@
listening_objects -= src
return ..()
/obj/item/integrated_circuit/input/microphone/hear_talk(mob/living/M, msg, var/verb="says", datum/language/speaking=null)
/obj/item/integrated_circuit/input/microphone/hear_talk(mob/M, list/message_pieces, verb)
var/msg = multilingual_to_message(message_pieces, requires_machine_understands = TRUE)
var/translated = FALSE
if(M && msg)
if(speaking)
if(!speaking.machine_understands)
msg = speaking.scramble(msg)
if(!istype(speaking, /datum/language/common))
for(var/datum/multilingual_say_piece/S in message_pieces)
// S.speaking && here is not redundant, it's preventing `S.speaking = null` from flagging
// as a translation, when it is not.
if(S.speaking && !istype(S.speaking, /datum/language/common))
translated = TRUE
set_pin_data(IC_OUTPUT, 1, M.GetVoice())
set_pin_data(IC_OUTPUT, 2, msg)
@@ -577,25 +579,41 @@
var/datum/language/newlang = GLOB.all_languages[lang]
my_langs |= newlang
/obj/item/integrated_circuit/input/microphone/sign/hear_talk(mob/living/M, msg, var/verb="says", datum/language/speaking=null)
/obj/item/integrated_circuit/input/microphone/sign/hear_talk(mob/M, list/message_pieces, verb)
var/msg = multilingual_to_message(message_pieces)
var/translated = FALSE
if(M && msg)
if(speaking)
if(!((speaking.flags & NONVERBAL) || (speaking.flags & SIGNLANG)))
for(var/datum/multilingual_say_piece/S in message_pieces)
if(S.speaking)
if(!((S.speaking.flags & NONVERBAL) || (S.speaking.flags & SIGNLANG)))
translated = TRUE
msg = speaking.scramble(msg, my_langs)
msg = stars(msg)
break
set_pin_data(IC_OUTPUT, 1, M.GetVoice())
set_pin_data(IC_OUTPUT, 2, msg)
push_data()
if(!translated)
activate_pin(1)
if(translated)
else
activate_pin(2)
/obj/item/integrated_circuit/input/microphone/sign/hear_signlang(text, verb, datum/language/speaking, mob/M as mob)
hear_talk(M, text, verb, speaking)
return
var/translated = FALSE
if(M && text)
if(speaking)
if(!((speaking.flags & NONVERBAL) || (speaking.flags & SIGNLANG)))
translated = TRUE
text = speaking.scramble(text, my_langs)
set_pin_data(IC_OUTPUT, 1, M.GetVoice())
set_pin_data(IC_OUTPUT, 2, text)
push_data()
if(!translated)
activate_pin(1)
else
activate_pin(2)
/obj/item/integrated_circuit/input/sensor
name = "sensor"

View File

@@ -1,4 +1,4 @@
/mob/observer/dead/say(var/message)
/mob/observer/dead/say(var/message, var/datum/language/speaking = null, var/whispering = 0)
message = sanitize(message)
if(!message)
@@ -6,58 +6,37 @@
log_ghostsay(message, src)
if (src.client)
if (client)
if(message)
client.handle_spam_prevention(MUTE_DEADCHAT)
if(src.client.prefs.muted & MUTE_DEADCHAT)
if(client.prefs.muted & MUTE_DEADCHAT)
to_chat(src, "<font color='red'>You cannot talk in deadchat (muted).</font>")
return
. = src.say_dead(message)
. = say_dead(message)
/mob/observer/dead/emote(var/act, var/type, var/message)
//message = sanitize(message) - already sanitized in verb/me_verb()
/mob/observer/dead/me_verb(message as text)
if(!message)
return
if(act != "me")
return
log_ghostemote(message, src)
if(src.client)
if(client)
if(message)
client.handle_spam_prevention(MUTE_DEADCHAT)
if(src.client.prefs.muted & MUTE_DEADCHAT)
if(client.prefs.muted & MUTE_DEADCHAT)
to_chat(src, "<font color='red'>You cannot emote in deadchat (muted).</font>")
return
. = src.emote_dead(message)
. = emote_dead(message)
/*
for (var/mob/M in hearers(null, null))
if (!M.stat)
if(M.job == "Chaplain")
if (prob (49))
M.show_message("<span class='game'><i>You hear muffled speech... but nothing is there...</i></span>", 2)
if(prob(20))
playsound(src.loc, pick('sound/effects/ghost.ogg','sound/effects/ghost2.ogg'), 10, 1)
else
M.show_message("<span class='game'><i>You hear muffled speech... you can almost make out some words...</i></span>", 2)
// M.show_message("<span class='game'><i>[stutter(message)]</i></span>", 2)
if(prob(30))
playsound(src.loc, pick('sound/effects/ghost.ogg','sound/effects/ghost2.ogg'), 10, 1)
else
if (prob(50))
return
else if (prob (95))
M.show_message("<span class='game'><i>You hear muffled speech... but nothing is there...</i></span>", 2)
if(prob(20))
playsound(src.loc, pick('sound/effects/ghost.ogg','sound/effects/ghost2.ogg'), 10, 1)
else
M.show_message("<span class='game'><i>You hear muffled speech... you can almost make out some words...</i></span>", 2)
// M.show_message("<span class='game'><i>[stutter(message)]</i></span>", 2)
playsound(src.loc, pick('sound/effects/ghost.ogg','sound/effects/ghost2.ogg'), 10, 1)
*/
/mob/observer/dead/handle_track(message, verb = "says", mob/speaker = null, speaker_name, hard_to_hear)
return "[speaker_name] ([ghost_follow_link(speaker, src)])"
/mob/observer/dead/handle_speaker_name(mob/speaker = null, vname, hard_to_hear)
var/speaker_name = ..()
//Announce computer and various stuff that broadcasts doesn't use it's real name but AI's can't pretend to be other mobs.
if(speaker && (speaker_name != speaker.real_name) && !isAI(speaker))
speaker_name = "[speaker.real_name] ([speaker_name])"
return speaker_name

View File

@@ -1,54 +1,96 @@
// At minimum every mob has a hear_say proc.
/mob/proc/combine_message(var/list/message_pieces, var/verb, var/mob/speaker, always_stars = FALSE, var/radio = FALSE)
var/iteration_count = 0
var/msg = "" // This is to make sure that the pieces have actually added something
. = "[verb], \""
for(var/datum/multilingual_say_piece/SP in message_pieces)
iteration_count++
var/piece = SP.message
if(piece == "")
continue
/mob/proc/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
if(SP.speaking && SP.speaking.flags & INNATE) // Snowflake for noise lang
if(radio)
return SP.speaking.format_message_radio(piece)
else
return SP.speaking.format_message(piece)
if(iteration_count == 1)
piece = capitalize(piece)
if(always_stars)
piece = stars(piece)
else if(!say_understands(speaker, SP.speaking))
piece = saypiece_scramble(SP)
if(isliving(speaker))
var/mob/living/S = speaker
if(istype(S.say_list) && length(S.say_list.speak))
piece = pick(S.say_list.speak)
if(!SP.speaking) // Catch the most generic case first
piece = "<span class='message body'>[piece]</span>"
else if(radio) // SP.speaking == TRUE enforced by previous !SP.speaking
piece = SP.speaking.format_message_radio(piece)
else // SP.speaking == TRUE && radio == FALSE
piece = SP.speaking.format_message(piece)
msg += (piece + " ")
if(msg == "")
// There is literally no content left in this message, we need to shut this shit down
. = "" // hear_say will suppress it
else
. = trim(. + trim(msg))
. += "\""
/mob/proc/saypiece_scramble(datum/multilingual_say_piece/SP)
if(SP.speaking)
return SP.speaking.scramble(SP.message)
else
return stars(SP.message)
/mob/proc/hear_say(var/list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
if(!client && !teleop)
return
return FALSE
if(speaker && !speaker.client && istype(src,/mob/observer/dead) && is_preference_enabled(/datum/client_preference/ghost_ears) && !(speaker in view(src)))
if(isobserver(src) && is_preference_enabled(/datum/client_preference/ghost_ears))
if(speaker && !speaker.client && !(speaker in view(src)))
//Does the speaker have a client? It's either random stuff that observers won't care about (Experiment 97B says, 'EHEHEHEHEHEHEHE')
//Or someone snoring. So we make it where they won't hear it.
return
return FALSE
//make sure the air can transmit speech - hearer's side
var/turf/T = get_turf(src)
if ((T) && (!(istype(src, /mob/observer/dead)))) //Ghosts can hear even in vacuum.
if(T && !isobserver(src)) //Ghosts can hear even in vacuum.
var/datum/gas_mixture/environment = T.return_air()
var/pressure = (environment)? environment.return_pressure() : 0
var/pressure = environment ? environment.return_pressure() : 0
if(pressure < SOUND_MINIMUM_PRESSURE && get_dist(speaker, src) > 1)
return
return FALSE
if(pressure < ONE_ATMOSPHERE * 0.4) //sound distortion pressure, to help clue people in that the air is thin, even if it isn't a vacuum yet
italics = 1
sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact
if(sleeping || stat == 1)
hear_sleep(message)
return
//non-verbal languages are garbled if you can't see the speaker. Yes, this includes if they are inside a closet.
if (language && (language.flags & NONVERBAL))
if (!speaker || (src.sdisabilities & BLIND || src.blinded) || !(speaker in view(src)))
message = stars(message)
if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages
if(!say_understands(speaker,language))
if(language)
message = language.scramble(message, languages)
else
message = stars(message)
var/speaker_name = speaker.name
if(istype(speaker, /mob/living/carbon/human))
if(ishuman(speaker))
var/mob/living/carbon/human/H = speaker
speaker_name = H.GetVoice()
var/message = combine_message(message_pieces, verb, speaker)
if(message == "")
return
if(sleeping || stat == UNCONSCIOUS)
hear_sleep(message)
return FALSE
if(italics)
message = "<i>[message]</i>"
message = encode_html_emphasis(message)
var/track = null
if(istype(src, /mob/observer/dead))
if(isobserver(src))
if(italics && is_preference_enabled(/datum/client_preference/ghost_radio))
return
if(speaker_name != speaker.real_name && speaker.real_name)
@@ -58,31 +100,27 @@
message = "<b>[message]</b>"
if(is_deaf())
if(!language || !(language.flags & INNATE)) // INNATE is the flag for audible-emote-language, so we don't want to show an "x talks but you cannot hear them" message if it's set
if(speaker == src)
to_chat(src, "<span class='warning'>You cannot hear yourself speak!</span>")
to_chat(src, "<span class='filter_say'><span class='warning'>You cannot hear yourself speak!</span></span>")
else
to_chat(src, "<span class='name'>[speaker_name]</span>[alt_name] talks but you cannot hear.")
to_chat(src, "<span class='filter_say'><span class='name'>[speaker_name]</span>[speaker.GetAltName()] makes a noise, possibly speech, but you cannot hear them.</span>")
else
var/message_to_send = null
if(language)
message_to_send = "<span class='game say'><span class='name'>[speaker_name]</span>[alt_name] [track][language.format_message(message, verb)]</span>"
else
message_to_send = "<span class='game say'><span class='name'>[speaker_name]</span>[alt_name] [track][verb], <span class='message'><span class='body'>\"[message]\"</span></span></span>"
if(check_mentioned(message) && is_preference_enabled(/datum/client_preference/check_mention))
message_to_send = "<span class='game say'><span class='name'>[speaker_name]</span>[speaker.GetAltName()] [track][message]</span>"
if(check_mentioned(multilingual_to_message(message_pieces)) && is_preference_enabled(/datum/client_preference/check_mention))
message_to_send = "<font size='3'><b>[message_to_send]</b></font>"
on_hear_say(message_to_send)
if (speech_sound && (get_dist(speaker, src) <= world.view && src.z == speaker.z))
if(speech_sound && (get_dist(speaker, src) <= world.view && z == speaker.z))
var/turf/source = speaker ? get_turf(speaker) : get_turf(src)
src.playsound_local(source, speech_sound, sound_vol, 1)
playsound_local(source, speech_sound, sound_vol, 1)
// Done here instead of on_hear_say() since that is NOT called if the mob is clientless (which includes most AI mobs).
/mob/living/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
/mob/living/hear_say(var/list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
..()
if(has_AI()) // Won't happen if no ai_holder exists or there's a player inside w/o autopilot active.
ai_holder.on_hear_say(speaker, message)
ai_holder.on_hear_say(speaker, multilingual_to_message(message_pieces))
/mob/proc/on_hear_say(var/message)
to_chat(src, message)
@@ -124,114 +162,25 @@
return tagged_message
/mob/proc/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/part_c, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="")
/mob/proc/hear_radio(var/list/message_pieces, var/verb = "says", var/part_a, var/part_b, var/part_c, var/mob/speaker = null, var/hard_to_hear = 0, var/vname = "")
if(!client)
return
if(sleeping || stat==1) //If unconscious or sleeping
var/message = combine_message(message_pieces, verb, speaker, always_stars = hard_to_hear, radio = TRUE)
if(sleeping || stat == UNCONSCIOUS) //If unconscious or sleeping
hear_sleep(message)
return
var/track = null
//non-verbal languages are garbled if you can't see the speaker. Yes, this includes if they are inside a closet.
if (language && (language.flags & NONVERBAL))
if (!speaker || (src.sdisabilities & BLIND || src.blinded) || !(speaker in view(src)))
message = stars(message)
if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages
if(!say_understands(speaker,language))
if(language)
message = language.scramble(message, languages)
else
message = stars(message)
if(hard_to_hear)
message = stars(message)
var/speaker_name = speaker.name
if(vname)
speaker_name = vname
if(istype(speaker, /mob/living/carbon/human))
var/mob/living/carbon/human/H = speaker
if(H.voice)
speaker_name = H.voice
if(hard_to_hear)
speaker_name = "unknown"
var/changed_voice
if(istype(src, /mob/living/silicon/ai) && !hard_to_hear)
var/jobname // the mob's "job"
var/mob/living/carbon/human/impersonating //The crew member being impersonated, if any.
if (ishuman(speaker))
var/mob/living/carbon/human/H = speaker
if(H.wear_mask && istype(H.wear_mask,/obj/item/clothing/mask/gas/voice))
changed_voice = 1
var/list/impersonated = new()
var/mob/living/carbon/human/I = impersonated[speaker_name]
if(!I)
for(var/mob/living/carbon/human/M in mob_list)
if(M.real_name == speaker_name)
I = M
impersonated[speaker_name] = I
break
// If I's display name is currently different from the voice name and using an agent ID then don't impersonate
// as this would allow the AI to track I and realize the mismatch.
if(I && !(I.name != speaker_name && I.wear_id && istype(I.wear_id,/obj/item/weapon/card/id/syndicate)))
impersonating = I
jobname = impersonating.get_assignment()
else
jobname = "Unknown"
else
jobname = H.get_assignment()
else if (iscarbon(speaker)) // Nonhuman carbon mob
jobname = "No id"
else if (isAI(speaker))
jobname = "AI"
else if (isrobot(speaker))
jobname = "Cyborg"
else if (istype(speaker, /mob/living/silicon/pai))
jobname = "Personal AI"
else
jobname = "Unknown"
if(changed_voice)
if(impersonating)
track = "<a href='byond://?src=\ref[src];trackname=[html_encode(speaker_name)];track=\ref[impersonating]'>[speaker_name] ([jobname])</a>"
else
track = "[speaker_name] ([jobname])"
else
track = "<a href='byond://?src=\ref[src];trackname=[html_encode(speaker_name)];track=\ref[speaker]'>[speaker_name] ([jobname])</a>"
if(istype(src, /mob/observer/dead))
if(speaker_name != speaker.real_name && !isAI(speaker)) //Announce computer and various stuff that broadcasts doesn't use it's real name but AI's can't pretend to be other mobs.
speaker_name = "[speaker.real_name] ([speaker_name])"
track = "[speaker_name] ([ghost_follow_link(speaker, src)])"
var/speaker_name = handle_speaker_name(speaker, vname, hard_to_hear)
var/track = handle_track(message, verb, speaker, speaker_name, hard_to_hear)
message = encode_html_emphasis(message)
var/formatted
if(language)
formatted = "[language.format_message_radio(message, verb)][part_c]"
else
formatted = "[verb], <span class=\"body\">\"[message]\"</span>[part_c]"
if((sdisabilities & DEAF) || ear_deaf)
if(prob(20))
to_chat(src, "<span class='warning'>You feel your headset vibrate but can hear nothing from it!</span>")
else
on_hear_radio(part_a, speaker_name, track, part_b, formatted)
on_hear_radio(part_a, speaker_name, track, part_b, message)
/proc/say_timestamp()
return "<span class='say_quote'>\[[stationtime2text()]\]</span>"
@@ -296,9 +245,40 @@
heardword = copytext(heardword,2)
if(copytext(heardword,-1) in punctuation)
heardword = copytext(heardword,1,length(heardword))
heard = "<span class = 'game_say'>...You hear something about...[heardword]</span>"
heard = "<span class='game say'>...You hear something about...[heardword]</span>"
else
heard = "<span class = 'game_say'>...<i>You almost hear someone talking</i>...</span>"
heard = "<span class='game say'>...<i>You almost hear someone talking</i>...</span>"
to_chat(src, heard)
/mob/proc/handle_speaker_name(mob/speaker, vname, hard_to_hear)
var/speaker_name = "unknown"
if(speaker)
speaker_name = speaker.name
if(ishuman(speaker))
var/mob/living/carbon/human/H = speaker
if(H.voice)
speaker_name = H.voice
if(vname)
speaker_name = vname
if(hard_to_hear)
speaker_name = "unknown"
return speaker_name
/mob/proc/handle_track(message, verb = "says", mob/speaker = null, speaker_name, hard_to_hear)
return
/mob/proc/hear_holopad_talk(list/message_pieces, var/verb = "says", var/mob/speaker = null)
var/message = combine_message(message_pieces, verb, speaker)
var/name = speaker.name
if(!say_understands(speaker))
name = speaker.voice_name
var/rendered = "<span class='game say'><span class='name'>[name]</span> [message]</span>"
to_chat(src, rendered)

View File

@@ -5,12 +5,6 @@
key = ""
flags = RESTRICTED|NONGLOBAL|INNATE|NO_TALK_MSG|NO_STUTTER
/datum/language/noise/format_message(message, verb)
return "<span class='message'><span class='[colour]'>[message]</span></span>"
/datum/language/noise/format_message_plain(message, verb)
return message
/datum/language/noise/format_message_radio(message, verb)
return "<span class='[colour]'>[message]</span>"
@@ -143,6 +137,17 @@
var/obj/item/organ/external/hand/hands = locate() in speaker //you can't sign without hands
return (hands || !iscarbon(speaker))
/datum/language/sign/scramble(var/input, var/list/known_languages)
return stars(input)
// This is a little weird because broadcast is traditionally for hivemind languages
// But in practice, it's just a way for a language to override all other languages and bypass hear_say
// which is exactly what sign language does.
/datum/language/sign/broadcast(var/mob/living/speaker, var/message, var/speaker_mask)
log_say("(SIGN) [message]", speaker)
speaker.say_signlang(message, pick(signlang_verb), src)
// Silly language for those times when you try to talk a languague you normally can't
/datum/language/gibberish
name = LANGUAGE_GIBBERISH

View File

@@ -113,13 +113,13 @@
return scrambled_text
/datum/language/proc/format_message(message, verb)
return "[verb], <span class='message'><span class='[colour]'>\"[capitalize(message)]\"</span></span>"
return "<span class='message'><span class='[colour]'>[message]</span></span>"
/datum/language/proc/format_message_plain(message, verb)
return "[verb], \"[capitalize(message)]\""
return "[capitalize(message)]"
/datum/language/proc/format_message_radio(message, verb)
return "[verb], <span class='[colour]'>\"[capitalize(message)]\"</span>"
return "<span class='[colour]'>[capitalize(message)]</span>"
/datum/language/proc/get_talkinto_msg_range(message)
// if you yell, you'll be heard from two tiles over instead of one
@@ -129,7 +129,7 @@
log_say("(HIVE) [message]", speaker)
if(!speaker_mask) speaker_mask = speaker.name
message = format_message(message, get_spoken_verb(message))
message = "[get_spoken_verb(message)], \"[format_message(message, get_spoken_verb(message))]\""
for(var/mob/player in player_list)
player.hear_broadcast(src, speaker, speaker_mask, message)
@@ -233,36 +233,36 @@
return prefix in config.language_prefixes
//TBD
/mob/proc/check_lang_data()
. = ""
for(var/datum/language/L in languages)
if(!(L.flags & NONGLOBAL))
. += "<b>[L.name] ([get_language_prefix()][L.key])</b><br/>[L.desc]<br/><br/>"
/mob/living/check_lang_data()
. = ""
if(default_language)
. += "Current default language: [default_language] - <a href='byond://?src=\ref[src];default_lang=reset'>reset</a><br/><br/>"
for(var/datum/language/L in languages)
if(!(L.flags & NONGLOBAL))
if(L == default_language)
. += "<b>[L.name] ([get_language_prefix()][L.key])</b> - default - <a href='byond://?src=\ref[src];default_lang=reset'>reset</a><br/>[L.desc]<br/><br/>"
else if (can_speak(L))
. += "<b>[L.name] ([get_language_prefix()][L.key])</b> - <a href='byond://?src=\ref[src];default_lang=\ref[L]'>set default</a><br/>[L.desc]<br/><br/>"
else
. += "<b>[L.name] ([get_language_prefix()][L.key])</b> - cannot speak!<br/>[L.desc]<br/><br/>"
/mob/verb/check_languages()
set name = "Check Known Languages"
set category = "IC"
set src = usr
var/dat = "<b><font size = 5>Known Languages</font></b><br/><br/>"
for(var/datum/language/L in languages)
if(!(L.flags & NONGLOBAL))
dat += "<b>[L.name] ([get_language_prefix()][L.key])</b><br/>[L.desc]<br/><br/>"
src << browse(dat, "window=checklanguage")
return
/mob/living/check_languages()
var/dat = "<b><font size = 5>Known Languages</font></b><br/><br/>"
if(default_language)
dat += "Current default language: [default_language] - <a href='byond://?src=\ref[src];default_lang=reset'>reset</a><br/><br/>"
for(var/datum/language/L in languages)
if(!(L.flags & NONGLOBAL))
if(L == default_language)
dat += "<b>[L.name] ([get_language_prefix()][L.key])</b> - default - <a href='byond://?src=\ref[src];default_lang=reset'>reset</a><br/>[L.desc]<br/><br/>"
else if (can_speak(L))
dat += "<b>[L.name] ([get_language_prefix()][L.key])</b> - <a href='byond://?src=\ref[src];default_lang=\ref[L]'>set default</a><br/>[L.desc]<br/><br/>"
else
dat += "<b>[L.name] ([get_language_prefix()][L.key])</b> - cannot speak!<br/>[L.desc]<br/><br/>"
src << browse(dat, "window=checklanguage")
var/datum/browser/popup = new(src, "checklanguage", "Known Languages", 420, 470)
popup.set_content(check_lang_data())
popup.open()
/mob/living/Topic(href, href_list)
if(href_list["default_lang"])

View File

@@ -94,6 +94,10 @@
key = "l"
flags = WHITELISTED | SIGNLANG | NO_STUTTER | NONVERBAL
/datum/language/tajsign/broadcast(var/mob/living/speaker, var/message, var/speaker_mask)
log_say("(SIGN) [message]", speaker)
speaker.say_signlang(message, pick(signlang_verb), src)
/datum/language/tajsign/can_speak_special(var/mob/speaker) // TODO: If ever we make external organs assist languages, convert this over to the new format
var/list/allowed_species = list(SPECIES_TAJ, SPECIES_TESHARI) // Need a tail and ears and such to use this.
if(iscarbon(speaker))

View File

@@ -6,7 +6,7 @@
#define AUTOHISS_NUM 3
/mob/living/proc/handle_autohiss(message, datum/language/L)
/mob/proc/handle_autohiss(message, datum/language/L)
return message // no autohiss at this level
/mob/living/carbon/human/handle_autohiss(message, datum/language/L)

View File

@@ -144,12 +144,8 @@
/mob/living/bot/attack_ai(var/mob/user)
return attack_hand(user)
/mob/living/bot/say(var/message)
var/verb = "beeps"
message = sanitize(message)
..(message, null, verb)
/mob/living/bot/say_quote(var/message, var/datum/language/speaking = null)
return "beeps"
/mob/living/bot/speech_bubble_appearance()
return "machine"

View File

@@ -1,5 +1,4 @@
/mob/living/carbon/alien
name = "alien"
desc = "What IS that?"
icon = 'icons/mob/alien.dmi'
@@ -53,3 +52,22 @@
/mob/living/carbon/alien/cannot_use_vents()
return
/mob/living/carbon/alien/get_default_language()
if(default_language)
return default_language
return GLOB.all_languages["Xenomorph"]
/mob/living/carbon/alien/say_quote(var/message, var/datum/language/speaking = null)
var/verb = "hisses"
var/ending = copytext(message, length(message))
if(speaking && (speaking.name != "Galactic Common")) //this is so adminbooze xenos speaking common have their custom verbs,
verb = speaking.get_spoken_verb(ending) //and use normal verbs for their own languages and non-common languages
else
if(ending == "!")
verb = "roars"
else if(ending == "?")
verb = "hisses curiously"
return verb

View File

@@ -1,6 +1,5 @@
/mob/living/carbon/alien/diona/say_understands(var/mob/other, var/datum/language/speaking = null)
if (istype(other, /mob/living/carbon/human) && !speaking)
if(ishuman(other) && !speaking)
if(languages.len >= 2) // They have sucked down some blood.
return 1
return TRUE
return ..()

View File

@@ -1,131 +1,107 @@
/mob/living/carbon/alien/emote(var/act, var/m_type=1, var/message = null)
var/param = null
if(findtext(act, "-", 1, null))
var/t1 = findtext(act, "-", 1, null)
param = copytext(act, t1 + 1, length(act) + 1)
act = copytext(act, 1, t1)
if(findtext(act,"s",-1) && !findtext(act,"_",-2))//Removes ending s's unless they are prefixed with a '_'
act = copytext(act,1,length(act))
var/muzzled = is_muzzled()
switch(act)
if ("me")
if(silent)
return
if (src.client)
if (client.prefs.muted & MUTE_IC)
to_chat(src, "<font color='red'>You cannot send IC messages (muted).</font>")
return
if (stat)
return
if(!(message))
return
return custom_emote(m_type, message)
if ("custom")
return custom_emote(m_type, message)
if("sign")
if (!src.restrained())
message = text("<B>The alien</B> signs[].", (text2num(param) ? text(" the number []", text2num(param)) : null))
if(!restrained())
var/num = null
if(text2num(param))
num = "the number [text2num(param)]"
if(num)
message = "<B>[src]</B> signs [num]."
m_type = 1
if("burp")
if(!muzzled)
message = "<B>[src]</B> burps."
m_type = 2
if("deathgasp")
message = "<B>The [src.name]</B> lets out a waning guttural screech, green blood bubbling from its maw."
message = "<B>[src]</B> lets out a waning guttural screech, green blood bubbling from its maw."
m_type = 2
if("scratch")
if (!src.restrained())
message = "<B>The [src.name]</B> scratches."
if(!restrained())
message = "<B>[src]</B> scratches."
m_type = 1
if("whimper")
if(!muzzled)
message = "<B>The [src.name]</B> whimpers."
message = "<B>[src]</B> whimpers."
m_type = 2
if("tail")
message = "<B>The [src.name]</B> waves its tail."
message = "<B>[src]</B> waves its tail."
m_type = 1
if("gasp")
message = "<B>The [src.name]</B> gasps."
message = "<B>[src]</B> gasps."
m_type = 2
if("shiver")
message = "<B>The [src.name]</B> shivers."
message = "<B>[src]</B> shivers."
m_type = 2
if("drool")
message = "<B>The [src.name]</B> drools."
message = "<B>[src]</B> drools."
m_type = 1
if("scretch")
if(!muzzled)
message = "<B>The [src.name]</B> scretches."
message = "<B>[src]</B> scretches."
m_type = 2
if("choke")
message = "<B>The [src.name]</B> chokes."
message = "<B>[src]</B> chokes."
m_type = 2
if("moan")
message = "<B>The [src.name]</B> moans!"
message = "<B>[src]</B> moans!"
m_type = 2
if("nod")
message = "<B>The [src.name]</B> nods its head."
message = "<B>[src]</B> nods its head."
m_type = 1
// if("sit")
// message = "<B>The [src.name]</B> sits down." //Larvan can't sit down, /N
// message = "<B>[src]</B> sits down." //Larvan can't sit down, /N
// m_type = 1
if("sway")
message = "<B>The [src.name]</B> sways around dizzily."
message = "<B>[src]</B> sways around dizzily."
m_type = 1
if("sulk")
message = "<B>The [src.name]</B> sulks down sadly."
message = "<B>[src]</B> sulks down sadly."
m_type = 1
if("twitch")
message = "<B>The [src.name]</B> twitches."
message = "<B>[src]</B> twitches."
m_type = 1
if("twitch_v")
message = "<B>The [src.name]</B> twitches violently."
message = "<B>[src]</B> twitches violently."
m_type = 1
if("dance")
if (!src.restrained())
message = "<B>The [src.name]</B> dances around happily."
if(!restrained())
message = "<B>[src]</B> dances around happily."
m_type = 1
if("roll")
if (!src.restrained())
message = "<B>The [src.name]</B> rolls."
if(!restrained())
message = "<B>[src]</B> rolls."
m_type = 1
if("shake")
message = "<B>The [src.name]</B> shakes its head."
message = "<B>[src]</B> shakes its head."
m_type = 1
if("gnarl")
if(!muzzled)
message = "<B>The [src.name]</B> gnarls and shows its teeth.."
message = "<B>[src]</B> gnarls and shows its teeth.."
m_type = 2
if("jump")
message = "<B>The [src.name]</B> jumps!"
message = "<B>[src]</B> jumps!"
m_type = 1
if("hiss_")
message = "<B>The [src.name]</B> hisses softly."
message = "<B>[src]</B> hisses softly."
m_type = 1
if("collapse")
Paralyse(2)
message = text("<B>[]</B> collapses!", src)
message = "<B>[src]</B> collapses!"
m_type = 2
if("chirp")
message = "<B>The [src.name]</B> chirps!"
playsound(src.loc, 'sound/misc/nymphchirp.ogg', 50, 0)
message = "<B>[src]</B> chirps!"
playsound(loc, 'sound/misc/nymphchirp.ogg', 50, 0)
m_type = 2
if("help")
to_chat(src, "burp, chirp, choke, collapse, dance, drool, gasp, shiver, gnarl, jump, moan, nod, roll, scratch,\nscretch, shake, sign-#, sulk, sway, tail, twitch, whimper")
else
to_chat(src, "Invalid Emote: [act]")
if ((message && src.stat == 0))
log_emote(message, src)
if (m_type & 1)
for(var/mob/O in viewers(src, null))
O.show_message(message, m_type)
//Foreach goto(703)
else
for(var/mob/O in hearers(src, null))
O.show_message(message, m_type)
//Foreach goto(746)
return
to_chat(src, "<span class='filter_say'>burp, chirp, choke, collapse, dance, drool, gasp, shiver, gnarl, jump, moan, nod, roll, scratch,\nscretch, shake, sign-#, sulk, sway, tail, twitch, whimper</span>")
if(!stat)
..(act, m_type, message)

View File

@@ -1,25 +0,0 @@
/mob/living/carbon/alien/say(var/message)
var/verb = "says"
var/message_range = world.view
if(client)
if(client.prefs.muted & MUTE_IC)
to_chat(src, "<font color='red'>You cannot speak in IC (Muted).</font>")
return
message = sanitize(message)
if(stat == 2)
return say_dead(message)
if(copytext(message,1,2) == "*")
return emote(copytext(message,2))
var/datum/language/speaking = parse_language(message)
message = trim(message)
if(!message || stat)
return
..(message, speaking, verb, null, null, message_range, null)

View File

@@ -23,30 +23,13 @@
return ..()
/mob/living/carbon/brain/say_understands(var/other)//Goddamn is this hackish, but this say code is so odd
if (istype(other, /mob/living/silicon/ai))
if(!(container && istype(container, /obj/item/device/mmi)))
return 0
else
return 1
if (istype(other, /mob/living/silicon/decoy))
if(!(container && istype(container, /obj/item/device/mmi)))
return 0
else
return 1
if (istype(other, /mob/living/silicon/pai))
if(!(container && istype(container, /obj/item/device/mmi)))
return 0
else
return 1
if (istype(other, /mob/living/silicon/robot))
if(!(container && istype(container, /obj/item/device/mmi)))
return 0
else
return 1
if (istype(other, /mob/living/carbon/human))
return 1
if (istype(other, /mob/living/simple_mob/slime))
return 1
if(istype(container, /obj/item/device/mmi))
if(issilicon(other))
return TRUE
if(ishuman(other))
return TRUE
if(isslime(other))
return TRUE
return ..()
/mob/living/carbon/brain/update_canmove()

View File

@@ -6,27 +6,9 @@
var/t1 = findtext(act, "-", 1, null)
act = copytext(act, 1, t1)
if(findtext(act,"s",-1) && !findtext(act,"_",-2))//Removes ending s's unless they are prefixed with a '_'
act = copytext(act,1,length(act))
if(src.stat == DEAD)
if(stat == DEAD)
return
switch(act)
if ("me")
if(silent)
return
if (src.client)
if (client.prefs.muted & MUTE_IC)
to_chat(src, "<font color='red'>You cannot send IC messages (muted).</font>")
return
if (stat)
return
if(!(message))
return
return custom_emote(m_type, message)
if ("custom")
return custom_emote(m_type, message)
if("alarm")
to_chat(src, "You sound an alarm.")
message = "<B>[src]</B> sounds an alarm."
@@ -58,23 +40,8 @@
message = "<B>[src]</B> boops."
m_type = 2
if("help")
to_chat(src, "alarm,alert,notice,flash,blink,whistle,beep,boop")
else
to_chat(src, "<font color='blue'>Unusable emote '[act]'. Say *help for a list.</font>")
to_chat(src, "<span class='filter_say'>alarm, alert, notice, flash, blink, whistle, beep, boop</span>")
if (message)
log_emote(message, src)
if(!stat)
..(act, m_type, message)
for(var/mob/M in dead_mob_list)
if (!M.client || istype(M, /mob/new_player))
continue //skip monkeys, leavers, and new_players
if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_sight) && !(M in viewers(src,null)))
M.show_message(message)
if (m_type & 1)
for (var/mob/O in viewers(src, null))
O.show_message(message, m_type)
else if (m_type & 2)
for (var/mob/O in hearers(src.loc, null))
O.show_message(message, m_type)

View File

@@ -1,5 +1,5 @@
//TODO: Convert this over for languages.
/mob/living/carbon/brain/say(var/message)
/mob/living/carbon/brain/say(var/message, var/datum/language/speaking = null, var/whispering = 0)
if(silent)
return
@@ -8,32 +8,15 @@
if(!(container && container.can_speak))
return //Certain objects can speak, like MMIs. Most others cannot. -Q
else
var/datum/language/speaking = parse_language(message)
if(speaking)
message = copytext(message, 2+length(speaking.key))
var/verb = "says"
var/ending = copytext(message, length(message))
if (speaking)
verb = speaking.get_spoken_verb(ending)
else
if(ending=="!")
verb=pick("exclaims","shouts","yells")
if(ending=="?")
verb="asks"
if(prob(emp_damage * 4))
if(prob(10))//10% chance to drop the message entirely
return
else
message = Gibberish(message, (emp_damage*6))//scrambles the message, gets worse when emp_damage is higher
if(speaking && speaking.flags & HIVEMIND)
speaking.broadcast(src,trim(message))
return
..()
..(trim(message), speaking, verb)
/mob/living/carbon/brain/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name)
/mob/living/carbon/brain/handle_message_mode(message_mode, message, verb, speaking, used_radios)
..()
if(message_mode)
var/obj/item/device/mmi/R = container

View File

@@ -8,9 +8,6 @@
param = copytext(act, t1 + 1, length(act) + 1)
act = copytext(act, 1, t1)
if(findtext(act,"s",-1) && !findtext(act,"_",-2))//Removes ending s's unless they are prefixed with a '_'
act = copytext(act,1,length(act))
var/muzzled = is_muzzled()
//var/m_type = 1
@@ -19,7 +16,7 @@
if(I.implanted)
I.trigger(act, src)
if(src.stat == 2.0 && (act != "deathgasp"))
if(stat == DEAD && (act != "deathgasp"))
return
switch(act)
@@ -140,7 +137,7 @@
else
alert("Unable to use this emote, must be either hearable or visible.")
return
return custom_emote(m_type, message)
return custom_emote(m_type, input)
if("me")
@@ -736,13 +733,13 @@
message = "makes a light spitting noise, a poor attempt at a whistle."
if("help")
to_chat(src, "blink, blink_r, blush, bow-(none)/mob, burp, choke, chuckle, clap, collapse, cough, cry, custom, deathgasp, drool, eyebrow, fastsway/qwag, \
to_chat(src, "<span class='filter_say'>blink, blink_r, blush, bow-(none)/mob, burp, choke, chuckle, clap, collapse, cough, cry, custom, deathgasp, drool, eyebrow, fastsway/qwag, \
frown, gasp, giggle, glare-(none)/mob, grin, groan, grumble, handshake, hug-(none)/mob, laugh, look-(none)/mob, moan, mumble, nod, pale, point-atom, \
raise, salute, scream, sneeze, shake, shiver, shrug, sigh, signal-#1-10, slap-(none)/mob, smile, sneeze, sniff, snore, stare-(none)/mob, stopsway/swag, sway/wag, swish, tremble, twitch, \
twitch_v, vomit, whimper, wink, yawn. Prometheans: squish Synthetics: beep, buzz, dwoop, yes, no, rcough, rsneeze, ping. Skrell: warble")
twitch_v, vomit, whimper, wink, yawn. Prometheans: squish Synthetics: beep, buzz, dwoop, yes, no, rcough, rsneeze, ping. Skrell: warble</span>")
else
to_chat(src, "<font color='blue'>Unusable emote '[act]'. Say *help for a list.</font>")
to_chat(src, "<span class='filter_say'><font color='blue'>Unusable emote '[act]'. Say *help for a list.</font></span>")
if(message)
custom_emote(m_type,message)

View File

@@ -1,10 +1,8 @@
/mob/living/carbon/human/say(var/message,var/whispering=0)
var/alt_name = ""
/mob/living/carbon/human/GetAltName()
if(name != GetVoice())
alt_name = "(as [get_id_name("Unknown")])"
return " (as [get_id_name("Unknown")])"
message = sanitize(message)
..(message, alt_name = alt_name, whispering = whispering)
return ..()
/mob/living/carbon/human/proc/forcesay(list/append)
if(stat == CONSCIOUS)
@@ -48,35 +46,27 @@
return "normal"
/mob/living/carbon/human/say_understands(var/mob/other, var/datum/language/speaking = null)
if(has_brain_worms()) //Brain worms translate everything. Even mice and alien speak.
return 1
return TRUE
if(species.can_understand(other))
return 1
return TRUE
//These only pertain to common. Languages are handled by mob/say_understands()
if(!speaking)
if(istype(other, /mob/living/carbon/alien/diona))
if(other.languages.len >= 2) //They've sucked down some blood and can speak common now.
return 1
if (istype(other, /mob/living/silicon))
return 1
if (istype(other, /mob/living/carbon/brain))
return 1
if (istype(other, /mob/living/simple_mob/slime))
return 1
//This is already covered by mob/say_understands()
//if (istype(other, /mob/living/simple_animal))
// if((other.universal_speak && !speaking) || src.universal_speak || src.universal_understand)
// return 1
// return 0
return TRUE
if(issilicon(other))
return TRUE
if(isbrain(other))
return TRUE
if(isslime(other))
return TRUE
return ..()
/mob/living/carbon/human/GetVoice()
var/voice_sub
if(istype(get_rig(),/obj/item/weapon/rig))
var/obj/item/weapon/rig/rig = get_rig()
@@ -113,30 +103,6 @@
/mob/living/carbon/human/proc/GetSpecialVoice()
return special_voice
/*
***Deprecated***
let this be handled at the hear_say or hear_radio proc
This is left in for robot speaking when humans gain binary channel access until I get around to rewriting
robot_talk() proc.
There is no language handling build into it however there is at the /mob level so we accept the call
for it but just ignore it.
*/
/mob/living/carbon/human/say_quote(var/message, var/datum/language/speaking = null)
var/verb = "says"
var/ending = copytext(message, length(message))
if(speaking)
verb = speaking.get_spoken_verb(ending)
else
if(ending == "!")
verb=pick("exclaims","shouts","yells")
else if(ending == "?")
verb="asks"
return verb
/mob/living/carbon/human/handle_speech_problems(var/list/message_data)
if(silent || (sdisabilities & MUTE))
message_data[1] = ""
@@ -152,59 +118,72 @@
else
. = ..(message_data)
/mob/living/carbon/human/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name)
/mob/living/carbon/human/handle_message_mode(message_mode, list/message_pieces, verb, used_radios)
switch(message_mode)
if("intercom")
if(!src.restrained())
if(!restrained())
for(var/obj/item/device/radio/intercom/I in view(1))
I.talk_into(src, message, null, verb, speaking)
I.talk_into(src, message_pieces, null, verb)
I.add_fingerprint(src)
used_radios += I
if("headset")
if(l_ear && istype(l_ear,/obj/item/device/radio))
var/obj/item/device/radio/R = l_ear
R.talk_into(src,message,null,verb,speaking)
used_radios += l_ear
else if(r_ear && istype(r_ear,/obj/item/device/radio))
var/obj/item/device/radio/R = r_ear
R.talk_into(src,message,null,verb,speaking)
used_radios += r_ear
if("right ear")
var/obj/item/device/radio/R
var/has_radio = 0
if(r_ear && istype(r_ear,/obj/item/device/radio))
var/obj/item/device/radio/R = null
if(isradio(l_ear))
R = l_ear
if(R.talk_into(src, message_pieces, null, verb))
used_radios += R
return
if(isradio(r_ear))
R = r_ear
has_radio = 1
if(r_hand && istype(r_hand, /obj/item/device/radio))
if(R.talk_into(src, message_pieces, null, verb))
used_radios += R
return
if("right ear")
var/obj/item/device/radio/R = null
if(isradio(r_ear))
R = r_ear
if(isradio(r_hand))
R = r_hand
has_radio = 1
if(has_radio)
R.talk_into(src,message,null,verb,speaking)
if(istype(R))
if(R.talk_into(src, message_pieces, null, verb))
used_radios += R
if("left ear")
var/obj/item/device/radio/R
var/has_radio = 0
if(l_ear && istype(l_ear,/obj/item/device/radio))
var/obj/item/device/radio/R = null
if(isradio(l_ear))
R = l_ear
has_radio = 1
if(l_hand && istype(l_hand,/obj/item/device/radio))
if(isradio(l_hand))
R = l_hand
has_radio = 1
if(has_radio)
R.talk_into(src,message,null,verb,speaking)
if(istype(R))
if(R.talk_into(src, message_pieces, null, verb))
used_radios += R
else
if(message_mode)
if(l_ear && istype(l_ear,/obj/item/device/radio))
l_ear.talk_into(src,message, message_mode, verb, speaking)
if(isradio(l_ear))
if(l_ear.talk_into(src, message_pieces, message_mode, verb))
used_radios += l_ear
else if(r_ear && istype(r_ear,/obj/item/device/radio))
r_ear.talk_into(src,message, message_mode, verb, speaking)
return
if(isradio(r_ear))
if(r_ear.talk_into(src, message_pieces, message_mode, verb))
used_radios += r_ear
/mob/living/carbon/human/handle_speech_sound()
if(species.speech_sounds && prob(species.speech_chance))
var/list/returns[2]
if(species.speech_sounds && prob(species.speech_chance))
returns[1] = sound(pick(species.speech_sounds))
returns[2] = 50
return ..()
return returns
/mob/living/carbon/human/binarycheck()
. = FALSE
var/obj/item/device/radio/headset/R = null
if(istype(l_ear, /obj/item/device/radio/headset))
R = l_ear
if(R.translate_binary)
. = TRUE
if(istype(r_ear, /obj/item/device/radio/headset))
R = r_ear
if(R.translate_binary)
. = TRUE

View File

@@ -532,10 +532,9 @@
genders = list(PLURAL)
/datum/species/diona/can_understand(var/mob/other)
var/mob/living/carbon/alien/diona/D = other
if(istype(D))
return 1
return 0
if(istype(other, /mob/living/carbon/alien/diona))
return TRUE
return FALSE
/datum/species/diona/equip_survival_gear(var/mob/living/carbon/human/H)
if(H.backbag == 1)

View File

@@ -86,11 +86,9 @@
return "alien [caste_name] ([alien_number])"
/datum/species/xenos/can_understand(var/mob/other)
if(istype(other, /mob/living/carbon/alien/larva))
return 1
return 0
return TRUE
return FALSE
/datum/species/xenos/hug(var/mob/living/carbon/human/H,var/mob/living/target)
H.visible_message("<span class='notice'>[H] caresses [target] with its scythe-like arm.</span>", \

View File

@@ -1,22 +0,0 @@
/mob/living/carbon/slime/death(gibbed)
if(stat == DEAD) return
if(!gibbed && is_adult)
var/mob/living/carbon/slime/M = new /mob/living/carbon/slime(loc, colour)
M.rabid = 1
M.Friends = Friends.Copy()
step_away(M, src)
is_adult = 0
maxHealth = 150
revive()
if (!client) rabid = 1
number = rand(1, 1000)
name = "[colour] [is_adult ? "adult" : "baby"] slime ([number])"
return
. = ..(gibbed, "seizes up and falls limp...")
mood = null
regenerate_icons()
return

View File

@@ -1,99 +0,0 @@
/mob/living/carbon/slime/emote(var/act, var/m_type=1, var/message = null)
if (findtext(act, "-", 1, null))
var/t1 = findtext(act, "-", 1, null)
//param = copytext(act, t1 + 1, length(act) + 1)
act = copytext(act, 1, t1)
if(findtext(act,"s",-1) && !findtext(act,"_",-2))//Removes ending s's unless they are prefixed with a '_'
act = copytext(act,1,length(act))
var/updateicon = 0
switch(act) //Alphabetical please
if ("me")
if(silent)
return
if (src.client)
if (client.prefs.muted & MUTE_IC)
to_chat(src, "<font color='red'>You cannot send IC messages (muted).</font>")
return
if (stat)
return
if(!(message))
return
return custom_emote(m_type, message)
if("bounce")
message = "<B>The [src.name]</B> bounces in place."
m_type = 1
if ("custom")
return custom_emote(m_type, message)
if("jiggle")
message = "<B>The [src.name]</B> jiggles!"
m_type = 1
if("light")
message = "<B>The [src.name]</B> lights up for a bit, then stops."
m_type = 1
if("moan")
message = "<B>The [src.name]</B> moans."
m_type = 2
if("shiver")
message = "<B>The [src.name]</B> shivers."
m_type = 2
if("sway")
message = "<B>The [src.name]</B> sways around dizzily."
m_type = 1
if("twitch")
message = "<B>The [src.name]</B> twitches."
m_type = 1
if("vibrate")
message = "<B>The [src.name]</B> vibrates!"
m_type = 1
if("nomood")
mood = null
updateicon = 1
if("pout")
mood = "pout"
updateicon = 1
if("sad")
mood = "sad"
updateicon = 1
if("angry")
mood = "angry"
updateicon = 1
if("frown")
mood = "mischevous"
updateicon = 1
if("smile")
mood = ":3"
updateicon = 1
if ("help") //This is an exception
to_chat(src, "Help for slime emotes. You can use these emotes with say \"*emote\":\n\nbounce, custom, jiggle, light, moan, shiver, sway, twitch, vibrate. You can also set your face with: \n\nnomood, pout, sad, angry, frown, smile")
else
to_chat(src, "<font color='blue'>Unusable emote '[act]'. Say *help for a list.</font>")
if ((message && src.stat == 0))
if (m_type & 1)
for(var/mob/O in viewers(src, null))
O.show_message(message, m_type)
else
for(var/mob/O in hearers(src, null))
O.show_message(message, m_type)
if(updateicon)
regenerate_icons()
return

View File

@@ -1,31 +0,0 @@
/mob/living/carbon/slime/examine(mob/user)
..(user)
var/msg = ""
if (src.stat == DEAD)
msg += "<span class='deadsay'>It is limp and unresponsive.</span>\n"
else
if (src.getBruteLoss())
msg += "<span class='warning'>"
if (src.getBruteLoss() < 40)
msg += "It has some punctures in its flesh!"
else
msg += "<B>It has severe punctures and tears in its flesh!</B>"
msg += "</span>\n"
switch(powerlevel)
if(2 to 3)
msg += "It is flickering gently with a little electrical activity.\n"
if(4 to 5)
msg += "It is glowing gently with moderate levels of electrical activity.\n"
if(6 to 9)
msg += "<span class='warning'>It is glowing brightly with high levels of electrical activity.</span>\n"
if(10)
msg += "<span class='warning'><B>It is radiating with massive levels of electrical activity!</B></span>\n"
msg += "*---------*"
to_chat(user,msg)
return

View File

@@ -1,2 +0,0 @@
/mob/living/carbon/slime/handle_regular_hud_updates()
return

View File

@@ -1,392 +0,0 @@
/obj/item/slime_extract
name = "slime extract"
desc = "Goo extracted from a slime. Legends claim these to have \"magical powers\"."
icon = 'icons/mob/slimes.dmi'
icon_state = "grey slime extract"
force = 1.0
w_class = ITEMSIZE_TINY
throwforce = 0
throw_speed = 3
throw_range = 6
origin_tech = list(TECH_BIO = 4)
var/uses = 1 // uses before it goes inert
var/enhanced = 0 //has it been enhanced before?
flags = OPENCONTAINER
/*
/obj/item/slime_extract/attackby(obj/item/O as obj, mob/user as mob)
if(istype(O, /obj/item/weapon/slimesteroid2))
if(enhanced == 1)
to_chat(user, "<span class='warning'> This extract has already been enhanced!</span>")
return ..()
if(Uses == 0)
to_chat(user, "<span class='warning'> You can't enhance a used extract!</span>")
return ..()
to_chat(user, "You apply the enhancer. It now has triple the amount of uses.")
Uses = 3
enhanced = 1
qdel(O)
*/
/obj/item/slime_extract/New()
..()
create_reagents(5)
// reagents.add_reagent("slimejelly", 30)
/obj/item/slime_extract/grey
name = "grey slime extract"
icon_state = "grey slime extract"
/obj/item/slime_extract/gold
name = "gold slime extract"
icon_state = "gold slime extract"
/obj/item/slime_extract/silver
name = "silver slime extract"
icon_state = "silver slime extract"
/obj/item/slime_extract/metal
name = "metal slime extract"
icon_state = "metal slime extract"
/obj/item/slime_extract/purple
name = "purple slime extract"
icon_state = "purple slime extract"
/obj/item/slime_extract/dark_purple
name = "dark purple slime extract"
icon_state = "dark purple slime extract"
/obj/item/slime_extract/orange
name = "orange slime extract"
icon_state = "orange slime extract"
/obj/item/slime_extract/yellow
name = "yellow slime extract"
icon_state = "yellow slime extract"
/obj/item/slime_extract/red
name = "red slime extract"
icon_state = "red slime extract"
/obj/item/slime_extract/blue
name = "blue slime extract"
icon_state = "blue slime extract"
/obj/item/slime_extract/dark_blue
name = "dark blue slime extract"
icon_state = "dark blue slime extract"
/obj/item/slime_extract/pink
name = "pink slime extract"
icon_state = "pink slime extract"
/obj/item/slime_extract/green
name = "green slime extract"
icon_state = "green slime extract"
/obj/item/slime_extract/lightpink
name = "light pink slime extract"
icon_state = "light pink slime extract"
/obj/item/slime_extract/black
name = "black slime extract"
icon_state = "black slime extract"
/obj/item/slime_extract/oil
name = "oil slime extract"
icon_state = "oil slime extract"
/obj/item/slime_extract/adamantine
name = "adamantine slime extract"
icon_state = "adamantine slime extract"
/obj/item/slime_extract/bluespace
name = "bluespace slime extract"
icon_state = "bluespace slime extract"
/obj/item/slime_extract/pyrite
name = "pyrite slime extract"
icon_state = "pyrite slime extract"
/obj/item/slime_extract/cerulean
name = "cerulean slime extract"
icon_state = "cerulean slime extract"
/obj/item/slime_extract/sepia
name = "sepia slime extract"
icon_state = "sepia slime extract"
/obj/item/slime_extract/rainbow
name = "rainbow slime extract"
icon_state = "rainbow slime extract"
/obj/item/slimepotion
icon = 'icons/obj/chemical.dmi'
////Pet Slime Creation///
/*
/obj/item/slimepotion/docility
name = "docility potion"
desc = "A potent chemical mix that will nullify a slime's powers, causing it to become docile and tame."
icon_state = "bottle19"
/obj/item/slimepotion/docility/attack(mob/living/carbon/slime/M as mob, mob/user as mob)
if(!istype(M, /mob/living/carbon/slime))//If target is not a slime.
to_chat(user, "<span class='warning'> The potion only works on slimes!</span>")
return ..()
// if(M.is_adult) //Can't tame adults
// to_chat(user, "<span class='warning'> Only baby slimes can be tamed!</span>")
// return..()
if(M.stat)
to_chat(user, "<span class='warning'> The slime is dead!</span>")
return..()
if(M.mind)
to_chat(user, "<span class='warning'> The slime resists!</span>")
return ..()
var/mob/living/simple_animal/slime/pet = new /mob/living/simple_animal/slime(M.loc)
pet.icon_state = "[M.colour] [M.is_adult ? "adult" : "baby"] slime"
pet.icon_living = "[M.colour] [M.is_adult ? "adult" : "baby"] slime"
pet.icon_dead = "[M.colour] [M.is_adult ? "adult" : "baby"] slime dead"
pet.colour = "[M.colour]"
to_chat(user, "You feed the slime the potion, removing it's powers and calming it.")
qdel(M)
var/newname = sanitize(input(user, "Would you like to give the slime a name?", "Name your new pet", "pet slime") as null|text, MAX_NAME_LEN)
if (!newname)
newname = "pet slime"
pet.name = newname
pet.real_name = newname
qdel(src)
/obj/item/slimepotion/stabilizer
name = "slime stabilizer"
desc = "A potent chemical mix that will reduce the chance of a slime mutating."
icon_state = "potcyan"
/obj/item/slimepotion/stabilizer/attack(mob/living/carbon/slime/M, mob/user)
if(!isslime(M))
to_chat(user, "<span class='warning'>The stabilizer only works on slimes!</span>")
return ..()
if(M.stat)
to_chat(user, "<span class='warning'>The slime is dead!</span>")
return ..()
if(M.mutation_chance == 0)
to_chat(user, "<span class='warning'>The slime already has no chance of mutating!</span>")
return ..()
to_chat(user, "<span class='notice'>You feed the slime the stabilizer. It is now less likely to mutate.</span>")
M.mutation_chance = Clamp(M.mutation_chance-15,0,100)
qdel(src)
/obj/item/weapon/slimepotion2
name = "advanced docility potion"
desc = "A potent chemical mix that will nullify a slime's powers, causing it to become docile and tame. This one is meant for adult slimes"
icon = 'icons/obj/chemical.dmi'
icon_state = "bottle19"
attack(mob/living/carbon/slime/M as mob, mob/user as mob)
if(!istype(M, /mob/living/carbon/slime/))//If target is not a slime.
to_chat(user, "<span class='warning'> The potion only works on slimes!</span>")
return ..()
if(M.stat)
to_chat(user, "<span class='warning'> The slime is dead!</span>")
return..()
if(M.mind)
to_chat(user, "<span class='warning'> The slime resists!</span>")
return ..()
var/mob/living/simple_animal/adultslime/pet = new /mob/living/simple_animal/adultslime(M.loc)
pet.icon_state = "[M.colour] adult slime"
pet.icon_living = "[M.colour] adult slime"
pet.icon_dead = "[M.colour] baby slime dead"
pet.colour = "[M.colour]"
to_chat(user, "You feed the slime the potion, removing it's powers and calming it.")
qdel(M)
var/newname = sanitize(input(user, "Would you like to give the slime a name?", "Name your new pet", "pet slime") as null|text, MAX_NAME_LEN)
if (!newname)
newname = "pet slime"
pet.name = newname
pet.real_name = newname
qdel(src)
/obj/item/slimesteroid
name = "slime steroid"
desc = "A potent chemical mix that will cause a slime to generate more extract."
icon = 'icons/obj/chemical.dmi'
icon_state = "bottle16"
attack(mob/living/carbon/slime/M as mob, mob/user as mob)
if(!istype(M, /mob/living/carbon/slime))//If target is not a slime.
to_chat(user, "<span class='warning'> The steroid only works on baby slimes!</span>")
return ..()
if(M.is_adult) //Can't tame adults
to_chat(user, "<span class='warning'> Only baby slimes can use the steroid!</span>")
return..()
if(M.stat)
to_chat(user, "<span class='warning'> The slime is dead!</span>")
return..()
if(M.cores == 3)
to_chat(user, "<span class='warning'> The slime already has the maximum amount of extract!</span>")
return..()
to_chat(user, "You feed the slime the steroid. It now has triple the amount of extract.")
M.cores = 3
qdel(src)
/obj/item/slimesteroid2
name = "extract enhancer"
desc = "A potent chemical mix that will give a slime extract three uses."
icon = 'icons/obj/chemical.dmi'
icon_state = "bottle17"
/*afterattack(obj/target, mob/user , flag)
if(istype(target, /obj/item/slime_extract))
if(target.enhanced == 1)
to_chat(user, "<span class='warning'> This extract has already been enhanced!</span>")
return ..()
if(target.Uses == 0)
to_chat(user, "<span class='warning'> You can't enhance a used extract!</span>")
return ..()
to_chat(user, "You apply the enhancer. It now has triple the amount of uses.")
target.Uses = 3
target.enahnced = 1
qdel(src)*/
*/
/obj/effect/golemrune
anchored = 1
desc = "a strange rune used to create golems. It glows when spirits are nearby."
name = "rune"
icon = 'icons/obj/rune.dmi'
icon_state = "golem"
unacidable = 1
layer = TURF_LAYER
New()
..()
START_PROCESSING(SSobj, src)
process()
var/mob/observer/dead/ghost
for(var/mob/observer/dead/O in src.loc)
if(!O.client) continue
if(O.mind && O.mind.current && O.mind.current.stat != DEAD) continue
ghost = O
break
if(ghost)
icon_state = "golem2"
else
icon_state = "golem"
attack_hand(mob/living/user as mob)
var/mob/observer/dead/ghost
for(var/mob/observer/dead/O in src.loc)
if(!O.client) continue
if(O.mind && O.mind.current && O.mind.current.stat != DEAD) continue
ghost = O
break
if(!ghost)
to_chat(user, "The rune fizzles uselessly. There is no spirit nearby.")
return
var/mob/living/carbon/human/G = new(src.loc)
G.set_species("Golem")
G.key = ghost.key
to_chat(G, "You are an adamantine golem. You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools. Serve [user], and assist them in completing their goals at any cost.")
qdel(src)
proc/announce_to_ghosts()
for(var/mob/observer/dead/G in player_list)
if(G.client)
var/area/A = get_area(src)
if(A)
to_chat(G, "Golem rune created in [A.name].")
/mob/living/carbon/slime/has_eyes()
return 0
//////////////////////////////Old shit from metroids/RoRos, and the old cores, would not take much work to re-add them////////////////////////
/*
// Basically this slime Core catalyzes reactions that normally wouldn't happen anywhere
/obj/item/slime_core
name = "slime extract"
desc = "Goo extracted from a slime. Legends claim these to have \"magical powers\"."
icon = 'icons/mob/slimes.dmi'
icon_state = "slime extract"
force = 1.0
w_class = ITEMSIZE_TINY
throwforce = 1.0
throw_speed = 2
throw_range = 6
origin_tech = list(TECH_BIO = 4)
var/POWERFLAG = 0 // sshhhhhhh
var/Flush = 30
var/Uses = 5 // uses before it goes inert
/obj/item/slime_core/New()
..()
create_reagents(100)
POWERFLAG = rand(1,10)
Uses = rand(7, 25)
//flags |= NOREACT
/*
spawn()
Life()
proc/Life()
while(src)
sleep(25)
Flush--
if(Flush <= 0)
reagents.clear_reagents()
Flush = 30
*/
/obj/item/weapon/reagent_containers/food/snacks/egg/slime
name = "slime egg"
desc = "A small, gelatinous egg."
icon = 'icons/mob/mob.dmi'
icon_state = "slime egg-growing"
bitesize = 12
origin_tech = list(TECH_BIO = 4)
var/grown = 0
/obj/item/weapon/reagent_containers/food/snacks/egg/slime/Initialize()
. = ..()
reagents.add_reagent("nutriment", 4)
reagents.add_reagent("slimejelly", 1)
addtimer(CALLBACK(src, ./proc/Grow), rand(120 SECONDS, 150 SECONDS))
/obj/item/weapon/reagent_containers/food/snacks/egg/slime/proc/Grow()
grown = 1
icon_state = "slime egg-grown"
START_PROCESSING(SSobj, src)
return
/obj/item/weapon/reagent_containers/food/snacks/egg/slime/proc/Hatch()
STOP_PROCESSING(SSobj, src)
var/turf/T = get_turf(src)
src.visible_message("<span class='warning'> The [name] pulsates and quivers!</span>")
spawn(rand(50,100))
src.visible_message("<span class='warning'> The [name] bursts open!</span>")
new/mob/living/carbon/slime(T)
qdel(src)
/obj/item/weapon/reagent_containers/food/snacks/egg/slime/process()
var/turf/location = get_turf(src)
var/datum/gas_mixture/environment = location.return_air()
if (environment.phoron > MOLES_PHORON_VISIBLE)//phoron exposure causes the egg to hatch
src.Hatch()
/obj/item/weapon/reagent_containers/food/snacks/egg/slime/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(istype( W, /obj/item/weapon/pen/crayon ))
return
else
..()
*/

View File

@@ -1,527 +0,0 @@
/mob/living/carbon/slime/Life()
set invisibility = 0
set background = 1
if (src.transforming)
return
..()
if(stat != DEAD)
handle_nutrition()
if (!client)
handle_targets()
if (!AIproc)
spawn()
handle_AI()
handle_speech_and_mood()
/mob/living/carbon/slime/handle_environment(datum/gas_mixture/environment)
if(!environment)
adjustToxLoss(rand(10,20))
return
//var/environment_heat_capacity = environment.heat_capacity()
var/loc_temp = T0C
if(istype(get_turf(src), /turf/space))
//environment_heat_capacity = loc:heat_capacity
var/turf/heat_turf = get_turf(src)
loc_temp = heat_turf.temperature
else if(istype(loc, /obj/machinery/atmospherics/unary/cryo_cell))
loc_temp = loc:air_contents.temperature
else
loc_temp = environment.temperature
bodytemperature += adjust_body_temperature(bodytemperature, loc_temp, 1)
if(bodytemperature < (T0C + 5)) // start calculating temperature damage etc
if(bodytemperature <= hurt_temperature)
if(bodytemperature <= die_temperature)
adjustToxLoss(200)
else
// could be more fancy, but doesn't worth the complexity: when the slimes goes into a cold area
// the damage is mostly determined by how fast its body cools
adjustToxLoss(30)
updatehealth()
return //TODO: DEFERRED
/mob/living/carbon/slime/proc/adjust_body_temperature(current, loc_temp, boost)
var/temperature = current
var/difference = abs(current-loc_temp) //get difference
var/increments// = difference/10 //find how many increments apart they are
if(difference > 50)
increments = difference/5
else
increments = difference/10
var/change = increments*boost // Get the amount to change by (x per increment)
var/temp_change
if(current < loc_temp)
temperature = min(loc_temp, temperature+change)
else if(current > loc_temp)
temperature = max(loc_temp, temperature-change)
temp_change = (temperature - current)
return temp_change
/mob/living/carbon/slime/handle_chemicals_in_body()
chem_effects.Cut()
if(touching) touching.metabolize()
if(ingested) ingested.metabolize()
if(bloodstr) bloodstr.metabolize()
src.updatehealth()
return //TODO: DEFERRED
/mob/living/carbon/slime/handle_regular_status_updates()
src.blinded = null
health = getMaxHealth() - (getOxyLoss() + getToxLoss() + getFireLoss() + getBruteLoss() + getCloneLoss())
if(health < 0 && stat != DEAD)
death()
return
if (halloss)
halloss = 0
if(prob(30))
adjustOxyLoss(-1)
adjustToxLoss(-1)
adjustFireLoss(-1)
adjustCloneLoss(-1)
adjustBruteLoss(-1)
if (src.stat == DEAD)
src.lying = 1
src.blinded = 1
else
if (src.paralysis || src.stunned || src.weakened || (status_flags & FAKEDEATH)) //Stunned etc.
if (src.stunned > 0)
src.set_stat(CONSCIOUS)
if (src.weakened > 0)
src.lying = 0
src.set_stat(CONSCIOUS)
if (src.paralysis > 0)
src.blinded = 0
src.lying = 0
src.set_stat(CONSCIOUS)
else
src.lying = 0
src.set_stat(CONSCIOUS)
if (src.stuttering) src.stuttering = 0
if (src.eye_blind)
SetBlinded(0)
src.blinded = 1
if (src.ear_deaf > 0) src.ear_deaf = 0
if (src.ear_damage < 25)
src.ear_damage = 0
src.density = !( src.lying )
if (src.sdisabilities & BLIND)
src.blinded = 1
if (src.sdisabilities & DEAF)
src.ear_deaf = 1
if (src.eye_blurry > 0)
src.eye_blurry = 0
if (src.druggy > 0)
src.druggy = 0
return 1
/mob/living/carbon/slime/proc/handle_nutrition()
if (prob(15))
nutrition -= 1 + is_adult
if(nutrition <= 0)
nutrition = 0
adjustToxLoss(rand(1,3))
if (client && prob(5))
to_chat(src, "<span class='danger'>You are starving!</span>")
else if (nutrition >= get_grow_nutrition() && amount_grown < 10)
nutrition -= 20
amount_grown++
/mob/living/carbon/slime/proc/handle_targets()
if(attacked > 50) attacked = 50 // Let's not get into absurdly long periods of rage
if(attacked > 0)
attacked--
if(Discipline > 0)
if(Discipline >= 5 && rabid)
if(prob(60)) rabid = 0
if(prob(10))
Discipline--
if(!canmove) return
if(Victim) return // if it's eating someone already, continue eating!
if(Target)
--target_patience
if (target_patience <= 0 || SStun || Discipline || attacked) // Tired of chasing or something draws out attention
target_patience = 0
Target = null
var/hungry = 0 // determines if the slime is hungry
if (nutrition < get_starve_nutrition())
hungry = 2
else if (nutrition < get_grow_nutrition() && prob(25) || nutrition < get_hunger_nutrition())
hungry = 1
if(hungry == 2 && !client) // if a slime is starving, it starts losing its friends
if(Friends.len > 0 && prob(1))
var/mob/nofriend = pick(Friends)
if(nofriend && Friends[nofriend])
Friends[nofriend] -= 1
if (Friends[nofriend] <= 0)
Friends[nofriend] = null
Friends -= nofriend
Friends -= null
if(!Target)
if(will_hunt(hungry) || attacked || rabid) // Only add to the list if we need to
var/list/targets = list()
for(var/mob/living/L in view(7,src))
if(isslime(L) || L.stat == DEAD) // Ignore other slimes and dead mobs
continue
if(L in Friends) // No eating friends!
continue
if(issilicon(L) && (rabid || attacked)) // They can't eat silicons, but they can glomp them in defence
targets += L // Possible target found!
if(istype(L, /mob/living/carbon/human) && dna) //Ignore slime(wo)men
var/mob/living/carbon/human/H = L
if(H.species.name == "Promethean")
continue
if(!L.canmove) // Only one slime can latch on at a time.
var/notarget = 0
for(var/mob/living/carbon/slime/M in view(1,L))
if(M.Victim == L)
notarget = 1
if(notarget)
continue
targets += L // Possible target found!
if(targets.len > 0)
if(attacked || rabid || hungry == 2)
Target = targets[1] // I am attacked and am fighting back or so hungry I don't even care
else
for(var/mob/living/carbon/C in targets)
if(ishuman(C) && !Discipline && prob(5))
Target = C
break
if(isalien(C) || issmall(C) || isanimal(C))
Target = C
break
if (Target)
target_patience = rand(5,7)
if (is_adult)
target_patience += 3
if(!Target) // If we have no target, we are wandering or following orders
if (Leader)
if (holding_still)
holding_still = max(holding_still - 1, 0)
else if(canmove && isturf(loc))
step_to(src, Leader)
else if(hungry)
if (holding_still)
holding_still = max(holding_still - 1 - hungry, 0)
else if(canmove && isturf(loc) && prob(50))
step(src, pick(cardinal))
else
if (holding_still)
holding_still = max(holding_still - 1, 0)
else if(canmove && isturf(loc) && prob(33))
step(src, pick(cardinal))
/mob/living/carbon/slime/proc/handle_AI() // the master AI process
if(stat == DEAD || client || Victim) return // If we're dead or have a client, we don't need AI, if we're feeding, we continue feeding
AIproc = 1
if(amount_grown >= 10 && !Target)
if(is_adult)
Reproduce()
else
Evolve()
AIproc = 0
return
if(Target) // We're chasing the target
if(Target.stat == DEAD)
Target = null
AIproc = 0
return
for(var/mob/living/carbon/slime/M in view(1, Target))
if(M.Victim == Target)
Target = null
AIproc = 0
return
if(Target.Adjacent(src))
if(istype(Target, /mob/living/silicon)) // Glomp the silicons
if(!Atkcool)
a_intent = I_HURT
UnarmedAttack(Target)
Atkcool = 1
spawn(45)
Atkcool = 0
AIproc = 0
return
if(Target.client && !Target.lying && prob(60 + powerlevel * 4)) // Try to take down the target first
if(!Atkcool)
Atkcool = 1
spawn(45)
Atkcool = 0
a_intent = I_DISARM
UnarmedAttack(Target)
else
if(!Atkcool)
a_intent = I_GRAB
UnarmedAttack(Target)
else if(Target in view(7, src))
step_to(src, Target)
else
Target = null
AIproc = 0
return
else
var/mob/living/carbon/slime/frenemy
for (var/mob/living/carbon/slime/S in view(1, src))
if (S != src)
frenemy = S
if (frenemy && prob(1))
if (frenemy.colour == colour)
a_intent = I_HELP
else
a_intent = I_HURT
UnarmedAttack(frenemy)
var/sleeptime = movement_delay()
if(sleeptime <= 5) sleeptime = 5 // Maximum one action per half a second
spawn (sleeptime)
handle_AI()
return
/mob/living/carbon/slime/proc/handle_speech_and_mood()
//Mood starts here
var/newmood = ""
a_intent = I_HELP
if (rabid || attacked)
newmood = "angry"
a_intent = I_HURT
else if (Target) newmood = "mischevous"
if (!newmood)
if (Discipline && prob(25))
newmood = "pout"
else if (prob(1))
newmood = pick("sad", ":3", "pout")
if ((mood == "sad" || mood == ":3" || mood == "pout") && !newmood)
if (prob(75)) newmood = mood
if (newmood != mood) // This is so we don't redraw them every time
mood = newmood
regenerate_icons()
//Speech understanding starts here
var/to_say
if (speech_buffer.len > 0)
var/who = speech_buffer[1] // Who said it?
var/phrase = speech_buffer[2] // What did they say?
if ((findtext(phrase, num2text(number)) || findtext(phrase, "slimes"))) // Talking to us
if (findtext(phrase, "hello") || findtext(phrase, "hi"))
to_say = pick("Hello...", "Hi...")
else if (findtext(phrase, "follow"))
if (Leader)
if (Leader == who) // Already following him
to_say = pick("Yes...", "Lead...", "Following...")
else if (Friends[who] > Friends[Leader]) // VIVA
Leader = who
to_say = "Yes... I follow [who]..."
else
to_say = "No... I follow [Leader]..."
else
if (Friends[who] > 2)
Leader = who
to_say = "I follow..."
else // Not friendly enough
to_say = pick("No...", "I won't follow...")
else if (findtext(phrase, "stop"))
if (Victim) // We are asked to stop feeding
if (Friends[who] > 4)
Victim = null
Target = null
if (Friends[who] < 7)
--Friends[who]
to_say = "Grrr..." // I'm angry but I do it
else
to_say = "Fine..."
else if (Target) // We are asked to stop chasing
if (Friends[who] > 3)
Target = null
if (Friends[who] < 6)
--Friends[who]
to_say = "Grrr..." // I'm angry but I do it
else
to_say = "Fine..."
else if (Leader) // We are asked to stop following
if (Leader == who)
to_say = "Yes... I'll stay..."
Leader = null
else
if (Friends[who] > Friends[Leader])
Leader = null
to_say = "Yes... I'll stop..."
else
to_say = "No... I'll keep following..."
else if (findtext(phrase, "stay"))
if (Leader)
if (Leader == who)
holding_still = Friends[who] * 10
to_say = "Yes... Staying..."
else if (Friends[who] > Friends[Leader])
holding_still = (Friends[who] - Friends[Leader]) * 10
to_say = "Yes... Staying..."
else
to_say = "No... I'll keep following..."
else
if (Friends[who] > 2)
holding_still = Friends[who] * 10
to_say = "Yes... Staying..."
else
to_say = "No... I won't stay..."
speech_buffer = list()
//Speech starts here
if (to_say)
say (to_say)
else if(prob(1))
emote(pick("bounce","sway","light","vibrate","jiggle"))
else
var/t = 10
var/slimes_near = -1 // Don't count myself
var/dead_slimes = 0
var/friends_near = list()
for (var/mob/living/carbon/M in view(7,src))
if (isslime(M))
++slimes_near
if (M.stat == DEAD)
++dead_slimes
if (M in Friends)
t += 20
friends_near += M
if (nutrition < get_hunger_nutrition()) t += 10
if (nutrition < get_starve_nutrition()) t += 10
if (prob(2) && prob(t))
var/phrases = list()
if (Target) phrases += "[Target]... looks tasty..."
if (nutrition < get_starve_nutrition())
phrases += "So... hungry..."
phrases += "Very... hungry..."
phrases += "Need... food..."
phrases += "Must... eat..."
else if (nutrition < get_hunger_nutrition())
phrases += "Hungry..."
phrases += "Where is the food?"
phrases += "I want to eat..."
phrases += "Rawr..."
phrases += "Blop..."
phrases += "Blorble..."
if (rabid || attacked)
phrases += "Hrr..."
phrases += "Nhuu..."
phrases += "Unn..."
if (mood == ":3")
phrases += "Purr..."
if (attacked)
phrases += "Grrr..."
if (getToxLoss() > 30)
phrases += "Cold..."
if (getToxLoss() > 60)
phrases += "So... cold..."
phrases += "Very... cold..."
if (getToxLoss() > 90)
phrases += "..."
phrases += "C... c..."
if (Victim)
phrases += "Nom..."
phrases += "Tasty..."
if (powerlevel > 3) phrases += "Bzzz..."
if (powerlevel > 5) phrases += "Zap..."
if (powerlevel > 8) phrases += "Zap... Bzz..."
if (mood == "sad") phrases += "Bored..."
if (slimes_near) phrases += "Brother..."
if (slimes_near > 1) phrases += "Brothers..."
if (dead_slimes) phrases += "What happened?"
if (!slimes_near)
phrases += "Lonely..."
for (var/M in friends_near)
phrases += "[M]... friend..."
if (nutrition < get_hunger_nutrition())
phrases += "[M]... feed me..."
say (pick(phrases))
/mob/living/carbon/slime/proc/get_max_nutrition() // Can't go above it
if (is_adult) return 1200
else return 1000
/mob/living/carbon/slime/proc/get_grow_nutrition() // Above it we grow, below it we can eat
if (is_adult) return 1000
else return 800
/mob/living/carbon/slime/proc/get_hunger_nutrition() // Below it we will always eat
if (is_adult) return 600
else return 500
/mob/living/carbon/slime/proc/get_starve_nutrition() // Below it we will eat before everything else
if (is_adult) return 300
else return 200
/mob/living/carbon/slime/proc/will_hunt(var/hunger) // Check for being stopped from feeding and chasing
if (hunger == 2 || rabid || attacked) return 1
if (Leader) return 0
if (holding_still) return 0
if (hunger == 1 || prob(25))
return 1
return 0
/mob/living/carbon/slime/slip() //Can't slip something without legs.
return 0

View File

@@ -1,4 +0,0 @@
/mob/living/carbon/slime/Login()
..()
update_hud()
return

View File

@@ -1,384 +0,0 @@
/mob/living/carbon/slime
name = "baby slime"
icon = 'icons/mob/slimes.dmi'
icon_state = "grey baby slime"
pass_flags = PASSTABLE
var/is_adult = 0
speak_emote = list("chirps")
plane = MOB_PLANE
layer = ABOVE_MOB_LAYER
maxHealth = 150
health = 150
gender = NEUTER
update_icon = 0
nutrition = 700
see_in_dark = 8
update_slimes = 0
// canstun and canweaken don't affect slimes because they ignore stun and weakened variables
// for the sake of cleanliness, though, here they are.
status_flags = CANPARALYSE|CANPUSH
var/cores = 1 // the number of /obj/item/slime_extract's the slime has left inside
var/mutation_chance = 30 // Chance of mutating, should be between 25 and 35
var/powerlevel = 0 // 0-10 controls how much electricity they are generating
var/amount_grown = 0 // controls how long the slime has been overfed, if 10, grows or reproduces
var/number = 0 // Used to understand when someone is talking to it
var/mob/living/Victim = null // the person the slime is currently feeding on
var/mob/living/Target = null // AI variable - tells the slime to hunt this down
var/mob/living/Leader = null // AI variable - tells the slime to follow this person
var/attacked = 0 // Determines if it's been attacked recently. Can be any number, is a cooloff-ish variable
var/rabid = 0 // If set to 1, the slime will attack and eat anything it comes in contact with
var/holding_still = 0 // AI variable, cooloff-ish for how long it's going to stay in one place
var/target_patience = 0 // AI variable, cooloff-ish for how long it's going to follow its target
var/list/Friends = list() // A list of friends; they are not considered targets for feeding; passed down after splitting
var/list/speech_buffer = list() // Last phrase said near it and person who said it
var/mood = "" // To show its face
var/AIproc = 0 // If it's 0, we need to launch an AI proc
var/Atkcool = 0 // attack cooldown
var/SStun = 0 // NPC stun variable. Used to calm them down when they are attacked while feeding, or they will immediately re-attach
var/Discipline = 0 // if a slime has been hit with a freeze gun, or wrestled/attacked off a human, they become disciplined and don't attack anymore for a while. The part about freeze gun is a lie
var/hurt_temperature = T0C-50 // slime keeps taking damage when its bodytemperature is below this
var/die_temperature = 50 // slime dies instantly when its bodytemperature is below this
///////////TIME FOR SUBSPECIES
var/colour = "grey"
var/coretype = /obj/item/slime_extract/grey
var/list/slime_mutation[4]
var/core_removal_stage = 0 //For removing cores.
/mob/living/carbon/slime/New(var/location, var/colour="grey")
verbs += /mob/living/proc/ventcrawl
src.colour = colour
number = rand(1, 1000)
name = "[colour] [is_adult ? "adult" : "baby"] slime ([number])"
real_name = name
slime_mutation = mutation_table(colour)
mutation_chance = rand(25, 35)
var/sanitizedcolour = replacetext(colour, " ", "")
coretype = text2path("/obj/item/slime_extract/[sanitizedcolour]")
regenerate_icons()
..(location)
/mob/living/carbon/slime/movement_delay()
if (bodytemperature >= 330.23) // 135 F
return -1 // slimes become supercharged at high temperatures
var/tally = 0
var/health_deficiency = (getMaxHealth() - health)
if(health_deficiency >= 30) tally += (health_deficiency / 25)
if (bodytemperature < 183.222)
tally += (283.222 - bodytemperature) / 10 * 1.75
if(reagents)
if(reagents.has_reagent("hyperzine")) // Hyperzine slows slimes down
tally *= 2
if(reagents.has_reagent("frostoil")) // Frostoil also makes them move VEEERRYYYYY slow
tally *= 5
if(health <= 0) // if damaged, the slime moves twice as slow
tally *= 2
return tally + config.slime_delay
/mob/living/carbon/slime/Bump(atom/movable/AM as mob|obj, yes)
if ((!(yes) || now_pushing))
return
now_pushing = 1
if(isobj(AM) && !client && powerlevel > 0)
var/probab = 10
switch(powerlevel)
if(1 to 2) probab = 20
if(3 to 4) probab = 30
if(5 to 6) probab = 40
if(7 to 8) probab = 60
if(9) probab = 70
if(10) probab = 95
if(prob(probab))
if(istype(AM, /obj/structure/window) || istype(AM, /obj/structure/grille))
if(nutrition <= get_hunger_nutrition() && !Atkcool)
if (is_adult || prob(5))
UnarmedAttack(AM)
Atkcool = 1
spawn(45)
Atkcool = 0
if(ismob(AM))
var/mob/tmob = AM
if(is_adult)
if(istype(tmob, /mob/living/carbon/human))
if(prob(90))
now_pushing = 0
return
else
if(istype(tmob, /mob/living/carbon/human))
now_pushing = 0
return
now_pushing = 0
..()
/mob/living/carbon/slime/Process_Spacemove()
return 2
/mob/living/carbon/slime/Stat()
..()
statpanel("Status")
stat(null, "Health: [round((health / getMaxHealth()) * 100)]%")
stat(null, "Intent: [a_intent]")
if (client.statpanel == "Status")
stat(null, "Nutrition: [nutrition]/[get_max_nutrition()]")
if(amount_grown >= 10)
if(is_adult)
stat(null, "You can reproduce!")
else
stat(null, "You can evolve!")
stat(null,"Power Level: [powerlevel]")
/mob/living/carbon/slime/adjustFireLoss(amount)
..(-abs(amount)) // Heals them
return
/mob/living/carbon/slime/bullet_act(var/obj/item/projectile/Proj)
attacked += 10
..(Proj)
return 0
/mob/living/carbon/slime/emp_act(severity)
powerlevel = 0 // oh no, the power!
..()
/mob/living/carbon/slime/ex_act(severity)
..()
var/b_loss = null
var/f_loss = null
switch (severity)
if (1.0)
qdel(src)
return
if (2.0)
b_loss += 60
f_loss += 60
if(3.0)
b_loss += 30
adjustBruteLoss(b_loss)
adjustFireLoss(f_loss)
updatehealth()
/mob/living/carbon/slime/u_equip(obj/item/W as obj)
return
/mob/living/carbon/slime/attack_ui(slot)
return
/mob/living/carbon/slime/attack_hand(mob/living/carbon/human/M as mob)
..()
if(Victim)
if(Victim == M)
if(prob(60))
visible_message("<span class='warning'>[M] attempts to wrestle \the [name] off!</span>")
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
else
visible_message("<span class='warning'> [M] manages to wrestle \the [name] off!</span>")
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
if(prob(90) && !client)
Discipline++
SStun = 1
spawn(rand(45,60))
SStun = 0
Victim = null
anchored = 0
step_away(src,M)
return
else
if(prob(30))
visible_message("<span class='warning'>[M] attempts to wrestle \the [name] off of [Victim]!</span>")
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
else
visible_message("<span class='warning'> [M] manages to wrestle \the [name] off of [Victim]!</span>")
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
if(prob(80) && !client)
Discipline++
if(!is_adult)
if(Discipline == 1)
attacked = 0
SStun = 1
spawn(rand(55,65))
SStun = 0
Victim = null
anchored = 0
step_away(src,M)
return
switch(M.a_intent)
if (I_HELP)
help_shake_act(M)
if (I_GRAB)
if (M == src || anchored)
return
var/obj/item/weapon/grab/G = new /obj/item/weapon/grab(M, src)
M.put_in_active_hand(G)
G.synch()
LAssailant = M
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
visible_message("<span class='warning'>[M] has grabbed [src] passively!</span>")
else
var/damage = rand(1, 9)
attacked += 10
if (prob(90))
if (HULK in M.mutations)
damage += 5
if(Victim || Target)
Victim = null
Target = null
anchored = 0
if(prob(80) && !client)
Discipline++
spawn(0)
step_away(src,M,15)
sleep(3)
step_away(src,M,15)
playsound(loc, "punch", 25, 1, -1)
visible_message("<span class='danger'>[M] has punched [src]!</span>", \
"<span class='danger'>[M] has punched [src]!</span>")
adjustBruteLoss(damage)
updatehealth()
else
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
visible_message("<span class='danger'>[M] has attempted to punch [src]!</span>")
return
/mob/living/carbon/slime/attackby(obj/item/W, mob/user)
if(W.force > 0)
attacked += 10
if(prob(25))
to_chat(user, "<span class='danger'>[W] passes right through [src]!</span>")
return
if(Discipline && prob(50)) // wow, buddy, why am I getting attacked??
Discipline = 0
if(W.force >= 3)
if(is_adult)
if(prob(5 + round(W.force/2)))
if(Victim || Target)
if(prob(80) && !client)
Discipline++
Victim = null
Target = null
anchored = 0
SStun = 1
spawn(rand(5,20))
SStun = 0
spawn(0)
if(user)
canmove = 0
step_away(src, user)
if(prob(25 + W.force))
sleep(2)
if(user)
step_away(src, user)
canmove = 1
else
if(prob(10 + W.force*2))
if(Victim || Target)
if(prob(80) && !client)
Discipline++
if(Discipline == 1)
attacked = 0
SStun = 1
spawn(rand(5,20))
SStun = 0
Victim = null
Target = null
anchored = 0
spawn(0)
if(user)
canmove = 0
step_away(src, user)
if(prob(25 + W.force*4))
sleep(2)
if(user)
step_away(src, user)
canmove = 1
..()
/mob/living/carbon/slime/restrained()
return 0
/mob/living/carbon/slime/var/co2overloadtime = null
/mob/living/carbon/slime/var/temperature_resistance = T0C+75
/mob/living/carbon/slime/toggle_throw_mode()
return
/mob/living/carbon/slime/proc/gain_nutrition(var/amount)
nutrition += amount
if(prob(amount * 2)) // Gain around one level per 50 nutrition
powerlevel++
if(powerlevel > 10)
powerlevel = 10
adjustToxLoss(-10)
nutrition = max(nutrition, get_max_nutrition())

View File

@@ -1,169 +0,0 @@
/mob/living/carbon/slime/proc/Wrap(var/mob/living/M) // This is a proc for the clicks
if (Victim == M || src == M)
Feedstop()
return
if (Victim)
to_chat(src, "I am already feeding...")
return
var t = invalidFeedTarget(M)
if (t)
to_chat(src,t)
return
Feedon(M)
/mob/living/carbon/slime/proc/invalidFeedTarget(var/mob/living/M)
if (!M || !istype(M))
return "This subject is incomparable..."
if (istype(M, /mob/living/carbon/slime)) // No cannibalism... yet
return "I cannot feed on other slimes..."
if (!Adjacent(M))
return "This subject is too far away..."
if (istype(M, /mob/living/carbon) && M.getCloneLoss() >= M.getMaxHealth() * 1.5 || istype(M, /mob/living/simple_animal) && M.stat == DEAD)
return "This subject does not have an edible life energy..."
for(var/mob/living/carbon/slime/met in view())
if(met.Victim == M && met != src)
return "The [met.name] is already feeding on this subject..."
return 0
/mob/living/carbon/slime/proc/Feedon(var/mob/living/M)
Victim = M
loc = M.loc
canmove = 0
anchored = 1
regenerate_icons()
while(Victim && !invalidFeedTarget(M) && stat != 2)
canmove = 0
if(Adjacent(M))
UpdateFeed(M)
if(istype(M, /mob/living/carbon))
Victim.adjustCloneLoss(rand(5,6))
Victim.adjustToxLoss(rand(1,2))
if(Victim.health <= 0)
Victim.adjustToxLoss(rand(2,4))
else if(istype(M, /mob/living/simple_animal))
Victim.adjustBruteLoss(is_adult ? rand(7, 15) : rand(4, 12))
else
to_chat(src, "<span class='warning'>[pick("This subject is incompatable", "This subject does not have a life energy", "This subject is empty", "I am not satisified", "I can not feed from this subject", "I do not feel nourished", "This subject is not food")]...</span>")
Feedstop()
break
if(prob(15) && M.client && istype(M, /mob/living/carbon))
var/painMes = pick("You can feel your body becoming weak!", "You feel like you're about to die!", "You feel every part of your body screaming in agony!", "A low, rolling pain passes through your body!", "Your body feels as if it's falling apart!", "You feel extremely weak!", "A sharp, deep pain bathes every inch of your body!")
if (ishuman(M))
var/mob/living/carbon/human/H = M
H.custom_pain(painMes, 100)
else if (istype(M, /mob/living/carbon))
var/mob/living/carbon/C = M
if (C.can_feel_pain())
to_chat(M, "<span class='danger'>[painMes]</span>")
gain_nutrition(rand(20,25))
adjustOxyLoss(-10) //Heal yourself
adjustBruteLoss(-10)
adjustFireLoss(-10)
adjustCloneLoss(-10)
updatehealth()
if(Victim)
Victim.updatehealth()
sleep(30) // Deal damage every 3 seconds
else
break
canmove = 1
anchored = 0
if(M && invalidFeedTarget(M)) // This means that the slime drained the victim
if(!client)
if(Victim && !rabid && !attacked && Victim.LAssailant && Victim.LAssailant != Victim && prob(50))
if(!(Victim.LAssailant in Friends))
Friends[Victim.LAssailant] = 1
else
++Friends[Victim.LAssailant]
else
to_chat(src, "<span class='notice'>This subject does not have a strong enough life energy anymore...</span>")
Victim = null
/mob/living/carbon/slime/proc/Feedstop()
if(Victim)
if(Victim.client)
to_chat(Victim, "[src] has let go of your head!")
Victim = null
/mob/living/carbon/slime/proc/UpdateFeed(var/mob/M)
if(Victim)
if(Victim == M)
loc = M.loc // simple "attach to head" effect!
/mob/living/carbon/slime/verb/Evolve()
set category = "Slime"
set desc = "This will let you evolve from baby to adult slime."
if(stat)
to_chat(src, "<span class='notice'>I must be conscious to do this...</span>")
return
if(!is_adult)
if(amount_grown >= 10)
is_adult = 1
maxHealth = 200
amount_grown = 0
regenerate_icons()
name = text("[colour] [is_adult ? "adult" : "baby"] slime ([number])")
else
to_chat(src, "<span class='notice'>I am not ready to evolve yet...</span>")
else
to_chat(src, "<span class='notice'>I have already evolved...</span>")
/mob/living/carbon/slime/verb/Reproduce()
set category = "Slime"
set desc = "This will make you split into four Slimes."
if(stat)
to_chat(src, "<span class='notice'>I must be conscious to do this...</span>")
return
if(is_adult)
if(amount_grown >= 10)
if(stat)
to_chat(src, "<span class='notice'>I must be conscious to do this...</span>")
return
var/list/babies = list()
var/new_nutrition = round(nutrition * 0.9)
var/new_powerlevel = round(powerlevel / 4)
for(var/i = 1, i <= 4, i++)
var/t = colour
if(prob(mutation_chance))
t = slime_mutation[rand(1,4)]
var/mob/living/carbon/slime/M = new /mob/living/carbon/slime/(loc, t)
if(ckey) M.nutrition = new_nutrition //Player slimes are more robust at spliting. Once an oversight of poor copypasta, now a feature!
M.powerlevel = new_powerlevel
if(i != 1) step_away(M, src)
M.Friends = Friends.Copy()
babies += M
feedback_add_details("slime_babies_born","slimebirth_[replacetext(M.colour," ","_")]")
var/mob/living/carbon/slime/new_slime = pick(babies)
new_slime.universal_speak = universal_speak
if(src.mind)
src.mind.transfer_to(new_slime)
else
new_slime.key = src.key
qdel(src)
else
to_chat(src, "<span class='notice'>I am not ready to reproduce yet...</span>")
else
to_chat(src, "<span class='notice'>I am not old enough to reproduce yet...</span>")

View File

@@ -1,39 +0,0 @@
/mob/living/carbon/slime/say(var/message)
message = sanitize(message)
var/verb = say_quote(message)
if(copytext(message,1,2) == "*")
return emote(copytext(message,2))
return ..(message, null, verb)
/mob/living/carbon/slime/say_quote(var/text)
var/ending = copytext(text, length(text))
if (ending == "?")
return "asks";
else if (ending == "!")
return "cries";
return "chirps";
/mob/living/carbon/slime/say_understands(var/other)
if (istype(other, /mob/living/carbon/slime))
return 1
return ..()
/mob/living/carbon/slime/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
if (speaker in Friends)
speech_buffer = list()
speech_buffer.Add(speaker)
speech_buffer.Add(lowertext(html_decode(message)))
..()
/mob/living/carbon/slime/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/part_c, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="")
if (speaker in Friends)
speech_buffer = list()
speech_buffer.Add(speaker)
speech_buffer.Add(lowertext(html_decode(message)))
..()

View File

@@ -1,79 +0,0 @@
/mob/living/carbon/slime/proc/mutation_table(var/colour)
var/list/slime_mutation[4]
switch(colour)
//Tier 1
if("grey")
slime_mutation[1] = "orange"
slime_mutation[2] = "metal"
slime_mutation[3] = "blue"
slime_mutation[4] = "purple"
//Tier 2
if("purple")
slime_mutation[1] = "dark purple"
slime_mutation[2] = "dark blue"
slime_mutation[3] = "green"
slime_mutation[4] = "green"
if("metal")
slime_mutation[1] = "silver"
slime_mutation[2] = "yellow"
slime_mutation[3] = "gold"
slime_mutation[4] = "gold"
if("orange")
slime_mutation[1] = "dark purple"
slime_mutation[2] = "yellow"
slime_mutation[3] = "red"
slime_mutation[4] = "red"
if("blue")
slime_mutation[1] = "dark blue"
slime_mutation[2] = "silver"
slime_mutation[3] = "pink"
slime_mutation[4] = "pink"
//Tier 3
if("dark blue")
slime_mutation[1] = "purple"
slime_mutation[2] = "purple"
slime_mutation[3] = "blue"
slime_mutation[4] = "blue"
if("dark purple")
slime_mutation[1] = "purple"
slime_mutation[2] = "purple"
slime_mutation[3] = "orange"
slime_mutation[4] = "orange"
if("yellow")
slime_mutation[1] = "metal"
slime_mutation[2] = "metal"
slime_mutation[3] = "orange"
slime_mutation[4] = "orange"
if("silver")
slime_mutation[1] = "metal"
slime_mutation[2] = "metal"
slime_mutation[3] = "blue"
slime_mutation[4] = "blue"
//Tier 4
if("pink")
slime_mutation[1] = "pink"
slime_mutation[2] = "pink"
slime_mutation[3] = "light pink"
slime_mutation[4] = "light pink"
if("red")
slime_mutation[1] = "red"
slime_mutation[2] = "red"
slime_mutation[3] = "oil"
slime_mutation[4] = "oil"
if("gold")
slime_mutation[1] = "gold"
slime_mutation[2] = "gold"
slime_mutation[3] = "adamantine"
slime_mutation[4] = "adamantine"
if("green")
slime_mutation[1] = "green"
slime_mutation[2] = "green"
slime_mutation[3] = "black"
slime_mutation[4] = "black"
// Tier 5
else
slime_mutation[1] = colour
slime_mutation[2] = colour
slime_mutation[3] = colour
slime_mutation[4] = colour
return(slime_mutation)

View File

@@ -1,9 +0,0 @@
/mob/living/carbon/slime/regenerate_icons()
if (stat == DEAD)
icon_state = "[colour] baby slime dead"
else
icon_state = "[colour] [is_adult ? "adult" : "baby"] slime[Victim ? "" : " eat"]"
overlays.len = 0
if (mood)
overlays += image('icons/mob/slimes.dmi', icon_state = "aslime-[mood]")
..()

View File

@@ -10,7 +10,7 @@
return 0
if(language == GLOB.all_languages[src.species_language])
to_chat(src, "<span class='notice'>You will now speak your standard default language, [language], if you do not specify a language when speaking.</span>")
to_chat(src, "<span class='notice'>You will now speak your standard default language, [language ? language : "common"], if you do not specify a language when speaking.</span>")
else if (language)
if(language && !can_speak(language))

View File

@@ -68,56 +68,49 @@ proc/get_radio_key_from_channel(var/channel)
return key
/mob/living/proc/binarycheck()
return FALSE
if (istype(src, /mob/living/silicon/pai))
return
/mob/proc/get_default_language()
return null
if (!ishuman(src))
return
var/mob/living/carbon/human/H = src
if (H.l_ear || H.r_ear)
var/obj/item/device/radio/headset/dongle
if(istype(H.l_ear,/obj/item/device/radio/headset))
dongle = H.l_ear
else
dongle = H.r_ear
if(!istype(dongle)) return
if(dongle.translate_binary) return 1
/mob/living/proc/get_default_language()
/mob/living/get_default_language()
return default_language
//Takes a list of the form list(message, verb, whispering) and modifies it as needed
//Returns 1 if a speech problem was applied, 0 otherwise
/mob/living/proc/handle_speech_problems(var/list/message_data)
var/message = message_data[1]
var/list/message_pieces = message_data[1]
var/verb = message_data[2]
var/whispering = message_data[3]
. = 0
if((HULK in mutations) && health >= 25 && length(message))
message = "[uppertext(message)]!!!"
// Technically this rerolls the verb for as many say pieces as there are. _shrug_
for(var/datum/multilingual_say_piece/S in message_pieces)
if(S.speaking && (S.speaking.flags & NO_STUTTER || S.speaking.flags & SIGNLANG))
continue
if((HULK in mutations) && health >= 25 && length(S.message))
S.message = "[uppertext(S.message)]!!!"
verb = pick("yells","roars","hollers")
whispering = 0
. = 1
if(slurring)
message = slur(message)
S.message = slur(S.message)
verb = pick("slobbers","slurs")
. = 1
if(stuttering)
message = stutter(message)
S.message = stutter(S.message)
verb = pick("stammers","stutters")
. = 1
message_data[1] = message
message_data[1] = message_pieces
message_data[2] = verb
message_data[3] = whispering
/mob/living/proc/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name)
/mob/living/proc/handle_message_mode(message_mode, list/message_pieces, verb, used_radios)
if(message_mode == "intercom")
for(var/obj/item/device/radio/intercom/I in view(1, null))
I.talk_into(src, message, verb, speaking)
I.talk_into(src, message_pieces, verb)
used_radios += I
return 0
@@ -134,7 +127,7 @@ proc/get_radio_key_from_channel(var/channel)
return "asks"
return verb
/mob/living/say(var/message, var/datum/language/speaking = null, var/verb="says", var/alt_name="", var/whispering = 0)
/mob/living/say(var/message, var/whispering = 0)
//If you're muted for IC chat
if(client)
if(message)
@@ -172,62 +165,44 @@ proc/get_radio_key_from_channel(var/channel)
message = trim_left(message)
//Parse the language code and consume it
if(!speaking)
speaking = parse_language(message)
if(!speaking)
speaking = get_default_language()
if(!can_speak(speaking))
speaking = GLOB.all_languages[LANGUAGE_GIBBERISH]
var/babble_key = ",r"
message = babble_key + message
if(speaking == get_default_language())
var/new_message = ",[speaking.key]"
new_message += message
message = new_message
if(speaking)
message = copytext(message,2+length(speaking.key))
//HIVEMIND languages always send to all people with that language
if(speaking && (speaking.flags & HIVEMIND))
speaking.broadcast(src,trim(message))
var/list/message_pieces = parse_languages(message)
if(istype(message_pieces, /datum/multilingual_say_piece)) // Little quark for dealing with hivemind/signlang languages.
var/datum/multilingual_say_piece/S = message_pieces // Yay for BYOND's hilariously broken typecasting for allowing us to do this.
S.speaking.broadcast(src, S.message)
return 1
//Self explanatory.
if(is_muzzled() && !(speaking && (speaking.flags & SIGNLANG)))
if(!LAZYLEN(message_pieces))
log_runtime(EXCEPTION("Message failed to generate pieces. [message] - [json_encode(message_pieces)]"))
return 0
// If you're muzzled, you can only speak sign language
// However, sign language is handled above.
if(is_muzzled())
to_chat(src, "<span class='danger'>You're muzzled and cannot speak!</span>")
return
//Clean up any remaining junk on the left like spaces.
message = trim_left(message)
//Autohiss handles auto-rolling tajaran R's and unathi S's/Z's
message = handle_autohiss(message, speaking)
//Whisper vars
var/w_scramble_range = 5 //The range at which you get ***as*th**wi****
var/w_adverb //An adverb prepended to the verb in whispers
var/w_not_heard //The message for people in watching range
var/datum/multilingual_say_piece/first_piece = message_pieces[1]
var/verb = ""
//Handle language-specific verbs and adverb setup if necessary
if(!whispering) //Just doing normal 'say' (for now, may change below)
verb = say_quote(message, speaking)
else if(whispering && speaking.whisper_verb) //Language has defined whisper verb
verb = speaking.whisper_verb
verb = say_quote(message, first_piece.speaking)
else if(whispering && first_piece.speaking.whisper_verb) //Language has defined whisper verb
verb = first_piece.speaking.whisper_verb
w_not_heard = "[verb] something"
else //Whispering but language has no whisper verb, use say verb
w_adverb = pick("quietly", "softly")
verb = speaking.speech_verb
w_not_heard = "[speaking.speech_verb] something [w_adverb]"
verb = first_piece.speaking.speech_verb
w_not_heard = "[first_piece.speaking.speech_verb] something [w_adverb]"
//For speech disorders (hulk, slurring, stuttering)
if(!(speaking && (speaking.flags & NO_STUTTER || speaking.flags & SIGNLANG)))
var/list/message_data = list(message, verb, whispering)
var/list/message_data = list(message_pieces, verb, whispering)
if(handle_speech_problems(message_data))
message = message_data[1]
message_pieces = message_data[1]
whispering = message_data[3]
if(verb != message_data[2]) //They changed our verb
@@ -236,15 +211,16 @@ proc/get_radio_key_from_channel(var/channel)
verb = message_data[2]
//Whisper may have adverbs, add those if one was set
if(w_adverb) verb = "[verb] [w_adverb]"
if(w_adverb)
verb = "[verb] [w_adverb]"
//If something nulled or emptied the message, forget it
if(!message || message == "")
if(!LAZYLEN(message_pieces))
return 0
//Radio message handling
var/list/obj/item/used_radios = new
if(handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, whispering))
var/list/used_radios = list()
if(handle_message_mode(message_mode, message_pieces, verb, used_radios, whispering))
return 1
//For languages with actual speech sounds
@@ -260,14 +236,17 @@ proc/get_radio_key_from_channel(var/channel)
if(used_radios.len)
italics = 1
message_range = 1
if(speaking)
message_range = speaking.get_talkinto_msg_range(message)
if(first_piece.speaking)
message_range = first_piece.speaking.get_talkinto_msg_range(message)
var/msg
if(!speaking || !(speaking.flags & NO_TALK_MSG))
msg = "<span class='notice'>\The [src] talks into \the [used_radios[1]]</span>"
for(var/mob/living/M in hearers(5, src))
if((M != src) && msg)
if(!first_piece.speaking || !(first_piece.speaking.flags & NO_TALK_MSG))
msg = "<span class='notice'>[src] talks into [used_radios[1]]</span>"
if(msg)
for(var/mob/living/M in hearers(5, src) - src)
M.show_message(msg)
if(speech_sound)
sound_vol *= 0.5
@@ -277,15 +256,11 @@ proc/get_radio_key_from_channel(var/channel)
message_range = 1
sound_vol *= 0.5
//Handle nonverbal and sign languages here
if (speaking)
if (speaking.flags & SIGNLANG)
log_say("(SIGN) [message]", src)
return say_signlang(message, pick(speaking.signlang_verb), speaking)
if (speaking.flags & NONVERBAL)
//Handle nonverbal languages here
for(var/datum/multilingual_say_piece/S in message_pieces)
if(S.speaking.flags & NONVERBAL)
if(prob(30))
src.custom_emote(1, "[pick(speaking.signlang_verb)].")
custom_emote(1, "[pick(S.speaking.signlang_verb)].")
//These will contain the main receivers of the message
var/list/listening = list()
@@ -296,7 +271,7 @@ proc/get_radio_key_from_channel(var/channel)
if(T)
//Air is too thin to carry sound at all, contact speech only
var/datum/gas_mixture/environment = T.return_air()
var/pressure = (environment)? environment.return_pressure() : 0
var/pressure = environment ? environment.return_pressure() : 0
if(pressure < SOUND_MINIMUM_PRESSURE)
message_range = 1
@@ -347,16 +322,16 @@ proc/get_radio_key_from_channel(var/channel)
var/image/I1 = listening[M] || speech_bubble
images_to_clients[I1] |= M.client
M << I1
M.hear_say(message, verb, speaking, alt_name, italics, src, speech_sound, sound_vol)
M.hear_say(message_pieces, verb, italics, src, speech_sound, sound_vol)
if(whispering) //Don't even bother with these unless whispering
if(dst > message_range && dst <= w_scramble_range) //Inside whisper scramble range
if(M.client)
var/image/I2 = listening[M] || speech_bubble
images_to_clients[I2] |= M.client
M << I2
M.hear_say(stars(message), verb, speaking, alt_name, italics, src, speech_sound, sound_vol*0.2)
M.hear_say(stars_all(message_pieces), verb, italics, src, speech_sound, sound_vol*0.2)
if(dst > w_scramble_range && dst <= world.view) //Inside whisper 'visible' range
M.show_message("<span class='game say'><span class='name'>[src.name]</span> [w_not_heard].</span>", 2)
M.show_message("<span class='game say'><span class='name'>[name]</span> [w_not_heard].</span>", 2)
//Object message delivery
for(var/obj/O in listening_obj)
@@ -364,7 +339,7 @@ proc/get_radio_key_from_channel(var/channel)
if(O && src) //If we still exist, when the spawn processes
var/dst = get_dist(get_turf(O),get_turf(src))
if(dst <= message_range)
O.hear_talk(src, message, verb, speaking)
O.hear_talk(src, message_pieces, verb)
//Remove all those images. At least it's just ONE spawn this time.
spawn(30)
@@ -407,8 +382,41 @@ proc/get_radio_key_from_channel(var/channel)
/obj/effect/speech_bubble
var/mob/parent
/mob/living/proc/GetVoice()
/mob/proc/GetVoice()
return name
/mob/living/emote(var/act, var/type, var/message) //emote code is terrible, this is so that anything that isn't
if(stat) //already snowflaked to shit can call the parent and handle emoting sanely
return FALSE
if(..(act, type, message))
return TRUE
if(act && type && message)
log_emote(message, src)
for(var/mob/M in dead_mob_list)
if(!M.client)
continue
if(isnewplayer(M))
continue
if(isobserver(M) && M.is_preference_enabled(/datum/client_preference/ghost_sight))
M.show_message(message)
switch(type)
if(1) // Visible
visible_message(message)
return TRUE
if(2) // Audible
audible_message(message)
return TRUE
else
if(act == "help")
return // Mobs handle this individually
to_chat(src, "<span class='warning'>Unusable emote '[act]'. Say *help for a list.</span>")
/mob/proc/speech_bubble_appearance()
return "normal"

View File

@@ -824,6 +824,69 @@ var/list/ai_verbs_default = list(
// If that is ever fixed please update this proc.
return TRUE
/mob/living/silicon/ai/handle_track(message, verb = "says", mob/speaker = null, speaker_name, hard_to_hear)
if(hard_to_hear)
return
var/jobname // the mob's "job"
var/mob/living/carbon/human/impersonating //The crew member being impersonated, if any.
var/changed_voice
if(ishuman(speaker))
var/mob/living/carbon/human/H = speaker
if(H.wear_mask && istype(H.wear_mask,/obj/item/clothing/mask/gas/voice))
changed_voice = 1
var/list/impersonated = new()
var/mob/living/carbon/human/I = impersonated[speaker_name]
if(!I)
for(var/mob/living/carbon/human/M in mob_list)
if(M.real_name == speaker_name)
I = M
impersonated[speaker_name] = I
break
// If I's display name is currently different from the voice name and using an agent ID then don't impersonate
// as this would allow the AI to track I and realize the mismatch.
if(I && !(I.name != speaker_name && I.wear_id && istype(I.wear_id,/obj/item/weapon/card/id/syndicate)))
impersonating = I
jobname = impersonating.get_assignment()
else
jobname = "Unknown"
else
jobname = H.get_assignment()
else if(iscarbon(speaker)) // Nonhuman carbon mob
jobname = "No id"
else if(isAI(speaker))
jobname = "AI"
else if(isrobot(speaker))
jobname = "Cyborg"
else if(istype(speaker, /mob/living/silicon/pai))
jobname = "Personal AI"
else
jobname = "Unknown"
var/track = ""
if(changed_voice)
if(impersonating)
track = "<a href='byond://?src=\ref[src];trackname=[html_encode(speaker_name)];track=\ref[impersonating]'>[speaker_name] ([jobname])</a>"
else
track = "[speaker_name] ([jobname])"
else
track = "<a href='byond://?src=\ref[src];trackname=[html_encode(speaker_name)];track=\ref[speaker]'>[speaker_name] ([jobname])</a>"
return track
/mob/living/silicon/ai/proc/relay_speech(mob/living/M, list/message_pieces, verb)
var/message = combine_message(message_pieces, verb, M)
var/name_used = M.GetVoice()
//This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only.
var/rendered = "<i><span class='game say'>Relayed Speech: <span class='name'>[name_used]</span> [message]</span></i>"
show_message(rendered, 2)
//Special subtype kept around for global announcements
/mob/living/silicon/ai/announcer/
is_dummy = 1

View File

@@ -0,0 +1,113 @@
/mob/living/silicon/emote(var/act, var/m_type = 1,var/message = null)
var/param = null
if(findtext(act, "-", 1, null))
var/t1 = findtext(act, "-", 1, null)
param = copytext(act, t1 + 1, length(act) + 1)
act = copytext(act, 1, t1)
if(findtext(act, "s", -1) && !findtext(act, "_", -2))//Removes ending s's unless they are prefixed with a '_'
act = copytext(act, 1, length(act))
switch(act)
if("beep")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "<b>[src]</b> beeps at [param]."
else
message = "<b>[src]</b> beeps."
playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0)
m_type = 1
if("ping")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "<b>[src]</b> pings at [param]."
else
message = "<b>[src]</b> pings."
playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
m_type = 1
if("buzz")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "<b>[src]</b> buzzes at [param]."
else
message = "<b>[src]</b> buzzes."
playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
m_type = 1
if("yes", "ye")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "<b>[src]</b> emits an affirmative blip at [param]."
else
message = "<b>[src]</b> emits an affirmative blip."
playsound(src.loc, 'sound/machines/synth_yes.ogg', 50, 0)
m_type = 1
if("dwoop")
var/M = null
if(param)
for (var/mob/A in view(null, null))
M = A
break
if(!M)
param = null
if (param)
message = "<b>[src]</b> chirps happily at [param]"
else
message = "<b>[src]</b> chirps happily."
playsound(src.loc, 'sound/machines/dwoop.ogg', 50, 0)
m_type = 1
if("no")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "<b>[src]</b> emits a negative blip at [param]."
else
message = "<b>[src]</b> emits a negative blip."
playsound(src.loc, 'sound/machines/synth_no.ogg', 50, 0)
m_type = 1
..(act, m_type, message)

View File

@@ -1,5 +1,5 @@
/mob/living/silicon/pai/say(var/msg)
/mob/living/silicon/pai/say(var/message, var/datum/language/speaking = null, var/whispering = 0)
if(silence_time)
to_chat(src, "<font color=green>Communication circuits remain uninitialized.</font>")
else
..(msg)
..()

View File

@@ -1,4 +1,4 @@
/mob/living/silicon/robot/drone/say(var/message)
/mob/living/silicon/robot/drone/say(var/message, var/datum/language/speaking = null, var/whispering = 0)
if(local_transmit)
if (src.client)
if(client.prefs.muted & MUTE_IC)
@@ -36,4 +36,4 @@
if(M.client)
to_chat(M, "<b>[src]</b> transmits, \"[message]\"")
return 1
return ..(message, 0)
return ..()

View File

@@ -9,21 +9,6 @@
act = copytext(act,1,length(act))
switch(act)
if ("me")
if (src.client)
if(client.prefs.muted & MUTE_IC)
to_chat(src, "You cannot send IC messages (muted).")
return
if (stat)
return
if(!(message))
return
else
return custom_emote(m_type, message)
if ("custom")
return custom_emote(m_type, message)
if("salute")
if(!src.buckled)
var/M = null
@@ -36,9 +21,9 @@
param = null
if(param)
message = "salutes to [param]."
message = "<b>[src]</b> salutes to [param]."
else
message = "salutes."
message = "<b>[src]</b> salutes."
m_type = 1
if("bow")
if(!src.buckled)
@@ -52,39 +37,39 @@
param = null
if(param)
message = "bows to [param]."
message = "<b>[src]</b> bows to [param]."
else
message = "bows."
message = "<b>[src]</b> bows."
m_type = 1
if("clap")
if(!src.restrained())
message = "claps."
message = "<b>[src]</b> claps."
m_type = 2
if("flap")
if(!src.restrained())
message = "flaps its wings."
message = "<b>[src]</b> flaps its wings."
m_type = 2
if("aflap")
if(!src.restrained())
message = "flaps its wings ANGRILY!"
message = "<b>[src]</b> flaps its wings ANGRILY!"
m_type = 2
if("twitch")
message = "twitches."
message = "<b>[src]</b> twitches."
m_type = 1
if("twitch_v")
message = "twitches violently."
message = "<b>[src]</b> twitches violently."
m_type = 1
if("nod")
message = "nods."
message = "<b>[src]</b> nods."
m_type = 1
if("deathgasp")
message = "shudders violently for a moment, then becomes motionless, its eyes slowly darkening."
message = "<b>[src]</b> shudders violently for a moment, then becomes motionless, its eyes slowly darkening."
m_type = 1
if("glare")
@@ -98,9 +83,9 @@
param = null
if(param)
message = "glares at [param]."
message = "<b>[src]</b> glares at [param]."
else
message = "glares."
message = "<b>[src]</b> glares."
if("stare")
var/M = null
@@ -113,9 +98,9 @@
param = null
if(param)
message = "stares at [param]."
message = "<b>[src]</b> stares at [param]."
else
message = "stares."
message = "<b>[src]</b> stares."
if("look")
var/M = null
@@ -129,116 +114,14 @@
param = null
if(param)
message = "looks at [param]."
message = "<b>[src]</b> looks at [param]."
else
message = "looks."
m_type = 1
if("beep")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "beeps at [param]."
else
message = "beeps."
playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0)
m_type = 1
if("ping")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "pings at [param]."
else
message = "pings."
playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
m_type = 1
if("buzz")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "buzzes at [param]."
else
message = "buzzes."
playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
m_type = 1
if("yes", "ye")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "emits an affirmative blip at [param]."
else
message = "emits an affirmative blip."
playsound(src.loc, 'sound/machines/synth_yes.ogg', 50, 0)
m_type = 1
if("dwoop")
var/M = null
if(param)
for (var/mob/A in view(null, null))
M = A
break
if(!M)
param = null
if (param)
message = "chirps happily at [param]"
else
message = "chirps happily."
playsound(src.loc, 'sound/machines/dwoop.ogg', 50, 0)
m_type = 1
if("no")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "emits a negative blip at [param]."
else
message = "emits a negative blip."
playsound(src.loc, 'sound/machines/synth_no.ogg', 50, 0)
message = "<b>[src]</b> looks."
m_type = 1
if("law")
if(istype(module,/obj/item/weapon/robot_module/robot/security))
message = "shows its legal authorization barcode."
message = "<b>[src]</b> shows its legal authorization barcode."
playsound(src.loc, 'sound/voice/biamthelaw.ogg', 50, 0)
m_type = 2
@@ -247,7 +130,7 @@
if("halt")
if(istype(module,/obj/item/weapon/robot_module/robot/security))
message = "<B>'s</B> speakers skreech, \"Halt! Security!\"."
message = "<b>[src]</b> <B>'s</B> speakers skreech, \"Halt! Security!\"."
playsound(src.loc, 'sound/voice/halt.ogg', 50, 0)
m_type = 2
@@ -256,10 +139,5 @@
if("help")
to_chat(src, "salute, bow-(none)/mob, clap, flap, aflap, twitch, twitch_s, nod, deathgasp, glare-(none)/mob, stare-(none)/mob, look, beep, ping, \nbuzz, law, halt, yes, dwoop, no")
else
to_chat(src, "<font color='blue'>Unusable emote '[act]'. Say *help for a list.</font>")
if ((message && src.stat == 0))
custom_emote(m_type,message)
return
..(act, m_type, message)

View File

@@ -1,10 +1,4 @@
/mob/living/silicon/say(var/message, var/sanitize = 1, var/whispering = 0)
return ..((sanitize ? sanitize(message) : message), whispering = whispering)
/mob/living/silicon/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name)
log_say(message, src)
/mob/living/silicon/robot/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name)
/mob/living/silicon/robot/handle_message_mode(message_mode, message, verb, speaking, used_radios)
..()
if(message_mode)
if(!is_component_functioning("radio"))
@@ -17,7 +11,7 @@
/mob/living/silicon/speech_bubble_appearance()
return "synthetic"
/mob/living/silicon/ai/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name)
/mob/living/silicon/ai/handle_message_mode(message_mode, message, verb, speaking, used_radios)
..()
if(message_mode == "department")
return holopad_talk(message, verb, speaking)
@@ -29,7 +23,7 @@
message_mode = null
return aiRadio.talk_into(src,message,message_mode,verb,speaking)
/mob/living/silicon/pai/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name)
/mob/living/silicon/pai/handle_message_mode(message_mode, message, verb, speaking, used_radios)
..()
if(message_mode)
if(message_mode == "general")
@@ -53,65 +47,38 @@
/mob/living/silicon/say_understands(var/other, var/datum/language/speaking = null)
//These only pertain to common. Languages are handled by mob/say_understands()
if(!speaking)
if (istype(other, /mob/living/carbon))
return 1
if (istype(other, /mob/living/silicon))
return 1
if (istype(other, /mob/living/carbon/brain))
return 1
if(iscarbon(other))
return TRUE
if(issilicon(other))
return TRUE
if(isbrain(other))
return TRUE
return ..()
//For holopads only. Usable by AI.
/mob/living/silicon/ai/proc/holopad_talk(var/message, verb, datum/language/speaking)
log_say("(HPAD) [message]",src)
message = trim(message)
if (!message)
return
/mob/living/silicon/ai/proc/holopad_talk(list/message_pieces, verb)
log_say("(HPAD) [multilingual_to_message(message_pieces)]",src)
var/obj/machinery/hologram/holopad/T = src.holo
if(T && T.masters[src])//If there is a hologram and its master is the user.
//Human-like, sorta, heard by those who understand humans.
var/rendered_a
//Speech distorted, heard by those who do not understand AIs.
var/message_stars = stars(message)
var/rendered_b
if(speaking)
rendered_a = "<span class='game say'><span class='name'>[name]</span> [speaking.format_message(message, verb)]</span>"
rendered_b = "<span class='game say'><span class='name'>[voice_name]</span> [speaking.format_message(message_stars, verb)]</span>"
to_chat(src, "<i><span class='game say'>Holopad transmitted, <span class='name'>[real_name]</span> [speaking.format_message(message, verb)]</span></i>") //The AI can "hear" its own message.
else
rendered_a = "<span class='game say'><span class='name'>[name]</span> [verb], <span class='message'>\"[message]\"</span></span>"
rendered_b = "<span class='game say'><span class='name'>[voice_name]</span> [verb], <span class='message'>\"[message_stars]\"</span></span>"
to_chat(src, "<i><span class='game say'>Holopad transmitted, <span class='name'>[real_name]</span> [verb], <span class='message'><span class='body'>\"[message]\"</span></span></span></i>") //The AI can "hear" its own message.
var/list/listeners = get_mobs_and_objs_in_view_fast(get_turf(T), world.view)
var/list/listening = listeners["mobs"]
var/list/listening_obj = listeners["objs"]
for(var/mob/M in listening)
spawn(0)
if(M.say_understands(src))//If they understand AI speak. Humans and the like will be able to.
M.show_message(rendered_a, 2)
else//If they do not.
M.show_message(rendered_b, 2)
M.hear_holopad_talk(message_pieces, verb, src)
for(var/obj/O in listening_obj)
if(O == T) //Don't recieve your own speech
continue
spawn(0)
if(O && src) //If we still exist, when the spawn processes
O.hear_talk(src, message, verb, speaking)
O.hear_talk(src, message_pieces, verb)
/*Radios "filter out" this conversation channel so we don't need to account for them.
This is another way of saying that we won't bother dealing with them.*/
to_chat(src, "<i><span class='game say'>Holopad transmitted, <span class='name'>[real_name]</span> [combine_message(message_pieces, verb, src)]</span></i>")
else
to_chat(src, "No holopad connected.")
return 0
return 1
/mob/living/silicon/ai/proc/holopad_emote(var/message) //This is called when the AI uses the 'me' verb while using a holopad.
message = trim(message)
if(!message)
@@ -147,11 +114,11 @@
return 1
/mob/living/silicon/ai/emote(var/act, var/type, var/message)
var/obj/machinery/hologram/holopad/T = src.holo
var/obj/machinery/hologram/holopad/T = holo
if(T && T.masters[src]) //Is the AI using a holopad?
src.holopad_emote(message)
. = holopad_emote(message)
else //Emote normally, then.
..()
. = ..()
#undef IS_AI
#undef IS_ROBOT

View File

@@ -193,7 +193,14 @@
//Silicon mob language procs
/mob/living/silicon/can_speak(datum/language/speaking)
return universal_speak || (speaking in src.speech_synthesizer_langs) || (speaking.name == "Noise") //need speech synthesizer support to vocalize a language
if(universal_speak)
return TRUE
//need speech synthesizer support to vocalize a language
if(speaking in speech_synthesizer_langs)
return TRUE
if(speaking && speaking.flags & INNATE)
return TRUE
return FALSE
/mob/living/silicon/add_language(var/language, var/can_speak=1)
var/var/datum/language/added_language = GLOB.all_languages[language]
@@ -213,15 +220,11 @@
..(rem_language)
speech_synthesizer_langs -= removed_language
/mob/living/silicon/check_languages()
set name = "Check Known Languages"
set category = "IC"
set src = usr
var/dat = "<b><font size = 5>Known Languages</font></b><br/><br/>"
/mob/living/silicon/check_lang_data()
. = ""
if(default_language)
dat += "Current default language: [default_language] - <a href='byond://?src=\ref[src];default_lang=reset'>reset</a><br/><br/>"
. += "Current default language: [default_language] - <a href='byond://?src=\ref[src];default_lang=reset'>reset</a><br><br>"
for(var/datum/language/L in languages)
if(!(L.flags & NONGLOBAL))
@@ -232,10 +235,7 @@
default_str = " - <a href='byond://?src=\ref[src];default_lang=\ref[L]'>set default</a>"
var/synth = (L in speech_synthesizer_langs)
dat += "<b>[L.name] ([get_language_prefix()][L.key])</b>[synth ? default_str : null]<br/>Speech Synthesizer: <i>[synth ? "YES" : "NOT SUPPORTED"]</i><br/>[L.desc]<br/><br/>"
src << browse(dat, "window=checklanguage")
return
. += "<b>[L.name] ([get_language_prefix()][L.key])</b>[synth ? default_str : null]<br>Speech Synthesizer: <i>[synth ? "YES" : "NOT SUPPORTED"]</i><br>[L.desc]<br><br>"
/mob/living/silicon/proc/toggle_sensor_mode()
var/sensor_type = input("Please select sensor type.", "Sensor Integration", null) in list("Security","Medical","Disable")

View File

@@ -194,12 +194,6 @@
. = ..()
to_chat(src,"<b>You are \the [src].</b> [player_msg]")
/mob/living/simple_mob/emote(var/act, var/type, var/desc)
if(act)
..(act, type, desc)
/mob/living/simple_mob/SelfMove(turf/n, direct)
var/turf/old_turf = get_turf(src)
var/old_dir = dir
@@ -264,14 +258,11 @@
update_icon()
/mob/living/simple_mob/say(var/message,var/datum/language/language)
var/verb = "says"
/mob/living/simple_mob/say_quote(var/message, var/datum/language/speaking = null)
if(speak_emote.len)
verb = pick(speak_emote)
message = sanitize(message)
..(message, null, verb)
. = pick(speak_emote)
else if(speaking)
. = ..()
/mob/living/simple_mob/get_speech_ending(verb, var/ending)
return verb

View File

@@ -205,7 +205,7 @@
return
// This is awful but its literally say code.
/mob/living/simple_mob/animal/borer/say(message)
/mob/living/simple_mob/animal/borer/say(var/message, var/datum/language/speaking = null, var/whispering = 0)
message = sanitize(message)
message = capitalize(message)
@@ -224,9 +224,10 @@
if(copytext(message, 1, 2) == "*")
return emote(copytext(message, 2))
var/datum/language/L = parse_language(message)
if(L && L.flags & HIVEMIND)
L.broadcast(src,trim(copytext(message,3)), src.true_name)
var/list/message_pieces = parse_languages(message)
for(var/datum/multilingual_say_piece/S in message_pieces)
if(S.speaking && S.speaking.flags & HIVEMIND)
S.speaking.broadcast(src, trim(copytext(message, 3)), src.true_name)
return
if(!host)

View File

@@ -5,7 +5,7 @@
real_name = "host brain"
universal_understand = 1
/mob/living/captive_brain/say(var/message)
/mob/living/captive_brain/say(var/message, var/datum/language/speaking = null, var/whispering = 0)
if (src.client)
if(client.prefs.muted & MUTE_IC)
@@ -31,7 +31,12 @@
else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears))
to_chat(M, "The captive mind of [src] whispers, \"[message]\"")
/mob/living/captive_brain/me_verb(message as text)
to_chat(src, "<span class='danger'>You cannot emote as a captive mind.</span>")
return
/mob/living/captive_brain/emote(var/message)
to_chat(src, "<span class='danger'>You cannot emote as a captive mind.</span>")
return
/mob/living/captive_brain/process_resist()

View File

@@ -41,7 +41,7 @@
return say(message)
// Ugly saycode so parrots can use their headsets.
/mob/living/simple_mob/animal/passive/bird/parrot/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name)
/mob/living/simple_mob/animal/passive/bird/parrot/handle_message_mode(message_mode, message, verb, speaking, used_radios)
..()
if(message_mode)
if(my_headset && istype(my_headset, /obj/item/device/radio))

View File

@@ -104,7 +104,7 @@
// Proc: say()
// Parameters: 4 (generic say() arguments)
// Description: Adds a speech bubble to the communicator device, then calls ..() to do the real work.
/mob/living/voice/say(var/message, var/datum/language/speaking = null, var/verb="says", var/alt_name="", var/whispering=0)
/mob/living/voice/say(var/message, var/datum/language/speaking = null, var/whispering = 0)
//Speech bubbles.
if(comm)
var/speech_bubble_test = say_test(message)
@@ -117,7 +117,7 @@
M << speech_bubble
src << speech_bubble
..(message, speaking, verb, alt_name, whispering) //mob/living/say() can do the actual talking.
..() //mob/living/say() can do the actual talking.
// Proc: speech_bubble_appearance()
// Parameters: 0
@@ -128,12 +128,12 @@
/mob/living/voice/say_understands(var/other, var/datum/language/speaking = null)
//These only pertain to common. Languages are handled by mob/say_understands()
if(!speaking)
if (istype(other, /mob/living/carbon))
return 1
if (istype(other, /mob/living/silicon))
return 1
if (istype(other, /mob/living/carbon/brain))
return 1
if(iscarbon(other))
return TRUE
if(issilicon(other))
return TRUE
if(isbrain(other))
return TRUE
return ..()
/mob/living/voice/custom_emote(var/m_type=1,var/message = null,var/range=world.view)

View File

@@ -1217,3 +1217,6 @@ mob/proc/yank_out_object()
selfimage.loc = src
return selfimage
/mob/proc/GetAltName()
return ""

View File

@@ -184,6 +184,12 @@ proc/getsensorlevel(A)
p++
return t
/proc/stars_all(list/message_pieces, pr)
// eugh, we have to clone the list to avoid collateral damage due to the nature of these messages
. = list()
for(var/datum/multilingual_say_piece/S in message_pieces)
. += new /datum/multilingual_say_piece(S.speaking, stars(S.message))
proc/slur(phrase)
phrase = html_decode(phrase)
var/leng=length(phrase)

View File

@@ -581,7 +581,7 @@
return ready && ..()
// Prevents lobby players from seeing say, even with ghostears
/mob/new_player/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null)
/mob/new_player/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/italics = 0, var/mob/speaker = null)
return
// Prevents lobby players from seeing emotes, even with ghosteyes

View File

@@ -1,4 +1,4 @@
/mob/proc/say()
/mob/proc/say(var/message, var/datum/language/speaking = null, var/whispering = 0)
return
/mob/verb/whisper(message as text)
@@ -26,7 +26,7 @@
set_typing_indicator(FALSE)
if(use_me)
usr.emote("me",usr.emote_type,message)
custom_emote(usr.emote_type, message)
else
usr.emote(message)
@@ -38,7 +38,7 @@
if(!client)
return // Clientless mobs shouldn't be trying to talk in deadchat.
if(!src.client.holder)
if(!client.holder)
if(!config.dsay_allowed)
to_chat(src, "<span class='danger'>Deadchat is globally muted.</span>")
return
@@ -52,53 +52,53 @@
say_dead_direct("[pick("complains","moans","whines","laments","blubbers")], <span class='message'>\"[message]\"</span>", src)
/mob/proc/say_understands(var/mob/other, var/datum/language/speaking = null)
if (src.stat == DEAD)
return 1
if(stat == DEAD)
return TRUE
//Universal speak makes everything understandable, for obvious reasons.
else if(src.universal_speak || src.universal_understand)
return 1
else if(universal_speak || universal_understand)
return TRUE
//Languages are handled after.
if(!speaking)
if(!other)
return 1
return TRUE
if(other.universal_speak)
return 1
return TRUE
if(isAI(src) && ispAI(other))
return 1
if (istype(other, src.type) || istype(src, other.type))
return 1
return 0
return TRUE
if(istype(other, type) || istype(src, other.type))
return TRUE
return FALSE
if(speaking.flags & INNATE)
return 1
return TRUE
//non-verbal languages are garbled if you can't see the speaker. Yes, this includes if they are inside a closet.
if(speaking.flags & NONVERBAL)
if(sdisabilities & BLIND || blinded)
return FALSE
if(!other || !(other in view(src)))
return FALSE
//Language check.
for(var/datum/language/L in src.languages)
for(var/datum/language/L in languages)
if(speaking.name == L.name)
return 1
return TRUE
return 0
/*
***Deprecated***
let this be handled at the hear_say or hear_radio proc
This is left in for robot speaking when humans gain binary channel access until I get around to rewriting
robot_talk() proc.
There is no language handling build into it however there is at the /mob level so we accept the call
for it but just ignore it.
*/
return FALSE
/mob/proc/say_quote(var/message, var/datum/language/speaking = null)
var/verb = "says"
var/ending = copytext(message, length(message))
if(speaking)
verb = speaking.get_spoken_verb(ending)
else
if(ending == "!")
verb = pick("exclaims", "shouts", "yells")
else if(ending == "?")
verb = "asks"
return verb
@@ -135,19 +135,104 @@
return null
//parses the language code (e.g. :j) from text, such as that supplied to say.
//returns the language object only if the code corresponds to a language that src can speak, otherwise null.
/mob/proc/parse_language(var/message)
var/prefix = copytext(message,1,2)
// This is for audible emotes
if(length(message) >= 1 && prefix == "!")
return GLOB.all_languages["Noise"]
/datum/multilingual_say_piece
var/datum/language/speaking = null
var/message = ""
if(length(message) >= 2 && is_language_prefix(prefix))
var/language_prefix = copytext(message, 2 ,3)
var/datum/language/L = GLOB.language_keys[language_prefix]
if (can_speak(L))
return L
/datum/multilingual_say_piece/New(datum/language/new_speaking, new_message)
. = ..()
speaking = new_speaking
if(new_message)
message = new_message
/mob/proc/find_valid_prefixes(message)
var/list/prefixes = list() // [["Common", start, end], ["Gutter", start, end]]
for(var/i in 1 to length(message))
// This grabs trimmed 3 character substrings, to allow for up to 1 prefix and 1 letter language keys
var/selection = trim_right(lowertext(copytext(message, i, i + 2)))
// The first character in the selection will always be the prefix (if this is a valid language invocation)
var/prefix = copytext(selection, 1, 2)
var/language_key = copytext(selection, 2, 3)
if(is_language_prefix(prefix))
// Okay, we're definitely now trying to invoke a language (probably)
// This "[]" is probably unnecessary but BYOND will runtime if a number is used
var/datum/language/L = GLOB.language_keys["[language_key]"]
// It's kinda silly that we have to check L != null and this isn't done for us by can_speak (it runtimes instead), but w/e
if(L && can_speak(L))
// So we have a valid language invocation, and we can speak that language, let's make a piece for it
// This language will be the language until the next prefixes[] index, or the end of the message if there are none.
prefixes[++prefixes.len] = list(L, i, i + length(selection))
else if(L)
// We found a valid language, but they can't speak it. Let's make them speak gibberish instead.
prefixes[++prefixes.len] = list(GLOB.all_languages[LANGUAGE_GIBBERISH], i, i + length(selection))
continue
if(i == 1)
// This covers the case of "no prefixes in use."
prefixes[++prefixes.len] = list(get_default_language(), i, i)
return prefixes
/mob/proc/strip_prefixes(message, mob/prefixer = null)
. = ""
var/last_index = 1
for(var/i in 1 to length(message))
var/selection = trim_right(lowertext(copytext(message, i, i + 2)))
// The first character in the selection will always be the prefix (if this is a valid language invocation)
var/prefix = copytext(selection, 1, 2)
var/language_key = copytext(selection, 2, 3)
if(is_language_prefix(prefix))
var/datum/language/L = GLOB.language_keys["[language_key]"]
if(L)
. += copytext(message, last_index, i)
last_index = i + 2
if(i + 1 > length(message))
. += copytext(message, last_index)
// this returns a structured message with language sections
// list(/datum/multilingual_say_piece(common, "hi"), /datum/multilingual_say_piece(farwa, "squik"), /datum/multilingual_say_piece(common, "meow!"))
/mob/proc/parse_languages(message)
. = list()
// Noise language is a snowflake.
if(copytext(message, 1, 2) == "!" && length(message) > 1)
// Note that list() here is intended
// Returning a raw /datum/multilingual_say_piece is supported, but only for hivemind languages
// What we actually want is a normal say piece that's all noise lang
return list(new /datum/multilingual_say_piece(GLOB.all_languages["Noise"], trim(strip_prefixes(copytext(message, 2)))))
// Scan the message for prefixes
var/list/prefix_locations = find_valid_prefixes(message)
if(!LAZYLEN(prefix_locations)) // There are no prefixes... or at least, no _valid_ prefixes.
. += new /datum/multilingual_say_piece(get_default_language(), trim(strip_prefixes(message))) // So we'll just strip those pesky things and still make the message.
for(var/i in 1 to length(prefix_locations))
var/current = prefix_locations[i] // ["Common", start, end]
// There are a few things that will make us want to ignore all other languages in - namely, HIVEMIND languages.
var/datum/language/L = current[1]
if(L && (L.flags & HIVEMIND || L.flags & SIGNLANG))
return new /datum/multilingual_say_piece(L, trim(strip_prefixes(message)))
if(i + 1 > length(prefix_locations)) // We are out of lookaheads, that means the rest of the message is in cur lang
var/spoke_message = handle_autohiss(trim(copytext(message, current[3])), L)
. += new /datum/multilingual_say_piece(current[1], spoke_message)
else
return GLOB.all_languages[LANGUAGE_GIBBERISH]
return null
var/next = prefix_locations[i + 1] // We look ahead at the next message to see where we need to stop.
var/spoke_message = handle_autohiss(trim(copytext(message, current[3], next[2])), L)
. += new /datum/multilingual_say_piece(current[1], spoke_message)
/* These are here purely because it would be hell to try to convert everything over to using the multi-lingual system at once */
/proc/message_to_multilingual(message, datum/language/speaking = null)
. = list(new /datum/multilingual_say_piece(speaking, message))
/proc/multilingual_to_message(list/message_pieces, var/requires_machine_understands = FALSE, var/with_capitalization = FALSE)
. = ""
for(var/datum/multilingual_say_piece/S in message_pieces)
var/message_to_append = S.message
if(S.speaking)
if(with_capitalization)
message_to_append = S.speaking.format_message_plain(S.message)
if(requires_machine_understands && !S.speaking.machine_understands)
message_to_append = S.speaking.scramble(S.message)
. += message_to_append + " "
. = trim_right(.)

View File

@@ -41,12 +41,12 @@
return owner.examine(user, distance, infix, suffix)
// Relay some stuff they hear
/mob/zshadow/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
/mob/zshadow/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
if(speaker && speaker.z != src.z)
return // Only relay speech on our acutal z, otherwise we might relay sounds that were themselves relayed up!
if(isliving(owner))
verb += " from above"
return owner.hear_say(message, verb, language, alt_name, italics, speaker, speech_sound, sound_vol)
return owner.hear_say(message, verb, language, italics, speaker, speech_sound, sound_vol)
/mob/zshadow/proc/sync_icon(var/mob/M)
name = M.name

View File

@@ -29,11 +29,11 @@
var/mob/living/M = src.loc
M.say(pick(heard_talk))
/obj/item/clothing/mask/gas/poltergeist/hear_talk(mob/M as mob, text)
/obj/item/clothing/mask/gas/poltergeist/hear_talk(mob/M, list/message_pieces, verb)
..()
if(heard_talk.len > max_stored_messages)
heard_talk.Remove(pick(heard_talk))
heard_talk.Add(text)
heard_talk.Add(multilingual_to_message(message_pieces))
if(istype(src.loc, /mob/living) && world.time - last_twitch > 50)
last_twitch = world.time
@@ -116,7 +116,7 @@
else if(get_dist(W, src) > 10)
shadow_wights.Remove(wight_check_index)
/obj/item/weapon/vampiric/hear_talk(mob/M as mob, text)
/obj/item/weapon/vampiric/hear_talk(mob/M, list/message_pieces, verb)
..()
if(world.time - last_bloodcall >= bloodcall_interval && M in view(7, src))
bloodcall(M)

View File

@@ -112,7 +112,7 @@ Divergence proc, used in mutation to make unique datums.
/mob/living/simple_animal/xeno/proc/RandomizeTraits()
return
/mob/living/simple_animal/xeno/hear_say(var/message, var/verb = "says", var/datum/language/language, var/alt_name = "",var/italics = 0, var/mob/speaker = null)
/mob/living/simple_animal/xeno/hear_say(var/message, var/verb = "says", var/datum/language/language, var/italics = 0, var/mob/speaker = null)
if(traitdat.traits[TRAIT_XENO_CANLEARN])
/*
Until this gets sorted out to a functioning point, or waiting on Psi's saycode update.
@@ -128,7 +128,7 @@ Divergence proc, used in mutation to make unique datums.
*/
if(!(message in speak))
speech_buffer.Add(message)
..(message,verb,language,alt_name,italics,speaker)
..(message,verb,language,italics,speaker)
/mob/living/simple_animal/xeno/proc/ProcessSpeechBuffer()
if(speech_buffer.len)

View File

@@ -2064,7 +2064,6 @@
#include "code\modules\mob\living\carbon\alien\emote.dm"
#include "code\modules\mob\living\carbon\alien\life.dm"
#include "code\modules\mob\living\carbon\alien\progression.dm"
#include "code\modules\mob\living\carbon\alien\say.dm"
#include "code\modules\mob\living\carbon\alien\update_icons.dm"
#include "code\modules\mob\living\carbon\alien\diona\diona.dm"
#include "code\modules\mob\living\carbon\alien\diona\diona_attacks.dm"
@@ -2136,6 +2135,7 @@
#include "code\modules\mob\living\carbon\human\species\xenomorphs\alien_species.dm"
#include "code\modules\mob\living\carbon\human\species\xenomorphs\xenomorphs.dm"
#include "code\modules\mob\living\silicon\death.dm"
#include "code\modules\mob\living\silicon\emote.dm"
#include "code\modules\mob\living\silicon\laws.dm"
#include "code\modules\mob\living\silicon\login.dm"
#include "code\modules\mob\living\silicon\say.dm"