diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index 506080fdc9..c87338d26c 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -1,10 +1,10 @@ -/mob/living/carbon/human/say(var/message) +/mob/living/carbon/human/say(var/message,var/whispering=0) var/alt_name = "" if(name != GetVoice()) alt_name = "(as [get_id_name("Unknown")])" message = sanitize(message) - ..(message, alt_name = alt_name) + ..(message, alt_name = alt_name, whispering = whispering) /mob/living/carbon/human/proc/forcesay(list/append) if(stat == CONSCIOUS) @@ -181,9 +181,6 @@ if(has_radio) R.talk_into(src,message,null,verb,speaking) used_radios += R - if("whisper") - whisper_say(message, speaking, alt_name) - return 1 else if(message_mode) if(l_ear && istype(l_ear,/obj/item/device/radio)) diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm deleted file mode 100644 index 534f660a44..0000000000 --- a/code/modules/mob/living/carbon/human/whisper.dm +++ /dev/null @@ -1,132 +0,0 @@ -//Lallander was here -/mob/living/carbon/human/whisper(message as text) - var/alt_name = "" - - if(say_disabled) //This is here to try to identify lag problems - usr << "\red Speech is currently admin-disabled." - return - - message = sanitize(message) - log_whisper("[src.name]/[src.key] : [message]") - - if (src.client) - if (src.client.prefs.muted & MUTE_IC) - src << "\red You cannot whisper (muted)." - return - - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return - - if (src.stat == 2) - return src.say_dead(message) - - if (src.stat) - return - - if(name != GetVoice()) - alt_name = "(as [get_id_name("Unknown")])" - - //parse the language code and consume it - var/datum/language/speaking = parse_language(message) - if (speaking) - message = copytext(message,2+length(speaking.key)) - - whisper_say(message, speaking, alt_name) - - -//This is used by both the whisper verb and human/say() to handle whispering -/mob/living/carbon/human/proc/whisper_say(var/message, var/datum/language/speaking = null, var/alt_name="", var/verb="whispers") - - if (is_muzzled()) - src << "You're muzzled and cannot speak!" - return - - var/message_range = 1 - var/eavesdropping_range = 2 - var/watching_range = 5 - var/italics = 1 - - var/not_heard //the message displayed to people who could not hear the whispering - var/adverb - if (speaking) - if (speaking.whisper_verb) - verb = speaking.whisper_verb - not_heard = "[verb] something" - else - adverb = pick("quietly", "softly") - verb = speaking.speech_verb - not_heard = "[speaking.speech_verb] something [adverb]" - else - not_heard = "[verb] something" //TODO get rid of the null language and just prevent speech if language is null - - message = capitalize(trim(message)) - - //speech problems - if(!(speaking && (speaking.flags & NO_STUTTER))) - var/list/message_data = list(message, verb, 1) - if(handle_speech_problems(message_data)) - message = message_data[1] - - if(!message_data[3]) //if a speech problem like hulk forces someone to yell then everyone hears it - verb = message_data[2] //assume that if they are going to force not-whispering then they will set an appropriate verb too - message_range = world.view - else if(verb != message_data[2]) - adverb = pick("quietly", "softly") //new verb given, so 'whisperize' it with an adverb - verb = message_data[2] - - //consoldiate the adverb if we have one - if(adverb) verb = "[verb] [adverb]" - - if(!message || message=="") - return - - var/list/listening = hearers(message_range, src) - listening |= src - - //ghosts - for (var/mob/M in dead_mob_list) //does this include players who joined as observers as well? - if (!(M.client)) - continue - if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - listening |= M - - //Pass whispers on to anything inside the immediate listeners. - for(var/mob/L in listening) - for(var/mob/C in L.contents) - if(istype(C,/mob/living)) - listening += C - - //pass on the message to objects that can hear us. - for (var/obj/O in view(message_range, src)) - spawn (0) - if (O) - O.hear_talk(src, message, verb, speaking) - - var/list/eavesdropping = hearers(eavesdropping_range, src) - eavesdropping -= src - eavesdropping -= listening - - var/list/watching = hearers(watching_range, src) - watching -= src - watching -= listening - watching -= eavesdropping - - //now mobs - var/speech_bubble_test = say_test(message) - var/image/speech_bubble = image('icons/mob/talk.dmi',src,"h[speech_bubble_test]") - spawn(30) qdel(speech_bubble) - - for(var/mob/M in listening) - M << speech_bubble - M.hear_say(message, verb, speaking, alt_name, italics, src) - - if (eavesdropping.len) - var/new_message = stars(message) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less. - for(var/mob/M in eavesdropping) - M << speech_bubble - M.hear_say(new_message, verb, speaking, alt_name, italics, src) - - if (watching.len) - var/rendered = "[src.name] [not_heard]." - for (var/mob/M in watching) - M.show_message(rendered, 2) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index df769d1c88..73427a63ed 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -82,21 +82,18 @@ proc/get_radio_key_from_channel(var/channel) /mob/living/proc/get_default_language() return default_language -/mob/living/proc/is_muzzled() - return 0 - //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/verb = message_data[2] - + var/whispering = message_data[3] . = 0 if((HULK in mutations) && health >= 25 && length(message)) message = "[uppertext(message)]!!!" verb = pick("yells","roars","hollers") - message_data[3] = 0 + whispering = 0 . = 1 if(slurring) message = slur(message) @@ -109,6 +106,7 @@ proc/get_radio_key_from_channel(var/channel) message_data[1] = message message_data[2] = verb + message_data[2] = whispering /mob/living/proc/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name) if(message_mode == "intercom") @@ -130,33 +128,42 @@ 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="") +/mob/living/say(var/message, var/datum/language/speaking = null, var/verb="says", var/alt_name="", var/whispering = 0) + //If you're muted for IC chat if(client) - if(client.prefs.muted & MUTE_IC) - src << "\red You cannot speak in IC (Muted)." + if((client.prefs.muted & MUTE_IC) || say_disabled) + src << "You cannot speak in IC (Muted)." return + //Redirect to say_dead if talker is dead if(stat) - if(stat == 2) + if(stat == DEAD) return say_dead(message) return + //Parse the mode var/message_mode = parse_message_mode(message, "headset") + //Maybe they are using say/whisper to do a quick emote, so do those switch(copytext(message,1,2)) if("*") return emote(copytext(message,2)) if("^") return custom_emote(1, copytext(message,2)) - //parse the radio code and consume it + //Parse the radio code and consume it if (message_mode) if (message_mode == "headset") message = copytext(message,2) //it would be really nice if the parse procs could do this for us. + else if (message_mode == "whisper") + whispering = 1 + message_mode = null + message = copytext(message,3) else message = copytext(message,3) + //Clean up any remaining space on the left message = trim_left(message) - //parse the language code and consume it + //Parse the language code and consume it if(!speaking) speaking = parse_language(message) if(speaking) @@ -164,43 +171,72 @@ proc/get_radio_key_from_channel(var/channel) else speaking = get_default_language() - // This is broadcast to all mobs with the language, - // irrespective of distance or anything else. + //HIVEMIND languages always send to all people with that language if(speaking && (speaking.flags & HIVEMIND)) speaking.broadcast(src,trim(message)) return 1 - verb = say_quote(message, speaking) - + //Self explanatory. if(is_muzzled()) src << "You're muzzled and cannot speak!" 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 + + //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 + 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]" + + //For speech disorders (hulk, slurring, stuttering) if(!(speaking && (speaking.flags & NO_STUTTER))) - var/list/message_data = list(message, verb, 0) + var/list/message_data = list(message, verb, whispering) if(handle_speech_problems(message_data)) message = message_data[1] - verb = message_data[2] + whispering = message_data[3] + if(verb != message_data[2]) //They changed our verb + if(whispering) + w_adverb = pick("quietly", "softly") + verb = message_data[2] + + //Whisper may have adverbs, add those if one was set + if(w_adverb) verb = "[verb] [w_adverb]" + + //If something nulled or emptied the message, forget it if(!message || message == "") 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)) + if(handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, whispering)) return 1 + //For languages with actual speech sounds var/list/handle_v = handle_speech_sound() var/sound/speech_sound = handle_v[1] var/sound_vol = handle_v[2] - var/italics = 0 + //Default range and italics, may be overridden past here var/message_range = world.view + var/italics = 0 - //speaking into radios + //Speaking into radios if(used_radios.len) italics = 1 message_range = 1 @@ -215,9 +251,13 @@ proc/get_radio_key_from_channel(var/channel) if (speech_sound) sound_vol *= 0.5 - var/turf/T = get_turf(src) + //Set vars if we're still whispering by this point + if(whispering) + italics = 1 + message_range = 1 + sound_vol *= 0.5 - //handle nonverbal and sign languages here + //Handle nonverbal and sign languages here if (speaking) if (speaking.flags & NONVERBAL) if (prob(30)) @@ -226,42 +266,64 @@ proc/get_radio_key_from_channel(var/channel) if (speaking.flags & SIGNLANG) return say_signlang(message, pick(speaking.signlang_verb), speaking) + //These will contain the main receivers of the message var/list/listening = list() var/list/listening_obj = list() + //Atmosphere calculations (speaker's side only, for now) + var/turf/T = get_turf(src) if(T) - //make sure the air can transmit speech - speaker's side + //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 if(pressure < SOUND_MINIMUM_PRESSURE) message_range = 1 - 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 + //Air is nearing minimum levels, make text italics as a hint, and muffle sound + if (pressure < ONE_ATMOSPHERE*0.4) italics = 1 - sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact + sound_vol *= 0.5 - var/list/results = get_mobs_and_objs_in_view_fast(T, message_range) + //Obtain the mobs and objects in the message range + var/list/results = get_mobs_and_objs_in_view_fast(T, world.view) listening = results["mobs"] listening_obj = results["objs"] + else + return 1 //If we're in nullspace, then forget it. + //The 'post-say' static speech bubble var/speech_bubble_test = say_test(message) var/image/speech_bubble = image('icons/mob/talk.dmi',src,"h[speech_bubble_test]") spawn(30) qdel(speech_bubble) + //Main 'say' and 'whisper' message delivery for(var/mob/M in listening) - try - M << speech_bubble - M.hear_say(message, verb, speaking, alt_name, italics, src, speech_sound, sound_vol) - catch(var/exception/e) - log_debug("hear_say exception on mob - [e] in [e.file]:[e.line]") + spawn(0) //Using spawns to queue all the messages for AFTER this proc is done, and stop runtimes + if(M && src) //If we still exist, when the spawn processes + var/dst = get_dist(get_turf(M),get_turf(src)) + + if(dst <= message_range) //Inside normal message range, FYI -1 is "from self to self" range + M << speech_bubble + M.hear_say(message, verb, speaking, alt_name, 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 + M << speech_bubble + M.hear_say(stars(message), verb, speaking, alt_name, italics, src, speech_sound, sound_vol*0.2) + if(dst > w_scramble_range && dst <= world.view) //Inside whisper 'visible' range + M.show_message("[src.name] [w_not_heard].", 2) + + //Object message delivery for(var/obj/O in listening_obj) - try - O.hear_talk(src, message, verb, speaking) - catch(var/exception/e) - log_debug("hear_talk exception on obj - [e] in [e.file]:[e.line]") + spawn(0) + 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) - log_say("[name]/[key] : [message]") + //Log the message to file + log_say("[name]/[key][whispering ? " (W)" : ""]: [message]") return 1 /mob/living/proc/say_signlang(var/message, var/verb="gestures", var/datum/language/language) diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index ec47effaba..f7aaa52f78 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -1,29 +1,15 @@ /mob/proc/say() return -/mob/verb/whisper() +/mob/verb/whisper(message as text) set name = "Whisper" set category = "IC" - return + + usr.say(message,whispering=1) /mob/verb/say_verb(message as text) set name = "Say" set category = "IC" - if(say_disabled) //This is here to try to identify lag problems - usr << "\red Speech is currently admin-disabled." - return - //Let's try to make users fix their errors - we try to detect single, out-of-place letters and 'unintended' words - /* - var/first_letter = copytext(message,1,2) - if((copytext(message,2,3) == " " && first_letter != "I" && first_letter != "A" && first_letter != ";") || cmptext(copytext(message,1,5), "say ") || cmptext(copytext(message,1,4), "me ") || cmptext(copytext(message,1,6), "looc ") || cmptext(copytext(message,1,5), "ooc ") || cmptext(copytext(message,2,6), "say ")) - var/response = alert(usr, "Do you really want to say this using the *say* verb?\n\n[message]\n", "Confirm your message", "Yes", "Edit message", "No") - if(response == "Edit message") - message = input(usr, "Please edit your message carefully:", "Edit message", message) - if(!message) - return - else if(response == "No") - return - */ set_typing_indicator(0) usr.say(message) @@ -62,7 +48,7 @@ /mob/proc/say_understands(var/mob/other,var/datum/language/speaking = null) - if (src.stat == 2) //Dead + if (src.stat == DEAD) return 1 //Universal speak makes everything understandable, for obvious reasons. diff --git a/polaris.dme b/polaris.dme index 1ab16549c5..b352561b1e 100644 --- a/polaris.dme +++ b/polaris.dme @@ -1380,7 +1380,6 @@ #include "code\modules\mob\living\carbon\human\stripping.dm" #include "code\modules\mob\living\carbon\human\unarmed_attack.dm" #include "code\modules\mob\living\carbon\human\update_icons.dm" -#include "code\modules\mob\living\carbon\human\whisper.dm" #include "code\modules\mob\living\carbon\human\species\species.dm" #include "code\modules\mob\living\carbon\human\species\species_attack.dm" #include "code\modules\mob\living\carbon\human\species\species_getters.dm"