diff --git a/code/__DEFINES/dcs/flags.dm b/code/__DEFINES/dcs/flags.dm
index 7b43c54ab3a..4fb68d0bff2 100644
--- a/code/__DEFINES/dcs/flags.dm
+++ b/code/__DEFINES/dcs/flags.dm
@@ -37,9 +37,9 @@
#define ARCH_MAXDROP "max_drop_amount" //each item's max drop amount
//Ouch my toes!
-#define CALTROP_BYPASS_SHOES 1
-#define CALTROP_IGNORE_WALKERS 2
-#define CALTROP_SILENT 3
+#define CALTROP_BYPASS_SHOES (1 << 0)
+#define CALTROP_IGNORE_WALKERS (1 << 1)
+#define CALTROP_SILENT (1 << 2)
//Ingredient type in datum/component/customizable_reagent_holder
#define CUSTOM_INGREDIENT_TYPE_EDIBLE 1
diff --git a/code/datums/components/caltrop.dm b/code/datums/components/caltrop.dm
deleted file mode 100644
index a8b736532ee..00000000000
--- a/code/datums/components/caltrop.dm
+++ /dev/null
@@ -1,71 +0,0 @@
-/datum/component/caltrop
- var/min_damage
- var/max_damage
- var/probability
- var/flags
- COOLDOWN_DECLARE(caltrop_cooldown)
-
-
-/datum/component/caltrop/Initialize(_min_damage = 0, _max_damage = 0, _probability = 100, _flags = NONE)
- min_damage = _min_damage
- max_damage = max(_min_damage, _max_damage)
- probability = _probability
- flags = _flags
-
- RegisterSignal(parent, list(COMSIG_MOVABLE_CROSSED), .proc/Crossed)
-
-/datum/component/caltrop/proc/Crossed(datum/source, atom/movable/AM)
- SIGNAL_HANDLER
-
- if(!prob(probability))
- return
-
- if(ishuman(AM))
- var/mob/living/carbon/human/H = AM
- if(HAS_TRAIT(H, TRAIT_PIERCEIMMUNE))
- return
-
- if((flags & CALTROP_IGNORE_WALKERS) && H.m_intent == MOVE_INTENT_WALK)
- return
-
- //move these next two down a level if you add more mobs to this.
- if(H.movement_type & (FLOATING|FLYING)) //check if they are able to pass over us
- return //gravity checking only our parent would prevent us from triggering they're using magboots / other gravity assisting items that would cause them to still touch us.
- if(H.buckled) //if they're buckled to something, that something should be checked instead.
- return
- if(H.body_position == LYING_DOWN) //if were not standing we cant step on the caltrop
- return
-
- var/picked_def_zone = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
- var/obj/item/bodypart/O = H.get_bodypart(picked_def_zone)
- if(!istype(O))
- return
- if(O.status == BODYPART_ROBOTIC)
- return
-
- if (!(flags & CALTROP_BYPASS_SHOES))
- if ((H.wear_suit?.body_parts_covered | H.w_uniform?.body_parts_covered | H.shoes?.body_parts_covered) & FEET)
- return
-
- // SKYRAT EDIT ADDITION BEGIN - Hardened Soles Quirk
- if(HAS_TRAIT(H, TRAIT_HARD_SOLES) && !(flags & CALTROP_BYPASS_SHOES))
- return
- // SKYRAT EDIT ADDITION END
-
- var/damage = rand(min_damage, max_damage)
- if(HAS_TRAIT(H, TRAIT_LIGHT_STEP))
- damage *= 0.75
-
-
- if(!(flags & CALTROP_SILENT) && COOLDOWN_FINISHED(src, caltrop_cooldown))
- COOLDOWN_START(src, caltrop_cooldown, 1 SECONDS) //cooldown to avoid message spam.
- var/atom/A = parent
- if(!H.incapacitated(ignore_restraints = TRUE))
- H.visible_message("[H] steps on [A].", \
- "You step on [A]!")
- else
- H.visible_message("[H] slides on [A]!", \
- "You slide on [A]!")
-
- H.apply_damage(damage, BRUTE, picked_def_zone, wound_bonus = CANT_WOUND)
- H.Paralyze(60)
diff --git a/code/datums/elements/caltrop.dm b/code/datums/elements/caltrop.dm
new file mode 100644
index 00000000000..ab36067a4dd
--- /dev/null
+++ b/code/datums/elements/caltrop.dm
@@ -0,0 +1,87 @@
+/**
+ * Caltrop element; for hurting people when they walk over this.
+ *
+ * Used for broken glass, cactuses and four sided dice.
+ */
+/datum/element/caltrop
+ element_flags = ELEMENT_BESPOKE
+ id_arg_index = 2
+
+ ///Minimum damage done when crossed
+ var/min_damage
+
+ ///Maximum damage done when crossed
+ var/max_damage
+
+ ///Probability of actually "firing", stunning and doing damage
+ var/probability
+
+ ///Miscelanous caltrop flags; shoe bypassing, walking interaction, silence
+ var/flags
+
+/datum/element/caltrop/Attach(datum/target, min_damage = 0, max_damage = 0, probability = 100, flags = NONE)
+ . = ..()
+ if(!isatom(target))
+ return ELEMENT_INCOMPATIBLE
+
+ src.min_damage = min_damage
+ src.max_damage = max(min_damage, max_damage)
+ src.probability = probability
+ src.flags = flags
+
+ RegisterSignal(target, COMSIG_MOVABLE_CROSSED, .proc/Crossed)
+
+/datum/element/caltrop/proc/Crossed(atom/caltrop, atom/movable/AM)
+ SIGNAL_HANDLER
+
+ if(!prob(probability))
+ return
+
+ if(!ishuman(AM))
+ return
+
+ var/mob/living/carbon/human/H = AM
+ if(HAS_TRAIT(H, TRAIT_PIERCEIMMUNE))
+ return
+
+ if((flags & CALTROP_IGNORE_WALKERS) && H.m_intent == MOVE_INTENT_WALK)
+ return
+
+ if(H.movement_type & (FLOATING|FLYING)) //check if they are able to pass over us
+ //gravity checking only our parent would prevent us from triggering they're using magboots / other gravity assisting items that would cause them to still touch us.
+ return
+
+ if(H.buckled) //if they're buckled to something, that something should be checked instead.
+ return
+
+ if(H.body_position == LYING_DOWN) //if we're not standing we cant step on the caltrop
+ return
+
+ var/picked_def_zone = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
+ var/obj/item/bodypart/O = H.get_bodypart(picked_def_zone)
+ if(!istype(O))
+ return
+
+ if(O.status == BODYPART_ROBOTIC)
+ return
+
+ if (!(flags & CALTROP_BYPASS_SHOES))
+ // SKYRAT EDIT ADDITION BEGIN - Hardened Soles Quirk
+ if(HAS_TRAIT(H, TRAIT_HARD_SOLES))
+ return
+ // SKYRAT EDIT ADDITION END
+ if ((H.wear_suit?.body_parts_covered | H.w_uniform?.body_parts_covered | H.shoes?.body_parts_covered) & FEET)
+ return
+
+ var/damage = rand(min_damage, max_damage)
+ if(HAS_TRAIT(H, TRAIT_LIGHT_STEP))
+ damage *= 0.75
+
+
+ if(!(flags & CALTROP_SILENT) && !H.has_status_effect(/datum/status_effect/caltropped))
+ H.apply_status_effect(/datum/status_effect/caltropped)
+ H.visible_message("[H] steps on [caltrop].", \
+ "You step on [caltrop]!")
+
+ H.apply_damage(damage, BRUTE, picked_def_zone, wound_bonus = CANT_WOUND)
+ H.Paralyze(60)
diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm
index d911f000a31..b2793d2fc94 100644
--- a/code/datums/status_effects/neutral.dm
+++ b/code/datums/status_effects/neutral.dm
@@ -292,3 +292,19 @@
/// Something fishy is going on here...
/datum/status_effect/high_fiving/proc/dropped_slap(obj/item/source)
slap_item = null
+
+/*
+ * A status effect used for preventing caltrop message spam
+ *
+ * While a mob has this status effect, they won't recieve any messages about
+ * stepping on caltrops. But they will be stunned and damaged regardless.
+ *
+ * The status effect itself has no effect, other than to disappear after
+ * a second.
+ */
+/datum/status_effect/caltropped
+ id = "caltropped"
+ duration = 1 SECONDS
+ tick_interval = INFINITY
+ status_type = STATUS_EFFECT_REFRESH
+ alert_type = null
diff --git a/code/game/objects/items/dice.dm b/code/game/objects/items/dice.dm
index 3bb2835cab6..ff72dc4d627 100644
--- a/code/game/objects/items/dice.dm
+++ b/code/game/objects/items/dice.dm
@@ -88,7 +88,8 @@
/obj/item/dice/d4/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/caltrop, 1, 4) //1d4 damage
+ // 1d4 damage
+ AddElement(/datum/element/caltrop, min_damage = 1, max_damage = 4)
/obj/item/dice/d6
name = "d6"
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index 68916c316f7..6814e2f2079 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -273,7 +273,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
/obj/item/shard/Initialize()
. = ..()
- AddComponent(/datum/component/caltrop, force)
+ AddElement(/datum/element/caltrop, min_damage = force)
AddComponent(/datum/component/butchering, 150, 65)
icon_state = pick("large", "medium", "small")
switch(icon_state)
diff --git a/code/modules/hydroponics/grown/towercap.dm b/code/modules/hydroponics/grown/towercap.dm
index 49d79ee3fe9..d34bd080bc5 100644
--- a/code/modules/hydroponics/grown/towercap.dm
+++ b/code/modules/hydroponics/grown/towercap.dm
@@ -143,7 +143,7 @@
/obj/structure/punji_sticks/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/caltrop, 20, 30, 100, CALTROP_BYPASS_SHOES)
+ AddElement(/datum/element/caltrop, min_damage = 20, max_damage = 30, flags = CALTROP_BYPASS_SHOES)
/obj/structure/punji_sticks/spikes
name = "wooden spikes"
diff --git a/code/modules/mining/lavaland/ash_flora.dm b/code/modules/mining/lavaland/ash_flora.dm
index 0f88ac7e08d..ff0f10e5b85 100644
--- a/code/modules/mining/lavaland/ash_flora.dm
+++ b/code/modules/mining/lavaland/ash_flora.dm
@@ -144,8 +144,7 @@
/obj/structure/flora/ash/cacti/Initialize(mapload)
. = ..()
- // min dmg 3, max dmg 6, prob(70)
- AddComponent(/datum/component/caltrop, 3, 6, 70)
+ AddElement(/datum/element/caltrop, min_damage = 3, max_damage = 6, probability = 70)
///Snow flora to exist on icebox.
/obj/structure/flora/ash/chilly
diff --git a/code/modules/mob/living/simple_animal/hostile/cockroach.dm b/code/modules/mob/living/simple_animal/hostile/cockroach.dm
index 6c0e4ef7b75..54cb9b60e03 100644
--- a/code/modules/mob/living/simple_animal/hostile/cockroach.dm
+++ b/code/modules/mob/living/simple_animal/hostile/cockroach.dm
@@ -90,9 +90,9 @@
sharpness = SHARP_POINTY
squish_chance = 0 // manual squish if relevant
-/mob/living/simple_animal/hostile/cockroach/hauberoach/ComponentInitialize()
+/mob/living/simple_animal/hostile/cockroach/hauberoach/Initialize()
. = ..()
- AddComponent(/datum/component/caltrop, 10, 15, 100, (CALTROP_BYPASS_SHOES | CALTROP_SILENT))
+ AddElement(/datum/element/caltrop, min_damage = 10, max_damage = 15, flags = (CALTROP_BYPASS_SHOES | CALTROP_SILENT))
/mob/living/simple_animal/hostile/cockroach/hauberoach/make_squashable()
AddElement(/datum/element/squashable, squash_chance = 100, squash_damage = 1, squash_callback = /mob/living/simple_animal/hostile/cockroach/hauberoach/.proc/on_squish)
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index f8e1ec4f3ee..d861f4e6a87 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -936,12 +936,9 @@
/obj/item/light/Initialize()
. = ..()
create_reagents(LIGHT_REAGENT_CAPACITY, INJECTABLE | DRAINABLE)
+ AddElement(/datum/element/caltrop, min_damage = force)
update()
-/obj/item/light/ComponentInitialize()
- . = ..()
- AddComponent(/datum/component/caltrop, force)
-
/obj/item/light/Crossed(atom/movable/AM)
. = ..()
if(!isliving(AM))
diff --git a/tgstation.dme b/tgstation.dme
index a160b3cb393..724079acb48 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -446,7 +446,6 @@
#include "code\datums\components\beetlejuice.dm"
#include "code\datums\components\bloodysoles.dm"
#include "code\datums\components\butchering.dm"
-#include "code\datums\components\caltrop.dm"
#include "code\datums\components\chasm.dm"
#include "code\datums\components\construction.dm"
#include "code\datums\components\creamed.dm"
@@ -612,6 +611,7 @@
#include "code\datums\elements\backblast.dm"
#include "code\datums\elements\bed_tucking.dm"
#include "code\datums\elements\bsa_blocker.dm"
+#include "code\datums\elements\caltrop.dm"
#include "code\datums\elements\cleaning.dm"
#include "code\datums\elements\decal.dm"
#include "code\datums\elements\digitalcamo.dm"