mirror of
https://github.com/KabKebab/GS13.git
synced 2026-07-18 19:39:53 +01:00
Merge pull request #10813 from Ghommie/Ghommie-cit540
Unicode support updates from tgstation.
This commit is contained in:
@@ -232,14 +232,16 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
|
||||
var/b_val
|
||||
var/g_val
|
||||
var/color_format = length(input_color)
|
||||
if(color_format != length_char(input_color))
|
||||
return 0
|
||||
if(color_format == 3)
|
||||
r_val = hex2num(copytext(input_color, 1, 2))*16
|
||||
g_val = hex2num(copytext(input_color, 2, 3))*16
|
||||
b_val = hex2num(copytext(input_color, 3, 0))*16
|
||||
r_val = hex2num(copytext(input_color, 1, 2)) * 16
|
||||
g_val = hex2num(copytext(input_color, 2, 3)) * 16
|
||||
b_val = hex2num(copytext(input_color, 3, 0)) * 16
|
||||
else if(color_format == 6)
|
||||
r_val = hex2num(copytext(input_color, 1, 3))
|
||||
g_val = hex2num(copytext(input_color, 3, 5))
|
||||
b_val = hex2num(copytext(input_color, 5, 0))
|
||||
b_val = hex2num(copytext(input_color, 5, 7))
|
||||
else
|
||||
return 0 //If the color format is not 3 or 6, you're using an unexpected way to represent a color.
|
||||
|
||||
@@ -253,7 +255,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
|
||||
if(b_val > 255)
|
||||
b_val = 255
|
||||
|
||||
return num2hex(r_val, 2) + num2hex(g_val, 2) + num2hex(b_val, 2)
|
||||
return copytext(rgb(r_val, g_val, b_val), 2)
|
||||
|
||||
/*
|
||||
Transfer_mind is there to check if mob is being deleted/not going to have a body.
|
||||
@@ -262,7 +264,7 @@ Works together with spawning an observer, noted above.
|
||||
|
||||
/mob/proc/ghostize(can_reenter_corpse = TRUE, special = FALSE, penalize = FALSE)
|
||||
penalize = suiciding || penalize // suicide squad.
|
||||
if(!key || cmptext(copytext(key,1,2),"@") || (SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, can_reenter_corpse, special, penalize) & COMPONENT_BLOCK_GHOSTING))
|
||||
if(!key || key[1] == "@" || (SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, can_reenter_corpse, special, penalize) & COMPONENT_BLOCK_GHOSTING))
|
||||
return //mob has no key, is an aghost or some component hijacked.
|
||||
stop_sound_channel(CHANNEL_HEARTBEAT) //Stop heartbeat sounds because You Are A Ghost Now
|
||||
var/mob/dead/observer/ghost = new(src) // Transfer safety to observer spawning proc.
|
||||
@@ -357,7 +359,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
||||
if(!can_reenter_corpse)
|
||||
to_chat(src, "<span class='warning'>You cannot re-enter your body.</span>")
|
||||
return
|
||||
if(mind.current.key && copytext(mind.current.key,1,2)!="@") //makes sure we don't accidentally kick any clients
|
||||
if(mind.current.key && mind.current.key[1] != "@") //makes sure we don't accidentally kick any clients
|
||||
to_chat(usr, "<span class='warning'>Another consciousness is in your body...It is resisting you.</span>")
|
||||
return
|
||||
client.change_view(CONFIG_GET(string/default_view))
|
||||
|
||||
@@ -1,43 +1,42 @@
|
||||
/mob/dead/observer/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
|
||||
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
|
||||
if (!message)
|
||||
return
|
||||
|
||||
var/message_mode = get_message_mode(message)
|
||||
if(client && (message_mode == MODE_ADMIN || message_mode == MODE_DEADMIN))
|
||||
message = copytext(message, 3)
|
||||
if(findtext(message, " ", 1, 2))
|
||||
message = copytext(message, 2)
|
||||
|
||||
if(message_mode == MODE_ADMIN)
|
||||
client.cmd_admin_say(message)
|
||||
else if(message_mode == MODE_DEADMIN)
|
||||
client.dsay(message)
|
||||
return
|
||||
|
||||
src.log_talk(message, LOG_SAY, tag="ghost")
|
||||
|
||||
if(check_emote(message))
|
||||
return
|
||||
|
||||
. = say_dead(message)
|
||||
|
||||
/mob/dead/observer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
|
||||
. = ..()
|
||||
var/atom/movable/to_follow = speaker
|
||||
if(radio_freq)
|
||||
var/atom/movable/virtualspeaker/V = speaker
|
||||
|
||||
if(isAI(V.source))
|
||||
var/mob/living/silicon/ai/S = V.source
|
||||
to_follow = S.eyeobj
|
||||
else
|
||||
to_follow = V.source
|
||||
var/link = FOLLOW_LINK(src, to_follow)
|
||||
// Create map text prior to modifying message for goonchat
|
||||
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
|
||||
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||
// Recompose the message, because it's scrambled by default
|
||||
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)
|
||||
to_chat(src, "[link] [message]")
|
||||
|
||||
/mob/dead/observer/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
|
||||
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
|
||||
if (!message)
|
||||
return
|
||||
|
||||
var/message_mode = get_message_mode(message)
|
||||
if(client && (message_mode == MODE_ADMIN || message_mode == MODE_DEADMIN))
|
||||
message = copytext_char(message, 3)
|
||||
message = trim_left(message)
|
||||
|
||||
if(message_mode == MODE_ADMIN)
|
||||
client.cmd_admin_say(message)
|
||||
else if(message_mode == MODE_DEADMIN)
|
||||
client.dsay(message)
|
||||
return
|
||||
|
||||
src.log_talk(message, LOG_SAY, tag="ghost")
|
||||
|
||||
if(check_emote(message))
|
||||
return
|
||||
|
||||
. = say_dead(message)
|
||||
|
||||
/mob/dead/observer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
|
||||
. = ..()
|
||||
var/atom/movable/to_follow = speaker
|
||||
if(radio_freq)
|
||||
var/atom/movable/virtualspeaker/V = speaker
|
||||
|
||||
if(isAI(V.source))
|
||||
var/mob/living/silicon/ai/S = V.source
|
||||
to_follow = S.eyeobj
|
||||
else
|
||||
to_follow = V.source
|
||||
var/link = FOLLOW_LINK(src, to_follow)
|
||||
// Create map text prior to modifying message for goonchat
|
||||
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
|
||||
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||
// Recompose the message, because it's scrambled by default
|
||||
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)
|
||||
to_chat(src, "[link] [message]")
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
var/param = message
|
||||
var/custom_param = findchar(act, " ")
|
||||
if(custom_param)
|
||||
param = copytext(act, custom_param + 1, length(act) + 1)
|
||||
param = copytext(act, custom_param + length(act[custom_param]))
|
||||
act = copytext(act, 1, custom_param)
|
||||
|
||||
var/datum/emote/E
|
||||
|
||||
@@ -185,7 +185,7 @@
|
||||
var/datum/disease/D = thing
|
||||
blood_data["viruses"] += D.Copy()
|
||||
|
||||
blood_data["blood_DNA"] = copytext(dna.unique_enzymes,1,0)
|
||||
blood_data["blood_DNA"] = dna.unique_enzymes
|
||||
blood_data["bloodcolor"] = bloodtype_to_color(dna.blood_type)
|
||||
if(disease_resistances && disease_resistances.len)
|
||||
blood_data["resistances"] = disease_resistances.Copy()
|
||||
@@ -204,7 +204,7 @@
|
||||
|
||||
if(!suiciding)
|
||||
blood_data["cloneable"] = 1
|
||||
blood_data["blood_type"] = copytext(dna.blood_type,1,0)
|
||||
blood_data["blood_type"] = dna.blood_type
|
||||
blood_data["gender"] = gender
|
||||
blood_data["real_name"] = real_name
|
||||
blood_data["features"] = dna.features
|
||||
|
||||
@@ -120,7 +120,8 @@ Des: Removes all infected images from the alien.
|
||||
/mob/living/carbon/alien/proc/RemoveInfectionImages()
|
||||
if (client)
|
||||
for(var/image/I in client.images)
|
||||
if(dd_hasprefix_case(I.icon_state, "infected"))
|
||||
var/searchfor = "infected"
|
||||
if(findtext(I.icon_state, searchfor, 1, length(searchfor) + 1))
|
||||
qdel(I)
|
||||
return
|
||||
|
||||
|
||||
@@ -1,137 +1,138 @@
|
||||
// This is to replace the previous datum/disease/alien_embryo for slightly improved handling and maintainability
|
||||
// It functions almost identically (see code/datums/diseases/alien_embryo.dm)
|
||||
/obj/item/organ/body_egg/alien_embryo
|
||||
name = "alien embryo"
|
||||
icon = 'icons/mob/alien.dmi'
|
||||
icon_state = "larva0_dead"
|
||||
var/stage = 0
|
||||
var/bursting = FALSE
|
||||
|
||||
/obj/item/organ/body_egg/alien_embryo/on_find(mob/living/finder)
|
||||
..()
|
||||
if(stage < 4)
|
||||
to_chat(finder, "It's small and weak, barely the size of a foetus.")
|
||||
else
|
||||
to_chat(finder, "It's grown quite large, and writhes slightly as you look at it.")
|
||||
if(prob(10))
|
||||
AttemptGrow(0)
|
||||
|
||||
/obj/item/organ/body_egg/alien_embryo/prepare_eat()
|
||||
var/obj/S = ..()
|
||||
S.reagents.add_reagent(/datum/reagent/toxin/acid, 10)
|
||||
return S
|
||||
|
||||
/obj/item/organ/body_egg/alien_embryo/on_life()
|
||||
.=..()
|
||||
switch(stage)
|
||||
if(2, 3)
|
||||
if(prob(2))
|
||||
owner.emote("sneeze")
|
||||
if(prob(2))
|
||||
owner.emote("cough")
|
||||
if(prob(2))
|
||||
to_chat(owner, "<span class='danger'>Your throat feels sore.</span>")
|
||||
if(prob(2))
|
||||
to_chat(owner, "<span class='danger'>Mucous runs down the back of your throat.</span>")
|
||||
if(4)
|
||||
if(prob(2))
|
||||
owner.emote("sneeze")
|
||||
if(prob(2))
|
||||
owner.emote("cough")
|
||||
if(prob(4))
|
||||
to_chat(owner, "<span class='danger'>Your muscles ache.</span>")
|
||||
if(prob(20))
|
||||
owner.take_bodypart_damage(1)
|
||||
if(prob(4))
|
||||
to_chat(owner, "<span class='danger'>Your stomach hurts.</span>")
|
||||
if(prob(20))
|
||||
owner.adjustToxLoss(1)
|
||||
if(5)
|
||||
to_chat(owner, "<span class='danger'>You feel something tearing its way out of your stomach...</span>")
|
||||
owner.adjustToxLoss(10)
|
||||
|
||||
/obj/item/organ/body_egg/alien_embryo/egg_process()
|
||||
if(stage < 5 && prob(3))
|
||||
stage++
|
||||
INVOKE_ASYNC(src, .proc/RefreshInfectionImage)
|
||||
|
||||
if(stage == 5 && prob(50))
|
||||
for(var/datum/surgery/S in owner.surgeries)
|
||||
if(S.location == BODY_ZONE_CHEST && istype(S.get_surgery_step(), /datum/surgery_step/manipulate_organs))
|
||||
AttemptGrow(0)
|
||||
return
|
||||
AttemptGrow()
|
||||
|
||||
|
||||
|
||||
/obj/item/organ/body_egg/alien_embryo/proc/AttemptGrow(var/kill_on_sucess=TRUE)
|
||||
if(!owner || bursting)
|
||||
return
|
||||
|
||||
bursting = TRUE
|
||||
|
||||
var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", ROLE_ALIEN, null, ROLE_ALIEN, 100, POLL_IGNORE_ALIEN_LARVA)
|
||||
|
||||
if(QDELETED(src) || QDELETED(owner))
|
||||
return
|
||||
|
||||
if(!candidates.len || !owner)
|
||||
bursting = FALSE
|
||||
stage = 4
|
||||
return
|
||||
|
||||
var/mob/dead/observer/ghost = pick(candidates)
|
||||
|
||||
var/mutable_appearance/overlay = mutable_appearance('icons/mob/alien.dmi', "burst_lie")
|
||||
owner.add_overlay(overlay)
|
||||
|
||||
var/atom/xeno_loc = get_turf(owner)
|
||||
var/mob/living/carbon/alien/larva/new_xeno = new(xeno_loc)
|
||||
new_xeno.key = ghost.key
|
||||
SEND_SOUND(new_xeno, sound('sound/voice/hiss5.ogg',0,0,0,100)) //To get the player's attention
|
||||
new_xeno.canmove = 0 //so we don't move during the bursting animation
|
||||
new_xeno.notransform = 1
|
||||
new_xeno.invisibility = INVISIBILITY_MAXIMUM
|
||||
|
||||
sleep(6)
|
||||
|
||||
if(QDELETED(src) || QDELETED(owner))
|
||||
return
|
||||
|
||||
if(new_xeno)
|
||||
new_xeno.canmove = 1
|
||||
new_xeno.notransform = 0
|
||||
new_xeno.invisibility = 0
|
||||
|
||||
if(kill_on_sucess) //ITS TOO LATE
|
||||
new_xeno.visible_message("<span class='danger'>[new_xeno] bursts out of [owner]!</span>", "<span class='userdanger'>You exit [owner], your previous host.</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
|
||||
owner.apply_damage(rand(100,300),BRUTE,zone,FALSE) //Random high damage to torso so health sensors don't metagame.
|
||||
owner.spill_organs(TRUE,FALSE,TRUE) //Lets still make the death gruesome and impossible to just simply defib someone.
|
||||
owner.death(FALSE) //Just in case some freak occurance occurs where you somehow survive all your organs being removed from you and the 100-300 brute damage.
|
||||
else //When it is removed via surgery at a late stage, rather than forced.
|
||||
new_xeno.visible_message("<span class='danger'>[new_xeno] wriggles out of [owner]!</span>", "<span class='userdanger'>You exit [owner], your previous host.</span>")
|
||||
owner.adjustBruteLoss(40)
|
||||
owner.cut_overlay(overlay)
|
||||
qdel(src)
|
||||
|
||||
|
||||
/*----------------------------------------
|
||||
Proc: AddInfectionImages(C)
|
||||
Des: Adds the infection image to all aliens for this embryo
|
||||
----------------------------------------*/
|
||||
/obj/item/organ/body_egg/alien_embryo/AddInfectionImages()
|
||||
for(var/mob/living/carbon/alien/alien in GLOB.player_list)
|
||||
if(alien.client)
|
||||
var/I = image('icons/mob/alien.dmi', loc = owner, icon_state = "infected[stage]")
|
||||
alien.client.images += I
|
||||
|
||||
/*----------------------------------------
|
||||
Proc: RemoveInfectionImage(C)
|
||||
Des: Removes all images from the mob infected by this embryo
|
||||
----------------------------------------*/
|
||||
/obj/item/organ/body_egg/alien_embryo/RemoveInfectionImages()
|
||||
for(var/mob/living/carbon/alien/alien in GLOB.player_list)
|
||||
if(alien.client)
|
||||
for(var/image/I in alien.client.images)
|
||||
if(dd_hasprefix_case(I.icon_state, "infected") && I.loc == owner)
|
||||
qdel(I)
|
||||
// This is to replace the previous datum/disease/alien_embryo for slightly improved handling and maintainability
|
||||
// It functions almost identically (see code/datums/diseases/alien_embryo.dm)
|
||||
/obj/item/organ/body_egg/alien_embryo
|
||||
name = "alien embryo"
|
||||
icon = 'icons/mob/alien.dmi'
|
||||
icon_state = "larva0_dead"
|
||||
var/stage = 0
|
||||
var/bursting = FALSE
|
||||
|
||||
/obj/item/organ/body_egg/alien_embryo/on_find(mob/living/finder)
|
||||
..()
|
||||
if(stage < 4)
|
||||
to_chat(finder, "It's small and weak, barely the size of a foetus.")
|
||||
else
|
||||
to_chat(finder, "It's grown quite large, and writhes slightly as you look at it.")
|
||||
if(prob(10))
|
||||
AttemptGrow(0)
|
||||
|
||||
/obj/item/organ/body_egg/alien_embryo/prepare_eat()
|
||||
var/obj/S = ..()
|
||||
S.reagents.add_reagent(/datum/reagent/toxin/acid, 10)
|
||||
return S
|
||||
|
||||
/obj/item/organ/body_egg/alien_embryo/on_life()
|
||||
.=..()
|
||||
switch(stage)
|
||||
if(2, 3)
|
||||
if(prob(2))
|
||||
owner.emote("sneeze")
|
||||
if(prob(2))
|
||||
owner.emote("cough")
|
||||
if(prob(2))
|
||||
to_chat(owner, "<span class='danger'>Your throat feels sore.</span>")
|
||||
if(prob(2))
|
||||
to_chat(owner, "<span class='danger'>Mucous runs down the back of your throat.</span>")
|
||||
if(4)
|
||||
if(prob(2))
|
||||
owner.emote("sneeze")
|
||||
if(prob(2))
|
||||
owner.emote("cough")
|
||||
if(prob(4))
|
||||
to_chat(owner, "<span class='danger'>Your muscles ache.</span>")
|
||||
if(prob(20))
|
||||
owner.take_bodypart_damage(1)
|
||||
if(prob(4))
|
||||
to_chat(owner, "<span class='danger'>Your stomach hurts.</span>")
|
||||
if(prob(20))
|
||||
owner.adjustToxLoss(1)
|
||||
if(5)
|
||||
to_chat(owner, "<span class='danger'>You feel something tearing its way out of your stomach...</span>")
|
||||
owner.adjustToxLoss(10)
|
||||
|
||||
/obj/item/organ/body_egg/alien_embryo/egg_process()
|
||||
if(stage < 5 && prob(3))
|
||||
stage++
|
||||
INVOKE_ASYNC(src, .proc/RefreshInfectionImage)
|
||||
|
||||
if(stage == 5 && prob(50))
|
||||
for(var/datum/surgery/S in owner.surgeries)
|
||||
if(S.location == BODY_ZONE_CHEST && istype(S.get_surgery_step(), /datum/surgery_step/manipulate_organs))
|
||||
AttemptGrow(0)
|
||||
return
|
||||
AttemptGrow()
|
||||
|
||||
|
||||
|
||||
/obj/item/organ/body_egg/alien_embryo/proc/AttemptGrow(var/kill_on_sucess=TRUE)
|
||||
if(!owner || bursting)
|
||||
return
|
||||
|
||||
bursting = TRUE
|
||||
|
||||
var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", ROLE_ALIEN, null, ROLE_ALIEN, 100, POLL_IGNORE_ALIEN_LARVA)
|
||||
|
||||
if(QDELETED(src) || QDELETED(owner))
|
||||
return
|
||||
|
||||
if(!candidates.len || !owner)
|
||||
bursting = FALSE
|
||||
stage = 4
|
||||
return
|
||||
|
||||
var/mob/dead/observer/ghost = pick(candidates)
|
||||
|
||||
var/mutable_appearance/overlay = mutable_appearance('icons/mob/alien.dmi', "burst_lie")
|
||||
owner.add_overlay(overlay)
|
||||
|
||||
var/atom/xeno_loc = get_turf(owner)
|
||||
var/mob/living/carbon/alien/larva/new_xeno = new(xeno_loc)
|
||||
new_xeno.key = ghost.key
|
||||
SEND_SOUND(new_xeno, sound('sound/voice/hiss5.ogg',0,0,0,100)) //To get the player's attention
|
||||
new_xeno.canmove = 0 //so we don't move during the bursting animation
|
||||
new_xeno.notransform = 1
|
||||
new_xeno.invisibility = INVISIBILITY_MAXIMUM
|
||||
|
||||
sleep(6)
|
||||
|
||||
if(QDELETED(src) || QDELETED(owner))
|
||||
return
|
||||
|
||||
if(new_xeno)
|
||||
new_xeno.canmove = 1
|
||||
new_xeno.notransform = 0
|
||||
new_xeno.invisibility = 0
|
||||
|
||||
if(kill_on_sucess) //ITS TOO LATE
|
||||
new_xeno.visible_message("<span class='danger'>[new_xeno] bursts out of [owner]!</span>", "<span class='userdanger'>You exit [owner], your previous host.</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
|
||||
owner.apply_damage(rand(100,300),BRUTE,zone,FALSE) //Random high damage to torso so health sensors don't metagame.
|
||||
owner.spill_organs(TRUE,FALSE,TRUE) //Lets still make the death gruesome and impossible to just simply defib someone.
|
||||
owner.death(FALSE) //Just in case some freak occurance occurs where you somehow survive all your organs being removed from you and the 100-300 brute damage.
|
||||
else //When it is removed via surgery at a late stage, rather than forced.
|
||||
new_xeno.visible_message("<span class='danger'>[new_xeno] wriggles out of [owner]!</span>", "<span class='userdanger'>You exit [owner], your previous host.</span>")
|
||||
owner.adjustBruteLoss(40)
|
||||
owner.cut_overlay(overlay)
|
||||
qdel(src)
|
||||
|
||||
|
||||
/*----------------------------------------
|
||||
Proc: AddInfectionImages(C)
|
||||
Des: Adds the infection image to all aliens for this embryo
|
||||
----------------------------------------*/
|
||||
/obj/item/organ/body_egg/alien_embryo/AddInfectionImages()
|
||||
for(var/mob/living/carbon/alien/alien in GLOB.player_list)
|
||||
if(alien.client)
|
||||
var/I = image('icons/mob/alien.dmi', loc = owner, icon_state = "infected[stage]")
|
||||
alien.client.images += I
|
||||
|
||||
/*----------------------------------------
|
||||
Proc: RemoveInfectionImage(C)
|
||||
Des: Removes all images from the mob infected by this embryo
|
||||
----------------------------------------*/
|
||||
/obj/item/organ/body_egg/alien_embryo/RemoveInfectionImages()
|
||||
for(var/mob/living/carbon/alien/alien in GLOB.player_list)
|
||||
if(alien.client)
|
||||
for(var/image/I in alien.client.images)
|
||||
var/searchfor = "infected"
|
||||
if(I.loc == owner && findtext(I.icon_state, searchfor, 1, length(searchfor) + 1))
|
||||
qdel(I)
|
||||
|
||||
@@ -1,116 +1,108 @@
|
||||
/mob/living/carbon/human/say_mod(input, message_mode)
|
||||
verb_say = dna.species.say_mod
|
||||
. = ..()
|
||||
if(message_mode != MODE_CUSTOM_SAY && message_mode != MODE_WHISPER_CRIT)
|
||||
switch(slurring)
|
||||
if(10 to 25)
|
||||
return "jumbles"
|
||||
if(25 to 50)
|
||||
return "slurs"
|
||||
if(50 to INFINITY)
|
||||
return "garbles"
|
||||
|
||||
/mob/living/carbon/human/GetVoice()
|
||||
if(istype(wear_mask, /obj/item/clothing/mask/chameleon))
|
||||
var/obj/item/clothing/mask/chameleon/V = wear_mask
|
||||
if(V.vchange && wear_id)
|
||||
var/obj/item/card/id/idcard = wear_id.GetID()
|
||||
if(istype(idcard))
|
||||
return idcard.registered_name
|
||||
else
|
||||
return real_name
|
||||
else
|
||||
return real_name
|
||||
if(mind)
|
||||
var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
|
||||
if(changeling && changeling.mimicing )
|
||||
return changeling.mimicing
|
||||
if(GetSpecialVoice())
|
||||
return GetSpecialVoice()
|
||||
return real_name
|
||||
|
||||
/mob/living/carbon/human/IsVocal()
|
||||
// how do species that don't breathe talk? magic, that's what.
|
||||
if(!HAS_TRAIT_FROM(src, TRAIT_NOBREATH, SPECIES_TRAIT) && !getorganslot(ORGAN_SLOT_LUNGS))
|
||||
return FALSE
|
||||
if(mind)
|
||||
return !mind.miming
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/human/proc/SetSpecialVoice(new_voice)
|
||||
if(new_voice)
|
||||
special_voice = new_voice
|
||||
return
|
||||
|
||||
/mob/living/carbon/human/proc/UnsetSpecialVoice()
|
||||
special_voice = ""
|
||||
return
|
||||
|
||||
/mob/living/carbon/human/proc/GetSpecialVoice()
|
||||
return special_voice
|
||||
|
||||
/mob/living/carbon/human/binarycheck()
|
||||
if(ears)
|
||||
var/obj/item/radio/headset/dongle = ears
|
||||
if(!istype(dongle))
|
||||
return FALSE
|
||||
if(dongle.translate_binary)
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/human/radio(message, message_mode, list/spans, language)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(message_mode)
|
||||
if(MODE_HEADSET)
|
||||
if (ears)
|
||||
ears.talk_into(src, message, , spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
|
||||
if(MODE_DEPARTMENT)
|
||||
if (ears)
|
||||
ears.talk_into(src, message, message_mode, spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
|
||||
if(message_mode in GLOB.radiochannels)
|
||||
if(ears)
|
||||
ears.talk_into(src, message, message_mode, spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
|
||||
return 0
|
||||
|
||||
/mob/living/carbon/human/get_alt_name()
|
||||
if(name != GetVoice())
|
||||
return " (as [get_id_name("Unknown")])"
|
||||
|
||||
/mob/living/carbon/human/proc/forcesay(list/append) //this proc is at the bottom of the file because quote fuckery makes notepad++ cri
|
||||
if(stat == CONSCIOUS)
|
||||
if(client)
|
||||
var/virgin = 1 //has the text been modified yet?
|
||||
var/temp = winget(client, "input", "text")
|
||||
if(findtextEx(temp, "Say \"", 1, 7) && length(temp) > 5) //"case sensitive means
|
||||
|
||||
temp = replacetext(temp, ";", "") //general radio
|
||||
|
||||
if(findtext(trim_left(temp), ":", 6, 7)) //dept radio
|
||||
temp = copytext(trim_left(temp), 8)
|
||||
virgin = 0
|
||||
|
||||
if(virgin)
|
||||
temp = copytext(trim_left(temp), 6) //normal speech
|
||||
virgin = 0
|
||||
|
||||
while(findtext(trim_left(temp), ":", 1, 2)) //dept radio again (necessary)
|
||||
temp = copytext(trim_left(temp), 3)
|
||||
|
||||
if(findtext(temp, "*", 1, 2)) //emotes
|
||||
return
|
||||
|
||||
var/trimmed = trim_left(temp)
|
||||
if(length(trimmed))
|
||||
if(append)
|
||||
temp += pick(append)
|
||||
|
||||
say(temp)
|
||||
winset(client, "input", "text=[null]")
|
||||
/mob/living/carbon/human/say_mod(input, message_mode)
|
||||
verb_say = dna.species.say_mod
|
||||
. = ..()
|
||||
if(message_mode != MODE_CUSTOM_SAY && message_mode != MODE_WHISPER_CRIT)
|
||||
switch(slurring)
|
||||
if(10 to 25)
|
||||
return "jumbles"
|
||||
if(25 to 50)
|
||||
return "slurs"
|
||||
if(50 to INFINITY)
|
||||
return "garbles"
|
||||
|
||||
/mob/living/carbon/human/GetVoice()
|
||||
if(istype(wear_mask, /obj/item/clothing/mask/chameleon))
|
||||
var/obj/item/clothing/mask/chameleon/V = wear_mask
|
||||
if(V.vchange && wear_id)
|
||||
var/obj/item/card/id/idcard = wear_id.GetID()
|
||||
if(istype(idcard))
|
||||
return idcard.registered_name
|
||||
else
|
||||
return real_name
|
||||
else
|
||||
return real_name
|
||||
if(mind)
|
||||
var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
|
||||
if(changeling && changeling.mimicing )
|
||||
return changeling.mimicing
|
||||
if(GetSpecialVoice())
|
||||
return GetSpecialVoice()
|
||||
return real_name
|
||||
|
||||
/mob/living/carbon/human/IsVocal()
|
||||
// how do species that don't breathe talk? magic, that's what.
|
||||
if(!HAS_TRAIT_FROM(src, TRAIT_NOBREATH, SPECIES_TRAIT) && !getorganslot(ORGAN_SLOT_LUNGS))
|
||||
return FALSE
|
||||
if(mind)
|
||||
return !mind.miming
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/human/proc/SetSpecialVoice(new_voice)
|
||||
if(new_voice)
|
||||
special_voice = new_voice
|
||||
return
|
||||
|
||||
/mob/living/carbon/human/proc/UnsetSpecialVoice()
|
||||
special_voice = ""
|
||||
return
|
||||
|
||||
/mob/living/carbon/human/proc/GetSpecialVoice()
|
||||
return special_voice
|
||||
|
||||
/mob/living/carbon/human/binarycheck()
|
||||
if(ears)
|
||||
var/obj/item/radio/headset/dongle = ears
|
||||
if(!istype(dongle))
|
||||
return FALSE
|
||||
if(dongle.translate_binary)
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/human/radio(message, message_mode, list/spans, language)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(message_mode)
|
||||
if(MODE_HEADSET)
|
||||
if (ears)
|
||||
ears.talk_into(src, message, , spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
|
||||
if(MODE_DEPARTMENT)
|
||||
if (ears)
|
||||
ears.talk_into(src, message, message_mode, spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
|
||||
if(message_mode in GLOB.radiochannels)
|
||||
if(ears)
|
||||
ears.talk_into(src, message, message_mode, spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
|
||||
return 0
|
||||
|
||||
/mob/living/carbon/human/get_alt_name()
|
||||
if(name != GetVoice())
|
||||
return " (as [get_id_name("Unknown")])"
|
||||
|
||||
/mob/living/carbon/human/proc/forcesay(list/append) //this proc is at the bottom of the file because quote fuckery makes notepad++ cri
|
||||
if(stat == CONSCIOUS)
|
||||
if(client)
|
||||
var/temp = winget(client, "input", "text")
|
||||
var/say_starter = "Say \"" //"
|
||||
if(findtextEx(temp, say_starter, 1, length(say_starter) + 1) && length(temp) > length(say_starter)) //case sensitive means
|
||||
|
||||
temp = trim_left(copytext(temp, length(say_starter + 1)))
|
||||
temp = replacetext(temp, ";", "", 1, 2) //general radio
|
||||
while(trim_left(temp)[1] == ":") //dept radio again (necessary)
|
||||
temp = copytext_char(trim_left(temp), 3)
|
||||
|
||||
if(temp[1] == "*") //emotes
|
||||
return
|
||||
|
||||
var/trimmed = trim_left(temp)
|
||||
if(length(trimmed))
|
||||
if(append)
|
||||
trimmed += pick(append)
|
||||
|
||||
say(trimmed)
|
||||
winset(client, "input", "text=[null]")
|
||||
|
||||
+512
-519
File diff suppressed because it is too large
Load Diff
+435
-437
@@ -1,437 +1,435 @@
|
||||
GLOBAL_LIST_INIT(department_radio_prefixes, list(":", "."))
|
||||
|
||||
GLOBAL_LIST_INIT(department_radio_keys, list(
|
||||
// Location
|
||||
MODE_KEY_R_HAND = MODE_R_HAND,
|
||||
MODE_KEY_L_HAND = MODE_L_HAND,
|
||||
MODE_KEY_INTERCOM = MODE_INTERCOM,
|
||||
|
||||
// Department
|
||||
MODE_KEY_DEPARTMENT = MODE_DEPARTMENT,
|
||||
RADIO_KEY_COMMAND = RADIO_CHANNEL_COMMAND,
|
||||
RADIO_KEY_SCIENCE = RADIO_CHANNEL_SCIENCE,
|
||||
RADIO_KEY_MEDICAL = RADIO_CHANNEL_MEDICAL,
|
||||
RADIO_KEY_ENGINEERING = RADIO_CHANNEL_ENGINEERING,
|
||||
RADIO_KEY_SECURITY = RADIO_CHANNEL_SECURITY,
|
||||
RADIO_KEY_SUPPLY = RADIO_CHANNEL_SUPPLY,
|
||||
RADIO_KEY_SERVICE = RADIO_CHANNEL_SERVICE,
|
||||
|
||||
// Faction
|
||||
RADIO_KEY_SYNDICATE = RADIO_CHANNEL_SYNDICATE,
|
||||
RADIO_KEY_CENTCOM = RADIO_CHANNEL_CENTCOM,
|
||||
|
||||
// Admin
|
||||
MODE_KEY_ADMIN = MODE_ADMIN,
|
||||
MODE_KEY_DEADMIN = MODE_DEADMIN,
|
||||
|
||||
// Misc
|
||||
RADIO_KEY_AI_PRIVATE = RADIO_CHANNEL_AI_PRIVATE, // AI Upload channel
|
||||
MODE_KEY_VOCALCORDS = MODE_VOCALCORDS, // vocal cords, used by Voice of God
|
||||
|
||||
|
||||
//kinda localization -- rastaf0
|
||||
//same keys as above, but on russian keyboard layout. This file uses cp1251 as encoding.
|
||||
// Location
|
||||
"ê" = MODE_R_HAND,
|
||||
"ä" = MODE_L_HAND,
|
||||
"ø" = MODE_INTERCOM,
|
||||
|
||||
// Department
|
||||
"ð" = MODE_DEPARTMENT,
|
||||
"ñ" = RADIO_CHANNEL_COMMAND,
|
||||
"ò" = RADIO_CHANNEL_SCIENCE,
|
||||
"ü" = RADIO_CHANNEL_MEDICAL,
|
||||
"ó" = RADIO_CHANNEL_ENGINEERING,
|
||||
"û" = RADIO_CHANNEL_SECURITY,
|
||||
"ã" = RADIO_CHANNEL_SUPPLY,
|
||||
"ì" = RADIO_CHANNEL_SERVICE,
|
||||
|
||||
// Faction
|
||||
"å" = RADIO_CHANNEL_SYNDICATE,
|
||||
"í" = RADIO_CHANNEL_CENTCOM,
|
||||
|
||||
// Admin
|
||||
"ç" = MODE_ADMIN,
|
||||
"â" = MODE_ADMIN,
|
||||
|
||||
// Misc
|
||||
"ù" = RADIO_CHANNEL_AI_PRIVATE,
|
||||
"÷" = MODE_VOCALCORDS
|
||||
))
|
||||
|
||||
/mob/living/proc/Ellipsis(original_msg, chance = 50, keep_words)
|
||||
if(chance <= 0)
|
||||
return "..."
|
||||
if(chance >= 100)
|
||||
return original_msg
|
||||
|
||||
var/list/words = splittext(original_msg," ")
|
||||
var/list/new_words = list()
|
||||
|
||||
var/new_msg = ""
|
||||
|
||||
for(var/w in words)
|
||||
if(prob(chance))
|
||||
new_words += "..."
|
||||
if(!keep_words)
|
||||
continue
|
||||
new_words += w
|
||||
|
||||
new_msg = jointext(new_words," ")
|
||||
|
||||
return new_msg
|
||||
|
||||
/mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
|
||||
var/static/list/crit_allowed_modes = list(MODE_WHISPER = TRUE, MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
|
||||
var/static/list/unconscious_allowed_modes = list(MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
|
||||
var/talk_key = get_key(message)
|
||||
|
||||
var/static/list/one_character_prefix = list(MODE_HEADSET = TRUE, MODE_ROBOT = TRUE, MODE_WHISPER = TRUE, MODE_SING = TRUE)
|
||||
|
||||
if(sanitize)
|
||||
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
|
||||
if(!message || message == "")
|
||||
return
|
||||
|
||||
var/datum/saymode/saymode = SSradio.saymodes[talk_key]
|
||||
var/message_mode = get_message_mode(message)
|
||||
var/original_message = message
|
||||
var/in_critical = InCritical()
|
||||
|
||||
if(one_character_prefix[message_mode])
|
||||
message = copytext(message, 2)
|
||||
else if(message_mode || saymode)
|
||||
message = copytext(message, 3)
|
||||
if(findtext(message, " ", 1, 2))
|
||||
message = copytext(message, 2)
|
||||
|
||||
if(message_mode == MODE_ADMIN)
|
||||
if(client)
|
||||
client.cmd_admin_say(message)
|
||||
return
|
||||
|
||||
if(message_mode == MODE_DEADMIN)
|
||||
if(client)
|
||||
client.dsay(message)
|
||||
return
|
||||
|
||||
if(stat == DEAD)
|
||||
say_dead(original_message)
|
||||
return
|
||||
|
||||
if(check_emote(original_message) || !can_speak_basic(original_message, ignore_spam))
|
||||
return
|
||||
|
||||
if(in_critical)
|
||||
if(!(crit_allowed_modes[message_mode]))
|
||||
return
|
||||
else if(stat == UNCONSCIOUS)
|
||||
if(!(unconscious_allowed_modes[message_mode]))
|
||||
return
|
||||
|
||||
// language comma detection.
|
||||
var/datum/language/message_language = get_message_language(message)
|
||||
if(message_language)
|
||||
// No, you cannot speak in xenocommon just because you know the key
|
||||
if(can_speak_in_language(message_language))
|
||||
language = message_language
|
||||
message = copytext(message, 3)
|
||||
|
||||
// Trim the space if they said ",0 I LOVE LANGUAGES"
|
||||
if(findtext(message, " ", 1, 2))
|
||||
message = copytext(message, 2)
|
||||
|
||||
if(!language)
|
||||
language = get_default_language()
|
||||
|
||||
// Detection of language needs to be before inherent channels, because
|
||||
// AIs use inherent channels for the holopad. Most inherent channels
|
||||
// ignore the language argument however.
|
||||
|
||||
if(saymode && !saymode.handle_message(src, message, language))
|
||||
return
|
||||
|
||||
if(!can_speak_vocal(message))
|
||||
to_chat(src, "<span class='warning'>You find yourself unable to speak!</span>")
|
||||
return
|
||||
|
||||
var/message_range = 7
|
||||
|
||||
var/succumbed = FALSE
|
||||
|
||||
var/fullcrit = InFullCritical()
|
||||
if((InCritical() && !fullcrit) || message_mode == MODE_WHISPER)
|
||||
message_range = 1
|
||||
message_mode = MODE_WHISPER
|
||||
src.log_talk(message, LOG_WHISPER)
|
||||
if(fullcrit)
|
||||
var/health_diff = round(-HEALTH_THRESHOLD_DEAD + health)
|
||||
// If we cut our message short, abruptly end it with a-..
|
||||
var/message_len = length(message)
|
||||
message = copytext(message, 1, health_diff) + "[message_len > health_diff ? "-.." : "..."]"
|
||||
message = Ellipsis(message, 10, 1)
|
||||
message_mode = MODE_WHISPER_CRIT
|
||||
succumbed = TRUE
|
||||
else
|
||||
src.log_talk(message, LOG_SAY, forced_by=forced)
|
||||
|
||||
message = treat_message(message) // unfortunately we still need this
|
||||
var/sigreturn = SEND_SIGNAL(src, COMSIG_MOB_SAY, args)
|
||||
if (sigreturn & COMPONENT_UPPERCASE_SPEECH)
|
||||
message = uppertext(message)
|
||||
if(!message)
|
||||
return
|
||||
|
||||
last_words = message
|
||||
|
||||
spans |= speech_span
|
||||
|
||||
if(language)
|
||||
var/datum/language/L = GLOB.language_datum_instances[language]
|
||||
spans |= L.spans
|
||||
|
||||
if(message_mode == MODE_SING)
|
||||
#if DM_VERSION < 513
|
||||
var/randomnote = "~"
|
||||
#else
|
||||
var/randomnote = pick("\u2669", "\u266A", "\u266B")
|
||||
#endif
|
||||
spans |= SPAN_SINGING
|
||||
message = "[randomnote] [message] [randomnote]"
|
||||
|
||||
var/radio_return = radio(message, message_mode, spans, language)
|
||||
if(radio_return & ITALICS)
|
||||
spans |= SPAN_ITALICS
|
||||
if(radio_return & REDUCE_RANGE)
|
||||
message_range = 1
|
||||
if(radio_return & NOPASS)
|
||||
return 1
|
||||
|
||||
//No screams in space, unless you're next to someone.
|
||||
var/turf/T = get_turf(src)
|
||||
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) //Thin air, let's italicise the message
|
||||
spans |= SPAN_ITALICS
|
||||
|
||||
send_speech(message, message_range, src, bubble_type, spans, language, message_mode)
|
||||
|
||||
if(succumbed)
|
||||
succumb()
|
||||
to_chat(src, compose_message(src, language, message, null, spans, message_mode))
|
||||
|
||||
return 1
|
||||
|
||||
/mob/living/compose_message(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, face_name = FALSE, atom/movable/source)
|
||||
. = ..()
|
||||
if(isliving(speaker))
|
||||
var/turf/sourceturf = get_turf(source)
|
||||
var/turf/T = get_turf(src)
|
||||
if(sourceturf && T && !(sourceturf in get_hear(5, T)))
|
||||
. = "<span class='small'>[.]</span>"
|
||||
|
||||
/mob/living/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
|
||||
. = ..()
|
||||
if(!client)
|
||||
return
|
||||
var/deaf_message
|
||||
var/deaf_type
|
||||
if(speaker != src)
|
||||
if(!radio_freq) //These checks have to be seperate, else people talking on the radio will make "You can't hear yourself!" appear when hearing people over the radio while deaf.
|
||||
deaf_message = "<span class='name'>[speaker]</span> [speaker.verb_say] something but you cannot hear [speaker.p_them()]."
|
||||
deaf_type = 1
|
||||
else
|
||||
deaf_message = "<span class='notice'>You can't hear yourself!</span>"
|
||||
deaf_type = 2 // Since you should be able to hear yourself without looking
|
||||
// Create map text prior to modifying message for goonchat
|
||||
if (client?.prefs.chat_on_map && stat != UNCONSCIOUS && (client.prefs.see_chat_non_mob || ismob(speaker)) && can_hear())
|
||||
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||
if (client?.prefs.radiosounds && stat != UNCONSCIOUS && can_hear() && radio_freq)
|
||||
playsound_local(src,'sound/voice/radio.ogg', 30, 1)
|
||||
|
||||
// Recompose message for AI hrefs, language incomprehension.
|
||||
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)
|
||||
message = hear_intercept(message, speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)
|
||||
|
||||
show_message(message, MSG_AUDIBLE, deaf_message, deaf_type)
|
||||
return message
|
||||
|
||||
/mob/living/proc/hear_intercept(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
|
||||
return message
|
||||
|
||||
/mob/living/send_speech(message, message_range = 6, obj/source = src, bubble_type = bubble_icon, list/spans, datum/language/message_language=null, message_mode)
|
||||
var/static/list/eavesdropping_modes = list(MODE_WHISPER = TRUE, MODE_WHISPER_CRIT = TRUE)
|
||||
var/eavesdrop_range = 0
|
||||
if(eavesdropping_modes[message_mode])
|
||||
eavesdrop_range = EAVESDROP_EXTRA_RANGE
|
||||
var/list/listening = get_hearers_in_view(message_range+eavesdrop_range, source)
|
||||
var/list/the_dead = list()
|
||||
var/list/yellareas //CIT CHANGE - adds the ability for yelling to penetrate walls and echo throughout areas
|
||||
if(!eavesdrop_range && say_test(message) == "2") //CIT CHANGE - ditto
|
||||
yellareas = get_areas_in_range(message_range*0.5, source) //CIT CHANGE - ditto
|
||||
for(var/_M in GLOB.player_list)
|
||||
var/mob/M = _M
|
||||
if(M.stat != DEAD) //not dead, not important
|
||||
if(yellareas) //CIT CHANGE - see above. makes yelling penetrate walls
|
||||
var/area/A = get_area(M) //CIT CHANGE - ditto
|
||||
if(istype(A) && A.ambientsounds != SPACE && A in yellareas) //CIT CHANGE - ditto
|
||||
listening |= M //CIT CHANGE - ditto
|
||||
continue
|
||||
if(!M.client || !client) //client is so that ghosts don't have to listen to mice
|
||||
continue
|
||||
if(get_dist(M, source) > 7 || M.z != z) //they're out of range of normal hearing
|
||||
if(eavesdropping_modes[message_mode] && !(M.client.prefs.chat_toggles & CHAT_GHOSTWHISPER)) //they're whispering and we have hearing whispers at any range off
|
||||
continue
|
||||
if(!(M.client.prefs.chat_toggles & CHAT_GHOSTEARS)) //they're talking normally and we have hearing at any range off
|
||||
continue
|
||||
listening |= M
|
||||
the_dead[M] = TRUE
|
||||
|
||||
var/eavesdropping
|
||||
var/eavesrendered
|
||||
if(eavesdrop_range)
|
||||
eavesdropping = stars(message)
|
||||
eavesrendered = compose_message(src, message_language, eavesdropping, null, spans, message_mode, FALSE, source)
|
||||
|
||||
var/rendered = compose_message(src, message_language, message, null, spans, message_mode, FALSE, source)
|
||||
for(var/_AM in listening)
|
||||
var/atom/movable/AM = _AM
|
||||
if(eavesdrop_range && get_dist(source, AM) > message_range && !(the_dead[AM]))
|
||||
AM.Hear(eavesrendered, src, message_language, eavesdropping, null, spans, message_mode, source)
|
||||
else
|
||||
AM.Hear(rendered, src, message_language, message, null, spans, message_mode, source)
|
||||
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_LIVING_SAY_SPECIAL, src, message)
|
||||
|
||||
//speech bubble
|
||||
var/list/speech_bubble_recipients = list()
|
||||
for(var/mob/M in listening)
|
||||
if(M.client && !M.client.prefs.chat_on_map)
|
||||
speech_bubble_recipients.Add(M.client)
|
||||
var/image/I = image('icons/mob/talk.dmi', src, "[bubble_type][say_test(message)]", FLY_LAYER)
|
||||
I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
|
||||
INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_recipients, 30)
|
||||
|
||||
/mob/proc/binarycheck()
|
||||
return FALSE
|
||||
|
||||
/mob/living/can_speak(message) //For use outside of Say()
|
||||
if(can_speak_basic(message) && can_speak_vocal(message))
|
||||
return 1
|
||||
|
||||
/mob/living/proc/can_speak_basic(message, ignore_spam = FALSE) //Check BEFORE handling of xeno and ling channels
|
||||
if(client)
|
||||
if(client.prefs.muted & MUTE_IC)
|
||||
to_chat(src, "<span class='danger'>You cannot speak in IC (muted).</span>")
|
||||
return 0
|
||||
if(!ignore_spam && client.handle_spam_prevention(message,MUTE_IC))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
/mob/living/proc/can_speak_vocal(message) //Check AFTER handling of xeno and ling channels
|
||||
if(HAS_TRAIT(src, TRAIT_MUTE))
|
||||
return 0
|
||||
|
||||
if(is_muzzled())
|
||||
return 0
|
||||
|
||||
if(!IsVocal())
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
/mob/living/proc/get_key(message)
|
||||
var/key = copytext(message, 1, 2)
|
||||
if(key in GLOB.department_radio_prefixes)
|
||||
return lowertext(copytext(message, 2, 3))
|
||||
|
||||
/mob/living/proc/get_message_language(message)
|
||||
if(copytext(message, 1, 2) == ",")
|
||||
var/key = copytext(message, 2, 3)
|
||||
for(var/ld in GLOB.all_languages)
|
||||
var/datum/language/LD = ld
|
||||
if(initial(LD.key) == key)
|
||||
return LD
|
||||
return null
|
||||
|
||||
/mob/living/proc/treat_message(message)
|
||||
|
||||
if(HAS_TRAIT(src, TRAIT_UNINTELLIGIBLE_SPEECH))
|
||||
message = unintelligize(message)
|
||||
|
||||
if(derpspeech)
|
||||
message = derpspeech(message, stuttering)
|
||||
|
||||
if(stuttering)
|
||||
message = stutter(message)
|
||||
|
||||
if(slurring)
|
||||
message = slur(message,slurring)
|
||||
|
||||
if(cultslurring)
|
||||
message = cultslur(message)
|
||||
|
||||
message = capitalize(message)
|
||||
|
||||
return message
|
||||
|
||||
/mob/living/proc/radio(message, message_mode, list/spans, language)
|
||||
var/obj/item/implant/radio/imp = locate() in implants
|
||||
if(imp?.radio.on)
|
||||
if(message_mode == MODE_HEADSET)
|
||||
imp.radio.talk_into(src, message, , spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
if(message_mode == MODE_DEPARTMENT || message_mode in GLOB.radiochannels)
|
||||
imp.radio.talk_into(src, message, message_mode, spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
|
||||
switch(message_mode)
|
||||
if(MODE_WHISPER)
|
||||
return ITALICS
|
||||
if(MODE_R_HAND)
|
||||
for(var/obj/item/r_hand in get_held_items_for_side("r", all = TRUE))
|
||||
if (r_hand)
|
||||
return r_hand.talk_into(src, message, , spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
if(MODE_L_HAND)
|
||||
for(var/obj/item/l_hand in get_held_items_for_side("l", all = TRUE))
|
||||
if (l_hand)
|
||||
return l_hand.talk_into(src, message, , spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
|
||||
if(MODE_INTERCOM)
|
||||
for (var/obj/item/radio/intercom/I in view(1, null))
|
||||
I.talk_into(src, message, , spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
|
||||
if(MODE_BINARY)
|
||||
return ITALICS | REDUCE_RANGE //Does not return 0 since this is only reached by humans, not borgs or AIs.
|
||||
|
||||
return 0
|
||||
|
||||
/mob/living/say_mod(input, message_mode)
|
||||
. = ..()
|
||||
if(message_mode == MODE_WHISPER_CRIT)
|
||||
. = "[verb_whisper] in [p_their()] last breath"
|
||||
else if(message_mode != MODE_CUSTOM_SAY)
|
||||
if(message_mode == MODE_WHISPER)
|
||||
. = verb_whisper
|
||||
else if(stuttering)
|
||||
. = "stammers"
|
||||
else if(derpspeech)
|
||||
. = "gibbers"
|
||||
|
||||
/mob/living/whisper(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
|
||||
say("#[message]", bubble_type, spans, sanitize, language, ignore_spam, forced)
|
||||
|
||||
/mob/living/get_language_holder(shadow=TRUE)
|
||||
if(mind && shadow)
|
||||
// Mind language holders shadow mob holders.
|
||||
. = mind.get_language_holder()
|
||||
if(.)
|
||||
return .
|
||||
|
||||
. = ..()
|
||||
GLOBAL_LIST_INIT(department_radio_prefixes, list(":", "."))
|
||||
|
||||
GLOBAL_LIST_INIT(department_radio_keys, list(
|
||||
// Location
|
||||
MODE_KEY_R_HAND = MODE_R_HAND,
|
||||
MODE_KEY_L_HAND = MODE_L_HAND,
|
||||
MODE_KEY_INTERCOM = MODE_INTERCOM,
|
||||
|
||||
// Department
|
||||
MODE_KEY_DEPARTMENT = MODE_DEPARTMENT,
|
||||
RADIO_KEY_COMMAND = RADIO_CHANNEL_COMMAND,
|
||||
RADIO_KEY_SCIENCE = RADIO_CHANNEL_SCIENCE,
|
||||
RADIO_KEY_MEDICAL = RADIO_CHANNEL_MEDICAL,
|
||||
RADIO_KEY_ENGINEERING = RADIO_CHANNEL_ENGINEERING,
|
||||
RADIO_KEY_SECURITY = RADIO_CHANNEL_SECURITY,
|
||||
RADIO_KEY_SUPPLY = RADIO_CHANNEL_SUPPLY,
|
||||
RADIO_KEY_SERVICE = RADIO_CHANNEL_SERVICE,
|
||||
|
||||
// Faction
|
||||
RADIO_KEY_SYNDICATE = RADIO_CHANNEL_SYNDICATE,
|
||||
RADIO_KEY_CENTCOM = RADIO_CHANNEL_CENTCOM,
|
||||
|
||||
// Admin
|
||||
MODE_KEY_ADMIN = MODE_ADMIN,
|
||||
MODE_KEY_DEADMIN = MODE_DEADMIN,
|
||||
|
||||
// Misc
|
||||
RADIO_KEY_AI_PRIVATE = RADIO_CHANNEL_AI_PRIVATE, // AI Upload channel
|
||||
MODE_KEY_VOCALCORDS = MODE_VOCALCORDS, // vocal cords, used by Voice of God
|
||||
|
||||
|
||||
//kinda localization -- rastaf0
|
||||
//same keys as above, but on russian keyboard layout. This file uses cp1251 as encoding.
|
||||
// Location
|
||||
"ê" = MODE_R_HAND,
|
||||
"ä" = MODE_L_HAND,
|
||||
"ø" = MODE_INTERCOM,
|
||||
|
||||
// Department
|
||||
"ð" = MODE_DEPARTMENT,
|
||||
"ñ" = RADIO_CHANNEL_COMMAND,
|
||||
"ò" = RADIO_CHANNEL_SCIENCE,
|
||||
"ü" = RADIO_CHANNEL_MEDICAL,
|
||||
"ó" = RADIO_CHANNEL_ENGINEERING,
|
||||
"û" = RADIO_CHANNEL_SECURITY,
|
||||
"ã" = RADIO_CHANNEL_SUPPLY,
|
||||
"ì" = RADIO_CHANNEL_SERVICE,
|
||||
|
||||
// Faction
|
||||
"å" = RADIO_CHANNEL_SYNDICATE,
|
||||
"í" = RADIO_CHANNEL_CENTCOM,
|
||||
|
||||
// Admin
|
||||
"ç" = MODE_ADMIN,
|
||||
"â" = MODE_ADMIN,
|
||||
|
||||
// Misc
|
||||
"ù" = RADIO_CHANNEL_AI_PRIVATE,
|
||||
"÷" = MODE_VOCALCORDS
|
||||
))
|
||||
|
||||
/mob/living/proc/Ellipsis(original_msg, chance = 50, keep_words)
|
||||
if(chance <= 0)
|
||||
return "..."
|
||||
if(chance >= 100)
|
||||
return original_msg
|
||||
|
||||
var/list/words = splittext(original_msg," ")
|
||||
var/list/new_words = list()
|
||||
|
||||
var/new_msg = ""
|
||||
|
||||
for(var/w in words)
|
||||
if(prob(chance))
|
||||
new_words += "..."
|
||||
if(!keep_words)
|
||||
continue
|
||||
new_words += w
|
||||
|
||||
new_msg = jointext(new_words," ")
|
||||
|
||||
return new_msg
|
||||
|
||||
/mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
|
||||
var/static/list/crit_allowed_modes = list(MODE_WHISPER = TRUE, MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
|
||||
var/static/list/unconscious_allowed_modes = list(MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
|
||||
var/talk_key = get_key(message)
|
||||
|
||||
var/static/list/one_character_prefix = list(MODE_HEADSET = TRUE, MODE_ROBOT = TRUE, MODE_WHISPER = TRUE, MODE_SING = TRUE)
|
||||
|
||||
if(sanitize)
|
||||
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
|
||||
if(!message || message == "")
|
||||
return
|
||||
|
||||
var/datum/saymode/saymode = SSradio.saymodes[talk_key]
|
||||
var/message_mode = get_message_mode(message)
|
||||
var/original_message = message
|
||||
var/in_critical = InCritical()
|
||||
|
||||
if(one_character_prefix[message_mode])
|
||||
message = copytext_char(message, 2)
|
||||
else if(message_mode || saymode)
|
||||
message = copytext_char(message, 3)
|
||||
message = trim_left(message)
|
||||
|
||||
if(message_mode == MODE_ADMIN)
|
||||
if(client)
|
||||
client.cmd_admin_say(message)
|
||||
return
|
||||
|
||||
if(message_mode == MODE_DEADMIN)
|
||||
if(client)
|
||||
client.dsay(message)
|
||||
return
|
||||
|
||||
if(stat == DEAD)
|
||||
say_dead(original_message)
|
||||
return
|
||||
|
||||
if(check_emote(original_message) || !can_speak_basic(original_message, ignore_spam))
|
||||
return
|
||||
|
||||
if(in_critical)
|
||||
if(!(crit_allowed_modes[message_mode]))
|
||||
return
|
||||
else if(stat == UNCONSCIOUS)
|
||||
if(!(unconscious_allowed_modes[message_mode]))
|
||||
return
|
||||
|
||||
// language comma detection.
|
||||
var/datum/language/message_language = get_message_language(message)
|
||||
if(message_language)
|
||||
// No, you cannot speak in xenocommon just because you know the key
|
||||
if(can_speak_in_language(message_language))
|
||||
language = message_language
|
||||
message = copytext_char(message, 3)
|
||||
|
||||
// Trim the space if they said ",0 I LOVE LANGUAGES"
|
||||
message = trim_left(message)
|
||||
|
||||
if(!language)
|
||||
language = get_default_language()
|
||||
|
||||
// Detection of language needs to be before inherent channels, because
|
||||
// AIs use inherent channels for the holopad. Most inherent channels
|
||||
// ignore the language argument however.
|
||||
|
||||
if(saymode && !saymode.handle_message(src, message, language))
|
||||
return
|
||||
|
||||
if(!can_speak_vocal(message))
|
||||
to_chat(src, "<span class='warning'>You find yourself unable to speak!</span>")
|
||||
return
|
||||
|
||||
var/message_range = 7
|
||||
|
||||
var/succumbed = FALSE
|
||||
|
||||
var/fullcrit = InFullCritical()
|
||||
if((InCritical() && !fullcrit) || message_mode == MODE_WHISPER)
|
||||
message_range = 1
|
||||
message_mode = MODE_WHISPER
|
||||
src.log_talk(message, LOG_WHISPER)
|
||||
if(fullcrit)
|
||||
var/health_diff = round(-HEALTH_THRESHOLD_DEAD + health)
|
||||
// If we cut our message short, abruptly end it with a-..
|
||||
var/message_len = length_char(message)
|
||||
message = copytext_char(message, 1, health_diff) + "[message_len > health_diff ? "-.." : "..."]"
|
||||
message = Ellipsis(message, 10, 1)
|
||||
message_mode = MODE_WHISPER_CRIT
|
||||
succumbed = TRUE
|
||||
else
|
||||
src.log_talk(message, LOG_SAY, forced_by=forced)
|
||||
|
||||
message = treat_message(message) // unfortunately we still need this
|
||||
var/sigreturn = SEND_SIGNAL(src, COMSIG_MOB_SAY, args)
|
||||
if (sigreturn & COMPONENT_UPPERCASE_SPEECH)
|
||||
message = uppertext(message)
|
||||
if(!message)
|
||||
return
|
||||
|
||||
last_words = message
|
||||
|
||||
spans |= speech_span
|
||||
|
||||
if(language)
|
||||
var/datum/language/L = GLOB.language_datum_instances[language]
|
||||
spans |= L.spans
|
||||
|
||||
if(message_mode == MODE_SING)
|
||||
#if DM_VERSION < 513
|
||||
var/randomnote = "~"
|
||||
#else
|
||||
var/randomnote = pick("\u2669", "\u266A", "\u266B")
|
||||
#endif
|
||||
spans |= SPAN_SINGING
|
||||
message = "[randomnote] [message] [randomnote]"
|
||||
|
||||
var/radio_return = radio(message, message_mode, spans, language)
|
||||
if(radio_return & ITALICS)
|
||||
spans |= SPAN_ITALICS
|
||||
if(radio_return & REDUCE_RANGE)
|
||||
message_range = 1
|
||||
if(radio_return & NOPASS)
|
||||
return 1
|
||||
|
||||
//No screams in space, unless you're next to someone.
|
||||
var/turf/T = get_turf(src)
|
||||
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) //Thin air, let's italicise the message
|
||||
spans |= SPAN_ITALICS
|
||||
|
||||
send_speech(message, message_range, src, bubble_type, spans, language, message_mode)
|
||||
|
||||
if(succumbed)
|
||||
succumb()
|
||||
to_chat(src, compose_message(src, language, message, null, spans, message_mode))
|
||||
|
||||
return 1
|
||||
|
||||
/mob/living/compose_message(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, face_name = FALSE, atom/movable/source)
|
||||
. = ..()
|
||||
if(isliving(speaker))
|
||||
var/turf/sourceturf = get_turf(source)
|
||||
var/turf/T = get_turf(src)
|
||||
if(sourceturf && T && !(sourceturf in get_hear(5, T)))
|
||||
. = "<span class='small'>[.]</span>"
|
||||
|
||||
/mob/living/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
|
||||
. = ..()
|
||||
if(!client)
|
||||
return
|
||||
var/deaf_message
|
||||
var/deaf_type
|
||||
if(speaker != src)
|
||||
if(!radio_freq) //These checks have to be seperate, else people talking on the radio will make "You can't hear yourself!" appear when hearing people over the radio while deaf.
|
||||
deaf_message = "<span class='name'>[speaker]</span> [speaker.verb_say] something but you cannot hear [speaker.p_them()]."
|
||||
deaf_type = 1
|
||||
else
|
||||
deaf_message = "<span class='notice'>You can't hear yourself!</span>"
|
||||
deaf_type = 2 // Since you should be able to hear yourself without looking
|
||||
// Create map text prior to modifying message for goonchat
|
||||
if (client?.prefs.chat_on_map && stat != UNCONSCIOUS && (client.prefs.see_chat_non_mob || ismob(speaker)) && can_hear())
|
||||
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||
if (client?.prefs.radiosounds && stat != UNCONSCIOUS && can_hear() && radio_freq)
|
||||
playsound_local(src,'sound/voice/radio.ogg', 30, 1)
|
||||
|
||||
// Recompose message for AI hrefs, language incomprehension.
|
||||
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)
|
||||
message = hear_intercept(message, speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)
|
||||
|
||||
show_message(message, MSG_AUDIBLE, deaf_message, deaf_type)
|
||||
return message
|
||||
|
||||
/mob/living/proc/hear_intercept(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
|
||||
return message
|
||||
|
||||
/mob/living/send_speech(message, message_range = 6, obj/source = src, bubble_type = bubble_icon, list/spans, datum/language/message_language=null, message_mode)
|
||||
var/static/list/eavesdropping_modes = list(MODE_WHISPER = TRUE, MODE_WHISPER_CRIT = TRUE)
|
||||
var/eavesdrop_range = 0
|
||||
if(eavesdropping_modes[message_mode])
|
||||
eavesdrop_range = EAVESDROP_EXTRA_RANGE
|
||||
var/list/listening = get_hearers_in_view(message_range+eavesdrop_range, source)
|
||||
var/list/the_dead = list()
|
||||
var/list/yellareas //CIT CHANGE - adds the ability for yelling to penetrate walls and echo throughout areas
|
||||
if(!eavesdrop_range && say_test(message) == "2") //CIT CHANGE - ditto
|
||||
yellareas = get_areas_in_range(message_range*0.5, source) //CIT CHANGE - ditto
|
||||
for(var/_M in GLOB.player_list)
|
||||
var/mob/M = _M
|
||||
if(M.stat != DEAD) //not dead, not important
|
||||
if(yellareas) //CIT CHANGE - see above. makes yelling penetrate walls
|
||||
var/area/A = get_area(M) //CIT CHANGE - ditto
|
||||
if(istype(A) && A.ambientsounds != SPACE && A in yellareas) //CIT CHANGE - ditto
|
||||
listening |= M //CIT CHANGE - ditto
|
||||
continue
|
||||
if(!M.client || !client) //client is so that ghosts don't have to listen to mice
|
||||
continue
|
||||
if(get_dist(M, source) > 7 || M.z != z) //they're out of range of normal hearing
|
||||
if(eavesdropping_modes[message_mode] && !(M.client.prefs.chat_toggles & CHAT_GHOSTWHISPER)) //they're whispering and we have hearing whispers at any range off
|
||||
continue
|
||||
if(!(M.client.prefs.chat_toggles & CHAT_GHOSTEARS)) //they're talking normally and we have hearing at any range off
|
||||
continue
|
||||
listening |= M
|
||||
the_dead[M] = TRUE
|
||||
|
||||
var/eavesdropping
|
||||
var/eavesrendered
|
||||
if(eavesdrop_range)
|
||||
eavesdropping = stars(message)
|
||||
eavesrendered = compose_message(src, message_language, eavesdropping, null, spans, message_mode, FALSE, source)
|
||||
|
||||
var/rendered = compose_message(src, message_language, message, null, spans, message_mode, FALSE, source)
|
||||
for(var/_AM in listening)
|
||||
var/atom/movable/AM = _AM
|
||||
if(eavesdrop_range && get_dist(source, AM) > message_range && !(the_dead[AM]))
|
||||
AM.Hear(eavesrendered, src, message_language, eavesdropping, null, spans, message_mode, source)
|
||||
else
|
||||
AM.Hear(rendered, src, message_language, message, null, spans, message_mode, source)
|
||||
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_LIVING_SAY_SPECIAL, src, message)
|
||||
|
||||
//speech bubble
|
||||
var/list/speech_bubble_recipients = list()
|
||||
for(var/mob/M in listening)
|
||||
if(M.client && !M.client.prefs.chat_on_map)
|
||||
speech_bubble_recipients.Add(M.client)
|
||||
var/image/I = image('icons/mob/talk.dmi', src, "[bubble_type][say_test(message)]", FLY_LAYER)
|
||||
I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
|
||||
INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_recipients, 30)
|
||||
|
||||
/mob/proc/binarycheck()
|
||||
return FALSE
|
||||
|
||||
/mob/living/can_speak(message) //For use outside of Say()
|
||||
if(can_speak_basic(message) && can_speak_vocal(message))
|
||||
return 1
|
||||
|
||||
/mob/living/proc/can_speak_basic(message, ignore_spam = FALSE) //Check BEFORE handling of xeno and ling channels
|
||||
if(client)
|
||||
if(client.prefs.muted & MUTE_IC)
|
||||
to_chat(src, "<span class='danger'>You cannot speak in IC (muted).</span>")
|
||||
return 0
|
||||
if(!ignore_spam && client.handle_spam_prevention(message,MUTE_IC))
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
/mob/living/proc/can_speak_vocal(message) //Check AFTER handling of xeno and ling channels
|
||||
if(HAS_TRAIT(src, TRAIT_MUTE))
|
||||
return 0
|
||||
|
||||
if(is_muzzled())
|
||||
return 0
|
||||
|
||||
if(!IsVocal())
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
/mob/living/proc/get_key(message)
|
||||
var/key = message[1]
|
||||
if(key in GLOB.department_radio_prefixes)
|
||||
return lowertext(message[1 + length(key)])
|
||||
|
||||
/mob/living/proc/get_message_language(message)
|
||||
if(message[1] == ",")
|
||||
var/key = message[1 + length(message[1])]
|
||||
for(var/ld in GLOB.all_languages)
|
||||
var/datum/language/LD = ld
|
||||
if(initial(LD.key) == key)
|
||||
return LD
|
||||
return null
|
||||
|
||||
/mob/living/proc/treat_message(message)
|
||||
|
||||
if(HAS_TRAIT(src, TRAIT_UNINTELLIGIBLE_SPEECH))
|
||||
message = unintelligize(message)
|
||||
|
||||
if(derpspeech)
|
||||
message = derpspeech(message, stuttering)
|
||||
|
||||
if(stuttering)
|
||||
message = stutter(message)
|
||||
|
||||
if(slurring)
|
||||
message = slur(message,slurring)
|
||||
|
||||
if(cultslurring)
|
||||
message = cultslur(message)
|
||||
|
||||
message = capitalize(message)
|
||||
|
||||
return message
|
||||
|
||||
/mob/living/proc/radio(message, message_mode, list/spans, language)
|
||||
var/obj/item/implant/radio/imp = locate() in implants
|
||||
if(imp?.radio.on)
|
||||
if(message_mode == MODE_HEADSET)
|
||||
imp.radio.talk_into(src, message, , spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
if(message_mode == MODE_DEPARTMENT || message_mode in GLOB.radiochannels)
|
||||
imp.radio.talk_into(src, message, message_mode, spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
|
||||
switch(message_mode)
|
||||
if(MODE_WHISPER)
|
||||
return ITALICS
|
||||
if(MODE_R_HAND)
|
||||
for(var/obj/item/r_hand in get_held_items_for_side("r", all = TRUE))
|
||||
if (r_hand)
|
||||
return r_hand.talk_into(src, message, , spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
if(MODE_L_HAND)
|
||||
for(var/obj/item/l_hand in get_held_items_for_side("l", all = TRUE))
|
||||
if (l_hand)
|
||||
return l_hand.talk_into(src, message, , spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
|
||||
if(MODE_INTERCOM)
|
||||
for (var/obj/item/radio/intercom/I in view(1, null))
|
||||
I.talk_into(src, message, , spans, language)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
|
||||
if(MODE_BINARY)
|
||||
return ITALICS | REDUCE_RANGE //Does not return 0 since this is only reached by humans, not borgs or AIs.
|
||||
|
||||
return 0
|
||||
|
||||
/mob/living/say_mod(input, message_mode)
|
||||
. = ..()
|
||||
if(message_mode == MODE_WHISPER_CRIT)
|
||||
. = "[verb_whisper] in [p_their()] last breath"
|
||||
else if(message_mode != MODE_CUSTOM_SAY)
|
||||
if(message_mode == MODE_WHISPER)
|
||||
. = verb_whisper
|
||||
else if(stuttering)
|
||||
. = "stammers"
|
||||
else if(derpspeech)
|
||||
. = "gibbers"
|
||||
|
||||
/mob/living/whisper(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
|
||||
say("#[message]", bubble_type, spans, sanitize, language, ignore_spam, forced)
|
||||
|
||||
/mob/living/get_language_holder(shadow=TRUE)
|
||||
if(mind && shadow)
|
||||
// Mind language holders shadow mob holders.
|
||||
. = mind.get_language_holder()
|
||||
if(.)
|
||||
return .
|
||||
|
||||
. = ..()
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
..()
|
||||
|
||||
/mob/living/silicon/ai/get_message_mode(message)
|
||||
if(copytext(message, 1, 3) in list(":h", ":H", ".h", ".H", "#h", "#H"))
|
||||
var/static/regex/holopad_finder = regex(@"[:.#][hH]")
|
||||
if(holopad_finder.Find(message, 1, 1))
|
||||
return MODE_HOLOPAD
|
||||
else
|
||||
return ..()
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
/datum/paiCandidate/proc/savefile_path(mob/user)
|
||||
return "data/player_saves/[copytext(user.ckey, 1, 2)]/[user.ckey]/pai.sav"
|
||||
return "data/player_saves/[user.ckey[1]]/[user.ckey]/pai.sav"
|
||||
|
||||
/datum/paiCandidate/proc/savefile_save(mob/user)
|
||||
if(IsGuestKey(user.key))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -84,7 +84,7 @@
|
||||
if(!client)
|
||||
return
|
||||
|
||||
msg = copytext(msg, 1, MAX_MESSAGE_LEN)
|
||||
msg = copytext_char(msg, 1, MAX_MESSAGE_LEN)
|
||||
|
||||
if(type)
|
||||
if(type & MSG_VISUAL && eye_blind )//Vision related
|
||||
@@ -418,7 +418,7 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA
|
||||
set name = "Add Note"
|
||||
set category = "IC"
|
||||
|
||||
msg = copytext(msg, 1, MAX_MESSAGE_LEN)
|
||||
msg = copytext_char(msg, 1, MAX_MESSAGE_LEN)
|
||||
msg = sanitize(msg)
|
||||
|
||||
if(mind)
|
||||
|
||||
+530
-537
File diff suppressed because it is too large
Load Diff
+138
-138
@@ -1,138 +1,138 @@
|
||||
//Speech verbs.
|
||||
// the _keybind verbs uses "as text" versus "as text|null" to force a popup when pressed by a keybind.
|
||||
/mob/verb/say_typing_indicator()
|
||||
set name = "say_indicator"
|
||||
set hidden = TRUE
|
||||
set category = "IC"
|
||||
display_typing_indicator()
|
||||
var/message = input(usr, "", "say") as text|null
|
||||
// If they don't type anything just drop the message.
|
||||
clear_typing_indicator() // clear it immediately!
|
||||
if(!length(message))
|
||||
return
|
||||
return say_verb(message)
|
||||
|
||||
/mob/verb/say_verb(message as text)
|
||||
set name = "say"
|
||||
set category = "IC"
|
||||
if(!length(message))
|
||||
return
|
||||
if(GLOB.say_disabled) //This is here to try to identify lag problems
|
||||
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
|
||||
return
|
||||
clear_typing_indicator() // clear it immediately!
|
||||
say(message)
|
||||
|
||||
/mob/say_mod(input, message_mode)
|
||||
var/customsayverb = findtext(input, "*")
|
||||
if(customsayverb && message_mode != MODE_WHISPER_CRIT)
|
||||
message_mode = MODE_CUSTOM_SAY
|
||||
return lowertext(copytext(input, 1, customsayverb))
|
||||
else
|
||||
return ..()
|
||||
|
||||
/mob/verb/me_typing_indicator()
|
||||
set name = "me_indicator"
|
||||
set hidden = TRUE
|
||||
set category = "IC"
|
||||
display_typing_indicator()
|
||||
var/message = input(usr, "", "me") as message|null
|
||||
// If they don't type anything just drop the message.
|
||||
clear_typing_indicator() // clear it immediately!
|
||||
if(!length(message))
|
||||
return
|
||||
return me_verb(message)
|
||||
|
||||
/mob/verb/me_verb(message as message)
|
||||
set name = "me"
|
||||
set category = "IC"
|
||||
if(!length(message))
|
||||
return
|
||||
if(GLOB.say_disabled) //This is here to try to identify lag problems
|
||||
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
|
||||
return
|
||||
|
||||
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
|
||||
clear_typing_indicator() // clear it immediately!
|
||||
|
||||
usr.emote("me",1,message,TRUE)
|
||||
|
||||
|
||||
/mob/verb/whisper_verb(message as text)
|
||||
set name = "Whisper"
|
||||
set category = "IC"
|
||||
if(!length(message))
|
||||
return
|
||||
if(GLOB.say_disabled) //This is here to try to identify lag problems
|
||||
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
|
||||
return
|
||||
whisper(message)
|
||||
|
||||
/mob/proc/whisper(message, datum/language/language=null)
|
||||
say(message, language) //only living mobs actually whisper, everything else just talks
|
||||
|
||||
/mob/proc/say_dead(var/message)
|
||||
var/name = real_name
|
||||
var/alt_name = ""
|
||||
|
||||
if(GLOB.say_disabled) //This is here to try to identify lag problems
|
||||
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
|
||||
return
|
||||
|
||||
var/jb = jobban_isbanned(src, "OOC")
|
||||
if(QDELETED(src))
|
||||
return
|
||||
|
||||
if(jb)
|
||||
to_chat(src, "<span class='danger'>You have been banned from deadchat.</span>")
|
||||
return
|
||||
|
||||
|
||||
|
||||
if (src.client)
|
||||
if(src.client.prefs.muted & MUTE_DEADCHAT)
|
||||
to_chat(src, "<span class='danger'>You cannot talk in deadchat (muted).</span>")
|
||||
return
|
||||
|
||||
if(src.client.handle_spam_prevention(message,MUTE_DEADCHAT))
|
||||
return
|
||||
|
||||
var/mob/dead/observer/O = src
|
||||
if(isobserver(src) && O.deadchat_name)
|
||||
name = "[O.deadchat_name]"
|
||||
else
|
||||
if(mind && mind.name)
|
||||
name = "[mind.name]"
|
||||
else
|
||||
name = real_name
|
||||
if(name != real_name)
|
||||
alt_name = " (died as [real_name])"
|
||||
|
||||
var/spanned = say_quote(say_emphasis(message))
|
||||
message = emoji_parse(message)
|
||||
var/rendered = "<span class='game deadsay'><span class='prefix'>DEAD:</span> <span class='name'>[name]</span>[alt_name] <span class='message'>[emoji_parse(spanned)]</span></span>"
|
||||
log_talk(message, LOG_SAY, tag="DEAD")
|
||||
deadchat_broadcast(rendered, follow_target = src, speaker_key = key)
|
||||
|
||||
/mob/proc/check_emote(message)
|
||||
if(copytext(message, 1, 2) == "*")
|
||||
emote(copytext(message, 2), intentional = TRUE)
|
||||
return 1
|
||||
|
||||
/mob/proc/hivecheck()
|
||||
return 0
|
||||
|
||||
/mob/proc/lingcheck()
|
||||
return LINGHIVE_NONE
|
||||
|
||||
/mob/proc/get_message_mode(message)
|
||||
var/key = copytext(message, 1, 2)
|
||||
if(key == "#")
|
||||
return MODE_WHISPER
|
||||
else if(key == "%")
|
||||
return MODE_SING
|
||||
else if(key == ";")
|
||||
return MODE_HEADSET
|
||||
else if(length(message) > 2 && (key in GLOB.department_radio_prefixes))
|
||||
var/key_symbol = lowertext(copytext(message, 2, 3))
|
||||
return GLOB.department_radio_keys[key_symbol]
|
||||
//Speech verbs.
|
||||
// the _keybind verbs uses "as text" versus "as text|null" to force a popup when pressed by a keybind.
|
||||
/mob/verb/say_typing_indicator()
|
||||
set name = "say_indicator"
|
||||
set hidden = TRUE
|
||||
set category = "IC"
|
||||
display_typing_indicator()
|
||||
var/message = input(usr, "", "say") as text|null
|
||||
// If they don't type anything just drop the message.
|
||||
clear_typing_indicator() // clear it immediately!
|
||||
if(!length(message))
|
||||
return
|
||||
return say_verb(message)
|
||||
|
||||
/mob/verb/say_verb(message as text)
|
||||
set name = "say"
|
||||
set category = "IC"
|
||||
if(!length(message))
|
||||
return
|
||||
if(GLOB.say_disabled) //This is here to try to identify lag problems
|
||||
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
|
||||
return
|
||||
clear_typing_indicator() // clear it immediately!
|
||||
say(message)
|
||||
|
||||
/mob/say_mod(input, message_mode)
|
||||
var/customsayverb = findtext(input, "*")
|
||||
if(customsayverb && message_mode != MODE_WHISPER_CRIT)
|
||||
message_mode = MODE_CUSTOM_SAY
|
||||
return lowertext(copytext_char(input, 1, customsayverb))
|
||||
else
|
||||
return ..()
|
||||
|
||||
/mob/verb/me_typing_indicator()
|
||||
set name = "me_indicator"
|
||||
set hidden = TRUE
|
||||
set category = "IC"
|
||||
display_typing_indicator()
|
||||
var/message = input(usr, "", "me") as message|null
|
||||
// If they don't type anything just drop the message.
|
||||
clear_typing_indicator() // clear it immediately!
|
||||
if(!length(message))
|
||||
return
|
||||
return me_verb(message)
|
||||
|
||||
/mob/verb/me_verb(message as message)
|
||||
set name = "me"
|
||||
set category = "IC"
|
||||
if(!length(message))
|
||||
return
|
||||
if(GLOB.say_disabled) //This is here to try to identify lag problems
|
||||
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
|
||||
return
|
||||
|
||||
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
|
||||
clear_typing_indicator() // clear it immediately!
|
||||
|
||||
usr.emote("me",1,message,TRUE)
|
||||
|
||||
|
||||
/mob/verb/whisper_verb(message as text)
|
||||
set name = "Whisper"
|
||||
set category = "IC"
|
||||
if(!length(message))
|
||||
return
|
||||
if(GLOB.say_disabled) //This is here to try to identify lag problems
|
||||
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
|
||||
return
|
||||
whisper(message)
|
||||
|
||||
/mob/proc/whisper(message, datum/language/language=null)
|
||||
say(message, language) //only living mobs actually whisper, everything else just talks
|
||||
|
||||
/mob/proc/say_dead(var/message)
|
||||
var/name = real_name
|
||||
var/alt_name = ""
|
||||
|
||||
if(GLOB.say_disabled) //This is here to try to identify lag problems
|
||||
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
|
||||
return
|
||||
|
||||
var/jb = jobban_isbanned(src, "OOC")
|
||||
if(QDELETED(src))
|
||||
return
|
||||
|
||||
if(jb)
|
||||
to_chat(src, "<span class='danger'>You have been banned from deadchat.</span>")
|
||||
return
|
||||
|
||||
|
||||
|
||||
if (src.client)
|
||||
if(src.client.prefs.muted & MUTE_DEADCHAT)
|
||||
to_chat(src, "<span class='danger'>You cannot talk in deadchat (muted).</span>")
|
||||
return
|
||||
|
||||
if(src.client.handle_spam_prevention(message,MUTE_DEADCHAT))
|
||||
return
|
||||
|
||||
var/mob/dead/observer/O = src
|
||||
if(isobserver(src) && O.deadchat_name)
|
||||
name = "[O.deadchat_name]"
|
||||
else
|
||||
if(mind && mind.name)
|
||||
name = "[mind.name]"
|
||||
else
|
||||
name = real_name
|
||||
if(name != real_name)
|
||||
alt_name = " (died as [real_name])"
|
||||
|
||||
var/spanned = say_quote(say_emphasis(message))
|
||||
message = emoji_parse(message)
|
||||
var/rendered = "<span class='game deadsay'><span class='prefix'>DEAD:</span> <span class='name'>[name]</span>[alt_name] <span class='message'>[emoji_parse(spanned)]</span></span>"
|
||||
log_talk(message, LOG_SAY, tag="DEAD")
|
||||
deadchat_broadcast(rendered, follow_target = src, speaker_key = key)
|
||||
|
||||
/mob/proc/check_emote(message)
|
||||
if(message[1] == "*")
|
||||
emote(copytext(message, length(message[1]) + 1), intentional = TRUE)
|
||||
return TRUE
|
||||
|
||||
/mob/proc/hivecheck()
|
||||
return 0
|
||||
|
||||
/mob/proc/lingcheck()
|
||||
return LINGHIVE_NONE
|
||||
|
||||
/mob/proc/get_message_mode(message)
|
||||
var/key = message[1]
|
||||
if(key == "#")
|
||||
return MODE_WHISPER
|
||||
else if(key == "%")
|
||||
return MODE_SING
|
||||
else if(key == ";")
|
||||
return MODE_HEADSET
|
||||
else if((length(message) > (length(key) + 1)) && (key in GLOB.department_radio_prefixes))
|
||||
var/key_symbol = lowertext(message[length(key) + 1])
|
||||
return GLOB.department_radio_keys[key_symbol]
|
||||
|
||||
+13
-28
@@ -6,11 +6,10 @@
|
||||
set src in usr
|
||||
if(usr != src)
|
||||
to_chat(usr, "No.")
|
||||
var/msg = stripped_multiline_input(usr, "Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!", "Flavor Text", html_decode(flavor_text), MAX_MESSAGE_LEN*2, TRUE)
|
||||
var/msg = stripped_multiline_input(usr, "Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!", "Flavor Text", html_decode(flavor_text), MAX_MESSAGE_LEN, TRUE)
|
||||
|
||||
if(!isnull(msg))
|
||||
msg = copytext(msg, 1, MAX_MESSAGE_LEN)
|
||||
msg = html_encode(msg)
|
||||
if(msg)
|
||||
flavor_text = html_encode(msg)
|
||||
|
||||
flavor_text = msg
|
||||
|
||||
@@ -23,10 +22,10 @@
|
||||
if(flavor_text && flavor_text != "")
|
||||
// We are decoding and then encoding to not only get correct amount of characters, but also to prevent partial escaping characters being shown.
|
||||
var/msg = html_decode(replacetext(flavor_text, "\n", " "))
|
||||
if(length(msg) <= 40)
|
||||
if(length_char(msg) <= 40)
|
||||
return "<span class='notice'>[html_encode(msg)]</span>"
|
||||
else
|
||||
return "<span class='notice'>[html_encode(copytext(msg, 1, 37))]... <a href='?src=[REF(src)];flavor_more=1'>More...</span></a>"
|
||||
return "<span class='notice'>[html_encode(copytext_char(msg, 1, 37))]... <a href='?src=[REF(src)];flavor_more=1'>More...</span></a>"
|
||||
|
||||
/mob/proc/get_top_level_mob()
|
||||
if(istype(src.loc,/mob)&&src.loc!=src)
|
||||
@@ -53,17 +52,10 @@ proc/get_top_level_mob(var/mob/S)
|
||||
mob_type_blacklist_typecache = list(/mob/living/brain)
|
||||
|
||||
/datum/emote/living/subtle/proc/check_invalid(mob/user, input)
|
||||
. = TRUE
|
||||
if(copytext(input,1,5) == "says")
|
||||
if(stop_bad_mime.Find(input, 1, 1))
|
||||
to_chat(user, "<span class='danger'>Invalid emote.</span>")
|
||||
else if(copytext(input,1,9) == "exclaims")
|
||||
to_chat(user, "<span class='danger'>Invalid emote.</span>")
|
||||
else if(copytext(input,1,6) == "yells")
|
||||
to_chat(user, "<span class='danger'>Invalid emote.</span>")
|
||||
else if(copytext(input,1,5) == "asks")
|
||||
to_chat(user, "<span class='danger'>Invalid emote.</span>")
|
||||
else
|
||||
. = FALSE
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/emote/living/subtle/run_emote(mob/user, params, type_override = null)
|
||||
if(jobban_isbanned(user, "emote"))
|
||||
@@ -73,7 +65,7 @@ proc/get_top_level_mob(var/mob/S)
|
||||
to_chat(user, "You cannot send IC messages (muted).")
|
||||
return FALSE
|
||||
else if(!params)
|
||||
var/subtle_emote = copytext(sanitize(input("Choose an emote to display.") as message|null), 1, MAX_MESSAGE_LEN)
|
||||
var/subtle_emote = stripped_multiline_input("Choose an emote to display.", "Subtle", null, MAX_MESSAGE_LEN)
|
||||
if(subtle_emote && !check_invalid(user, subtle_emote))
|
||||
var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable")
|
||||
switch(type)
|
||||
@@ -124,17 +116,10 @@ proc/get_top_level_mob(var/mob/S)
|
||||
|
||||
|
||||
/datum/emote/living/subtler/proc/check_invalid(mob/user, input)
|
||||
. = TRUE
|
||||
if(copytext(input,1,5) == "says")
|
||||
if(stop_bad_mime.Find(input, 1, 1))
|
||||
to_chat(user, "<span class='danger'>Invalid emote.</span>")
|
||||
else if(copytext(input,1,9) == "exclaims")
|
||||
to_chat(user, "<span class='danger'>Invalid emote.</span>")
|
||||
else if(copytext(input,1,6) == "yells")
|
||||
to_chat(user, "<span class='danger'>Invalid emote.</span>")
|
||||
else if(copytext(input,1,5) == "asks")
|
||||
to_chat(user, "<span class='danger'>Invalid emote.</span>")
|
||||
else
|
||||
. = FALSE
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/emote/living/subtler/run_emote(mob/user, params, type_override = null)
|
||||
if(jobban_isbanned(user, "emote"))
|
||||
@@ -144,7 +129,7 @@ proc/get_top_level_mob(var/mob/S)
|
||||
to_chat(user, "You cannot send IC messages (muted).")
|
||||
return FALSE
|
||||
else if(!params)
|
||||
var/subtle_emote = copytext(sanitize(input("Choose an emote to display.") as message|null), 1, MAX_MESSAGE_LEN)
|
||||
var/subtle_emote = stripped_multiline_input(user, "Choose an emote to display.", "Subtler" , null, MAX_MESSAGE_LEN)
|
||||
if(subtle_emote && !check_invalid(user, subtle_emote))
|
||||
var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable")
|
||||
switch(type)
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
|
||||
// hash the original name?
|
||||
if(tr_flags & TR_HASHNAME)
|
||||
O.name = "monkey ([copytext(md5(real_name), 2, 6)])"
|
||||
O.real_name = "monkey ([copytext(md5(real_name), 2, 6)])"
|
||||
O.name = "monkey ([copytext_char(md5(real_name), 2, 6)])"
|
||||
O.real_name = "monkey ([copytext_char(md5(real_name), 2, 6)])"
|
||||
|
||||
//handle DNA and other attributes
|
||||
dna.transfer_identity(O)
|
||||
@@ -201,7 +201,7 @@
|
||||
dna.transfer_identity(O)
|
||||
O.updateappearance(mutcolor_update=1)
|
||||
|
||||
if(cmptext("monkey",copytext(O.dna.real_name,1,7)))
|
||||
if(findtext(O.dna.real_name, "monkey", 1, 7)) //7 == length("monkey") + 1
|
||||
O.real_name = random_unique_name(O.gender)
|
||||
O.dna.generate_unique_enzymes(O)
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user