From a8fe50f4f41c2fa09cdad9fc34d4ce666d8f5f23 Mon Sep 17 00:00:00 2001 From: necromanceranne <40847847+necromanceranne@users.noreply.github.com> Date: Sat, 28 Jun 2025 16:08:09 +1000 Subject: [PATCH] Makes the mech clamp attack do only brute damage, checks for block and armor, and gives it an attack animation and sound. (#91563) ## About The Pull Request If you use the mech clamp offensively, it actually performs an attack animation and sound, and properly checks armor and block. It only deals brute damage. As a consequence of these changes, it actually logs attacks made against mobs that die when gibbed. Also, xenos take x3 damage from the clamp. ## Why It's Good For The Game This is a pretty sinister kind of attack, as it is completely silent except for throwing a warning into chat, and can be done even in a large crowd of people. Someone who isn't aware that the clamp can be used this way may not even understand what is happening before it is too late. > block check While mech melee doesn't normally check block, this is an improvised attack on a non-combat mech. I think it should stay a bit weak compared to a proper mech melee in most ways and have some additional limitations. > Xenos I thought this was already a thing. It's thematically on point, no? ## Changelog :cl: balance: Mech hydraulic clamps perform an attack animation and sound when attacking mobs. balance: Mech hydraulic clamps can be blocked and respect armor. balance: Mech hydraulic clamps do triple damage to xenomorphs. /:cl: (cherry picked from commit fb9824a401e1881d75996e796400048a15e32b37) --- code/__DEFINES/combat.dm | 2 ++ code/game/objects/items/dualsaber.dm | 3 +++ code/game/objects/items/melee/misc.dm | 16 ++++++++-------- code/game/objects/items/shields.dm | 16 ++++++++++++---- code/game/objects/items/weaponry.dm | 4 ++-- code/modules/antagonists/cult/cult_items.dm | 7 ++++++- .../jobs/job_types/chaplain/chaplain_nullrod.dm | 8 ++++---- .../lavaland/mining_loot/cursed_katana.dm | 4 ++-- code/modules/projectiles/guns/magic/staff.dm | 4 ++-- .../mecha/equipment/tools/work_tools.dm | 17 +++++++++++------ 10 files changed, 52 insertions(+), 29 deletions(-) diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 89ff98b5429..0367f1e3029 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -139,6 +139,8 @@ DEFINE_BITFIELD(status_flags, list( #define THROWN_PROJECTILE_ATTACK 4 /// We're being tackled or leaped at. #define LEAP_ATTACK 5 +/// We're being attacked with an oversized object, perhaps a road roller. Not that anyone use such a thing as a waepon. So only relevant for certain mech based attacks. +#define OVERWHELMING_ATTACK 6 /// Used in check block to get what mob is attacking the blocker. #define GET_ASSAILANT(weapon) (get(weapon, /mob/living)) diff --git a/code/game/objects/items/dualsaber.dm b/code/game/objects/items/dualsaber.dm index cb8e43e5715..69a128eefa6 100644 --- a/code/game/objects/items/dualsaber.dm +++ b/code/game/objects/items/dualsaber.dm @@ -170,6 +170,9 @@ if(attack_type == LEAP_ATTACK) final_block_chance -= 50 //We are particularly bad at blocking someone JUMPING at us.. + if(attack_type == OVERWHELMING_ATTACK) + final_block_chance = 0 //Far too small to block these kinds of attacks. + return ..() /obj/item/dualsaber/process() diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index b4450b10f8f..cfd1885e07f 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -119,8 +119,8 @@ INVOKE_ASYNC(baned_target, TYPE_PROC_REF(/mob/living/carbon/human, emote), "scream") /obj/item/melee/sabre/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK) - final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword + if(attack_type == (PROJECTILE_ATTACK || LEAP_ATTACK || OVERWHELMING_ATTACK)) + final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword. Or a road roller, if one happened to hit you. return ..() /obj/item/melee/sabre/on_exit_storage(datum/storage/container) @@ -201,8 +201,8 @@ AddComponent(/datum/component/jousting) /obj/item/melee/parsnip_sabre/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK) - final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword + if(attack_type == (PROJECTILE_ATTACK || LEAP_ATTACK || OVERWHELMING_ATTACK)) + final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword. Or a road roller, if one happened to hit you. return ..() /obj/item/melee/parsnip_sabre/on_exit_storage(datum/storage/container) @@ -237,8 +237,8 @@ block_sound = 'sound/items/weapons/parry.ogg' /obj/item/melee/beesword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK) - final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword + if(attack_type == (PROJECTILE_ATTACK || LEAP_ATTACK || OVERWHELMING_ATTACK)) + final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword. Or a road roller, if one happened to hit you. return ..() /obj/item/melee/beesword/afterattack(atom/target, mob/user, list/modifiers, list/attack_modifiers) @@ -566,6 +566,6 @@ desc = "[initial(desc)] Its handle is made of [material.name]." /obj/item/melee/cleric_mace/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK) - final_block_chance = 0 //Don't bring a...mace to a gunfight, and also you aren't going to really block someone full body tackling you with a mace + if(attack_type == (PROJECTILE_ATTACK || LEAP_ATTACK || OVERWHELMING_ATTACK)) + final_block_chance = 0 //Don't bring a...mace to a gunfight, and also you aren't going to really block someone full body tackling you with a mace. Or a road roller, if one happened to hit you. return ..() diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 76a47fba68b..a2b7642e8dd 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -44,12 +44,16 @@ AddElement(/datum/element/disarm_attack) /obj/item/shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) + var/effective_block_chance = final_block_chance if(transparent && (hitby.pass_flags & PASSGLASS)) return FALSE if(attack_type == THROWN_PROJECTILE_ATTACK) - final_block_chance += 30 + effective_block_chance += 30 if(attack_type == LEAP_ATTACK) - final_block_chance = 100 + effective_block_chance = 100 + if(attack_type == OVERWHELMING_ATTACK) + effective_block_chance -= 25 + final_block_chance = clamp(effective_block_chance, 0, 100) . = ..() if(.) on_shield_block(owner, hitby, attack_text, damage, attack_type, damage_type) @@ -336,12 +340,16 @@ if(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE)) return FALSE + var/effective_block_chance = final_block_chance + if(attack_type == OVERWHELMING_ATTACK) + effective_block_chance -= 25 + if(attack_type == PROJECTILE_ATTACK) var/obj/projectile/our_projectile = hitby if(our_projectile.reflectable) //We handle this via IsReflect() instead. - final_block_chance = 0 - + effective_block_chance = 0 + final_block_chance = clamp(effective_block_chance, 0, 100) return ..() /obj/item/shield/energy/IsReflect() diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index ec8ed1ea802..d11c78f68ba 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -155,8 +155,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 return BRUTELOSS /obj/item/claymore/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK) - final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword + if(attack_type == (PROJECTILE_ATTACK || LEAP_ATTACK || OVERWHELMING_ATTACK)) + final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword. Or a road roller, if one happened to hit you. return ..() //statistically similar to e-cutlasses diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm index 3c8dd7a01fb..56fd1dce01d 100644 --- a/code/modules/antagonists/cult/cult_items.dm +++ b/code/modules/antagonists/cult/cult_items.dm @@ -55,7 +55,7 @@ Striking a noncultist, however, will tear their flesh."} if(owner.get_active_held_item() != src) block_message = "[owner] parries [attack_text] with [src] in their offhand" - if(IS_CULTIST(owner) && prob(final_block_chance) && attack_type != PROJECTILE_ATTACK) + if(IS_CULTIST(owner) && prob(final_block_chance) && attack_type != (PROJECTILE_ATTACK || OVERWHELMING_ATTACK)) new /obj/effect/temp_visual/cult/sparks(get_turf(owner)) owner.visible_message(span_danger("[block_message]")) return TRUE @@ -123,6 +123,9 @@ Striking a noncultist, however, will tear their flesh."} ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT) /obj/item/melee/cultblade/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) + if(attack_type == OVERWHELMING_ATTACK) + return FALSE + if(IS_CULTIST(owner) && prob(final_block_chance)) new /obj/effect/temp_visual/cult/sparks(get_turf(owner)) owner.visible_message(span_danger("[owner] parries [attack_text] with [src]!")) @@ -940,6 +943,8 @@ Striking a noncultist, however, will tear their flesh."} qdel(src) /obj/item/melee/cultblade/halberd/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) + if(attack_type == OVERWHELMING_ATTACK) + return FALSE if(HAS_TRAIT(src, TRAIT_WIELDED)) final_block_chance *= 2 if(IS_CULTIST(owner) && prob(final_block_chance)) diff --git a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm index ff0690ffaed..f48bbcc1e76 100644 --- a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm +++ b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm @@ -142,8 +142,8 @@ means that you'll be forced to move carefully while it's on. Fits in pockets, an AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -3) /obj/item/nullrod/claymore/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK) - final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword + if(attack_type == (PROJECTILE_ATTACK || LEAP_ATTACK || OVERWHELMING_ATTACK)) + final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword. Or a road roller, if one happened to hit you. return ..() /obj/item/nullrod/claymore/darkblade @@ -661,8 +661,8 @@ means that you'll be forced to move carefully while it's on. Fits in pockets, an return ..() /obj/item/nullrod/bostaff/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == PROJECTILE_ATTACK) - final_block_chance = 0 //Don't bring a sword to a gunfight + if(attack_type == (PROJECTILE_ATTACK || LEAP_ATTACK || OVERWHELMING_ATTACK)) + final_block_chance = 0 //Don't bring a stick to a gunfight, and also you aren't going to really block someone full body tackling you with a stick. Or a road roller, if one happened to hit you. return ..() // Arrhythmic Knife - Lets your walk without rhythm by varying your walk speed. Can't be put away. diff --git a/code/modules/mining/lavaland/mining_loot/cursed_katana.dm b/code/modules/mining/lavaland/mining_loot/cursed_katana.dm index 3f267695805..8de94828932 100644 --- a/code/modules/mining/lavaland/mining_loot/cursed_katana.dm +++ b/code/modules/mining/lavaland/mining_loot/cursed_katana.dm @@ -107,8 +107,8 @@ return ..() /obj/item/cursed_katana/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK) - final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword + if(attack_type == (PROJECTILE_ATTACK || LEAP_ATTACK || OVERWHELMING_ATTACK)) + final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword. Or a road roller, if one happened to hit you. return ..() /obj/item/cursed_katana/proc/can_combo_attack(mob/user, mob/living/target) diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index 75cb5957a2d..244b34abcdf 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -264,8 +264,8 @@ ) /obj/item/gun/magic/staff/spellblade/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) - if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK) - final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword + if(attack_type == (PROJECTILE_ATTACK || LEAP_ATTACK || OVERWHELMING_ATTACK)) + final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword. Or a road roller, if one happened to hit you. return ..() /obj/item/gun/magic/staff/locker diff --git a/code/modules/vehicles/mecha/equipment/tools/work_tools.dm b/code/modules/vehicles/mecha/equipment/tools/work_tools.dm index 651ccf999bb..44598a84f6a 100644 --- a/code/modules/vehicles/mecha/equipment/tools/work_tools.dm +++ b/code/modules/vehicles/mecha/equipment/tools/work_tools.dm @@ -94,7 +94,7 @@ var/mob/living/victim = target if(victim.stat == DEAD) - return + return ..() if(!source.combat_mode) step_away(victim, chassis) @@ -107,6 +107,11 @@ span_notice("[chassis] pushes you aside.")) return ..() + if(victim.check_block(chassis, clamp_damage, name, attack_type = OVERWHELMING_ATTACK)) + source.visible_message(span_danger("[chassis] attempts to squeeze [victim] with [src], but the [name] is blocked!"), span_userdanger("You attempt to squeeze [victim] with [src], but [victim.p_They()] managed to block the attempt!"), ignored_mobs = victim) + to_chat(victim, span_userdanger("You block [chassis]'s attempt to squeeze you with [src]!")) + return ..() + if(iscarbon(victim) && killer_clamp)//meme clamp here var/mob/living/carbon/carbon_victim = target var/torn_off = FALSE @@ -124,15 +129,15 @@ span_userdanger("[chassis] rips your arms off!")) log_combat(source, carbon_victim, "removed both arms with a real clamp,", "[name]", "(COMBAT MODE: [uppertext(source.combat_mode)] (DAMTYPE: [uppertext(damtype)])") return ..() - - victim.take_overall_damage(clamp_damage) - if(isnull(victim)) //get gibbed stoopid - return ..() - victim.adjustOxyLoss(round(clamp_damage/2)) + var/armor_check = clamp(victim.run_armor_check(null, MELEE) / 3, 0, 100) //our target only benefits from a third of their armor. Because it's a huge ass clamp victim.visible_message(span_danger("[chassis] squeezes [victim]!"), \ span_userdanger("[chassis] squeezes you!"),\ span_hear("You hear something crack.")) log_combat(source, victim, "attacked", "[name]", "(Combat mode: [source.combat_mode ? "On" : "Off"]) (DAMTYPE: [uppertext(damtype)])") + var/final_damage = isalien(victim) ? clamp_damage * 3 : clamp_damage + chassis.do_attack_animation(victim) + playsound(chassis, clampsound, 30, FALSE, -6) + victim.apply_damage(final_damage, BRUTE, blocked = armor_check, spread_damage = TRUE) return ..() //This is pretty much just for the death-ripley