mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
1471 lines
46 KiB
Plaintext
1471 lines
46 KiB
Plaintext
/mob/living/Initialize()
|
|
. = ..()
|
|
if(unique_name)
|
|
name = "[name] ([rand(1, 1000)])"
|
|
real_name = name
|
|
var/datum/atom_hud/data/human/medical/advanced/medhud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
|
|
medhud.add_to_hud(src)
|
|
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
|
|
diag_hud.add_to_hud(src)
|
|
faction += "[REF(src)]"
|
|
GLOB.mob_living_list += src
|
|
initialize_footstep()
|
|
if(startDead)
|
|
death(FALSE)
|
|
|
|
/mob/living/proc/initialize_footstep()
|
|
AddComponent(/datum/component/footstep)
|
|
|
|
/mob/living/prepare_huds()
|
|
..()
|
|
prepare_data_huds()
|
|
|
|
/mob/living/proc/prepare_data_huds()
|
|
med_hud_set_health()
|
|
med_hud_set_status()
|
|
|
|
/mob/living/Destroy()
|
|
for(var/datum/status_effect/effect as anything in status_effects)
|
|
// The status effect calls on_remove when its mob is deleted
|
|
if(effect.on_remove_on_mob_delete)
|
|
qdel(effect)
|
|
else
|
|
effect.be_replaced()
|
|
|
|
if(ranged_ability)
|
|
ranged_ability.remove_ranged_ability(src)
|
|
|
|
if(buckled)
|
|
buckled.unbuckle_mob(src,force=1)
|
|
|
|
remove_from_all_data_huds()
|
|
GLOB.mob_living_list -= src
|
|
QDEL_LIST(diseases)
|
|
QDEL_LIST(surgeries)
|
|
return ..()
|
|
|
|
/mob/living/onZImpact(turf/T, levels)
|
|
ZImpactDamage(T, levels)
|
|
return ..()
|
|
|
|
/mob/living/proc/ZImpactDamage(turf/T, levels)
|
|
visible_message(span_danger("[src] crashes into [T] with a sickening noise!"))
|
|
adjustBruteLoss((levels * 5) ** 1.5)
|
|
Knockdown(levels * 50)
|
|
|
|
/mob/living/proc/OpenCraftingMenu()
|
|
return
|
|
|
|
//Generic Bump(). Override MobBump() and ObjBump() instead of this.
|
|
/mob/living/Bump(atom/A)
|
|
if(..()) //we are thrown onto something
|
|
return
|
|
if (buckled || now_pushing)
|
|
return
|
|
if(ismob(A))
|
|
var/mob/M = A
|
|
if(MobBump(M))
|
|
return
|
|
if(isobj(A))
|
|
var/obj/O = A
|
|
if(ObjBump(O))
|
|
return
|
|
if(ismovable(A))
|
|
var/atom/movable/AM = A
|
|
if(PushAM(AM, move_force))
|
|
return
|
|
|
|
/mob/living/Bumped(atom/movable/AM)
|
|
..()
|
|
last_bumped = world.time
|
|
|
|
//Called when we bump onto a mob
|
|
/mob/living/proc/MobBump(mob/M)
|
|
//Even if we don't push/swap places, we "touched" them, so spread fire
|
|
spreadFire(M)
|
|
|
|
if(now_pushing)
|
|
return TRUE
|
|
|
|
var/they_can_move = TRUE
|
|
if(isliving(M))
|
|
var/mob/living/L = M
|
|
they_can_move = L.mobility_flags & MOBILITY_MOVE
|
|
//Also spread diseases
|
|
for(var/thing in diseases)
|
|
var/datum/disease/D = thing
|
|
if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)
|
|
L.ContactContractDisease(D)
|
|
|
|
for(var/thing in L.diseases)
|
|
var/datum/disease/D = thing
|
|
if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)
|
|
ContactContractDisease(D)
|
|
|
|
//Should stop you pushing a restrained person out of the way
|
|
if(L.pulledby && L.pulledby != src && L.restrained())
|
|
if(!(world.time % 5))
|
|
to_chat(src, span_warning("[L] is restrained, you cannot push past."))
|
|
return TRUE
|
|
|
|
if(L.pulling)
|
|
if(ismob(L.pulling))
|
|
var/mob/P = L.pulling
|
|
if(P.restrained())
|
|
if(!(world.time % 5))
|
|
to_chat(src, span_warning("[L] is restraining [P], you cannot push past."))
|
|
return TRUE
|
|
|
|
if(moving_diagonally)//no mob swap during diagonal moves.
|
|
return TRUE
|
|
|
|
if(!M.buckled && !M.has_buckled_mobs())
|
|
var/mob_swap = FALSE
|
|
var/too_strong = (M.move_resist > move_force) //can't swap with immovable objects unless they help us
|
|
if(!they_can_move) //we have to physically move them
|
|
if(!too_strong)
|
|
mob_swap = TRUE
|
|
else
|
|
//You can swap with the person you are dragging on grab intent, and restrained people in most cases
|
|
if(M.pulledby == src && a_intent == INTENT_GRAB && !too_strong)
|
|
mob_swap = TRUE
|
|
else if(
|
|
!(HAS_TRAIT(M, TRAIT_NOMOBSWAP) || HAS_TRAIT(src, TRAIT_NOMOBSWAP))&&\
|
|
((M.restrained() && !too_strong) || M.a_intent == INTENT_HELP) &&\
|
|
(restrained() || a_intent == INTENT_HELP)
|
|
)
|
|
mob_swap = TRUE
|
|
if(mob_swap)
|
|
//switch our position with M
|
|
if(loc && !loc.Adjacent(M.loc))
|
|
return TRUE
|
|
now_pushing = 1
|
|
var/oldloc = loc
|
|
var/oldMloc = M.loc
|
|
|
|
|
|
var/M_passmob = (M.pass_flags & PASSMOB) // we give PASSMOB to both mobs to avoid bumping other mobs during swap.
|
|
var/src_passmob = (pass_flags & PASSMOB)
|
|
M.pass_flags |= PASSMOB
|
|
pass_flags |= PASSMOB
|
|
|
|
var/move_failed = FALSE
|
|
if(!M.Move(oldloc) || !Move(oldMloc))
|
|
M.forceMove(oldMloc)
|
|
forceMove(oldloc)
|
|
move_failed = TRUE
|
|
if(!src_passmob)
|
|
pass_flags &= ~PASSMOB
|
|
if(!M_passmob)
|
|
M.pass_flags &= ~PASSMOB
|
|
|
|
now_pushing = 0
|
|
|
|
if(!move_failed)
|
|
return TRUE
|
|
|
|
//okay, so we didn't switch. but should we push?
|
|
//not if he's not CANPUSH of course
|
|
if(!(M.status_flags & CANPUSH))
|
|
return TRUE
|
|
if(isliving(M))
|
|
var/mob/living/L = M
|
|
if(HAS_TRAIT(L, TRAIT_PUSHIMMUNE))
|
|
return TRUE
|
|
//If they're a human, and they're not in help intent, block pushing
|
|
if(ishuman(M) && (M.a_intent != INTENT_HELP))
|
|
return TRUE
|
|
//anti-riot equipment is also anti-push
|
|
for(var/obj/item/I in M.held_items)
|
|
if(!istype(M, /obj/item/clothing))
|
|
if(prob(I.block_chance*2))
|
|
return
|
|
|
|
/mob/living/get_photo_description(obj/item/camera/camera)
|
|
var/list/mob_details = list()
|
|
var/list/holding = list()
|
|
var/len = length(held_items)
|
|
if(len)
|
|
for(var/obj/item/I in held_items)
|
|
if(!holding.len)
|
|
holding += "They are holding \a [I]"
|
|
else if(held_items.Find(I) == len)
|
|
holding += ", and \a [I]."
|
|
else
|
|
holding += ", \a [I]"
|
|
holding += "."
|
|
mob_details += "You can also see [src] on the photo[health < (maxHealth * 0.75) ? ", looking a bit hurt":""][holding ? ". [holding.Join("")]":"."]."
|
|
return mob_details.Join("")
|
|
|
|
//Called when we bump onto an obj
|
|
/mob/living/proc/ObjBump(obj/O)
|
|
return
|
|
|
|
//Called when we want to push an atom/movable
|
|
/mob/living/proc/PushAM(atom/movable/AM, force = move_force)
|
|
if(now_pushing)
|
|
return TRUE
|
|
if(moving_diagonally)// no pushing during diagonal moves.
|
|
return TRUE
|
|
if(!client && (mob_size < MOB_SIZE_SMALL))
|
|
return
|
|
now_pushing = TRUE
|
|
var/t = get_dir(src, AM)
|
|
var/push_anchored = FALSE
|
|
if((AM.move_resist * MOVE_FORCE_CRUSH_RATIO) <= force)
|
|
if(move_crush(AM, move_force, t))
|
|
push_anchored = TRUE
|
|
if((AM.move_resist * MOVE_FORCE_FORCEPUSH_RATIO) <= force) //trigger move_crush and/or force_push regardless of if we can push it normally
|
|
if(force_push(AM, move_force, t, push_anchored))
|
|
push_anchored = TRUE
|
|
if((AM.anchored && !push_anchored) || (force < (AM.move_resist * MOVE_FORCE_PUSH_RATIO)))
|
|
now_pushing = FALSE
|
|
return
|
|
if (istype(AM, /obj/structure/window))
|
|
var/obj/structure/window/W = AM
|
|
if(W.fulltile)
|
|
for(var/obj/structure/window/win in get_step(W,t))
|
|
now_pushing = FALSE
|
|
return
|
|
if(pulling == AM)
|
|
stop_pulling()
|
|
var/current_dir
|
|
if(isliving(AM))
|
|
current_dir = AM.dir
|
|
if(AM.Move(get_step(AM.loc, t), t, glide_size))
|
|
Move(get_step(loc, t), t)
|
|
if(current_dir)
|
|
AM.setDir(current_dir)
|
|
now_pushing = FALSE
|
|
|
|
/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, supress_message = FALSE)
|
|
if(!AM || !src)
|
|
return FALSE
|
|
if(!(AM.can_be_pulled(src, state, force)))
|
|
return FALSE
|
|
if(throwing || !(mobility_flags & MOBILITY_PULL))
|
|
return FALSE
|
|
|
|
AM.add_fingerprint(src)
|
|
|
|
// If we're pulling something then drop what we're currently pulling and pull this instead.
|
|
if(pulling)
|
|
// Are we trying to pull something we are already pulling? Then just stop here, no need to continue.
|
|
if(AM == pulling)
|
|
return
|
|
stop_pulling()
|
|
|
|
changeNext_move(CLICK_CD_GRABBING)
|
|
|
|
if(AM.pulledby)
|
|
if(!supress_message)
|
|
visible_message(span_danger("[src] has pulled [AM] from [AM.pulledby]'s grip."))
|
|
log_combat(AM, AM.pulledby, "pulled from", src)
|
|
AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once.
|
|
|
|
pulling = AM
|
|
AM.pulledby = src
|
|
if(!supress_message)
|
|
var/sound_to_play = 'sound/weapons/thudswoosh.ogg'
|
|
if(ishuman(src))
|
|
var/mob/living/carbon/human/H = src
|
|
if(H.dna.species.grab_sound)
|
|
sound_to_play = H.dna.species.grab_sound
|
|
if(HAS_TRAIT(H, TRAIT_STRONG_GRABBER))
|
|
sound_to_play = null
|
|
playsound(src.loc, sound_to_play, 50, 1, -1)
|
|
update_pull_hud_icon()
|
|
|
|
if(ismob(AM))
|
|
var/mob/M = AM
|
|
|
|
log_combat(src, M, "grabbed", addition="passive grab")
|
|
if(!supress_message && !(iscarbon(AM) && HAS_TRAIT(src, TRAIT_STRONG_GRABBER)))
|
|
visible_message(span_warning("[src] has grabbed [M] passively!"))
|
|
if(!iscarbon(src))
|
|
M.LAssailant = null
|
|
else
|
|
M.LAssailant = WEAKREF(usr)
|
|
if(isliving(M))
|
|
var/mob/living/L = M
|
|
//Share diseases that are spread by touch
|
|
for(var/thing in diseases)
|
|
var/datum/disease/D = thing
|
|
if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)
|
|
L.ContactContractDisease(D)
|
|
|
|
for(var/thing in L.diseases)
|
|
var/datum/disease/D = thing
|
|
if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)
|
|
ContactContractDisease(D)
|
|
|
|
if(iscarbon(L))
|
|
var/mob/living/carbon/C = L
|
|
if(HAS_TRAIT(src, TRAIT_STRONG_GRABBER))
|
|
C.grippedby(src)
|
|
|
|
update_pull_movespeed()
|
|
|
|
set_pull_offsets(M, state)
|
|
|
|
/mob/living/proc/set_pull_offsets(mob/living/M, grab_state = GRAB_PASSIVE)
|
|
if(M.buckled)
|
|
return //don't make them change direction or offset them if they're buckled into something.
|
|
var/offset = 0
|
|
switch(grab_state)
|
|
if(GRAB_PASSIVE)
|
|
offset = GRAB_PIXEL_SHIFT_PASSIVE
|
|
if(GRAB_AGGRESSIVE)
|
|
offset = GRAB_PIXEL_SHIFT_AGGRESSIVE
|
|
if(GRAB_NECK)
|
|
offset = GRAB_PIXEL_SHIFT_NECK
|
|
if(GRAB_KILL)
|
|
offset = GRAB_PIXEL_SHIFT_NECK
|
|
M.setDir(get_dir(M, src))
|
|
switch(M.dir)
|
|
if(NORTH)
|
|
animate(M, pixel_x = 0, pixel_y = offset, 3)
|
|
if(SOUTH)
|
|
animate(M, pixel_x = 0, pixel_y = -offset, 3)
|
|
if(EAST)
|
|
if(M.lying == 270) //update the dragged dude's direction if we've turned
|
|
M.lying = 90
|
|
M.update_transform() //force a transformation update, otherwise it'll take a few ticks for update_mobility() to do so
|
|
M.lying_prev = M.lying
|
|
animate(M, pixel_x = offset, pixel_y = 0, 3)
|
|
if(WEST)
|
|
if(M.lying == 90)
|
|
M.lying = 270
|
|
M.update_transform()
|
|
M.lying_prev = M.lying
|
|
animate(M, pixel_x = -offset, pixel_y = 0, 3)
|
|
|
|
/mob/living/proc/reset_pull_offsets(mob/living/M, override)
|
|
if(!override && M.buckled)
|
|
return
|
|
animate(M, pixel_x = 0, pixel_y = 0, 1)
|
|
|
|
//mob verbs are a lot faster than object verbs
|
|
//for more info on why this is not atom/pull, see examinate() in mob.dm
|
|
/mob/living/verb/pulled(atom/movable/AM as mob|obj in oview(1))
|
|
set name = "Pull"
|
|
set category = "Object"
|
|
|
|
if(istype(AM) && Adjacent(AM))
|
|
start_pulling(AM)
|
|
else
|
|
stop_pulling()
|
|
|
|
/mob/living/stop_pulling()
|
|
if(ismob(pulling))
|
|
reset_pull_offsets(pulling)
|
|
..()
|
|
update_pull_movespeed()
|
|
update_pull_hud_icon()
|
|
|
|
/mob/living/verb/stop_pulling1()
|
|
set name = "Stop Pulling"
|
|
set category = "IC"
|
|
stop_pulling()
|
|
|
|
//same as above
|
|
/mob/living/pointed(atom/A as mob|obj|turf in view())
|
|
if(incapacitated())
|
|
return FALSE
|
|
if(HAS_TRAIT(src, TRAIT_DEATHCOMA))
|
|
return FALSE
|
|
if(!..())
|
|
return FALSE
|
|
visible_message("<b>[src]</b> points at [A].", span_notice("You point at [A]."))
|
|
return TRUE
|
|
|
|
/mob/living/verb/succumb(whispered as null)
|
|
set hidden = TRUE
|
|
if (InCritical())
|
|
log_message("Has [whispered ? "whispered his final words" : "succumbed to death"] while in [InFullCritical() ? "hard":"soft"] critical with [round(health, 0.1)] points of health!", LOG_ATTACK)
|
|
adjustOxyLoss(health - HEALTH_THRESHOLD_DEAD)
|
|
updatehealth()
|
|
if(!whispered)
|
|
to_chat(src, span_notice("You have given up life and succumbed to death."))
|
|
death()
|
|
|
|
/mob/living/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, check_immobilized = FALSE, ignore_stasis = FALSE)
|
|
if(stat || IsUnconscious() || IsStun() || IsParalyzed() || (check_immobilized && IsImmobilized()) || (!ignore_restraints && restrained(ignore_grab)) || (!ignore_stasis && IS_IN_STASIS(src)))
|
|
return TRUE
|
|
|
|
/mob/living/canUseStorage()
|
|
if (get_num_arms() <= 0)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/mob/living/proc/InCritical()
|
|
return (health <= crit_threshold && (stat == SOFT_CRIT || stat == UNCONSCIOUS))
|
|
|
|
/mob/living/proc/InFullCritical()
|
|
return (health <= HEALTH_THRESHOLD_FULLCRIT && stat == UNCONSCIOUS)
|
|
|
|
//This proc is used for mobs which are affected by pressure to calculate the amount of pressure that actually
|
|
//affects them once clothing is factored in. ~Errorage
|
|
/mob/living/proc/calculate_affecting_pressure(pressure)
|
|
return pressure
|
|
|
|
/mob/living/proc/adjustBodyTemp(actual, desired, incrementboost)
|
|
var/temperature = actual
|
|
var/difference = abs(actual-desired) //get difference
|
|
var/increments = difference/10 //find how many increments apart they are
|
|
var/change = increments*incrementboost // Get the amount to change by (x per increment)
|
|
|
|
// Too cold
|
|
if(actual < desired)
|
|
temperature += change
|
|
if(actual > desired)
|
|
temperature = desired
|
|
// Too hot
|
|
if(actual > desired)
|
|
temperature -= change
|
|
if(actual < desired)
|
|
temperature = desired
|
|
return temperature
|
|
|
|
/mob/living/proc/getMaxHealth()
|
|
return maxHealth
|
|
|
|
/mob/living/proc/setMaxHealth(newMaxHealth)
|
|
maxHealth = newMaxHealth
|
|
|
|
// MOB PROCS //END
|
|
|
|
/mob/living/proc/mob_sleep()
|
|
set name = "Sleep"
|
|
set category = "IC"
|
|
|
|
if(IsSleeping())
|
|
to_chat(src, span_notice("You are already sleeping."))
|
|
return
|
|
else
|
|
if(tgui_alert(usr, "You sure you want to sleep for a while?", "Sleep", list("Yes", "No")) == "Yes")
|
|
SetSleeping(400) //Short nap
|
|
update_mobility()
|
|
|
|
/mob/proc/get_contents()
|
|
|
|
/mob/living/proc/lay_down()
|
|
set name = "Rest"
|
|
set category = "IC"
|
|
|
|
if(!resting)
|
|
set_resting(TRUE, FALSE)
|
|
else
|
|
if(do_after(src, 1 SECONDS, src))
|
|
set_resting(FALSE, FALSE)
|
|
else
|
|
to_chat(src, span_notice("You fail to get up."))
|
|
|
|
/mob/living/proc/set_resting(rest, silent = TRUE)
|
|
if(!silent)
|
|
if(rest)
|
|
to_chat(src, span_notice("You are now resting."))
|
|
else
|
|
to_chat(src, span_notice("You get up."))
|
|
resting = rest
|
|
update_resting()
|
|
|
|
/mob/living/proc/update_resting()
|
|
update_rest_hud_icon()
|
|
update_mobility()
|
|
|
|
//Recursive function to find everything a mob is holding. Really shitty proc tbh.
|
|
/mob/living/get_contents()
|
|
var/list/ret = list()
|
|
ret |= contents //add our contents
|
|
for(var/i in ret.Copy()) //iterate storage objects
|
|
var/atom/A = i
|
|
SEND_SIGNAL(A, COMSIG_TRY_STORAGE_RETURN_INVENTORY, ret)
|
|
for(var/obj/item/folder/F in ret.Copy()) //very snowflakey-ly iterate folders
|
|
ret |= F.contents
|
|
return ret
|
|
|
|
// Living mobs use can_inject() to make sure that the mob is not syringe-proof in general.
|
|
/mob/living/proc/can_inject()
|
|
return TRUE
|
|
|
|
/mob/living/is_injectable(mob/user, allowmobs = TRUE)
|
|
return (allowmobs && reagents && can_inject(user))
|
|
|
|
/mob/living/is_drawable(mob/user, allowmobs = TRUE)
|
|
return (allowmobs && reagents && can_inject(user))
|
|
|
|
/mob/living/proc/updatehealth()
|
|
if(status_flags & GODMODE)
|
|
return
|
|
health = maxHealth - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() - getCloneLoss()
|
|
staminaloss = getStaminaLoss()
|
|
update_stat()
|
|
med_hud_set_health()
|
|
med_hud_set_status()
|
|
update_health_hud()
|
|
|
|
/mob/living/update_health_hud()
|
|
var/severity = 0
|
|
var/healthpercent = (health/maxHealth) * 100
|
|
if(hud_used?.healthdoll) //to really put you in the boots of a simplemob
|
|
var/atom/movable/screen/healthdoll/living/livingdoll = hud_used.healthdoll
|
|
switch(healthpercent)
|
|
if(100 to INFINITY)
|
|
livingdoll.icon_state = "living0"
|
|
if(80 to 100)
|
|
livingdoll.icon_state = "living1"
|
|
severity = 1
|
|
if(60 to 80)
|
|
livingdoll.icon_state = "living2"
|
|
severity = 2
|
|
if(40 to 60)
|
|
livingdoll.icon_state = "living3"
|
|
severity = 3
|
|
if(20 to 40)
|
|
livingdoll.icon_state = "living4"
|
|
severity = 4
|
|
if(1 to 20)
|
|
livingdoll.icon_state = "living5"
|
|
severity = 5
|
|
else
|
|
livingdoll.icon_state = "living6"
|
|
severity = 6
|
|
if(!livingdoll.filtered)
|
|
livingdoll.filtered = TRUE
|
|
var/icon/mob_mask = icon(icon, icon_state)
|
|
if(mob_mask.Height() > world.icon_size || mob_mask.Width() > world.icon_size)
|
|
var/health_doll_icon_state = health_doll_icon ? health_doll_icon : "megasprite"
|
|
mob_mask = icon('icons/mob/screen_gen.dmi', health_doll_icon_state) //swap to something generic if they have no special doll
|
|
UNLINT(livingdoll.filters += filter(type="alpha", icon = mob_mask))
|
|
livingdoll.filters += filter(type="drop_shadow", size = -1)
|
|
if(severity > 0)
|
|
overlay_fullscreen("brute", /atom/movable/screen/fullscreen/brute, severity)
|
|
else
|
|
clear_fullscreen("brute")
|
|
|
|
//proc used to ressuscitate a mob
|
|
/mob/living/proc/revive(full_heal = 0, admin_revive = 0)
|
|
if(full_heal)
|
|
fully_heal(admin_revive)
|
|
if(stat == DEAD && can_be_revived()) //in some cases you can't revive (e.g. no brain)
|
|
remove_from_dead_mob_list()
|
|
add_to_alive_mob_list()
|
|
set_suicide(FALSE)
|
|
stat = UNCONSCIOUS //the mob starts unconscious,
|
|
blind_eyes(1)
|
|
losebreath = 0 //losebreath stacks were persisting beyond death, immediately killing again after revival until they ran out natureally from time
|
|
updatehealth() //then we check if the mob should wake up.
|
|
update_mobility()
|
|
update_sight()
|
|
clear_alert("not_enough_oxy")
|
|
reload_fullscreen()
|
|
revive_guardian()
|
|
. = 1
|
|
if(mind)
|
|
for(var/S in mind.spell_list)
|
|
var/obj/effect/proc_holder/spell/spell = S
|
|
spell.updateButtonIcon()
|
|
|
|
/mob/living/proc/remove_CC(should_update_mobility = TRUE)
|
|
SetStun(0, FALSE)
|
|
SetKnockdown(0, FALSE)
|
|
SetImmobilized(0, FALSE)
|
|
SetParalyzed(0, FALSE)
|
|
SetSleeping(0, FALSE)
|
|
setStaminaLoss(0)
|
|
SetUnconscious(0, FALSE)
|
|
if(should_update_mobility)
|
|
update_mobility()
|
|
|
|
//proc used to completely heal a mob.
|
|
/mob/living/proc/fully_heal(admin_revive = 0)
|
|
restore_blood()
|
|
setToxLoss(0, 0) //zero as second argument not automatically call updatehealth().
|
|
setOxyLoss(0, 0)
|
|
setCloneLoss(0, 0)
|
|
remove_CC(FALSE)
|
|
set_disgust(0)
|
|
radiation = 0
|
|
set_nutrition(NUTRITION_LEVEL_FED + 50)
|
|
bodytemperature = BODYTEMP_NORMAL
|
|
set_blindness(0)
|
|
set_blurriness(0)
|
|
set_dizziness(0)
|
|
|
|
cure_nearsighted()
|
|
cure_blind()
|
|
cure_husk()
|
|
hallucination = 0
|
|
heal_overall_damage(INFINITY, INFINITY, INFINITY, null, TRUE) //heal brute and burn dmg on both organic and robotic limbs, and update health right away.
|
|
ExtinguishMob()
|
|
losebreath = 0
|
|
fire_stacks = 0
|
|
confused = 0
|
|
dizziness = 0
|
|
drowsyness = 0
|
|
stuttering = 0
|
|
slurring = 0
|
|
jitteriness = 0
|
|
var/datum/component/mood/mood = GetComponent(/datum/component/mood)
|
|
if (mood)
|
|
mood.remove_temp_moods(admin_revive)
|
|
update_mobility()
|
|
stop_sound_channel(CHANNEL_HEARTBEAT)
|
|
if(admin_revive)
|
|
cure_fakedeath()
|
|
|
|
//proc called by revive(), to check if we can actually ressuscitate the mob (we don't want to revive him and have him instantly die again)
|
|
/mob/living/proc/can_be_revived()
|
|
. = 1
|
|
if(health <= HEALTH_THRESHOLD_DEAD)
|
|
return 0
|
|
|
|
/mob/living/proc/update_damage_overlays()
|
|
return
|
|
|
|
/mob/living/Move(atom/newloc, direct, glide_size_override)
|
|
if (buckled && buckled.loc != newloc) //not updating position
|
|
if (!buckled.anchored)
|
|
return buckled.Move(newloc, direct, glide_size)
|
|
else
|
|
return 0
|
|
|
|
var/old_direction = dir
|
|
var/turf/T = loc
|
|
|
|
if(pulling)
|
|
update_pull_movespeed()
|
|
|
|
. = ..()
|
|
|
|
if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1 && (pulledby != moving_from_pull))//separated from our puller and not in the middle of a diagonal move.
|
|
pulledby.stop_pulling()
|
|
else
|
|
if(isliving(pulledby))
|
|
var/mob/living/L = pulledby
|
|
L.set_pull_offsets(src, pulledby.grab_state)
|
|
|
|
if(active_storage && !(CanReach(active_storage.parent,view_only = TRUE)))
|
|
active_storage.close(src)
|
|
|
|
if(!(mobility_flags & MOBILITY_STAND) && !buckled && prob(getBruteLoss()*200/maxHealth))
|
|
makeTrail(newloc, T, old_direction)
|
|
|
|
/mob/living/proc/makeTrail(turf/target_turf, turf/start, direction)
|
|
if(!has_gravity() || !isturf(start) || !blood_volume)
|
|
return
|
|
var/blood_exists = locate(/obj/effect/decal/cleanable/trail_holder) in start
|
|
|
|
var/trail_type = getTrail()
|
|
if(!trail_type)
|
|
return
|
|
|
|
var/brute_ratio = round(getBruteLoss() / maxHealth, 0.1)
|
|
if(blood_volume < max(BLOOD_VOLUME_NORMAL(src)*(1 - brute_ratio * 0.25), 0))//don't leave trail if blood volume below a threshold
|
|
return
|
|
|
|
var/bleed_amount = bleedDragAmount()
|
|
blood_volume = max(blood_volume - bleed_amount, 0) //that depends on our brute damage.
|
|
var/newdir = get_dir(target_turf, start)
|
|
if(newdir != direction)
|
|
newdir = newdir | direction
|
|
if(newdir == (NORTH|SOUTH))
|
|
newdir = NORTH
|
|
else if(newdir == (EAST|WEST))
|
|
newdir = EAST
|
|
if((newdir in GLOB.cardinals) && (prob(50)))
|
|
newdir = turn(get_dir(target_turf, start), 180)
|
|
if(!blood_exists)
|
|
new /obj/effect/decal/cleanable/trail_holder(start, get_static_viruses())
|
|
|
|
for(var/obj/effect/decal/cleanable/trail_holder/TH in start)
|
|
if((!(newdir in TH.existing_dirs) || trail_type == "trails_1" || trail_type == "trails_2") && TH.existing_dirs.len <= 16) //maximum amount of overlays is 16 (all light & heavy directions filled)
|
|
TH.existing_dirs += newdir
|
|
TH.add_overlay(image('icons/effects/blood.dmi', trail_type, dir = newdir))
|
|
TH.transfer_mob_blood_dna(src)
|
|
|
|
/mob/living/carbon/human/makeTrail(turf/T)
|
|
if((NOBLOOD in dna.species.species_traits) || !is_bleeding() || bleedsuppress)
|
|
return
|
|
..()
|
|
|
|
///Returns how much blood we're losing from being dragged a tile, from [mob/living/proc/makeTrail]
|
|
/mob/living/proc/bleedDragAmount()
|
|
var/brute_ratio = round(getBruteLoss() / maxHealth, 0.1)
|
|
return max(1, brute_ratio * 2)
|
|
|
|
/mob/living/carbon/bleedDragAmount()
|
|
var/bleed_amount = 0
|
|
for(var/i in all_wounds)
|
|
var/datum/wound/iter_wound = i
|
|
bleed_amount += iter_wound.drag_bleed_amount()
|
|
return bleed_amount
|
|
|
|
/mob/living/proc/getTrail()
|
|
if(getBruteLoss() < 300)
|
|
if(ispolysmorph(src))
|
|
return pick("xltrails_1", "xltrails_2")
|
|
return pick("ltrails_1", "ltrails_2")
|
|
else
|
|
if(ispolysmorph(src))
|
|
return pick("xttrails_1", "xttrails_2")
|
|
return pick("trails_1", "trails_2")
|
|
|
|
/mob/living/experience_pressure_difference(pressure_difference, direction, pressure_resistance_prob_delta = 0)
|
|
if(buckled)
|
|
return
|
|
if(client && client.move_delay >= world.time + world.tick_lag*2)
|
|
pressure_resistance_prob_delta -= 10
|
|
|
|
var/list/turfs_to_check = list()
|
|
|
|
if(has_limbs)
|
|
var/turf/T = get_step(src, angle2dir(dir2angle(direction)+90))
|
|
if (T)
|
|
turfs_to_check += T
|
|
|
|
T = get_step(src, angle2dir(dir2angle(direction)-90))
|
|
if(T)
|
|
turfs_to_check += T
|
|
|
|
for(var/t in turfs_to_check)
|
|
T = t
|
|
if(T.density)
|
|
pressure_resistance_prob_delta -= 5
|
|
continue
|
|
for (var/atom/movable/AM in T)
|
|
if (AM.density && AM.anchored)
|
|
pressure_resistance_prob_delta -= 5
|
|
break
|
|
if(!force_moving)
|
|
..(pressure_difference, direction, pressure_resistance_prob_delta)
|
|
|
|
/mob/living/can_resist()
|
|
return !((next_move > world.time) || incapacitated(ignore_restraints = TRUE, ignore_stasis = TRUE))
|
|
|
|
/mob/living/verb/resist()
|
|
set name = "Resist"
|
|
set category = "IC"
|
|
|
|
if(!can_resist())
|
|
return
|
|
changeNext_move(CLICK_CD_RESIST)
|
|
|
|
SEND_SIGNAL(src, COMSIG_LIVING_RESIST, src)
|
|
//resisting grabs (as if it helps anyone...)
|
|
if(!restrained(ignore_grab = 1) && pulledby)
|
|
log_combat(src, pulledby, "resisted grab")
|
|
resist_grab()
|
|
return
|
|
|
|
//unbuckling yourself
|
|
if(buckled && last_special <= world.time)
|
|
resist_buckle()
|
|
|
|
//Breaking out of a container (Locker, sleeper, cryo...)
|
|
else if(isobj(loc))
|
|
var/obj/C = loc
|
|
C.container_resist(src)
|
|
|
|
else if(mobility_flags & MOBILITY_MOVE)
|
|
if(on_fire)
|
|
resist_fire() //stop, drop, and roll
|
|
else if(last_special <= world.time)
|
|
resist_restraints() //trying to remove cuffs.
|
|
|
|
|
|
/mob/proc/resist_grab(moving_resist)
|
|
return 1 //returning 0 means we successfully broke free
|
|
|
|
/mob/living/resist_grab(moving_resist)
|
|
. = TRUE
|
|
if(pulledby.grab_state || resting)
|
|
var/altered_grab_state = pulledby.grab_state
|
|
if(resting && pulledby.grab_state < GRAB_KILL) //If resting, resisting out of a grab is equivalent to 1 grab state higher. wont make the grab state exceed the normal max, however
|
|
altered_grab_state++
|
|
var/resist_chance = BASE_GRAB_RESIST_CHANCE // see defines/combat.dm
|
|
resist_chance = max((resist_chance/altered_grab_state)-sqrt((getBruteLoss()+getFireLoss()+getOxyLoss()+getToxLoss()+getCloneLoss())*0.5+getStaminaLoss()), 0) //stamina loss is weighted twice as heavily as the other damage types in this calculation
|
|
if(prob(resist_chance))
|
|
visible_message(span_danger("[src] has broken free of [pulledby]'s grip!"))
|
|
log_combat(pulledby, src, "broke grab")
|
|
pulledby.stop_pulling()
|
|
return FALSE
|
|
else
|
|
adjustStaminaLoss(rand(8,15))//8 is from 7.5 rounded up
|
|
visible_message(span_danger("[src] struggles as they fail to break free of [pulledby]'s grip!"))
|
|
if(moving_resist && client) //we resisted by trying to move
|
|
client.move_delay = world.time + 20
|
|
else
|
|
pulledby.stop_pulling()
|
|
return FALSE
|
|
|
|
/mob/living/proc/resist_buckle()
|
|
buckled.user_unbuckle_mob(src,src)
|
|
|
|
/mob/living/proc/resist_fire()
|
|
return
|
|
|
|
/mob/living/proc/resist_restraints()
|
|
return
|
|
|
|
/mob/living/proc/get_visible_name()
|
|
return name
|
|
|
|
/mob/living/update_gravity(has_gravity,override = 0)
|
|
if(!SSticker.HasRoundStarted())
|
|
return
|
|
if(has_gravity)
|
|
if(has_gravity == 1)
|
|
clear_alert("gravity")
|
|
else
|
|
if(has_gravity >= GRAVITY_DAMAGE_TRESHOLD)
|
|
throw_alert("gravity", /atom/movable/screen/alert/veryhighgravity)
|
|
else
|
|
throw_alert("gravity", /atom/movable/screen/alert/highgravity)
|
|
else
|
|
throw_alert("gravity", /atom/movable/screen/alert/weightless)
|
|
if(!override && !is_flying())
|
|
float(!has_gravity)
|
|
|
|
/mob/living/float(on)
|
|
if(throwing)
|
|
return
|
|
var/fixed = 0
|
|
if(anchored || (buckled && buckled.anchored))
|
|
fixed = 1
|
|
if(on && !(movement_type & FLOATING) && !fixed)
|
|
animate(src, pixel_y = pixel_y + 2, time = 1 SECONDS, loop = -1)
|
|
animate(pixel_y = pixel_y - 2, time = 1 SECONDS)
|
|
setMovetype(movement_type | FLOATING)
|
|
else if(((!on || fixed) && (movement_type & FLOATING)))
|
|
animate(src, pixel_y = get_standard_pixel_y_offset(lying), time = 1 SECONDS)
|
|
setMovetype(movement_type & ~FLOATING)
|
|
|
|
// The src mob is trying to strip an item from someone
|
|
// Override if a certain type of mob should be behave differently when stripping items (can't, for example)
|
|
/mob/living/stripPanelUnequip(obj/item/what, mob/who, where)
|
|
if(!what.canStrip(who))
|
|
to_chat(src, span_warning("You can't remove \the [what.name], it appears to be stuck!"))
|
|
return
|
|
who.visible_message(span_danger("[src] tries to remove [who]'s [what.name]."), \
|
|
span_userdanger("[src] tries to remove [who]'s [what.name]."))
|
|
what.add_fingerprint(src)
|
|
SEND_SIGNAL(what, COMSIG_ITEM_PRESTRIP)
|
|
if(do_mob(src, who, what.strip_delay))
|
|
if(what && Adjacent(who))
|
|
if(islist(where))
|
|
var/list/L = where
|
|
if(what == who.get_item_for_held_index(L[2]))
|
|
if(what.doStrip(src, who))
|
|
log_combat(src, who, "stripped [what] off")
|
|
if(what == who.get_item_by_slot(where))
|
|
if(what.doStrip(src, who))
|
|
log_combat(src, who, "stripped [what] off")
|
|
|
|
if(Adjacent(who)) //update inventory window
|
|
who.show_inv(src)
|
|
else
|
|
src << browse(null,"window=mob[REF(who)]")
|
|
|
|
// The src mob is trying to place an item on someone
|
|
// Override if a certain mob should be behave differently when placing items (can't, for example)
|
|
/mob/living/stripPanelEquip(obj/item/what, mob/who, where)
|
|
what = src.get_active_held_item()
|
|
if(what && (HAS_TRAIT(what, TRAIT_NODROP)))
|
|
to_chat(src, span_warning("You can't put \the [what.name] on [who], it's stuck to your hand!"))
|
|
return
|
|
if(what)
|
|
var/list/where_list
|
|
var/final_where
|
|
|
|
if(islist(where))
|
|
where_list = where
|
|
final_where = where[1]
|
|
else
|
|
final_where = where
|
|
|
|
if(!what.mob_can_equip(who, src, final_where, TRUE, TRUE))
|
|
to_chat(src, span_warning("\The [what.name] doesn't fit in that place!"))
|
|
return
|
|
|
|
visible_message(span_notice("[src] tries to put [what] on [who]."))
|
|
if(do_mob(src, who, what.equip_delay_other))
|
|
if(what && Adjacent(who) && what.mob_can_equip(who, src, final_where, TRUE, TRUE))
|
|
if(temporarilyRemoveItemFromInventory(what))
|
|
if(where_list)
|
|
if(!who.put_in_hand(what, where_list[2]))
|
|
what.forceMove(get_turf(who))
|
|
else
|
|
who.equip_to_slot(what, where, TRUE)
|
|
|
|
if(Adjacent(who)) //update inventory window
|
|
who.show_inv(src)
|
|
else
|
|
src << browse(null,"window=mob[REF(who)]")
|
|
|
|
/mob/living/singularity_pull(S, current_size)
|
|
..()
|
|
if(current_size >= STAGE_SIX) //your puny magboots/wings/whatever will not save you against supermatter singularity
|
|
throw_at(S, 14, 3, src, TRUE)
|
|
else if(!src.mob_negates_gravity())
|
|
step_towards(src,S)
|
|
|
|
/mob/living/proc/do_jitter_animation(jitteriness)
|
|
var/amplitude = min(4, (jitteriness/100) + 1)
|
|
var/pixel_x_diff = rand(-amplitude, amplitude)
|
|
var/pixel_y_diff = rand(-amplitude/3, amplitude/3)
|
|
var/final_pixel_x = get_standard_pixel_x_offset(lying)
|
|
var/final_pixel_y = get_standard_pixel_y_offset(lying)
|
|
animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff , time = 0.2 SECONDS, loop = 6)
|
|
animate(pixel_x = final_pixel_x , pixel_y = final_pixel_y , time = 0.2 SECONDS)
|
|
setMovetype(movement_type & ~FLOATING) // If we were without gravity, the bouncing animation got stopped, so we make sure to restart it in next life().
|
|
|
|
/mob/living/proc/get_temperature(datum/gas_mixture/environment)
|
|
var/loc_temp = environment ? environment.return_temperature() : T0C
|
|
if(isobj(loc))
|
|
var/obj/oloc = loc
|
|
var/obj_temp = oloc.return_temperature()
|
|
if(obj_temp != null)
|
|
loc_temp = obj_temp
|
|
else if(isspaceturf(get_turf(src)))
|
|
var/turf/heat_turf = get_turf(src)
|
|
loc_temp = heat_turf.temperature
|
|
return loc_temp
|
|
|
|
/mob/living/proc/get_standard_pixel_x_offset(lying = 0)
|
|
return initial(pixel_x)
|
|
|
|
/mob/living/proc/get_standard_pixel_y_offset(lying = 0)
|
|
return initial(pixel_y)
|
|
|
|
/mob/living/cancel_camera()
|
|
..()
|
|
cameraFollow = null
|
|
|
|
/mob/living/proc/can_track(mob/living/user)
|
|
//basic fast checks go first. When overriding this proc, I recommend calling ..() at the end.
|
|
var/turf/T = get_turf(src)
|
|
if(!T)
|
|
return 0
|
|
if(is_centcom_level(T.z)) //dont detect mobs on centcom
|
|
return 0
|
|
if(is_away_level(T.z))
|
|
return 0
|
|
if(user != null && src == user)
|
|
return 0
|
|
if(invisibility || alpha == 0)//cloaked
|
|
return 0
|
|
if(digitalcamo || digitalinvis)
|
|
return 0
|
|
|
|
// Now, are they viewable by a camera? (This is last because it's the most intensive check)
|
|
if(!near_camera(src))
|
|
return 0
|
|
|
|
return 1
|
|
|
|
//used in datum/reagents/reaction() proc
|
|
/mob/living/proc/get_permeability_protection(list/target_zones)
|
|
return 0
|
|
|
|
/mob/living/proc/harvest(mob/living/user) //used for extra objects etc. in butchering
|
|
return
|
|
|
|
// Hotkeys Exist, so this stops it from making runtimes
|
|
/mob/living/proc/give()
|
|
return
|
|
|
|
/mob/living/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE)
|
|
if(incapacitated())
|
|
to_chat(src, span_warning("You can't do that right now!"))
|
|
return FALSE
|
|
if(be_close && !in_range(M, src))
|
|
to_chat(src, span_warning("You are too far away!"))
|
|
return FALSE
|
|
if(!no_dextery)
|
|
to_chat(src, span_warning("You don't have the dexterity to do this!"))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/mob/living/proc/can_use_guns(obj/item/G)//actually used for more than guns!
|
|
if(G.trigger_guard != TRIGGER_GUARD_ALLOW_ALL && !IsAdvancedToolUser())
|
|
to_chat(src, span_warning("You don't have the dexterity to do this!"))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/mob/living/proc/update_stamina()
|
|
return
|
|
|
|
/mob/living/carbon/alien/update_stamina()
|
|
return
|
|
|
|
/mob/living/proc/owns_soul()
|
|
if(mind)
|
|
return mind.soulOwner == mind
|
|
return TRUE
|
|
|
|
/mob/living/proc/return_soul()
|
|
hellbound = 0
|
|
if(mind)
|
|
var/datum/antagonist/devil/devilInfo = mind.soulOwner.has_antag_datum(/datum/antagonist/devil)
|
|
if(devilInfo)//Not sure how this could be null, but let's just try anyway.
|
|
devilInfo.remove_soul(mind)
|
|
mind.soulOwner = mind
|
|
|
|
/mob/living/proc/has_bane(banetype)
|
|
var/datum/antagonist/devil/devilInfo = is_devil(src)
|
|
return devilInfo && banetype == devilInfo.bane
|
|
|
|
/mob/living/proc/check_weakness(obj/item/weapon, mob/living/attacker)
|
|
if(mind && mind.has_antag_datum(/datum/antagonist/devil))
|
|
return check_devil_bane_multiplier(weapon, attacker)
|
|
return 1 //This is not a boolean, it's the multiplier for the damage the weapon does.
|
|
|
|
/mob/living/proc/check_acedia()
|
|
if(mind && mind.has_objective(/datum/objective/sintouched/acedia))
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/mob/living/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, quickstart = TRUE)
|
|
stop_pulling()
|
|
. = ..()
|
|
|
|
// Called when we are hit by a bolt of polymorph and changed
|
|
// Generally the mob we are currently in is about to be deleted
|
|
/mob/living/proc/wabbajack_act(mob/living/new_mob)
|
|
new_mob.name = real_name
|
|
new_mob.real_name = real_name
|
|
|
|
if(mind)
|
|
mind.transfer_to(new_mob)
|
|
else
|
|
new_mob.key = key
|
|
|
|
for(var/para in hasparasites())
|
|
var/mob/living/simple_animal/hostile/guardian/G = para
|
|
G.summoner = new_mob
|
|
G.Recall()
|
|
to_chat(G, span_holoparasite("Your summoner has changed form!"))
|
|
|
|
/mob/living/rad_act(amount)
|
|
. = ..()
|
|
|
|
if(!amount || (amount < RAD_MOB_SKIN_PROTECTION) || HAS_TRAIT(src, TRAIT_RADIMMUNE))
|
|
return
|
|
|
|
amount -= RAD_BACKGROUND_RADIATION // This will always be at least 1 because of how skin protection is calculated
|
|
|
|
var/blocked = getarmor(null, RAD)
|
|
|
|
if(amount > RAD_BURN_THRESHOLD)
|
|
apply_damage((amount-RAD_BURN_THRESHOLD)/RAD_BURN_THRESHOLD, BURN, null, blocked)
|
|
|
|
apply_effect((amount*RAD_MOB_COEFFICIENT)/max(1, (radiation**2)*RAD_OVERDOSE_REDUCTION), EFFECT_IRRADIATE, blocked)
|
|
|
|
///Return any anti magic atom on this mob that matches the magic type
|
|
/mob/living/anti_magic_check(magic = TRUE, holy = FALSE, tinfoil = FALSE, chargecost = 1, self = FALSE)
|
|
if(!magic && !holy && !tinfoil)
|
|
return
|
|
var/list/protection_sources = list()
|
|
if(SEND_SIGNAL(src, COMSIG_MOB_RECEIVE_MAGIC, src, magic, holy, tinfoil, chargecost, self, protection_sources) & COMPONENT_BLOCK_MAGIC)
|
|
if(protection_sources.len)
|
|
return pick(protection_sources)
|
|
else
|
|
return src
|
|
if((magic && HAS_TRAIT(src, TRAIT_ANTIMAGIC)) || (holy && HAS_TRAIT(src, TRAIT_HOLY)))
|
|
return src
|
|
|
|
/mob/living/proc/fakefireextinguish()
|
|
return
|
|
|
|
/mob/living/proc/fakefire()
|
|
return
|
|
|
|
/mob/living/proc/unfry_mob() //Callback proc to tone down spam from multiple sizzling frying oil dipping.
|
|
REMOVE_TRAIT(src, TRAIT_OIL_FRIED, "cooking_oil_react")
|
|
|
|
/**
|
|
* add_body_temperature_change Adds modifications to the body temperature
|
|
*
|
|
* This collects all body temperature changes that the mob is experiencing to the list body_temp_changes
|
|
* the aggrogate result is used to derive the new body temperature for the mob
|
|
*
|
|
* arguments:
|
|
* * key_name (str) The unique key for this change, if it already exist it will be overridden
|
|
* * amount (int) The amount of change from the base body temperature
|
|
*/
|
|
/mob/living/proc/add_body_temperature_change(key_name, amount)
|
|
body_temp_changes["[key_name]"] = amount
|
|
|
|
/**
|
|
* remove_body_temperature_change Removes the modifications to the body temperature
|
|
*
|
|
* This removes the recorded change to body temperature from the body_temp_changes list
|
|
*
|
|
* arguments:
|
|
* * key_name (str) The unique key for this change that will be removed
|
|
*/
|
|
/mob/living/proc/remove_body_temperature_change(key_name)
|
|
body_temp_changes -= key_name
|
|
|
|
/**
|
|
* get_body_temp_normal_change Returns the aggregate change to body temperature
|
|
*
|
|
* This aggregates all the changes in the body_temp_changes list and returns the result
|
|
*/
|
|
/mob/living/proc/get_body_temp_normal_change()
|
|
var/total_change = 0
|
|
if(body_temp_changes.len)
|
|
for(var/change in body_temp_changes)
|
|
total_change += body_temp_changes["[change]"]
|
|
return total_change
|
|
|
|
|
|
|
|
/**
|
|
* get_body_temp_normal Returns the mobs normal body temperature with any modifications applied
|
|
*
|
|
* This applies the result from proc/get_body_temp_normal_change() against the BODYTEMP_NORMAL and returns the result
|
|
*
|
|
* arguments:
|
|
* * apply_change (optional) Default True This applies the changes to body temperature normal
|
|
*/
|
|
/mob/living/proc/get_body_temp_normal(apply_change=TRUE)
|
|
if(!apply_change)
|
|
return BODYTEMP_NORMAL
|
|
return BODYTEMP_NORMAL + get_body_temp_normal_change()
|
|
|
|
//Mobs on Fire
|
|
/mob/living/proc/IgniteMob()
|
|
if(fire_stacks > 0 && !on_fire)
|
|
on_fire = 1
|
|
src.visible_message(span_warning("[src] catches fire!"), \
|
|
span_userdanger("You're set on fire!"))
|
|
new/obj/effect/dummy/lighting_obj/moblight/fire(src)
|
|
throw_alert("fire", /atom/movable/screen/alert/fire)
|
|
update_fire()
|
|
SEND_SIGNAL(src, COMSIG_LIVING_IGNITED,src)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/mob/living/proc/ExtinguishMob()
|
|
if(on_fire)
|
|
on_fire = 0
|
|
fire_stacks = 0
|
|
for(var/obj/effect/dummy/lighting_obj/moblight/fire/F in src)
|
|
qdel(F)
|
|
clear_alert("fire")
|
|
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "on_fire")
|
|
SEND_SIGNAL(src, COMSIG_LIVING_EXTINGUISHED, src)
|
|
update_fire()
|
|
|
|
/mob/living/proc/adjust_fire_stacks(add_fire_stacks) //Adjusting the amount of fire_stacks we have on person
|
|
if(fire_stacks > 0) //don't let people on fire instantly get -20 fire_stacks, but still let them extinguish themselves. Stops preternis from hurting themselves for trying to put out fire
|
|
fire_stacks = clamp(fire_stacks + add_fire_stacks, -1, 20)
|
|
else
|
|
fire_stacks = clamp(fire_stacks + add_fire_stacks, -20, 20)
|
|
if(on_fire && fire_stacks <= 0)
|
|
ExtinguishMob()
|
|
|
|
//Share fire evenly between the two mobs
|
|
//Called in MobBump() and Crossed()
|
|
/mob/living/proc/spreadFire(mob/living/L)
|
|
if(!istype(L))
|
|
return
|
|
|
|
if(on_fire)
|
|
if(L.on_fire) // If they were also on fire
|
|
var/firesplit = (fire_stacks + L.fire_stacks)/2
|
|
fire_stacks = firesplit
|
|
L.fire_stacks = firesplit
|
|
else // If they were not
|
|
fire_stacks /= 2
|
|
L.adjust_fire_stacks(fire_stacks)
|
|
if(L.IgniteMob()) // Ignite them
|
|
log_game("[key_name(src)] bumped into [key_name(L)] and set them on fire")
|
|
|
|
else if(L.on_fire) // If they were on fire and we were not
|
|
L.fire_stacks /= 2
|
|
adjust_fire_stacks(L.fire_stacks)
|
|
IgniteMob() // Ignite us
|
|
|
|
//Mobs on Fire end
|
|
|
|
// used by secbot and monkeys Crossed
|
|
/mob/living/proc/knockOver(var/mob/living/carbon/C)
|
|
if(C.key) //save us from monkey hordes
|
|
C.visible_message("<span class='warning'>[pick( \
|
|
"[C] dives out of [src]'s way!", \
|
|
"[C] stumbles over [src]!", \
|
|
"[C] jumps out of [src]'s path!", \
|
|
"[C] trips over [src] and falls!", \
|
|
"[C] topples over [src]!", \
|
|
"[C] leaps out of [src]'s way!")]</span>")
|
|
C.Paralyze(40)
|
|
|
|
/mob/living/can_be_pulled()
|
|
return ..() && !(buckled && buckled.buckle_prevents_pull)
|
|
|
|
//Updates canmove, lying and icons. Could perhaps do with a rename but I can't think of anything to describe it.
|
|
//Robots, animals and brains have their own version so don't worry about them
|
|
/mob/living/proc/update_mobility()
|
|
var/stat_softcrit = stat == SOFT_CRIT
|
|
var/stat_conscious = (stat == CONSCIOUS) || stat_softcrit
|
|
var/conscious = !IsUnconscious() && stat_conscious && !HAS_TRAIT(src, TRAIT_DEATHCOMA)
|
|
var/chokehold = pulledby && pulledby.grab_state >= GRAB_NECK
|
|
var/restrained = restrained()
|
|
var/has_legs = get_num_legs()
|
|
var/has_arms = get_num_arms()
|
|
var/paralyzed = IsParalyzed()
|
|
var/stun = IsStun()
|
|
var/knockdown = IsKnockdown()
|
|
var/ignore_legs = get_leg_ignore()
|
|
var/canmove = !IsImmobilized() && !stun && conscious && !paralyzed && !buckled && (!stat_softcrit || !pulledby) && !chokehold && !IsFrozen() && !IS_IN_STASIS(src) && (has_arms || ignore_legs || has_legs)
|
|
if(canmove)
|
|
mobility_flags |= MOBILITY_MOVE
|
|
else
|
|
mobility_flags &= ~MOBILITY_MOVE
|
|
var/canstand_involuntary = conscious && !stat_softcrit && !knockdown && !chokehold && !paralyzed && (ignore_legs || has_legs) && !(buckled && buckled.buckle_lying)
|
|
var/canstand = canstand_involuntary && !resting
|
|
|
|
var/should_be_lying = !canstand
|
|
if(buckled)
|
|
if(buckled.buckle_lying != -1)
|
|
should_be_lying = buckled.buckle_lying
|
|
|
|
if(should_be_lying)
|
|
mobility_flags &= ~MOBILITY_STAND
|
|
if(buckled)
|
|
if(buckled.buckle_lying != -1)
|
|
lying = buckled.buckle_lying
|
|
if(!lying) //force them on the ground
|
|
lying = pick(90, 270)
|
|
else
|
|
mobility_flags |= MOBILITY_STAND
|
|
lying = 0
|
|
|
|
if(should_be_lying || restrained || incapacitated())
|
|
mobility_flags &= ~(MOBILITY_UI|MOBILITY_PULL)
|
|
else
|
|
mobility_flags |= MOBILITY_UI|MOBILITY_PULL
|
|
|
|
|
|
|
|
var/canitem = !paralyzed && !stun && conscious && !chokehold && !restrained && has_arms
|
|
if(canitem)
|
|
mobility_flags |= (MOBILITY_USE | MOBILITY_PICKUP | MOBILITY_STORAGE)
|
|
else
|
|
mobility_flags &= ~(MOBILITY_USE | MOBILITY_PICKUP | MOBILITY_STORAGE)
|
|
if(!(mobility_flags & MOBILITY_USE))
|
|
drop_all_held_items()
|
|
if(!(mobility_flags & MOBILITY_PULL))
|
|
if(pulling)
|
|
stop_pulling()
|
|
if(!(mobility_flags & MOBILITY_UI))
|
|
unset_machine()
|
|
density = !lying
|
|
if(lying)
|
|
if(!lying_prev)
|
|
fall(!canstand_involuntary)
|
|
if(layer == initial(layer)) //to avoid special cases like hiding larvas.
|
|
layer = LYING_MOB_LAYER //so mob lying always appear behind standing mobs
|
|
else
|
|
if(layer == LYING_MOB_LAYER)
|
|
layer = initial(layer)
|
|
update_transform()
|
|
lying_prev = lying
|
|
|
|
/mob/living/proc/fall(forced)
|
|
if(!(mobility_flags & MOBILITY_USE))
|
|
drop_all_held_items()
|
|
|
|
/mob/living/proc/AddAbility(obj/effect/proc_holder/A)
|
|
abilities.Add(A)
|
|
A.on_gain(src)
|
|
if(A.has_action)
|
|
A.action.Grant(src)
|
|
|
|
/mob/living/proc/RemoveAbility(obj/effect/proc_holder/A)
|
|
abilities.Remove(A)
|
|
A.on_lose(src)
|
|
if(A.action)
|
|
A.action.Remove(src)
|
|
|
|
/mob/living/proc/add_abilities_to_panel()
|
|
var/list/L = list()
|
|
for(var/obj/effect/proc_holder/A in abilities)
|
|
L[++L.len] = list("[A.panel]",A.get_panel_text(),A.name,"[REF(A)]")
|
|
return L
|
|
|
|
/mob/living/lingcheck()
|
|
if(mind)
|
|
var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
|
|
if(changeling)
|
|
if(changeling.changeling_speak)
|
|
return LINGHIVE_LING
|
|
return LINGHIVE_OUTSIDER
|
|
if(mind && mind.linglink)
|
|
return LINGHIVE_LINK
|
|
return LINGHIVE_NONE
|
|
|
|
/mob/living/forceMove(atom/destination)
|
|
stop_pulling()
|
|
if(buckled)
|
|
buckled.unbuckle_mob(src, force = TRUE)
|
|
if(has_buckled_mobs())
|
|
unbuckle_all_mobs(force = TRUE)
|
|
. = ..()
|
|
if(.)
|
|
if(client)
|
|
reset_perspective()
|
|
update_mobility() //if the mob was asleep inside a container and then got forceMoved out we need to make them fall.
|
|
|
|
/mob/living/proc/update_z(new_z) // 1+ to register, null to unregister
|
|
if (registered_z != new_z)
|
|
if (registered_z)
|
|
SSmobs.clients_by_zlevel[registered_z] -= src
|
|
if (client)
|
|
if (new_z)
|
|
SSmobs.clients_by_zlevel[new_z] += src
|
|
for (var/I in length(SSidlenpcpool.idle_mobs_by_zlevel[new_z]) to 1 step -1) //Backwards loop because we're removing (guarantees optimal rather than worst-case performance), it's fine to use .len here but doesn't compile on 511
|
|
var/mob/living/simple_animal/SA = SSidlenpcpool.idle_mobs_by_zlevel[new_z][I]
|
|
if (SA)
|
|
SA.toggle_ai(AI_ON) // Guarantees responsiveness for when appearing right next to mobs
|
|
else
|
|
SSidlenpcpool.idle_mobs_by_zlevel[new_z] -= SA
|
|
|
|
registered_z = new_z
|
|
else
|
|
registered_z = null
|
|
|
|
/mob/living/onTransitZ(old_z,new_z)
|
|
..()
|
|
update_z(new_z)
|
|
|
|
/mob/living/MouseDrop_T(atom/pickable, atom/user)
|
|
var/mob/living/U = user
|
|
if(isliving(pickable))
|
|
var/mob/living/M = pickable
|
|
if(M.can_be_held && U.pulling == M)
|
|
M.mob_try_pickup(U)
|
|
return //dont open the mobs inventory if you are picking them up
|
|
. = ..()
|
|
|
|
/mob/living/proc/mob_pickup(mob/living/L)
|
|
if(!held_state)
|
|
held_state = icon_state
|
|
var/obj/item/clothing/mob_holder/holder = new(get_turf(src), src, held_state, held_icon, held_lh, held_rh, worn_layer, mob_size, worn_slot_flags)
|
|
L.visible_message(span_warning("[L] scoops up [src]!"))
|
|
L.put_in_hands(holder)
|
|
|
|
/mob/living/proc/mob_try_pickup(mob/living/user)
|
|
if(!ishuman(user))
|
|
return
|
|
if(user.get_active_held_item())
|
|
to_chat(user, span_warning("Your hands are full!"))
|
|
return FALSE
|
|
if(buckled)
|
|
to_chat(user, span_warning("[src] is buckled to something!"))
|
|
return FALSE
|
|
user.visible_message(span_notice("[user] starts trying to scoop up [src]!"))
|
|
if(!do_after(user, 2 SECONDS, src))
|
|
return FALSE
|
|
mob_pickup(user)
|
|
return TRUE
|
|
|
|
/mob/living/proc/get_static_viruses() //used when creating blood and other infective objects
|
|
if(!LAZYLEN(diseases))
|
|
return
|
|
var/list/datum/disease/result = list()
|
|
for(var/datum/disease/D in diseases)
|
|
var/static_virus = D.Copy()
|
|
result += static_virus
|
|
return result
|
|
|
|
/mob/living/reset_perspective(atom/A)
|
|
if(..())
|
|
update_sight()
|
|
if(client.eye && client.eye != src)
|
|
var/atom/AT = client.eye
|
|
AT.get_remote_view_fullscreens(src)
|
|
else
|
|
clear_fullscreen("remote_view", 0)
|
|
update_pipe_vision()
|
|
|
|
/mob/living/update_mouse_pointer()
|
|
..()
|
|
if (client && ranged_ability && ranged_ability.ranged_mousepointer)
|
|
client.mouse_pointer_icon = ranged_ability.ranged_mousepointer
|
|
|
|
/mob/living/vv_edit_var(var_name, var_value)
|
|
switch(var_name)
|
|
if ("maxHealth")
|
|
if (!isnum(var_value) || var_value <= 0)
|
|
return FALSE
|
|
if("stat")
|
|
if((stat == DEAD) && (var_value < DEAD))//Bringing the dead back to life
|
|
remove_from_dead_mob_list()
|
|
add_to_alive_mob_list()
|
|
if((stat < DEAD) && (var_value == DEAD))//Kill he
|
|
remove_from_dead_mob_list()
|
|
add_to_alive_mob_list()
|
|
. = ..()
|
|
switch(var_name)
|
|
if("knockdown")
|
|
SetParalyzed(var_value)
|
|
if("stun")
|
|
SetStun(var_value)
|
|
if("unconscious")
|
|
SetUnconscious(var_value)
|
|
if("sleeping")
|
|
SetSleeping(var_value)
|
|
if("eye_blind")
|
|
set_blindness(var_value)
|
|
if("eye_damage")
|
|
var/obj/item/organ/eyes/E = getorganslot(ORGAN_SLOT_EYES)
|
|
if(E)
|
|
E.setOrganDamage(var_value)
|
|
if("eye_blurry")
|
|
set_blurriness(var_value)
|
|
if("maxHealth")
|
|
updatehealth()
|
|
if("resize")
|
|
update_transform()
|
|
if("lighting_alpha")
|
|
sync_lighting_plane_alpha()
|
|
|
|
/mob/living/vv_get_header()
|
|
. = ..()
|
|
var/refid = REF(src)
|
|
. += {"
|
|
<br><font size='1'><a href='?_src_=vars;[HrefToken()];datumedit=[refid];varnameedit=ckey' id='ckey'>[ckey || "No ckey"]</a> / [VV_HREF_TARGETREF_1V(refid, VV_HK_BASIC_EDIT, "[real_name || "no real name"]", NAMEOF(src, real_name))]</font>
|
|
<br><font size='1'>
|
|
BRUTE:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=[BRUTE]' id='brute'>[getBruteLoss()]</a>
|
|
BURN:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=[BURN]' id='burn'>[getFireLoss()]</a>
|
|
TOXIN:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=[TOX]' id='toxin'>[getToxLoss()]</a>
|
|
OXY:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=[OXY]' id='oxygen'>[getOxyLoss()]</a>
|
|
CLONE:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=[CLONE]' id='clone'>[getCloneLoss()]</a>
|
|
BRAIN:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=[BRAIN]' id='brain'>[getOrganLoss(ORGAN_SLOT_BRAIN)]</a>
|
|
STAMINA:<font size='1'><a href='?_src_=vars;[HrefToken()];mobToDamage=[refid];adjustDamage=[STAMINA]' id='stamina'>[getStaminaLoss()]</a>
|
|
</font>
|
|
"}
|
|
|
|
/mob/living/proc/is_convert_antag()
|
|
var/list/bad_antags = list(
|
|
/datum/antagonist/clockcult,
|
|
/datum/antagonist/cult,
|
|
/datum/antagonist/darkspawn,
|
|
/datum/antagonist/rev,
|
|
/datum/antagonist/shadowling,
|
|
/datum/antagonist/veil
|
|
)
|
|
for(var/antagcheck in bad_antags)
|
|
if(mind?.has_antag_datum(antagcheck))
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/// Only defined for carbons who can wear masks and helmets, we just assume other mobs have visible faces
|
|
/mob/living/proc/is_face_visible()
|
|
return isturf(loc) // Yogs -- forbids making eye contact with things hidden within objects
|