Files
Bubberstation/code/game/objects/obj_defense.dm
san7890 1b5c0489a4 ex_act() will work on basic mobs again (lol) + Unit Test (#74953)
basically ex_act's implementation on basic mobs would call parent and
then react to it's value, this is presumably to do the first check about
space vine mutations and whatever. the problem is that the `/mob/living`
implementation would itself also call parent, and that would always
return null because `/atom/proc/ex_act` doesn't have a set return value.
So, this simply would _always_ early return, with ex_act presumably
*never* working on basic mobs for at least four months now.

I decided to then change up the return values for pretty much all
implementations of `ex_act()` since there was no rhyme or reason to
returning null/FALSE/TRUE, and documenting why it's like that.

Just to make sure I wasn't breaking anything doing this (at least on
base implementations), I wrote a unit test for all of the three major
physical types in game (objs, mobs, turfs) because i am a paranoid
fuckar. we should be good to go now though.
## Why It's Good For The Game

i noticed this because placing c4's on sargeant araneus wouldn't
actually damage it whatsoever. now it actually does the stated 30
damage, but araneus has like 250 health so it doesn't actually matter in
the long run. whatever at least it does the damn 30 now.

also adds a unit test for this specific case as well as a range of other
cases to ensure this stuff doesn't silently break in this way anymore
2023-05-03 14:56:46 +00:00

167 lines
6.0 KiB
Plaintext

