diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm
index b6b2c4cc774..7618aca8d07 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/components.dm
@@ -74,6 +74,7 @@
// /mob/living/carbon/human signals
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target)
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker)
+#define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit" //Hit by successful disarm attack (mob/living/carbon/human/attacker,zone_targeted)
#define CALTROP_BYPASS_SHOES 1
#define CALTROP_IGNORE_WALKERS 2
diff --git a/code/datums/components/knockoff.dm b/code/datums/components/knockoff.dm
new file mode 100644
index 00000000000..f00e5c21fba
--- /dev/null
+++ b/code/datums/components/knockoff.dm
@@ -0,0 +1,46 @@
+//Items with these will have a chance to get knocked off when disarming
+/datum/component/knockoff
+ var/knockoff_chance = 100 //Chance to knockoff
+ var/list/target_zones //Aiming for these zones will cause the knockoff, null means all zones allowed
+ var/list/slots_knockoffable //Can be only knocked off from these slots, null means all slots allowed
+ var/datum/component/redirect/disarm_redirect
+
+/datum/component/knockoff/Initialize(knockoff_chance,zone_override,slots_knockoffable)
+ if(!isitem(parent))
+ CRASH("Knockoff component misuse")
+ RegisterSignal(COMSIG_ITEM_EQUIPPED,.proc/OnEquipped)
+ RegisterSignal(COMSIG_ITEM_DROPPED,.proc/OnDropped)
+
+ src.knockoff_chance = knockoff_chance
+
+ if(zone_override)
+ target_zones = zone_override
+
+ if(slots_knockoffable)
+ src.slots_knockoffable = slots_knockoffable
+
+/datum/component/knockoff/proc/Knockoff(mob/living/attacker,zone)
+ var/obj/item/I = parent
+ var/mob/living/carbon/human/wearer = I.loc
+ if(!istype(wearer))
+ return
+ if(target_zones && !(zone in target_zones))
+ return
+ if(prob(knockoff_chance))
+ if(wearer.dropItemToGround(I))
+ wearer.visible_message("[attacker] knocks off [wearer] [I.name]!","[attacker] knocks off your [I.name]!")
+
+/datum/component/knockoff/proc/OnEquipped(mob/living/carbon/human/H,slot)
+ if(!istype(H))
+ return
+ if(slots_knockoffable && !(slot in slots_knockoffable))
+ if(disarm_redirect)
+ QDEL_NULL(disarm_redirect)
+ return
+ if(!disarm_redirect)
+ disarm_redirect = H.AddComponent(/datum/component/redirect,list(COMSIG_HUMAN_DISARM_HIT),CALLBACK(src,.proc/Knockoff))
+
+/datum/component/knockoff/proc/OnDropped(mob/living/M)
+ if(disarm_redirect)
+ QDEL_NULL(disarm_redirect)
+
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index c7c3d7280b6..9c6d24142f7 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -412,6 +412,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
if(DROPDEL_1 & flags_1)
qdel(src)
in_inventory = FALSE
+ SendSignal(COMSIG_ITEM_DROPPED,user)
// called just as an item is picked up (loc is not yet changed)
/obj/item/proc/pickup(mob/user)
@@ -442,6 +443,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
var/datum/action/A = X
if(item_action_slot_check(slot, user)) //some items only give their actions buttons when in a specific slot.
A.Grant(user)
+ SendSignal(COMSIG_ITEM_EQUIPPED,user,slot)
in_inventory = TRUE
//sometimes we only want to grant the item's action if it's equipped in a specific slot.
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 8c7a59a77d3..a85639faa09 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1216,10 +1216,12 @@ GLOBAL_LIST_EMPTY(roundstart_races)
return 1
else
user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
-
+
if(target.w_uniform)
target.w_uniform.add_fingerprint(user)
- var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(user.zone_selected))
+ var/randomized_zone = ran_zone(user.zone_selected)
+ target.SendSignal(COMSIG_HUMAN_DISARM_HIT, user, user.zone_selected)
+ var/obj/item/bodypart/affecting = target.get_bodypart(randomized_zone)
var/randn = rand(1, 100)
if(randn <= 25)
playsound(target, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
diff --git a/tgstation.dme b/tgstation.dme
index cce449562f6..edc541587b3 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -307,6 +307,7 @@
#include "code\datums\components\caltrop.dm"
#include "code\datums\components\chasm.dm"
#include "code\datums\components\decal.dm"
+#include "code\datums\components\knockoff.dm"
#include "code\datums\components\infective.dm"
#include "code\datums\components\jousting.dm"
#include "code\datums\components\material_container.dm"