diff --git a/code/datums/elements/object_reskinning.dm b/code/datums/elements/object_reskinning.dm
new file mode 100644
index 0000000000..eeb2a0601b
--- /dev/null
+++ b/code/datums/elements/object_reskinning.dm
@@ -0,0 +1,68 @@
+/datum/element/object_reskinning
+ element_flags = ELEMENT_DETACH
+
+/datum/element/object_reskinning/Attach(datum/target)
+ . = ..()
+ var/obj/the_obj = target
+ if(!istype(the_obj))
+ return ELEMENT_INCOMPATIBLE
+ if(!islist(the_obj.unique_reskin) || !length(the_obj.unique_reskin))
+ message_admins("[src] was given to an object without any unique reskins, if you really need to, give it a couple skins first.")
+ return ELEMENT_INCOMPATIBLE
+
+ RegisterSignal(the_obj, COMSIG_PARENT_EXAMINE, .proc/on_examine)
+ RegisterSignal(the_obj, COMSIG_CLICK_ALT, .proc/reskin)
+
+/datum/element/object_reskinning/Detach(datum/source, force)
+ UnregisterSignal(source, list(COMSIG_PARENT_EXAMINE, COMSIG_CLICK_ALT))
+ return ..()
+
+/datum/element/object_reskinning/proc/on_examine(obj/obj, mob/user, list/examine_list)
+ examine_list += span_notice("Alt-click to reskin it ([length(obj.unique_reskin)] possible styles).")
+ if(obj.always_reskinnable)
+ examine_list += span_notice("It has no limit to reskinning.")
+
+/// Reskins an object according to user's choice, modified to be overridable and supports different icons
+/datum/element/object_reskinning/proc/reskin(obj/to_reskin, mob/user)
+ // Just stop early
+ if(!(LAZYLEN(to_reskin.unique_reskin) && user.canUseTopic(to_reskin, BE_CLOSE, NO_DEXTERY)))
+ message_admins("[ADMIN_LOOKUPFLW(user)] attempted to reskin an object that has no skins!")
+ Detach(to_reskin)
+ return FALSE
+
+ var/list/items = list()
+ for(var/reskin_option in to_reskin.unique_reskin)
+ var/image/item_image = image(
+ icon = to_reskin.unique_reskin[reskin_option]["icon"] ? to_reskin.unique_reskin[reskin_option]["icon"] : to_reskin.icon,
+ icon_state = to_reskin.unique_reskin[reskin_option]["icon_state"] ? to_reskin.unique_reskin[reskin_option]["icon_state"] : to_reskin.icon_state)
+ items += list("[reskin_option]" = item_image)
+ sortList(items)
+
+ var/pick = show_radial_menu(user, to_reskin, items, custom_check = CALLBACK(src, .proc/check_reskin_menu, user, to_reskin), radius = 38, require_near = TRUE)
+ if(!pick)
+ return FALSE
+ to_reskin.current_skin = pick
+ for(var/reskin_var in to_reskin.unique_reskin[pick])
+ to_reskin.vars[reskin_var] = to_reskin.unique_reskin[pick][reskin_var]
+ to_chat(user, "[to_reskin] is now skinned as '[pick].'")
+ to_reskin.reskin_obj(user)
+ if(!to_reskin.always_reskinnable)
+ Detach(to_reskin)
+ return TRUE
+
+/**
+ * Checks if we are allowed to interact with a radial menu for reskins
+ *
+ * Arguments:
+ * * user The mob interacting with the menu
+ */
+/datum/element/object_reskinning/proc/check_reskin_menu(mob/user, obj/obj)
+ if(QDELETED(obj))
+ return FALSE
+ if(obj.current_skin)
+ return FALSE
+ if(!istype(user))
+ return FALSE
+ if(user.incapacitated())
+ return FALSE
+ return TRUE
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 59fe688b69..3575699c55 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -76,6 +76,10 @@
var/turf/T = loc
T.add_blueprints_preround(src)
+/obj/ComponentInitialize()
+ . = ..()
+ if(islist(unique_reskin) && length(unique_reskin))
+ AddElement(/datum/element/object_reskinning)
/obj/Destroy(force=FALSE)
if(!ismachinery(src))
@@ -340,57 +344,10 @@
. = ..()
if(obj_flags & UNIQUE_RENAME)
. += "Use a pen on it to rename it or change its description."
- if(unique_reskin && (!current_skin || always_reskinnable))
- . += "Alt-click it to reskin it."
-/obj/AltClick(mob/user)
- . = ..()
- if(unique_reskin && (!current_skin || always_reskinnable) && user.canUseTopic(src, BE_CLOSE, NO_DEXTERY))
- reskin_obj(user)
- return TRUE
-
-/// Reskins an object according to M's choice, modified to be overridable and supports different icons
-/obj/proc/reskin_obj(mob/M)
- if(!LAZYLEN(unique_reskin))
- return FALSE
-
- var/list/items = list()
- for(var/reskin_option in unique_reskin)
- var/image/item_image = image(
- icon = unique_reskin[reskin_option]["icon"] ? unique_reskin[reskin_option]["icon"] : icon,
- icon_state = unique_reskin[reskin_option]["icon_state"])
- items += list("[reskin_option]" = item_image)
- sortList(items)
-
- var/pick = show_radial_menu(M, src, items, custom_check = CALLBACK(src, .proc/check_reskin_menu, M), radius = 38, require_near = TRUE)
- if(!pick)
- return FALSE
- if(!unique_reskin[pick]["icon_state"])
- return FALSE
- current_skin = pick
- var/has_icon = unique_reskin[pick]["icon"]
- if(has_icon)
- icon = has_icon
- icon_state = unique_reskin[pick]["icon_state"]
- to_chat(M, "[src] is now skinned as '[pick].'")
- return TRUE
-
-/**
- * Checks if we are allowed to interact with a radial menu for reskins
- *
- * Arguments:
- * * user The mob interacting with the menu
- */
-/obj/proc/check_reskin_menu(mob/user)
- if(QDELETED(src))
- return FALSE
- if(current_skin)
- return FALSE
- if(!istype(user))
- return FALSE
- if(user.incapacitated())
- return FALSE
- return TRUE
+/// Do you want to make overrides, of course you do! Will be called if an object was reskinned successfully
+/obj/proc/reskin_obj(mob/user)
+ return
/obj/update_overlays()
. = ..()
diff --git a/tgstation.dme b/tgstation.dme
index 43bab0253b..8891cf3e14 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -702,6 +702,7 @@
#include "code\datums\elements\forced_gravity.dm"
#include "code\datums\elements\ghost_role_eligibility.dm"
#include "code\datums\elements\mob_holder.dm"
+#include "code\datums\elements\object_reskinning.dm"
#include "code\datums\elements\photosynthesis.dm"
#include "code\datums\elements\polychromic.dm"
#include "code\datums\elements\scavenging.dm"