diff --git a/code/datums/components/caltrop.dm b/code/datums/components/caltrop.dm index 3f6bba15541b..bdaf9fc1b441 100644 --- a/code/datums/components/caltrop.dm +++ b/code/datums/components/caltrop.dm @@ -78,7 +78,7 @@ if((flags & CALTROP_IGNORE_WALKERS) && digitigrade_fan.move_intent == MOVE_INTENT_WALK) return - if(digitigrade_fan.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) //check if they are able to pass over us + if((digitigrade_fan.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || !QDELETED(digitigrade_fan.throwing)) //check if they are able to pass over us //gravity checking only our parent would prevent us from triggering they're using magboots / other gravity assisting items that would cause them to still touch us. return diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm index 520fa161b69b..525efcaa67ce 100644 --- a/code/datums/components/tackle.dm +++ b/code/datums/components/tackle.dm @@ -488,25 +488,13 @@ var/obj/machinery/vending/darth_vendor = hit darth_vendor.tilt(user, 100) return - else if(istype(hit, /obj/structure/window)) - var/obj/structure/window/W = hit - splatWindow(user, W) - if(QDELETED(W)) - return COMPONENT_MOVABLE_IMPACT_NEVERMIND - return var/oopsie_mod = 0 var/danger_zone = (speed - 1) * 13 // for every extra speed we have over 1, take away 13 of the safest chance danger_zone = max(min(danger_zone, 100), 1) - if(ishuman(user)) - var/mob/living/carbon/human/S = user - var/head_slot = S.get_item_by_slot(ITEM_SLOT_HEAD) - var/suit_slot = S.get_item_by_slot(ITEM_SLOT_OCLOTHING) - if(head_slot && (istype(head_slot,/obj/item/clothing/head/helmet) || istype(head_slot,/obj/item/clothing/head/utility/hardhat))) - oopsie_mod -= 6 - if(suit_slot && (istype(suit_slot,/obj/item/clothing/suit/armor/riot))) - oopsie_mod -= 6 + oopsie_mod -= floor(user.getarmor(BODY_ZONE_HEAD, MELEE) * 0.18) + oopsie_mod -= floor(user.getarmor(BODY_ZONE_CHEST, MELEE) * 0.12) if(HAS_TRAIT(user, TRAIT_CLUMSY)) oopsie_mod += 6 //honk! @@ -597,31 +585,6 @@ QDEL_NULL(tackle_ref) UnregisterSignal(parent, COMSIG_MOVABLE_MOVED) -///A special case for splatting for handling windows -/datum/component/tackler/proc/splatWindow(mob/living/carbon/user, obj/structure/window/W) - playsound(user, 'sound/effects/Glasshit.ogg', 140, TRUE) - - if(W.type in list(/obj/structure/window, /obj/structure/window/fulltile, /obj/structure/window/unanchored, /obj/structure/window/fulltile/unanchored)) // boring unreinforced windows - for(var/i in 1 to speed) - var/obj/item/shard/shard = new /obj/item/shard(get_turf(user)) - shard.embedding = list(embed_chance = 100, ignore_throwspeed_threshold = TRUE, impact_pain_mult=3, pain_chance=5) - shard.updateEmbedding() - user.hitby(shard, skipcatch = TRUE, hitpush = FALSE) - shard.embedding = null - shard.updateEmbedding() - W.atom_destruction() - user.adjustStaminaLoss(10 * speed) - user.Paralyze(3 SECONDS) - user.visible_message(span_danger("[user] smacks into [W] and shatters it, shredding [user.p_them()]self with glass!"), span_userdanger("You smacks into [W] and shatter it, shredding yourself with glass!")) - - else - user.visible_message(span_danger("[user] smacks into [W] like a bug!"), span_userdanger("You smacks into [W] like a bug!")) - user.Paralyze(1 SECONDS) - user.Knockdown(3 SECONDS) - W.take_damage(30 * speed) - user.adjustStaminaLoss(10 * speed, updating_stamina=FALSE) - user.adjustBruteLoss(5 * speed) - /datum/component/tackler/proc/delayedSmash(obj/structure/window/W) if(W) W.atom_destruction() diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index a2989ad1444a..86f7b620e11b 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -1,7 +1,87 @@ /obj/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - ..() - take_damage(AM.throwforce, BRUTE, MELEE, 1, get_dir(src, AM)) + . = ..() + if(QDELETED(src)) + return + hit_by_damage(AM, throwingdatum) + +/obj/proc/hit_by_damage(atom/movable/hitting_us, datum/thrownthing/throwingdatum) + var/base_dam = hitting_us.throwforce + if(isliving(hitting_us)) + var/mob/living/living_mob = hitting_us + var/speed_bonus = throwingdatum.speed - living_mob.throw_speed + if(speed_bonus > 0) + base_dam += (5 * speed_bonus) + base_dam += (5 * max(0, living_mob.mob_size - 1)) + if(isitem(hitting_us)) + var/obj/item/hit_item = hitting_us + base_dam += (5 * max(0, hit_item.w_class - 2)) + + // no armor penetration + take_damage(base_dam, BRUTE, MELEE, TRUE, get_dir(src, hitting_us), 0) + +/obj/structure/window/Initialize(mapload, direct) + . = ..() + // glass will buckle before being pushed around + ADD_TRAIT(src, TRAIT_NO_THROW_HITPUSH, INNATE_TRAIT) + +/obj/structure/window/Cross(atom/movable/crossed_atom) + . = ..() + if(.) + return . + if(!isliving(crossed_atom) || QDELETED(crossed_atom.throwing)) + return . + if(anchored && get_integrity_percentage() > 0.5) + return . + + var/turf/old_loc = loc + + take_damage(INFINITY, BRUTE, MELEE, TRUE, get_dir(src, crossed_atom), 0) + + if(!QDELETED(src)) + return . + + var/mob/living/defenestrated = crossed_atom + var/has_grille = locate(/obj/structure/grille) in old_loc + var/list/obj/item/shards = list() + for(var/obj/item/shard/shard in old_loc) + shards += shard + + for(var/zone in shuffle(BODY_ZONES_ALL)) + var/obj/item/bodypart/part = defenestrated.get_bodypart(zone) + if(!part) + continue + if(has_grille && prob(66)) + continue + + defenestrated.apply_damage(10, BRUTE, part, blocked = min(90, defenestrated.getarmor(part, MELEE)), sharpness = SHARP_POINTY, wound_bonus = 4, bare_wound_bonus = 8, attacking_item = (length(shards) ? shards[1] : "glass")) + if(prob(25 * length(shards)) && shards[1].tryEmbed(part, TRUE)) + shards -= shards[1] + + if(has_grille) + defenestrated.Paralyze(1 SECONDS) + defenestrated.Knockdown(2 SECONDS) + defenestrated.visible_message( + span_danger("[defenestrated] is thrown against [src], shattering it!"), + span_userdanger("You are thrown against [src], shattering it!"), + ) + + else + defenestrated.Paralyze(3 SECONDS) + defenestrated.Knockdown(6 SECONDS) + defenestrated.visible_message( + span_danger("[defenestrated] is thrown clean through [src]!"), + span_userdanger("You are thrown clean through [src]!"), + ) + + return TRUE + +/obj/structure/window/hit_by_damage(atom/movable/hitting_us, datum/thrownthing/throwingdatum) + if(reinf || !isliving(hitting_us)) + return ..() + + // take a lot of damage from being hit with a mob - so we can defenestrate + take_damage(max_integrity * min(0.75, (get_armor_rating(MELEE) / 100)), BRUTE, MELEE, TRUE, get_dir(src, hitting_us), 0) /obj/ex_act(severity, target) if(resistance_flags & INDESTRUCTIBLE) @@ -47,6 +127,19 @@ return damage_sustained > 0 ? BULLET_ACT_HIT : BULLET_ACT_BLOCK +/obj/structure/window/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit) + // don't smack the window and its grille same turf, ever + for(var/obj/structure/grille/grille in loc) + hitting_projectile.impacted[grille] = TRUE + + . = ..() + if(QDELETED(hitting_projectile) || . != BULLET_ACT_HIT) + return . + if(QDELETED(src) && prob(80)) + // right through the window! + return BULLET_ACT_FORCE_PIERCE + return . + /obj/attack_hulk(mob/living/carbon/human/user) ..() if(density) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 1baf2f3b5b41..d990f83bfc8f 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -60,8 +60,10 @@ take_bodypart_damage(5 + 5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5) else if(!iscarbon(hit_atom) && extra_speed) take_bodypart_damage(5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5) - visible_message(span_danger("[src] crashes into [hit_atom][extra_speed ? " really hard" : ""]"),\ - span_userdanger("You violently crash into [hit_atom][extra_speed ? " extra hard" : ""]!")) + visible_message( + span_danger("[src] crashes into [hit_atom][extra_speed ? " really hard" : ""]!"), + span_userdanger("You[extra_speed ? " violently" : ""] crash into [hit_atom][extra_speed ? " extra hard" : ""]!"), + ) log_combat(hit_atom, src, "crashes ") oof_noise = TRUE diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 51e3506d2cf5..bb6c0ab512d1 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -293,23 +293,20 @@ hitx = target.pixel_x + rand(-8, 8) hity = target.pixel_y + rand(-8, 8) - if(damage > 0 && (damage_type == BRUTE || damage_type == BURN) && iswallturf(target_turf) && prob(75)) - var/turf/closed/wall/target_wall = target_turf - if(impact_effect_type && !hitscan) - new impact_effect_type(target_wall, hitx, hity) - - target_wall.add_dent(WALL_DENT_SHOT, hitx, hity) - - return BULLET_ACT_HIT + if((isturf(target) || (isobj(target) && target.density)) && hitsound_wall) + var/volume = clamp(vol_by_damage() + 20, 0, 100) + if(suppressed) + volume = 5 + playsound(loc, hitsound_wall, volume, TRUE, -1) if(!isliving(target)) if(impact_effect_type && !hitscan) new impact_effect_type(target_turf, hitx, hity) - if(isturf(target) && hitsound_wall) - var/volume = clamp(vol_by_damage() + 20, 0, 100) - if(suppressed) - volume = 5 - playsound(loc, hitsound_wall, volume, TRUE, -1) + + if(damage > 0 && (damage_type == BRUTE || damage_type == BURN) && iswallturf(target_turf) && prob(75)) + var/turf/closed/wall/target_wall = target_turf + target_wall.add_dent(WALL_DENT_SHOT, hitx, hity) + return BULLET_ACT_HIT var/mob/living/living_target = target @@ -344,9 +341,7 @@ playsound(loc, hitsound, 5, TRUE, -1) to_chat(living_target, span_userdanger("You're shot by \a [src][organ_hit_text]!")) else - if(hitsound) - var/volume = vol_by_damage() - playsound(src, hitsound, volume, TRUE, -1) + playsound(loc, hitsound, vol_by_damage(), TRUE, -1) living_target.visible_message(span_danger("[living_target] is hit by \a [src][organ_hit_text]!"), \ span_userdanger("You're hit by \a [src][organ_hit_text]!"), null, COMBAT_MESSAGE_RANGE) if(living_target.is_blind()) @@ -596,7 +591,8 @@ var/mob/target_mob = target if(faction_check(target_mob.faction, ignored_factions)) return FALSE - if(target.density || cross_failed) //This thing blocks projectiles, hit it regardless of layer/mob stuns/etc. + // melbert todo upstream this. stops grilles from being hit under windows + if((target.density && !target.IsObscured()) || cross_failed) //This thing blocks projectiles, hit it regardless of layer/mob stuns/etc. return TRUE if(!isliving(target)) if(isturf(target)) // non dense turfs