// At minimum every mob has a hear_say proc. /** * hear_say's return value determines whether or not the mob in question also receives a langchat image. */ /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, var/font_size = null) if(!istype(src, /mob/living/test) && cant_hear()) return FALSE if(speaker && !istype(speaker, /mob/living/test) && (!speaker.client && istype(src,/mob/abstract/ghost/observer) && client.prefs.toggles & CHAT_GHOSTEARS && !(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 FALSE //make sure the air can transmit speech - hearer's side var/turf/T = get_turf(src) var/vacuum_proof = ((language && (language.flags & PRESSUREPROOF)) || isghost(src)) var/speaker_name = speaker.name if(ishuman(speaker)) var/mob/living/carbon/human/H = speaker speaker_name = H.GetVoice() if(T && !vacuum_proof) //Ghosts can hear even in vacuum. var/datum/gas_mixture/environment = T.return_air() var/pressure = (environment)? environment.return_pressure() : 0 var/distance_to_speaker = get_dist(speaker, src) if(pressure < SOUND_MINIMUM_PRESSURE && distance_to_speaker > 1) // Yeah, this isn't quite realistic to be able to see if someone is talking through, say, an opaque mask, but for gameplay purposes it should help indicate that you're not bugged. if(distance_to_speaker <= 4 && !italics) to_chat(src, SPAN_NOTICE("[speaker_name] talks, but you're in a vacuum. Maybe if you were close enough for the sound to transmit through touch...")) 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((language && (language.flags & KNOWONLYHEAR)) && !say_understands(speaker, language)) return TRUE if(!vr_mob && (sleeping || stat == UNCONSCIOUS)) hear_sleep(message) return FALSE //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))) && !isghost(src)) message = stars(message) if(!(language && (language.flags & INNATE)) && !say_understands(speaker, language)) // skip understanding checks for INNATE languages message = language ? language.scramble(message, languages) : stars(message) var/accent_icon = speaker.get_accent_icon(language, src) if(italics) message = "[message]" var/track = null if(isghost(src)) if(speaker_name != speaker.real_name && speaker.real_name) speaker_name = "[speaker.real_name] ([speaker_name])" track = "[ghost_follow_link(speaker, src)] " if((client.prefs.toggles & CHAT_GHOSTEARS) && (get_turf(speaker) in view(src))) message = "[message]" if(isdeaf(src)) 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_WARNING("You cannot hear yourself speak!")) return FALSE else to_chat(src, "[speaker_name][alt_name] talks but you cannot hear them.") return FALSE else if(language) if(font_size) on_hear_say("[track][accent_icon ? accent_icon + " " : ""][speaker_name][alt_name] [language.format_message(message, verb)]") else on_hear_say("[track][accent_icon ? accent_icon + " " : ""][speaker_name][alt_name] [language.format_message(message, verb)]") else if(font_size) on_hear_say("[track][accent_icon ? accent_icon + " " : ""][speaker_name][alt_name] [verb], \"[message]\"") else on_hear_say("[track][accent_icon ? accent_icon + " " : ""][speaker_name][alt_name] [verb], \"[message]\"") if (speech_sound && (get_dist(speaker, src) <= world.view && src.z == speaker.z)) var/turf/source = speaker? get_turf(speaker) : get_turf(src) playsound(source, speech_sound, sound_vol, vary = TRUE) return TRUE /mob/proc/cant_hear() if(!client && !vr_mob) return TRUE return FALSE /mob/living/carbon/cant_hear() . = ..() if(.) var/datum/dionastats/DS = get_dionastats() if(DS?.nym) return FALSE /mob/living/carbon/alien/diona/cant_hear() . = ..() if(. && detached && gestalt) return FALSE /mob/proc/on_hear_say(var/message) to_chat(src, message) if(vr_mob) to_chat(vr_mob, message) /mob/living/carbon/on_hear_say(var/message) ..() var/datum/dionastats/DS = get_dionastats() if(DS?.nym) var/mob/living/carbon/alien/diona/D = DS.nym.resolve() if(D) to_chat(D, message) /mob/living/carbon/alien/diona/on_hear_say(var/message) to_chat(src, message) if(detached && gestalt) to_chat(gestalt, message) /mob/living/silicon/on_hear_say(var/message) var/time = say_timestamp() to_chat(src, "[time] [message]") if(vr_mob) to_chat(vr_mob, "[time] [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) if(!client && !vr_mob) return if(!vr_mob && (sleeping || stat == UNCONSCIOUS)) 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(istype(speaker,/mob/living/simple_animal)) var/mob/living/simple_animal/S = speaker if(S.speak && S.speak.len) message = pick(S.speak) else return else if(language) message = language.scramble(message, languages) else message = stars(message) if(hard_to_hear) message = stars(message) var/speaker_name if(speaker != null) speaker_name = speaker.name else speaker_name = "Unknown" if(ishuman(speaker)) speaker_name = speaker.GetVoice() if(hard_to_hear) speaker_name = "Unknown" var/changed_voice var/accent_icon = speaker.get_accent_icon(language, src) accent_icon = accent_icon ? accent_icon + " " : "" part_a = replacetext(part_a, "%ACCENT%", accent_icon) 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 = list() var/mob/living/carbon/human/I = impersonated[speaker_name] if(!I) for(var/mob/living/carbon/human/M in GLOB.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/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 = "[speaker_name] ([jobname])" else track = "[speaker_name] ([jobname])" else track = "[speaker_name] ([jobname])" if(isghost(src)) if(speaker != null) 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 = "[ghost_follow_link(speaker, src)] " var/formatted if(language) formatted = language.format_message_radio(message, verb) else formatted = "[verb], \"[message]\"" formatted += part_c if(isdeaf(src)) if(prob(20)) to_chat(src, SPAN_WARNING("You feel your headset vibrate but can hear nothing from it!")) else on_hear_radio(part_a, speaker_name, track, part_b, formatted, accent_icon) /proc/say_timestamp() return "\[[worldtime2text()]\]" /mob/proc/on_hear_radio(part_a, speaker_name, track, part_b, formatted, accent_icon) to_chat(src, "[part_a][speaker_name][part_b][formatted]") if(vr_mob) to_chat(vr_mob, "[part_a][speaker_name][part_b][formatted]") /mob/abstract/ghost/observer/on_hear_radio(part_a, speaker_name, track, part_b, formatted) to_chat(src, "[track][part_a][speaker_name][part_b][formatted]") /mob/living/silicon/on_hear_radio(part_a, speaker_name, track, part_b, formatted) var/time = say_timestamp() to_chat(src, "[time][part_a][speaker_name][part_b][formatted]") if(vr_mob) to_chat(vr_mob, "[time][part_a][speaker_name][part_b][formatted]") /mob/living/silicon/ai/on_hear_radio(part_a, speaker_name, track, part_b, formatted) var/time = say_timestamp() to_chat(src, "[time][part_a][track][part_b][formatted]") if(vr_mob) to_chat(vr_mob, "[time][part_a][track][part_b][formatted]") /mob/proc/hear_signlang(var/message, var/verb = "gestures", var/datum/language/language, var/mob/speaker = null, var/list/sign_adv_length = null) if(!client || !speaker) return if(say_understands(speaker, language)) message = "[speaker] [verb], \"[message]\"" else if (length(sign_adv_length) <= 4) sign_adv_length = list(" briefly", " a short message", " a message", " a lengthy message", " a very lengthy message") var/adverb var/length = length(message) * pick(0.8, 0.9, 1.0, 1.1, 1.2) //Inserts a little fuzziness. switch(length) if(0 to 12) adverb = sign_adv_length[1] if(12 to 30) adverb = sign_adv_length[2] if(30 to 48) adverb = sign_adv_length[3] if(48 to 90) adverb = sign_adv_length[4] else adverb = sign_adv_length[5] message = "[speaker] [verb][adverb]." if(src.status_flags & PASSEMOTES) for(var/obj/item/holder/H in src.contents) H.show_message(message) for(var/mob/living/M in src.contents) M.show_message(message) src.show_message(message) /mob/proc/hear_sleep(var/message) if (isdeaf(src)) return var/heard = "" if(prob(15)) var/list/punctuation = list(",", "!", ".", ";", "?") var/list/messages = text2list(message, " ") var/R = rand(1, messages.len) var/heardword = messages[R] if(copytext(heardword,1, 1) in punctuation) heardword = copytext(heardword,2) if(copytext(heardword,-1) in punctuation) heardword = copytext(heardword,1,length(heardword)) heard = "...You hear something about...[heardword]" else heard = "...You almost hear someone talking..." to_chat(src, heard)