Files
S.P.L.U.R.T-Station-13/code/modules/mob/living/carbon/human/human.dm
SandPoot e5a9280ad3 push
2024-07-04 23:44:35 -03:00

1150 lines
46 KiB
Plaintext

/mob/living/carbon/human
name = "Unknown"
real_name = "Unknown"
icon = 'icons/mob/human.dmi'
icon_state = "caucasian_m"
SET_APPEARANCE_FLAGS(KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE)
/mob/living/carbon/human/Initialize(mapload)
add_verb(src, /mob/living/proc/mob_sleep)
add_verb(src, /mob/living/proc/lay_down)
add_verb(src, /mob/living/carbon/human/verb/underwear_toggle)
add_verb(src, /mob/living/verb/subtle)
add_verb(src, /mob/living/verb/subtler)
//initialize limbs first
create_bodyparts()
setup_human_dna()
if(dna.species)
set_species(dna.species.type)
//initialise organs
create_internal_organs() //most of it is done in set_species now, this is only for parent call
physiology = new()
AddComponent(/datum/component/personal_crafting)
AddComponent(/datum/component/footstep, FOOTSTEP_MOB_HUMAN, 1, 2)
. = ..()
if(CONFIG_GET(flag/disable_stambuffer))
enable_intentional_sprint_mode()
RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, TYPE_PROC_REF(/atom, clean_blood))
GLOB.human_list += src
/mob/living/carbon/human/proc/setup_human_dna()
//initialize dna. for spawned humans; overwritten by other code
create_dna(src)
randomize_human(src)
dna.initialize_dna()
/mob/living/carbon/human/ComponentInitialize()
. = ..()
if(!CONFIG_GET(flag/disable_human_mood))
AddComponent(/datum/component/mood)
AddComponent(/datum/component/combat_mode)
AddElement(/datum/element/flavor_text/carbon, _name = "Flavor Text", _save_key = "flavor_text")
AddElement(/datum/element/flavor_text/carbon/temporary, "", "Set Pose (Temporary Flavor Text)", "This should be used only for things pertaining to the current round!", _save_key = null)
AddElement(/datum/element/flavor_text, _name = "OOC Notes", _addendum = "Put information on ERP/vore/lewd-related preferences here. THIS SHOULD NOT CONTAIN REGULAR FLAVORTEXT!!", _always_show = TRUE, _save_key = "ooc_notes", _examine_no_preview = TRUE)
AddElement(/datum/element/strippable, GLOB.strippable_human_items, TYPE_PROC_REF(/mob/living/carbon/human, should_strip))
/mob/living/carbon/human/Destroy()
QDEL_NULL(physiology)
QDEL_NULL_LIST(vore_organs) // CITADEL EDIT belly stuff
GLOB.human_list -= src
return ..()
/mob/living/carbon/human/prepare_data_huds()
//Update med hud images...
..()
//...sec hud images...
sec_hud_set_ID()
sec_hud_set_implants()
sec_hud_set_security_status()
//...and display them.
add_to_all_human_data_huds()
/mob/living/carbon/human/get_status_tab_items()
. = ..()
. += "Intent: [a_intent]"
. += "Move Mode: [m_intent]"
if(internal)
if(!internal.air_contents)
qdel(internal)
else
. += ""
. += "Internal Atmosphere Info: [internal.name]"
. += "Tank Pressure: [internal.air_contents.return_pressure()]"
. += "Distribution Pressure: [internal.distribute_pressure]"
if(mind)
var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling)
. += ""
. += "Chemical Storage: [changeling.chem_charges]/[changeling.chem_storage]"
. += "Absorbed DNA: [changeling.absorbedcount]"
//NINJACODE
if(istype(wear_suit, /obj/item/clothing/suit/space/space_ninja)) //Only display if actually a ninja.
var/obj/item/clothing/suit/space/space_ninja/SN = wear_suit
. += "SpiderOS Status: [SN.s_initialized ? "Initialized" : "Disabled"]"
. += "Current Time: [STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)]"
if(SN.s_initialized)
//Suit gear
. += "Energy Charge: [round(SN.cell.charge/100)]%"
//Ninja status
. += "Fingerprints: [md5(dna.uni_identity)]"
. += "Unique Identity: [dna.unique_enzymes]"
. += "Overall Status: [stat > 1 ? "dead" : "[health]% healthy"]"
. += "Nutrition Status: [nutrition]"
. += "Oxygen Loss: [getOxyLoss()]"
. += "Toxin Levels: [getToxLoss()]"
. += "Burn Severity: [getFireLoss()]"
. += "Brute Trauma: [getBruteLoss()]"
. += "Radiation Levels: [radiation] rad"
. += "Body Temperature: [bodytemperature-T0C] degrees C ([bodytemperature*1.8-459.67] degrees F)"
//Diseases
if(length(diseases))
. += "Viruses:"
for(var/thing in diseases)
var/datum/disease/D = thing
. += "* [D.name], Type: [D.spread_text], Stage: [D.stage]/[D.max_stages], Possible Cure: [D.cure_text]"
// called when something steps onto a human
// this could be made more general, but for now just handle mulebot
/mob/living/carbon/human/Crossed(atom/movable/AM)
SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM)
var/mob/living/simple_animal/bot/mulebot/MB = AM
if(istype(MB))
MB.RunOver(src)
spreadFire(AM)
/mob/living/carbon/human/Topic(href, href_list)
if(usr.canUseTopic(src, BE_CLOSE, NO_DEXTERY))
if(href_list["embedded_object"])
var/obj/item/bodypart/L = locate(href_list["embedded_limb"]) in bodyparts
if(!L)
return
var/obj/item/I = locate(href_list["embedded_object"]) in L.embedded_objects
if(!I || I.loc != src) //no item, no limb, or item is not in limb or in the person anymore
return
SEND_SIGNAL(src, COMSIG_CARBON_EMBED_RIP, I, L)
return
///////HUDs///////
if(href_list["hud"])
if(ishuman(usr))
var/mob/living/carbon/human/H = usr
var/perpname = get_face_name(get_id_name(""))
if(istype(H.glasses, /obj/item/clothing/glasses/hud) || istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud))
var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.general)
if(href_list["photo_front"] || href_list["photo_side"])
if(R)
if(!H.canUseHUD())
return
else if(!istype(H.glasses, /obj/item/clothing/glasses/hud) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/medical))
return
var/obj/item/photo/P = null
if(href_list["photo_front"])
P = R.fields["photo_front"]
else if(href_list["photo_side"])
P = R.fields["photo_side"]
if(P)
P.show(H)
if(href_list["hud"] == "m")
if(istype(H.glasses, /obj/item/clothing/glasses/hud/health) || istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/medical))
if(href_list["p_stat"])
var/health_status = input(usr, "Specify a new physical status for this person.", "Medical HUD", R.fields["p_stat"]) in list("Active", "Physically Unfit", "*Unconscious*", "*Deceased*", "Cancel")
if(R)
if(!H.canUseHUD())
return
else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/health) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/medical))
return
if(health_status && health_status != "Cancel")
R.fields["p_stat"] = health_status
return
if(href_list["m_stat"])
var/health_status = input(usr, "Specify a new mental status for this person.", "Medical HUD", R.fields["m_stat"]) in list("Stable", "*Watch*", "*Unstable*", "*Insane*", "Cancel")
if(R)
if(!H.canUseHUD())
return
else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/health) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/medical))
return
if(health_status && health_status != "Cancel")
R.fields["m_stat"] = health_status
return
if(href_list["evaluation"])
if(!getBruteLoss() && !getFireLoss() && !getOxyLoss() && getToxLoss() < 20)
to_chat(usr, "<span class='notice'>No external injuries detected.</span><br>")
return
var/span = "notice"
var/status = ""
if(getBruteLoss())
to_chat(usr, "<b>Physical trauma analysis:</b>")
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
var/brutedamage = BP.brute_dam
if(brutedamage > 0)
status = "received minor physical injuries."
span = "notice"
if(brutedamage > 20)
status = "been seriously damaged."
span = "danger"
if(brutedamage > 40)
status = "sustained major trauma!"
span = "userdanger"
if(brutedamage)
to_chat(usr, "<span class='[span]'>[BP] appears to have [status]</span>")
if(getFireLoss())
to_chat(usr, "<b>Analysis of skin burns:</b>")
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
var/burndamage = BP.burn_dam
if(burndamage > 0)
status = "signs of minor burns."
span = "notice"
if(burndamage > 20)
status = "serious burns."
span = "danger"
if(burndamage > 40)
status = "major burns!"
span = "userdanger"
if(burndamage)
to_chat(usr, "<span class='[span]'>[BP] appears to have [status]</span>")
if(getOxyLoss())
to_chat(usr, "<span class='danger'>Patient has signs of suffocation, emergency treatment may be required!</span>")
if(getToxLoss() > 20)
to_chat(usr, "<span class='danger'>Gathered data is inconsistent with the analysis, possible cause: poisoning.</span>")
if(href_list["hud"] == "s")
if(istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security))
if(usr.stat || usr == src) //|| !usr.canmove || usr.restrained()) Fluff: Sechuds have eye-tracking technology and sets 'arrest' to people that the wearer looks and blinks at.
return //Non-fluff: This allows sec to set people to arrest as they get disarmed or beaten
// Checks the user has security clearence before allowing them to change arrest status via hud, comment out to enable all access
var/allowed_access = null
var/obj/item/clothing/glasses/G = H.glasses
if (!(G.obj_flags & EMAGGED))
if(H.wear_id)
var/list/access = H.wear_id.GetAccess()
if(ACCESS_SEC_DOORS in access)
allowed_access = H.get_authentification_name()
else
allowed_access = "@%&ERROR_%$*"
if(!allowed_access)
to_chat(H, "<span class='warning'>ERROR: Invalid Access</span>")
return
if(perpname)
R = find_record("name", perpname, GLOB.data_core.security)
if(R)
if(href_list["status"])
var/setcriminal = input(usr, "Specify a new criminal status for this person.", "Security HUD", R.fields["criminal"]) in list("None", "*Arrest*", "Incarcerated", "Paroled", "Discharged", "Cancel")
if(setcriminal != "Cancel")
if(R)
if(H.canUseHUD())
if(istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security))
investigate_log("[key_name(src)] has been set from [R.fields["criminal"]] to [setcriminal] by [key_name(usr)].", INVESTIGATE_RECORDS)
R.fields["criminal"] = setcriminal
sec_hud_set_security_status()
return
if(href_list["view"])
if(R)
if(!H.canUseHUD())
return
else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security))
return
to_chat(usr, "<b>Name:</b> [R.fields["name"]] <b>Criminal Status:</b> [R.fields["criminal"]]")
to_chat(usr, "<b>Minor Crimes:</b>")
for(var/datum/data/crime/c in R.fields["mi_crim"])
to_chat(usr, "<b>Crime:</b> [c.crimeName]")
to_chat(usr, "<b>Details:</b> [c.crimeDetails]")
to_chat(usr, "Added by [c.author] at [c.time]")
to_chat(usr, "----------")
to_chat(usr, "<b>Major Crimes:</b>")
for(var/datum/data/crime/c in R.fields["ma_crim"])
to_chat(usr, "<b>Crime:</b> [c.crimeName]")
to_chat(usr, "<b>Details:</b> [c.crimeDetails]")
to_chat(usr, "Added by [c.author] at [c.time]")
to_chat(usr, "----------")
to_chat(usr, "<b>Notes:</b> [R.fields["notes"]]")
return
if(href_list["add_crime"])
switch(alert("What crime would you like to add?","Security HUD","Minor Crime","Major Crime","Cancel"))
if("Minor Crime")
if(R)
var/t1 = stripped_input("Please input minor crime names:", "Security HUD", "", null)
var/t2 = stripped_multiline_input("Please input minor crime details:", "Security HUD", "", null)
if(R)
if (!t1 || !t2 || !allowed_access)
return
else if(!H.canUseHUD())
return
else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security))
return
var/crime = GLOB.data_core.createCrimeEntry(t1, t2, allowed_access, STATION_TIME_TIMESTAMP("hh:mm:ss", world.time))
GLOB.data_core.addMinorCrime(R.fields["id"], crime)
investigate_log("New Minor Crime: <strong>[t1]</strong>: [t2] | Added to [R.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS)
to_chat(usr, "<span class='notice'>Successfully added a minor crime.</span>")
return
if("Major Crime")
if(R)
var/t1 = stripped_input("Please input major crime names:", "Security HUD", "", null)
var/t2 = stripped_multiline_input("Please input major crime details:", "Security HUD", "", null)
if(R)
if (!t1 || !t2 || !allowed_access)
return
else if (!H.canUseHUD())
return
else if (!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security))
return
var/crime = GLOB.data_core.createCrimeEntry(t1, t2, allowed_access, STATION_TIME_TIMESTAMP("hh:mm:ss", world.time))
GLOB.data_core.addMajorCrime(R.fields["id"], crime)
investigate_log("New Major Crime: <strong>[t1]</strong>: [t2] | Added to [R.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS)
to_chat(usr, "<span class='notice'>Successfully added a major crime.</span>")
return
if(href_list["view_comment"])
if(R)
if(!H.canUseHUD())
return
else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security))
return
to_chat(usr, "<b>Comments/Log:</b>")
var/counter = 1
while(R.fields[text("com_[]", counter)])
to_chat(usr, R.fields[text("com_[]", counter)])
to_chat(usr, "----------")
counter++
return
if(href_list["add_comment"])
if(R)
var/t1 = stripped_multiline_input("Add Comment:", "Secure. records", null, null)
if(R)
if (!t1 || !allowed_access)
return
else if(!H.canUseHUD())
return
else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security))
return
var/counter = 1
while(R.fields[text("com_[]", counter)])
counter++
R.fields["com_[counter]"] = "Made by [allowed_access] on [STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)] [time2text(world.realtime, "MMM DD")], [GLOB.year_integer]<BR>[t1]"
to_chat(usr, "<span class='notice'>Successfully added comment.</span>")
return
to_chat(usr, "<span class='warning'>Unable to locate a data core entry for this person.</span>")
/mob/living/carbon/human/proc/canUseHUD()
return CHECK_MOBILITY(src, MOBILITY_UI)
/mob/living/carbon/human/can_inject(mob/user, error_msg, target_zone, penetrate_thick = FALSE, bypass_immunity = FALSE)
. = 1 // Default to returning true.
if(user && !target_zone)
target_zone = user.zone_selected
if(HAS_TRAIT(src, TRAIT_PIERCEIMMUNE) && !bypass_immunity)
. = 0
// If targeting the head, see if the head item is thin enough.
// If targeting anything else, see if the wear suit is thin enough.
if (!penetrate_thick)
if(above_neck(target_zone))
if(head && istype(head, /obj/item/clothing))
var/obj/item/clothing/CH = head
if (CH.clothing_flags & THICKMATERIAL)
. = 0
else
if(wear_suit && istype(wear_suit, /obj/item/clothing))
var/obj/item/clothing/CS = wear_suit
if (CS.clothing_flags & THICKMATERIAL)
. = 0
if(!. && error_msg && user)
// Might need re-wording.
to_chat(user, "<span class='alert'>There is no exposed flesh or thin material [above_neck(target_zone) ? "on [p_their()] head" : "on [p_their()] body"].</span>")
/mob/living/carbon/human/check_obscured_slots()
. = ..()
if(wear_suit)
if(wear_suit.flags_inv & HIDEGLOVES)
LAZYOR(., ITEM_SLOT_GLOVES)
if(wear_suit.flags_inv & HIDEJUMPSUIT)
LAZYOR(., ITEM_SLOT_ICLOTHING)
if(wear_suit.flags_inv & HIDESHOES)
LAZYOR(., ITEM_SLOT_FEET)
/mob/living/carbon/human/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null)
if(judgement_criteria & JUDGE_EMAGGED)
return 10 //Everyone is a criminal!
var/threatcount = 0
//Lasertag bullshit
if(lasercolor)
if(lasercolor == "b")//Lasertag turrets target the opposing team, how great is that? -Sieve
if(istype(wear_suit, /obj/item/clothing/suit/redtag))
threatcount += 4
if(is_holding_item_of_type(/obj/item/gun/energy/laser/redtag))
threatcount += 4
if(istype(belt, /obj/item/gun/energy/laser/redtag))
threatcount += 2
if(lasercolor == "r")
if(istype(wear_suit, /obj/item/clothing/suit/bluetag))
threatcount += 4
if(is_holding_item_of_type(/obj/item/gun/energy/laser/bluetag))
threatcount += 4
if(istype(belt, /obj/item/gun/energy/laser/bluetag))
threatcount += 2
return threatcount
//Check for ID
var/obj/item/card/id/idcard = get_idcard(FALSE)
if( (judgement_criteria & JUDGE_IDCHECK) && !idcard && name=="Unknown")
threatcount += 4
//Check for weapons
if( (judgement_criteria & JUDGE_WEAPONCHECK) && weaponcheck)
if(!idcard || !(ACCESS_WEAPONS in idcard.access))
for(var/obj/item/I in held_items) //if they're holding a gun
if(weaponcheck.Invoke(I))
threatcount += 4
if(weaponcheck.Invoke(belt) || weaponcheck.Invoke(back)) //if a weapon is present in the belt or back slot
threatcount += 2 //not enough to trigger look_for_perp() on it's own unless they also have criminal status.
//Check for arrest warrant
if(judgement_criteria & JUDGE_RECORDCHECK)
var/perpname = get_face_name(get_id_name())
var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.security)
if(R && R.fields["criminal"])
switch(R.fields["criminal"])
if("*Arrest*")
threatcount += 5
if("Incarcerated")
threatcount += 2
if("Paroled")
threatcount += 2
//Check for dresscode violations
if(istype(head, /obj/item/clothing/head/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/syndi) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi))
threatcount += 4 //fuk u antags <3 //no you
//mindshield implants imply trustworthyness
if(HAS_TRAIT(src, TRAIT_MINDSHIELD))
threatcount -= 1
//Agent cards lower threatlevel.
if(istype(idcard, /obj/item/card/id/syndicate))
threatcount -= 2
return threatcount
//Used for new human mobs created by cloning/goleming/podding
/mob/living/carbon/human/proc/set_cloned_appearance()
if(dna.features["body_model"] == MALE)
facial_hair_style = "Full Beard"
else
facial_hair_style = "Shaved"
hair_style = pick("Bedhead", "Bedhead 2", "Bedhead 3")
underwear = "Nude"
undershirt = "Nude"
socks = "Nude"
update_body(TRUE)
update_hair()
/mob/living/carbon/human/singularity_pull(S, current_size)
..()
if(current_size >= STAGE_THREE)
for(var/obj/item/hand in held_items)
if(prob(current_size * 5) && hand.w_class >= ((11-current_size)/2) && dropItemToGround(hand))
step_towards(hand, src)
to_chat(src, "<span class='warning'>\The [S] pulls \the [hand] from your grip!</span>")
rad_act(current_size * 3)
if(mob_negates_gravity())
return
/mob/living/carbon/human/proc/do_cpr(mob/living/carbon/C)
CHECK_DNA_AND_SPECIES(C)
if(C.stat == DEAD || (HAS_TRAIT(C, TRAIT_FAKEDEATH)))
to_chat(src, "<span class='warning'>[C.name] is dead!</span>")
return
if(is_mouth_covered())
to_chat(src, "<span class='warning'>Remove your mask first!</span>")
return FALSE
if(C.is_mouth_covered())
to_chat(src, "<span class='warning'>Remove [p_their()] mask first!</span>")
return FALSE
if(C.cpr_time < world.time + 30)
visible_message("<span class='notice'>[src] is trying to perform CPR on [C.name]!</span>", \
"<span class='notice'>You try to perform CPR on [C.name]... Hold still!</span>")
if(!do_mob(src, C))
to_chat(src, "<span class='warning'>You fail to perform CPR on [C]!</span>")
return FALSE
var/they_breathe = !HAS_TRAIT(C, TRAIT_NOBREATH)
var/they_lung = C.getorganslot(ORGAN_SLOT_LUNGS)
if(C.health > C.crit_threshold)
return
src.visible_message("[src] performs CPR on [C.name]!", "<span class='notice'>You perform CPR on [C.name].</span>")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "perform_cpr", /datum/mood_event/perform_cpr)
C.cpr_time = world.time
log_combat(src, C, "CPRed")
if(they_breathe && they_lung)
var/suff = min(C.getOxyLoss(), 7)
C.adjustOxyLoss(-suff)
C.updatehealth()
to_chat(C, "<span class='unconscious'>You feel a breath of fresh air enter your lungs... It feels good...</span>")
else if(they_breathe && !they_lung)
to_chat(C, "<span class='unconscious'>You feel a breath of fresh air... but you don't feel any better...</span>")
else
to_chat(C, "<span class='unconscious'>You feel a breath of fresh air... which is a sensation you don't recognise...</span>")
/mob/living/carbon/human/cuff_resist(obj/item/I)
if(dna && dna.check_mutation(HULK))
say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk")
if(..(I, cuff_break = FAST_CUFFBREAK))
dropItemToGround(I)
else
if(..())
dropItemToGround(I)
/mob/living/carbon/human/clean_blood()
var/mob/living/carbon/human/H = src
if(H.gloves)
if(H.gloves.clean_blood())
H.update_inv_gloves()
else
..() // Clear the Blood_DNA list
if(H.bloody_hands)
H.bloody_hands = 0
H.update_inv_gloves()
update_icons() //apply the now updated overlays to the mob
/mob/living/carbon/human/wash_cream()
if(creamed) //clean both to prevent a rare bug
cut_overlay(mutable_appearance('icons/effects/creampie.dmi', "creampie_snout"))
cut_overlay(mutable_appearance('icons/effects/creampie.dmi', "creampie_human"))
creamed = FALSE
//Turns a mob black, flashes a skeleton overlay
//Just like a cartoon!
/mob/living/carbon/human/proc/electrocution_animation(anim_duration)
//Handle mutant parts if possible
if(dna && dna.species)
add_atom_colour("#000000", TEMPORARY_COLOUR_PRIORITY)
var/static/mutable_appearance/electrocution_skeleton_anim
if(!electrocution_skeleton_anim)
electrocution_skeleton_anim = mutable_appearance(icon, "electrocuted_base")
electrocution_skeleton_anim.appearance_flags |= RESET_COLOR|KEEP_APART
add_overlay(electrocution_skeleton_anim)
addtimer(CALLBACK(src, PROC_REF(end_electrocution_animation), electrocution_skeleton_anim), anim_duration)
else //or just do a generic animation
flick_overlay_view(image(icon,src,"electrocuted_generic",ABOVE_MOB_LAYER), src, anim_duration)
/mob/living/carbon/human/proc/end_electrocution_animation(mutable_appearance/MA)
remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, "#000000")
cut_overlay(MA)
/mob/living/carbon/human/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE, check_resting = TRUE)
if(incapacitated() || (check_resting && !CHECK_MOBILITY(src, MOBILITY_STAND)))
to_chat(src, "<span class='warning'>You can't do that right now!</span>")
return FALSE
if(!Adjacent(M) && (M.loc != src))
if((be_close == 0) || (!no_tk && (dna.check_mutation(TK) && tkMaxRangeCheck(src, M))))
return TRUE
to_chat(src, "<span class='warning'>You are too far away!</span>")
return FALSE
return TRUE
/mob/living/carbon/human/resist_restraints()
if(wear_suit && wear_suit.breakouttime)
MarkResistTime()
cuff_resist(wear_suit)
else
..()
/mob/living/carbon/human/replace_records_name(oldname,newname) // Only humans have records right now, move this up if changed.
for(var/list/L in list(GLOB.data_core.general,GLOB.data_core.medical,GLOB.data_core.security,GLOB.data_core.locked))
var/datum/data/record/R = find_record("name", oldname, L)
if(R)
R.fields["name"] = newname
/mob/living/carbon/human/get_total_tint()
. = ..()
if(glasses)
. += glasses.tint
/mob/living/carbon/human/update_health_hud()
if(!client || !hud_used)
return
if(dna.species.update_health_hud())
return
else
if(hud_used.healths)
var/health_amount = min(health, maxHealth - clamp(getStaminaLoss()-50, 0, 80))//CIT CHANGE - makes staminaloss have less of an impact on the health hud
if(..(health_amount)) //not dead
switch(hal_screwyhud)
if(SCREWYHUD_CRIT)
hud_used.healths.icon_state = "health6"
if(SCREWYHUD_DEAD)
hud_used.healths.icon_state = "health7"
if(SCREWYHUD_HEALTHY)
hud_used.healths.icon_state = "health0"
if(hud_used.healthdoll)
hud_used.healthdoll.cut_overlays()
if(stat != DEAD)
hud_used.healthdoll.icon_state = "healthdoll_OVERLAY"
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
var/damage = BP.burn_dam + BP.brute_dam
var/comparison = (BP.max_damage/5)
var/icon_num = 0
if(damage)
icon_num = 1
if(damage > (comparison))
icon_num = 2
if(damage > (comparison*2))
icon_num = 3
if(damage > (comparison*3))
icon_num = 4
if(damage > (comparison*4))
icon_num = 5
if(hal_screwyhud == SCREWYHUD_HEALTHY)
icon_num = 0
if(icon_num)
hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[BP.body_zone][icon_num]"))
for(var/t in get_missing_limbs()) //Missing limbs
hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[t]6"))
for(var/t in get_disabled_limbs()) //Disabled limbs
hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[t]7"))
else
hud_used.healthdoll.icon_state = "healthdoll_DEAD"
hud_used.staminas?.update_icon_state()
hud_used.staminabuffer?.mark_dirty()
/mob/living/carbon/human/fully_heal(admin_revive = FALSE)
if(admin_revive)
regenerate_limbs()
regenerate_organs()
remove_all_embedded_objects()
set_heartattack(FALSE)
drunkenness = 0
for(var/datum/mutation/human/HM in dna.mutations)
if(HM.quality != POSITIVE)
dna.remove_mutation(HM.name)
if(blood_volume < (BLOOD_VOLUME_NORMAL*blood_ratio))
blood_volume = (BLOOD_VOLUME_NORMAL*blood_ratio)
integrating_blood = 0
..()
/mob/living/carbon/human/check_weakness(obj/item/weapon, mob/living/attacker)
. = ..()
if (dna && dna.species)
. += dna.species.check_weakness(weapon, attacker)
/mob/living/carbon/human/is_literate()
return TRUE
/mob/living/carbon/human/update_gravity(has_gravity,override = 0)
if(dna && dna.species) //prevents a runtime while a human is being monkeyfied
override = dna.species.override_float
..()
/mob/living/carbon/human/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, vomit_type = VOMIT_TOXIC, harm = TRUE, force = FALSE, purge_ratio = 0.1)
if(blood && dna?.species && (NOBLOOD in dna.species.species_traits))
if(message)
visible_message("<span class='warning'>[src] dry heaves!</span>", \
"<span class='userdanger'>You try to throw up, but there's nothing in your stomach!</span>")
if(stun)
DefaultCombatKnockdown(200)
return TRUE
..()
/mob/living/carbon/human/vv_get_dropdown()
. = ..()
VV_DROPDOWN_OPTION("", "---------")
VV_DROPDOWN_OPTION(VV_HK_COPY_OUTFIT, "Copy Outfit")
VV_DROPDOWN_OPTION(VV_HK_MOD_QUIRKS, "Add/Remove Quirks")
VV_DROPDOWN_OPTION(VV_HK_MAKE_MONKEY, "Make Monkey")
VV_DROPDOWN_OPTION(VV_HK_MAKE_CYBORG, "Make Cyborg")
VV_DROPDOWN_OPTION(VV_HK_MAKE_SLIME, "Make Slime")
VV_DROPDOWN_OPTION(VV_HK_MAKE_ALIEN, "Make Alien")
VV_DROPDOWN_OPTION(VV_HK_SET_SPECIES, "Set Species")
VV_DROPDOWN_OPTION(VV_HK_PURRBATION, "Toggle Purrbation")
VV_DROPDOWN_OPTION(VV_HK_APPLY_PREFS, "Apply preferences")
/mob/living/carbon/human/vv_do_topic(list/href_list)
. = ..()
if(href_list[VV_HK_COPY_OUTFIT])
if(!check_rights(R_SPAWN))
return
copy_outfit()
if(href_list[VV_HK_MOD_QUIRKS])
if(!check_rights(R_SPAWN))
return
var/list/options = list("Clear"="Clear")
for(var/x in subtypesof(/datum/quirk))
var/datum/quirk/T = x
var/qname = initial(T.name)
options[has_quirk(T) ? "[qname] (Remove)" : "[qname] (Add)"] = T
var/result = input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in options
if(result)
if(result == "Clear")
for(var/datum/quirk/q in roundstart_quirks)
remove_quirk(q.type)
else
var/T = options[result]
if(has_quirk(T))
remove_quirk(T)
else
add_quirk(T,TRUE)
if(href_list[VV_HK_MAKE_MONKEY])
if(!check_rights(R_SPAWN))
return
if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
return
usr.client.holder.Topic("vv_override", list("monkeyone"=href_list[VV_HK_TARGET]))
if(href_list[VV_HK_MAKE_CYBORG])
if(!check_rights(R_SPAWN))
return
if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
return
usr.client.holder.Topic("vv_override", list("makerobot"=href_list[VV_HK_TARGET]))
if(href_list[VV_HK_MAKE_ALIEN])
if(!check_rights(R_SPAWN))
return
if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
return
usr.client.holder.Topic("vv_override", list("makealien"=href_list[VV_HK_TARGET]))
if(href_list[VV_HK_MAKE_SLIME])
if(!check_rights(R_SPAWN))
return
if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
return
usr.client.holder.Topic("vv_override", list("makeslime"=href_list[VV_HK_TARGET]))
if(href_list[VV_HK_SET_SPECIES])
if(!check_rights(R_SPAWN))
return
var/result = input(usr, "Please choose a new species","Species") as null|anything in GLOB.species_list
if(result)
var/newtype = GLOB.species_list[result]
admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [src] to [result]")
set_species(newtype)
if(href_list[VV_HK_PURRBATION])
if(!check_rights(R_SPAWN))
return
if(!ishumanbasic(src))
to_chat(usr, "This can only be done to the basic human species at the moment.")
return
var/success = purrbation_toggle(src)
if(success)
to_chat(usr, "Put [src] on purrbation.")
log_admin("[key_name(usr)] has put [key_name(src)] on purrbation.")
var/msg = "<span class='notice'>[key_name_admin(usr)] has put [key_name(src)] on purrbation.</span>"
message_admins(msg)
admin_ticket_log(src, msg)
else
to_chat(usr, "Removed [src] from purrbation.")
log_admin("[key_name(usr)] has removed [key_name(src)] from purrbation.")
var/msg = "<span class='notice'>[key_name_admin(usr)] has removed [key_name(src)] from purrbation.</span>"
message_admins(msg)
admin_ticket_log(src, msg)
if(href_list[VV_HK_APPLY_PREFS])
if(!check_rights(R_SPAWN))
return
if(!client)
var/bigtext = {"This action requires a client, if you need to do anything special, follow this short guide:
<blockquote class="info">
Mark this mob, then navigate to the preferences of the client you desire and call copy_to() with one argument, when it asks for the argument, browse to the bottom of the list and select marked datum, if you've followed this guide correctly, the mob will be turned into the character from the preferences you used.
</blockquote>
"}
to_chat(usr, bigtext)
return
var/datum/preferences/copying_this_one = client.prefs // turns out that prefs always exist if the client leaves, i'm not checking for client again
var/is_this_guy_trolling_the_admin = copying_this_one.default_slot
if(alert(usr, "Confirm reapply preferences?", "", "I'm sure", "Cancel") != "I'm sure")
return
if(is_this_guy_trolling_the_admin != copying_this_one.default_slot) // why would you do this, broooo
if(alert(usr, "The user changed their character slot while you were deciding, are you sure you want to do this? They might change their mind again and i will not protect again this time", "Uh oh", "I'm sure", "They did what?") != "I'm sure")
return
copying_this_one.copy_to(src)
var/change_text = "reapplied [key_name(src, TRUE)]'s preferences, [(is_this_guy_trolling_the_admin != copying_this_one.default_slot) ? "changing their character" : "resetting their character"]."
to_chat(usr, capitalize(change_text))
log_admin("[key_name(usr)] has [change_text]")
message_admins(span_notice("[key_name_admin(usr)] has [change_text]"))
admin_ticket_log(src, span_notice("[key_name_admin(usr, FALSE)] has [change_text]")) // In case they complained in an ahelp, we'll let them know anything happened
/mob/living/carbon/human/MouseDrop_T(mob/living/target, mob/living/user)
var/GS_needed = istype(target, /mob/living/silicon/pai)? GRAB_PASSIVE : GRAB_AGGRESSIVE
if(pulling == target && grab_state >= GS_needed && stat == CONSCIOUS)
//If they dragged themselves and we're currently aggressively grabbing them try to piggyback
if(user == target && can_piggyback(target))
piggyback(target)
return
//If you dragged them to you and you're aggressively grabbing try to fireman carry them
else if(user == src)
if(user.a_intent == INTENT_GRAB)
fireman_carry(target)
return
. = ..()
//src is the user that will be carrying, target is the mob to be carried
/mob/living/carbon/human/proc/can_piggyback(mob/living/target)
return (iscarbon(target) || ispAI(target)) && target.stat == CONSCIOUS && CHECK_MOBILITY(src, MOBILITY_STAND)
/mob/living/carbon/human/proc/can_be_firemanned(mob/living/carbon/target)
return (ishuman(target) && !CHECK_MOBILITY(target, MOBILITY_STAND)) || ispAI(target)
/mob/living/carbon/human/proc/fireman_carry(mob/living/carbon/target)
var/carrydelay = 50 //if you have latex you are faster at grabbing
var/skills_space = "" //cobby told me to do this
if(HAS_TRAIT(src, TRAIT_QUICKER_CARRY))
carrydelay = 30
skills_space = "expertly "
else if(HAS_TRAIT(src, TRAIT_QUICK_CARRY))
carrydelay = 40
skills_space = "quickly "
if(can_be_firemanned(target) && !incapacitated(FALSE, TRUE))
visible_message("<span class='notice'>[src] starts [skills_space]lifting [target] onto their back..</span>",
//Joe Medic starts quickly/expertly lifting Grey Tider onto their back..
"<span class='notice'>[carrydelay < 35 ? "Using your gloves' nanochips, you" : "You"] [skills_space]start to lift [target] onto your back[carrydelay == 40 ? ", while assisted by the nanochips in your gloves.." : "..."]</span>")
//(Using your gloves' nanochips, you/You) ( /quickly/expertly) start to lift Grey Tider onto your back(, while assisted by the nanochips in your gloves../...)
if(do_after(src, carrydelay, target, extra_checks = CALLBACK(src, PROC_REF(can_be_firemanned), target)))
//Second check to make sure they're still valid to be carried
if(can_be_firemanned(target) && !incapacitated(FALSE, TRUE))
buckle_mob(target, TRUE, TRUE, 90, 1, 0, TRUE)
return
visible_message("<span class='warning'>[src] fails to fireman carry [target]!")
else
if (ishuman(target))
to_chat(src, "<span class='notice'>You can't fireman carry [target] while they're standing!</span>")
else
to_chat(src, "<span class='notice'>You can't seem to fireman carry that kind of species.</span>")
/mob/living/carbon/human/proc/piggyback(mob/living/carbon/target)
if(can_piggyback(target))
visible_message("<span class='notice'>[target] starts to climb onto [src]...</span>")
if(do_after(target, 1.5 SECONDS, src, IGNORE_INCAPACITATED, extra_checks = CALLBACK(src, PROC_REF(can_piggyback), target)))
if(can_piggyback(target))
if(target.incapacitated(FALSE, TRUE) || incapacitated(FALSE, TRUE))
target.visible_message("<span class='warning'>[target] can't hang onto [src]!</span>")
return
buckle_mob(target, TRUE, TRUE, 0, 1, 2, FALSE)
else
visible_message("<span class='warning'>[target] fails to climb onto [src]!</span>")
else
to_chat(target, "<span class='warning'>You can't piggyback ride [src] right now!</span>")
/mob/living/carbon/human/buckle_mob(mob/living/target, force = FALSE, check_loc = TRUE, lying_buckle = 0, hands_needed = 0, target_hands_needed = 0, fireman = FALSE)
if(!force)//humans are only meant to be ridden through piggybacking and special cases
return
if(!is_type_in_typecache(target, can_ride_typecache))
target.visible_message("<span class='warning'>[target] really can't seem to mount [src]...</span>")
return
buckle_lying = lying_buckle
var/datum/component/riding/human/riding_datum = LoadComponent(/datum/component/riding/human)
if(target_hands_needed)
riding_datum.ride_check_rider_restrained = TRUE
if(buckled_mobs && ((target in buckled_mobs) || (buckled_mobs.len >= max_buckled_mobs)) || buckled)
return
if(istype(target, /mob/living/silicon/pai))
hands_needed = 1
target_hands_needed = 0
var/equipped_hands_self
var/equipped_hands_target
if(hands_needed)
equipped_hands_self = riding_datum.equip_buckle_inhands(src, hands_needed, target)
if(target_hands_needed)
equipped_hands_target = riding_datum.equip_buckle_inhands(target, target_hands_needed)
if(hands_needed || target_hands_needed)
if(hands_needed && !equipped_hands_self)
src.visible_message("<span class='warning'>[src] can't get a grip on [target] because their hands are full!</span>",
"<span class='warning'>You can't get a grip on [target] because your hands are full!</span>")
return
else if(target_hands_needed && !equipped_hands_target)
target.visible_message("<span class='warning'>[target] can't get a grip on [src] because their hands are full!</span>",
"<span class='warning'>You can't get a grip on [src] because your hands are full!</span>")
return
stop_pulling()
riding_datum.handle_vehicle_layer(dir)
riding_datum.fireman_carrying = fireman
. = ..(target, force, check_loc)
/mob/living/carbon/human/proc/is_shove_knockdown_blocked() //If you want to add more things that block shove knockdown, extend this
for(var/obj/item/clothing/C in get_equipped_items()) //doesn't include pockets
if(C.blocks_shove_knockdown)
return TRUE
return FALSE
/mob/living/carbon/human/updatehealth()
. = ..()
dna?.species.spec_updatehealth(src)
if(HAS_TRAIT(src, TRAIT_IGNORESLOWDOWN)) //if we want to ignore slowdown from damage and equipment
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown)
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying)
return
if(!HAS_TRAIT(src, TRAIT_IGNOREDAMAGESLOWDOWN)) //if we want to ignore slowdown from damage, but not from equipment
var/scaling = maxHealth / 100
var/health_deficiency = max(((maxHealth / scaling) - (health / scaling)), max(0, getStaminaLoss() - 39))
if(health_deficiency >= 40)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown, TRUE, health_deficiency / 75)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying, TRUE, health_deficiency / 25)
else
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown)
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying)
else
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown)
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying)
/mob/living/carbon/human/is_bleeding()
if(NOBLOOD in dna.species.species_traits || bleedsuppress)
return FALSE
return ..()
/mob/living/carbon/human/get_total_bleed_rate()
if(NOBLOOD in dna.species.species_traits)
return FALSE
return ..()
/mob/living/carbon/human/species
var/race = null
/mob/living/carbon/human/species/Initialize(mapload)
. = ..()
set_species(race)
/**
* # `spec_trait_examine_font()`
*
* This gets a humanoid's special examine font, which is used to color their species name during examine / health analyzing.
* The first of these that applies is returned.
* Returns:
* * Metallic font if robotic
* * Cyan if a toxinlover
* * Purple if plasmaperson
* * Rock / Brownish if a golem
* * Green if none of the others apply (aka, generic organic)
*/
/mob/living/carbon/human/proc/spec_trait_examine_font()
if(HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
return "<font color='#aaa9ad'>"
if(HAS_TRAIT(src, TRAIT_TOXINLOVER))
return "<font color='#00ffff'>"
if(isplasmaman(src))
return "<font color='#800080'>"
if(isgolem(src))
return "<font color='#8b4513'>"
return "<font color='#18d855'>"
/mob/living/carbon/human/get_tooltip_data()
var/t_He = p_they(TRUE)
var/t_is = p_are()
. = list()
var/skipface = (wear_mask && (wear_mask.flags_inv & HIDEFACE)) || (head && (head.flags_inv & HIDEFACE))
if(skipface || get_visible_name() == "Unknown")
. += "You can't make out what species they are."
else
. += "[t_He] [t_is] a [spec_trait_examine_font()][dna.custom_species ? dna.custom_species : dna.species.name]</font>"
SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, usr, .)
/mob/living/carbon/human/chestonly
bodyparts = list(/obj/item/bodypart/chest)
/mob/living/carbon/human/species/abductor
race = /datum/species/abductor
/mob/living/carbon/human/species/android
race = /datum/species/android
/mob/living/carbon/human/species/corporate
race = /datum/species/corporate
/mob/living/carbon/human/species/felinid
race = /datum/species/human/felinid
/mob/living/carbon/human/species/fly
race = /datum/species/fly
/mob/living/carbon/human/species/golem
race = /datum/species/golem
/mob/living/carbon/human/species/golem/random
race = /datum/species/golem/random
/mob/living/carbon/human/species/golem/adamantine
race = /datum/species/golem/adamantine
/mob/living/carbon/human/species/golem/plasma
race = /datum/species/golem/plasma
/mob/living/carbon/human/species/golem/diamond
race = /datum/species/golem/diamond
/mob/living/carbon/human/species/golem/gold
race = /datum/species/golem/gold
/mob/living/carbon/human/species/golem/silver
race = /datum/species/golem/silver
/mob/living/carbon/human/species/golem/plasteel
race = /datum/species/golem/plasteel
/mob/living/carbon/human/species/golem/titanium
race = /datum/species/golem/titanium
/mob/living/carbon/human/species/golem/plastitanium
race = /datum/species/golem/plastitanium
/mob/living/carbon/human/species/golem/alien_alloy
race = /datum/species/golem/alloy
/mob/living/carbon/human/species/golem/wood
race = /datum/species/golem/wood
/mob/living/carbon/human/species/golem/uranium
race = /datum/species/golem/uranium
/mob/living/carbon/human/species/golem/sand
race = /datum/species/golem/sand
/mob/living/carbon/human/species/golem/glass
race = /datum/species/golem/glass
/mob/living/carbon/human/species/golem/bluespace
race = /datum/species/golem/bluespace
/mob/living/carbon/human/species/golem/bananium
race = /datum/species/golem/bananium
/mob/living/carbon/human/species/golem/blood_cult
race = /datum/species/golem/runic
/mob/living/carbon/human/species/golem/cloth
race = /datum/species/golem/cloth
/mob/living/carbon/human/species/golem/plastic
race = /datum/species/golem/plastic
/mob/living/carbon/human/species/golem/bronze
race = /datum/species/golem/bronze
/mob/living/carbon/human/species/golem/cardboard
race = /datum/species/golem/cardboard
/mob/living/carbon/human/species/golem/leather
race = /datum/species/golem/leather
/mob/living/carbon/human/species/golem/bone
race = /datum/species/golem/bone
/mob/living/carbon/human/species/golem/durathread
race = /datum/species/golem/durathread
/mob/living/carbon/human/species/golem/clockwork
race = /datum/species/golem/clockwork
/mob/living/carbon/human/species/golem/clockwork/no_scrap
race = /datum/species/golem/clockwork/no_scrap
/mob/living/carbon/human/species/jelly
race = /datum/species/jelly
/mob/living/carbon/human/species/jelly/slime
race = /datum/species/jelly/slime
/mob/living/carbon/human/species/jelly/stargazer
race = /datum/species/jelly/stargazer
/mob/living/carbon/human/species/jelly/luminescent
race = /datum/species/jelly/luminescent
/mob/living/carbon/human/species/lizard
race = /datum/species/lizard
/mob/living/carbon/human/species/ethereal
race = /datum/species/ethereal
/mob/living/carbon/human/species/lizard/ashwalker
race = /datum/species/lizard/ashwalker
/mob/living/carbon/human/species/insect
race = /datum/species/insect
/mob/living/carbon/human/species/mush
race = /datum/species/mush
/mob/living/carbon/human/species/plasma
race = /datum/species/plasmaman
/mob/living/carbon/human/species/pod
race = /datum/species/pod
/mob/living/carbon/human/species/shadow
race = /datum/species/shadow
/mob/living/carbon/human/species/shadow/nightmare
race = /datum/species/shadow/nightmare
/mob/living/carbon/human/species/skeleton
race = /datum/species/skeleton
/mob/living/carbon/human/species/synth
race = /datum/species/synth
/mob/living/carbon/human/species/synth/military
race = /datum/species/synth/military
/mob/living/carbon/human/species/vampire
race = /datum/species/vampire
/mob/living/carbon/human/species/zombie
race = /datum/species/zombie
/mob/living/carbon/human/species/zombie/infectious
race = /datum/species/zombie/infectious
/mob/living/carbon/human/species/zombie/krokodil_addict
race = /datum/species/krokodil_addict
/mob/living/carbon/human/species/mammal
race = /datum/species/mammal
/mob/living/carbon/human/species/insect
race = /datum/species/insect
/mob/living/carbon/human/species/xeno
race = /datum/species/xeno
/mob/living/carbon/human/species/ipc
race = /datum/species/ipc
/mob/living/carbon/human/species/roundstartslime
race = /datum/species/jelly/roundstartslime
/mob/living/carbon/human/species/arachnid
race = /datum/species/arachnid