Files
Aurora.3/code/modules/mob/living/living_defense.dm
Matt Atlas 556f1aa86f Fullscreen overlay rework, full-black unconsciousness overlay, overlay fadeout animations + more. (#10053)
Ported the clickcatcher from tg/bay. You can now click black spots to turn. Isn't that handy, especially with vision cones.

    Reworked fullscreen overlays into an easier and better system, courtesy of PsiOmegaDelta.

    Similiarly, added fadeout animations to all pain overlays, plus unconsciousness.

    Fixed the vampire frenzy overlay never showing.

    Unconsciousness is now FULL darkness.

    Flashing mobs is now an inbuilt proc.
2020-10-02 08:53:14 +03:00

434 lines
14 KiB
Plaintext

/*
run_armor_check() args
def_zone - What part is getting hit, if null will check entire body
attack_flag - The type of armor to be checked
armor_pen - reduces the effectiveness of armor
absorb_text - shown if the armor check is 100% successful
soften_text - shown if the armor check is more than 0% successful and less than 100%
Returns
a blocked amount between 0 - 100, representing the success of the armor check.
*/
#define MOB_FIRE_LIGHT_RANGE 3 //These control the intensity and range of light given off by a mob which is on fire
#define MOB_FIRE_LIGHT_POWER 2
/mob/living/proc/run_armor_check(var/def_zone = null, var/attack_flag = "melee", var/armor_pen = 0, var/absorb_text = null, var/soften_text = null)
if(armor_pen >= 100)
return 0 //might as well just skip the processing
var/armor = getarmor(def_zone, attack_flag)
if(armor_pen >= armor)
return 0 //effective_armor is going to be 0
var/blocked = (armor - armor_pen)
if(blocked >= 100)
if(absorb_text)
show_message("<span class='warning'>[absorb_text]</span>")
else
show_message("<span class='warning'>Your armor absorbs the blow!</span>")
return 100
if(blocked > 20)
//Should we show this every single time?
if(soften_text)
show_message("<span class='warning'>[soften_text]</span>")
else
show_message("<span class='warning'>Your armor softens the blow!</span>")
return round(blocked, 1)
//Adds two armor values together.
//If armor_a and armor_b are between 0-100 the result will always also be between 0-100.
/proc/add_armor(var/armor_a, var/armor_b)
if(armor_a >= 100 || armor_b >= 100)
return 100 //adding to infinite protection doesn't make it any bigger
var/protection_a = 1/(BLOCKED_MULT(armor_a)) - 1
var/protection_b = 1/(BLOCKED_MULT(armor_b)) - 1
return 100 - 1/(protection_a + protection_b + 1)*100
//if null is passed for def_zone, then this should return something appropriate for all zones (e.g. area effect damage)
/mob/living/proc/getarmor(var/def_zone, var/type)
return 0
/mob/living/bullet_act(var/obj/item/projectile/P, var/def_zone, var/used_weapon = null)
//Being hit while using a cloaking device
var/obj/item/cloaking_device/C = locate(/obj/item/cloaking_device) in src
if(C && C.active)
C.attack_self(src)//Should shut it off
update_icon()
to_chat(src, "<span class='notice'>Your [C.name] was disrupted!</span>")
Stun(2)
//Being hit while using a deadman switch
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))
log_and_message_admins("has triggered a signaler deadman's switch")
src.visible_message("<span class='warning'>[src] triggers their deadman's switch!</span>")
signaler.signal()
//Stun Beams
if(P.taser_effect)
stun_effect_act(0, P.agony, def_zone, P)
to_chat(src, "<span class='warning'>You have been hit by [P]!</span>")
qdel(P)
return
//Armor
var/absorb = run_armor_check(def_zone, P.check_armor, P.armor_penetration)
var/damaged
if(prob(absorb))
if(P.damage_flags & DAM_SHARP || P.damage_flags & DAM_SHARP || P.damage_flags & DAM_LASER)
P.damage_flags &= ~DAM_SHARP
P.damage_flags &= ~DAM_EDGE
P.damage_flags &= ~DAM_LASER
if(!P.nodamage)
damaged = apply_damage(P.damage, P.damage_type, def_zone, absorb, 0, P, damage_flags = P.damage_flags, used_weapon = "\a [P.name]")
bullet_impact_visuals(P, def_zone, damaged)
P.on_hit(src, absorb, def_zone)
return absorb
/mob/living/proc/aura_check(var/type)
if(!auras)
return TRUE
. = TRUE
var/list/newargs = args - args[1]
for(var/a in auras)
var/obj/aura/aura = a
var/result = 0
switch(type)
if(AURA_TYPE_WEAPON)
result = aura.attackby(arglist(newargs))
if(AURA_TYPE_BULLET)
result = aura.bullet_act(arglist(newargs))
if(AURA_TYPE_THROWN)
result = aura.hitby(arglist(newargs))
if(AURA_TYPE_LIFE)
result = aura.life_tick()
if(result & AURA_FALSE)
. = FALSE
if(result & AURA_CANCEL)
break
//For visuals, blood splatters and so on.
/mob/living/proc/bullet_impact_visuals(var/obj/item/projectile/P, var/def_zone, var/damage)
var/list/impact_sounds = LAZYACCESS(P.impact_sounds, get_bullet_impact_effect_type(def_zone))
if(length(impact_sounds))
playsound(src, pick(impact_sounds), 75)
/mob/living/get_bullet_impact_effect_type(var/def_zone)
return BULLET_IMPACT_MEAT
//Handles the effects of "stun" weapons
/mob/living/proc/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon=null)
flash_pain(stun_amount)
if(stun_amount)
Stun(stun_amount)
Weaken(stun_amount)
apply_effect(stun_amount, STUTTER)
apply_effect(stun_amount, EYE_BLUR)
if(agony_amount)
apply_damage(agony_amount, PAIN, def_zone, 0, used_weapon)
apply_effect(agony_amount / 10, STUTTER)
apply_effect(agony_amount / 10, EYE_BLUR)
/mob/living/proc/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/tesla_shock = 0, var/ground_zero)
return 0 //only carbon liveforms have this proc
/mob/living/emp_act(severity)
var/list/L = src.get_contents()
for(var/obj/O in L)
O.emp_act(severity)
..()
/mob/living/proc/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone)
return target_zone
//Called when the mob is hit with an item in combat. Returns the blocked result
/mob/living/proc/hit_with_weapon(obj/item/I, mob/living/user, var/effective_force, var/hit_zone, var/ground_zero)
visible_message("<span class='danger'>[src] has been [LAZYPICK(I.attack_verb,"attacked")] with [I] by [user]!</span>")
var/blocked = run_armor_check(hit_zone, "melee")
standard_weapon_hit_effects(I, user, effective_force, blocked, hit_zone)
if(I.damtype == BRUTE && prob(33)) // Added blood for whacking non-humans too
var/turf/simulated/location = get_turf(src)
if(istype(location)) location.add_blood_floor(src)
return blocked
//returns 0 if the effects failed to apply for some reason, 1 otherwise.
/mob/living/proc/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/hit_zone)
if(!effective_force || blocked >= 100)
return 0
//Hulk modifier
if(HULK in user.mutations)
effective_force *= 2
//Apply weapon damage
var/damage_flags = I.damage_flags()
if(prob(blocked)) //armor provides a chance to turn sharp/edge weapon attacks into blunt ones
damage_flags &= ~DAM_SHARP
damage_flags &= ~DAM_EDGE
apply_damage(effective_force, I.damtype, hit_zone, blocked, used_weapon=I, damage_flags = damage_flags)
return 1
//this proc handles being hit by a thrown atom
/mob/living/hitby(atom/movable/AM, var/speed = THROWFORCE_SPEED_DIVISOR)//Standardization and logging -Sieve
if(!aura_check(AURA_TYPE_THROWN, AM, speed))
return
if(istype(AM,/obj/))
var/obj/O = AM
var/dtype = O.damtype
var/throw_damage = O.throwforce*(speed/THROWFORCE_SPEED_DIVISOR)
var/miss_chance = 15
if (O.throw_source)
var/distance = get_dist(O.throw_source, loc)
miss_chance = max(15*(distance-2), 0)
if (prob(miss_chance))
visible_message("<span class='notice'>\The [O] misses [src] narrowly!</span>")
playsound(src, 'sound/effects/throw_miss.ogg', 50, 1)
return
src.visible_message("<span class='warning'>[src] has been hit by [O].</span>")
var/armor = run_armor_check(null, "melee")
var/damage_flags = O.damage_flags()
apply_damage(throw_damage, dtype, null, armor, O, damage_flags = damage_flags)
O.throwing = 0 //it hit, so stop moving
if(ismob(O.thrower))
var/mob/M = O.thrower
var/client/assailant = M.client
if(assailant)
src.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been hit with a [O], thrown by [M.name] ([assailant.ckey])</font>")
M.attack_log += text("\[[time_stamp()]\] <span class='warning'>Hit [src.name] ([src.ckey]) with a thrown [O]</span>")
if(!istype(src,/mob/living/simple_animal/rat))
msg_admin_attack("[src.name] ([src.ckey]) was hit by a [O], thrown by [M.name] ([assailant.ckey]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[src.x];Y=[src.y];Z=[src.z]'>JMP</a>)",ckey=key_name(M),ckey_target=key_name(src))
// Begin BS12 momentum-transfer code.
var/mass = 1.5
if(istype(O, /obj/item))
var/obj/item/I = O
mass = I.w_class/THROWNOBJ_KNOCKBACK_DIVISOR
var/momentum = speed*mass
if(O.throw_source && momentum >= THROWNOBJ_KNOCKBACK_SPEED)
var/dir = get_dir(O.throw_source, src)
visible_message("<span class='warning'>[src] staggers under the impact!</span>","<span class='warning'>You stagger under the impact!</span>")
src.throw_at(get_edge_target_turf(src,dir),1,momentum)
if(!O || !src) return
if(O.sharp) //Projectile is suitable for pinning.
//Handles embedding for non-humans and simple_animals.
embed(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
/mob/living/proc/embed(var/obj/O, var/def_zone=null)
O.forceMove(src)
src.embedded += O
src.verbs += /mob/proc/yank_out_object
/mob/living/proc/turf_collision(var/turf/T, var/speed = THROWFORCE_SPEED_DIVISOR)
visible_message("<span class='danger'>[src] slams into \the [T]!</span>")
playsound(T, 'sound/effects/bangtaper.ogg', 50, 1, 1)//so it plays sounds on the turf instead, makes for awesome carps to hull collision and such
apply_damage(speed*5, BRUTE)
/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.
/mob/living/attack_generic(var/mob/user, var/damage, var/attack_message)
if(!damage)
return
adjustBruteLoss(damage)
user.attack_log += text("\[[time_stamp()]\] <span class='warning'>attacked [src.name] ([src.ckey])</span>")
src.attack_log += text("\[[time_stamp()]\] <font color='orange'>was attacked by [user.name] ([user.ckey])</font>")
if (attack_message)
src.visible_message("<span class='danger'>[user] has [attack_message] [src]!</span>")
user.do_attack_animation(src)
spawn(1) updatehealth()
return 1
/mob/living/proc/IgniteMob(var/fire_stacks_to_add = 0)
if(fire_stacks_to_add)
adjust_fire_stacks(fire_stacks_to_add)
if(fire_stacks > 0 && !on_fire)
on_fire = 1
set_light(light_range + MOB_FIRE_LIGHT_RANGE, light_power + MOB_FIRE_LIGHT_POWER)
update_fire()
return TRUE
return FALSE
/mob/living/proc/ExtinguishMob(var/fire_stacks_to_remove = 0)
if (fire_stacks_to_remove)
adjust_fire_stacks(-fire_stacks_to_remove)
if(fire_stacks <= 0 && on_fire)
on_fire = 0
set_light(max(0, light_range - MOB_FIRE_LIGHT_RANGE), max(0, light_power - MOB_FIRE_LIGHT_POWER))
update_fire()
return TRUE
return FALSE
/mob/living/proc/ExtinguishMobCompletely()
return ExtinguishMob(fire_stacks)
/mob/living/proc/update_fire()
return
/mob/living/proc/adjust_fire_stacks(var/add_fire_stacks)
fire_stacks = Clamp(fire_stacks + add_fire_stacks, FIRE_MIN_STACKS, FIRE_MAX_STACKS)
return fire_stacks
/mob/living/proc/handle_fire()
if(fire_stacks < 0)
fire_stacks = min(0, ++fire_stacks) //If we've doused ourselves in water to avoid fire, dry off slowly
if(!on_fire)
return 1
else if(fire_stacks <= 0)
ExtinguishMobCompletely() //Fire's been put out.
return 1
var/datum/gas_mixture/G = loc.return_air() // Check if we're standing in an oxygenless environment
if(G.gas[GAS_OXYGEN] < 1)
ExtinguishMobCompletely() //If there's no oxygen in the tile we're on, put out the fire
return 1
var/turf/location = get_turf(src)
location.hotspot_expose(fire_burn_temperature(), 50, 1)
/mob/living/fire_act()
IgniteMob(2)
/mob/living/proc/get_cold_protection()
return 0
/mob/living/proc/get_heat_protection()
return 0
//Finds the effective temperature that the mob is burning at.
/mob/living/proc/fire_burn_temperature()
if (fire_stacks <= 0)
return 0
//Scale quadratically so that single digit numbers of fire stacks don't burn ridiculously hot.
//lower limit of 700 K, same as matches and roughly the temperature of a cool flame.
return max(2.25*round(FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE*(fire_stacks/FIRE_MAX_FIRESUIT_STACKS)**2), 700)
/mob/living/proc/reagent_permeability()
return 1
/mob/living/proc/handle_actions()
//Pretty bad, i'd use picked/dropped instead but the parent calls in these are nonexistent
for(var/datum/action/A in actions)
if(A.CheckRemoval(src))
A.Remove(src)
for(var/obj/item/I in src)
if(I.action_button_name)
if(!I.action)
I.action = new I.default_action_type
I.action.name = I.action_button_name
I.action.SetTarget(I)
I.action.Grant(src)
return
/mob/living/update_action_buttons()
if(!hud_used) return
if(!client) return
if(hud_used.hud_shown != 1) //Hud toggled to minimal
return
client.screen -= hud_used.hide_actions_toggle
for(var/datum/action/A in actions)
if(A.button)
client.screen -= A.button
if(hud_used.action_buttons_hidden)
if(!hud_used.hide_actions_toggle)
hud_used.hide_actions_toggle = new(hud_used)
hud_used.hide_actions_toggle.update_icon()
if(!hud_used.hide_actions_toggle.moved)
hud_used.hide_actions_toggle.screen_loc = hud_used.ButtonNumberToScreenCoords(1)
//hud_used.SetButtonCoords(hud_used.hide_actions_toggle,1)
client.screen += hud_used.hide_actions_toggle
return
var/button_number = 0
for(var/datum/action/A in actions)
button_number++
if(A.button == null)
var/obj/screen/movable/action_button/N = new(hud_used)
N.owner = A
A.button = N
var/obj/screen/movable/action_button/B = A.button
B.update_icon()
B.name = A.UpdateName()
client.screen += B
if(!B.moved)
B.screen_loc = hud_used.ButtonNumberToScreenCoords(button_number)
//hud_used.SetButtonCoords(B,button_number)
if(button_number > 0)
if(!hud_used.hide_actions_toggle)
hud_used.hide_actions_toggle = new(hud_used)
hud_used.hide_actions_toggle.InitialiseIcon(src)
if(!hud_used.hide_actions_toggle.moved)
hud_used.hide_actions_toggle.screen_loc = hud_used.ButtonNumberToScreenCoords(button_number+1)
//hud_used.SetButtonCoords(hud_used.hide_actions_toggle,button_number+1)
client.screen += hud_used.hide_actions_toggle
#undef MOB_FIRE_LIGHT_RANGE //These control the intensity and range of light given off by a mob which is on fire
#undef MOB_FIRE_LIGHT_POWER