mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2025-12-24 09:14:17 +00:00
820 lines
26 KiB
Plaintext
820 lines
26 KiB
Plaintext
/proc/issmall(A)
|
|
if(A && ishuman(A))
|
|
var/mob/living/carbon/human/H = A
|
|
if(H.dna.species && H.dna.species.is_small)
|
|
return 1
|
|
return 0
|
|
|
|
/proc/ispet(A)
|
|
if(isanimal(A))
|
|
var/mob/living/simple_animal/SA = A
|
|
if(SA.can_collar)
|
|
return 1
|
|
return 0
|
|
|
|
/mob/proc/get_screen_colour()
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
// OOC Colourblind setting takes priority over everything else.
|
|
if(client?.prefs)
|
|
switch(client.prefs.colourblind_mode)
|
|
if(COLOURBLIND_MODE_NONE)
|
|
. = null
|
|
|
|
/*
|
|
Also it goes without saying
|
|
|
|
For the love of god, do NOT mess with the matricies below.
|
|
The values may look arbitrary as hell, but they follow colour filtering rules
|
|
to accent specific colours and block out others, which helps different
|
|
forms of colourblindness. Its not perfect but it helps.
|
|
|
|
If you ever want to modify these matricies, test them with someone who
|
|
suffers that form of colourblindness, and ask if its an improvement or a hinderance.
|
|
I cannot stress this enough
|
|
|
|
-aa07
|
|
*/
|
|
if(COLOURBLIND_MODE_DEUTER)
|
|
// Red-green (green weak, deuteranopia)
|
|
// Below is a colour matrix to account for that
|
|
. = list(
|
|
1.8, 0, -0.14, 0,
|
|
-1.05, 1, 0.1, 0,
|
|
0.3, 0, 1, 0,
|
|
0, 0, 0, 1
|
|
) // Time spent creating this matrix: 1 hour 32 minutes
|
|
|
|
if(COLOURBLIND_MODE_PROT)
|
|
// Red-green (red weak, protanopia)
|
|
// Below is a colour matrix to account for that
|
|
. = list(
|
|
1, 0.475, 0.594, 0,
|
|
0, 0.482, -0.68, 0,
|
|
0, 0.044, 1.087, 0,
|
|
0, 0, 0, 1
|
|
) // Time spent creating this matrix: 57 minutes
|
|
|
|
if(COLOURBLIND_MODE_TRIT)
|
|
// Blue-yellow (tritanopia)
|
|
// Below is a colour matrix to account for that
|
|
. = list(
|
|
0.74, 0.07, 0, 0,
|
|
-0.405, 0.593, 0, 0,
|
|
0.665, 0.335, 1, 0,
|
|
0, 0, 0, 1
|
|
) // Time spent creating this matrix: 34 minutes
|
|
|
|
return
|
|
|
|
/mob/proc/update_client_colour(time = 10) //Update the mob's client.color with an animation the specified time in length.
|
|
if(!client) //No client_colour without client. If the player logs back in they'll be back through here anyway.
|
|
return
|
|
client.colour_transition(get_screen_colour(), time = time) //Get the colour matrix we're going to transition to depending on relevance (magic glasses first, eyes second).
|
|
|
|
/mob/living/carbon/human/get_screen_colour() //Fetch the colour matrix from wherever (e.g. eyes) so it can be compared to client.color.
|
|
. = ..()
|
|
if(.)
|
|
return .
|
|
|
|
var/obj/item/clothing/glasses/worn_glasses = glasses
|
|
var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes)
|
|
if(istype(worn_glasses) && worn_glasses.color_view) //Check to see if they got those magic glasses and they're augmenting the colour of what the wearer sees. If they're not, color_view should be null.
|
|
return worn_glasses.color_view
|
|
else if(eyes) //If they're not, check to see if their eyes got one of them there colour matrices. Will be null if eyes are robotic/the mob isn't colourblind and they have no default colour matrix.
|
|
return eyes.get_colourmatrix()
|
|
|
|
/**
|
|
* Flash up a color as an overlay on a player's screen, then fade back to normal.
|
|
*
|
|
* Arguments:
|
|
* * flash_color - The color to overlay on the screen.
|
|
* * flash_time - The time it takes for the color to fade back to normal.
|
|
*/
|
|
/mob/proc/flash_screen_color(flash_color, flash_time)
|
|
if(!client)
|
|
return
|
|
if(client?.prefs.colourblind_mode != COLOURBLIND_MODE_NONE)
|
|
return
|
|
client.color = flash_color
|
|
INVOKE_ASYNC(client, TYPE_PROC_REF(/client, colour_transition), get_screen_colour(), flash_time)
|
|
|
|
/proc/ismindshielded(A) //Checks to see if the person contains a mindshield implant, then checks that the implant is actually inside of them
|
|
for(var/obj/item/bio_chip/mindshield/L in A)
|
|
if(L && L.implanted)
|
|
return 1
|
|
return 0
|
|
|
|
/proc/isLivingSSD(mob/M)
|
|
return istype(M) && M.player_logged && M.stat != DEAD
|
|
|
|
/proc/isAntag(A)
|
|
if(isliving(A))
|
|
var/mob/living/L = A
|
|
if(L.mind?.special_role)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/proc/isNonCrewAntag(A)
|
|
if(!isAntag(A))
|
|
return 0
|
|
|
|
var/mob/living/carbon/C = A
|
|
var/special_role = C.mind.special_role
|
|
var/list/crew_roles = list(
|
|
SPECIAL_ROLE_BLOB,
|
|
SPECIAL_ROLE_CULTIST,
|
|
SPECIAL_ROLE_CHANGELING,
|
|
SPECIAL_ROLE_ERT,
|
|
SPECIAL_ROLE_HEAD_REV,
|
|
SPECIAL_ROLE_REV,
|
|
SPECIAL_ROLE_TRAITOR,
|
|
SPECIAL_ROLE_VAMPIRE,
|
|
SPECIAL_ROLE_VAMPIRE_THRALL,
|
|
SPECIAL_ROLE_DEATHSQUAD
|
|
)
|
|
if(special_role in crew_roles)
|
|
return 0
|
|
|
|
return 1
|
|
|
|
/proc/iscuffed(A)
|
|
if(iscarbon(A))
|
|
var/mob/living/carbon/C = A
|
|
if(C.handcuffed)
|
|
return 1
|
|
return 0
|
|
|
|
/proc/hassensorlevel(A, level)
|
|
var/mob/living/carbon/human/H = A
|
|
if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under))
|
|
var/obj/item/clothing/under/U = H.w_uniform
|
|
return U.sensor_mode >= level
|
|
return 0
|
|
|
|
/proc/getsensorlevel(A)
|
|
var/mob/living/carbon/human/H = A
|
|
if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under))
|
|
var/obj/item/clothing/under/U = H.w_uniform
|
|
return U.sensor_mode
|
|
return SUIT_SENSOR_OFF
|
|
|
|
/proc/offer_control(mob/M)
|
|
to_chat(M, "Control of your mob has been offered to dead players.")
|
|
log_admin("[key_name(usr)] has offered control of ([key_name(M)]) to ghosts.")
|
|
var/minhours = input(usr, "Minimum hours required to play [M]?", "Set Min Hrs", 10) as num
|
|
message_admins("[key_name_admin(usr)] has offered control of ([key_name_admin(M)]) to ghosts with [minhours] hrs playtime")
|
|
var/question = "Do you want to play as [M.real_name ? M.real_name : M.name][M.job ? " ([M.job])" : ""]"
|
|
if(alert("Do you want to show the antag status?","Show antag status","Yes","No") == "Yes")
|
|
question += ", [M.mind?.special_role ? M.mind?.special_role : "No special role"]"
|
|
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("[question]?", poll_time = 10 SECONDS, min_hours = minhours, source = M)
|
|
var/mob/dead/observer/theghost = null
|
|
|
|
if(length(candidates))
|
|
if(QDELETED(M))
|
|
return
|
|
theghost = pick(candidates)
|
|
to_chat(M, "Your mob has been taken over by a ghost!")
|
|
message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)])")
|
|
M.ghostize()
|
|
M.key = theghost.key
|
|
dust_if_respawnable(theghost)
|
|
else
|
|
to_chat(M, "There were no ghosts willing to take control.")
|
|
message_admins("No ghosts were willing to take control of [key_name_admin(M)])")
|
|
|
|
/proc/check_zone(zone)
|
|
if(!zone) return "chest"
|
|
switch(zone)
|
|
if("eyes")
|
|
zone = "head"
|
|
if("mouth")
|
|
zone = "head"
|
|
return zone
|
|
|
|
// Returns zone with a certain probability.
|
|
// If the probability misses, returns "chest" instead.
|
|
// If "chest" was passed in as zone, then on a "miss" will return "head", "l_arm", or "r_arm"
|
|
// Do not use this if someone is intentionally trying to hit a specific body part.
|
|
// Use get_zone_with_miss_chance() for that.
|
|
/proc/ran_zone(zone, probability = 80)
|
|
|
|
zone = check_zone(zone)
|
|
|
|
if(prob(probability))
|
|
return zone
|
|
|
|
var/t = rand(1, 18) // randomly pick a different zone, or maybe the same one
|
|
switch(t)
|
|
if(1) return "head"
|
|
if(2) return "chest"
|
|
if(3 to 4) return "l_arm"
|
|
if(5 to 6) return "l_hand"
|
|
if(7 to 8) return "r_arm"
|
|
if(9 to 10) return "r_hand"
|
|
if(11 to 12) return "l_leg"
|
|
if(13 to 14) return "l_foot"
|
|
if(15 to 16) return "r_leg"
|
|
if(17 to 18) return "r_foot"
|
|
|
|
return zone
|
|
|
|
/proc/above_neck(zone)
|
|
var/list/zones = list("head", "mouth", "eyes")
|
|
if(zones.Find(zone))
|
|
return 1
|
|
else
|
|
return 0
|
|
|
|
/proc/stars(n, pr)
|
|
if(pr == null)
|
|
pr = 25
|
|
if(pr <= 0)
|
|
return null
|
|
else
|
|
if(pr >= 100)
|
|
return n
|
|
var/te = n
|
|
var/t = ""
|
|
n = length(n)
|
|
var/p = null
|
|
p = 1
|
|
while(p <= n)
|
|
if((copytext(te, p, p + 1) == " " || prob(pr)))
|
|
t = "[t][copytext(te, p, p + 1)]"
|
|
else
|
|
t = "[t]*"
|
|
p++
|
|
return t
|
|
|
|
/proc/stars_all(list/message_pieces, pr)
|
|
for(var/datum/multilingual_say_piece/S in message_pieces)
|
|
S.message = stars(S.message, pr)
|
|
|
|
/proc/slur(phrase, list/slurletters = ("'"))//use a different list as an input if you want to make robots slur with $#@%! characters
|
|
phrase = html_decode(phrase)
|
|
var/leng=length(phrase)
|
|
var/counter=length(phrase)
|
|
var/newphrase=""
|
|
var/newletter=""
|
|
while(counter>=1)
|
|
newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2)
|
|
if(rand(1,3)==3)
|
|
if(lowertext(newletter)=="o") newletter="u"
|
|
if(lowertext(newletter)=="s") newletter="ch"
|
|
if(lowertext(newletter)=="a") newletter="ah"
|
|
if(lowertext(newletter)=="c") newletter="k"
|
|
switch(rand(1,15))
|
|
if(1,3,5,8) newletter="[lowertext(newletter)]"
|
|
if(2,4,6,15) newletter="[uppertext(newletter)]"
|
|
if(7) newletter+=pick(slurletters)
|
|
else
|
|
pass()
|
|
newphrase+="[newletter]"
|
|
counter-=1
|
|
return newphrase
|
|
|
|
/proc/stutter(n)
|
|
var/te = html_decode(n)
|
|
var/t = "" //placed before the message. Not really sure what it's for.
|
|
n = length(n) //length of the entire word
|
|
var/p = null
|
|
p = 1 //1 is the start of any word
|
|
while(p <= n) //while P, which starts at 1 is less or equal to N which is the length.
|
|
var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time.
|
|
if(prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z")))
|
|
if(prob(5))
|
|
n_letter = text("[n_letter]-[n_letter]-[n_letter]") //replaces the current letter with this instead.
|
|
else
|
|
if(prob(5))
|
|
n_letter = null
|
|
else
|
|
n_letter = text("[n_letter]-[n_letter]")
|
|
t = text("[t][n_letter]") //since the above is ran through for each letter, the text just adds up back to the original word.
|
|
p++ //for each letter p is increased to find where the next letter will be.
|
|
return sanitize(copytext(t,1,MAX_MESSAGE_LEN))
|
|
|
|
/proc/robostutter(n) //for robutts
|
|
var/te = html_decode(n)
|
|
var/t = ""//placed before the message. Not really sure what it's for.
|
|
n = length(n)//length of the entire word
|
|
var/p = null
|
|
p = 1//1 is the start of any word
|
|
while(p <= n)//while P, which starts at 1 is less or equal to N which is the length.
|
|
var/robotletter = pick("@", "!", "#", "$", "%", "&", "?") //for beep boop
|
|
var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time.
|
|
if(prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z")))
|
|
if(prob(10))
|
|
n_letter = text("[n_letter]-[robotletter]-[n_letter]-[n_letter]")//replaces the current letter with this instead.
|
|
else
|
|
if(prob(20))
|
|
n_letter = text("[n_letter]-[robotletter]-[n_letter]")
|
|
else
|
|
if(prob(5))
|
|
n_letter = robotletter
|
|
else
|
|
n_letter = text("[n_letter]-[n_letter]")
|
|
t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word.
|
|
p++//for each letter p is increased to find where the next letter will be.
|
|
return sanitize(copytext(t,1,MAX_MESSAGE_LEN))
|
|
|
|
|
|
/proc/Gibberish(t, p, replace_rate = 50)//t is the inputted message, and any value higher than 70 for p will cause letters to be replaced instead of added. replace_rate is the chance a letter is corrupted.
|
|
/* Turn text into complete gibberish! */
|
|
var/returntext = ""
|
|
for(var/i = 1, i <= length(t), i++)
|
|
|
|
var/letter = copytext(t, i, i+1)
|
|
if(prob(replace_rate))
|
|
if(p >= 70)
|
|
letter = ""
|
|
|
|
for(var/j = 1, j <= rand(0, 2), j++)
|
|
letter += pick("#","@","*","&","%","$","/", ";","*","*","*","*","*","*","*")
|
|
|
|
returntext += letter
|
|
|
|
return returntext
|
|
|
|
/proc/Gibberish_all(list/message_pieces, p, replace_rate)
|
|
for(var/datum/multilingual_say_piece/S in message_pieces)
|
|
S.message = Gibberish(S.message, p, replace_rate)
|
|
|
|
|
|
/proc/muffledspeech(phrase)
|
|
phrase = html_decode(phrase)
|
|
var/leng=length(phrase)
|
|
var/counter=length(phrase)
|
|
var/newphrase=""
|
|
var/newletter=""
|
|
while(counter>=1)
|
|
newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2)
|
|
if(newletter in list(" ", "!", "?", ".", ","))
|
|
// Skip these
|
|
counter -= 1
|
|
continue
|
|
|
|
else if(lowertext(newletter) in list("a", "e", "i", "o", "u", "y"))
|
|
newletter = "ph"
|
|
|
|
else
|
|
newletter = "m"
|
|
|
|
newphrase += "[newletter]"
|
|
counter -= 1
|
|
return newphrase
|
|
|
|
/proc/muffledspeech_all(list/message_pieces)
|
|
for(var/datum/multilingual_say_piece/S in message_pieces)
|
|
S.message = muffledspeech(S.message)
|
|
|
|
|
|
///Shake the camera of the person viewing the mob SO REAL!
|
|
/proc/shake_camera(mob/M, duration, strength = 1)
|
|
if(!M || !M.client || duration < 1)
|
|
return
|
|
var/client/C = M.client
|
|
var/oldx = C.pixel_x
|
|
var/oldy = C.pixel_y
|
|
var/max = strength * world.icon_size
|
|
var/min = -(strength * world.icon_size)
|
|
|
|
for(var/i in 0 to duration - 1)
|
|
if(i == 0)
|
|
animate(C, pixel_x = rand(min, max), pixel_y = rand(min, max), time = 1)
|
|
else
|
|
animate(pixel_x = rand(min, max), pixel_y = rand(min, max), time = 1)
|
|
animate(pixel_x = oldx, pixel_y = oldy, time = 1)
|
|
|
|
|
|
/proc/findname(msg)
|
|
for(var/mob/M in GLOB.mob_list)
|
|
if(M.real_name == text("[msg]"))
|
|
return 1
|
|
return 0
|
|
|
|
|
|
/mob/proc/abiotic(full_body = 0)
|
|
if(full_body && ((l_hand && !(l_hand.flags & ABSTRACT)) || (r_hand && !(r_hand.flags & ABSTRACT)) || (back || wear_mask)))
|
|
return 1
|
|
|
|
if((l_hand && !(l_hand.flags & ABSTRACT)) || (r_hand && !(r_hand.flags & ABSTRACT)))
|
|
return 1
|
|
|
|
return 0
|
|
|
|
//converts intent-strings into numbers and back
|
|
/proc/intent_numeric(argument)
|
|
if(istext(argument))
|
|
switch(argument)
|
|
if(INTENT_HELP) return 0
|
|
if(INTENT_DISARM) return 1
|
|
if(INTENT_GRAB) return 2
|
|
else return 3
|
|
else
|
|
switch(argument)
|
|
if(0) return INTENT_HELP
|
|
if(1) return INTENT_DISARM
|
|
if(2) return INTENT_GRAB
|
|
else return INTENT_HARM
|
|
|
|
//change a mob's act-intent. Input the intent as a string such as "help" or use "right"/"left
|
|
/mob/verb/a_intent_change(input as text)
|
|
set name = "a-intent"
|
|
set hidden = 1
|
|
|
|
if(can_change_intents)
|
|
if(ishuman(src) || isalienadult(src) || isbrain(src))
|
|
switch(input)
|
|
if(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM)
|
|
a_intent = input
|
|
if("right")
|
|
a_intent = intent_numeric((intent_numeric(a_intent)+1) % 4)
|
|
if("left")
|
|
a_intent = intent_numeric((intent_numeric(a_intent)+3) % 4)
|
|
if(hud_used && hud_used.action_intent)
|
|
hud_used.action_intent.icon_state = "[a_intent]"
|
|
|
|
else if(isrobot(src) || islarva(src) || isanimal(src) || isAI(src))
|
|
switch(input)
|
|
if(INTENT_HELP)
|
|
a_intent = INTENT_HELP
|
|
if(INTENT_HARM)
|
|
a_intent = INTENT_HARM
|
|
if("right","left")
|
|
a_intent = intent_numeric(intent_numeric(a_intent) - 3)
|
|
if(hud_used && hud_used.action_intent)
|
|
if(a_intent == INTENT_HARM)
|
|
hud_used.action_intent.icon_state = "harm"
|
|
else
|
|
hud_used.action_intent.icon_state = "help"
|
|
|
|
|
|
/mob/living/verb/mob_sleep()
|
|
set name = "Sleep"
|
|
set category = "IC"
|
|
|
|
if(IsSleeping())
|
|
to_chat(src, "<span class='notice'>You are already sleeping.</span>")
|
|
return
|
|
if(alert(src, "You sure you want to sleep for a while?", "Sleep", "Yes", "No") == "Yes")
|
|
SetSleeping(40 SECONDS, voluntary = TRUE) //Short nap
|
|
|
|
/mob/living/verb/rest()
|
|
set name = "Rest"
|
|
set category = "IC"
|
|
|
|
|
|
resting = !resting // this happens before the do_mob so that you can stay resting if you are stunned.
|
|
|
|
if(resting)
|
|
to_chat(src, "<span class='notice'>You are now trying to rest.</span>")
|
|
else
|
|
to_chat(src, "<span class='notice'>You are now trying to get up.</span>")
|
|
|
|
if(!do_mob(src, src, 1 SECONDS, extra_checks = list(CALLBACK(src, TYPE_PROC_REF(/mob/living, cannot_stand))), only_use_extra_checks = TRUE))
|
|
return
|
|
|
|
if(resting)
|
|
lay_down()
|
|
else
|
|
stand_up()
|
|
|
|
/proc/get_multitool(mob/user as mob)
|
|
// Get tool
|
|
var/obj/item/multitool/P
|
|
if(isrobot(user) || ishuman(user))
|
|
P = user.get_active_hand()
|
|
else if(isAI(user))
|
|
var/mob/living/silicon/ai/AI=user
|
|
P = AI.aiMulti
|
|
|
|
if(!istype(P))
|
|
return null
|
|
return P
|
|
|
|
/proc/get_both_hands(mob/living/carbon/M)
|
|
return list(M.l_hand, M.r_hand)
|
|
|
|
|
|
//Direct dead say used both by emote and say
|
|
//It is somewhat messy. I don't know what to do.
|
|
//I know you can't see the change, but I rewrote the name code. It is significantly less messy now
|
|
/proc/say_dead_direct(message, mob/subject = null)
|
|
var/name
|
|
var/keyname
|
|
if(subject && subject.client)
|
|
var/client/C = subject.client
|
|
keyname = (C.holder && C.holder.fakekey) ? C.holder.fakekey : C.key
|
|
if(C.mob) //Most of the time this is the dead/observer mob; we can totally use him if there is no better name
|
|
var/mindname
|
|
var/realname = C.mob.real_name
|
|
if(C.mob.mind)
|
|
mindname = C.mob.mind.name
|
|
if(C.mob.mind.original_mob_name)
|
|
realname = C.mob.mind.original_mob_name
|
|
if(mindname && mindname != realname)
|
|
name = "[realname] died as [mindname]"
|
|
else
|
|
name = realname
|
|
|
|
for(var/obj/item/radio/deadsay_radio_system as anything in GLOB.deadsay_radio_systems)
|
|
deadsay_radio_system.attempt_send_deadsay_message(subject, message)
|
|
|
|
for(var/mob/M in GLOB.player_list)
|
|
if(M.client && ((!isnewplayer(M) && M.stat == DEAD) || check_rights(R_ADMIN|R_MOD,0,M)) && M.get_preference(PREFTOGGLE_CHAT_DEAD))
|
|
var/follow
|
|
var/lname
|
|
if(subject)
|
|
if(subject != M)
|
|
follow = "([ghost_follow_link(subject, ghost=M)]) "
|
|
if(M.stat != DEAD && check_rights(R_ADMIN|R_MOD,0,M))
|
|
follow = "([admin_jump_link(subject)]) "
|
|
var/mob/dead/observer/DM
|
|
if(isobserver(subject))
|
|
DM = subject
|
|
if(check_rights(R_ADMIN|R_MOD, FALSE, M)) // What admins see
|
|
lname = "[keyname][(DM?.client.prefs.toggles2 & PREFTOGGLE_2_ANON) ? (@"[ANON]") : (DM ? "" : "^")] ([name])"
|
|
//lname = "[keyname][(DM?.client.prefs.toggles2 & PREFTOGGLE_2_ANON) ? TRUE : (DM ? "" : "^")] ([name])"
|
|
else
|
|
if(DM?.client.prefs.toggles2 & PREFTOGGLE_2_ANON) // If the person is actually observer they have the option to be anonymous
|
|
lname = "<i>Anon</i> ([name])"
|
|
else if(DM) // Non-anons
|
|
lname = "[keyname] ([name])"
|
|
else // Everyone else (dead people who didn't ghost yet, etc.)
|
|
lname = name
|
|
lname = "<span class='name'>[lname]</span> "
|
|
to_chat(M, "<span class='deadsay'>[lname][follow][message]</span>")
|
|
|
|
/proc/notify_ghosts(message, ghost_sound = null, enter_link = null, title = null, atom/source = null, image/alert_overlay = null, flashwindow = TRUE, action = NOTIFY_JUMP, role = null) //Easy notification of ghosts.
|
|
for(var/mob/O in GLOB.player_list)
|
|
if(O.client && HAS_TRAIT(O, TRAIT_RESPAWNABLE) && (!role || (role in O.client.prefs.be_special)))
|
|
to_chat(O, "<span class='ghostalert'>[message][(enter_link) ? " [enter_link]" : ""]</span>")
|
|
if(ghost_sound)
|
|
SEND_SOUND(O, sound(ghost_sound))
|
|
if(flashwindow)
|
|
window_flash(O.client)
|
|
if(source)
|
|
var/obj/screen/alert/notify_action/A = O.throw_alert("\ref[source]_notify_action", /obj/screen/alert/notify_action)
|
|
if(A)
|
|
if(O.client.prefs && O.client.prefs.UI_style)
|
|
A.icon = ui_style2icon(O.client.prefs.UI_style)
|
|
if(title)
|
|
A.name = title
|
|
A.desc = message
|
|
A.action = action
|
|
A.target = source
|
|
if(!alert_overlay)
|
|
// if the icon is greater than 32x, use its base icon instead of its full actual appearance
|
|
// otherwise, if we don't do this, it totally messes with the viewport
|
|
var/icon/atom_icon = icon(source.icon, source.icon_state)
|
|
var/width = atom_icon.Width()
|
|
var/height = atom_icon.Height()
|
|
if(width > 32 || height > 32)
|
|
atom_icon.Scale(32, 32)
|
|
atom_icon.Crop(0, 0, 32, 32)
|
|
A.overlays += atom_icon
|
|
else
|
|
var/image/appearance = image(source)
|
|
appearance.layer = FLOAT_LAYER
|
|
appearance.plane = FLOAT_PLANE
|
|
A.overlays += appearance
|
|
else
|
|
alert_overlay.layer = FLOAT_LAYER
|
|
alert_overlay.plane = FLOAT_PLANE
|
|
A.overlays += alert_overlay
|
|
|
|
/**
|
|
* Checks if a mob's ghost can reenter their body or not. Used to check for DNR or AntagHUD.
|
|
*
|
|
* Returns FALSE if there is a ghost, and it can't reenter the body. Returns TRUE otherwise.
|
|
*/
|
|
/mob/proc/ghost_can_reenter()
|
|
var/mob/dead/observer/ghost = get_ghost(TRUE)
|
|
if(ghost && !ghost.can_reenter_corpse)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/mob/proc/switch_to_camera(obj/machinery/camera/C)
|
|
if(!C.can_use() || incapacitated() || (get_dist(C, src) > 1 || machine != src || !has_vision()))
|
|
return FALSE
|
|
check_eye(src)
|
|
return TRUE
|
|
|
|
/mob/proc/rename_character(oldname, newname)
|
|
if(!newname)
|
|
return FALSE
|
|
real_name = newname
|
|
name = newname
|
|
if(mind)
|
|
mind.name = newname
|
|
if(dna)
|
|
dna.real_name = real_name
|
|
|
|
if(oldname)
|
|
//update the datacore records! This is goig to be a bit costly.
|
|
for(var/list/L in list(GLOB.data_core.general, GLOB.data_core.medical, GLOB.data_core.security, GLOB.data_core.locked))
|
|
for(var/datum/data/record/R in L)
|
|
if(R.fields["name"] == oldname)
|
|
R.fields["name"] = newname
|
|
break
|
|
|
|
//update our pda and id if we have them on our person
|
|
var/list/searching = GetAllContents(searchDepth = 3)
|
|
var/search_id = TRUE
|
|
var/search_pda = TRUE
|
|
|
|
for(var/A in searching)
|
|
if(search_id && istype(A,/obj/item/card/id))
|
|
var/obj/item/card/id/ID = A
|
|
if(ID.registered_name == oldname)
|
|
ID.registered_name = newname
|
|
ID.name = "[newname]'s ID Card ([ID.assignment])"
|
|
ID.RebuildHTML()
|
|
if(!search_pda)
|
|
break
|
|
search_id = FALSE
|
|
|
|
else if(search_pda && istype(A,/obj/item/pda))
|
|
var/obj/item/pda/PDA = A
|
|
if(PDA.owner == oldname)
|
|
PDA.owner = newname
|
|
PDA.name = "PDA-[newname] ([PDA.ownjob])"
|
|
if(!search_id)
|
|
break
|
|
search_pda = FALSE
|
|
|
|
//Fixes renames not being reflected in objective text
|
|
if(mind)
|
|
for(var/datum/objective/objective in GLOB.all_objectives)
|
|
if(objective.target != mind)
|
|
continue
|
|
objective.update_explanation_text()
|
|
return TRUE
|
|
|
|
/mob/proc/rename_self(role, allow_numbers = FALSE, force = FALSE)
|
|
spawn(0)
|
|
var/oldname = real_name
|
|
|
|
var/time_passed = world.time
|
|
var/newname
|
|
|
|
for(var/i=1,i<=3,i++) //we get 3 attempts to pick a suitable name.
|
|
if(force)
|
|
newname = clean_input("Pick a new name.", "Name Change", oldname, src)
|
|
else
|
|
newname = clean_input("You are a [role]. Would you like to change your name to something else? (You have 3 minutes to select a new name.)", "Name Change", oldname, src)
|
|
if(((world.time - time_passed) > 1800) && !force)
|
|
alert(src, "Unfortunately, more than 3 minutes have passed for selecting your name. If you are a robot, use the Namepick verb; otherwise, adminhelp.", "Name Change")
|
|
return //took too long
|
|
newname = reject_bad_name(newname,allow_numbers) //returns null if the name doesn't meet some basic requirements. Tidies up a few other things like bad-characters.
|
|
|
|
for(var/mob/living/M in GLOB.player_list)
|
|
if(M == src)
|
|
continue
|
|
if(!newname || M.real_name == newname)
|
|
newname = null
|
|
break
|
|
if(newname)
|
|
break //That's a suitable name!
|
|
to_chat(src, "Sorry, that [role]-name wasn't appropriate, please try another. It's possibly too long/short, has bad characters or is already taken.")
|
|
|
|
if(!newname) //we'll stick with the oldname then
|
|
return
|
|
|
|
rename_character(oldname, newname)
|
|
|
|
/proc/cultslur(n) // Inflicted on victims of a stun talisman
|
|
var/phrase = html_decode(n)
|
|
var/leng = length(phrase)
|
|
var/counter=length(phrase)
|
|
var/newphrase=""
|
|
var/newletter=""
|
|
while(counter>=1)
|
|
newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2)
|
|
if(rand(1,2)==2)
|
|
if(lowertext(newletter)=="o")
|
|
newletter="u"
|
|
if(lowertext(newletter)=="t")
|
|
newletter="ch"
|
|
if(lowertext(newletter)=="a")
|
|
newletter="ah"
|
|
if(lowertext(newletter)=="u")
|
|
newletter="oo"
|
|
if(lowertext(newletter)=="c")
|
|
newletter=" NAR "
|
|
if(lowertext(newletter)=="s")
|
|
newletter=" SIE "
|
|
if(rand(1,4)==4)
|
|
if(newletter==" ")
|
|
newletter=" no hope... "
|
|
if(newletter=="H")
|
|
newletter=" IT COMES... "
|
|
|
|
switch(rand(1,15))
|
|
if(1)
|
|
newletter="'"
|
|
if(2)
|
|
newletter+="agn"
|
|
if(3)
|
|
newletter="fth"
|
|
if(4)
|
|
newletter="nglu"
|
|
if(5)
|
|
newletter="glor"
|
|
else
|
|
pass()
|
|
|
|
newphrase+="[newletter]";counter-=1
|
|
return newphrase
|
|
|
|
// Why does this exist?
|
|
/mob/proc/get_preference(toggleflag)
|
|
if(!client)
|
|
return FALSE
|
|
if(!client.prefs)
|
|
. = FALSE
|
|
CRASH("Mob '[src]', ckey '[ckey]' is missing a prefs datum on the client!")
|
|
// Cast to 1/0
|
|
return !!(client.prefs.toggles & toggleflag)
|
|
|
|
/**
|
|
* Helper proc to determine if a mob can use emotes that make sound or not.
|
|
*/
|
|
/mob/proc/can_use_audio_emote(intentional)
|
|
var/emote_status = intentional ? audio_emote_cd_status : audio_emote_unintentional_cd_status
|
|
switch(emote_status)
|
|
if(EMOTE_INFINITE) // Spam those emotes
|
|
return TRUE
|
|
if(EMOTE_ADMIN_BLOCKED) // Cooldown emotes were disabled by an admin, prevent use
|
|
return FALSE
|
|
if(EMOTE_ON_COOLDOWN) // Already on CD, prevent use
|
|
return FALSE
|
|
if(EMOTE_READY)
|
|
return TRUE
|
|
|
|
CRASH("Invalid emote type")
|
|
|
|
/**
|
|
* Start the cooldown for an emote that plays audio.
|
|
*
|
|
* Arguments:
|
|
* * intentional - Whether or not the user deliberately triggered this emote.
|
|
* * cooldown - The amount of time that should be waited before any other audio emote can fire.
|
|
*/
|
|
/mob/proc/start_audio_emote_cooldown(intentional, cooldown = AUDIO_EMOTE_COOLDOWN)
|
|
if(!can_use_audio_emote(intentional))
|
|
return FALSE
|
|
|
|
var/cooldown_source = intentional ? audio_emote_cd_status : audio_emote_unintentional_cd_status
|
|
|
|
if(cooldown_source == EMOTE_READY)
|
|
// we do have to juggle between cooldowns a little bit, but this lets us keep them on separate cooldowns so
|
|
// a user screaming every five seconds doesn't prevent them from sneezing.
|
|
if(intentional)
|
|
audio_emote_cd_status = EMOTE_ON_COOLDOWN // Starting cooldown
|
|
else
|
|
audio_emote_unintentional_cd_status = EMOTE_ON_COOLDOWN
|
|
addtimer(CALLBACK(src, PROC_REF(on_audio_emote_cooldown_end), intentional), cooldown)
|
|
return TRUE // proceed with emote
|
|
|
|
|
|
/mob/proc/on_audio_emote_cooldown_end(intentional)
|
|
if(intentional)
|
|
if(audio_emote_cd_status == EMOTE_ON_COOLDOWN)
|
|
// only reset to ready if we're in a cooldown state
|
|
audio_emote_cd_status = EMOTE_READY
|
|
else
|
|
if(audio_emote_unintentional_cd_status == EMOTE_ON_COOLDOWN)
|
|
audio_emote_unintentional_cd_status = EMOTE_READY
|
|
|
|
/proc/stat_to_text(stat)
|
|
switch(stat)
|
|
if(CONSCIOUS)
|
|
return "conscious"
|
|
if(UNCONSCIOUS)
|
|
return "unconscious"
|
|
if(DEAD)
|
|
return "dead"
|
|
|
|
/mob/proc/attempt_listen_to_deadsay()
|
|
|
|
|
|
/mob/proc/is_roundstart_observer()
|
|
return (ckey in GLOB.roundstart_observer_keys)
|
|
|
|
/mob/proc/has_ahudded()
|
|
return (ckey in GLOB.antag_hud_users)
|
|
|
|
/// Proc to PROPERLY set mob invisibility, huds gotta get set too!
|
|
/mob/proc/set_invisible(invis_value)
|
|
if(invis_value)
|
|
invisibility = invis_value
|
|
else
|
|
invisibility = initial(invisibility)
|
|
for(var/hud in hud_possible)
|
|
var/image/actual_hud = hud_list[hud]
|
|
if(invis_value)
|
|
actual_hud.invisibility = invis_value
|
|
else
|
|
actual_hud.invisibility = initial(actual_hud.invisibility)
|