mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-28 07:20:09 +01:00
Several fixes to chemicals that deal brute or burn. The problem was that their damage was being healed by autoheal faster than it could add up. Damage over time chemicals with more than 1dps would suddenly double in how fast they killed you when they crossed the autoheal threshold. This fixes that by adding a 60 second delay on wounds autohealing. This also affects ALL wounds. If a wound is bandaged it starts autohealing immediately, if it's still under the authoheal threshold. To compensate, I've reduced the damage of chlorine/phoron exposure, as this change effectively increased the damage by 1. Overall phoron/chlorine exposure is now more dangerous to naked people. Less dangerous to clothed people. Less dangerous to people who do the reasonable thing and put their internals on, previously this was actually worse for you. Also fixes contamination of items from chlorine and phoron: - Contaminated items now only damage the bodyparts they cover. - Contamination now checks if the item can be contaminated before contaminating it. - Contamination is now a 100% chance per tick, multiplied by the permeability of an item. (Most items are permeability 1, labcoats & other chem resistant medical clothing are 0.5, latex gloves are 0.1, biosuits, gas masks, etc are 0). - Contamination respects the permeability of your gloves, it's safe to handle contaminated items with latex gloves now. - Air tanks are now permeability 0, your internals won't get contaminated and force you to take them off. - Contaminated clothes now deal damage even if you put a spacesuit on over the top of them. - Life no longer checks every mob's internal organs for contamination every tick.
1088 lines
30 KiB
Plaintext
1088 lines
30 KiB
Plaintext
//mob verbs are faster than object verbs. See mob/verb/examine.
|
|
/mob/living/verb/pulled(atom/movable/atom_movable as mob|obj in oview(1))
|
|
set name = "Pull"
|
|
set category = "Object"
|
|
|
|
if(atom_movable.Adjacent(src))
|
|
src.start_pulling(atom_movable)
|
|
|
|
return
|
|
|
|
/mob/living/_pointed(atom/pointing_at)
|
|
if(src.stat || src.restrained())
|
|
return FALSE
|
|
if(src.status_flags & FAKEDEATH)
|
|
return FALSE
|
|
|
|
. = ..()
|
|
|
|
if(.)
|
|
visible_message("<b>\The [src]</b> points to \the [pointing_at].")
|
|
|
|
/mob/living/drop_from_inventory(var/obj/item/item, var/atom/target, update_icons = TRUE, force = FALSE)
|
|
. = ..()
|
|
if(item?.GetID())
|
|
BITSET(hud_updateflag, ID_HUD) //If we drop our ID, update ID HUD
|
|
|
|
/mob/living/carbon/drop_from_inventory(obj/item/W, atom/target, update_icons = TRUE, force = FALSE)
|
|
if(!force && (W in internal_organs))
|
|
return
|
|
return ..()
|
|
|
|
/*one proc, four uses
|
|
swapping: if it's 1, the mobs are trying to switch, if 0, non-passive is pushing passive
|
|
default behaviour is:
|
|
- non-passive mob passes the passive version
|
|
- passive mob checks to see if its mob_bump_flag is in the non-passive's mob_bump_flags
|
|
- if si, the proc returns
|
|
*/
|
|
/mob/living/proc/can_move_mob(var/mob/living/swapped, swapping = 0, passive = 0)
|
|
if(!swapped)
|
|
return TRUE
|
|
if(!passive)
|
|
return swapped.can_move_mob(src, swapping, 1)
|
|
else
|
|
var/context_flags = 0
|
|
if(swapping)
|
|
context_flags = swapped.mob_swap_flags
|
|
else
|
|
context_flags = swapped.mob_push_flags
|
|
if(!mob_bump_flag) //nothing defined, go wild
|
|
return TRUE
|
|
if(mob_bump_flag & context_flags)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/mob/living
|
|
var/tmp/last_push_notif
|
|
|
|
/mob/living/Collide(atom/movable/target_movable_atom)
|
|
if(now_pushing || !loc)
|
|
return
|
|
|
|
now_pushing = TRUE
|
|
if(istype(target_movable_atom, /mob/living))
|
|
var/mob/living/target_mob = target_movable_atom
|
|
|
|
for(var/mob/living/nearby_mob in range(target_mob, 1))
|
|
if(target_mob.pinned.len || ((nearby_mob.pulling == target_mob && (target_mob.restrained() && !(nearby_mob.restrained()) && nearby_mob.stat == 0)) || locate(/obj/item/grab, target_mob.grabbed_by.len)))
|
|
if(last_push_notif + 0.5 SECONDS <= world.time)
|
|
to_chat(src, SPAN_WARNING("[target_mob] is restrained, you cannot push past"))
|
|
last_push_notif = world.time
|
|
|
|
now_pushing = FALSE
|
|
return
|
|
if(target_mob.pulling == nearby_mob && (nearby_mob.restrained() && !( target_mob.restrained()) && target_mob.stat == 0))
|
|
if(last_push_notif + 0.5 SECONDS <= world.time)
|
|
to_chat(src, SPAN_WARNING("[target_mob] is restraining [nearby_mob], you cannot push past"))
|
|
last_push_notif = world.time
|
|
|
|
now_pushing = FALSE
|
|
return
|
|
|
|
//Leaping mobs just land on the tile, no pushing, no anything.
|
|
if(status_flags & LEAPING)
|
|
forceMove(target_mob.loc)
|
|
status_flags &= ~LEAPING
|
|
now_pushing = FALSE
|
|
return
|
|
|
|
if(can_swap_with(target_mob)) // mutual brohugs all around!
|
|
var/turf/target_mob_oldloc = get_turf(target_mob)
|
|
var/turf/src_oldloc = get_turf(src)
|
|
if(pulling?.density)
|
|
target_mob.forceMove(pulling.loc)
|
|
forceMove(target_mob_oldloc)
|
|
pulling.forceMove(src_oldloc)
|
|
else if(target_mob.pulling?.density)
|
|
forceMove(target_mob.pulling.loc)
|
|
target_mob.forceMove(src_oldloc)
|
|
target_mob.pulling.forceMove(target_mob_oldloc)
|
|
else
|
|
forceMove(target_mob_oldloc)
|
|
if(pulling)
|
|
pulling.forceMove(src_oldloc)
|
|
target_mob.forceMove(src_oldloc)
|
|
if(target_mob.pulling)
|
|
target_mob.pulling.forceMove(target_mob_oldloc)
|
|
for(var/obj/item/grab/grab_item in list(l_hand, r_hand))
|
|
grab_item.affecting.forceMove(loc)
|
|
for(var/obj/item/grab/grab_item in list(target_mob.l_hand, target_mob.r_hand))
|
|
grab_item.affecting.forceMove(target_mob.loc)
|
|
now_pushing = FALSE
|
|
for(var/mob/living/carbon/slime/slime in view(2, target_mob))
|
|
if(slime.victim == target_mob)
|
|
slime.UpdateFeed()
|
|
return
|
|
|
|
if(!can_move_mob(target_mob, 0, 0))
|
|
now_pushing = FALSE
|
|
return
|
|
|
|
if(a_intent == I_HELP || src.restrained())
|
|
now_pushing = FALSE
|
|
return
|
|
|
|
if(istype(target_mob.r_hand, /obj/item/shield/riot))
|
|
if(prob(99))
|
|
now_pushing = FALSE
|
|
return
|
|
|
|
if(istype(target_mob.l_hand, /obj/item/shield/riot))
|
|
if(prob(99))
|
|
now_pushing = FALSE
|
|
return
|
|
|
|
if(!(target_mob.status_flags & CANPUSH))
|
|
now_pushing = FALSE
|
|
return
|
|
|
|
target_mob.LAssailant = WEAKREF(src)
|
|
|
|
now_pushing = FALSE
|
|
. = ..()
|
|
if(!istype(target_movable_atom, /atom/movable))
|
|
return
|
|
if(!now_pushing)
|
|
now_pushing = TRUE
|
|
|
|
if(!target_movable_atom.anchored)
|
|
if(isobj(target_movable_atom))
|
|
var/obj/object = target_movable_atom
|
|
if((can_pull_size == 0) || (can_pull_size < object.w_class))
|
|
now_pushing = FALSE
|
|
return
|
|
|
|
var/target_direction = get_dir(src, target_movable_atom)
|
|
if(istype(target_movable_atom, /obj/structure/window))
|
|
for(var/obj/structure/window/win in get_step(target_movable_atom,target_direction))
|
|
now_pushing = FALSE
|
|
return
|
|
|
|
if(target_movable_atom == src.pulling)
|
|
stop_pulling()
|
|
|
|
step(target_movable_atom, target_direction)
|
|
if(ishuman(target_movable_atom))
|
|
var/mob/living/carbon/human/target_human = target_movable_atom
|
|
if(target_human.grabbed_by)
|
|
for (var/obj/item/grab/G in list(target_human.l_hand, target_human.r_hand))
|
|
step(G.assailant, get_dir(G.assailant, target_human))
|
|
G.adjust_position()
|
|
now_pushing = FALSE
|
|
|
|
/**
|
|
* Checks if two mobs can swap with each other based on the density
|
|
*
|
|
* Returns `TRUE` if the density allows them to swap, `FALSE` otherwise
|
|
*
|
|
* swapper - A `/mob`, the one trying to perform the swap
|
|
* swapee - A `/mob`, the one the `swapper` is trying to swap with
|
|
*/
|
|
/proc/swap_density_check(var/mob/swapper, var/mob/swapee)
|
|
SHOULD_NOT_SLEEP(TRUE)
|
|
SHOULD_BE_PURE(TRUE)
|
|
|
|
var/turf/T = get_turf(swapper)
|
|
|
|
if(!T)
|
|
return FALSE
|
|
|
|
if(T.density)
|
|
return TRUE
|
|
|
|
for(var/atom/movable/A in T)
|
|
|
|
if(A == swapper)
|
|
continue
|
|
|
|
if(!A.CanPass(swapee, T, 1))
|
|
return TRUE
|
|
|
|
/mob/living/proc/can_swap_with(var/mob/living/target_mob)
|
|
if(target_mob.buckled_to || buckled_to)
|
|
return FALSE
|
|
|
|
//BubbleWrap: people in handcuffs are always switched around as if they were on 'help' intent to prevent a person being pulled from being seperated from their puller
|
|
if(!(target_mob.mob_always_swap || (target_mob.a_intent == I_HELP || target_mob.restrained()) && (a_intent == I_HELP || src.restrained())))
|
|
return FALSE
|
|
|
|
if(!target_mob.canmove || !canmove)
|
|
return FALSE
|
|
|
|
if(swap_density_check(src, target_mob))
|
|
return FALSE
|
|
|
|
if(swap_density_check(target_mob, src))
|
|
return FALSE
|
|
|
|
if(pulling?.density && target_mob.pulling?.density) // if both are pulling, don't shuffle
|
|
return FALSE
|
|
|
|
return can_move_mob(target_mob, 1, 0)
|
|
|
|
/mob/living/verb/succumb()
|
|
set hidden = 1
|
|
if(health < maxhealth / 3)
|
|
adjustBrainLoss(health + maxhealth * 2) // Deal 2x health in BrainLoss damage, as before but variable.
|
|
to_chat(src, SPAN_NOTICE("You have given up life and succumbed to death."))
|
|
else
|
|
to_chat(src, SPAN_WARNING("You are not injured enough to succumb to death!"))
|
|
|
|
|
|
/mob/living/proc/updatehealth()
|
|
if(status_flags & GODMODE)
|
|
health = maxhealth
|
|
set_stat(CONSCIOUS)
|
|
else
|
|
health = maxhealth - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() - getCloneLoss()
|
|
|
|
//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(var/pressure)
|
|
return
|
|
|
|
|
|
//sort of a legacy burn method for /electrocute, /shock
|
|
/mob/living/proc/burn_skin(burn_amount)
|
|
take_overall_damage(0, burn_amount)
|
|
return TRUE
|
|
|
|
/mob/living/carbon/human/burn_skin(burn_amount)
|
|
if((mutations & mShock)) //shockproof
|
|
return FALSE
|
|
if((mutations & COLD_RESISTANCE)) //fireproof
|
|
return FALSE
|
|
. = ..()
|
|
updatehealth()
|
|
|
|
/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
|
|
|
|
|
|
// ++++ROCKDTBEN++++ MOB PROCS -- Ask me before touching.
|
|
// Stop! ... Hammertime! ~Carn
|
|
// I touched them without asking... I'm soooo edgy ~Erro (added nodamage checks)
|
|
|
|
/mob/living/proc/getBruteLoss()
|
|
return maxhealth - health
|
|
|
|
/mob/living/proc/adjustBruteLoss(var/amount)
|
|
if(status_flags & GODMODE)
|
|
return
|
|
health = clamp(health - amount, 0, maxhealth)
|
|
|
|
/mob/living/proc/getOxyLoss()
|
|
return 0
|
|
|
|
/mob/living/proc/adjustOxyLoss(var/amount)
|
|
return
|
|
|
|
/mob/living/proc/setOxyLoss(var/amount)
|
|
return
|
|
|
|
/mob/living/proc/getToxLoss()
|
|
return 0
|
|
|
|
/mob/living/proc/adjustToxLoss(var/amount)
|
|
adjustBruteLoss(amount * 0.5)
|
|
|
|
/mob/living/proc/setToxLoss(var/amount)
|
|
adjustBruteLoss((amount * 0.5)-getBruteLoss())
|
|
|
|
/mob/living/proc/getFireLoss()
|
|
return
|
|
|
|
/mob/living/proc/adjustFireLoss(var/amount)
|
|
adjustBruteLoss(amount * 0.5)
|
|
|
|
/mob/living/proc/setFireLoss(var/amount)
|
|
adjustBruteLoss((amount * 0.5)-getBruteLoss())
|
|
|
|
/mob/living/proc/getHalLoss()
|
|
return 0
|
|
|
|
/mob/living/proc/get_shock()
|
|
return getHalLoss()
|
|
|
|
/mob/living/proc/getCloneLoss()
|
|
return 0
|
|
|
|
/mob/living/proc/adjustCloneLoss(var/amount)
|
|
return
|
|
|
|
/mob/living/proc/setCloneLoss(var/amount)
|
|
return
|
|
|
|
/mob/living/proc/getBrainLoss()
|
|
return 0
|
|
|
|
/mob/living/proc/adjustBrainLoss(var/amount, var/maximum)
|
|
return
|
|
|
|
/mob/living/proc/setBrainLoss(var/amount)
|
|
return
|
|
|
|
/mob/living/proc/adjustHalLoss(var/amount)
|
|
adjustBruteLoss(amount * 0.5)
|
|
|
|
/mob/living/proc/setHalLoss(var/amount)
|
|
adjustBruteLoss((amount * 0.5)-getBruteLoss())
|
|
|
|
/mob/living/proc/getMaxHealth()
|
|
return maxhealth
|
|
|
|
/mob/living/proc/setMaxHealth(var/newMaxHealth)
|
|
maxhealth = newMaxHealth
|
|
|
|
// ++++ROCKDTBEN++++ MOB PROCS //END
|
|
|
|
/mob/proc/get_contents()
|
|
return list()
|
|
|
|
//Recursive function to find everything a mob is holding.
|
|
/mob/living/get_contents(var/obj/item/storage/Storage = null)
|
|
var/list/L = list()
|
|
|
|
if(Storage) //If it called itself
|
|
L += Storage.return_inv()
|
|
|
|
//Leave this commented out, it will cause storage items to exponentially add duplicate to the list
|
|
//for(var/obj/item/storage/S in Storage.return_inv()) //Check for storage items
|
|
// L += get_contents(S)
|
|
|
|
for(var/obj/item/gift/G in Storage.return_inv()) //Check for gift-wrapped items
|
|
L += G.gift
|
|
if(istype(G.gift, /obj/item/storage))
|
|
L += get_contents(G.gift)
|
|
|
|
for(var/obj/item/smallDelivery/D in Storage.return_inv()) //Check for package wrapped items
|
|
L += D.wrapped
|
|
if(istype(D.wrapped, /obj/item/storage)) //this should never happen
|
|
L += get_contents(D.wrapped)
|
|
return L
|
|
|
|
else
|
|
|
|
L += src.contents
|
|
for(var/obj/item/storage/S in src.contents) //Check for storage items
|
|
L += get_contents(S)
|
|
|
|
for(var/obj/item/gift/G in src.contents) //Check for gift-wrapped items
|
|
L += G.gift
|
|
if(istype(G.gift, /obj/item/storage))
|
|
L += get_contents(G.gift)
|
|
|
|
for(var/obj/item/smallDelivery/D in src.contents) //Check for package wrapped items
|
|
L += D.wrapped
|
|
if(istype(D.wrapped, /obj/item/storage)) //this should never happen
|
|
L += get_contents(D.wrapped)
|
|
return L
|
|
|
|
/// Returns TRUE if mob has obj of A type anywhere in its contents.
|
|
/mob/living/proc/check_contents_for(A)
|
|
var/list/L = src.get_contents()
|
|
|
|
for(var/obj/B in L)
|
|
if(B.type == A)
|
|
return 1
|
|
return 0
|
|
|
|
// Returns injection time modifier, if 0 then injection fails
|
|
/mob/living/proc/can_inject()
|
|
return 1
|
|
|
|
/mob/living/proc/get_organ_target()
|
|
var/mob/shooter = src
|
|
var/t = shooter.zone_sel?.selecting
|
|
if((t in list( BP_EYES, BP_MOUTH )))
|
|
t = BP_HEAD
|
|
var/obj/item/organ/external/def_zone = ran_zone(t)
|
|
return def_zone
|
|
|
|
|
|
// heal ONE external organ, organ gets randomly selected from damaged ones.
|
|
/mob/living/proc/heal_organ_damage(var/brute, var/burn)
|
|
adjustBruteLoss(-brute)
|
|
adjustFireLoss(-burn)
|
|
src.updatehealth()
|
|
|
|
// damage ONE external organ, organ gets randomly selected from damaged ones.
|
|
/mob/living/proc/take_organ_damage(var/brute, var/burn, var/emp=0, var/used_weapon = null, var/damage_flags, var/silent)
|
|
if(status_flags & GODMODE) return 0 //godmode
|
|
adjustBruteLoss(brute)
|
|
adjustFireLoss(burn)
|
|
src.updatehealth()
|
|
|
|
// heal MANY external organs, in random order
|
|
/mob/living/proc/heal_overall_damage(var/brute, var/burn)
|
|
adjustBruteLoss(-brute)
|
|
adjustFireLoss(-burn)
|
|
src.updatehealth()
|
|
|
|
// damage MANY external organs, in random order
|
|
/mob/living/proc/take_overall_damage(var/brute, var/burn, var/used_weapon = null)
|
|
if(status_flags & GODMODE) return 0 //godmode
|
|
adjustBruteLoss(brute)
|
|
adjustFireLoss(burn)
|
|
src.updatehealth()
|
|
|
|
/mob/living/proc/restore_all_organs()
|
|
return
|
|
|
|
/mob/living/update_gravity(has_gravity)
|
|
if(!ROUND_IS_STARTED)
|
|
return
|
|
if(has_gravity)
|
|
stop_floating()
|
|
else
|
|
start_floating()
|
|
|
|
/mob/living/proc/revive(reset_to_roundstart = TRUE) // this param is only used in human regen.
|
|
// Stop killing yourself. Please.
|
|
// if(suiciding)
|
|
// suiciding = 0
|
|
|
|
rejuvenate()
|
|
if(buckled_to)
|
|
buckled_to.unbuckle()
|
|
if(iscarbon(src))
|
|
var/mob/living/carbon/C = src
|
|
|
|
if(C.handcuffed && !initial(C.handcuffed))
|
|
C.drop_from_inventory(C.handcuffed)
|
|
C.handcuffed = initial(C.handcuffed)
|
|
|
|
if(C.legcuffed && !initial(C.legcuffed))
|
|
C.drop_from_inventory(C.legcuffed)
|
|
C.legcuffed = initial(C.legcuffed)
|
|
BITSET(hud_updateflag, HEALTH_HUD)
|
|
BITSET(hud_updateflag, STATUS_HUD)
|
|
BITSET(hud_updateflag, LIFE_HUD)
|
|
ExtinguishMobCompletely()
|
|
|
|
/mob/living/proc/rejuvenate()
|
|
if(!isnull(reagents))
|
|
reagents.clear_reagents()
|
|
|
|
// shut down various types of badness
|
|
setToxLoss(0)
|
|
setOxyLoss(0)
|
|
setCloneLoss(0)
|
|
setBrainLoss(0)
|
|
SetParalysis(0)
|
|
SetStunned(0)
|
|
SetWeakened(0)
|
|
|
|
// shut down ongoing problems
|
|
stamina = max_stamina
|
|
total_radiation = 0
|
|
nutrition = 400
|
|
hydration = 400
|
|
bodytemperature = T20C
|
|
sdisabilities = 0
|
|
disabilities = 0
|
|
hallucination = 0
|
|
silent = 0
|
|
dizziness = 0
|
|
drowsiness = 0
|
|
stuttering = 0
|
|
confused = 0
|
|
jitteriness = 0
|
|
|
|
// fix blindness and deafness
|
|
blinded = 0
|
|
eye_blind = 0
|
|
eye_blurry = 0
|
|
ear_deaf = 0
|
|
ear_damage = 0
|
|
heal_overall_damage(getBruteLoss(), getFireLoss())
|
|
|
|
// fix all of our organs
|
|
restore_all_organs()
|
|
|
|
// remove the character from the list of the dead
|
|
if(stat == DEAD)
|
|
GLOB.dead_mob_list -= src
|
|
GLOB.living_mob_list += src
|
|
tod = null
|
|
timeofdeath = 0
|
|
|
|
// restore us to conciousness
|
|
set_stat(CONSCIOUS)
|
|
|
|
// make the icons look correct
|
|
regenerate_icons()
|
|
|
|
BITSET(hud_updateflag, HEALTH_HUD)
|
|
BITSET(hud_updateflag, STATUS_HUD)
|
|
BITSET(hud_updateflag, LIFE_HUD)
|
|
|
|
failed_last_breath = 0 //So mobs that died of oxyloss don't revive and have perpetual out of breath.
|
|
|
|
return
|
|
|
|
/mob/living/proc/basic_revival(var/repair_brain = TRUE)
|
|
|
|
if(repair_brain && getBrainLoss() > 50)
|
|
repair_brain = FALSE
|
|
setBrainLoss(50)
|
|
|
|
if(stat == DEAD)
|
|
switch_from_dead_to_living_mob_list()
|
|
timeofdeath = 0
|
|
|
|
set_stat(CONSCIOUS)
|
|
regenerate_icons()
|
|
|
|
BITSET(hud_updateflag, HEALTH_HUD)
|
|
BITSET(hud_updateflag, STATUS_HUD)
|
|
BITSET(hud_updateflag, LIFE_HUD)
|
|
|
|
failed_last_breath = 0 //So mobs that died of oxyloss don't revive and have perpetual out of breath.
|
|
|
|
/mob/living/carbon/basic_revival(var/repair_brain = TRUE)
|
|
if(repair_brain && should_have_organ(BP_BRAIN))
|
|
repair_brain = FALSE
|
|
var/obj/item/organ/internal/brain/brain = internal_organs_by_name[BP_BRAIN]
|
|
if(brain.damage > (brain.max_damage/2))
|
|
brain.damage = (brain.max_damage/2)
|
|
if(brain.status & ORGAN_DEAD)
|
|
brain.status &= ~ORGAN_DEAD
|
|
START_PROCESSING(SSprocessing, brain)
|
|
brain.update_icon()
|
|
..(repair_brain)
|
|
|
|
/mob/living/proc/UpdateDamageIcon()
|
|
return
|
|
|
|
/mob/living/Move(atom/newloc, direct)
|
|
if(buckled_to)
|
|
return
|
|
|
|
if(restrained())
|
|
stop_pulling()
|
|
|
|
|
|
var/t7 = 1
|
|
if(restrained())
|
|
for(var/mob/living/M in range(src, 1))
|
|
if((M.pulling == src && M.stat == 0 && !( M.restrained() )))
|
|
t7 = null
|
|
if((t7 && (pulling && ((get_dist(src, pulling) <= 1 || pulling.loc == loc) && (client && client.moving)))))
|
|
var/turf/T = loc
|
|
. = ..()
|
|
|
|
if(pulling && pulling.loc)
|
|
if(!( isturf(pulling.loc) ))
|
|
stop_pulling()
|
|
return
|
|
|
|
/////
|
|
if(pulling && pulling.anchored)
|
|
stop_pulling()
|
|
return
|
|
|
|
if(!restrained())
|
|
var/diag = get_dir(src, pulling)
|
|
if(!((diag - 1) & diag))
|
|
diag = null
|
|
if((get_dist(src, pulling) > 1 || diag))
|
|
if(isliving(pulling))
|
|
var/mob/living/M = pulling
|
|
var/ok = 1
|
|
if(locate(/obj/item/grab, M.grabbed_by))
|
|
if(prob(75))
|
|
var/obj/item/grab/G = pick(M.grabbed_by)
|
|
if(istype(G, /obj/item/grab))
|
|
for(var/mob/O in viewers(M, null))
|
|
O.show_message(SPAN_WARNING("[G.affecting] has been pulled from [G.assailant]'s grip by [src]"), 1)
|
|
//G = null
|
|
qdel(G)
|
|
else
|
|
ok = 0
|
|
if(locate(/obj/item/grab, M.grabbed_by.len))
|
|
ok = 0
|
|
if(ok)
|
|
var/atom/movable/t = M.pulling
|
|
M.stop_pulling()
|
|
|
|
if(!istype(M.loc, /turf/space))
|
|
var/area/A = get_area(M)
|
|
if(A.has_gravity())
|
|
//this is the gay blood on floor shit -- Added back -- Skie
|
|
if(M.lying && (prob(M.getBruteLoss() / 6)))
|
|
var/turf/location = M.loc
|
|
if(istype(location, /turf/simulated))
|
|
location.add_blood(M)
|
|
//pull damage with injured people
|
|
if(prob(25))
|
|
M.adjustBruteLoss(1)
|
|
visible_message(SPAN_DANGER("\The [M]'s [M.isSynthetic() ? "state worsens": "wounds open more"] from being dragged!"))
|
|
if(M.pull_damage())
|
|
if(prob(25))
|
|
M.adjustBruteLoss(2)
|
|
visible_message(SPAN_DANGER("\The [M]'s [M.isSynthetic() ? "state" : "wounds"] worsen terribly from being dragged!"))
|
|
var/turf/location = M.loc
|
|
if(istype(location, /turf/simulated))
|
|
location.add_blood(M)
|
|
if(ishuman(M))
|
|
var/mob/living/carbon/human/H = M
|
|
var/total_blood = round(REAGENT_VOLUME(H.vessel, /singleton/reagent/blood))
|
|
if(total_blood > 0)
|
|
H.vessel.remove_reagent(/singleton/reagent/blood, 1)
|
|
|
|
|
|
step(pulling, get_dir(pulling.loc, T))
|
|
if(t)
|
|
M.start_pulling(t)
|
|
else
|
|
if(pulling)
|
|
if(istype(pulling, /obj/structure/window))
|
|
var/obj/structure/window/W = pulling
|
|
if(W.is_full_window())
|
|
for(var/obj/structure/window/win in get_step(pulling,get_dir(pulling.loc, T)))
|
|
stop_pulling()
|
|
if(pulling)
|
|
step(pulling, get_dir(pulling.loc, T))
|
|
else
|
|
stop_pulling()
|
|
. = ..()
|
|
|
|
if(s_active && !s_active.Adjacent(src)) //check !( s_active in contents ) first so we hopefully don't have to call get_turf() so much.
|
|
s_active.close(src)
|
|
|
|
if(update_slimes)
|
|
for(var/mob/living/carbon/slime/M in view(1,src))
|
|
M.UpdateFeed(src)
|
|
|
|
/mob/living/verb/resist()
|
|
set name = "Resist"
|
|
set category = "IC"
|
|
|
|
DEFAULT_QUEUE_OR_CALL_VERB(VERB_CALLBACK(src, PROC_REF(execute_resist)))
|
|
|
|
///proc extender of [/mob/living/verb/resist] meant to make the process queable if the server is overloaded when the verb is called
|
|
/mob/living/proc/execute_resist()
|
|
|
|
if(!incapacitated(INCAPACITATION_KNOCKOUT) && canClick())
|
|
SEND_SIGNAL(src, COMSIG_MOB_RESISTED)
|
|
resist_grab()
|
|
if(!weakened)
|
|
process_resist()
|
|
|
|
/mob/living/proc/process_resist()
|
|
//Getting out of someone's inventory.
|
|
if(istype(src.loc, /obj/item/holder))
|
|
escape_inventory(src.loc)
|
|
return
|
|
|
|
//unbuckling yourself
|
|
if(buckled_to)
|
|
spawn() escape_buckle()
|
|
|
|
//Breaking out of a locker?
|
|
if( src.loc && istype(src.loc, /obj/structure/closet) )
|
|
var/obj/structure/closet/C = loc
|
|
spawn() C.mob_breakout(src)
|
|
|
|
/mob/living/proc/escape_inventory(obj/item/holder/H)
|
|
if(H != src.loc)
|
|
return
|
|
if(health < maxhealth * 0.6)
|
|
to_chat(src, SPAN_WARNING("You're too injured to escape..."))
|
|
return
|
|
|
|
var/mob/M = H.loc //Get our mob holder (if any).
|
|
|
|
if(istype(M))
|
|
M.drop_from_inventory(H)
|
|
to_chat(M, SPAN_WARNING("\The [H] wriggles out of your grip!"))
|
|
to_chat(src, SPAN_WARNING("You wriggle out of \the [M]'s grip!"))
|
|
|
|
// Update whether or not this mob needs to pass emotes to contents.
|
|
for(var/atom/A in M.contents)
|
|
if(istype(A,/mob/living/simple_animal/borer) || istype(A,/obj/item/holder))
|
|
return
|
|
M.status_flags &= ~PASSEMOTES
|
|
|
|
else if(istype(H.loc,/obj/item/clothing/accessory/holster))
|
|
var/obj/item/clothing/accessory/holster/holster = H.loc
|
|
if(holster.holstered == H)
|
|
holster.clear_holster()
|
|
to_chat(src, SPAN_WARNING("You extricate yourself from \the [holster]."))
|
|
H.forceMove(get_turf(H))
|
|
else if(istype(H.loc,/obj/item))
|
|
to_chat(src, SPAN_WARNING("You struggle free of \the [H.loc]."))
|
|
H.forceMove(get_turf(H))
|
|
|
|
/mob/living/proc/escape_buckle()
|
|
if(buckled_to)
|
|
buckled_to.user_unbuckle(src)
|
|
|
|
/mob/living/var/last_resist
|
|
|
|
/mob/living/proc/resist_grab()
|
|
if(last_resist + 10 > world.time)
|
|
return
|
|
last_resist = world.time
|
|
if(stunned > 10)
|
|
to_chat(src, SPAN_NOTICE("You can't move..."))
|
|
return
|
|
var/resisting = 0
|
|
for(var/obj/O in requests)
|
|
requests.Remove(O)
|
|
qdel(O)
|
|
resisting++
|
|
var/resist_power = get_resist_power() // How easily the mob can break out of a grab
|
|
for(var/obj/item/grab/G in grabbed_by)
|
|
resisting++
|
|
var/resist_chance
|
|
var/resist_msg
|
|
switch(G.state)
|
|
if(GRAB_PASSIVE)
|
|
if(incapacitated(INCAPACITATION_DISABLED) || src.lying)
|
|
resist_chance = 30 * resist_power
|
|
else
|
|
resist_chance = 70 * resist_power //only a bit difficult to break out of a passive grab
|
|
resist_msg = SPAN_WARNING("[src] pulls away from [G.assailant]'s grip!")
|
|
if(GRAB_AGGRESSIVE)
|
|
if(incapacitated(INCAPACITATION_DISABLED) || src.lying)
|
|
resist_chance = 15 * resist_power
|
|
else
|
|
resist_chance = 50 * resist_power
|
|
resist_msg = SPAN_WARNING("[src] has broken free of [G.assailant]'s grip!")
|
|
if(GRAB_NECK)
|
|
//If the you move when grabbing someone then it's easier for them to break free. Same if the affected mob is immune to stun.
|
|
if(world.time - G.assailant.l_move_time < 30 || !stunned || !src.lying || incapacitated(INCAPACITATION_DISABLED))
|
|
resist_chance = 15 * resist_power
|
|
else
|
|
resist_chance = 3 * resist_power
|
|
resist_msg = SPAN_DANGER("[src] has broken free of [G.assailant]'s headlock!")
|
|
|
|
if(prob(resist_chance))
|
|
visible_message(resist_msg)
|
|
qdel(G)
|
|
break
|
|
|
|
if(resisting)
|
|
visible_message(SPAN_WARNING("[src] resists!"))
|
|
setClickCooldown(2.5 SECONDS)
|
|
|
|
/mob/living/verb/lay_down()
|
|
set name = "Rest"
|
|
set category = "IC.Maneuver"
|
|
|
|
if(last_special + 1 SECOND > world.time)
|
|
to_chat(src, SPAN_WARNING("You're too tired to do this now!"))
|
|
return
|
|
if(in_neck_grab())
|
|
to_chat(src, SPAN_WARNING("You are being restrained!"))
|
|
return
|
|
last_special = world.time
|
|
resting = !resting
|
|
to_chat(src, SPAN_NOTICE("You are now [resting ? "resting" : "getting up"]."))
|
|
SEND_SIGNAL(src, COMSIG_MOB_RESTED)
|
|
update_canmove()
|
|
update_icon()
|
|
|
|
/mob/living/proc/cannot_use_vents()
|
|
return "You can't fit into that vent."
|
|
|
|
/mob/living/proc/has_brain()
|
|
return 1
|
|
|
|
/mob/living/proc/has_eyes()
|
|
return 1
|
|
|
|
/mob/living/proc/eyes_protected(var/obj/stab_item, var/stabbed = FALSE) // if stabbed is set to true if we're being stabbed and not just checking
|
|
if(!has_eyes())
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/mob/living/proc/slip(var/slipped_on,stun_duration=8)
|
|
return 0
|
|
|
|
/mob/living/proc/under_door()
|
|
//This function puts a silicon on a layer that makes it draw under doors, then periodically checks if its still standing on a door
|
|
if(layer > UNDERDOOR)//Don't toggle it if we're hiding
|
|
layer = UNDERDOOR
|
|
underdoor = 1
|
|
|
|
/mob/living/touch_map_edge()
|
|
|
|
//check for nuke disks
|
|
if(client && stat != DEAD) //if they are clientless and dead don't bother, the parent will treat them as any other container
|
|
if(istype(SSticker.mode, /datum/game_mode/nuclear)) //only really care if the game mode is nuclear
|
|
var/datum/game_mode/nuclear/G = SSticker.mode
|
|
if(G.check_mob(src))
|
|
if(x <= TRANSITIONEDGE)
|
|
inertia_dir = 4
|
|
else if(x >= world.maxx -TRANSITIONEDGE)
|
|
inertia_dir = 8
|
|
else if(y <= TRANSITIONEDGE)
|
|
inertia_dir = 1
|
|
else if(y >= world.maxy -TRANSITIONEDGE)
|
|
inertia_dir = 2
|
|
to_chat(src, SPAN_WARNING("Something you are carrying is preventing you from leaving."))
|
|
return
|
|
|
|
..()
|
|
|
|
//damage/heal the mob ears and adjust the deaf amount
|
|
/mob/living/adjustEarDamage(var/damage, var/deaf, var/ringing = FALSE)
|
|
var/alreadydeaf = FALSE
|
|
if(ear_deaf)
|
|
alreadydeaf = TRUE
|
|
|
|
ear_damage = max(0, ear_damage + damage)
|
|
ear_deaf = max(0, ear_deaf + deaf)
|
|
|
|
if(ringing && !alreadydeaf)
|
|
if(ear_damage >= 5)
|
|
if(ear_damage >= 15)
|
|
to_chat(src, SPAN_DANGER("Your ears start to ring badly!"))
|
|
else
|
|
to_chat(src, SPAN_DANGER("Your ears start to ring!"))
|
|
|
|
|
|
//pass a negative argument to skip one of the variable
|
|
|
|
/mob/living/setEarDamage(var/damage, var/deaf)
|
|
if(damage >= 0)
|
|
ear_damage = damage
|
|
if(deaf >= 0)
|
|
ear_deaf = deaf
|
|
|
|
/mob/proc/can_be_possessed_by(var/mob/abstract/ghost/observer/possessor)
|
|
return istype(possessor) && possessor.client
|
|
|
|
/mob/living/can_be_possessed_by(var/mob/abstract/ghost/observer/possessor)
|
|
if(!..())
|
|
return 0
|
|
if(!possession_candidate)
|
|
to_chat(possessor, SPAN_WARNING("That animal cannot be possessed."))
|
|
return 0
|
|
if(jobban_isbanned(possessor, "Animal"))
|
|
to_chat(possessor, SPAN_WARNING("You are banned from animal roles."))
|
|
return 0
|
|
if(!possessor.MayRespawn(1,ANIMAL))
|
|
return 0
|
|
return 1
|
|
|
|
/mob/living/proc/do_possession(var/mob/abstract/ghost/observer/possessor)
|
|
|
|
if(!(istype(possessor) && possessor.ckey))
|
|
return 0
|
|
|
|
if(src.ckey || src.client)
|
|
to_chat(possessor, SPAN_WARNING("\The [src] already has a player."))
|
|
return 0
|
|
|
|
message_admins("<span class='adminnotice'>[key_name_admin(possessor)] has taken control of \the [src].</span>")
|
|
log_admin("[key_name(possessor)] took control of \the [src].")
|
|
src.ckey = possessor.ckey
|
|
qdel(possessor)
|
|
|
|
if(round_is_spooky(6)) // Six or more active cultists.
|
|
to_chat(src, SPAN_NOTICE("You reach out with tendrils of ectoplasm and invade the mind of \the [src]..."))
|
|
to_chat(src, "<b>You have assumed direct control of \the [src].</b>")
|
|
to_chat(src, SPAN_NOTICE("Due to the spookiness of the round, you have taken control of the poor animal as an invading, possessing spirit - roleplay accordingly."))
|
|
src.universal_speak = 1
|
|
src.universal_understand = 1
|
|
//src.cultify() // Maybe another time.
|
|
return
|
|
|
|
to_chat(src, "<b>You are now \the [src]!</b>")
|
|
to_chat(src, SPAN_NOTICE("Remember to stay in character for a mob of this type!"))
|
|
return 1
|
|
|
|
/mob/living/Initialize()
|
|
. = ..()
|
|
add_to_target_grid()
|
|
ability_master = new /atom/movable/screen/movable/ability_master(FALSE, src)
|
|
|
|
var/static/list/loc_connections = list(
|
|
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
|
|
)
|
|
|
|
register_init_signals()
|
|
|
|
AddElement(/datum/element/connect_loc, loc_connections)
|
|
|
|
/mob/living/Destroy()
|
|
|
|
//Aiming overlay
|
|
QDEL_NULL(aiming)
|
|
QDEL_LIST(aimed_at_by)
|
|
|
|
//Psi complexus
|
|
QDEL_NULL(psi)
|
|
|
|
if(vr_mob)
|
|
vr_mob = null
|
|
if(old_mob)
|
|
old_mob = null
|
|
|
|
//Remove contained mobs
|
|
if(loc)
|
|
for(var/mob/M in contents)
|
|
M.dropInto(loc)
|
|
else
|
|
for(var/mob/M in contents)
|
|
qdel(M)
|
|
|
|
QDEL_NULL(reagents)
|
|
|
|
if(auras)
|
|
for(var/a in auras)
|
|
remove_aura(a)
|
|
|
|
return ..()
|
|
|
|
/mob/living/proc/nervous_system_failure()
|
|
return FALSE
|
|
|
|
/mob/living/proc/get_digestion_product()
|
|
return null
|
|
|
|
/proc/is_valid_for_devour(var/mob/living/test, var/eat_types)
|
|
//eat_types must contain all types that the mob has. For example we need both humanoid and synthetic to eat an IPC.
|
|
var/test_types = test.find_type()
|
|
. = (eat_types & test_types) == test_types
|
|
|
|
/mob/living/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
|
SIGNAL_HANDLER
|
|
|
|
if(istype(arrived, /mob/living/heavy_vehicle))
|
|
var/mob/living/heavy_vehicle/MB = arrived
|
|
MB.trample(src)
|
|
|
|
#define PPM 9 //Protein per meat, used for calculating the quantity of protein in an animal
|
|
/mob/living/proc/calculate_composition()
|
|
if(!composition_reagent)//if no reagent has been set, then we'll set one
|
|
var/type = find_type(src)
|
|
if(type & TYPE_SYNTHETIC)
|
|
src.composition_reagent = /singleton/reagent/iron
|
|
else
|
|
src.composition_reagent = /singleton/reagent/nutriment/protein
|
|
|
|
//if the mob is a simple animal with a defined meat quantity
|
|
if(istype(src, /mob/living/simple_animal))
|
|
var/mob/living/simple_animal/SA = src
|
|
if(SA.meat_amount)
|
|
src.composition_reagent_quantity = SA.meat_amount*2*PPM
|
|
|
|
//The quantity of protein is based on the meat_amount, but multiplied by 2
|
|
|
|
var/size_reagent = (src.mob_size * src.mob_size) * 3//The quantity of protein is set to 3x mob size squared
|
|
if(size_reagent > src.composition_reagent_quantity)//We take the larger of the two
|
|
src.composition_reagent_quantity = size_reagent
|
|
#undef PPM
|
|
|
|
/mob/living/proc/get_resist_power()
|
|
return 1
|
|
|
|
/mob/living/proc/seizure(var/severity_multiplier = 1)
|
|
if(!paralysis && stat == CONSCIOUS)
|
|
visible_message(SPAN_HIGHDANGER("\The [src] starts having a seizure!"))
|
|
Paralyse(24*severity_multiplier)
|
|
make_jittery(200*severity_multiplier)
|
|
adjustHalLoss(60*severity_multiplier)
|
|
|
|
/mob/living/proc/InStasis()
|
|
return FALSE
|
|
|
|
/mob/living/update_icon()
|
|
for(var/aura in auras)
|
|
var/obj/aura/A = aura
|
|
var/icon/aura_overlay = icon(A.icon, icon_state = A.icon_state)
|
|
AddOverlays(aura_overlay)
|
|
|
|
/mob/living/proc/add_aura(var/obj/aura/aura)
|
|
LAZYDISTINCTADD(auras, aura)
|
|
update_icon()
|
|
return TRUE
|
|
|
|
/mob/living/proc/remove_aura(var/obj/aura/aura)
|
|
LAZYREMOVE(auras, aura)
|
|
update_icon()
|
|
return TRUE
|
|
|
|
/mob/living/proc/needs_wheelchair()
|
|
return FALSE
|
|
|
|
/mob/living/verb/toggle_run_intent()
|
|
set hidden = 1
|
|
set name = "mov_intent"
|
|
if(hud_used?.move_intent)
|
|
hud_used.move_intent.Click()
|
|
|
|
/mob/living/verb/toggle_intentionally_lying()
|
|
set hidden = 1
|
|
set name = "lie_down"
|
|
if(hud_used?.move_intent)
|
|
hud_used.move_intent.Click(params="button=middle")
|
|
|
|
/**
|
|
* Used by a macro in skin.dmf to toggle the throw
|
|
*/
|
|
/mob/living/verb/throw_intent_keyDown()
|
|
set hidden = 1
|
|
set name = "throw_intent"
|
|
if(!(src.in_throw_mode))
|
|
toggle_throw_mode()
|
|
|
|
/mob/living/verb/throw_intent_keyUp()
|
|
set hidden = 1
|
|
set name = "throw_intent_up"
|
|
if(src.in_throw_mode)
|
|
toggle_throw_mode()
|
|
|
|
/mob/living/verb/throw_intent_toggle()
|
|
set hidden = 1
|
|
set name = "throw_intent_toggle"
|
|
toggle_throw_mode()
|
|
|
|
/mob/living/proc/add_hallucinate(var/amount)
|
|
hallucination += amount
|
|
hallucination += amount
|
|
|
|
/mob/living/set_respawn_time()
|
|
set_death_time(CREW, world.time)
|
|
//Used by simple animals and monkey species for renaming. M is the one doing the renaming
|
|
/mob/living/proc/can_name(var/mob/living/M)
|
|
return FALSE
|
|
|
|
/mob/living/proc/is_anti_materiel_vulnerable()
|
|
return FALSE
|
|
|
|
/mob/living/get_speech_bubble_state_modifier()
|
|
return isSynthetic() ? "robot" : ..()
|
|
|
|
///Performs the aftereffects of blocking a projectile.
|
|
/mob/living/proc/block_projectile_effects()
|
|
return
|
|
|
|
/// Uses presence of [TRAIT_UNDENSE] to figure out what is the correct density state for the mob. Triggered by trait signal.
|
|
/mob/living/proc/update_density()
|
|
if(HAS_TRAIT(src, TRAIT_UNDENSE))
|
|
set_density(FALSE)
|
|
else
|
|
set_density(TRUE)
|