Files
Chap c3d773402c [REFACTOR] Remove the flying var, adds the flying trait. (#26881)
* Remove the flying var, adds the flying trait

* Remove flight when broomstick is unwielded

* Adds some comments

* tab indentation

* Fix carp flight

* Remove comment

---------

Co-authored-by: Adrer <adrermail@gmail.com>
2024-10-16 17:46:19 +00:00

1183 lines
36 KiB
Plaintext

/mob/living/Initialize()
. = ..()
var/datum/atom_hud/data/human/medical/advanced/medhud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
medhud.add_to_hud(src)
register_init_signals()
faction += "\ref[src]"
determine_move_and_pull_forces()
GLOB.mob_living_list += src
if(advanced_bullet_dodge_chance)
RegisterSignal(src, COMSIG_ATOM_PREHIT, PROC_REF(advanced_bullet_dodge))
for(var/initial_trait in initial_traits)
ADD_TRAIT(src, initial_trait, INNATE_TRAIT)
// Used to determine the forces dependend on the mob size
// Will only change the force if the force was not set in the mob type itself
/mob/living/proc/determine_move_and_pull_forces()
var/value
switch(mob_size)
if(MOB_SIZE_TINY)
value = MOVE_FORCE_EXTREMELY_WEAK
if(MOB_SIZE_SMALL)
value = MOVE_FORCE_WEAK
if(MOB_SIZE_HUMAN)
value = MOVE_FORCE_NORMAL
if(MOB_SIZE_LARGE)
value = MOVE_FORCE_NORMAL // For now
if(!move_force)
move_force = value
if(!pull_force)
pull_force = value
if(!move_resist)
move_resist = value
/mob/living/prepare_huds()
..()
prepare_data_huds()
/mob/living/proc/prepare_data_huds()
med_hud_set_health()
med_hud_set_status()
/mob/living/Destroy()
if(ranged_ability)
ranged_ability.remove_ranged_ability(src)
remove_from_all_data_huds()
GLOB.mob_living_list -= src
if(LAZYLEN(status_effects))
for(var/s in status_effects)
var/datum/status_effect/S = s
if(S.on_remove_on_mob_delete) //the status effect calls on_remove when its mob is deleted
qdel(S)
else
S.be_replaced()
QDEL_NULL(middleClickOverride)
if(mind?.current == src)
mind.current = null
UnregisterSignal(src, COMSIG_ATOM_PREHIT)
return ..()
/mob/living/ghostize(can_reenter_corpse = 1)
var/prev_client = client
. = ..()
if(.)
if(ranged_ability && prev_client)
ranged_ability.remove_mousepointer(prev_client)
SEND_SIGNAL(src, COMSIG_LIVING_GHOSTIZED)
/mob/living/proc/OpenCraftingMenu()
return
//Generic Bump(). Override MobBump() and ObjBump() instead of this.
/mob/living/Bump(atom/A, yes)
if(..()) //we are thrown onto something
return
if(buckled || !yes || now_pushing)
return
if(ismob(A))
if(MobBump(A))
return
if(isobj(A))
if(ObjBump(A))
return
if(istype(A, /atom/movable))
if(PushAM(A, move_force))
return
//Called when we bump into a mob
/mob/living/proc/MobBump(mob/M)
if(m_intent == MOVE_INTENT_WALK)
return TRUE
//Even if we don't push/swap places, we "touched" them, so spread fire
spreadFire(M)
SEND_SIGNAL(src, COMSIG_LIVING_MOB_BUMP, M)
// No pushing if we're already pushing past something, or if the mob we're pushing into is anchored.
if(now_pushing || M.anchored)
return TRUE
//Should stop you pushing a restrained person out of the way
if(isliving(M))
var/mob/living/L = M
if(L.pulledby && L.pulledby != src && L.restrained())
if(!(world.time % 5))
to_chat(src, "<span class='warning'>[L] is restrained, you cannot push past.</span>")
return TRUE
if(pulledby == L && (a_intent != INTENT_HELP || L.a_intent != INTENT_HELP)) //prevents boosting the person pulling you, but you can still move through them on help intent
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 class='warning'>[L] is restrained, you cannot push past.</span>")
return TRUE
if(moving_diagonally) //no mob swap during diagonal moves.
return TRUE
if(a_intent == INTENT_HELP) // Help intent doesn't mob swap a mob pulling a structure
if(isstructure(M.pulling) || isstructure(pulling))
return TRUE
//Let us check if the person has riot equipment. We should not move past them or push them, unless they are on *walk* intent. This is so officers batoning on help can't me moved past.
var/riot_equipment_used = (M.r_hand?.GetComponent(/datum/component/parry) || M.l_hand?.GetComponent(/datum/component/parry))
if(!M.buckled && !M.has_buckled_mobs())
var/mob_swap
//the puller can always swap with it's victim if on grab intent
if(length(M.grabbed_by) && a_intent == INTENT_GRAB)
mob_swap = TRUE
//restrained people act if they were on 'help' intent to prevent a person being pulled from being seperated from their puller
else if(((M.restrained() || M.a_intent == INTENT_HELP) && !(riot_equipment_used && M.m_intent == MOVE_INTENT_RUN)) && (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 = TRUE
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
M.Move(oldloc)
Move(oldMloc)
if(!src_passmob)
pass_flags &= ~PASSMOB
if(!M_passmob)
M.pass_flags &= ~PASSMOB
now_pushing = FALSE
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
//anti-riot equipment is also anti-push
if(riot_equipment_used)
return TRUE
//Called when we bump into 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(isstructure(AM) && AM.pulledby)
if(a_intent == INTENT_HELP && AM.pulledby != src) // Help intent doesn't push other peoples pulled structures
return FALSE
if(get_dist(get_step(AM, get_dir(src, AM)), AM.pulledby)>1)//Release pulled structures beyond 1 distance
AM.pulledby.stop_pulling()
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(step(AM, t))
step(src, t)
if(current_dir)
AM.setDir(current_dir)
now_pushing = FALSE
/mob/living/CanPathfindPass(to_dir, datum/can_pass_info/pass_info)
return TRUE // Unless you're a mule, something's trying to run you over.
/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 FALSE
if(!is_level_reachable(T.z))
return FALSE
if(!isnull(user) && src == user)
return FALSE
if(invisibility || alpha == 0)//cloaked
return FALSE
if(HAS_TRAIT(src, TRAIT_AI_UNTRACKABLE))
return FALSE
if(user && user.is_jammed())
return FALSE
// Now, are they viewable by a camera? (This is last because it's the most intensive check)
if(!near_camera(src))
return FALSE
return TRUE
/mob/living/proc/is_jammed()
for(var/obj/item/jammer/jammer in GLOB.active_jammers)
if(atoms_share_level(get_turf(src), get_turf(jammer)) && get_dist(get_turf(src), get_turf(jammer)) < jammer.range)
return TRUE
return FALSE
//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 = null
if(istype(AM) && Adjacent(AM))
start_pulling(AM, show_message = TRUE)
else
stop_pulling()
/mob/living/stop_pulling()
..()
if(pullin)
pullin.update_icon(UPDATE_ICON_STATE)
/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(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
return FALSE
if(HAS_TRAIT(src, TRAIT_FAKEDEATH))
return FALSE
return ..()
/mob/living/run_pointed(atom/A)
if(!..())
return FALSE
var/obj/item/hand_item = get_active_hand()
if(istype(hand_item) && hand_item.run_pointed_on_item(src, A))
return TRUE
var/pointed_object = "\the [A]"
if(A.loc in src)
pointed_object += " inside [A.loc]"
visible_message("<b>[src]</b> points to [pointed_object].")
return TRUE
/mob/living/verb/succumb()
set hidden = TRUE
// if you use the verb you better mean it
do_succumb(FALSE)
/mob/living/proc/do_succumb(cancel_on_no_words)
if(stat == DEAD)
to_chat(src, "<span class='notice'>It's too late, you're already dead!</span>")
return
if(health >= HEALTH_THRESHOLD_SUCCUMB)
to_chat(src, "<span class='warning'>You are unable to succumb to death! This life continues!</span>")
return
last_words = null // In case we kept some from last time
var/final_words = tgui_input_text(src, "Do you have any last words?", "Goodnight, Sweet Prince", encode = FALSE)
if(isnull(final_words) && cancel_on_no_words)
to_chat(src, "<span class='notice'>You decide you aren't quite ready to die.</span>")
return
if(stat == DEAD)
return
if(health >= HEALTH_THRESHOLD_SUCCUMB)
to_chat(src, "<span class='warning'>You are unable to succumb to death! This life continues!</span>")
return
if(!isnull(final_words))
create_log(MISC_LOG, "gave their final words, [last_words]")
last_words = final_words
whisper(final_words)
add_attack_logs(src, src, "[src] has [!isnull(final_words) ? "whispered [p_their()] final words" : "succumbed to death"] with [round(health, 0.1)] points of health!")
create_log(MISC_LOG, "has succumbed to death with [round(health, 0.1)] points of health")
adjustOxyLoss(max(health - HEALTH_THRESHOLD_DEAD, 0))
// super check for weird mobs, including ones that adjust hp
// we don't want to go overboard and gib them, though
for(var/i in 1 to 5)
if(health < HEALTH_THRESHOLD_DEAD)
break
take_overall_damage(max(5, health - HEALTH_THRESHOLD_DEAD), 0)
if(!isnull(final_words))
addtimer(CALLBACK(src, PROC_REF(death)), 1 SECONDS)
else
death()
to_chat(src, "<span class='notice'>You have given up life and succumbed to death.</span>")
apply_status_effect(STATUS_EFFECT_RECENTLY_SUCCUMBED)
/mob/living/proc/InCritical()
return (health < HEALTH_THRESHOLD_CRIT && health > HEALTH_THRESHOLD_DEAD && stat == UNCONSCIOUS)
/mob/living/ex_act(severity)
..()
flash_eyes()
/mob/living/acid_act(acidpwr, acid_volume)
take_organ_damage(acidpwr * min(1, acid_volume * 0.1))
return 1
/mob/living/welder_act(mob/user, obj/item/I)
if(!I.tool_use_check(user, 0, TRUE))
return
if(IgniteMob())
message_admins("[key_name_admin(user)] set [key_name_admin(src)] on fire with [I]")
log_game("[key_name(user)] set [key_name(src)] on fire with [I]")
/mob/living/proc/updatehealth(reason = "none given")
if(status_flags & GODMODE)
health = maxHealth
set_stat(CONSCIOUS)
return
health = maxHealth - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() - getCloneLoss()
SEND_SIGNAL(src, COMSIG_LIVING_HEALTH_UPDATE)
update_stat("updatehealth([reason])")
med_hud_set_health()
med_hud_set_status()
update_health_hud()
update_stamina_hud()
//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 0
/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
// if(istype(src, /mob/living/carbon/human))
// to_chat(world, "[src] ~ [bodytemperature] ~ [temperature]")
return temperature
/mob/proc/get_contents()
return
//Recursive function to find everything a mob is holding.
/mob/living/get_contents(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(isstorage(G.gift))
L += get_contents(G.gift)
for(var/obj/item/smallDelivery/D in Storage.return_inv()) //Check for package wrapped items
L += D.wrapped
if(isstorage(D.wrapped)) //this should never happen
L += get_contents(D.wrapped)
return L
else
L += contents
for(var/obj/item/storage/S in contents) //Check for storage items
L += get_contents(S)
for(var/obj/item/mod/control/C in contents) //Check for modsuit storage
for(var/obj/item/mod/module/storage/MS in C.contents)
for(var/obj/item/storage/MSB in MS.contents)
L += get_contents(MSB)
for(var/obj/item/clothing/suit/storage/S in contents)//Check for labcoats and jackets
L += get_contents(S)
for(var/obj/item/clothing/accessory/storage/S in contents)//Check for holsters
L += get_contents(S)
for(var/obj/item/bio_chip/storage/I in contents) //Check for storage implants.
L += I.get_contents()
for(var/obj/item/gift/G in contents) //Check for gift-wrapped items
L += G.gift
if(isstorage(G.gift))
L += get_contents(G.gift)
for(var/obj/item/smallDelivery/D in contents) //Check for package wrapped items
L += D.wrapped
if(isstorage(D.wrapped)) //this should never happen
L += get_contents(D.wrapped)
for(var/obj/item/folder/F in contents)
L += F.contents //Folders can't store any storage items.
return L
/mob/living/proc/check_contents_for(A)
var/list/L = get_contents()
for(var/obj/B in L)
if(B.type == A)
return 1
return 0
// Living mobs use can_inject() to make sure that the mob is not syringe-proof in general.
/mob/living/proc/can_inject(mob/user, error_msg, target_zone, penetrate_thick)
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/restore_all_organs()
return
/mob/living/proc/revive()
rejuvenate()
if(iscarbon(src))
var/mob/living/carbon/C = src
if(C.handcuffed && !initial(C.handcuffed))
C.unEquip(C.handcuffed)
C.handcuffed = initial(C.handcuffed)
C.update_handcuffed()
if(C.legcuffed && !initial(C.legcuffed))
C.unEquip(C.legcuffed)
C.legcuffed = initial(C.legcuffed)
C.update_inv_legcuffed()
if(C.reagents)
C.reagents.clear_reagents()
QDEL_LIST_CONTENTS(C.reagents.addiction_list)
C.reagents.addiction_threshold_accumulated.Cut()
QDEL_LIST_CONTENTS(C.processing_patches)
// rejuvenate: Called by `revive` to get the mob into a revivable state
// the admin "rejuvenate" command calls `revive`, not this proc.
/mob/living/proc/rejuvenate()
var/mob/living/carbon/human/human_mob = null //Get this declared for use later.
// shut down various types of badness
setToxLoss(0)
setOxyLoss(0)
setCloneLoss(0)
setBrainLoss(0)
setStaminaLoss(0)
SetSleeping(0)
SetParalysis(0, TRUE)
SetStunned(0, TRUE)
SetWeakened(0, TRUE)
SetSlowed(0)
SetImmobilized(0)
SetKnockDown(0)
SetLoseBreath(0)
SetDizzy(0)
SetJitter(0)
SetStuttering(0)
SetConfused(0)
SetDrowsy(0)
radiation = 0
SetDruggy(0)
SetHallucinate(0)
set_nutrition(NUTRITION_LEVEL_FED + 50)
bodytemperature = 310
cure_blind()
cure_nearsighted()
CureMute()
CureDeaf()
CureTourettes()
CureEpilepsy()
CureCoughing()
CureNervous()
SetEyeBlind(0)
SetEyeBlurry(0)
SetDeaf(0)
heal_overall_damage(1000, 1000)
ExtinguishMob()
SEND_SIGNAL(src, COMSIG_LIVING_CLEAR_STUNS)
fire_stacks = 0
on_fire = 0
suiciding = 0
if(buckled) //Unbuckle the mob and clear the alerts.
unbuckle(force = TRUE)
if(iscarbon(src))
var/mob/living/carbon/C = src
C.handcuffed = initial(C.handcuffed)
for(var/thing in C.viruses)
var/datum/disease/D = thing
D.cure(0)
// restore all of the human's blood and reset their shock stage
if(ishuman(src))
human_mob = src
human_mob.set_heartattack(FALSE)
human_mob.restore_blood()
human_mob.decaylevel = 0
human_mob.remove_all_embedded_objects()
SEND_SIGNAL(src, COMSIG_LIVING_AHEAL)
restore_all_organs()
surgeries.Cut() //End all surgeries.
if(stat == DEAD)
update_revive()
else if(stat == UNCONSCIOUS)
WakeUp()
update_fire()
regenerate_icons()
restore_blood()
if(human_mob)
human_mob.update_eyes()
human_mob.update_dna()
resting = FALSE
stand_up() // wake the fuck up badmin, we've got an "event" to burn
return
/mob/living/proc/remove_CC()
SetWeakened(0)
SetKnockDown(0)
SetStunned(0)
SetParalysis(0)
SetImmobilized(0)
SetSleeping(0)
setStaminaLoss(0)
SetSlowed(0)
/mob/living/proc/UpdateDamageIcon()
return
/mob/living/Move(atom/newloc, direct, movetime)
if(buckled && buckled.loc != newloc) //not updating position
if(!buckled.anchored)
return buckled.Move(newloc, direct)
else
return 0
var/atom/movable/pullee = pulling
if(pullee && get_dist(src, pullee) > 1)
stop_pulling()
if(pullee && !isturf(pullee.loc) && pullee.loc != loc)
log_game("DEBUG: [src]'s pull on [pullee] was broken despite [pullee] being in [pullee.loc]. Pull stopped manually.")
stop_pulling()
if(restrained() || HAS_TRAIT(src, TRAIT_CANNOT_PULL))
stop_pulling()
var/turf/old_loc = loc
. = ..()
if(.)
step_count++
pull_pulled(old_loc, pullee, movetime)
if(s_active && !(s_active in contents) && get_turf(s_active) != get_turf(src)) //check !( s_active in contents) first so we hopefully don't have to call get_turf() so much.
s_active.close(src)
/mob/living/proc/pull_pulled(turf/dest, atom/movable/pullee, movetime)
if(pulling && pulling == pullee) // we were pulling a thing and didn't lose it during our move.
if(pulling.anchored)
stop_pulling()
return
var/pull_dir = get_dir(src, pulling)
if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir))) // puller and pullee more than one tile away or in diagonal position
if(isliving(pulling))
var/mob/living/M = pulling
if(IS_HORIZONTAL(M) && !M.buckled && (prob(M.getBruteLoss() * 200 / M.maxHealth))) // So once you reach 50 brute damage you hit 100% chance to leave a blood trail for every tile you're pulled
M.makeTrail(dest)
pulling.Move(dest, get_dir(pulling, dest), movetime) // the pullee tries to reach our previous position
if(pulling && get_dist(src, pulling) > 1) // the pullee couldn't keep up
stop_pulling()
/mob/living/proc/makeTrail(turf/turf_to_trail_on)
if(!has_gravity(src))
return
if(!isturf(loc))
return
var/trail_type = getTrail()
if(!trail_type)
return
var/brute_ratio = round(getBruteLoss() / maxHealth, 0.1)
if(!blood_volume && !(blood_volume > max(BLOOD_VOLUME_NORMAL * (1 - brute_ratio * 0.25), 0))) // Okay let's dive into the maths. For every 50 brute damage taken, the minimal blood level you can have decreases by 12,5%
return
blood_volume = max(blood_volume - max(1, brute_ratio * 2), 0) // The amount of blood lost per tile of movement is always at least 1cl, and every 50 damage after reaching 50 brute damage taken will increase the bleed by 1cl per tile
var/newdir = get_dir(turf_to_trail_on, loc)
if(newdir != dir)
newdir |= dir
if(newdir == (NORTH|SOUTH))
newdir = NORTH
else if(newdir == (EAST|WEST))
newdir = EAST
if(IS_DIR_CARDINAL(newdir) && prob(50))
newdir = turn(get_dir(turf_to_trail_on, loc), 180)
var/blood_exists = locate(/obj/effect/decal/cleanable/trail_holder) in loc //checks for blood splatter already on the floor
if(!blood_exists)
new /obj/effect/decal/cleanable/trail_holder(loc)
for(var/obj/effect/decal/cleanable/trail_holder/existing_trail in loc)
if((!(newdir in existing_trail.existing_dirs) || trail_type == "trails_1" || trail_type == "trails_2") && length(existing_trail.existing_dirs) <= 16) //maximum amount of overlays is 16 (all light & heavy directions filled)
existing_trail.existing_dirs += newdir
existing_trail.overlays.Add(image('icons/effects/blood.dmi', trail_type, dir = newdir))
existing_trail.transfer_mob_blood_dna(src)
if(ishuman(src))
var/mob/living/carbon/human/H = src
if(H.dna.species.blood_color)
existing_trail.color = H.dna.species.blood_color
else
existing_trail.color = "#A10808"
/mob/living/carbon/human/makeTrail(turf/T)
if((NO_BLOOD in dna.species.species_traits) || dna.species.exotic_blood || !bleed_rate || bleedsuppress)
return
..()
/mob/living/proc/getTrail()
if(getBruteLoss() < 300)
return pick("ltrails_1", "ltrails_2")
else
return pick("trails_1", "trails_2")
/mob/living/experience_pressure_difference(flow_x, flow_y, pressure_resistance_prob_delta = 0)
if(buckled)
return
if(client && client.move_delay >= world.time + world.tick_lag * 2)
pressure_resistance_prob_delta -= 30
var/list/turfs_to_check = list()
if(has_limbs)
var/direction = 0
if(flow_x > 100)
direction |= EAST
if(flow_x < -100)
direction |= WEST
if(flow_y > 100)
direction |= NORTH
if(flow_y < -100)
direction |= SOUTH
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 -= 20
continue
for(var/atom/movable/AM in T)
if(AM.density && AM.anchored)
pressure_resistance_prob_delta -= 20
break
..(flow_x, flow_y, pressure_resistance_prob_delta)
/*//////////////////////
START RESIST PROCS
*///////////////////////
/mob/living/can_resist()
return !((next_move > world.time) || incapacitated(ignore_restraints = TRUE))
/mob/living/verb/resist()
set name = "Resist"
set category = "IC"
DEFAULT_QUEUE_OR_CALL_VERB(VERB_CALLBACK(src, PROC_REF(run_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/run_resist()
if(!can_resist())
return
changeNext_move(CLICK_CD_RESIST)
SEND_SIGNAL(src, COMSIG_LIVING_RESIST, src)
if(!restrained())
if(resist_grab())
return
//unbuckling yourself
if(buckled)
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
resist_restraints() //trying to remove cuffs.
/*////////////////////
RESIST SUBPROCS
*/////////////////////
/mob/living/proc/resist_grab()
var/resisting = 0
if(HAS_TRAIT(src, TRAIT_IMMOBILIZED))
return FALSE //You can't move, so you can't resist
for(var/X in grabbed_by)
var/obj/item/grab/G = X
resisting++
switch(G.state)
if(GRAB_PASSIVE)
qdel(G)
if(GRAB_AGGRESSIVE)
if(prob(60))
visible_message("<span class='danger'>[src] has broken free of [G.assailant]'s grip!</span>")
qdel(G)
if(GRAB_NECK)
if(prob(5))
visible_message("<span class='danger'>[src] has broken free of [G.assailant]'s headlock!</span>")
qdel(G)
if(resisting)
visible_message("<span class='danger'>[src] resists!</span>")
return 1
/mob/living/proc/resist_buckle()
spawn(0)
resist_muzzle()
buckled.user_unbuckle_mob(src,src)
/mob/living/proc/resist_muzzle()
return
/mob/living/proc/resist_fire()
return
/mob/living/proc/resist_restraints()
spawn(0)
resist_muzzle()
return
/*//////////////////////
END RESIST PROCS
*///////////////////////
/// Unbuckle the mob from whatever it is buckled to.
/mob/living/proc/unbuckle(force)
buckled.unbuckle_mob(src, force)
/mob/living/proc/Exhaust()
to_chat(src, "<span class='notice'>You're too exhausted to keep going...</span>")
Weaken(10 SECONDS)
/mob/living/proc/get_visible_name()
return name
/mob/living/proc/is_facehugged()
return FALSE
/mob/living/update_gravity(has_gravity)
if(!SSticker)
return
if(has_gravity)
clear_alert("weightless")
else
throw_alert("weightless", /atom/movable/screen/alert/weightless)
if(!HAS_TRAIT(src, TRAIT_FLYING))
float(!has_gravity)
/mob/living/proc/float(on)
if(throwing)
return
var/fixed = FALSE
if(anchored || (buckled && buckled.anchored))
fixed = TRUE
if(on && !floating && !fixed)
animate(src, pixel_y = pixel_y + 2, time = 10, loop = -1)
animate(pixel_y = pixel_y - 2, time = 10, loop = -1)
floating = TRUE
else if(((!on || fixed) && floating))
animate(src, pixel_y = get_standard_pixel_y_offset(), time = 10)
floating = FALSE
/mob/living/proc/can_use_vents()
return "You can't fit into that vent."
//Checks for anything other than eye protection that would stop flashing. Overridden in carbon.dm and human.dm
/mob/living/proc/can_be_flashed(intensity = 1, override_blindness_check = 0)
if(check_eye_prot() >= intensity || (!override_blindness_check && (HAS_TRAIT(src, TRAIT_BLIND) || HAS_TRAIT(src, TRAIT_FLASH_PROTECTION))))
return FALSE
return TRUE
//called when the mob receives a bright flash
/mob/living/proc/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, laser_pointer = FALSE, type = /atom/movable/screen/fullscreen/stretch/flash)
if(can_be_flashed(intensity, override_blindness_check))
overlay_fullscreen("flash", type)
addtimer(CALLBACK(src, PROC_REF(clear_fullscreen), "flash", 25), 25)
return 1
/mob/living/proc/check_eye_prot()
return 0
/mob/living/proc/check_ear_prot()
return
/mob/living/singularity_act()
investigate_log("([key_name(src)]) has been consumed by the singularity.","singulo") //Oh that's where the clown ended up!
gib()
return 20
/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(!mob_negates_gravity())
step_towards(src,S)
/mob/living/narsie_act()
if(client)
make_new_construct(/mob/living/simple_animal/hostile/construct/harvester, src, cult_override = TRUE, create_smoke = TRUE)
spawn_dust()
gib()
/mob/living/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
if(!used_item)
used_item = get_active_hand()
if(!visual_effect_icon && used_item?.attack_effect_override)
visual_effect_icon = used_item.attack_effect_override
..()
floating = FALSE // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement.
/mob/living/proc/do_jitter_animation(jitteriness, loop_amount = 6)
var/amplitude = min(4, (jitteriness / 100) + 1)
var/pixel_x_diff = rand(-amplitude, amplitude)
var/pixel_y_diff = rand(-amplitude / 3, amplitude / 3)
animate(src, pixel_x = pixel_x_diff, pixel_y = pixel_y_diff , time = 2, loop = loop_amount, flags = ANIMATION_RELATIVE|ANIMATION_PARALLEL)
animate(pixel_x = -pixel_x_diff , pixel_y = -pixel_y_diff , time = 2, flags = ANIMATION_RELATIVE)
floating = FALSE // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement.
/mob/living/proc/get_temperature(datum/gas_mixture/environment)
var/loc_temp = T0C
if(ismecha(loc))
var/obj/mecha/M = loc
var/datum/gas_mixture/cabin = M.return_obj_air()
if(cabin)
loc_temp = cabin.temperature()
else
loc_temp = environment.temperature()
else if(istype(loc, /obj/structure/transit_tube_pod))
loc_temp = environment.temperature()
else if(isspaceturf(get_turf(src)))
var/turf/heat_turf = get_turf(src)
loc_temp = heat_turf.temperature
else if(istype(loc, /obj/machinery/atmospherics/unary/cryo_cell))
var/obj/machinery/atmospherics/unary/cryo_cell/C = loc
if(C.air_contents.total_moles() < 10)
loc_temp = environment.temperature()
else
loc_temp = C.air_contents.temperature()
else
loc_temp = environment.temperature()
return loc_temp
/mob/living/proc/get_standard_pixel_x_offset()
return initial(pixel_x)
/mob/living/proc/get_standard_pixel_y_offset()
return initial(pixel_y)
/mob/living/proc/spawn_dust()
new /obj/effect/decal/cleanable/ash(loc)
//used in datum/reagents/reaction() proc
/mob/living/proc/get_permeability_protection()
return 0
/mob/living/proc/attempt_harvest(obj/item/I, mob/user)
if(user.a_intent == INTENT_HARM && stat == DEAD && butcher_results && I.sharp) //can we butcher it?
to_chat(user, "<span class='notice'>You begin to butcher [src]...</span>")
playsound(loc, 'sound/weapons/slice.ogg', 50, TRUE, -1)
if(do_mob(user, src, 8 SECONDS) && Adjacent(I))
harvest(user)
return TRUE
/mob/living/proc/harvest(mob/living/user)
if(QDELETED(src))
return
if(butcher_results)
for(var/path in butcher_results)
for(var/i = 1, i <= butcher_results[path], i++)
new path(loc)
butcher_results.Remove(path) //In case you want to have things like simple_animals drop their butcher results on gib, so it won't double up below.
visible_message("<span class='notice'>[user] butchers [src].</span>")
gib()
/mob/living/proc/can_use(atom/movable/M, be_close = FALSE)
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
to_chat(src, "<span class='warning'>You can't do that right now!</span>")
return FALSE
if(be_close && !in_range(M, src))
to_chat(src, "<span class='warning'>You are too far away!</span>")
return FALSE
return TRUE
/mob/living/movement_delay(ignorewalk = 0)
. = ..()
if(isturf(loc))
var/turf/T = loc
. += T.slowdown
var/datum/status_effect/incapacitating/slowed/S = IsSlowed()
if(S)
. += S.slowdown_value
if(forced_look)
. += DIRECTION_LOCK_SLOWDOWN
if(ignorewalk)
. += GLOB.configuration.movement.base_run_speed
else
switch(m_intent)
if(MOVE_INTENT_RUN)
if(get_drowsiness() > 0)
. += 6
. += GLOB.configuration.movement.base_run_speed
if(MOVE_INTENT_WALK)
. += GLOB.configuration.movement.base_walk_speed
/mob/living/proc/can_use_guns(obj/item/gun/G)
if(G.trigger_guard != TRIGGER_GUARD_ALLOW_ALL && !IsAdvancedToolUser() && !issmall(src))
to_chat(src, "<span class='warning'>You don't have the dexterity to do this!</span>")
return FALSE
if(G.trigger_guard == TRIGGER_GUARD_NONE)
to_chat(src, "<span class='warning'>This gun is only built to be fired by machines!</span>")
return FALSE
return 1
/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
if(!AM || !src)
return FALSE
if(!(AM.can_be_pulled(src, state, force, show_message)))
return FALSE
if(incapacitated())
return
if(SEND_SIGNAL(src, COMSIG_LIVING_TRY_PULL, AM, force) & COMSIG_LIVING_CANCEL_PULL)
return FALSE
// If we're pulling something then drop what we're currently pulling and pull this instead.
AM.add_fingerprint(src)
if(pulling)
if(AM == pulling)// Are we trying to pull something we are already pulling? Then just stop here, no need to continue.
return
stop_pulling()
if(AM.pulledby)
visible_message("<span class='danger'>[src] has pulled [AM] from [AM.pulledby]'s grip.</span>")
AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once.
pulling = AM
AM.pulledby = src
if(pullin)
pullin.update_icon(UPDATE_ICON_STATE)
if(ismob(AM))
var/mob/M = AM
if(!iscarbon(src))
M.LAssailant = null
else
M.LAssailant = usr
/mob/living/proc/check_pull()
if(pulling && !pulling.Adjacent(src))
stop_pulling()
/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)
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/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(blocked == INFINITY) // Full protection, go no further.
return
if(amount > RAD_BURN_THRESHOLD)
apply_damage(RAD_BURN_CURVE(amount), BURN, null, blocked)
apply_effect((amount * RAD_MOB_COEFFICIENT) / max(1, (radiation ** 2) * RAD_OVERDOSE_REDUCTION), IRRADIATE, ARMOUR_VALUE_TO_PERCENTAGE(blocked))
/mob/living/proc/fakefireextinguish()
return
/mob/living/proc/fakefire()
return
/mob/living/extinguish_light(force = FALSE)
for(var/atom/A in src)
if(A.light_range > 0)
A.extinguish_light(force)
/mob/living/vv_edit_var(var_name, var_value)
switch(var_name)
if("stat")
if((stat == DEAD) && (var_value < DEAD))//Bringing the dead back to life
GLOB.dead_mob_list -= src
GLOB.alive_mob_list += src
if((stat != DEAD) && (var_value == DEAD))//Kill he
GLOB.alive_mob_list -= src
GLOB.dead_mob_list += src
. = ..()
switch(var_name)
if("resize")
update_transform()
if("lighting_alpha")
sync_lighting_plane_alpha()
if("advanced_bullet_dodge_chance")
UnregisterSignal(src, COMSIG_ATOM_PREHIT)
RegisterSignal(src, COMSIG_ATOM_PREHIT, PROC_REF(advanced_bullet_dodge))
if("maxHealth")
updatehealth("var edit")
if("resize")
update_transform()
/mob/living/throw_at(atom/target, range, speed, mob/thrower, spin, diagonals_first, datum/callback/callback, force, dodgeable, block_movement)
stop_pulling()
return ..()
/mob/living/hit_by_thrown_mob(mob/living/C, datum/thrownthing/throwingdatum, damage, mob_hurt, self_hurt)
if(C == src || HAS_TRAIT(src, TRAIT_FLYING) || !density)
return
playsound(src, 'sound/weapons/punch1.ogg', 50, 1)
if(mob_hurt)
return
if(!self_hurt)
take_organ_damage(damage)
if(issilicon(C))
C.adjustBruteLoss(damage)
C.Weaken(3 SECONDS)
else
C.take_organ_damage(damage)
C.KnockDown(3 SECONDS)
C.visible_message("<span class='danger'>[C] crashes into [src], knocking them both over!</span>", "<span class='userdanger'>You violently crash into [src]!</span>")
/**
* Sets the mob's direction lock towards a given atom.
*
* Arguments:
* * a - The atom to face towards.
* * track - If TRUE, updates our direction relative to the atom when moving.
*/
/mob/living/proc/set_forced_look(atom/A, track = FALSE)
forced_look = track ? A.UID() : get_cardinal_dir(src, A)
to_chat(src, "<span class='userdanger'>You are now facing [track ? A : dir2text(forced_look)]. To cancel this, shift-middleclick yourself.</span>")
throw_alert("direction_lock", /atom/movable/screen/alert/direction_lock)
/**
* Clears the mob's direction lock if enabled.
*
* Arguments:
* * quiet - Whether to display a chat message.
*/
/mob/living/proc/clear_forced_look(quiet = FALSE)
if(!forced_look)
return
forced_look = null
if(!quiet)
to_chat(src, "<span class='notice'>Cancelled direction lock.</span>")
clear_alert("direction_lock")
/mob/living/setDir(new_dir)
if(forced_look)
if(isnum(forced_look))
dir = forced_look
else
var/atom/A = locateUID(forced_look)
if(istype(A))
dir = get_cardinal_dir(src, A)
return
return ..()
/mob/living/Moved(OldLoc, Dir, Forced = FALSE)
. = ..()
for(var/obj/O in src)
O.on_mob_move(Dir, src)
/mob/living/Crossed(atom/movable/mover)
if(istype(mover, /obj/singularity/energy_ball))
dust()
return ..()
/// Can a mob interact with the apc remotely like a pulse demon, cyborg, or AI?
/mob/living/proc/can_remote_apc_interface(obj/machinery/power/apc/ourapc)
return FALSE