mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-20 11:31:12 +01:00
Refactored the projectile code, mostly in line with TG's now. Refactored various procs that are used or depends on it. Projectiles can now ricochet if enabled to. Damage falloffs with distance. Homing projectiles can now have accuracy falloff with distance. Projectiles have a maximum range. Muzzle flash is configurable per projectile. Impact effect of the projectile is configurable per projectile. Accuracy decreases with distance. Projectiles work with signals and emits them, for easy hooking up from other parts of the code. Meatshielding is now less effective . Impact sound is now configurable per projectile. High risk.
1331 lines
35 KiB
Plaintext
1331 lines
35 KiB
Plaintext
// fun if you want to typecast humans/monkeys/etc without writing long path-filled lines.
|
|
|
|
/proc/issmall(A)
|
|
if(A && istype(A, /mob/living))
|
|
var/mob/living/L = A
|
|
return L.mob_size <= MOB_SMALL
|
|
return 0
|
|
|
|
/mob/living/proc/isSynthetic()
|
|
return 0
|
|
|
|
/mob/living/carbon/human/isSynthetic()
|
|
// If they are 100% robotic, they count as synthetic.
|
|
for(var/obj/item/organ/external/E in organs)
|
|
if(!(E.status & ORGAN_ROBOT))
|
|
return 0
|
|
return 1
|
|
|
|
/mob/living/carbon/human/proc/isFBP()
|
|
return species && (species.appearance_flags & HAS_FBP)
|
|
|
|
/mob/living/carbon/human/proc/isShell()
|
|
return species && (species.name in list(SPECIES_IPC_SHELL, SPECIES_IPC_SHELL_ROGUE))
|
|
|
|
/proc/isMMI(A)
|
|
if(isbrain(A))
|
|
var/mob/living/carbon/brain/B = A
|
|
return istype(B.container, /obj/item/device/mmi)
|
|
|
|
/mob/living/bot/isSynthetic()
|
|
return 1
|
|
|
|
/mob/living/silicon/isSynthetic()
|
|
return 1
|
|
|
|
/mob/proc/isMonkey()
|
|
return 0
|
|
|
|
/mob/living/carbon/human/isMonkey()
|
|
return istype(species, /datum/species/monkey)
|
|
|
|
|
|
/proc/ishuman_species(A)
|
|
if(istype(A, /mob/living/carbon/human) && (A:get_species() == SPECIES_HUMAN))
|
|
return 1
|
|
return 0
|
|
|
|
/proc/isoffworlder(A)
|
|
if(ishuman(A))
|
|
var/mob/living/carbon/human/H = A
|
|
if(H.get_species() == SPECIES_HUMAN_OFFWORLD)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/proc/isgolem(A)
|
|
if(ishuman(A))
|
|
var/mob/living/carbon/human/H = A
|
|
if(istype(H.species, /datum/species/golem))
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/proc/isunathi(A)
|
|
if(ishuman(A))
|
|
var/mob/living/carbon/human/H = A
|
|
switch(H.get_species())
|
|
if (SPECIES_UNATHI)
|
|
return 1
|
|
if (SPECIES_ZOMBIE_UNATHI)
|
|
return 1
|
|
return 0
|
|
|
|
/proc/istajara(A)
|
|
if(istype(A, /mob/living/carbon/human))
|
|
switch(A:get_species())
|
|
if (SPECIES_TAJARA)
|
|
return 1
|
|
if(SPECIES_TAJARA_ZHAN)
|
|
return 1
|
|
if(SPECIES_TAJARA_MSAI)
|
|
return 1
|
|
if (SPECIES_ZOMBIE_TAJARA)
|
|
return 1
|
|
if (SPECIES_TAJARA_TESLA_BODY)
|
|
return 1
|
|
return 0
|
|
|
|
/proc/isskrell(A)
|
|
if(istype(A, /mob/living/carbon/human))
|
|
switch(A:get_species())
|
|
if (SPECIES_SKRELL)
|
|
return 1
|
|
if (SPECIES_SKRELL_AXIORI)
|
|
return 1
|
|
if (SPECIES_ZOMBIE_SKRELL)
|
|
return 1
|
|
return 0
|
|
|
|
/proc/isvaurca(A, var/isbreeder = FALSE)
|
|
if(istype(A, /mob/living/carbon/human))
|
|
switch(A:get_species())
|
|
if(SPECIES_VAURCA_WORKER)
|
|
if(isbreeder)
|
|
return FALSE
|
|
return TRUE
|
|
if(SPECIES_VAURCA_WARRIOR)
|
|
if(isbreeder)
|
|
return FALSE
|
|
return TRUE
|
|
if(SPECIES_VAURCA_BREEDER)
|
|
return TRUE
|
|
if(SPECIES_VAURCA_BULWARK)
|
|
if(isbreeder)
|
|
return FALSE
|
|
return TRUE
|
|
if(SPECIES_VAURCA_WARFORM)
|
|
if(isbreeder)
|
|
return FALSE
|
|
return TRUE
|
|
if(SPECIES_MONKEY_VAURCA)
|
|
if(isbreeder)
|
|
return FALSE
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/proc/isipc(A)
|
|
. = 0
|
|
if(ishuman(A))
|
|
var/mob/living/carbon/human/H = A
|
|
. = H.species && (H.species.flags & IS_MECHANICAL)
|
|
|
|
/mob/proc/is_diona()
|
|
return FALSE
|
|
|
|
/mob/living/carbon/human/is_diona()
|
|
if(istype(species, /datum/species/diona))
|
|
return DIONA_WORKER
|
|
return FALSE
|
|
|
|
/mob/living/carbon/alien/diona/is_diona()
|
|
return DIONA_NYMPH
|
|
|
|
/proc/is_mob_special(A) // determines special mobs. has restrictions on certain things, like welderbombing
|
|
if(isrevenant(A))
|
|
return TRUE
|
|
if(iszombie(A))
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/proc/isskeleton(A)
|
|
if(istype(A, /mob/living/carbon/human) && (A:get_species() == SPECIES_SKELETON))
|
|
return 1
|
|
return 0
|
|
|
|
/proc/iszombie(A)
|
|
if(ishuman(A))
|
|
var/mob/living/carbon/human/H = A
|
|
|
|
if(istype(H.get_species(TRUE), /datum/species/zombie))
|
|
return TRUE
|
|
else
|
|
return FALSE
|
|
|
|
return FALSE
|
|
|
|
/proc/isundead(A)
|
|
if(ishuman(A))
|
|
var/mob/living/carbon/human/H = A
|
|
switch(H.get_species())
|
|
if(SPECIES_SKELETON)
|
|
return TRUE
|
|
if(SPECIES_CULTGHOST)
|
|
return TRUE
|
|
if(iszombie(A))
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/proc/isrevenant(A)
|
|
if(ishuman(A))
|
|
var/mob/living/carbon/human/H = A
|
|
switch(H.get_species())
|
|
if(SPECIES_REVENANT)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/proc/islesserform(A)
|
|
if(istype(A, /mob/living/carbon/human))
|
|
switch(A:get_species())
|
|
if (SPECIES_MONKEY)
|
|
return 1
|
|
if (SPECIES_MONKEY_TAJARA)
|
|
return 1
|
|
if (SPECIES_MONKEY_SKRELL)
|
|
return 1
|
|
if (SPECIES_MONKEY_UNATHI)
|
|
return 1
|
|
if (SPECIES_MONKEY_VAURCA)
|
|
return 1
|
|
return 0
|
|
|
|
/proc/isdeaf(A)
|
|
if(istype(A, /mob))
|
|
var/mob/M = A
|
|
return M.ear_deaf
|
|
return 0
|
|
|
|
/proc/iscuffed(A)
|
|
if(istype(A, /mob/living/carbon))
|
|
var/mob/living/carbon/C = A
|
|
if(C.handcuffed)
|
|
return 1
|
|
return 0
|
|
|
|
/proc/hassensorlevel(A, var/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/is_admin(var/mob/user)
|
|
return check_rights(R_ADMIN, 0, user) != 0
|
|
|
|
/proc/hsl2rgb(h, s, l)
|
|
return //TODO: Implement
|
|
|
|
/mob/proc/is_berserk()
|
|
return FALSE
|
|
|
|
/mob/proc/is_pacified()
|
|
return FALSE
|
|
|
|
/mob/proc/is_blind()
|
|
return (sdisabilities & BLIND) || blinded
|
|
|
|
/*
|
|
Miss Chance
|
|
*/
|
|
|
|
//TODO: Integrate defence zones and targeting body parts with the actual organ system, move these into organ definitions.
|
|
|
|
//The base miss chance for the different defence zones
|
|
var/list/global/base_miss_chance = list(
|
|
BP_HEAD = 70,
|
|
BP_CHEST = 10,
|
|
BP_GROIN = 20,
|
|
BP_L_LEG = 40,
|
|
BP_R_LEG = 40,
|
|
BP_L_ARM = 40,
|
|
BP_R_ARM = 40,
|
|
BP_L_HAND = 60,
|
|
BP_R_HAND = 60,
|
|
BP_L_FOOT = 60,
|
|
BP_R_FOOT = 60
|
|
)
|
|
|
|
//Used to weight organs when an organ is hit randomly (i.e. not a directed, aimed attack).
|
|
//Also used to weight the protection value that armor provides for covering that body part when calculating protection from full-body effects.
|
|
var/list/global/organ_rel_size = list(
|
|
BP_HEAD = 25,
|
|
BP_CHEST = 70,
|
|
BP_GROIN = 30,
|
|
BP_L_LEG = 25,
|
|
BP_R_LEG = 25,
|
|
BP_L_ARM = 25,
|
|
BP_R_ARM = 25,
|
|
BP_L_HAND = 10,
|
|
BP_R_HAND = 10,
|
|
BP_L_FOOT = 10,
|
|
BP_R_FOOT = 10
|
|
)
|
|
|
|
///Find the mob at the bottom of a buckle chain
|
|
/mob/proc/lowest_buckled_mob()
|
|
. = src
|
|
//buckled -> buckled_to from TG
|
|
if(buckled_to && ismob(buckled_to))
|
|
var/mob/Buckled = buckled_to
|
|
. = Buckled.lowest_buckled_mob()
|
|
|
|
/proc/check_zone(zone)
|
|
if(!zone)
|
|
return BP_CHEST
|
|
switch(zone)
|
|
if(BP_EYES)
|
|
zone = BP_HEAD
|
|
if(BP_MOUTH)
|
|
zone = BP_HEAD
|
|
return zone
|
|
|
|
/**
|
|
* Return the zone or randomly, another valid zone
|
|
*
|
|
* _Do not use this if someone is intentionally trying to hit a specific body part - use get_zone_with_miss_chance() for that_
|
|
*
|
|
* probability controls the chance it chooses the passed in zone, or another random zone
|
|
* defaults to 80
|
|
*/
|
|
/proc/ran_zone(zone, probability = 80, list/weighted_list)
|
|
if(prob(probability))
|
|
zone = check_zone(zone)
|
|
else
|
|
zone = pick_weight(weighted_list ? weighted_list : organ_rel_size) //Slightly different from TG, we have a list with organ sizes
|
|
return zone
|
|
|
|
/// Emulates targetting a specific body part, and miss chances
|
|
/// May return null if missed
|
|
/// miss_chance_mod may be negative.
|
|
/proc/get_zone_with_miss_chance(zone, var/mob/target, var/miss_chance_mod = 0, var/ranged_attack=0, var/point_blank = FALSE)
|
|
zone = check_zone(zone)
|
|
|
|
if(!ranged_attack)
|
|
// you cannot miss if your target is prone or restrained
|
|
if(target.buckled_to || target.lying)
|
|
return zone
|
|
// if your target is being grabbed aggressively by someone you cannot miss either
|
|
for(var/obj/item/grab/G in target.grabbed_by)
|
|
if(G.state >= GRAB_AGGRESSIVE)
|
|
return zone
|
|
if(point_blank)
|
|
return zone //Point blank shots don't miss.
|
|
|
|
return target.calculate_zone_with_miss_chance(zone, miss_chance_mod)
|
|
|
|
/mob/proc/calculate_zone_with_miss_chance(var/zone, var/miss_chance_mod)
|
|
var/miss_chance = 10
|
|
if (zone in base_miss_chance)
|
|
miss_chance = base_miss_chance[zone]
|
|
miss_chance = max(miss_chance + miss_chance_mod, 0)
|
|
if(prob(miss_chance))
|
|
if(prob(70))
|
|
return null
|
|
return pick(base_miss_chance)
|
|
return zone
|
|
|
|
// never a chance to miss, but you might not hit what you want to hit
|
|
/mob/living/heavy_vehicle/calculate_zone_with_miss_chance(zone, miss_chance_mod)
|
|
var/miss_chance = 10
|
|
if(zone in base_miss_chance)
|
|
miss_chance = base_miss_chance[zone]
|
|
miss_chance = max(miss_chance + miss_chance_mod, 0)
|
|
if(prob(miss_chance))
|
|
return pick(base_miss_chance)
|
|
return zone
|
|
|
|
/**
|
|
* Convert random parts of a passed in message to stars
|
|
*
|
|
* * phrase - the string to convert
|
|
* * probability - probability any character gets changed
|
|
*
|
|
* This proc is dangerously laggy, avoid it or die
|
|
*/
|
|
/proc/stars(phrase, probability = 25)
|
|
if(length(phrase) == 0)
|
|
return
|
|
|
|
var/list/chars = splittext_char(html_decode(phrase), "")
|
|
for(var/i in 1 to length(chars))
|
|
if(chars[i] == " " || !prob(probability))
|
|
continue
|
|
chars[i] = "*"
|
|
return sanitize(jointext(chars, ""))
|
|
|
|
/proc/slur(phrase, strength = 100)
|
|
phrase = html_decode(phrase)
|
|
var/leng = length_char(phrase)
|
|
var/counter = length_char(phrase)
|
|
var/newphrase = ""
|
|
var/newletter = ""
|
|
while (counter >= 1)
|
|
newletter = copytext_char(phrase, (leng - counter) + 1, (leng - counter) + 2)
|
|
if (prob(strength))
|
|
if (rand(1, 3) == 3)
|
|
switch (lowertext(newletter))
|
|
if ("o")
|
|
newletter = "u"
|
|
if ("s")
|
|
newletter = "ch"
|
|
if ("a")
|
|
newletter = "ah"
|
|
if ("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 += "'"
|
|
else
|
|
. = null // For dreamchecker, does nothing
|
|
newphrase += "[newletter]"
|
|
counter -= 1
|
|
return newphrase
|
|
|
|
/proc/Gibberish(t, p)//t is the inputted message, and any value higher than 70 for p will cause letters to be replaced instead of added
|
|
/* Turn text into complete gibberish! */
|
|
var/returntext = ""
|
|
for(var/i = 1, i <= length(t), i++)
|
|
|
|
var/letter = copytext(t, i, i+1)
|
|
if(prob(50))
|
|
if(p >= 70)
|
|
letter = ""
|
|
|
|
for(var/j = 1, j <= rand(0, 2), j++)
|
|
letter += pick("#","@","*","&","%","$","/", "<", ">", ";","*","*","*","*","*","*","*")
|
|
|
|
returntext += letter
|
|
|
|
return returntext
|
|
|
|
|
|
/proc/ninjaspeak(n)
|
|
/*
|
|
The difference with stutter is that this proc can stutter more than 1 letter
|
|
The issue here is that anything that does not have a space is treated as one word (in many instances). For instance, "LOOKING," is a word, including the comma.
|
|
It's fairly easy to fix if dealing with single letters but not so much with compounds of letters./N
|
|
*/
|
|
var/te = html_decode(n)
|
|
var/t = ""
|
|
n = length(n)
|
|
var/p = 1
|
|
while(p <= n)
|
|
var/n_letter
|
|
var/n_mod = rand(1,4)
|
|
if(p+n_mod>n+1)
|
|
n_letter = copytext(te, p, n+1)
|
|
else
|
|
n_letter = copytext(te, p, p+n_mod)
|
|
if (prob(50))
|
|
if (prob(30))
|
|
n_letter = "[n_letter]-[n_letter]-[n_letter]"
|
|
else
|
|
n_letter = "[n_letter]-[n_letter]"
|
|
else
|
|
n_letter = "[n_letter]"
|
|
t = "[t][n_letter]"
|
|
p=p+n_mod
|
|
return sanitize(t)
|
|
|
|
|
|
#define TICKS_PER_RECOIL_ANIM 2
|
|
#define PIXELS_PER_STRENGTH_VAL 16
|
|
|
|
/proc/shake_camera(mob/M, duration, strength = 1)
|
|
var/current_time = world.time
|
|
if(!M || !M.client || (M.shakecamera > current_time)|| M.stat || isEye(M) || isAI(M))
|
|
return
|
|
if(((M.client.view != world.view) || (M.client.pixel_x != 0) || (M.client.pixel_y != 0))) //to prevent it while zooming, because zoom does not play well with this code
|
|
return
|
|
M.shakecamera = current_time + max(TICKS_PER_RECOIL_ANIM, duration)
|
|
strength = abs(strength)*PIXELS_PER_STRENGTH_VAL
|
|
var/steps = min(1, FLOOR(duration/TICKS_PER_RECOIL_ANIM, 1))-1
|
|
animate(M.client, pixel_x = rand(-(strength), strength), pixel_y = rand(-(strength), strength), time = TICKS_PER_RECOIL_ANIM, easing = JUMP_EASING|EASE_IN)
|
|
if(steps)
|
|
for(var/i = 1 to steps)
|
|
animate(pixel_x = 0 + rand(-(strength), strength), pixel_y = 0 + rand(-(strength), strength), time = TICKS_PER_RECOIL_ANIM, easing = JUMP_EASING|EASE_IN)
|
|
animate(pixel_x = 0, pixel_y = 0, time = TICKS_PER_RECOIL_ANIM)
|
|
|
|
/proc/findname(msg)
|
|
for(var/mob/M in GLOB.mob_list)
|
|
if (M.real_name == "[msg]")
|
|
return 1
|
|
return 0
|
|
|
|
|
|
/mob/proc/abiotic(var/full_body = 0)
|
|
if(full_body && ((src.l_hand && !( src.l_hand.abstract )) || (src.r_hand && !( src.r_hand.abstract )) || (src.back || src.wear_mask)))
|
|
return 1
|
|
|
|
if((src.l_hand && !( src.l_hand.abstract )) || (src.r_hand && !( src.r_hand.abstract )))
|
|
return 1
|
|
|
|
return 0
|
|
|
|
//converts intent-strings into numbers and back
|
|
var/list/intents = list(I_HELP,I_DISARM,I_GRAB,I_HURT)
|
|
/proc/intent_numeric(argument)
|
|
if(istext(argument))
|
|
switch(argument)
|
|
if(I_HELP) return 0
|
|
if(I_DISARM) return 1
|
|
if(I_GRAB) return 2
|
|
else return 3
|
|
else
|
|
switch(argument)
|
|
if(0) return I_HELP
|
|
if(1) return I_DISARM
|
|
if(2) return I_GRAB
|
|
else return I_HURT
|
|
|
|
//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(ishuman(src) || isbrain(src) || isslime(src))
|
|
switch(input)
|
|
if(I_HELP,I_DISARM,I_GRAB,I_HURT)
|
|
set_intent(input)
|
|
if("right")
|
|
set_intent(intent_numeric((intent_numeric(a_intent)+1) % 4))
|
|
if("left")
|
|
set_intent(intent_numeric((intent_numeric(a_intent)+3) % 4))
|
|
if(hud_used && hud_used.action_intent)
|
|
hud_used.action_intent.icon_state = "intent_[a_intent]"
|
|
|
|
else if(isrobot(src))
|
|
switch(input)
|
|
if(I_HELP)
|
|
set_intent(I_HELP)
|
|
if(I_HURT)
|
|
set_intent(I_HURT)
|
|
if("right","left")
|
|
set_intent(intent_numeric(intent_numeric(a_intent) - 3))
|
|
if(hud_used && hud_used.action_intent)
|
|
if(a_intent == I_HURT)
|
|
hud_used.action_intent.icon_state = I_HURT
|
|
else
|
|
hud_used.action_intent.icon_state = I_HELP
|
|
|
|
/proc/broadcast_security_hud_message(var/message, var/broadcast_source)
|
|
broadcast_hud_message(message, broadcast_source, GLOB.sec_hud_users, /obj/item/clothing/glasses/hud/security)
|
|
|
|
/proc/broadcast_medical_hud_message(var/message, var/broadcast_source)
|
|
broadcast_hud_message(message, broadcast_source, GLOB.med_hud_users, /obj/item/clothing/glasses/hud/health)
|
|
|
|
/proc/broadcast_hud_message(var/message, var/broadcast_source, var/list/targets, var/icon)
|
|
var/turf/sourceturf = get_turf(broadcast_source)
|
|
for(var/mob/M in targets)
|
|
var/turf/targetturf = get_turf(M)
|
|
if(targetturf && (targetturf.z == sourceturf.z))
|
|
M.show_message("<span class='info'>[icon2html(icon, M)] [message]</span>", 1)
|
|
|
|
/proc/mobs_in_area(var/area/A)
|
|
var/list/mobs = new
|
|
for(var/mob/living/M in GLOB.mob_list)
|
|
if(get_area(M) == A)
|
|
mobs += M
|
|
return mobs
|
|
|
|
//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(var/message, var/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 && C.mob.mind.original.real_name)
|
|
realname = C.mob.mind.original.real_name
|
|
if(mindname && mindname != realname)
|
|
name = "[realname] died as [mindname]"
|
|
else
|
|
name = realname
|
|
|
|
for(var/mob/M in GLOB.player_list)
|
|
if(M.client && ((!istype(M, /mob/abstract/new_player) && M.stat == DEAD) || (M.client.holder && check_rights(R_DEV|R_MOD|R_ADMIN, 0, M))) && (M.client.prefs.toggles & CHAT_DEAD))
|
|
var/follow
|
|
var/lname
|
|
if(subject)
|
|
if(subject != M)
|
|
follow = "[ghost_follow_link(subject, M)] "
|
|
if(M.stat != DEAD && M.client.holder)
|
|
follow = "([admin_jump_link(subject, M.client.holder)]) "
|
|
var/mob/abstract/observer/DM
|
|
if(istype(subject, /mob/abstract/observer))
|
|
DM = subject
|
|
if(M.client.holder) // What admins see
|
|
lname = "[keyname][(DM && DM.anonsay) ? "*" : (DM ? "" : "^")] ([name])"
|
|
else
|
|
if(DM && DM.anonsay) // If the person is actually observer they have the option to be anonymous
|
|
lname = "Ghost of [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, "[follow] <span class='deadsay'>" + create_text_tag("DEAD", M.client) + " [lname][message]</span>")
|
|
|
|
//Announces that a ghost has joined/left, mainly for use with wizards
|
|
/proc/announce_ghost_joinleave(O, var/joined_ghosts = 1, var/message = "")
|
|
var/client/C
|
|
//Accept any type, sort what we want here
|
|
if(ismob(O))
|
|
var/mob/M = O
|
|
if(M.client)
|
|
C = M.client
|
|
else if(isclient(O))
|
|
C = O
|
|
else if(istype(O, /datum/mind))
|
|
var/datum/mind/M = O
|
|
if(M.current && M.current.client)
|
|
C = M.current.client
|
|
else if(M.original && M.original.client)
|
|
C = M.original.client
|
|
|
|
if(C)
|
|
var/name
|
|
if(C.mob)
|
|
var/mob/M = C.mob
|
|
if(M.mind && M.mind.name)
|
|
name = M.mind.name
|
|
if(M.real_name && M.real_name != name)
|
|
if(name)
|
|
name += " ([M.real_name])"
|
|
else
|
|
name = M.real_name
|
|
if(!name)
|
|
name = (C.holder && C.holder.fakekey) ? C.holder.fakekey : C.key
|
|
var/last_at = ""
|
|
if(C.mob.lastarea)
|
|
last_at = " at [C.mob.lastarea]"
|
|
if(joined_ghosts)
|
|
say_dead_direct("The ghost of <span class='name'>[name]</span> now [pick("skulks","lurks","prowls","creeps","stalks")] among the dead[last_at]. [message]")
|
|
else
|
|
say_dead_direct("<span class='name'>[name]</span> no longer [pick("skulks","lurks","prowls","creeps","stalks")] in the realm of the dead. [message]")
|
|
|
|
/mob/proc/switch_to_camera(var/obj/machinery/camera/C)
|
|
if (!C.can_use() || stat || (get_dist(C, src) > 1 || machine != src || blinded || !canmove))
|
|
return 0
|
|
check_eye(src)
|
|
return 1
|
|
|
|
/mob/living/silicon/ai/switch_to_camera(var/obj/machinery/camera/C)
|
|
if(!C.can_use() || !is_in_chassis())
|
|
return 0
|
|
|
|
eyeobj.setLoc(C)
|
|
return 1
|
|
|
|
// Returns true if the mob has a client which has been active in the last given X minutes.
|
|
/mob/proc/is_client_active(var/active = 1)
|
|
return client && client.inactivity < active MINUTES
|
|
|
|
/mob/proc/can_eat()
|
|
return 1
|
|
|
|
/mob/proc/can_force_feed()
|
|
return 1
|
|
|
|
#define SAFE_PERP -50
|
|
/mob/living/proc/assess_perp(var/obj/access_obj, var/check_access, var/auth_weapons, var/check_records, var/check_arrest)
|
|
if(stat == DEAD)
|
|
return SAFE_PERP
|
|
|
|
return 0
|
|
|
|
/mob/living/carbon/assess_perp(var/obj/access_obj, var/check_access, var/auth_weapons, var/check_records, var/check_arrest)
|
|
if(handcuffed)
|
|
return SAFE_PERP
|
|
|
|
return ..()
|
|
|
|
/mob/living/carbon/human/assess_perp(var/obj/access_obj, var/check_access, var/auth_weapons, var/check_records, var/check_arrest)
|
|
var/threatcount = ..()
|
|
if(. == SAFE_PERP)
|
|
return SAFE_PERP
|
|
|
|
//Agent cards lower threatlevel.
|
|
var/obj/item/card/id/id = GetIdCard()
|
|
if(id && istype(id, /obj/item/card/id/syndicate))
|
|
threatcount -= 2
|
|
// A proper CentCom id is hard currency.
|
|
else if(id && istype(id, /obj/item/card/id/centcom))
|
|
return SAFE_PERP
|
|
|
|
if(check_access && !access_obj.allowed(src))
|
|
threatcount += 4
|
|
|
|
if(auth_weapons && !access_obj.allowed(src))
|
|
if(istype(l_hand, /obj/item/gun) || istype(l_hand, /obj/item/melee))
|
|
threatcount += 4
|
|
|
|
if(istype(r_hand, /obj/item/gun) || istype(r_hand, /obj/item/melee))
|
|
threatcount += 4
|
|
|
|
if(istype(belt, /obj/item/gun) || istype(belt, /obj/item/melee))
|
|
threatcount += 2
|
|
|
|
if(species.name != SPECIES_HUMAN)
|
|
threatcount += 2
|
|
|
|
if(check_records || check_arrest)
|
|
var/perpname = name
|
|
if(id)
|
|
perpname = id.registered_name
|
|
|
|
var/datum/record/general/R = SSrecords.find_record("name", perpname)
|
|
if(check_records && !R)
|
|
threatcount += 4
|
|
|
|
if(check_arrest && R && R.security && (R.security.criminal == "*Arrest*"))
|
|
threatcount += 4
|
|
|
|
return threatcount
|
|
|
|
/mob/living/simple_animal/hostile/assess_perp(var/obj/access_obj, var/check_access, var/auth_weapons, var/check_records, var/check_arrest)
|
|
var/threatcount = ..()
|
|
if(threatcount == SAFE_PERP)
|
|
return SAFE_PERP
|
|
|
|
if(istype(src, /mob/living/simple_animal/hostile/retaliate/goat) || istype(src, /mob/living/simple_animal/hostile/commanded))
|
|
return threatcount
|
|
|
|
return threatcount + 4
|
|
|
|
|
|
/mob/living/proc/bucklecheck(var/mob/living/user)
|
|
if (buckled_to && istype(buckled_to, /obj/structure))
|
|
if (istype(user,/mob/living/silicon/robot))
|
|
return 2
|
|
else
|
|
to_chat(user, "You must unbuckle the subject first")
|
|
return 0
|
|
return 1
|
|
|
|
/mob/living/carbon/human/proc/delayed_vomit()
|
|
if(QDELETED(src))
|
|
return
|
|
|
|
if(!check_has_mouth())
|
|
return
|
|
if(stat == DEAD)
|
|
return
|
|
if(chem_effects[CE_ANTIEMETIC])
|
|
to_chat(src, SPAN_WARNING("You feel a very brief wave of nausea, but it quickly disapparates."))
|
|
return
|
|
|
|
if(!lastpuke)
|
|
lastpuke = 1
|
|
to_chat(src, SPAN_WARNING("You feel nauseous..."))
|
|
spawn(150) //15 seconds until second warning
|
|
to_chat(src, SPAN_WARNING("You feel like you are about to throw up!"))
|
|
spawn(100) //and you have 10 more for mad dash to the bucket
|
|
if(!QDELETED(src))
|
|
empty_stomach()
|
|
spawn(350) //wait 35 seconds before next volley
|
|
lastpuke = 0
|
|
|
|
/obj/proc/get_equip_slot()
|
|
//This function is called by an object which is somewhere on a humanoid mob
|
|
//It will return the number of the equipment slot its in
|
|
|
|
if (!istype(loc, /mob/living/carbon/human))//This function is for finding where we are on a human. not valid otherwise
|
|
return null
|
|
|
|
var/mob/living/carbon/human/H = loc
|
|
|
|
|
|
//Now we check various slots on the mob, the order of these is optimised based on how likely we are to be in that slot
|
|
if (H.l_hand == src)
|
|
return slot_l_hand
|
|
else if (H.r_hand == src)
|
|
return slot_r_hand
|
|
else if (H.l_store == src)
|
|
return slot_l_store
|
|
else if (H.r_store == src)
|
|
return slot_r_store
|
|
else if (H.head == src)
|
|
return slot_head
|
|
else if (H.wear_suit == src)
|
|
return slot_wear_suit
|
|
else if (H.s_store == src)
|
|
return slot_s_store
|
|
else if (H.wear_mask == src)
|
|
return slot_wear_mask
|
|
else if (H.wear_id == src)
|
|
return slot_wear_id
|
|
else if (H.w_uniform == src)
|
|
return slot_w_uniform
|
|
else if (H.gloves == src)
|
|
return slot_gloves
|
|
else if (H.belt == src)
|
|
return slot_belt
|
|
else if (H.back == src)
|
|
return slot_back
|
|
else if (H.r_ear == src)
|
|
return slot_r_ear
|
|
else if (H.l_ear == src)
|
|
return slot_l_ear
|
|
else if (H.shoes == src)
|
|
return slot_shoes
|
|
else if (H.wrists == src)
|
|
return slot_wrists
|
|
else
|
|
return null//We failed to find the slot
|
|
|
|
/* Variables to check
|
|
l_hand
|
|
r_hand
|
|
head
|
|
l_store //Left and right pockets
|
|
r_store
|
|
s_store //Suit storage?
|
|
|
|
wear_mask,
|
|
wear_id
|
|
w_uniform //the uniform
|
|
wear_suit
|
|
gloves
|
|
belt
|
|
back
|
|
r_ear
|
|
l_ear
|
|
|
|
shoes
|
|
|
|
*/
|
|
|
|
/obj/proc/report_onmob_location(var/justmoved, var/slot = null, var/mob/reportto)
|
|
if(istype(reportto.loc, /mob/living/bot))
|
|
to_chat(reportto, SPAN_NOTICE("You are currently housed within \the [reportto.loc]."))
|
|
return
|
|
var/mob/living/carbon/human/H//The person who the item is on
|
|
var/newlocation
|
|
var/preposition= ""
|
|
var/action = ""
|
|
var/action3 = ""
|
|
if (!reportto)
|
|
return 0
|
|
|
|
if (istype(loc, /mob/living/carbon/human))//This function is for finding where we are on a human. not valid otherwise
|
|
H = loc
|
|
|
|
else
|
|
H = get_holding_mob()
|
|
|
|
|
|
if (slot != null)
|
|
|
|
if (slot_l_hand == slot)
|
|
if (justmoved)
|
|
action += "now "
|
|
preposition = "in"
|
|
action += "being held"
|
|
action3 = "holds"
|
|
newlocation = "left hand"
|
|
else if (slot_r_hand == slot)
|
|
if (justmoved)
|
|
action += "now "
|
|
preposition = "in"
|
|
action += "being held"
|
|
action3 = "holds"
|
|
newlocation = "right hand"
|
|
else if (slot_l_store == slot)
|
|
if (justmoved)
|
|
preposition = "into"
|
|
action = "placed"
|
|
action3 = "places"
|
|
else
|
|
preposition = "inside"
|
|
newlocation = "left pocket"
|
|
else if (slot_r_store == slot)
|
|
if (justmoved)
|
|
preposition = "into"
|
|
action = "placed"
|
|
action3 = "places"
|
|
else
|
|
preposition = "inside"
|
|
newlocation = "right pocket"
|
|
else if (slot_s_store == slot)
|
|
if (justmoved)
|
|
preposition = "into"
|
|
action = "placed"
|
|
action3 = "places"
|
|
else
|
|
preposition = "inside"
|
|
newlocation = "suit storage"
|
|
else
|
|
if (justmoved)
|
|
action += "now "
|
|
action += "being worn"
|
|
|
|
if (slot_head == slot)
|
|
preposition = "as"
|
|
action3 = "wears"
|
|
newlocation = "hat"
|
|
else if (slot_wear_suit == slot)
|
|
preposition = "over"
|
|
action3 = "wears"
|
|
newlocation = "uniform"
|
|
else if (slot_wear_mask == slot)
|
|
preposition = "on"
|
|
action3 = "wears"
|
|
newlocation = "face"
|
|
else if (slot_wear_id == slot)
|
|
preposition = "as"
|
|
action3 = "wears"
|
|
newlocation = "ID"
|
|
else if (slot_w_uniform == slot)
|
|
preposition = "on"
|
|
action3 = "wears"
|
|
newlocation = "body"
|
|
else if (slot_gloves == slot)
|
|
preposition = "on"
|
|
action3 = "wears"
|
|
newlocation = "hands"
|
|
else if (slot_belt == slot)
|
|
preposition = "around"
|
|
action3 = "wears"
|
|
newlocation = "waist"
|
|
else if (slot_back == slot)
|
|
preposition = "on"
|
|
action3 = "wears"
|
|
newlocation = "back"
|
|
else if (slot_r_ear == slot)
|
|
preposition = "on"
|
|
action3 = "wears"
|
|
newlocation = "right shoulder"//Ill use ear slots for wearing mobs on the shoulder in future
|
|
else if (slot_l_ear == slot)
|
|
preposition = "on"
|
|
action3 = "wears"
|
|
newlocation = "left shoulder"
|
|
else if (slot_shoes == slot)
|
|
preposition = "on"
|
|
action3 = "wears"
|
|
newlocation = "feet"
|
|
else if (istype(loc,/obj/item/modular_computer))
|
|
var/obj/item/modular_computer/S = loc
|
|
newlocation = S.name
|
|
if (justmoved)
|
|
preposition = "into"
|
|
action = "slotted"
|
|
action3 = "slots"
|
|
else
|
|
action = "installed"
|
|
preposition = "in"
|
|
else if (istype(loc,/obj/item/storage))
|
|
var/obj/item/storage/S = loc
|
|
newlocation = S.name
|
|
if (justmoved)
|
|
preposition = "into"
|
|
action = "placed"
|
|
action3 = "places"
|
|
else
|
|
action = "tucked"
|
|
preposition = "inside"
|
|
|
|
if (justmoved)
|
|
reportto.contained_visible_message(H, SPAN_NOTICE("[H] [action3] [reportto] [preposition] their [newlocation]."), SPAN_NOTICE("You are [action] [preposition] [H]'s [newlocation]."), "", 1)
|
|
else
|
|
to_chat(reportto, SPAN_NOTICE("You are [action] [preposition] [H]'s [newlocation]."))
|
|
|
|
/atom/proc/get_holding_mob()
|
|
//This function will return the mob which is holding this holder, or null if it's not held
|
|
//It recurses up the hierarchy out of containers until it reaches a mob, or aturf, or hits the limit
|
|
var/x = 0//As a safety, we'll crawl up a maximum of five layers
|
|
var/atom/a = src
|
|
while (x < 5)
|
|
x++
|
|
if (isnull(a))
|
|
return null
|
|
|
|
a = a.loc
|
|
if (istype(a, /turf))
|
|
return null//We must be on a table or a floor, or maybe in a wall. Either way we're not held.
|
|
|
|
if (istype(a, /mob))
|
|
return a
|
|
//If none of the above are true, we must be inside a box or backpack or something. Keep recursing up.
|
|
|
|
return null//If we get here, the holder must be buried many layers deep in nested containers. Shouldn't happen
|
|
|
|
|
|
//This proc retrieves the relevant time of death from
|
|
/mob/proc/get_death_time(var/which)
|
|
var/datum/preferences/P
|
|
if (client)
|
|
P = client.prefs
|
|
else if (ckey)
|
|
// To avoid runtimes during adminghost.
|
|
if (copytext(ckey, 1, 2) == "@")
|
|
P = preferences_datums[copytext(ckey, 2)]
|
|
else
|
|
P = preferences_datums[ckey]
|
|
else
|
|
return null
|
|
|
|
if (!P)
|
|
return null
|
|
|
|
return P.time_of_death[which]
|
|
|
|
/mob/proc/set_death_time(var/which, var/value)
|
|
var/datum/preferences/P
|
|
if (client)
|
|
P = client.prefs
|
|
else if (ckey)
|
|
// To avoid runtimes during adminghost.
|
|
if (copytext(ckey, 1, 2) == "@")
|
|
P = preferences_datums[copytext(ckey, 2)]
|
|
else
|
|
P = preferences_datums[ckey]
|
|
else
|
|
return 0
|
|
|
|
if (!P)
|
|
return 0
|
|
|
|
P.time_of_death[which] = value
|
|
return 1
|
|
|
|
/**
|
|
* Resets death timers for a mob. Should only be called during new player creation.
|
|
*/
|
|
/mob/proc/reset_death_timers()
|
|
var/datum/preferences/P
|
|
if (client)
|
|
P = client.prefs
|
|
else if (ckey)
|
|
// To avoid runtimes during adminghost.
|
|
if (copytext(ckey, 1, 2) == "@")
|
|
P = preferences_datums[copytext(ckey, 2)]
|
|
else
|
|
P = preferences_datums[ckey]
|
|
else
|
|
return
|
|
|
|
if (!P)
|
|
return
|
|
|
|
P.time_of_death.Cut()
|
|
|
|
//Below here is stuff related to devouring, but which is generally helpful and thus placed here
|
|
//See Devour.dm for more info in how these are used
|
|
|
|
// Returns a bitfield representing the mob's type as relevant to the devour system.
|
|
/mob/proc/find_type()
|
|
return 0
|
|
|
|
/mob/living/carbon/human/find_type()
|
|
. = ..()
|
|
. |= isSynthetic() ? TYPE_SYNTHETIC : TYPE_ORGANIC
|
|
if (!islesserform(src))
|
|
. |= TYPE_HUMANOID
|
|
|
|
/mob/living/carbon/slime/find_type()
|
|
. = ..()
|
|
. |= TYPE_WEIRD
|
|
|
|
/mob/living/bot/find_type()
|
|
. = ..()
|
|
. |= TYPE_SYNTHETIC
|
|
|
|
/mob/living/silicon/find_type()
|
|
. = ..()
|
|
. |= TYPE_SYNTHETIC
|
|
|
|
// Yeah, I'm just going to cheat and do istype(src) checks here.
|
|
// It's not worth adding a proc for every single one of these types.
|
|
/mob/living/simple_animal/find_type()
|
|
. = ..()
|
|
if (is_type_in_typecache(src, SSmobs.mtl_synthetic))
|
|
. |= TYPE_SYNTHETIC
|
|
|
|
if (is_type_in_typecache(src, SSmobs.mtl_weird))
|
|
. |= TYPE_WEIRD
|
|
|
|
if (is_type_in_typecache(src, SSmobs.mtl_incorporeal))
|
|
. |= TYPE_INCORPOREAL
|
|
|
|
// If it's not TYPE_SYNTHETIC, TYPE_WEIRD or TYPE_INCORPOREAL, we can assume it's TYPE_ORGANIC.
|
|
if (!(. & (TYPE_SYNTHETIC|TYPE_WEIRD|TYPE_INCORPOREAL)))
|
|
. |= TYPE_ORGANIC
|
|
|
|
if (is_type_in_typecache(src, SSmobs.mtl_humanoid))
|
|
. |= TYPE_HUMANOID
|
|
|
|
#undef SAFE_PERP
|
|
|
|
/mob/proc/get_multitool(var/obj/P)
|
|
if(P?.ismultitool())
|
|
return P
|
|
|
|
/mob/abstract/observer/get_multitool()
|
|
return can_admin_interact() && ..(ghost_multitool)
|
|
|
|
/mob/living/carbon/human/get_multitool()
|
|
return ..(get_active_hand())
|
|
|
|
/mob/living/silicon/robot/get_multitool()
|
|
return ..(get_active_hand())
|
|
|
|
/mob/living/silicon/ai/get_multitool()
|
|
return ..(ai_multi)
|
|
|
|
/mob/proc/get_hydration_mul(var/minscale = 0, var/maxscale = 1)
|
|
|
|
if(status_flags & GODMODE) //Godmode
|
|
return maxscale
|
|
|
|
if(max_hydration <= 0) //Has no hydration
|
|
return maxscale
|
|
|
|
var/hydration_mul = hydration/max_hydration
|
|
|
|
if(hydration_mul >= CREW_HYDRATION_OVERHYDRATED)
|
|
return minscale + ( (maxscale - minscale) * 0.66)
|
|
|
|
if(hydration_mul <= CREW_HYDRATION_DEHYDRATED)
|
|
return minscale
|
|
|
|
if(hydration_mul <= CREW_HYDRATION_VERYTHIRSTY)
|
|
return minscale + ( (maxscale - minscale) * 0.33)
|
|
|
|
if(hydration_mul <= CREW_HYDRATION_THIRSTY)
|
|
return minscale + ( (maxscale - minscale) * 0.66)
|
|
|
|
return minscale + ( (maxscale - minscale) * 1)
|
|
|
|
/mob/proc/get_nutrition_mul(var/minscale = 0, var/maxscale = 1)
|
|
|
|
if(status_flags & GODMODE) //Godmode
|
|
return maxscale
|
|
|
|
if(max_nutrition <= 0) //Has no nutrition
|
|
return maxscale
|
|
|
|
var/nutrition_mul = nutrition/max_nutrition
|
|
|
|
if(nutrition_mul >= CREW_NUTRITION_OVEREATEN)
|
|
return minscale + ( (maxscale - minscale) * 0.66)
|
|
|
|
if(nutrition_mul <= CREW_NUTRITION_STARVING)
|
|
return minscale
|
|
|
|
if(nutrition_mul <= CREW_NUTRITION_VERYHUNGRY)
|
|
return minscale + ( (maxscale - minscale) * 0.33)
|
|
|
|
if(nutrition_mul <= CREW_NUTRITION_HUNGRY)
|
|
return minscale + ( (maxscale - minscale) * 0.66)
|
|
|
|
return minscale + ( (maxscale - minscale) * 1)
|
|
|
|
|
|
/mob/proc/adjustNutritionLoss(var/amount)
|
|
|
|
if(max_nutrition <= 0)
|
|
return FALSE
|
|
nutrition = max(0,min(max_nutrition,nutrition - amount))
|
|
|
|
return TRUE
|
|
|
|
/mob/proc/adjustHydrationLoss(var/amount)
|
|
|
|
if(max_hydration <= 0)
|
|
return FALSE
|
|
hydration = max(0,min(max_hydration,hydration - amount))
|
|
|
|
return TRUE
|
|
|
|
/mob/proc/get_accumulated_vision_handlers()
|
|
var/result[2]
|
|
var/asight = 0
|
|
var/ainvis = 0
|
|
for(var/atom/vision_handler in additional_vision_handlers)
|
|
//Grab their flags
|
|
asight |= vision_handler.additional_sight_flags()
|
|
ainvis = max(ainvis, vision_handler.additional_see_invisible())
|
|
result[1] = asight
|
|
result[2] = ainvis
|
|
|
|
return result
|
|
|
|
/mob/proc/remove_blood_simple(var/blood)
|
|
return
|
|
|
|
/mob/living/carbon/human/needs_wheelchair()
|
|
return species.handle_stance_damage(src, TRUE) >= 4
|
|
|
|
/mob/living/carbon/human/proc/equip_wheelchair()
|
|
var/obj/structure/bed/stool/chair/office/wheelchair/W = new(get_turf(src))
|
|
if(isturf(loc))
|
|
buckled_to = W
|
|
update_canmove()
|
|
W.set_dir(dir)
|
|
W.buckled = src
|
|
W.add_fingerprint(src)
|
|
|
|
/mob/proc/set_intent(var/set_intent)
|
|
a_intent = set_intent
|
|
|
|
/mob/proc/get_accent_icon(var/datum/language/speaking, var/mob/hearer, var/force_accent)
|
|
SHOULD_CALL_PARENT(TRUE)
|
|
var/used_accent = force_accent ? force_accent : accent
|
|
if(used_accent && (speaking?.allow_accents || !speaking))
|
|
var/datum/accent/a = SSrecords.accents[used_accent]
|
|
if(istype(a))
|
|
if(hearer && hearer.client && hearer.client.prefs?.toggles_secondary & ACCENT_TAG_TEXT)
|
|
return {"<a href='byond://?src=[REF(src)];accent_tag=[url_encode(a)]'>([a.text_tag])</a>"}
|
|
else
|
|
var/datum/asset/spritesheet/S = get_asset_datum(/datum/asset/spritesheet/chat)
|
|
var/final_icon = "accent-[a.tag_icon]"
|
|
return {"<span onclick="window.location.href='byond://?src=[REF(src)];accent_tag=[url_encode(a)]'">[S.icon_tag(final_icon)]</span>"}
|
|
|
|
/mob/assign_player(var/mob/user)
|
|
ckey = user.ckey
|
|
resting = FALSE // ghosting sets resting to true
|
|
client.init_verbs()
|
|
return src
|
|
|
|
/mob/proc/remove_nearsighted()
|
|
disabilities &= ~NEARSIGHTED
|
|
|
|
/mob/proc/remove_deaf()
|
|
sdisabilities &= ~DEAF
|
|
|
|
/mob/proc/get_antag_datum(var/antag_role)
|
|
if(!mind)
|
|
return
|
|
var/datum/D = mind.antag_datums[antag_role]
|
|
if(D)
|
|
return D
|
|
|
|
/mob/dump_contents()
|
|
for(var/thing in get_contained_external_atoms())
|
|
var/atom/movable/AM = thing
|
|
drop_from_inventory(AM, loc)
|
|
if(ismob(AM))
|
|
var/mob/M = AM
|
|
if(M.client)
|
|
M.client.eye = M.client.mob
|
|
M.client.perspective = MOB_PERSPECTIVE
|
|
|
|
/mob/proc/in_neck_grab()
|
|
for(var/thing in grabbed_by)
|
|
var/obj/item/grab/G = thing
|
|
if(istype(G))
|
|
if(G.state >= GRAB_NECK)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/mob/get_cell()
|
|
return FALSE
|
|
|
|
/mob/proc/get_radio()
|
|
return null
|
|
|
|
/mob/proc/can_hear_radio(var/list/speaker_coverage = list())
|
|
var/turf/ear = get_turf(src)
|
|
if(ear && speaker_coverage[ear])
|
|
return TRUE
|
|
|
|
/mob/proc/handle_vision()
|
|
return
|
|
|
|
/mob/proc/set_name(var/new_name)
|
|
real_name = new_name
|
|
name = real_name
|
|
voice_name = real_name
|
|
if(mind)
|
|
mind.name = real_name
|
|
|
|
/mob/proc/get_organ_name_from_zone(var/def_zone)
|
|
return parse_zone(def_zone)
|
|
|
|
/mob/living/silicon/robot/set_name(var/new_name, var/prefix)
|
|
..()
|
|
if(mmi)
|
|
mmi.brainmob.name = real_name
|
|
mmi.brainmob.real_name = real_name
|
|
mmi.name = "[initial(mmi.name)]: [real_name]"
|
|
|
|
// We also need to update our internal ID
|
|
if(id_card)
|
|
if(prefix)
|
|
id_card.assignment = prefix
|
|
id_card.registered_name = real_name
|
|
id_card.update_name()
|
|
|
|
//We also need to update name of internal camera.
|
|
if(camera)
|
|
camera.c_tag = real_name
|
|
|
|
/mob/proc/get_talk_bubble()
|
|
return 'icons/mob/talk.dmi'
|
|
|
|
/mob/proc/adjust_typing_indicator_offsets(var/atom/movable/typing_indicator/indicator)
|
|
return
|
|
|
|
/datum/proc/get_client()
|
|
return null
|
|
|
|
/client/get_client()
|
|
return src
|
|
|
|
/mob/get_client()
|
|
return client
|
|
|
|
///Can the mob hear
|
|
/mob/proc/can_hear()
|
|
return !isdeaf(src)
|
|
|
|
|
|
/// Sends a message to the mob if the current world time is less than the previous' message next_message_time, additionally sends a floating message if show_floating_message is set to true. key is an optional replacement that'll go in the assoc list, used for stuff like picklists
|
|
/mob/proc/notify_message(var/message, var/next_message_time = 1 SECOND, var/show_floating_message = FALSE, var/key)
|
|
// initializes the message_notifications list so we can use standard list handling from here on out
|
|
// it's only lazy to save memory
|
|
LAZYINITLIST(message_notifications)
|
|
|
|
// if we have a key, use that, otherwise check the message
|
|
var/key_check = key || message
|
|
if((key_check in message_notifications) && world.time < message_notifications[key_check])
|
|
return
|
|
|
|
to_chat(src, message)
|
|
if(show_floating_message)
|
|
balloon_alert(src, strip_html_full(message))
|
|
|
|
// we only keep 10 entries in the assoc list, this is an arbitrary number meant to keep it from ballooning in size and taking up memory
|
|
if(length(message_notifications) >= 10)
|
|
message_notifications.Cut(1, 2)
|
|
|
|
message_notifications[key_check] = world.time + next_message_time
|