/obj/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
..()
take_damage(AM.throwforce, BRUTE, MELEE, 1, get_dir(src, AM))
/obj/ex_act(severity, target)
if(resistance_flags & INDESTRUCTIBLE)
return FALSE
. = ..() //contents explosion
if(QDELETED(src))
return TRUE
if(target == src)
take_damage(INFINITY, BRUTE, BOMB, 0)
return TRUE
switch(severity)
if(EXPLODE_DEVASTATE)
take_damage(INFINITY, BRUTE, BOMB, 0)
if(EXPLODE_HEAVY)
take_damage(rand(100, 250), BRUTE, BOMB, 0)
if(EXPLODE_LIGHT)
take_damage(rand(10, 90), BRUTE, BOMB, 0)
return TRUE
/obj/bullet_act(obj/projectile/P)
. = ..()
playsound(src, P.hitsound, 50, TRUE)
var/damage
if(!QDELETED(src)) //Bullet on_hit effect might have already destroyed this object
damage = take_damage(P.damage, P.damage_type, P.armor_flag, 0, turn(P.dir, 180), P.armour_penetration)
if(P.suppressed != SUPPRESSED_VERY)
visible_message(span_danger("[src] is hit by \a [P][damage ? "" : ", without leaving a mark"]!"), null, null, COMBAT_MESSAGE_RANGE)
/obj/attack_hulk(mob/living/carbon/human/user)
..()
if(density)
playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE)
else
playsound(src, 'sound/effects/bang.ogg', 50, TRUE)
var/damage = take_damage(hulk_damage(), BRUTE, MELEE, 0, get_dir(src, user))
user.visible_message(span_danger("[user] smashes [src][damage ? "" : ", without leaving a mark"]!"), span_danger("You smash [src][damage ? "" : ", without leaving a mark"]!"), null, COMBAT_MESSAGE_RANGE)
return TRUE
/obj/blob_act(obj/structure/blob/B)
if (!..())
return
if(isturf(loc))
var/turf/T = loc
if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && HAS_TRAIT(src, TRAIT_T_RAY_VISIBLE))
return
take_damage(400, BRUTE, MELEE, 0, get_dir(src, B))
/obj/attack_alien(mob/living/carbon/alien/adult/user, list/modifiers)
if(attack_generic(user, 60, BRUTE, MELEE, 0))
playsound(src.loc, 'sound/weapons/slash.ogg', 100, TRUE)
/obj/attack_animal(mob/living/simple_animal/user, list/modifiers)
. = ..()
if(!user.melee_damage_upper && !user.obj_damage)
user.emote("custom", message = "[user.friendly_verb_continuous] [src].")
return FALSE
else
var/turf/current_turf = get_turf(src) //we want to save the turf to play the sound there, cause being destroyed deletes us!
var/play_soundeffect = user.environment_smash
if(user.obj_damage)
. = attack_generic(user, user.obj_damage, user.melee_damage_type, MELEE, play_soundeffect, user.armour_penetration)
else
. = attack_generic(user, rand(user.melee_damage_lower,user.melee_damage_upper), user.melee_damage_type, MELEE, play_soundeffect, user.armour_penetration)
if(. && play_soundeffect)
playsound(current_turf, 'sound/effects/meteorimpact.ogg', 100, TRUE)
if(user.client)
log_combat(user, src, "attacked")
/obj/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
return TRUE
/obj/move_crushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
collision_damage(pusher, force, direction)
return TRUE
/obj/proc/collision_damage(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
var/amt = max(0, ((force - (move_resist * MOVE_FORCE_CRUSH_RATIO)) / (move_resist * MOVE_FORCE_CRUSH_RATIO)) * 10)
take_damage(amt, BRUTE)
/obj/attack_slime(mob/living/simple_animal/slime/user, list/modifiers)
if(!user.is_adult)
return
if(attack_generic(user, rand(10, 15), BRUTE, MELEE, 1))
log_combat(user, src, "attacked")
/obj/singularity_act()
SSexplosions.high_mov_atom += src
if(src && !QDELETED(src))
qdel(src)
return 2
///// ACID
///the obj's reaction when touched by acid
/obj/acid_act(acidpwr, acid_volume)
. = ..()
if((resistance_flags & UNACIDABLE) || (acid_volume <= 0) || (acidpwr <= 0))
return FALSE
AddComponent(/datum/component/acid, acidpwr, acid_volume, custom_acid_overlay || GLOB.acid_overlay)
return TRUE
///called when the obj is destroyed by acid.
/obj/proc/acid_melt()
deconstruct(FALSE)
//// FIRE
///Called when the obj is exposed to fire.
/obj/fire_act(exposed_temperature, exposed_volume)
if(isturf(loc))
var/turf/our_turf = loc
if(our_turf.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && HAS_TRAIT(src, TRAIT_T_RAY_VISIBLE))
return
if(exposed_temperature && !(resistance_flags & FIRE_PROOF))
take_damage(clamp(0.02 * exposed_temperature, 0, 20), BURN, FIRE, 0)
if(!(resistance_flags & ON_FIRE) && (resistance_flags & FLAMMABLE) && !(resistance_flags & FIRE_PROOF))
AddComponent(/datum/component/burning, custom_fire_overlay || GLOB.fire_overlay, burning_particles)
return TRUE
return ..()
/// Should be called when the atom is destroyed by fire, comparable to acid_melt() proc
/obj/proc/burn()
deconstruct(FALSE)
///Called when the obj is hit by a tesla bolt.
/obj/zap_act(power, zap_flags)
if(QDELETED(src))
return 0
ADD_TRAIT(src, TRAIT_BEING_SHOCKED, WAS_SHOCKED)
addtimer(TRAIT_CALLBACK_REMOVE(src, TRAIT_BEING_SHOCKED, WAS_SHOCKED), 1 SECONDS)
return power / 2
//The surgeon general warns that being buckled to certain objects receiving powerful shocks is greatly hazardous to your health
///Only tesla coils, vehicles, and grounding rods currently call this because mobs are already targeted over all other objects, but this might be useful for more things later.
/obj/proc/zap_buckle_check(strength)
if(has_buckled_mobs())
for(var/m in buckled_mobs)
var/mob/living/buckled_mob = m
buckled_mob.electrocute_act((clamp(round(strength/400), 10, 90) + rand(-5, 5)), src, flags = SHOCK_TESLA)
///the obj is deconstructed into pieces, whether through careful disassembly or when destroyed.
/obj/proc/deconstruct(disassembled = TRUE)
SEND_SIGNAL(src, COMSIG_OBJ_DECONSTRUCT, disassembled)
qdel(src)
///what happens when the obj's integrity reaches zero.
/obj/atom_destruction(damage_flag)
. = ..()
if(damage_flag == ACID)
acid_melt()
else if(damage_flag == FIRE)
burn()
else
deconstruct(FALSE)
///returns how much the object blocks an explosion. Used by subtypes.
/obj/proc/GetExplosionBlock()
CRASH("Unimplemented GetExplosionBlock()")