Files
vgstation13/code/modules/mob/living/living_defense.dm
west3436 ff68825c5d Mob Ignition Fixes/Cleanup (#36968)
* mob ignition fix

* debug text
2024-08-29 19:47:02 -05:00

358 lines
12 KiB
Plaintext

/*
run_armor_check(a,b,c,d,e,f,g)
args
a:def_zone - What part is getting hit, if null will check entire body
b:attack_flag - What type of attack, bullet, laser, energy, melee
c: What text is to be shown should the blow be fully negated
d: What text is to be shown should the blow be partially negated
e: Modifier on top of the armor, multiplier
f: Whether messages should be shown or not to the user
g: Armor penetration - How much armor to be negated.
Returns
percent damage reduction
*/
/mob/living/proc/run_armor_check(var/def_zone = null, var/attack_flag = "melee", var/absorb_text = null, var/soften_text = null, modifier = 1, var/quiet = 0, var/armor_penetration = 0)
var/armor = max(0, (getarmor(def_zone, attack_flag)-armor_penetration)*modifier)
if(armor >= 100) //Absolutely no damage
if(!quiet)
if(absorb_text)
show_message("[absorb_text]")
else
show_message("<span class='borange'>Your armor ABSORBS the blow!</span>")
else if(armor >= 50)
if(!quiet)
if(absorb_text)
show_message("[soften_text]",4)
else
show_message("<span class='borange'>Your armor SOFTENS the blow!</span>")
return armor
/mob/living/proc/getarmor(var/def_zone, var/type)
return 0
/mob/living/proc/getarmorabsorb(var/def_zone, var/type)
return 0
/mob/living/proc/run_armor_absorb(var/def_zone = null, var/attack_flag = "melee", var/initial_damage)
var/armor = getarmorabsorb(def_zone, attack_flag)
var/final_damage = initial_damage
if(armor)
var/damage_multiplier = final_damage/armor
if(damage_multiplier < 1)
final_damage *= damage_multiplier
return final_damage
/mob/living/bullet_act(var/obj/item/projectile/P, var/def_zone)
var/obj/item/weapon/cloaking_device/C = locate(/obj/item/weapon/cloaking_device) in src
if(C && C.active)
C.attack_self(src)//Should shut it off
update_icons()
to_chat(src, "<span class='notice'>Your [C.name] was disrupted!</span>")
Stun(2)
flash_weak_pain()
if(istype(get_active_hand(),/obj/item/device/assembly/signaler))
var/obj/item/device/assembly/signaler/signaler = get_active_hand()
if(signaler.deadman && prob(80))
src.visible_message("<span class='warning'>[src] triggers their deadman's switch!</span>")
signaler.signal()
var/absorb = run_armor_check(def_zone, P.flag, armor_penetration = P.armor_penetration)
if(absorb >= 100)
P.on_hit(src,100)
return PROJECTILE_COLLISION_BLOCKED
if(!P.nodamage)
var/damage = run_armor_absorb(def_zone, P.flag, P.damage)
apply_damage(damage, P.damage_type, def_zone, absorb, P.is_sharp(), used_weapon = P)
regenerate_icons()
P.on_hit(src, absorb)
if(istype(P, /obj/item/projectile/beam/lightning))
if(P.damage >= 200)
src.dust()
return PROJECTILE_COLLISION_DEFAULT
//Multiplier for damage when an object has hit.
/mob/living/proc/thrown_defense(var/obj/O)
return 1
/mob/living/hitby(atom/movable/AM as mob|obj,var/speed = 5,var/dir,var/list/hit_whitelist)//Standardization and logging -Sieve
. = ..()
if(.)
return
if(flags & INVULNERABLE)
return
if(hit_whitelist && (src in hit_whitelist))
return
if(istype(AM,/obj/) && !istype(AM,/obj/effect/))
var/obj/O = AM
var/zone = ran_zone(LIMB_CHEST,75)//Hits a random part of the body, geared towards the chest
var/dtype = BRUTE
if(istype(O,/obj/item/weapon))
var/obj/item/weapon/W = O
dtype = W.damtype
visible_message("<span class='warning'>[src] has been hit by [O].</span>")
if(O.impactsound)
playsound(loc, O.impactsound, 80, 1, -1)
var/zone_normal_name
switch(zone)
if(LIMB_LEFT_ARM)
zone_normal_name = "left arm"
if(LIMB_RIGHT_ARM)
zone_normal_name = "right arm"
if(LIMB_LEFT_LEG)
zone_normal_name = "left leg"
if(LIMB_RIGHT_LEG)
zone_normal_name = "right leg"
else
zone_normal_name = zone
var/armor = run_armor_check(zone, "melee", "Your armor has protected your [zone_normal_name].", "Your armor has softened the blow to your [zone_normal_name].", armor_penetration = O.throwforce*(speed/5)*O.sharpness)
if(armor < 100) //Stop the damage if the person is immune
var/damage = run_armor_absorb(zone, "melee", O.throwforce*(speed/5) * thrown_defense(O))
apply_damage(damage, dtype, zone, armor, O.is_sharp(), O)
// Begin BS12 momentum-transfer code.
if(speed >= EMBED_THROWING_SPEED)
var/obj/item/weapon/W = O
var/momentum = speed/2
visible_message("<span class='warning'>[src] staggers under the impact!</span>","<span class='warning'>You stagger under the impact!</span>")
throw_at(get_edge_target_turf(src,dir),1,momentum)
if(istype(W.loc,/mob/living) && W.sharpness_flags & SHARP_TIP) //Projectile is embedded and suitable for pinning.
if(!istype(src,/mob/living/carbon/human)) //Handles embedding for non-humans and simple_animals.
O.forceMove(src)
src.embedded += O
var/turf/T = near_wall(dir,2)
if(T)
src.forceMove(T)
visible_message("<span class='warning'>[src] is pinned to the wall by [O]!</span>","<span class='warning'>You are pinned to the wall by [O]!</span>")
src.anchored = 1
src.pinned += O
//Log stuff!
if(!src.ckey || src.isDead()) //Message admins if the hit mob is alive and has a ckey
return
if(!O.fingerprintslast)
return
var/client/assailant = directory[ckey(O.fingerprintslast)]
if(assailant && assailant.ckey && assailant.mob)
msg_admin_attack("[src.name] ([src.ckey]) was hit by a thrown [O], last touched by [assailant.mob.name] ([assailant.ckey]) (speed: [speed]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[src.x];Y=[src.y];Z=[src.z]'>JMP</a>)")
src.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been hit with a thrown [O], last touched by [assailant.mob.name] ([assailant.ckey]) (speed: [speed])</font>")
assailant.mob.attack_log += text("\[[time_stamp()]\] <font color='red'>Hit [src.name] ([src.ckey]) with a thrown [O] (speed: [speed])</font>")
assaulted_by(assailant.mob)
/*
Ear and eye protection
Some mobs have built-in ear or eye protection, mobs that can wear equipment may account their eye/ear wear into this proc
*/
//earprot(): retuns 0 for no protection, 1 for full protection (no ears, earmuffs, etc)
/mob/living/proc/earprot()
return 0
//eyecheck(): retuns 0 for no protection, 1 for partial protection, 2 for full protection
//EYECHECK_NO_PROTECTION, EYECHECK_PARTIAL_PROTECTION, EYECHECK_FULL_PROTECTION
/mob/living/proc/eyecheck()
return EYECHECK_NO_PROTECTION
//BITES
/mob/living/bite_act(mob/living/carbon/human/M as mob)
if(M.head && istype(M.head,/obj/item/clothing/head))
var/obj/item/clothing/head/H = M.head
if(H.bite_action(src))
return //Head slot item overrode the bite
var/datum/butchering_product/teeth/T = locate(/datum/butchering_product/teeth) in M.butchering_drops
var/damage = 0
var/attacktype = "bitten"
if(T?.amount > 0)
damage = rand(1, 5)
else //no teeth time to GUM
damage = 1
attacktype = "gummed"
if(M.organ_has_mutation(LIMB_HEAD, M_BEAK)) //Beaks = stronger bites
damage += 4
if(!damage)
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
visible_message("<span class='borange'>\The [M] has attempted to bite \the [src]!</span>")
add_logs(M, src, "miss-bit", admin=1, object=null, addition=null)
return 0
playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1)
src.visible_message("<span class='danger'>\The [M] has [attacktype] \the [src]!</span>", "<span class='userdanger'>You were [attacktype] by \the [M]!</span>")
add_logs(M, src, "bit", admin=1, object=null, addition="DMG: [damage]")
adjustBruteLoss(damage)
var/block = 0
// biting causes the check to consider that both sides are bleeding, allowing for blood-only disease transmission through biting.
if (check_contact_sterility(FULL_TORSO))//only one side has to wear protective clothing to prevent contact infection
block = 1
share_contact_diseases(M,block,1)
//KICKS
/mob/living/kick_act(mob/living/carbon/human/M)
//Pick a random usable foot to perform the kick with
var/datum/organ/external/foot_organ = M.pick_usable_organ(LIMB_RIGHT_FOOT, LIMB_LEFT_FOOT)
M.delayNextAttack(20) //Kicks are slow
if((M_CLUMSY in M.mutations) && prob(20)) //Kicking yourself (or being clumsy) = stun
M.visible_message("<span class='notice'>\The [M] trips while attempting to kick \the [src]!</span>", "<span class='userdanger'>While attempting to kick \the [src], you trip and fall!</span>")
var/incapacitation_duration = rand(1, 10)
M.Knockdown(incapacitation_duration)
M.Stun(incapacitation_duration)
return
var/stomping = 0
var/attack_verb = "kicks"
if(M.size > size && !flying) //On the ground, the kicker is bigger than/equal size of the victim = stomp
stomping = 1
var/damage = rand(0,7)
if(stomping) //Stomps = more damage and armor bypassing
damage += rand(0,7)
attack_verb = "stomps on"
else if(M.reagents && M.reagents.has_reagent(GYRO))
damage += rand(0,4)
attack_verb = "roundhouse kicks"
if(!damage)
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
visible_message("<span class='borange'>\The [M] attempts to kick \the [src]!</span>")
add_logs(M, src, "miss-kicked", admin=1, object=null, addition=null)
return 0
//Handle shoes
var/obj/item/clothing/shoes/S = M.shoes
if(istype(S))
damage += S.bonus_kick_damage
S.on_kick(M, src)
else if(M.organ_has_mutation(foot_organ, M_TALONS)) //Not wearing shoes and having talons = bonus 1-6 damage
damage += rand(1,6)
playsound(loc, "punch", 30, 1, -1)
visible_message("<span class='danger'>\The [M] [attack_verb] \the [src]!</span>", "<span class='userdanger'>\The [M] [attack_verb] you!</span>")
if(M.size != size) //The bigger the kicker, the more damage
damage = max(damage + (rand(1,5) * (1 + M.size - size)), 0)
add_logs(M, src, "kicked", admin=1, object=null, addition="DMG: [damage]")
adjustBruteLoss(damage)
var/block = 0
var/bleeding = 0
if ( M.check_contact_sterility(FEET) || check_contact_sterility(FULL_TORSO))//only one side has to wear protective clothing to prevent contact infection
block = 1
if ( M.check_bodypart_bleeding(FEET) && check_bodypart_bleeding(FULL_TORSO))//both sides have to be bleeding to allow for blood infections
bleeding = 1
share_contact_diseases(M,block,bleeding)
/mob/living/proc/near_wall(var/direction,var/distance=1)
var/turf/T = get_step(get_turf(src),direction)
var/turf/last_turf = src.loc
var/i = 1
while(i>0 && i<=distance)
if(T.density) //Turf is a wall!
return last_turf
i++
last_turf = T
T = get_step(T,direction)
return 0
// End BS12 momentum-transfer code.
//Mobs on Fire
/mob/living/ignite()
if(on_fire)
return 0
if(check_fire_protection())
return 0
if(!firelightdummy)
firelightdummy = new (src)
on_fire = 1
update_fire()
return 1
/mob/living/extinguish()
..()
update_fire()
/mob/living/check_fire_protection()
if(fire_stacks<0)
fire_stacks++
return 1
else
return 0
/mob/living/burnSolidFuel()
return
/mob/living/proc/update_fire()
return
/mob/living/proc/adjust_fire_stacks(add_fire_stacks) //Adjusting the amount of fire_stacks we have on person
fire_stacks = clamp(fire_stacks + add_fire_stacks, -20, 20)
//Activates when an attack misses a mob
/mob/living/proc/on_dodge(var/mob/living/attacker, var/obj/item/attacking_object)
return
/mob/living/proc/handle_fire()
if((flags & INVULNERABLE) && on_fire)
extinguish()
if(fire_stacks < 0)
fire_stacks++ //If we've doused ourselves in water to avoid fire, dry off slowly
fire_stacks = min(0, fire_stacks)//So we dry ourselves back to default, nonflammable.
if(!on_fire)
return 1
var/oxy=0
var/turf/T=get_turf(src)
if(istype(T))
var/datum/gas_mixture/G = T.return_air() // Check if we're standing in an oxygenless environment
if(G)
oxy = G.molar_density(GAS_OXYGEN)
if(oxy < (1 / CELL_VOLUME) || fire_stacks < 0)
extinguish() //If there's no oxygen in the tile we're on, put out the fire
return 1
T.hotspot_expose(700, SMALL_FLAME, 1)
/mob/living/fire_act(datum/gas_mixture/air, temperature, exposed_volume)
if(mutations.Find(M_UNBURNABLE))
return
adjust_fire_stacks(0.5)
ignite()
var/turf/T = get_turf(src)
var/flevel = air.calculate_firelevel(T)
var/pressure = air.return_pressure()
FireBurn(flevel,temperature, pressure)
/mob/living/proc/FireBurn(var/firelevel = 0, var/temperature, var/pressure)
var/mx = 5 * max(firelevel,1.5)/ZAS_firelevel_multiplier * min(pressure / ONE_ATMOSPHERE, 1)
apply_damage(2.5*mx, BURN)
//Mobs on Fire end
//Return true if thrown object misses
/mob/living/PreImpact(atom/movable/A, speed)
if(lying)
return TRUE
return FALSE