diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index c12c9b57713..19c87f3b4e0 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -370,3 +370,18 @@ GLOBAL_LIST_INIT(arm_zones, list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
#define COMBO_STEPS "steps"
/// The proc the combo calls
#define COMBO_PROC "proc"
+
+///Checks If the target can be moved at all by shoving them
+#define SHOVE_CAN_MOVE (1<<0)
+///If the target can be shoved into something something with perhaps special interactions.
+#define SHOVE_CAN_HIT_SOMETHING (1<<1)
+///Keeps knockdowns at bay for the target
+#define SHOVE_KNOCKDOWN_BLOCKED (1<<2)
+///If the target can be briefly paralized by shoving them once again after knocking them down.
+#define SHOVE_CAN_KICK_SIDE (1<<3)
+///Whether the staggered status effect can be applied on the target
+#define SHOVE_CAN_STAGGER (1<<4)
+///If the target could move, but didn't because there's an obstacle in the path.
+#define SHOVE_BLOCKED (1<<5)
+///If the obstacle is an object at the border of the turf (so no signal from being sent to the other turf)
+#define SHOVE_DIRECTIONAL_BLOCKED (1<<6)
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
index fe7673ee3ca..8b73010b8f9 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
@@ -13,13 +13,6 @@
///Called from /mob/living/carbon/help_shake_act on the helper, after any hugs have ocurred. (mob/living/helped)
#define COMSIG_CARBON_HELPED "carbon_helped_someone"
-///Before a carbon mob is shoved, sent to the turf we're trying to shove onto (mob/living/carbon/shover, mob/living/carbon/target)
-#define COMSIG_CARBON_DISARM_PRESHOVE "carbon_disarm_preshove"
- #define COMSIG_CARBON_ACT_SOLID (1<<0) //Tells disarm code to act as if the mob was shoved into something solid, even we we're not
-///When a carbon mob is disarmed, this is sent to the turf we're trying to shove onto (mob/living/carbon/shover, mob/living/carbon/target, shove_blocked)
-#define COMSIG_CARBON_DISARM_COLLIDE "carbon_disarm_collision"
- #define COMSIG_CARBON_SHOVE_HANDLED (1<<0)
-
///When a carbon slips. Called on /turf/open/handle_slip()
#define COMSIG_ON_CARBON_SLIP "carbon_slip"
// /mob/living/carbon physiology signals
@@ -125,8 +118,6 @@
///Applied preferences to a human
#define COMSIG_HUMAN_PREFS_APPLIED "human_prefs_applied"
-///Hit by successful disarm attack (mob/living/carbon/human/attacker,zone_targeted)
-#define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit"
///Whenever EquipRanked is called, called after job is set
#define COMSIG_JOB_RECEIVED "job_received"
///from /mob/living/carbon/human/proc/set_coretemperature(): (oldvalue, newvalue)
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
index b1838793e98..7ac77efcaa7 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
@@ -228,6 +228,15 @@
#define COMSIG_LIVING_CHECK_BLOCK "living_check_block"
#define SUCCESSFUL_BLOCK (1<<0)
+///Hit by successful disarm attack (mob/living/attacker, zone_targeted, item/weapon)
+#define COMSIG_LIVING_DISARM_HIT "living_disarm_hit"
+///Before a living mob is shoved, sent to the turf we're trying to shove onto (mob/living/shover, mob/living/target)
+#define COMSIG_LIVING_DISARM_PRESHOVE "living_disarm_preshove"
+ #define COMSIG_LIVING_ACT_SOLID (1<<0) //Tells disarm code to act as if the mob was shoved into something solid, even we we're not
+///When a living mob is disarmed, this is sent to the turf we're trying to shove onto (mob/living/shover, mob/living/target, shove_blocked)
+#define COMSIG_LIVING_DISARM_COLLIDE "living_disarm_collision"
+ #define COMSIG_LIVING_SHOVE_HANDLED (1<<0)
+
/// Sent on a mob from /datum/component/mob_chain when component is attached with it as the "front" : (mob/living/basic/tail)
#define COMSIG_MOB_GAINED_CHAIN_TAIL "living_gained_chain_tail"
/// Sent on a mob from /datum/component/mob_chain when component is detached from it as the "front" : (mob/living/basic/tail)
diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm
index b3149b82719..25efe87272f 100644
--- a/code/__DEFINES/dcs/signals/signals_object.dm
+++ b/code/__DEFINES/dcs/signals/signals_object.dm
@@ -444,7 +444,7 @@
#define COMPONENT_SECONDARY_CONTINUE_ATTACK_CHAIN (1<<1)
#define COMPONENT_SECONDARY_CALL_NORMAL_ATTACK_CHAIN (1<<2)
/// From base of [/obj/item/proc/attack_secondary()]: (atom/target, mob/user, params)
-#define COMSIG_ITEM_ATTACK_SECONDARY "item_pre_attack_secondary"
+#define COMSIG_ITEM_ATTACK_SECONDARY "item_attack_secondary"
///from base of obj/item/afterattack(): (atom/target, mob/user, proximity_flag, click_parameters)
#define COMSIG_ITEM_AFTERATTACK "item_afterattack"
/// Flag for when /afterattack potentially acts on an item.
@@ -462,6 +462,10 @@
/// from base of obj/item/failedEmbed()
#define COMSIG_ITEM_FAILED_EMBED "item_failed_embed"
+/// from base of datum/element/disarm_attack/secondary_attack(), used to prevent shoving: (victim, user, send_message)
+#define COMSIG_ITEM_CAN_DISARM_ATTACK "item_pre_disarm_attack"
+ #define COMPONENT_BLOCK_ITEM_DISARM_ATTACK (1<<0)
+
///from /obj/item/assembly/proc/pulsed(mob/pulser)
#define COMSIG_ASSEMBLY_PULSED "assembly_pulsed"
diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm
index 9427328fcc2..8ace303b843 100644
--- a/code/__DEFINES/obj_flags.dm
+++ b/code/__DEFINES/obj_flags.dm
@@ -68,28 +68,26 @@
#define VOICEBOX_TOGGLABLE (1<<6)
/// The voicebox is currently turned off.
#define VOICEBOX_DISABLED (1<<7)
-/// Prevents shovies against a dense object from knocking the wearer down.
-#define BLOCKS_SHOVE_KNOCKDOWN (1<<8)
/// Prevents knock-off from things like hat-throwing.
-#define SNUG_FIT (1<<9)
+#define SNUG_FIT (1<<8)
/// Hats with negative effects when worn (i.e the tinfoil hat).
-#define ANTI_TINFOIL_MANEUVER (1<<10)
+#define ANTI_TINFOIL_MANEUVER (1<<9)
/// Clothes that cause a larger notification when placed on a person.
-#define DANGEROUS_OBJECT (1<<11)
+#define DANGEROUS_OBJECT (1<<10)
/// Clothes that use large icons, for applying the proper overlays like blood
-#define LARGE_WORN_ICON (1<<12)
+#define LARGE_WORN_ICON (1<<11)
/// Clothes that block speech (i.e the muzzle). Can be applied to any clothing piece.
-#define BLOCKS_SPEECH (1<<13)
+#define BLOCKS_SPEECH (1<<12)
/// prevents from placing on plasmaman helmet or modsuit hat holder
-#define STACKABLE_HELMET_EXEMPT (1<<14)
+#define STACKABLE_HELMET_EXEMPT (1<<13)
/// Prevents plasmamen from igniting when wearing this
-#define PLASMAMAN_PREVENT_IGNITION (1<<15)
+#define PLASMAMAN_PREVENT_IGNITION (1<<14)
/// Usable as casting clothes by wizards (matters for suits, glasses and headwear)
-#define CASTING_CLOTHES (1<<16)
+#define CASTING_CLOTHES (1<<15)
///Moths can't eat the clothing that has this flag.
-#define INEDIBLE_CLOTHING (1<<17)
+#define INEDIBLE_CLOTHING (1<<16)
/// Headgear/helmet allows internals
-#define HEADINTERNALS (1<<18)
+#define HEADINTERNALS (1<<17)
/// Integrity defines for clothing (not flags but close enough)
#define CLOTHING_PRISTINE 0 // We have no damage on the clothing
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index af171751f33..132f8d91487 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -211,6 +211,12 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_BLOOD_DEFICIENCY "blood_deficiency"
#define TRAIT_JOLLY "jolly"
#define TRAIT_NOCRITDAMAGE "no_crit"
+/// Prevents shovies against a dense object from knocking them down.
+#define TRAIT_SHOVE_KNOCKDOWN_BLOCKED "shove_knockdown_blocked"
+/// Prevents staggering.
+#define TRAIT_NO_STAGGER "no_stagger"
+/// Getting hit by thrown movables won't push you away
+#define TRAIT_NO_THROW_HITPUSH "no_throw_hitpush"
///Added to mob or mind, changes the icons of the fish shown in the minigame UI depending on the possible reward.
#define TRAIT_REVEAL_FISH "reveal_fish"
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index 89c0b4e1c4d..ccfffecd7eb 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -83,7 +83,6 @@ DEFINE_BITFIELD(car_traits, list(
DEFINE_BITFIELD(clothing_flags, list(
"ANTI_TINFOIL_MANEUVER" = ANTI_TINFOIL_MANEUVER,
- "BLOCKS_SHOVE_KNOCKDOWN" = BLOCKS_SHOVE_KNOCKDOWN,
"BLOCKS_SPEECH" = BLOCKS_SPEECH,
"BLOCK_GAS_SMOKE_EFFECT" = BLOCK_GAS_SMOKE_EFFECT,
"CASTING_CLOTHES" = CASTING_CLOTHES,
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 98275fe8231..5eda73a12f4 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -52,6 +52,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NO_FLOATING_ANIM" = TRAIT_NO_FLOATING_ANIM,
"TRAIT_NO_MANIFEST_CONTENTS_ERROR" = TRAIT_NO_MANIFEST_CONTENTS_ERROR,
"TRAIT_NO_MISSING_ITEM_ERROR" = TRAIT_NO_MISSING_ITEM_ERROR,
+ "TRAIT_NO_THROW_HITPUSH" = TRAIT_NO_THROW_HITPUSH,
"TRAIT_NOT_ENGRAVABLE" = TRAIT_NOT_ENGRAVABLE,
"TRAIT_ODD_CUSTOMIZABLE_FOOD_INGREDIENT" = TRAIT_ODD_CUSTOMIZABLE_FOOD_INGREDIENT,
"TRAIT_RUNECHAT_HIDDEN" = TRAIT_RUNECHAT_HIDDEN,
@@ -328,6 +329,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NO_SLIP_SLIDE" = TRAIT_NO_SLIP_SLIDE,
"TRAIT_NO_SLIP_WATER" = TRAIT_NO_SLIP_WATER,
"TRAIT_NO_SOUL" = TRAIT_NO_SOUL,
+ "TRAIT_NO_STAGGER" = TRAIT_NO_STAGGER,
"TRAIT_NO_STRIP" = TRAIT_NO_STRIP,
"TRAIT_NO_TRANSFORM" = TRAIT_NO_TRANSFORM,
"TRAIT_NO_TWOHANDING" = TRAIT_NO_TWOHANDING,
@@ -393,6 +395,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_SHAVED" = TRAIT_SHAVED,
"TRAIT_SHIFTY_EYES" = TRAIT_SHIFTY_EYES,
"TRAIT_SHOCKIMMUNE" = TRAIT_SHOCKIMMUNE,
+ "TRAIT_SHOVE_KNOCKDOWN_BLOCKED" = TRAIT_SHOVE_KNOCKDOWN_BLOCKED,
"TRAIT_SIGN_LANG" = TRAIT_SIGN_LANG,
"TRAIT_SILENT_FOOTSTEPS" = TRAIT_SILENT_FOOTSTEPS,
"TRAIT_SIXTHSENSE" = TRAIT_SIXTHSENSE,
diff --git a/code/datums/components/gunpoint.dm b/code/datums/components/gunpoint.dm
index cb8dfb1ec8d..ed4142f41ce 100644
--- a/code/datums/components/gunpoint.dm
+++ b/code/datums/components/gunpoint.dm
@@ -45,7 +45,7 @@
COMSIG_LIVING_START_PULL), PROC_REF(trigger_reaction))
RegisterSignal(targ, COMSIG_ATOM_EXAMINE, PROC_REF(examine_target))
RegisterSignal(targ, COMSIG_LIVING_PRE_MOB_BUMP, PROC_REF(block_bumps_target))
- RegisterSignals(targ, list(COMSIG_HUMAN_DISARM_HIT, COMSIG_LIVING_GET_PULLED), PROC_REF(cancel))
+ RegisterSignals(targ, list(COMSIG_LIVING_DISARM_HIT, COMSIG_LIVING_GET_PULLED), PROC_REF(cancel))
RegisterSignals(weapon, list(COMSIG_ITEM_DROPPED, COMSIG_ITEM_EQUIPPED), PROC_REF(cancel))
var/distance = min(get_dist(shooter, target), 1) // treat 0 distance as adjacent
@@ -88,7 +88,7 @@
RegisterSignals(parent, list(COMSIG_LIVING_START_PULL, COMSIG_MOVABLE_BUMP), PROC_REF(check_bump))
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(examine))
RegisterSignal(parent, COMSIG_LIVING_PRE_MOB_BUMP, PROC_REF(block_bumps_parent))
- RegisterSignal(parent, COMSIG_HUMAN_DISARM_HIT, PROC_REF(cancel))
+ RegisterSignal(parent, COMSIG_LIVING_DISARM_HIT, PROC_REF(cancel))
/datum/component/gunpoint/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_MOVABLE_MOVED)
@@ -98,7 +98,7 @@
UnregisterSignal(parent, list(COMSIG_LIVING_START_PULL, COMSIG_MOVABLE_BUMP))
UnregisterSignal(parent, COMSIG_ATOM_EXAMINE)
UnregisterSignal(parent, COMSIG_LIVING_PRE_MOB_BUMP)
- UnregisterSignal(parent, COMSIG_HUMAN_DISARM_HIT)
+ UnregisterSignal(parent, COMSIG_LIVING_DISARM_HIT)
///If the shooter bumps the target, cancel the holdup to avoid cheesing and forcing the charged shot
/datum/component/gunpoint/proc/check_bump(atom/B, atom/A)
diff --git a/code/datums/components/knockoff.dm b/code/datums/components/knockoff.dm
index b5563433c72..db72072789c 100644
--- a/code/datums/components/knockoff.dm
+++ b/code/datums/components/knockoff.dm
@@ -29,11 +29,11 @@
var/obj/item/item_parent = parent
if(ismob(item_parent.loc))
- UnregisterSignal(item_parent.loc, list(COMSIG_HUMAN_DISARM_HIT, COMSIG_LIVING_STATUS_KNOCKDOWN))
+ UnregisterSignal(item_parent.loc, list(COMSIG_LIVING_DISARM_HIT, COMSIG_LIVING_STATUS_KNOCKDOWN))
-/// Signal proc for [COMSIG_HUMAN_DISARM_HIT] on the mob who's equipped our parent
+/// Signal proc for [COMSIG_LIVING_DISARM_HIT] on the mob who's equipped our parent
/// Rolls a chance for knockoff whenever we're disarmed
-/datum/component/knockoff/proc/on_equipped_mob_disarm(mob/living/carbon/human/source, mob/living/attacker, zone)
+/datum/component/knockoff/proc/on_equipped_mob_disarm(mob/living/source, mob/living/attacker, zone, obj/item/weapon)
SIGNAL_HANDLER
if(!istype(source))
@@ -85,10 +85,10 @@
return
if(slots_knockoffable && !(slot & slots_knockoffable))
- UnregisterSignal(equipper, list(COMSIG_HUMAN_DISARM_HIT, COMSIG_LIVING_STATUS_KNOCKDOWN))
+ UnregisterSignal(equipper, list(COMSIG_LIVING_DISARM_HIT, COMSIG_LIVING_STATUS_KNOCKDOWN))
return
- RegisterSignal(equipper, COMSIG_HUMAN_DISARM_HIT, PROC_REF(on_equipped_mob_disarm), TRUE)
+ RegisterSignal(equipper, COMSIG_LIVING_DISARM_HIT, PROC_REF(on_equipped_mob_disarm), TRUE)
RegisterSignal(equipper, COMSIG_LIVING_STATUS_KNOCKDOWN, PROC_REF(on_equipped_mob_knockdown), TRUE)
/// Signal proc for [COMSIG_ITEM_DROPPED]
@@ -96,4 +96,4 @@
/datum/component/knockoff/proc/on_dropped(datum/source, mob/living/dropper)
SIGNAL_HANDLER
- UnregisterSignal(dropper, list(COMSIG_HUMAN_DISARM_HIT, COMSIG_LIVING_STATUS_KNOCKDOWN))
+ UnregisterSignal(dropper, list(COMSIG_LIVING_DISARM_HIT, COMSIG_LIVING_STATUS_KNOCKDOWN))
diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm
index 9f8bb9227e0..520fa161b69 100644
--- a/code/datums/components/tackle.dm
+++ b/code/datums/components/tackle.dm
@@ -400,7 +400,7 @@
defense_mod += 2
if(tackle_target.mob_negates_gravity())
defense_mod += 1
- if(tackle_target.is_shove_knockdown_blocked()) // riot armor and such
+ if(HAS_TRAIT(tackle_target, TRAIT_SHOVE_KNOCKDOWN_BLOCKED)) // riot armor and such
defense_mod += 5
var/obj/item/organ/external/tail/lizard/el_tail = tackle_target.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL)
@@ -451,7 +451,7 @@
attack_mod += 15
human_sacker.adjustStaminaLoss(100) //AHAHAHAHAHAHAHAHA
- if(human_sacker.is_shove_knockdown_blocked()) // tackling with riot specialized armor, like riot armor, is effective but tiring
+ if(HAS_TRAIT(human_sacker, TRAIT_SHOVE_KNOCKDOWN_BLOCKED)) // tackling with riot specialized armor, like riot armor, is effective but tiring
attack_mod += 2
human_sacker.adjustStaminaLoss(20)
diff --git a/code/datums/elements/disarm_attack.dm b/code/datums/elements/disarm_attack.dm
new file mode 100644
index 00000000000..a788cd9f35e
--- /dev/null
+++ b/code/datums/elements/disarm_attack.dm
@@ -0,0 +1,35 @@
+///An element that allows items to be used to shove people around just like right-clicking would.
+/datum/element/disarm_attack
+
+/datum/element/disarm_attack/Attach(datum/target)
+ . = ..()
+ if(!isitem(target))
+ return ELEMENT_INCOMPATIBLE
+
+ RegisterSignal(target, COMSIG_ITEM_ATTACK_SECONDARY, PROC_REF(secondary_attack))
+ RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(examine))
+
+/datum/element/disarm_attack/Detach(datum/source)
+ UnregisterSignal(source, list(COMSIG_ATOM_EXAMINE, COMSIG_ITEM_ATTACK_SECONDARY))
+ return ..()
+
+/datum/element/disarm_attack/proc/secondary_attack(obj/item/source, mob/living/victim, mob/living/user, params)
+ SIGNAL_HANDLER
+ if(!user.can_disarm(victim) || !can_disarm_attack(source, victim, user))
+ return COMPONENT_SECONDARY_CANCEL_ATTACK_CHAIN
+ if(victim.check_block(source, 0, "the [source.name]", MELEE_ATTACK, 0))
+ return COMPONENT_SECONDARY_CANCEL_ATTACK_CHAIN
+ user.disarm(victim, source)
+ user.changeNext_move(source.secondary_attack_speed || source.attack_speed)
+ return COMPONENT_SECONDARY_CANCEL_ATTACK_CHAIN
+
+///check if the item conditions for the disarm action are met.
+/datum/element/disarm_attack/proc/can_disarm_attack(obj/item/source, mob/living/victim, mob/living/user, message = TRUE)
+ if(SEND_SIGNAL(source, COMSIG_ITEM_CAN_DISARM_ATTACK, victim, user, message) & COMPONENT_BLOCK_ITEM_DISARM_ATTACK)
+ return FALSE
+ return TRUE
+
+/datum/element/disarm_attack/proc/examine(obj/item/source, mob/user, list/examine_list)
+ SIGNAL_HANDLER
+ if(can_disarm_attack(source, user, user, FALSE))
+ examine_list += span_notice("You can use it to shove people with right-click.")
diff --git a/code/datums/status_effects/debuffs/staggered.dm b/code/datums/status_effects/debuffs/staggered.dm
index 0ffde66b1f1..89cf4943c19 100644
--- a/code/datums/status_effects/debuffs/staggered.dm
+++ b/code/datums/status_effects/debuffs/staggered.dm
@@ -10,7 +10,7 @@
/datum/status_effect/staggered/on_apply()
//you can't stagger the dead.
- if(owner.stat == DEAD)
+ if(owner.stat == DEAD || HAS_TRAIT(owner, TRAIT_NO_STAGGER))
return FALSE
RegisterSignal(owner, COMSIG_LIVING_DEATH, PROC_REF(clear_staggered))
@@ -31,10 +31,12 @@
/datum/status_effect/staggered/tick(seconds_between_ticks)
//you can't stagger the dead - in case somehow you die mid-stagger
- if(owner.stat == DEAD || HAS_TRAIT(owner, TRAIT_FAKEDEATH))
+ if(owner.stat == DEAD || HAS_TRAIT(owner, TRAIT_NO_STAGGER))
+ qdel(src)
return
- else
- owner.do_stagger_animation()
+ if(HAS_TRAIT(owner, TRAIT_FAKEDEATH))
+ return
+ owner.do_stagger_animation()
/// Helper proc that causes the mob to do a stagger animation.
/// Doesn't change significantly, just meant to represent swaying back and forth
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 0f21f9c5fd0..47c0ecdfa54 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -1255,9 +1255,11 @@
return hit_atom.hitby(src, throwingdatum=throwingdatum, hitpush=hitpush)
/atom/movable/hitby(atom/movable/hitting_atom, skipcatch, hitpush = TRUE, blocked, datum/thrownthing/throwingdatum)
+ if(HAS_TRAIT(src, TRAIT_NO_THROW_HITPUSH))
+ hitpush = FALSE
if(!anchored && hitpush && (!throwingdatum || (throwingdatum.force >= (move_resist * MOVE_FORCE_PUSH_RATIO))))
step(src, hitting_atom.dir)
- ..()
+ return ..()
/atom/movable/proc/safe_throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE)
if((force < (move_resist * MOVE_FORCE_THROW_RATIO)) || (move_resist == INFINITY))
diff --git a/code/game/objects/items/pillow.dm b/code/game/objects/items/pillow.dm
index 5364905bdc9..928f1a373bb 100644
--- a/code/game/objects/items/pillow.dm
+++ b/code/game/objects/items/pillow.dm
@@ -31,6 +31,7 @@
force_unwielded = 5, \
force_wielded = 10, \
)
+ AddElement(/datum/element/disarm_attack)
var/static/list/slapcraft_recipe_list = list(\
/datum/crafting_recipe/pillow_suit, /datum/crafting_recipe/pillow_hood,\
diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm
index ef434c1993d..b711bb63d45 100644
--- a/code/game/objects/items/shields.dm
+++ b/code/game/objects/items/shields.dm
@@ -35,6 +35,10 @@
fire = 80
acid = 70
+/obj/item/shield/Initialize(mapload)
+ . = ..()
+ 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)
if(transparent && (hitby.pass_flags & PASSGLASS))
return FALSE
@@ -57,21 +61,32 @@
if(0 to 25)
. += span_warning("It's falling apart!")
-/obj/item/shield/proc/shatter(mob/living/carbon/human/owner)
- playsound(owner, shield_break_sound, 50)
- new shield_break_leftover(get_turf(src))
-
/obj/item/shield/proc/on_shield_block(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
if(!breakable_by_damage || (damage_type != BRUTE && damage_type != BURN))
return TRUE
- if (atom_integrity <= damage)
- var/turf/owner_turf = get_turf(owner)
- owner_turf.visible_message(span_warning("[hitby] destroys [src]!"))
- shatter(owner)
- qdel(src)
- return FALSE
- take_damage(damage)
- return TRUE
+ var/penetration = 0
+ var/armor_flag = MELEE
+ if(isprojectile(hitby))
+ var/obj/projectile/bang_bang = hitby
+ armor_flag = bang_bang.armor_flag
+ penetration = bang_bang.armour_penetration
+ else if(isitem(hitby))
+ var/obj/item/weapon = hitby
+ penetration = weapon.armour_penetration
+ else if(isanimal(hitby))
+ var/mob/living/simple_animal/critter = hitby
+ penetration = critter.armour_penetration
+ else if(isbasicmob(hitby))
+ var/mob/living/basic/critter = hitby
+ penetration = critter.armour_penetration
+ take_damage(damage, damage_type, armor_flag, armour_penetration = penetration)
+
+/obj/item/shield/atom_destruction(damage_flag)
+ playsound(src, shield_break_sound, 50)
+ new shield_break_leftover(get_turf(src))
+ if(isliving(loc))
+ loc.balloon_alert(loc, "shield broken!")
+ return ..()
/obj/item/shield/buckler
name = "wooden buckler"
@@ -100,9 +115,14 @@
armor_type = /datum/armor/none
max_integrity = 30
+/datum/armor/item_shield/riot
+ melee = 80
+ bullet = 20
+ laser = 20
+
/obj/item/shield/riot
name = "riot shield"
- desc = "A shield adept at blocking blunt objects from connecting with the torso of the shield wielder."
+ desc = "A shield adept at blocking blunt objects from connecting with the torso of the shield wielder, less so bullets and laser beams."
icon_state = "riot"
inhand_icon_state = "riot"
custom_materials = list(/datum/material/glass= SHEET_MATERIAL_AMOUNT * 3.75, /datum/material/iron= HALF_SHEET_MATERIAL_AMOUNT)
@@ -110,6 +130,7 @@
max_integrity = 75
shield_break_sound = 'sound/effects/glassbr3.ogg'
shield_break_leftover = /obj/item/shard
+ armor_type = /datum/armor/item_shield/riot
/obj/item/shield/riot/Initialize(mapload)
. = ..()
@@ -177,7 +198,9 @@
QDEL_NULL(embedded_flash)
return ..()
-/obj/item/shield/riot/flash/attack(mob/living/target_mob, mob/user)
+/obj/item/shield/riot/flash/attack(mob/living/target_mob, mob/living/user)
+ if(user.combat_mode)
+ return ..()
flash_away(user, target_mob)
/obj/item/shield/riot/flash/attack_self(mob/living/carbon/user)
@@ -275,6 +298,7 @@
clumsy_check = !can_clumsy_use, \
)
RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform))
+ RegisterSignal(src, COMSIG_ITEM_CAN_DISARM_ATTACK, PROC_REF(can_disarm_attack))
/obj/item/shield/energy/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(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE))
@@ -302,6 +326,13 @@
playsound(src, active ? 'sound/weapons/saberon.ogg' : 'sound/weapons/saberoff.ogg', 35, TRUE)
return COMPONENT_NO_DEFAULT_MESSAGE
+/obj/item/shield/energy/proc/can_disarm_attack(datum/source, mob/living/victim, mob/living/user, send_message = TRUE)
+ SIGNAL_HANDLER
+ if(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE))
+ if(send_message)
+ balloon_alert(user, "activate it first!")
+ return COMPONENT_BLOCK_ITEM_DISARM_ATTACK
+
/obj/item/shield/energy/advanced
name = "advanced combat energy shield"
desc = "A hardlight shield capable of reflecting all energy projectiles, as well las providing well-rounded defense from most all other attacks. \
@@ -338,6 +369,7 @@
)
RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform))
+ RegisterSignal(src, COMSIG_ITEM_CAN_DISARM_ATTACK, PROC_REF(can_disarm_attack))
/obj/item/shield/riot/tele/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(HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE))
@@ -358,4 +390,11 @@
playsound(src, 'sound/weapons/batonextend.ogg', 50, TRUE)
return COMPONENT_NO_DEFAULT_MESSAGE
+/obj/item/shield/riot/tele/proc/can_disarm_attack(datum/source, mob/living/victim, mob/living/user, send_message = TRUE)
+ SIGNAL_HANDLER
+ if(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE))
+ if(send_message)
+ balloon_alert(user, "extend it first!")
+ return COMPONENT_BLOCK_ITEM_DISARM_ATTACK
+
#undef BATON_BASH_COOLDOWN
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index d00c2becbe2..b3d3c7085bd 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -145,7 +145,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
populate_contents_immediate()
var/static/list/loc_connections = list(
- COMSIG_CARBON_DISARM_COLLIDE = PROC_REF(locker_carbon),
+ COMSIG_LIVING_DISARM_COLLIDE = PROC_REF(locker_living),
COMSIG_ATOM_MAGICALLY_UNLOCKED = PROC_REF(on_magic_unlock),
)
AddElement(/datum/element/connect_loc, loc_connections)
@@ -1160,11 +1160,11 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
/obj/structure/closet/return_temperature()
return
-/obj/structure/closet/proc/locker_carbon(datum/source, mob/living/carbon/shover, mob/living/carbon/target, shove_blocked)
+/obj/structure/closet/proc/locker_living(datum/source, mob/living/shover, mob/living/target, shove_flags, obj/item/weapon)
SIGNAL_HANDLER
if(!opened && (locked || welded)) //Yes this could be less code, no I don't care
return
- if(!opened && !shove_blocked)
+ if(!opened && ((shove_flags & SHOVE_KNOCKDOWN_BLOCKED) || !(shove_flags & SHOVE_BLOCKED)))
return
var/was_opened = opened
if(!toggle())
@@ -1174,18 +1174,12 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
else
target.Knockdown(SHOVE_KNOCKDOWN_SOLID)
update_icon()
- if(target == shover)
- target.visible_message(span_danger("[target.name] shoves [target.p_them()]self into [src]!"),
- null,
- span_hear("You hear shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, shover)
- to_chat(shover, span_notice("You shove yourself into [src]!"))
- else
- target.visible_message(span_danger("[shover.name] shoves [target.name] into [src]!"),
- span_userdanger("You're shoved into [src] by [shover.name]!"),
- span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, shover)
- to_chat(src, span_danger("You shove [target.name] into [src]!"))
- log_combat(shover, target, "shoved", "into [src] (locker/crate)")
- return COMSIG_CARBON_SHOVE_HANDLED
+ target.visible_message(span_danger("[shover.name] shoves [target.name] into [src]!"),
+ span_userdanger("You're shoved into [src] by [shover.name]!"),
+ span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, shover)
+ to_chat(src, span_danger("You shove [target.name] into [src]!"))
+ log_combat(shover, target, "shoved", "into [src] (locker/crate)[weapon ? " with [weapon]" : ""]")
+ return COMSIG_LIVING_SHOVE_HANDLED
/// Signal proc for [COMSIG_ATOM_MAGICALLY_UNLOCKED]. Unlock and open up when we get knock casted.
/obj/structure/closet/proc/on_magic_unlock(datum/source, datum/action/cooldown/spell/aoe/knock/spell, atom/caster)
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index f9178684768..9131607c6c4 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -47,7 +47,7 @@
make_climbable()
var/static/list/loc_connections = list(
- COMSIG_CARBON_DISARM_COLLIDE = PROC_REF(table_carbon),
+ COMSIG_LIVING_DISARM_COLLIDE = PROC_REF(table_living),
)
AddElement(/datum/element/connect_loc, loc_connections)
@@ -323,17 +323,17 @@
return TRUE
return FALSE
-/obj/structure/table/proc/table_carbon(datum/source, mob/living/carbon/shover, mob/living/carbon/target, shove_blocked)
+/obj/structure/table/proc/table_living(datum/source, mob/living/shover, mob/living/target, shove_flags, obj/item/weapon)
SIGNAL_HANDLER
- if(!shove_blocked)
+ if((shove_flags & SHOVE_KNOCKDOWN_BLOCKED) || !(shove_flags & SHOVE_BLOCKED))
return
target.Knockdown(SHOVE_KNOCKDOWN_TABLE)
target.visible_message(span_danger("[shover.name] shoves [target.name] onto \the [src]!"),
- span_userdanger("You're shoved onto \the [src] by [shover.name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
+ span_userdanger("You're shoved onto \the [src] by [shover.name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, shover)
to_chat(shover, span_danger("You shove [target.name] onto \the [src]!"))
target.throw_at(src, 1, 1, null, FALSE) //1 speed throws with no spin are basically just forcemoves with a hard collision check
- log_combat(shover, target, "shoved", "onto [src] (table)")
- return COMSIG_CARBON_SHOVE_HANDLED
+ log_combat(shover, target, "shoved", "onto [src] (table)[weapon ? " with [weapon]" : ""]")
+ return COMSIG_LIVING_SHOVE_HANDLED
/obj/structure/table/greyscale
icon = 'icons/obj/smooth_structures/table_greyscale.dmi'
diff --git a/code/game/turfs/closed/walls.dm b/code/game/turfs/closed/walls.dm
index e660e95b72e..02e04557f42 100644
--- a/code/game/turfs/closed/walls.dm
+++ b/code/game/turfs/closed/walls.dm
@@ -72,7 +72,7 @@
)
RegisterSignals(src, list(
COMSIG_MOB_CLIENT_PRE_MOVE,
- COMSIG_HUMAN_DISARM_HIT,
+ COMSIG_LIVING_DISARM_HIT,
COMSIG_LIVING_GET_PULLED,
COMSIG_MOVABLE_TELEPORTING,
COMSIG_ATOM_DIR_CHANGE,
@@ -83,7 +83,7 @@
SIGNAL_HANDLER
UnregisterSignal(src, list(
COMSIG_MOB_CLIENT_PRE_MOVE,
- COMSIG_HUMAN_DISARM_HIT,
+ COMSIG_LIVING_DISARM_HIT,
COMSIG_LIVING_GET_PULLED,
COMSIG_MOVABLE_TELEPORTING,
COMSIG_ATOM_DIR_CHANGE,
diff --git a/code/modules/basketball/basketball.dm b/code/modules/basketball/basketball.dm
index 9fe176715db..77c3214fe56 100644
--- a/code/modules/basketball/basketball.dm
+++ b/code/modules/basketball/basketball.dm
@@ -48,7 +48,7 @@
RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(movement_effect))
RegisterSignal(user, COMSIG_MOB_EMOTED("spin"), PROC_REF(on_spin))
- RegisterSignal(user, COMSIG_HUMAN_DISARM_HIT, PROC_REF(on_equipped_mob_disarm))
+ RegisterSignal(user, COMSIG_LIVING_DISARM_HIT, PROC_REF(on_equipped_mob_disarm))
RegisterSignal(user, COMSIG_LIVING_STATUS_KNOCKDOWN, PROC_REF(on_equipped_mob_knockdown))
/obj/item/toy/basketball/proc/remove_ball_effects()
@@ -57,7 +57,7 @@
// unlike on_equip, this signal is triggered after the ball is removed from hands
// so we can just use is_holding_item_of_type() proc to check for multiple balls
if(!wielder.is_holding_item_of_type(/obj/item/toy/basketball))
- UnregisterSignal(wielder, list(COMSIG_MOVABLE_MOVED, COMSIG_MOB_EMOTED("spin"), COMSIG_HUMAN_DISARM_HIT, COMSIG_LIVING_STATUS_KNOCKDOWN, COMSIG_MOB_THROW))
+ UnregisterSignal(wielder, list(COMSIG_MOVABLE_MOVED, COMSIG_MOB_EMOTED("spin"), COMSIG_LIVING_DISARM_HIT, COMSIG_LIVING_STATUS_KNOCKDOWN, COMSIG_MOB_THROW))
wielder = null
@@ -99,12 +99,9 @@
/// Used to calculate our disarm chance based on stamina, direction, and spinning
/// Note - monkeys use attack_paw() and never trigger this signal (so they always have 100% disarm)
-/obj/item/toy/basketball/proc/on_equipped_mob_disarm(mob/living/baller, mob/living/stealer, zone)
+/obj/item/toy/basketball/proc/on_equipped_mob_disarm(mob/living/baller, mob/living/stealer, zone, obj/item/weapon)
SIGNAL_HANDLER
- if(!istype(baller))
- return
-
// spinning gives you a lower disarm chance but it drains stamina
var/disarm_chance = HAS_TRAIT(baller, TRAIT_SPINNING) ? 35 : 50
// ballers stamina results in lower disarm, stealer stamina results in higher disarm
diff --git a/code/modules/cargo/markets/market_items/misc.dm b/code/modules/cargo/markets/market_items/misc.dm
index d2b70704e7a..0953a73e0ac 100644
--- a/code/modules/cargo/markets/market_items/misc.dm
+++ b/code/modules/cargo/markets/market_items/misc.dm
@@ -50,6 +50,14 @@
stock_max = 2
availability_prob = 30
+/datum/market_item/misc/shove_blocker
+ name = "MOD Bulwark Module"
+ desc = "You have no idea how much effort it took us to extract this module from that damn safeguard MODsuit last shift."
+ price_min = CARGO_CRATE_VALUE * 4
+ price_max = CARGO_CRATE_VALUE * 5.75
+ stock_max = 1
+ availability_prob = 25
+
/datum/market_item/misc/holywater
name = "Flask of holy water"
desc = "Father Lootius' own brand of ready-made holy water."
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index df9db74b88b..ef10536341e 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -280,9 +280,9 @@
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
armor_type = /datum/armor/armor_riot
- clothing_flags = BLOCKS_SHOVE_KNOCKDOWN
strip_delay = 80
equip_delay_other = 60
+ clothing_traits = list(TRAIT_SHOVE_KNOCKDOWN_BLOCKED)
/datum/armor/armor_riot
melee = 50
@@ -379,13 +379,15 @@
armor_type = /datum/armor/armor_swat
strip_delay = 120
resistance_flags = FIRE_PROOF | ACID_PROOF
- clothing_flags = BLOCKS_SHOVE_KNOCKDOWN | THICKMATERIAL
+ clothing_flags = THICKMATERIAL
cold_protection = CHEST | GROIN | LEGS | FEET | ARMS | HANDS
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT_OFF
heat_protection = CHEST | GROIN | LEGS | FEET | ARMS | HANDS
max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
slowdown = 0.7
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
+ clothing_traits = list(TRAIT_SHOVE_KNOCKDOWN_BLOCKED)
+
//All of the armor below is mostly unused
diff --git a/code/modules/mob/living/basic/basic_defense.dm b/code/modules/mob/living/basic/basic_defense.dm
index 69370ed49be..646cabe339d 100644
--- a/code/modules/mob/living/basic/basic_defense.dm
+++ b/code/modules/mob/living/basic/basic_defense.dm
@@ -4,31 +4,7 @@
return TRUE
if(LAZYACCESS(modifiers, RIGHT_CLICK))
- if(user.move_force < move_resist)
- return
- user.do_attack_animation(src, ATTACK_EFFECT_DISARM)
- playsound(src, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
- var/shove_dir = get_dir(user, src)
- if(!Move(get_step(src, shove_dir), shove_dir))
- log_combat(user, src, "shoved", "failing to move it")
- user.visible_message(
- span_danger("[user.name] [response_disarm_continuous] [src]!"),
- span_danger("You [response_disarm_simple] [src]!"),
- span_hear("You hear aggressive shuffling!"),
- COMBAT_MESSAGE_RANGE,
- list(src),
- )
- to_chat(src, span_userdanger("You're shoved by [user.name]!"))
- return TRUE
- log_combat(user, src, "shoved", "pushing it")
- user.visible_message(
- span_danger("[user.name] [response_disarm_continuous] [src], pushing [p_them()]!"),
- span_danger("You [response_disarm_simple] [src], pushing [p_them()]!"),
- span_hear("You hear aggressive shuffling!"),
- COMBAT_MESSAGE_RANGE,
- list(src),
- )
- to_chat(src, span_userdanger("You're pushed by [user.name]!"))
+ user.disarm(src)
return TRUE
if(!user.combat_mode)
@@ -63,6 +39,19 @@
updatehealth()
return TRUE
+/mob/living/basic/get_shoving_message(mob/living/shover, obj/item/weapon, shove_flags)
+ if(weapon) // no "gently pushing aside" if you're pressing a shield at them.
+ return ..()
+ var/moved = !(shove_flags & SHOVE_BLOCKED)
+ shover.visible_message(
+ span_danger("[shover.name] [response_disarm_continuous] [src][moved ? ", pushing [p_them()]" : ""]!"),
+ span_danger("You [response_disarm_simple] [src][moved ? ", pushing [p_them()]" : ""]!"),
+ span_hear("You hear aggressive shuffling!"),
+ COMBAT_MESSAGE_RANGE,
+ list(src),
+ )
+ to_chat(src, span_userdanger("You're [moved ? "pushed" : "shoved"] by [shover.name]!"))
+
/mob/living/basic/attack_hulk(mob/living/carbon/human/user)
. = ..()
if(!.)
diff --git a/code/modules/mob/living/carbon/alien/adult/adult_defense.dm b/code/modules/mob/living/carbon/alien/adult/adult_defense.dm
index b4a119d5aba..d89f6de30d6 100644
--- a/code/modules/mob/living/carbon/alien/adult/adult_defense.dm
+++ b/code/modules/mob/living/carbon/alien/adult/adult_defense.dm
@@ -15,11 +15,8 @@
to_chat(user, span_danger("You [hitverb] [src]!"))
/mob/living/carbon/alien/adult/attack_hand(mob/living/carbon/human/user, list/modifiers)
- if(!..() || !user.combat_mode)
- return
- if(LAZYACCESS(modifiers, RIGHT_CLICK))
- if(stat < UNCONSCIOUS)
- user.disarm(src)
+ . = ..()
+ if(.)
return TRUE
var/damage = rand(1, 9)
if (prob(90))
@@ -41,7 +38,6 @@
span_danger("You avoid [user]'s punch!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_warning("Your punch misses [src]!"))
-
/mob/living/carbon/alien/adult/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
if(!no_effect && !visual_effect_icon)
visual_effect_icon = ATTACK_EFFECT_CLAW
diff --git a/code/modules/mob/living/carbon/alien/adult/queen.dm b/code/modules/mob/living/carbon/alien/adult/queen.dm
index f2093738eac..1a08d2446d2 100644
--- a/code/modules/mob/living/carbon/alien/adult/queen.dm
+++ b/code/modules/mob/living/carbon/alien/adult/queen.dm
@@ -18,6 +18,8 @@
. = ..()
// as a wise man once wrote: "pull over that ass too fat"
REMOVE_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
+ // that'd be a too cheeky shield bashing strat
+ ADD_TRAIT(src, TRAIT_SHOVE_KNOCKDOWN_BLOCKED, INNATE_TRAIT)
AddComponent(/datum/component/seethrough_mob)
/mob/living/carbon/alien/adult/royal/on_lying_down(new_lying_angle)
diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm
index 9388dcea560..3717cfcb89a 100644
--- a/code/modules/mob/living/carbon/alien/alien_defense.dm
+++ b/code/modules/mob/living/carbon/alien/alien_defense.dm
@@ -44,22 +44,22 @@ In all, this is a lot like the monkey code. /N
/mob/living/carbon/alien/attack_hand(mob/living/carbon/human/user, list/modifiers)
. = ..()
- if(.) //to allow surgery to return properly.
- return FALSE
-
- var/martial_result = user.apply_martial_art(src, modifiers)
- if (martial_result != MARTIAL_ATTACK_INVALID)
- return martial_result
-
- if(user.combat_mode)
- if(LAZYACCESS(modifiers, RIGHT_CLICK))
- user.do_attack_animation(src, ATTACK_EFFECT_DISARM)
- return TRUE
- user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
+ if(.)
return TRUE
+
+ if(LAZYACCESS(modifiers, RIGHT_CLICK))
+ user.disarm(src)
+ return TRUE
+ if(user.combat_mode)
+ user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
else
help_shake_act(user)
+ return TRUE
+/mob/living/carbon/alien/get_shove_flags(mob/living/shover, obj/item/weapon)
+ . = ..()
+ if(isnull(weapon) || stat != CONSCIOUS)
+ . &= ~(SHOVE_CAN_MOVE|SHOVE_CAN_HIT_SOMETHING|SHOVE_CAN_STAGGER)
/mob/living/carbon/alien/attack_paw(mob/living/carbon/human/user, list/modifiers)
if(..())
diff --git a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
index 3fa78271d6f..62dd4f88b21 100644
--- a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
+++ b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
@@ -1,24 +1,26 @@
/mob/living/carbon/alien/larva/attack_hand(mob/living/carbon/human/user, list/modifiers)
- if(..())
- var/damage = rand(1, 9)
- if (prob(90))
- playsound(loc, SFX_PUNCH, 25, TRUE, -1)
- log_combat(user, src, "attacked")
- visible_message(span_danger("[user] kicks [src]!"), \
- span_userdanger("[user] kicks you!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, user)
- to_chat(user, span_danger("You kick [src]!"))
- if ((stat != DEAD) && (damage > 4.9))
- Unconscious(rand(100,200))
+ . = ..()
+ if(.)
+ return TRUE
+ var/damage = rand(1, 9)
+ if (prob(90))
+ playsound(loc, SFX_PUNCH, 25, TRUE, -1)
+ log_combat(user, src, "attacked")
+ visible_message(span_danger("[user] kicks [src]!"), \
+ span_userdanger("[user] kicks you!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, user)
+ to_chat(user, span_danger("You kick [src]!"))
+ if ((stat != DEAD) && (damage > 4.9))
+ Unconscious(rand(100,200))
- var/obj/item/bodypart/affecting = get_bodypart(get_random_valid_zone(user.zone_selected))
- apply_damage(damage, BRUTE, affecting)
- else
- playsound(loc, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
- visible_message(span_danger("[user]'s kick misses [src]!"), \
- span_danger("You avoid [user]'s kick!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, user)
- to_chat(user, span_warning("Your kick misses [src]!"))
+ var/obj/item/bodypart/affecting = get_bodypart(get_random_valid_zone(user.zone_selected))
+ apply_damage(damage, BRUTE, affecting)
+ else
+ playsound(loc, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
+ visible_message(span_danger("[user]'s kick misses [src]!"), \
+ span_danger("You avoid [user]'s kick!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, user)
+ to_chat(user, span_warning("Your kick misses [src]!"))
/mob/living/carbon/alien/larva/attack_hulk(mob/living/carbon/human/user)
. = ..()
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index f4e7a6aa766..0c261c9f156 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -5,11 +5,6 @@
register_context()
GLOB.carbon_list += src
- var/static/list/loc_connections = list(
- COMSIG_CARBON_DISARM_PRESHOVE = PROC_REF(disarm_precollide),
- COMSIG_CARBON_DISARM_COLLIDE = PROC_REF(disarm_collision),
- )
- AddElement(/datum/element/connect_loc, loc_connections)
ADD_TRAIT(src, TRAIT_CAN_HOLD_ITEMS, INNATE_TRAIT) // Carbons are assumed to be innately capable of having arms, we check their arms count instead
/mob/living/carbon/Destroy()
@@ -1377,24 +1372,6 @@
if(mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD))
AddComponent(/datum/component/rot, 6 MINUTES, 10 MINUTES, 1)
-/mob/living/carbon/proc/disarm_precollide(datum/source, mob/living/carbon/shover, mob/living/carbon/target)
- SIGNAL_HANDLER
- if(can_be_shoved_into)
- return COMSIG_CARBON_ACT_SOLID
-
-/mob/living/carbon/proc/disarm_collision(datum/source, mob/living/carbon/shover, mob/living/carbon/target, shove_blocked)
- SIGNAL_HANDLER
- if(src == target || LAZYFIND(target.buckled_mobs, src) || !can_be_shoved_into)
- return
- target.Knockdown(SHOVE_KNOCKDOWN_HUMAN)
- if(!is_shove_knockdown_blocked())
- Knockdown(SHOVE_KNOCKDOWN_COLLATERAL)
- target.visible_message(span_danger("[shover] shoves [target.name] into [name]!"),
- span_userdanger("You're shoved into [name] by [shover]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
- to_chat(src, span_danger("You shove [target.name] into [name]!"))
- log_combat(shover, target, "shoved", addition = "into [name]")
- return COMSIG_CARBON_SHOVE_HANDLED
-
/**
* This proc is used to determine whether or not the mob can handle touching an acid affected object.
*/
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index a2faacb9a94..01d45b0686d 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -148,10 +148,9 @@
/mob/living/carbon/attack_drone_secondary(mob/living/basic/drone/user)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-//ATTACK HAND IGNORING PARENT RETURN VALUE
/mob/living/carbon/attack_hand(mob/living/carbon/human/user, list/modifiers)
- if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_HAND, user, modifiers) & COMPONENT_CANCEL_ATTACK_CHAIN)
- . = TRUE
+ . = ..()
+
for(var/thing in diseases)
var/datum/disease/D = thing
if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)
@@ -162,13 +161,8 @@
if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)
ContactContractDisease(D)
- for(var/datum/surgery/operations as anything in surgeries)
- if(user.combat_mode)
- break
- if(body_position != LYING_DOWN && (operations.surgery_flags & SURGERY_REQUIRE_RESTING))
- continue
- if(operations.next_step(user, modifiers))
- return TRUE
+ if(.)
+ return TRUE
for(var/datum/wound/wounds as anything in all_wounds)
if(wounds.try_handling(user))
@@ -234,97 +228,6 @@
return dam_zone
-/**
- * Attempt to disarm the target mob.
- * Will shove the target mob back, and drop them if they're in front of something dense
- * or another carbon.
-*/
-/mob/living/carbon/proc/disarm(mob/living/carbon/target)
- do_attack_animation(target, ATTACK_EFFECT_DISARM)
- playsound(target, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
- if (ishuman(target))
- var/mob/living/carbon/human/human_target = target
- human_target.w_uniform?.add_fingerprint(src)
-
- SEND_SIGNAL(target, COMSIG_HUMAN_DISARM_HIT, src, zone_selected)
- var/shove_dir = get_dir(loc, target.loc)
- var/turf/target_shove_turf = get_step(target.loc, shove_dir)
- var/shove_blocked = FALSE //Used to check if a shove is blocked so that if it is knockdown logic can be applied
- var/turf/target_old_turf = target.loc
-
- //Are we hitting anything? or
- if(SEND_SIGNAL(target_shove_turf, COMSIG_CARBON_DISARM_PRESHOVE) & COMSIG_CARBON_ACT_SOLID)
- shove_blocked = TRUE
- else
- target.Move(target_shove_turf, shove_dir)
- if(get_turf(target) == target_old_turf)
- shove_blocked = TRUE
-
- if(!shove_blocked)
- target.setGrabState(GRAB_PASSIVE)
-
- if(target.IsKnockdown() && !target.IsParalyzed()) //KICK HIM IN THE NUTS
- target.Paralyze(SHOVE_CHAIN_PARALYZE)
- target.visible_message(span_danger("[name] kicks [target.name] onto [target.p_their()] side!"),
- span_userdanger("You're kicked onto your side by [name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
- to_chat(src, span_danger("You kick [target.name] onto [target.p_their()] side!"))
- addtimer(CALLBACK(target, TYPE_PROC_REF(/mob/living, SetKnockdown), 0), SHOVE_CHAIN_PARALYZE)
- log_combat(src, target, "kicks", "onto their side (paralyzing)")
-
- var/directional_blocked = FALSE
- var/can_hit_something = (!target.is_shove_knockdown_blocked() && !target.buckled)
-
- //Directional checks to make sure that we're not shoving through a windoor or something like that
- if(shove_blocked && can_hit_something && (shove_dir in GLOB.cardinals))
- var/target_turf = get_turf(target)
- for(var/obj/obj_content in target_turf)
- if(obj_content.flags_1 & ON_BORDER_1 && obj_content.dir == shove_dir && obj_content.density)
- directional_blocked = TRUE
- break
- if(target_turf != target_shove_turf && !directional_blocked) //Make sure that we don't run the exact same check twice on the same tile
- for(var/obj/obj_content in target_shove_turf)
- if(obj_content.flags_1 & ON_BORDER_1 && obj_content.dir == REVERSE_DIR(shove_dir) && obj_content.density)
- directional_blocked = TRUE
- break
-
- if(can_hit_something)
- //Don't hit people through windows, ok?
- if(!directional_blocked && SEND_SIGNAL(target_shove_turf, COMSIG_CARBON_DISARM_COLLIDE, src, target, shove_blocked) & COMSIG_CARBON_SHOVE_HANDLED)
- return
- if(directional_blocked || shove_blocked)
- target.Knockdown(SHOVE_KNOCKDOWN_SOLID)
- target.visible_message(span_danger("[name] shoves [target.name], knocking [target.p_them()] down!"),
- span_userdanger("You're knocked down from a shove by [name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
- to_chat(src, span_danger("You shove [target.name], knocking [target.p_them()] down!"))
- log_combat(src, target, "shoved", "knocking them down")
- return
-
- target.visible_message(span_danger("[name] shoves [target.name]!"),
- span_userdanger("You're shoved by [name]!"), span_hear("You hear aggressive shuffling!"), COMBAT_MESSAGE_RANGE, src)
- to_chat(src, span_danger("You shove [target.name]!"))
-
- //Take their lunch money
- var/target_held_item = target.get_active_held_item()
- var/append_message = ""
- if(!is_type_in_typecache(target_held_item, GLOB.shove_disarming_types)) //It's too expensive we'll get caught
- target_held_item = null
-
- if(target_held_item && target.get_timed_status_effect_duration(/datum/status_effect/staggered))
- target.dropItemToGround(target_held_item)
- append_message = "causing [target.p_them()] to drop [target_held_item]"
- target.visible_message(span_danger("[target.name] drops \the [target_held_item]!"),
- span_warning("You drop \the [target_held_item]!"), null, COMBAT_MESSAGE_RANGE)
-
- target.adjust_staggered_up_to(STAGGERED_SLOWDOWN_LENGTH, 10 SECONDS)
-
- log_combat(src, target, "shoved", append_message)
-
-/mob/living/carbon/proc/is_shove_knockdown_blocked() //If you want to add more things that block shove knockdown, extend this
- for (var/obj/item/clothing/clothing in get_equipped_items())
- if(clothing.clothing_flags & BLOCKS_SHOVE_KNOCKDOWN)
- return TRUE
- return FALSE
-
/mob/living/carbon/blob_act(obj/structure/blob/B)
if (stat == DEAD)
return
@@ -798,4 +701,10 @@
organs -= organ_type
GLOB.bioscrambler_valid_organs = organs
+/mob/living/carbon/get_shove_flags(mob/living/shover, obj/item/weapon)
+ . = ..()
+ . |= SHOVE_CAN_STAGGER
+ if(IsKnockdown() && !IsParalyzed())
+ . |= SHOVE_CAN_KICK_SIDE
+
#undef SHAKE_ANIMATION_OFFSET
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 4d71c556736..4791c818e1a 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -29,6 +29,8 @@
AddElement(/datum/element/strippable, GLOB.strippable_human_items, TYPE_PROC_REF(/mob/living/carbon/human/, should_strip))
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
+ COMSIG_LIVING_DISARM_PRESHOVE = PROC_REF(disarm_precollide),
+ COMSIG_LIVING_DISARM_COLLIDE = PROC_REF(disarm_collision),
)
AddElement(/datum/element/connect_loc, loc_connections)
GLOB.human_list += src
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 094a812a0ab..5dfe6a7a4fa 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -129,12 +129,31 @@
apply_damage(15, BRUTE, wound_bonus=10)
/mob/living/carbon/human/attack_hand(mob/user, list/modifiers)
- if(..()) //to allow surgery to return properly.
- return
+ . = ..()
+ if(.)
+ return TRUE
if(ishuman(user))
var/mob/living/carbon/human/H = user
dna.species.spec_attack_hand(H, src, null, modifiers)
+/mob/living/carbon/human/proc/disarm_precollide(datum/source, mob/living/shover, mob/living/target, obj/item/weapon)
+ SIGNAL_HANDLER
+ return COMSIG_LIVING_ACT_SOLID
+
+/mob/living/carbon/human/proc/disarm_collision(datum/source, mob/living/shover, mob/living/target, shove_flags, obj/item/weapon)
+ SIGNAL_HANDLER
+ if(src == target || LAZYFIND(target.buckled_mobs, src) || !iscarbon(target))
+ return
+ if(!(shove_flags & SHOVE_KNOCKDOWN_BLOCKED))
+ target.Knockdown(SHOVE_KNOCKDOWN_HUMAN)
+ if(!HAS_TRAIT(src, TRAIT_SHOVE_KNOCKDOWN_BLOCKED))
+ Knockdown(SHOVE_KNOCKDOWN_COLLATERAL)
+ target.visible_message(span_danger("[shover] shoves [target.name] into [name]!"),
+ span_userdanger("You're shoved into [name] by [shover]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
+ to_chat(src, span_danger("You shove [target.name] into [name]!"))
+ log_combat(shover, target, "shoved", addition = "into [name][weapon ? " with [weapon]" : ""]")
+ return COMSIG_LIVING_SHOVE_HANDLED
+
/mob/living/carbon/human/attack_paw(mob/living/carbon/human/user, list/modifiers)
var/dam_zone = pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
var/obj/item/bodypart/affecting = get_bodypart(get_random_valid_zone(dam_zone))
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 17f8cdad23a..01c2ed409ff 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -371,9 +371,8 @@
return martial_result
if(LAZYACCESS(modifiers, RIGHT_CLICK))
- if (user != src && iscarbon(src))
- user.disarm(src)
- return TRUE
+ user.disarm(src)
+ return TRUE
if (!user.combat_mode)
return FALSE
if(HAS_TRAIT(user, TRAIT_PACIFISM))
@@ -615,6 +614,116 @@
ricocheting_projectile.set_angle(new_angle_s)
return TRUE
+/**
+ * Attempt to disarm the target mob. Some items might let you do it, also carbon can do it with right click.
+ * Will shove the target mob back, and drop them if they're in front of something dense
+ * or another carbon.
+*/
+/mob/living/proc/disarm(mob/living/target, obj/item/weapon)
+ if(!can_disarm(target))
+ return
+ var/shove_flags = target.get_shove_flags(src, weapon)
+ if(weapon)
+ do_attack_animation(target, used_item = weapon)
+ playsound(target, 'sound/effects/glassbash.ogg', 50, TRUE, -1)
+ else
+ do_attack_animation(target, ATTACK_EFFECT_DISARM)
+ playsound(target, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+ if (ishuman(target) && isnull(weapon))
+ var/mob/living/carbon/human/human_target = target
+ human_target.w_uniform?.add_fingerprint(src)
+
+ SEND_SIGNAL(target, COMSIG_LIVING_DISARM_HIT, src, zone_selected, weapon)
+ var/shove_dir = get_dir(loc, target.loc)
+ var/turf/target_shove_turf = get_step(target.loc, shove_dir)
+ var/turf/target_old_turf = target.loc
+
+ //Are we hitting anything? or
+ if(shove_flags & SHOVE_CAN_MOVE)
+ if(SEND_SIGNAL(target_shove_turf, COMSIG_LIVING_DISARM_PRESHOVE, src, target, weapon) & COMSIG_LIVING_ACT_SOLID)
+ shove_flags |= SHOVE_BLOCKED
+ else
+ target.Move(target_shove_turf, shove_dir)
+ if(get_turf(target) == target_old_turf)
+ shove_flags |= SHOVE_BLOCKED
+
+ if(!(shove_flags & SHOVE_BLOCKED))
+ target.setGrabState(GRAB_PASSIVE)
+
+ //Directional checks to make sure that we're not shoving through a windoor or something like that
+ if((shove_flags & SHOVE_BLOCKED) && (shove_dir in GLOB.cardinals))
+ var/target_turf = get_turf(target)
+ for(var/obj/obj_content in target_turf)
+ if(obj_content.flags_1 & ON_BORDER_1 && obj_content.dir == shove_dir && obj_content.density)
+ shove_flags |= SHOVE_DIRECTIONAL_BLOCKED
+ break
+ if(target_turf != target_shove_turf && !(shove_flags && SHOVE_DIRECTIONAL_BLOCKED)) //Make sure that we don't run the exact same check twice on the same tile
+ for(var/obj/obj_content in target_shove_turf)
+ if(obj_content.flags_1 & ON_BORDER_1 && obj_content.dir == REVERSE_DIR(shove_dir) && obj_content.density)
+ shove_flags |= SHOVE_DIRECTIONAL_BLOCKED
+ break
+
+ if(shove_flags & SHOVE_CAN_HIT_SOMETHING)
+ //Don't hit people through windows, ok?
+ if(!(shove_flags & SHOVE_DIRECTIONAL_BLOCKED) && (SEND_SIGNAL(target_shove_turf, COMSIG_LIVING_DISARM_COLLIDE, src, target, shove_flags, weapon) & COMSIG_LIVING_SHOVE_HANDLED))
+ return
+ if((shove_flags & SHOVE_BLOCKED) && !(shove_flags & (SHOVE_KNOCKDOWN_BLOCKED|SHOVE_CAN_KICK_SIDE)))
+ target.Knockdown(SHOVE_KNOCKDOWN_SOLID)
+ target.visible_message(span_danger("[name] shoves [target.name], knocking [target.p_them()] down!"),
+ span_userdanger("You're knocked down from a shove by [name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
+ to_chat(src, span_danger("You shove [target.name], knocking [target.p_them()] down!"))
+ log_combat(src, target, "shoved", "knocking them down[weapon ? " with [weapon]" : ""]")
+ return
+
+ if(shove_flags & SHOVE_CAN_KICK_SIDE) //KICK HIM IN THE NUTS
+ target.Paralyze(SHOVE_CHAIN_PARALYZE)
+ target.visible_message(span_danger("[name] kicks [target.name] onto [target.p_their()] side!"),
+ span_userdanger("You're kicked onto your side by [name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
+ to_chat(src, span_danger("You kick [target.name] onto [target.p_their()] side!"))
+ addtimer(CALLBACK(target, TYPE_PROC_REF(/mob/living, SetKnockdown), 0), SHOVE_CHAIN_PARALYZE)
+ log_combat(src, target, "kicks", "onto their side (paralyzing)")
+ return
+
+ target.get_shoving_message(src, weapon, shove_flags)
+
+ //Take their lunch money
+ var/target_held_item = target.get_active_held_item()
+ var/append_message = weapon ? " with [weapon]" : ""
+ if(!is_type_in_typecache(target_held_item, GLOB.shove_disarming_types)) //It's too expensive we'll get caught
+ target_held_item = null
+
+ if(target_held_item && target.get_timed_status_effect_duration(/datum/status_effect/staggered))
+ target.dropItemToGround(target_held_item)
+ append_message = "causing [target.p_them()] to drop [target_held_item]"
+ target.visible_message(span_danger("[target.name] drops \the [target_held_item]!"),
+ span_warning("You drop \the [target_held_item]!"), null, COMBAT_MESSAGE_RANGE)
+
+ if(shove_flags & SHOVE_CAN_STAGGER)
+ target.adjust_staggered_up_to(STAGGERED_SLOWDOWN_LENGTH, 10 SECONDS)
+
+ log_combat(src, target, "shoved", append_message)
+
+///Check if the universal conditions for disarming/shoving are met.
+/mob/living/proc/can_disarm(mob/living/target)
+ if(body_position != STANDING_UP || src == target || loc == target.loc)
+ return FALSE
+ return TRUE
+
+///Check if there's anything that could stop the knockdown from being shoved into something or someone.
+/mob/living/proc/get_shove_flags(mob/living/shover, obj/item/weapon)
+ if(shover.move_force >= move_resist)
+ . |= SHOVE_CAN_MOVE
+ if(!buckled)
+ . |= SHOVE_CAN_HIT_SOMETHING
+ if(HAS_TRAIT(src, TRAIT_SHOVE_KNOCKDOWN_BLOCKED))
+ . |= SHOVE_KNOCKDOWN_BLOCKED
+
+///Send the chat feedback message for shoving
+/mob/living/proc/get_shoving_message(mob/living/shover, obj/item/weapon, shove_flags)
+ visible_message(span_danger("[shover] shoves [name][weapon ? " with [weapon]" : ""]!"),
+ span_userdanger("You're shoved by [shover][weapon ? " with [weapon]" : ""]!"), span_hear("You hear aggressive shuffling!"), COMBAT_MESSAGE_RANGE, shover)
+ to_chat(shover, span_danger("You shove [name][weapon ? " with [weapon]" : ""]!"))
+
/mob/living/proc/check_block(atom/hit_by, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0, damage_type = BRUTE)
if(SEND_SIGNAL(src, COMSIG_LIVING_CHECK_BLOCK, hit_by, damage, attack_text, attack_type, armour_penetration, damage_type) & SUCCESSFUL_BLOCK)
return TRUE
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index 44e8583b86f..722b92835c8 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -229,6 +229,11 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
step_away(src, user, 15)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(_step_away), src, get_turf(user), 15), 3)
+/mob/living/silicon/robot/get_shove_flags(mob/living/shover, obj/item/weapon)
+ . = ..()
+ if(isnull(weapon) || stat != CONSCIOUS)
+ . &= ~(SHOVE_CAN_MOVE|SHOVE_CAN_HIT_SOMETHING)
+
/mob/living/silicon/robot/welder_act(mob/living/user, obj/item/tool)
if(user.combat_mode && usr != src)
return FALSE
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index 8a0c793103f..e17ba69550d 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -75,6 +75,7 @@
TRAIT_MADNESS_IMMUNE,
TRAIT_MARTIAL_ARTS_IMMUNE,
TRAIT_NOFIRE_SPREAD,
+ TRAIT_SHOVE_KNOCKDOWN_BLOCKED,
)
add_traits(traits_to_apply, ROUNDSTART_TRAIT)
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index ac04f02f953..f1561c2941b 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -56,25 +56,30 @@
span_userdanger("[user] punches you!"), null, COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_danger("You punch [src]!"))
-//ATTACK HAND IGNORING PARENT RETURN VALUE
/mob/living/silicon/attack_hand(mob/living/carbon/human/user, list/modifiers)
- . = FALSE
- if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_HAND, user, modifiers) & COMPONENT_CANCEL_ATTACK_CHAIN)
- . = TRUE
+ . = ..()
+ if(.)
+ return TRUE
+
+ if(LAZYACCESS(modifiers, RIGHT_CLICK))
+ user.disarm(src)
+ return TRUE
+
if(has_buckled_mobs() && !user.combat_mode)
user_unbuckle_mob(buckled_mobs[1], user)
+ return TRUE
+ if(user.combat_mode)
+ user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
+ playsound(src.loc, 'sound/effects/bang.ogg', 10, TRUE)
+ visible_message(span_danger("[user] punches [src], but doesn't leave a dent!"), \
+ span_warning("[user] punches you, but doesn't leave a dent!"), null, COMBAT_MESSAGE_RANGE, user)
+ to_chat(user, span_danger("You punch [src], but don't leave a dent!"))
+ return TRUE
else
- if(user.combat_mode)
- user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
- playsound(src.loc, 'sound/effects/bang.ogg', 10, TRUE)
- visible_message(span_danger("[user] punches [src], but doesn't leave a dent!"), \
- span_warning("[user] punches you, but doesn't leave a dent!"), null, COMBAT_MESSAGE_RANGE, user)
- to_chat(user, span_danger("You punch [src], but don't leave a dent!"))
- else
- visible_message(span_notice("[user] pets [src]."), \
- span_notice("[user] pets you."), null, null, user)
- to_chat(user, span_notice("You pet [src]."))
- SEND_SIGNAL(user, COMSIG_MOB_PAT_BORG)
+ visible_message(span_notice("[user] pets [src]."), span_notice("[user] pets you."), null, null, user)
+ to_chat(user, span_notice("You pet [src]."))
+ SEND_SIGNAL(user, COMSIG_MOB_PAT_BORG)
+ return TRUE
/mob/living/silicon/check_block(atom/hitby, damage, attack_text, attack_type, armour_penetration, damage_type, attack_flag)
. = ..()
diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm
index afe4c00db4d..2fea2871c03 100644
--- a/code/modules/mob/living/simple_animal/animal_defense.dm
+++ b/code/modules/mob/living/simple_animal/animal_defense.dm
@@ -4,21 +4,7 @@
return TRUE
if(LAZYACCESS(modifiers, RIGHT_CLICK))
- if(user.move_force < move_resist)
- return
- user.do_attack_animation(src, ATTACK_EFFECT_DISARM)
- playsound(src, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
- var/shove_dir = get_dir(user, src)
- if(!Move(get_step(src, shove_dir), shove_dir))
- log_combat(user, src, "shoved", "failing to move it")
- user.visible_message(span_danger("[user.name] shoves [src]!"),
- span_danger("You shove [src]!"), span_hear("You hear aggressive shuffling!"), COMBAT_MESSAGE_RANGE, list(src))
- to_chat(src, span_userdanger("You're shoved by [user.name]!"))
- return TRUE
- log_combat(user, src, "shoved", "pushing it")
- user.visible_message(span_danger("[user.name] shoves [src], pushing [p_them()]!"),
- span_danger("You shove [src], pushing [p_them()]!"), span_hear("You hear aggressive shuffling!"), COMBAT_MESSAGE_RANGE, list(src))
- to_chat(src, span_userdanger("You're pushed by [user.name]!"))
+ user.disarm(src)
return TRUE
if(!user.combat_mode)
@@ -44,6 +30,19 @@
updatehealth()
return TRUE
+/mob/living/simple_animal/get_shoving_message(mob/living/shover, obj/item/weapon, shove_flags)
+ if(weapon) // no "gently pushing aside" if you're pressing a shield at them.
+ return ..()
+ var/moved = !(shove_flags & SHOVE_BLOCKED)
+ shover.visible_message(
+ span_danger("[shover.name] [response_disarm_continuous] [src][moved ? ", pushing [p_them()]" : ""]!"),
+ span_danger("You [response_disarm_simple] [src][moved ? ", pushing [p_them()]" : ""]!"),
+ span_hear("You hear aggressive shuffling!"),
+ COMBAT_MESSAGE_RANGE,
+ list(src),
+ )
+ to_chat(src, span_userdanger("You're [moved ? "pushed" : "shoved"] by [shover.name]!"))
+
/mob/living/simple_animal/attack_hulk(mob/living/carbon/human/user)
. = ..()
if(!.)
diff --git a/code/modules/mod/mod_theme.dm b/code/modules/mod/mod_theme.dm
index 5e579723f78..4d8f8b4c6b7 100644
--- a/code/modules/mod/mod_theme.dm
+++ b/code/modules/mod/mod_theme.dm
@@ -760,6 +760,7 @@
armor_type = /datum/armor/mod_theme_safeguard
resistance_flags = FIRE_PROOF
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
+ inbuilt_modules = list(/obj/item/mod/module/shove_blocker/locked)
slowdown_inactive = 0.75
slowdown_active = 0.25
allowed_suit_storage = list(
@@ -1772,7 +1773,7 @@
UNSEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
),
CHESTPLATE_FLAGS = list(
- UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE|BLOCKS_SHOVE_KNOCKDOWN,
+ UNSEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
SEALED_INVISIBILITY = HIDEJUMPSUIT,
),
GAUNTLETS_FLAGS = list(
diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm
index 3e4b89e6a52..3bcd0751ce4 100644
--- a/code/modules/mod/mod_types.dm
+++ b/code/modules/mod/mod_types.dm
@@ -558,6 +558,7 @@
/obj/item/mod/module/magboot/advanced,
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/anomaly_locked/kinesis/admin,
+ /obj/item/mod/module/shove_blocker,
)
default_pins = list(
/obj/item/mod/module/stealth/ninja,
diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm
index b3527125fd2..6df1d9d0a6f 100644
--- a/code/modules/mod/modules/modules_security.dm
+++ b/code/modules/mod/modules/modules_security.dm
@@ -424,7 +424,7 @@
if(oldgroup == newgroup)
return
-
+
sorted_creatures[oldgroup] -= creature
sorted_creatures[newgroup] += creature
@@ -578,3 +578,22 @@
#undef SHOOTING_ASSISTANT_OFF
#undef STORMTROOPER_MODE
#undef SHARPSHOOTER_MODE
+
+/obj/item/mod/module/anti_stagger
+ name = "MOD bulwark module"
+ desc = "Layers upon layers of shock dampening plates, just to stop you from getting shoved into a wall by an angry mob."
+ icon_state = "bulwark"
+ complexity = 3
+ incompatible_modules = list(/obj/item/mod/module/shove_blocker)
+
+/obj/item/mod/module/shove_blocker/on_suit_activation()
+ mod.wearer.add_traits(list(TRAIT_SHOVE_KNOCKDOWN_BLOCKED, TRAIT_NO_STAGGER, TRAIT_NO_THROW_HITPUSH), MOD_TRAIT)
+
+/obj/item/mod/module/shove_blocker/on_suit_deactivation(deleting = FALSE)
+ mod.wearer.remove_traits(list(TRAIT_SHOVE_KNOCKDOWN_BLOCKED, TRAIT_NO_STAGGER, TRAIT_NO_THROW_HITPUSH), MOD_TRAIT)
+
+/obj/item/mod/module/shove_blocker/locked
+ name = "superglued MOD bulwark module"
+ desc = "Layers upon layers of shock dampening plates, just to stop you from getting shoved into a wall by an angry mob. Good luck removing this one."
+ removable = FALSE
+ complexity = 0
diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm
index a16e5add1bb..1976a61376f 100644
--- a/code/modules/recycling/disposal/bin.dm
+++ b/code/modules/recycling/disposal/bin.dm
@@ -61,7 +61,7 @@
RegisterSignal(src, COMSIG_RAT_INTERACT, PROC_REF(on_rat_rummage))
RegisterSignal(src, COMSIG_STORAGE_DUMP_CONTENT, PROC_REF(on_storage_dump))
var/static/list/loc_connections = list(
- COMSIG_CARBON_DISARM_COLLIDE = PROC_REF(trash_carbon),
+ COMSIG_LIVING_DISARM_COLLIDE = PROC_REF(trash_living),
COMSIG_TURF_RECEIVE_SWEEPED_ITEMS = PROC_REF(ready_for_trash),
)
AddElement(/datum/element/connect_loc, loc_connections)
@@ -551,17 +551,17 @@
return COMPONENT_RAT_INTERACTED
/// Handles a carbon mob getting shoved into the disposal bin
-/obj/machinery/disposal/proc/trash_carbon(datum/source, mob/living/carbon/shover, mob/living/carbon/target, shove_blocked)
+/obj/machinery/disposal/proc/trash_living(datum/source, mob/living/shover, mob/living/target, shove_flags, obj/item/weapon)
SIGNAL_HANDLER
- if(!shove_blocked)
+ if((shove_flags & SHOVE_KNOCKDOWN_BLOCKED) || !(shove_flags & SHOVE_BLOCKED))
return
target.Knockdown(SHOVE_KNOCKDOWN_SOLID)
target.forceMove(src)
target.visible_message(span_danger("[shover.name] shoves [target.name] into \the [src]!"),
- span_userdanger("You're shoved into \the [src] by [target.name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
+ span_userdanger("You're shoved into \the [src] by [target.name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, shover)
to_chat(src, span_danger("You shove [target.name] into \the [src]!"))
- log_combat(shover, target, "shoved", "into [src] (disposal bin)")
- return COMSIG_CARBON_SHOVE_HANDLED
+ log_combat(shover, target, "shoved", "into [src] (disposal bin)[weapon ? " with [weapon]" : ""]")
+ return COMSIG_LIVING_SHOVE_HANDLED
///Called when a push broom is trying to sweep items onto the turf this object is standing on. Garbage will be moved inside.
/obj/machinery/disposal/proc/ready_for_trash(datum/source, obj/item/pushbroom/broom, mob/user, list/items_to_sweep)
diff --git a/code/modules/surgery/organs/internal/heart/heart_ethereal.dm b/code/modules/surgery/organs/internal/heart/heart_ethereal.dm
index 50659115872..bd30318a722 100644
--- a/code/modules/surgery/organs/internal/heart/heart_ethereal.dm
+++ b/code/modules/surgery/organs/internal/heart/heart_ethereal.dm
@@ -88,12 +88,12 @@
crystalize_timer_id = addtimer(CALLBACK(src, PROC_REF(crystalize), victim), CRYSTALIZE_PRE_WAIT_TIME, TIMER_STOPPABLE)
- RegisterSignal(victim, COMSIG_HUMAN_DISARM_HIT, PROC_REF(reset_crystalizing))
+ RegisterSignal(victim, COMSIG_LIVING_DISARM_HIT, PROC_REF(reset_crystalizing))
RegisterSignal(victim, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine), override = TRUE)
RegisterSignal(victim, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(on_take_damage))
///Ran when disarmed, prevents the ethereal from reviving
-/obj/item/organ/internal/heart/ethereal/proc/reset_crystalizing(mob/living/defender, mob/living/attacker, zone)
+/obj/item/organ/internal/heart/ethereal/proc/reset_crystalizing(mob/living/defender, mob/living/attacker, zone, obj/item/weapon)
SIGNAL_HANDLER
defender.visible_message(
span_notice("The crystals on [defender] are gently broken off."),
@@ -120,7 +120,7 @@
///Stop the crystalization process, unregistering any signals and resetting any variables.
/obj/item/organ/internal/heart/ethereal/proc/stop_crystalization_process(mob/living/ethereal, succesful = FALSE)
- UnregisterSignal(ethereal, COMSIG_HUMAN_DISARM_HIT)
+ UnregisterSignal(ethereal, COMSIG_LIVING_DISARM_HIT)
UnregisterSignal(ethereal, COMSIG_ATOM_EXAMINE)
UnregisterSignal(ethereal, COMSIG_MOB_APPLY_DAMAGE)
diff --git a/icons/obj/clothing/modsuit/mod_modules.dmi b/icons/obj/clothing/modsuit/mod_modules.dmi
index f1d19c29da1..eb4b0c536ce 100644
Binary files a/icons/obj/clothing/modsuit/mod_modules.dmi and b/icons/obj/clothing/modsuit/mod_modules.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 121a7a69017..acdcf97e8b9 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1381,6 +1381,7 @@
#include "code\datums\elements\dextrous.dm"
#include "code\datums\elements\diggable.dm"
#include "code\datums\elements\digitalcamo.dm"
+#include "code\datums\elements\disarm_attack.dm"
#include "code\datums\elements\door_pryer.dm"
#include "code\datums\elements\drag_pickup.dm"
#include "code\datums\elements\dryable.dm"