mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-27 05:57:24 +01:00
* 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>
1339 lines
43 KiB
Plaintext
1339 lines
43 KiB
Plaintext
/mob/living/carbon/Initialize(mapload)
|
|
. = ..()
|
|
GLOB.carbon_list += src
|
|
|
|
/mob/living/carbon/Destroy()
|
|
// This clause is here due to items falling off from limb deletion
|
|
for(var/obj/item in get_all_slots())
|
|
if(QDELETED(item))
|
|
continue
|
|
unEquip(item, silent = TRUE)
|
|
qdel(item)
|
|
QDEL_LIST_CONTENTS(internal_organs)
|
|
QDEL_LIST_CONTENTS(stomach_contents)
|
|
QDEL_LIST_CONTENTS(processing_patches)
|
|
GLOB.carbon_list -= src
|
|
if(in_throw_mode)
|
|
toggle_throw_mode()
|
|
return ..()
|
|
|
|
/mob/living/carbon/ghostize(can_reenter_corpse)
|
|
if(in_throw_mode)
|
|
toggle_throw_mode()
|
|
return ..()
|
|
|
|
/mob/living/carbon/handle_atom_del(atom/A)
|
|
LAZYREMOVE(processing_patches, A)
|
|
return ..()
|
|
|
|
/mob/living/carbon/blob_act(obj/structure/blob/B)
|
|
if(stat == DEAD)
|
|
return
|
|
else
|
|
show_message("<span class='userdanger'>The blob attacks!</span>")
|
|
adjustBruteLoss(10)
|
|
|
|
/mob/living/carbon/Move(NewLoc, direct)
|
|
. = ..()
|
|
if(.)
|
|
if(nutrition && stat != DEAD)
|
|
adjust_nutrition(-(hunger_drain * 0.1))
|
|
if(m_intent == MOVE_INTENT_RUN)
|
|
adjust_nutrition(-(hunger_drain * 0.1))
|
|
if(HAS_TRAIT(src, TRAIT_FAT) && m_intent == MOVE_INTENT_RUN && bodytemperature <= 360)
|
|
bodytemperature += 2
|
|
|
|
// Moving around increases germ_level faster
|
|
if(germ_level < GERM_LEVEL_MOVE_CAP && prob(8))
|
|
germ_level++
|
|
|
|
#define STOMACH_ATTACK_DELAY 4
|
|
|
|
/mob/living/carbon/relaymove(mob/user, direction)
|
|
if(LAZYLEN(stomach_contents))
|
|
if(user in stomach_contents)
|
|
if(last_stomach_attack + STOMACH_ATTACK_DELAY > world.time)
|
|
return
|
|
|
|
last_stomach_attack = world.time
|
|
for(var/mob/M in hearers(4, src))
|
|
if(M.client)
|
|
M.show_message(text("<span class='warning'>You hear something rumbling inside [src]'s stomach...</span>"), 2)
|
|
|
|
var/obj/item/I = user.get_active_hand()
|
|
if(I && I.force)
|
|
var/d = rand(round(I.force / 4), I.force)
|
|
|
|
if(ishuman(src))
|
|
var/mob/living/carbon/human/H = src
|
|
var/obj/item/organ/external/organ = H.get_organ("chest")
|
|
if(istype(organ))
|
|
if(organ.receive_damage(d, 0))
|
|
H.UpdateDamageIcon()
|
|
|
|
H.updatehealth("stomach attack")
|
|
|
|
else
|
|
take_organ_damage(d)
|
|
|
|
for(var/mob/M in viewers(user, null))
|
|
if(M.client)
|
|
M.show_message(text("<span class='warning'><B>[user] attacks [src]'s stomach wall with [I]!</span>"), 2)
|
|
playsound(user.loc, 'sound/effects/attackblob.ogg', 50, 1)
|
|
|
|
if(prob(getBruteLoss() - 50))
|
|
gib()
|
|
|
|
#undef STOMACH_ATTACK_DELAY
|
|
|
|
/mob/living/carbon/proc/has_mutated_organs()
|
|
return FALSE
|
|
|
|
|
|
/mob/living/carbon/proc/vomit(lost_nutrition = 10, blood = 0, should_confuse = TRUE, distance = 0, message = 1)
|
|
. = TRUE
|
|
|
|
if(stat == DEAD || ismachineperson(src)) // Dead people and IPCs do not vomit particulates
|
|
return FALSE
|
|
|
|
if(should_confuse)
|
|
if(blood)
|
|
KnockDown(10 SECONDS)
|
|
AdjustConfused(8 SECONDS)
|
|
Slowed(8 SECONDS, 1)
|
|
|
|
if(!blood && nutrition < 100) // Nutrition vomiting while already starving
|
|
if(message)
|
|
visible_message("<span class='warning'>[src] dry heaves!</span>", \
|
|
"<span class='userdanger'>You try to throw up, but there's nothing in your stomach!</span>")
|
|
if(should_confuse)
|
|
KnockDown(20 SECONDS)
|
|
AdjustConfused(20 SECONDS)
|
|
return
|
|
|
|
if(message)
|
|
visible_message("<span class='danger'>[src] throws up!</span>", \
|
|
"<span class='userdanger'>You throw up!</span>")
|
|
|
|
playsound(get_turf(src), 'sound/effects/splat.ogg', 50, 1)
|
|
var/turf/T = get_turf(src)
|
|
for(var/i = 0 to distance)
|
|
if(blood)
|
|
if(T)
|
|
add_splatter_floor(T)
|
|
if(should_confuse)
|
|
adjustBruteLoss(3)
|
|
else
|
|
if(T)
|
|
T.add_vomit_floor()
|
|
adjust_nutrition(-lost_nutrition)
|
|
if(should_confuse)
|
|
adjustToxLoss(-3)
|
|
|
|
T = get_step(T, dir)
|
|
if(is_blocked_turf(T))
|
|
break
|
|
|
|
/mob/living/carbon/gib()
|
|
. = death(1)
|
|
if(!.)
|
|
return
|
|
for(var/obj/item/organ/internal/I in internal_organs)
|
|
if(isturf(loc))
|
|
I.remove(src)
|
|
I.forceMove(get_turf(src))
|
|
I.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
|
|
|
|
for(var/mob/M in src)
|
|
LAZYREMOVE(stomach_contents, M)
|
|
M.forceMove(drop_location())
|
|
visible_message("<span class='danger'>[M] bursts out of [src]!</span>")
|
|
|
|
///Adds to the parent by also adding functionality to propagate shocks through pulling and doing some fluff effects.
|
|
/mob/living/carbon/electrocute_act(shock_damage, source, siemens_coeff = 1, flags = NONE)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
//Propagation through pulling
|
|
if(!(flags & SHOCK_ILLUSION))
|
|
var/list/shocking_queue = list()
|
|
if(iscarbon(pulling) && source != pulling)
|
|
shocking_queue += pulling
|
|
if(iscarbon(pulledby) && source != pulledby)
|
|
shocking_queue += pulledby
|
|
if(iscarbon(buckled) && source != buckled)
|
|
shocking_queue += buckled
|
|
for(var/mob/living/carbon/carried in buckled_mobs)
|
|
if(source != carried)
|
|
shocking_queue += carried
|
|
//Found our victims, now lets shock them all
|
|
for(var/victim in shocking_queue)
|
|
var/mob/living/carbon/C = victim
|
|
C.electrocute_act(shock_damage * 0.75, src, 1, flags)
|
|
//Stun
|
|
var/should_stun = (!(flags & SHOCK_TESLA) || siemens_coeff > 0.5) && !(flags & SHOCK_NOSTUN)
|
|
if(should_stun)
|
|
Stun(1 SECONDS)
|
|
//Jitter and other fluff.
|
|
AdjustJitter(2000 SECONDS)
|
|
AdjustStuttering(4 SECONDS)
|
|
addtimer(CALLBACK(src, PROC_REF(secondary_shock), should_stun), 1 SECONDS)
|
|
return shock_damage
|
|
|
|
///Called slightly after electrocute act to reduce jittering and apply a secondary knockdown.
|
|
/mob/living/carbon/proc/secondary_shock(should_stun)
|
|
AdjustJitter(-2000 SECONDS, bound_lower = 20 SECONDS) //Still jittery, but vastly less
|
|
if(should_stun)
|
|
KnockDown(6 SECONDS)
|
|
|
|
/mob/living/carbon/swap_hand()
|
|
if(SEND_SIGNAL(src, COMSIG_MOB_SWAPPING_HANDS, get_active_hand()) == COMPONENT_BLOCK_SWAP)
|
|
return
|
|
hand = !hand
|
|
update_hands_hud()
|
|
SEND_SIGNAL(src, COMSIG_CARBON_SWAP_HANDS)
|
|
|
|
|
|
/mob/living/carbon/activate_hand(selhand)
|
|
if(selhand != hand)
|
|
swap_hand()
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/mob/living/carbon/proc/help_shake_act(mob/living/carbon/M)
|
|
if(health < HEALTH_THRESHOLD_CRIT)
|
|
return
|
|
if(src == M && ishuman(src))
|
|
check_self_for_injuries()
|
|
return
|
|
if(player_logged)
|
|
M.visible_message("<span class='notice'>[M] shakes [src], but [p_they()] [p_do()] not respond. Probably suffering from SSD.</span>", \
|
|
"<span class='notice'>You shake [src], but [p_theyre()] unresponsive. Probably suffering from SSD.</span>")
|
|
if(IS_HORIZONTAL(src)) // /vg/: For hugs. This is how update_icon figgers it out, anyway. - N3X15
|
|
add_attack_logs(M, src, "Shaked", ATKLOG_ALL)
|
|
if(ishuman(src))
|
|
var/mob/living/carbon/human/H = src
|
|
if(H.w_uniform)
|
|
H.w_uniform.add_fingerprint(M)
|
|
AdjustSleeping(-10 SECONDS)
|
|
AdjustParalysis(-6 SECONDS)
|
|
AdjustStunned(-6 SECONDS)
|
|
AdjustWeakened(-6 SECONDS)
|
|
AdjustKnockDown(-6 SECONDS)
|
|
adjustStaminaLoss(-10)
|
|
resting = FALSE
|
|
stand_up() // help them up if possible
|
|
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
|
|
if(!player_logged)
|
|
M.visible_message( \
|
|
"<span class='notice'>[M] shakes [src] trying to wake [p_them()] up!</span>",\
|
|
"<span class='notice'>You shake [src] trying to wake [p_them()] up!</span>",\
|
|
)
|
|
return
|
|
// If it has any of the highfive statuses, dap, handshake, etc
|
|
var/datum/status_effect/effect = has_status_effect_type(STATUS_EFFECT_HIGHFIVE)
|
|
if(effect)
|
|
M.apply_status_effect(effect.type)
|
|
return
|
|
// BEGIN HUGCODE - N3X
|
|
playsound(get_turf(src), 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
|
|
if(M.zone_selected == "head")
|
|
M.visible_message(\
|
|
"<span class='notice'>[M] pats [src] on the head.</span>",\
|
|
"<span class='notice'>You pat [src] on the head.</span>",\
|
|
)
|
|
return
|
|
M.visible_message(\
|
|
"<span class='notice'>[M] gives [src] a [pick("hug","warm embrace")].</span>",\
|
|
"<span class='notice'>You hug [src].</span>",\
|
|
)
|
|
if(ishuman(src))
|
|
var/mob/living/carbon/human/H = src
|
|
if(H.wear_suit)
|
|
H.wear_suit.add_fingerprint(M)
|
|
else if(H.w_uniform)
|
|
H.w_uniform.add_fingerprint(M)
|
|
|
|
/**
|
|
* Handles patting out a fire on someone.
|
|
*
|
|
* Removes 0.5 fire stacks per pat, with a 30% chance of the user burning their hand if they don't have adequate heat resistance.
|
|
* Arguments:
|
|
* * src - The mob doing the patting
|
|
* * target - The mob who is currently on fire
|
|
*/
|
|
/mob/living/carbon/proc/pat_out(mob/living/target)
|
|
if(target == src) // stop drop and roll, no trying to put out fire on yourself for free.
|
|
to_chat(src, "<span class='warning'>Stop drop and roll!</span>")
|
|
return
|
|
var/self_message = "<span class='warning'>You try to extinguish [target]!</span>"
|
|
if(prob(30) && ishuman(src)) // 30% chance of burning your hands
|
|
var/mob/living/carbon/human/H = src
|
|
var/protected = FALSE // Protected from the fire
|
|
if((H.gloves?.max_heat_protection_temperature > 360) || HAS_TRAIT(H, TRAIT_RESISTHEAT) || HAS_TRAIT(H, TRAIT_RESISTHEATHANDS))
|
|
protected = TRUE
|
|
|
|
var/obj/item/organ/external/active_hand = H.get_active_hand()
|
|
if(active_hand && !protected) // Wouldn't really work without a hand
|
|
active_hand.receive_damage(0, 5)
|
|
self_message = "<span class='danger'>You burn your hand trying to extinguish [target]!</span>"
|
|
H.update_icons()
|
|
|
|
target.visible_message("<span class='warning'>[src] tries to extinguish [target]!</span>", self_message)
|
|
playsound(target, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
|
|
target.adjust_fire_stacks(-0.5)
|
|
|
|
/mob/living/carbon/proc/check_self_for_injuries()
|
|
var/mob/living/carbon/human/H = src
|
|
visible_message("<span class='notice'>[src] examines [H.p_themselves()].</span>", \
|
|
"<span class='notice'>You check yourself for injuries.</span>" \
|
|
)
|
|
var/list/status_list = list()
|
|
|
|
var/list/missing = list("head", "chest", "groin", "l_arm", "r_arm", "l_hand", "r_hand", "l_leg", "r_leg", "l_foot", "r_foot")
|
|
for(var/X in H.bodyparts)
|
|
var/obj/item/organ/external/LB = X
|
|
missing -= LB.limb_name
|
|
var/status
|
|
var/brutedamage = LB.brute_dam
|
|
var/burndamage = LB.burn_dam
|
|
|
|
switch(brutedamage)
|
|
if(0.1 to 20)
|
|
status = "bruised"
|
|
if(20 to 40)
|
|
status = "battered"
|
|
if(40 to INFINITY)
|
|
status = "mangled"
|
|
if(brutedamage > 0 && (burndamage > 0 || LB.status & ORGAN_BURNT))
|
|
status += " and "
|
|
|
|
if(LB.status & ORGAN_BURNT)
|
|
status += "critically burnt" + (LB.status & ORGAN_SALVED ? ", but salved" : "")
|
|
else
|
|
switch(burndamage)
|
|
if(0.1 to 10)
|
|
status += "numb"
|
|
if(10 to 40)
|
|
status += "blistered"
|
|
if(40 to INFINITY)
|
|
status += "peeling away"
|
|
|
|
if(LB.status & ORGAN_MUTATED)
|
|
status = "weirdly shapen"
|
|
|
|
var/msg = "<span class='notice'>Your [LB.name] is OK.</span>"
|
|
if(!isnull(status))
|
|
msg = "<span class='warning'>Your [LB.name] is [status].</span>"
|
|
status_list += msg
|
|
|
|
for(var/obj/item/I in LB.embedded_objects)
|
|
status_list += "\t<a href='byond://?src=[UID()];embedded_object=[I.UID()];embedded_limb=[LB.UID()]' class='warning'>There is \a [I] embedded in your [LB.name]!</a>"
|
|
|
|
for(var/t in missing)
|
|
status_list += "<span class='boldannounceic'>Your [parse_zone(t)] is missing!</span>"
|
|
|
|
if(H.bleed_rate)
|
|
status_list += "<span class='danger'>You are bleeding!</span>"
|
|
if(staminaloss)
|
|
if(staminaloss > 30)
|
|
status_list += "<span class='notice'>You're completely exhausted.</span>"
|
|
else
|
|
status_list += "<span class='notice'>You feel fatigued.</span>"
|
|
|
|
to_chat(src, chat_box_examine(status_list.Join("\n")))
|
|
|
|
if(HAS_TRAIT(H, TRAIT_SKELETONIZED) && (!H.w_uniform) && (!H.wear_suit))
|
|
H.play_xylophone()
|
|
|
|
/mob/living/carbon/can_be_flashed(intensity = 1, override_blindness_check = 0)
|
|
var/obj/item/organ/internal/eyes/E = get_int_organ(/obj/item/organ/internal/eyes)
|
|
. = ..()
|
|
|
|
if((E?.status & ORGAN_DEAD) || E?.is_broken() || !.)
|
|
return FALSE
|
|
|
|
/mob/living/carbon/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, laser_pointer = FALSE, type = /atom/movable/screen/fullscreen/stretch/flash)
|
|
//Parent proc checks if a mob can_be_flashed()
|
|
. = ..()
|
|
|
|
SIGNAL_HANDLER
|
|
SEND_SIGNAL(src, COMSIG_CARBON_FLASH_EYES, laser_pointer)
|
|
var/damage = intensity - check_eye_prot()
|
|
var/extra_damage = 0
|
|
if(.)
|
|
if(visual)
|
|
return
|
|
|
|
//Checks that shouldn't stop flashing, but should stop eye damage, so they go here instead of in can_be_flashed()
|
|
var/obj/item/organ/internal/eyes/E = get_int_organ(/obj/item/organ/internal/eyes)
|
|
if(!E || E.weld_proof)
|
|
return
|
|
|
|
var/extra_darkview = 0
|
|
if(E.see_in_dark)
|
|
extra_darkview = max(E.see_in_dark - 2, 0)
|
|
extra_damage = extra_darkview
|
|
|
|
var/light_amount = 10 // assume full brightness
|
|
if(isturf(loc))
|
|
var/turf/T = loc
|
|
light_amount = round(T.get_lumcount() * 10)
|
|
|
|
// a dark view of 8, in full darkness, will result in maximum 1st tier damage
|
|
var/extra_prob = (10 - light_amount) * extra_darkview
|
|
|
|
switch(damage)
|
|
if(1)
|
|
to_chat(src, "<span class='warning'>Your eyes sting a little.</span>")
|
|
var/minor_damage_multiplier = min(40 + extra_prob, 100) / 100
|
|
var/minor_damage = minor_damage_multiplier * (1 + extra_damage)
|
|
E.receive_damage(minor_damage, 1)
|
|
if(2)
|
|
to_chat(src, "<span class='warning'>Your eyes burn.</span>")
|
|
E.receive_damage(rand(2, 4) + extra_damage, 1)
|
|
|
|
else
|
|
to_chat(src, "Your eyes itch and burn severely!</span>")
|
|
E.receive_damage(rand(12, 16) + extra_damage, 1)
|
|
|
|
if(E.damage > E.min_bruised_damage)
|
|
AdjustEyeBlind(damage STATUS_EFFECT_CONSTANT)
|
|
AdjustEyeBlurry(damage * rand(6 SECONDS, 12 SECONDS))
|
|
|
|
if(E.damage > (E.min_bruised_damage + E.min_broken_damage) / 2)
|
|
if(!E.is_robotic())
|
|
to_chat(src, "<span class='warning'>Your eyes start to burn badly!</span>")
|
|
else //snowflake conditions piss me off for the record
|
|
to_chat(src, "<span class='warning'>The flash blinds you!</span>")
|
|
|
|
else if(E.damage >= E.min_broken_damage)
|
|
to_chat(src, "<span class='warning'>You can't see anything!</span>")
|
|
|
|
else
|
|
to_chat(src, "<span class='warning'>Your eyes are really starting to hurt. This can't be good for you!</span>")
|
|
return 1
|
|
|
|
else if(damage == 0) // just enough protection
|
|
if(prob(20))
|
|
to_chat(src, "<span class='notice'>Something bright flashes in the corner of your vision!</span>")
|
|
|
|
|
|
/mob/living/carbon/proc/tintcheck()
|
|
return 0
|
|
|
|
/mob/living/carbon/proc/create_dna()
|
|
if(!dna)
|
|
dna = new()
|
|
|
|
/mob/living/carbon/proc/getDNA()
|
|
return dna
|
|
|
|
/mob/living/carbon/proc/setDNA(datum/dna/newDNA)
|
|
dna = newDNA
|
|
|
|
|
|
GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/vent_pump, /obj/machinery/atmospherics/unary/vent_scrubber))
|
|
|
|
/mob/living/handle_ventcrawl(atom/clicked_on) // Why is this proc even in carbon.dm ...
|
|
if(!Adjacent(clicked_on))
|
|
return
|
|
|
|
var/ventcrawlerlocal = VENTCRAWLER_NONE
|
|
if(ventcrawler)
|
|
ventcrawlerlocal = ventcrawler
|
|
|
|
if(ventcrawlerlocal == VENTCRAWLER_NONE) // You can't ventcrawl.
|
|
return
|
|
|
|
if(stat)
|
|
to_chat(src, "You must be conscious to do this!")
|
|
return
|
|
|
|
if(!(mobility_flags & MOBILITY_MOVE))
|
|
to_chat(src, "You can't vent crawl while you cannot move!")
|
|
return
|
|
|
|
if(has_buckled_mobs())
|
|
to_chat(src, "<span class='warning'>You can't vent crawl with other creatures on you!</span>")
|
|
return
|
|
|
|
if(buckled)
|
|
to_chat(src, "<span class='warning'>You can't vent crawl while buckled!</span>")
|
|
return
|
|
|
|
if(ishuman(src))
|
|
var/mob/living/carbon/human/H = src
|
|
if(H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under/rank/engineering/atmospheric_technician/contortionist))//IMMA SPCHUL SNOWFLAKE
|
|
var/obj/item/clothing/under/rank/engineering/atmospheric_technician/contortionist/C = H.w_uniform
|
|
if(!C.check_clothing(src))//return values confuse me right now
|
|
return
|
|
|
|
var/obj/machinery/atmospherics/unary/vent_found
|
|
|
|
if(clicked_on)
|
|
vent_found = clicked_on
|
|
if(!istype(vent_found) || !vent_found.can_crawl_through())
|
|
vent_found = null
|
|
|
|
|
|
if(!vent_found)
|
|
for(var/obj/machinery/atmospherics/machine in range(1, src))
|
|
if(is_type_in_list(machine, GLOB.ventcrawl_machinery) && machine.can_crawl_through())
|
|
vent_found = machine
|
|
break
|
|
|
|
if(!vent_found)
|
|
to_chat(src, "<span class='warning'>This ventilation duct is not connected to anything!</span>")
|
|
return
|
|
|
|
if(!vent_found.parent || !(length(vent_found.parent.members) || vent_found.parent.other_atmosmch))
|
|
return
|
|
|
|
visible_message("<span class='notice'>[src] begins climbing into the ventilation system...</span>", \
|
|
"<span class='notice'>You begin climbing into the ventilation system...</span>")
|
|
|
|
#ifdef UNIT_TESTS
|
|
var/ventcrawl_delay = 0 SECONDS
|
|
#else
|
|
var/ventcrawl_delay = 4.5 SECONDS
|
|
if(!client)
|
|
return
|
|
#endif
|
|
if(!do_after(src, ventcrawl_delay, target = src))
|
|
return
|
|
|
|
if(!vent_found.can_crawl_through())
|
|
to_chat(src, "<span class='warning'>You can't vent crawl through that!</span>")
|
|
return
|
|
|
|
if(has_buckled_mobs())
|
|
to_chat(src, "<span class='warning'>You can't vent crawl with other creatures on you!</span>")
|
|
return
|
|
|
|
if(buckled)
|
|
to_chat(src, "<span class='warning'>You cannot crawl into a vent while buckled to something!</span>")
|
|
return
|
|
|
|
if(iscarbon(src) && length(contents) && ventcrawlerlocal < VENTCRAWLER_ALWAYS) // If we're here you can only ventcrawl while completely nude
|
|
for(var/obj/item/I in contents)
|
|
if(istype(I, /obj/item/bio_chip))
|
|
continue
|
|
if(istype(I, /obj/item/reagent_containers/patch))
|
|
continue
|
|
if(I.flags & ABSTRACT)
|
|
continue
|
|
|
|
to_chat(src, "<span class='warning'>You can't crawl around in the ventilation ducts with items!</span>")
|
|
return
|
|
|
|
visible_message("<b>[src] scrambles into the ventilation ducts!</b>", "You climb into the ventilation system.")
|
|
forceMove(vent_found)
|
|
add_ventcrawl(vent_found)
|
|
|
|
/mob/living/proc/add_ventcrawl(obj/machinery/atmospherics/starting_machine, obj/machinery/atmospherics/target_move)
|
|
if(!istype(starting_machine) || !starting_machine.returnPipenet(target_move) || !starting_machine.can_see_pipes())
|
|
return
|
|
var/datum/pipeline/pipeline = starting_machine.returnPipenet(target_move)
|
|
var/list/totalMembers = list()
|
|
totalMembers |= pipeline.members
|
|
totalMembers |= pipeline.other_atmosmch
|
|
for(var/obj/machinery/atmospherics/A in totalMembers)
|
|
if(!A.pipe_image)
|
|
A.update_pipe_image()
|
|
pipes_shown += A.pipe_image
|
|
if(client)
|
|
client.images += A.pipe_image
|
|
|
|
/mob/living/proc/remove_ventcrawl()
|
|
if(client)
|
|
for(var/image/current_image in pipes_shown)
|
|
client.images -= current_image
|
|
client.eye = src
|
|
|
|
pipes_shown.len = 0
|
|
|
|
//OOP
|
|
/atom/proc/update_pipe_vision()
|
|
return
|
|
|
|
/mob/living/update_pipe_vision(obj/machinery/atmospherics/target_move)
|
|
if(!client)
|
|
pipes_shown.Cut()
|
|
return
|
|
if(length(pipes_shown) && !target_move)
|
|
if(!is_ventcrawling(src))
|
|
remove_ventcrawl()
|
|
else
|
|
if(is_ventcrawling(src))
|
|
if(target_move)
|
|
remove_ventcrawl()
|
|
add_ventcrawl(loc, target_move)
|
|
|
|
|
|
//Throwing stuff
|
|
|
|
/mob/living/carbon/throw_impact(atom/hit_atom, throwingdatum, speed = 1)
|
|
. = ..()
|
|
if(has_status_effect(STATUS_EFFECT_CHARGING))
|
|
var/hit_something = FALSE
|
|
if(ismovable(hit_atom))
|
|
var/atom/movable/AM = hit_atom
|
|
var/atom/throw_target = get_edge_target_turf(AM, dir)
|
|
if(!AM.anchored || ismecha(AM))
|
|
AM.throw_at(throw_target, 5, 12, src)
|
|
hit_something = TRUE
|
|
if(isobj(hit_atom))
|
|
var/obj/O = hit_atom
|
|
O.take_damage(150, BRUTE)
|
|
hit_something = TRUE
|
|
if(isliving(hit_atom))
|
|
var/mob/living/L = hit_atom
|
|
L.adjustBruteLoss(60)
|
|
L.KnockDown(12 SECONDS)
|
|
L.Confused(10 SECONDS)
|
|
shake_camera(L, 4, 3)
|
|
hit_something = TRUE
|
|
if(isturf(hit_atom))
|
|
var/turf/T = hit_atom
|
|
if(iswallturf(T))
|
|
T.dismantle_wall(TRUE)
|
|
hit_something = TRUE
|
|
if(hit_something)
|
|
visible_message("<span class='danger'>[src] slams into [hit_atom]!</span>", "<span class='userdanger'>You slam into [hit_atom]!</span>")
|
|
playsound(get_turf(src), 'sound/effects/meteorimpact.ogg', 100, TRUE)
|
|
return
|
|
if(has_status_effect(STATUS_EFFECT_IMPACT_IMMUNE))
|
|
return
|
|
|
|
var/damage = 10 + 1.5 * speed // speed while thrower is standing still is 2, while walking with an aggressive grab is 2.4, highest speed is 14
|
|
|
|
hit_atom.hit_by_thrown_mob(src, throwingdatum, damage, FALSE, FALSE)
|
|
|
|
/mob/living/carbon/hit_by_thrown_mob(mob/living/C, datum/thrownthing/throwingdatum, damage, mob_hurt, self_hurt)
|
|
for(var/obj/item/dualsaber/D in contents)
|
|
if(HAS_TRAIT(D, TRAIT_WIELDED) && D.force)
|
|
visible_message("<span class='danger'>[src] impales [C] with [D], before dropping them on the ground!</span>")
|
|
C.apply_damage(100, BRUTE, "chest", sharp = TRUE, used_weapon = "Impaled on [D].")
|
|
C.Stun(2 SECONDS) //Punishment. This could also be used by a traitor to throw someone into a dsword to kill them, but hey, teamwork!
|
|
C.KnockDown(6 SECONDS)
|
|
D.melee_attack_chain(src, C) //attack animation / jedi spin
|
|
C.emote("scream")
|
|
return
|
|
. = ..()
|
|
KnockDown(3 SECONDS)
|
|
|
|
/mob/living/carbon/proc/toggle_throw_mode()
|
|
if(in_throw_mode)
|
|
throw_mode_off()
|
|
else
|
|
throw_mode_on()
|
|
var/obj/item/I = get_active_hand()
|
|
if(I)
|
|
SEND_SIGNAL(I, COMSIG_CARBON_TOGGLE_THROW, in_throw_mode)
|
|
|
|
#define THROW_MODE_ICON 'icons/effects/cult_target.dmi'
|
|
|
|
/mob/living/carbon/proc/throw_mode_off()
|
|
in_throw_mode = FALSE
|
|
if(throw_icon) //in case we don't have the HUD and we use the hotkey
|
|
throw_icon.icon_state = "act_throw_off"
|
|
if(client?.mouse_pointer_icon == THROW_MODE_ICON)
|
|
client.mouse_pointer_icon = initial(client.mouse_pointer_icon)
|
|
|
|
/mob/living/carbon/proc/throw_mode_on()
|
|
SIGNAL_HANDLER //This signal is here so we can turn throw mode back on via carp when an object is caught
|
|
in_throw_mode = TRUE
|
|
if(throw_icon)
|
|
throw_icon.icon_state = "act_throw_on"
|
|
if(client?.mouse_pointer_icon == initial(client.mouse_pointer_icon))
|
|
client.mouse_pointer_icon = THROW_MODE_ICON
|
|
|
|
#undef THROW_MODE_ICON
|
|
|
|
/mob/proc/throw_item(atom/target)
|
|
return
|
|
|
|
/mob/living/carbon/throw_item(atom/target)
|
|
if(!target || !isturf(loc) || is_screen_atom(target))
|
|
throw_mode_off()
|
|
return
|
|
|
|
var/obj/item/I = get_active_hand()
|
|
|
|
if(!I || I.override_throw(src, target) || (I.flags & NODROP))
|
|
throw_mode_off()
|
|
return
|
|
|
|
throw_mode_off()
|
|
var/atom/movable/thrown_thing
|
|
|
|
if(istype(I, /obj/item/grab))
|
|
var/obj/item/grab/G = I
|
|
var/mob/throwable_mob = G.get_mob_if_throwable() //throw the person instead of the grab
|
|
qdel(G) //We delete the grab.
|
|
if(throwable_mob)
|
|
thrown_thing = throwable_mob
|
|
if(HAS_TRAIT(src, TRAIT_PACIFISM))
|
|
to_chat(src, "<span class='notice'>You gently let go of [throwable_mob].</span>")
|
|
return
|
|
var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors
|
|
var/turf/end_T = get_turf(target)
|
|
throwable_mob.forceMove(start_T)
|
|
if(start_T && end_T)
|
|
var/start_T_descriptor = "<font color='#6b5d00'>tile at [start_T.x], [start_T.y], [start_T.z] in area [get_area(start_T)]</font>"
|
|
var/end_T_descriptor = "<font color='#6b4400'>tile at [end_T.x], [end_T.y], [end_T.z] in area [get_area(end_T)]</font>"
|
|
|
|
add_attack_logs(src, throwable_mob, "Thrown from [start_T_descriptor] with the target [end_T_descriptor]")
|
|
|
|
else if(!(I.flags & ABSTRACT)) //can't throw abstract items
|
|
thrown_thing = I
|
|
unEquip(I, silent = TRUE)
|
|
|
|
if(HAS_TRAIT(src, TRAIT_PACIFISM) && I.throwforce)
|
|
to_chat(src, "<span class='notice'>You set [I] down gently on the ground.</span>")
|
|
return
|
|
|
|
if(QDELETED(thrown_thing))
|
|
return
|
|
|
|
if(!HAS_TRAIT(thrown_thing, TRAIT_NO_THROWN_MESSAGE))
|
|
visible_message("<span class='danger'>[src] has thrown [thrown_thing].</span>")
|
|
newtonian_move(get_dir(target, src))
|
|
thrown_thing.throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed, src, null, null, null, move_force)
|
|
|
|
/mob/living/carbon/can_use_hands()
|
|
if(handcuffed)
|
|
return FALSE
|
|
if(buckled && ! istype(buckled, /obj/structure/chair)) // buckling does not restrict hands
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/mob/living/carbon/restrained()
|
|
if(get_restraining_item())
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/mob/living/carbon/get_restraining_item()
|
|
return handcuffed
|
|
|
|
/mob/living/carbon/unEquip(obj/item/I, force, silent = FALSE) //THIS PROC DID NOT CALL ..()
|
|
. = ..() //Sets the default return value to what the parent returns.
|
|
if(!. || !I) //We don't want to set anything to null if the parent returned 0.
|
|
return
|
|
|
|
if(I == back)
|
|
back = null
|
|
update_inv_back()
|
|
else if(I == wear_mask)
|
|
if(ishuman(src)) //If we don't do this hair won't be properly rebuilt.
|
|
return
|
|
wear_mask = null
|
|
update_inv_wear_mask()
|
|
else if(I == handcuffed)
|
|
handcuffed = null
|
|
if(buckled && buckled.buckle_requires_restraints)
|
|
unbuckle()
|
|
update_handcuffed()
|
|
else if(I == legcuffed)
|
|
legcuffed = null
|
|
toggle_move_intent()
|
|
update_inv_legcuffed()
|
|
|
|
/mob/living/carbon/update_inv_legcuffed()
|
|
clear_alert("legcuffed")
|
|
if(!legcuffed)
|
|
return
|
|
throw_alert("legcuffed", /atom/movable/screen/alert/restrained/legcuffed, new_master = legcuffed)
|
|
if(m_intent != MOVE_INTENT_WALK)
|
|
m_intent = MOVE_INTENT_WALK
|
|
if(hud_used?.move_intent)
|
|
hud_used.move_intent.icon_state = "walking"
|
|
|
|
/mob/living/carbon/get_item_by_slot(slot_id)
|
|
switch(slot_id)
|
|
if(SLOT_HUD_BACK)
|
|
return back
|
|
if(SLOT_HUD_WEAR_MASK)
|
|
return wear_mask
|
|
if(SLOT_HUD_OUTER_SUIT)
|
|
return wear_suit
|
|
if(SLOT_HUD_LEFT_HAND)
|
|
return l_hand
|
|
if(SLOT_HUD_RIGHT_HAND)
|
|
return r_hand
|
|
if(SLOT_HUD_HANDCUFFED)
|
|
return handcuffed
|
|
if(SLOT_HUD_LEGCUFFED)
|
|
return legcuffed
|
|
return null
|
|
|
|
//generates realistic-ish pulse output based on preset levels
|
|
/mob/living/carbon/proc/get_pulse()
|
|
var/temp = 0
|
|
switch(pulse)
|
|
if(PULSE_NONE)
|
|
return "0"
|
|
if(PULSE_SLOW)
|
|
temp = rand(40, 60)
|
|
return num2text(temp)
|
|
if(PULSE_NORM)
|
|
temp = rand(60, 90)
|
|
return num2text(temp)
|
|
if(PULSE_FAST)
|
|
temp = rand(90, 120)
|
|
return num2text(temp)
|
|
if(PULSE_2FAST)
|
|
temp = rand(120, 160)
|
|
return num2text(temp)
|
|
if(PULSE_THREADY)
|
|
return ">250"
|
|
|
|
/mob/living/carbon/proc/canBeHandcuffed()
|
|
return FALSE
|
|
|
|
/mob/living/carbon/fall()
|
|
..()
|
|
loc.handle_fall()//it's loc so it doesn't call the mob's handle_fall which does nothing
|
|
|
|
/mob/living/carbon/is_muzzled()
|
|
return(istype(wear_mask, /obj/item/clothing/mask/muzzle))
|
|
|
|
/mob/living/carbon/is_facehugged()
|
|
return istype(wear_mask, /obj/item/clothing/mask/facehugger)
|
|
|
|
/mob/living/carbon/resist_buckle()
|
|
INVOKE_ASYNC(src, PROC_REF(resist_muzzle))
|
|
var/breakout_time = 0
|
|
var/obj/item/restraints = get_restraining_item()
|
|
|
|
if(istype(restraints, /obj/item/clothing/suit/straight_jacket))
|
|
var/obj/item/clothing/suit/straight_jacket/jacket = restraints
|
|
jacket.breakouttime = breakout_time
|
|
|
|
else if(istype(restraints, /obj/item/restraints))
|
|
var/obj/item/restraints/cuffs = restraints
|
|
breakout_time = cuffs.breakouttime
|
|
|
|
else if(isstructure(buckled))
|
|
var/obj/structure/struct = buckled
|
|
breakout_time = struct.unbuckle_time
|
|
|
|
if(!breakout_time)
|
|
buckled.user_unbuckle_mob(src, src)
|
|
return
|
|
|
|
if(has_status_effect(STATUS_EFFECT_UNBUCKLE))
|
|
to_chat(src, "<span class='notice'>You are already trying to unbuckle!</span>")
|
|
return
|
|
apply_status_effect(STATUS_EFFECT_UNBUCKLE)
|
|
|
|
visible_message("<span class='warning'>[src] attempts to unbuckle [p_themselves()]!</span>",
|
|
"<span class='notice'>You attempt to unbuckle yourself... (This will take around [breakout_time / 10] seconds and you need to stay still.)</span>")
|
|
if(!do_after(src, breakout_time, FALSE, src, allow_moving = TRUE, extra_checks = list(CALLBACK(src, PROC_REF(buckle_check))), allow_moving_target = TRUE))
|
|
if(src && buckled)
|
|
to_chat(src, "<span class='warning'>You fail to unbuckle yourself!</span>")
|
|
else
|
|
if(!buckled)
|
|
return
|
|
buckled.user_unbuckle_mob(src, src)
|
|
|
|
remove_status_effect(STATUS_EFFECT_UNBUCKLE)
|
|
|
|
/mob/living/carbon/proc/buckle_check()
|
|
if(!buckled) // No longer buckled
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/mob/living/carbon/proc/muzzle_check()
|
|
if(!is_muzzled()) // No longer muzzled
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/mob/living/carbon/resist_fire()
|
|
if(IsKnockedDown())
|
|
return
|
|
fire_stacks -= 5
|
|
Weaken (2 SECONDS, TRUE) //Your busy dying from fire, no way you could be able to roll and reach for a snack in your bag
|
|
KnockDown(6 SECONDS, TRUE) //Ok now you can have that snack if you want
|
|
spin(32, 2)
|
|
visible_message("<span class='danger'>[src] rolls on the floor, trying to put [p_themselves()] out!</span>",
|
|
"<span class='notice'>You stop, drop, and roll!</span>")
|
|
addtimer(CALLBACK(src, PROC_REF(extinguish_roll), 3 SECONDS))
|
|
|
|
/mob/living/carbon/proc/extinguish_roll()
|
|
if(fire_stacks <= 0)
|
|
visible_message("<span class='danger'>[src] has successfully extinguished [p_themselves()]!</span>","<span class='notice'>You extinguish yourself.</span>")
|
|
ExtinguishMob()
|
|
|
|
/mob/living/carbon/resist_restraints(attempt_breaking)
|
|
INVOKE_ASYNC(src, PROC_REF(resist_muzzle))
|
|
if(wear_suit && wear_suit.breakouttime)
|
|
wear_suit.resist_restraints(src, attempt_breaking)
|
|
return
|
|
|
|
var/obj/item/restraints/cuffs
|
|
if(handcuffed)
|
|
cuffs = handcuffed
|
|
else if(legcuffed)
|
|
cuffs = legcuffed
|
|
|
|
if(!cuffs)
|
|
return
|
|
cuff_resist(cuffs, attempt_breaking)
|
|
|
|
/mob/living/carbon/resist_muzzle()
|
|
if(!istype(wear_mask, /obj/item/clothing/mask/muzzle))
|
|
return
|
|
var/obj/item/clothing/mask/muzzle/I = wear_mask
|
|
var/time = I.resist_time
|
|
if(I.resist_time == 0)//if it's 0, you can't get out of it
|
|
to_chat(src, "[I] is too well made, you'll need hands for this one!")
|
|
return
|
|
if(has_status_effect(STATUS_EFFECT_REMOVE_MUZZLE))
|
|
to_chat(src, "<span class='notice'>You are already trying to remove [I]!</span>")
|
|
return
|
|
apply_status_effect(STATUS_EFFECT_REMOVE_MUZZLE)
|
|
visible_message("<span class='warning'>[src] gnaws on [I], trying to remove it!</span>")
|
|
to_chat(src, "<span class='notice'>You attempt to remove [I]... (This will take around [time/10] seconds and you need to stand still.)</span>")
|
|
if(do_after(src, time, FALSE, src, extra_checks = list(CALLBACK(src, PROC_REF(muzzle_check)))))
|
|
visible_message("<span class='warning'>[src] removes [I]!</span>")
|
|
to_chat(src, "<span class='notice'>You get rid of [I]!</span>")
|
|
if(I.security_lock)
|
|
I.do_break()
|
|
unEquip(I)
|
|
remove_status_effect(STATUS_EFFECT_REMOVE_MUZZLE)
|
|
|
|
/mob/living/carbon/proc/cuff_resist(obj/item/restraints/restraints, break_cuffs)
|
|
var/effective_breakout_time = restraints.breakouttime
|
|
if(break_cuffs)
|
|
effective_breakout_time = 5 SECONDS
|
|
|
|
if(has_status_effect(STATUS_EFFECT_REMOVE_CUFFS))
|
|
to_chat(src, "<span class='notice'>You are already trying to [break_cuffs ? "break" : "remove"] [restraints].</span>")
|
|
return
|
|
|
|
apply_status_effect(STATUS_EFFECT_REMOVE_CUFFS)
|
|
|
|
restraints.attempt_resist_restraints(src, break_cuffs, effective_breakout_time)
|
|
|
|
//called when we get cuffed/uncuffed
|
|
/mob/living/carbon/proc/update_handcuffed()
|
|
SEND_SIGNAL(src, COMSIG_CARBON_UPDATE_HANDCUFFED, handcuffed)
|
|
if(handcuffed)
|
|
drop_r_hand()
|
|
drop_l_hand()
|
|
stop_pulling()
|
|
throw_alert("handcuffed", /atom/movable/screen/alert/restrained/handcuffed, new_master = handcuffed)
|
|
ADD_TRAIT(src, TRAIT_RESTRAINED, "handcuffed")
|
|
else
|
|
REMOVE_TRAIT(src, TRAIT_RESTRAINED, "handcuffed")
|
|
clear_alert("handcuffed")
|
|
changeNext_move(CLICK_CD_RAPID) //reset click cooldown from handcuffs
|
|
update_action_buttons_icon() //some of our action buttons might be unusable when we're handcuffed.
|
|
update_inv_handcuffed()
|
|
update_hands_hud()
|
|
|
|
/mob/living/carbon/get_standard_pixel_y_offset()
|
|
if(IS_HORIZONTAL(src))
|
|
if(buckled)
|
|
return buckled.buckle_offset
|
|
else
|
|
return PIXEL_Y_OFFSET_LYING
|
|
return initial(pixel_y)
|
|
|
|
/mob/living/carbon/emp_act(severity)
|
|
..()
|
|
for(var/X in internal_organs)
|
|
var/obj/item/organ/internal/O = X
|
|
O.emp_act(severity)
|
|
|
|
/mob/living/carbon/get_status_tab_items()
|
|
var/list/status_tab_data = ..()
|
|
. = status_tab_data
|
|
var/obj/item/organ/internal/alien/plasmavessel/vessel = get_int_organ(/obj/item/organ/internal/alien/plasmavessel)
|
|
if(vessel)
|
|
status_tab_data[++status_tab_data.len] = list("Plasma Stored:", "[vessel.stored_plasma]/[vessel.max_plasma]")
|
|
|
|
/mob/living/carbon/get_all_slots()
|
|
return list(l_hand,
|
|
r_hand,
|
|
handcuffed,
|
|
legcuffed,
|
|
back,
|
|
wear_mask)
|
|
/**
|
|
* Clears a carbon mob's handcuffs and legcuffs, dropping them to the ground.
|
|
*
|
|
* Arguments:
|
|
* * show_message - if TRUE, will display a visible message when restraints are removed. FALSE by default.
|
|
*
|
|
* Returns:
|
|
* * TRUE if handcuffs or legcuffs were removed, FALSE otherwise
|
|
*/
|
|
/mob/living/carbon/proc/clear_restraints(show_message = FALSE)
|
|
. = clear_handcuffs(show_message)
|
|
. |= clear_legcuffs(show_message)
|
|
|
|
/**
|
|
* Removes a carbon's handcuffs, dropping them to the ground. Calls update_handcuffed(). Unbuckles if handcuffs were necessary for the buckle (pipe buckling, etc.)
|
|
*
|
|
* Arguments:
|
|
* * show_message - if TRUE, will display a visible message when restraints are removed. FALSE by default.
|
|
*
|
|
* Returns:
|
|
* * TRUE if handcuffs existed and were successfully removed, FALSE otherwise
|
|
*/
|
|
/mob/living/carbon/proc/clear_handcuffs(show_message = FALSE)
|
|
if(!handcuffed)
|
|
return FALSE
|
|
|
|
var/obj/item/W = handcuffed
|
|
if(isnull(W))
|
|
return FALSE
|
|
|
|
handcuffed = null
|
|
if(buckled && buckled.buckle_requires_restraints)
|
|
unbuckle()
|
|
update_handcuffed()
|
|
|
|
if(client)
|
|
client.screen -= W
|
|
|
|
if(show_message)
|
|
visible_message("<span class='warning'>[src] slips out of [W]!</span>")
|
|
|
|
W.forceMove(drop_location())
|
|
W.dropped(src)
|
|
if(W)
|
|
W.layer = initial(W.layer)
|
|
W.plane = initial(W.plane)
|
|
|
|
return TRUE
|
|
|
|
/**
|
|
* Removes a carbon's legcuffs, dropping them to the ground. Calls update_inv_legcuffed().
|
|
*
|
|
* Arguments:
|
|
* * show_message - if TRUE, will display a visible message when restraints are removed. FALSE by default.
|
|
*
|
|
* Returns:
|
|
* * TRUE if legcuffs existed and were successfully removed, FALSE otherwise
|
|
*/
|
|
/mob/living/carbon/proc/clear_legcuffs(show_message = FALSE)
|
|
if(!legcuffed)
|
|
return FALSE
|
|
|
|
var/obj/item/W = legcuffed
|
|
if(isnull(W))
|
|
return FALSE
|
|
|
|
legcuffed = null
|
|
toggle_move_intent()
|
|
update_inv_legcuffed()
|
|
|
|
if(client)
|
|
client.screen -= W
|
|
|
|
if(show_message)
|
|
visible_message("<span class='warning'>[W] falls off of [src]!</span>")
|
|
|
|
W.forceMove(drop_location())
|
|
W.dropped(src)
|
|
if(W)
|
|
W.layer = initial(W.layer)
|
|
W.plane = initial(W.plane)
|
|
|
|
return TRUE
|
|
|
|
|
|
/mob/living/carbon/proc/slip(description, knockdown, tilesSlipped, walkSafely, slipAny, slipVerb = "slip")
|
|
if(HAS_TRAIT(src, TRAIT_FLYING) || buckled || (walkSafely && m_intent == MOVE_INTENT_WALK))
|
|
return FALSE
|
|
|
|
if(IS_HORIZONTAL(src) && !tilesSlipped)
|
|
return FALSE
|
|
|
|
if(!(slipAny))
|
|
if(ishuman(src))
|
|
var/mob/living/carbon/human/H = src
|
|
if(HAS_TRAIT(H, TRAIT_NOSLIP))
|
|
return FALSE
|
|
|
|
if(tilesSlipped)
|
|
for(var/i in 1 to tilesSlipped)
|
|
spawn(i)
|
|
step(src, dir)
|
|
|
|
stop_pulling()
|
|
to_chat(src, "<span class='notice'>You [slipVerb]ped on [description]!</span>")
|
|
playsound(loc, 'sound/misc/slip.ogg', 50, TRUE, -3)
|
|
// Something something don't run with scissors
|
|
moving_diagonally = 0 //If this was part of diagonal move slipping will stop it.
|
|
KnockDown(knockdown)
|
|
return TRUE
|
|
|
|
/mob/living/carbon/proc/shock_reduction()
|
|
var/shock_reduction = 0
|
|
if(reagents)
|
|
for(var/datum/reagent/R in reagents.reagent_list)
|
|
if(R.shock_reduction)
|
|
shock_reduction += R.shock_reduction
|
|
return shock_reduction
|
|
|
|
/mob/living/carbon/proc/can_eat(flags = 255)
|
|
return TRUE
|
|
|
|
/mob/living/carbon/proc/eat(obj/item/food/to_eat, mob/user, bitesize_override)
|
|
if(!istype(to_eat))
|
|
return FALSE
|
|
|
|
var/fullness = nutrition + 10
|
|
if(istype(to_eat, /obj/item/food))
|
|
for(var/datum/reagent/consumable/C in reagents.reagent_list) // We add the nutrition value of what we're currently digesting
|
|
fullness += C.nutriment_factor * C.volume / (C.metabolization_rate * metabolism_efficiency)
|
|
|
|
if(user == src)
|
|
if(!selfFeed(to_eat, fullness))
|
|
return FALSE
|
|
else
|
|
if(!forceFed(to_eat, user, fullness))
|
|
return FALSE
|
|
|
|
consume(to_eat, bitesize_override)
|
|
SSticker.score.score_food_eaten++
|
|
return TRUE
|
|
|
|
/mob/living/carbon/proc/drink(obj/item/reagent_containers/drinks/to_drink, mob/user, drinksize_override)
|
|
if(user == src)
|
|
if(!selfDrink(to_drink))
|
|
return FALSE
|
|
else if(!forceFed(to_drink, user, nutrition))
|
|
return FALSE
|
|
|
|
if(to_drink.consume_sound)
|
|
playsound(loc, to_drink.consume_sound, rand(10, 50), TRUE)
|
|
if(to_drink.reagents.total_volume)
|
|
taste(to_drink.reagents)
|
|
var/drink_size = max(initial(to_drink.amount_per_transfer_from_this), 5)
|
|
if(drinksize_override)
|
|
drink_size = drinksize_override
|
|
to_drink.reagents.reaction(src, REAGENT_INGEST)
|
|
to_drink.reagents.trans_to(src, drink_size)
|
|
|
|
SSticker.score.score_food_eaten++
|
|
return TRUE
|
|
|
|
/mob/living/carbon/proc/selfFeed(obj/item/food/to_eat, fullness)
|
|
if(to_eat.junkiness && satiety < -150 && nutrition > NUTRITION_LEVEL_STARVING + 50)
|
|
to_chat(src, "<span class='notice'>You don't feel like eating any more junk food at the moment.</span>")
|
|
return FALSE
|
|
|
|
if(fullness <= 50)
|
|
to_chat(src, "<span class='warning'>You hungrily chew out a piece of [to_eat] and gobble it!</span>")
|
|
else if(fullness > 50 && fullness < 150)
|
|
to_chat(src, "<span class='notice'>You hungrily begin to eat [to_eat].</span>")
|
|
else if(fullness > 150 && fullness < 500)
|
|
to_chat(src, "<span class='notice'>You take a bite of [to_eat].</span>")
|
|
else if(fullness > 500 && fullness < 600)
|
|
to_chat(src, "<span class='notice'>You unwillingly chew a bit of [to_eat].</span>")
|
|
else if(fullness > (600 * (1 + overeatduration / 2000))) // The more you eat - the more you can eat
|
|
to_chat(src, "<span class='warning'>You cannot force any more of [to_eat] to go down your throat.</span>")
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/mob/living/carbon/proc/selfDrink(obj/item/reagent_containers/drinks/toDrink, mob/user)
|
|
return TRUE
|
|
|
|
/mob/living/carbon/proc/forceFed(obj/item/reagent_containers/to_eat, mob/user, fullness)
|
|
if(fullness > (600 * (1 + overeatduration / 1000)))
|
|
visible_message("<span class='warning'>[user] cannot force anymore of [to_eat] down [src]'s throat.</span>")
|
|
return FALSE
|
|
|
|
visible_message("<span class='warning'>[user] attempts to force [src] to swallow [to_eat].</span>")
|
|
if(!do_after(user, 3 SECONDS, TRUE, src))
|
|
return FALSE
|
|
forceFedAttackLog(to_eat, user)
|
|
visible_message("<span class='warning'>[user] forces [src] to swallow [to_eat].</span>")
|
|
return TRUE
|
|
|
|
/mob/living/carbon/proc/forceFedAttackLog(obj/item/reagent_containers/to_eat, mob/user)
|
|
add_attack_logs(user, src, "Fed [to_eat]. Reagents: [to_eat.reagents.log_list(to_eat)]", to_eat.reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null)
|
|
|
|
/*TO DO - If/when stomach organs are introduced, override this at the human level sending the item to the stomach
|
|
so that different stomachs can handle things in different ways VB*/
|
|
/mob/living/carbon/proc/consume(obj/item/food/to_eat, bitesize_override)
|
|
var/this_bite = bitesize_override || to_eat.bitesize
|
|
if(!to_eat.reagents)
|
|
return
|
|
if(satiety > -200)
|
|
satiety -= to_eat.junkiness
|
|
if(to_eat.consume_sound)
|
|
playsound(loc, to_eat.consume_sound, rand(10, 50), TRUE)
|
|
if(to_eat.reagents.total_volume)
|
|
taste(to_eat.reagents)
|
|
var/fraction = min(this_bite / to_eat.reagents.total_volume, 1)
|
|
to_eat.reagents.reaction(src, REAGENT_INGEST, fraction)
|
|
to_eat.reagents.trans_to(src, this_bite)
|
|
|
|
/mob/living/carbon/get_access()
|
|
. = ..()
|
|
|
|
var/obj/item/RH = get_active_hand()
|
|
if(RH)
|
|
. |= RH.GetAccess()
|
|
|
|
var/obj/item/LH = get_inactive_hand()
|
|
if(LH)
|
|
. |= LH.GetAccess()
|
|
|
|
/mob/living/carbon/proc/can_breathe_gas()
|
|
if(!wear_mask)
|
|
return TRUE
|
|
|
|
if(!(wear_mask.flags & BLOCK_GAS_SMOKE_EFFECT) && internal == null)
|
|
return TRUE
|
|
|
|
return FALSE
|
|
|
|
//to recalculate and update the mob's total tint from tinted equipment it's wearing.
|
|
/mob/living/carbon/proc/update_tint()
|
|
var/tinttotal = get_total_tint()
|
|
if(tinttotal >= TINT_BLIND)
|
|
become_blind("tint")
|
|
else if(tinttotal >= TINT_IMPAIR)
|
|
overlay_fullscreen("tint", /atom/movable/screen/fullscreen/stretch/impaired, 2)
|
|
cure_blind("tint")
|
|
else
|
|
clear_fullscreen("tint", 0)
|
|
cure_blind("tint")
|
|
|
|
/mob/living/carbon/proc/get_total_tint()
|
|
. = 0
|
|
if(istype(head, /obj/item/clothing/head))
|
|
var/obj/item/clothing/head/HT = head
|
|
. += HT.tint
|
|
if(wear_mask)
|
|
. += wear_mask.tint
|
|
|
|
var/obj/item/organ/internal/eyes/E = get_organ_slot("eyes")
|
|
if(E)
|
|
. += E.tint
|
|
|
|
/mob/living/carbon/human/get_total_tint()
|
|
. = ..()
|
|
if(glasses)
|
|
var/obj/item/clothing/glasses/G = glasses
|
|
. += G.tint
|
|
|
|
|
|
//handle stuff to update when a mob equips/unequips a mask.
|
|
/mob/living/proc/wear_mask_update(obj/item/clothing/C, toggle_off = 1)
|
|
update_inv_wear_mask()
|
|
|
|
/mob/living/carbon/wear_mask_update(obj/item/clothing/C, toggle_off = 1)
|
|
if(C.tint || initial(C.tint))
|
|
update_tint()
|
|
update_inv_wear_mask()
|
|
|
|
//handle stuff to update when a mob equips/unequips a headgear.
|
|
/mob/living/carbon/proc/head_update(obj/item/I, forced)
|
|
if(isclothing(I))
|
|
var/obj/item/clothing/C = I
|
|
if(C.tint || initial(C.tint))
|
|
update_tint()
|
|
update_sight()
|
|
if(I.flags_inv & HIDEMASK || forced)
|
|
update_inv_wear_mask()
|
|
update_inv_head()
|
|
|
|
/mob/living/carbon/update_sight()
|
|
if(!client)
|
|
return
|
|
|
|
if(stat == DEAD)
|
|
grant_death_vision()
|
|
return
|
|
|
|
see_invisible = initial(see_invisible)
|
|
see_in_dark = initial(see_in_dark)
|
|
sight = initial(sight)
|
|
lighting_alpha = initial(lighting_alpha)
|
|
|
|
if(client.eye != src)
|
|
var/atom/A = client.eye
|
|
if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates.
|
|
return
|
|
|
|
if(HAS_TRAIT(src, TRAIT_THERMAL_VISION))
|
|
sight |= (SEE_MOBS)
|
|
lighting_alpha = min(lighting_alpha, LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
|
|
|
|
if(HAS_TRAIT(src, TRAIT_XRAY_VISION))
|
|
sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
|
|
see_in_dark = max(see_in_dark, 8)
|
|
|
|
if(HAS_TRAIT(src, TRAIT_MESON_VISION))
|
|
sight |= SEE_TURFS
|
|
lighting_alpha = min(lighting_alpha, LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
|
|
|
|
if(HAS_TRAIT(src, TRAIT_NIGHT_VISION))
|
|
see_in_dark = max(see_in_dark, 8)
|
|
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
|
|
|
|
SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT)
|
|
sync_lighting_plane_alpha()
|
|
|
|
/mob/living/carbon/ExtinguishMob()
|
|
for(var/X in get_equipped_items())
|
|
var/obj/item/I = X
|
|
I.acid_level = 0 //washes off the acid on our clothes
|
|
I.extinguish() //extinguishes our clothes
|
|
..()
|
|
|
|
/mob/living/carbon/clean_blood(radiation_clean = FALSE, clean_hands = TRUE, clean_mask = TRUE, clean_feet = TRUE)
|
|
if(head)
|
|
if(head.clean_blood(radiation_clean))
|
|
update_inv_head()
|
|
if(head.flags_inv & HIDEMASK)
|
|
clean_mask = FALSE
|
|
if(wear_suit)
|
|
if(wear_suit.clean_blood(radiation_clean))
|
|
update_inv_wear_suit()
|
|
if(wear_suit.flags_inv & HIDESHOES)
|
|
clean_feet = FALSE
|
|
if(wear_suit.flags_inv & HIDEGLOVES)
|
|
clean_hands = FALSE
|
|
..(clean_hands, clean_mask, clean_feet)
|
|
|
|
/mob/living/carbon/fall_and_crush(turf/target_turf, crush_damage, should_crit, crit_damage_factor, datum/tilt_crit/forced_crit, weaken_time, knockdown_time, ignore_gravity, should_rotate = TRUE, angle)
|
|
// keep most of what's passed in, but don't change the angle
|
|
. = ..(target_turf, crush_damage, should_crit, crit_damage_factor, forced_crit, weaken_time, knockdown_time, should_rotate = FALSE, rightable = FALSE)
|
|
KnockDown(10 SECONDS)
|
|
|
|
/mob/living/carbon/rename_character(oldname, newname)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
|
|
for(var/obj/item/organ/internal/IO in internal_organs)
|
|
if(IO.dna?.real_name == oldname)
|
|
IO.dna.real_name = newname
|
|
|
|
/// Returns TRUE if an air tank compatible mask or breathing tube is equipped.
|
|
/mob/living/carbon/proc/can_breathe_internals()
|
|
return can_breathe_tube() || can_breathe_mask() || can_breathe_helmet()
|
|
|
|
/// Returns TRUE if an air tank compatible helmet is equipped.
|
|
/mob/living/carbon/proc/can_breathe_helmet()
|
|
return (isclothing(head) && (head.flags & AIRTIGHT))
|
|
|
|
/// Returns TRUE if an air tank compatible mask is equipped.
|
|
/mob/living/carbon/proc/can_breathe_mask()
|
|
return (isclothing(wear_mask) && (wear_mask.flags & AIRTIGHT))
|
|
|
|
/// Returns TRUE if a breathing tube is equipped.
|
|
/mob/living/carbon/proc/can_breathe_tube()
|
|
return get_organ_slot("breathing_tube")
|