diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 19cb08de54..a8ea046657 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -279,9 +279,13 @@ GLOBAL_LIST_INIT(pda_styles, list(MONO, VT, ORBITRON, SHARE))
#define PDA_SKIN_MODERN "Modern"
#define PDA_SKIN_MINIMAL "Minimal"
-GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_SKIN_ALT = 'icons/obj/pda_alt.dmi',
- PDA_SKIN_RUGGED = 'icons/obj/pda_rugged.dmi', PDA_SKIN_MODERN = 'icons/obj/pda_modern.dmi',
- PDA_SKIN_MINIMAL = 'icons/obj/pda_minimal.dmi'))
+GLOBAL_LIST_INIT(pda_reskins, list(
+ PDA_SKIN_CLASSIC = list("icon" = 'icons/obj/pda.dmi'),
+ PDA_SKIN_ALT = list("icon" = 'icons/obj/pda_alt.dmi'),
+ PDA_SKIN_RUGGED = list("icon" = 'icons/obj/pda_rugged.dmi'),
+ PDA_SKIN_MODERN = list("icon" = 'icons/obj/pda_modern.dmi'),
+ PDA_SKIN_MINIMAL = list("icon" = 'icons/obj/pda_minimal.dmi')
+ ))
/////////////////////////////////////
// atom.appearence_flags shortcuts //
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index c9e20bc01a..6e8d1de955 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -405,7 +405,7 @@
return
/atom/proc/CtrlShiftClick(mob/user)
- SEND_SIGNAL(src, COMSIG_CLICK_CTRL_SHIFT)
+ SEND_SIGNAL(src, COMSIG_CLICK_CTRL_SHIFT, user)
return
/*
diff --git a/code/datums/elements/object_reskinning.dm b/code/datums/elements/object_reskinning.dm
new file mode 100644
index 0000000000..8b5d905e72
--- /dev/null
+++ b/code/datums/elements/object_reskinning.dm
@@ -0,0 +1,104 @@
+/*
+ * # Element: Object Reskinning
+ *
+ * Allows players to modify the appearance of their object (and other attributes if possible)
+ * PLEASE DO NOT HAVE INVALID VALUES IN unique_reskin I DON'T EVEN WANT TO KNOW WHAT HAPPENS.
+ * USAGE:
+ * unique_reskins = list(
+ * "skin 1" = list(
+ * "desc" = "very cool description",
+ * "icon" = 'very_cool_icon.dmi'
+ * ),
+ * "skin 2" = list(
+ * "desc" = "not as cool description",
+ * "icon" = 'the_boring_skin.dmi'
+ * )
+ * )
+*/
+/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, the_obj.reskin_binding, .proc/reskin)
+
+/datum/element/object_reskinning/Detach(datum/source, force)
+ var/obj/being_deleted = source
+ UnregisterSignal(source, list(COMSIG_PARENT_EXAMINE, being_deleted.reskin_binding))
+ return ..()
+
+/datum/element/object_reskinning/proc/on_examine(obj/obj, mob/user, list/examine_list)
+ examine_list += span_notice("[capitalize(replacetext(obj.reskin_binding, "_", "-"))] 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.
+ * Will detach itself if there's no skins or if done successfully but not always reskinnable.
+ *
+ * Arguments:
+ * * to_reskin The object we will be reskinning
+ * * user The user who wants to choose a skin for the object
+*/
+/datum/element/object_reskinning/proc/reskin(obj/to_reskin, mob/user)
+ // Just stop early
+ if(!LAZYLEN(to_reskin.unique_reskin))
+ message_admins("[ADMIN_LOOKUPFLW(user)] attempted to reskin an object that has no skins!")
+ Detach(to_reskin)
+ return FALSE
+
+ // Can't use
+ if(!user.canUseTopic(to_reskin, BE_CLOSE, NO_DEXTERY, NO_TK))
+ return FALSE
+
+ // Get our choices
+ 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)
+
+ // Display to the user
+ 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
+
+ // Finish the work
+ 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)
+
+ // Only once or always?
+ 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
+ * * obj The obj to be checking against
+ */
+/datum/element/object_reskinning/proc/check_reskin_menu(mob/user, obj/obj)
+ if(QDELETED(obj))
+ return FALSE
+ if(!obj.always_reskinnable && obj.current_skin)
+ return FALSE
+ if(!istype(user))
+ return FALSE
+ if(user.incapacitated())
+ return FALSE
+ return TRUE
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index ab1ef6f001..b7ac29562c 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -32,6 +32,9 @@ GLOBAL_LIST_EMPTY(PDAs)
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 100, ACID = 100)
resistance_flags = FIRE_PROOF | ACID_PROOF
+ always_reskinnable = TRUE
+ reskin_binding = COMSIG_CLICK_CTRL_SHIFT
+
//Main variables
var/owner = null // String name of owner
var/default_cartridge = 0 // Access level defined by cartridge
@@ -112,10 +115,10 @@ GLOBAL_LIST_EMPTY(PDAs)
. += id ? "Alt-click to remove the id." : ""
if(inserted_item && (!isturf(loc)))
. += "Ctrl-click to remove [inserted_item]."
- if(LAZYLEN(GLOB.pda_reskins))
- . += "Ctrl-shift-click it to reskin it."
/obj/item/pda/Initialize(mapload)
+ if(GLOB.pda_reskins)
+ unique_reskin = GLOB.pda_reskins
. = ..()
if(fon)
set_light(f_lum, f_pow, f_col)
@@ -130,28 +133,10 @@ GLOBAL_LIST_EMPTY(PDAs)
new_overlays = TRUE
update_icon()
-/obj/item/pda/CtrlShiftClick(mob/living/user)
- . = ..()
- if(GLOB.pda_reskins && user.canUseTopic(src, BE_CLOSE, NO_DEXTERY))
- reskin_obj(user)
-
/obj/item/pda/reskin_obj(mob/M)
- if(!LAZYLEN(GLOB.pda_reskins))
- return
- var/dat = "Reskin options for [name]:"
- for(var/V in GLOB.pda_reskins)
- var/output = icon2html(GLOB.pda_reskins[V], M, icon_state)
- dat += "\n[V]: [output]"
- to_chat(M, dat)
-
- var/choice = input(M, "Choose the a reskin for [src]","Reskin Object") as null|anything in GLOB.pda_reskins
- var/new_icon = GLOB.pda_reskins[choice]
- if(QDELETED(src) || isnull(new_icon) || new_icon == icon || !M.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
- return
- icon = new_icon
+ . = ..()
new_overlays = TRUE
update_icon()
- to_chat(M, "[src] is now skinned as '[choice]'.")
/obj/item/pda/proc/set_new_overlays()
if(!overlays_offsets || !(icon in overlays_offsets))
@@ -191,7 +176,7 @@ GLOBAL_LIST_EMPTY(PDAs)
else
font_index = MODE_MONO
font_mode = FONT_MONO
- var/pref_skin = GLOB.pda_reskins[C.prefs.pda_skin]
+ var/pref_skin = GLOB.pda_reskins[C.prefs.pda_skin]["icon"]
if(icon != pref_skin)
icon = pref_skin
new_overlays = TRUE
diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm
index ca2fdbaeff..b389efe542 100644
--- a/code/game/objects/items/melee/energy.dm
+++ b/code/game/objects/items/melee/energy.dm
@@ -211,8 +211,6 @@
/obj/item/melee/transforming/energy/sword/saber/reskin_obj(mob/M)
. = ..()
- if(!.)
- return
switch(current_skin)
if("Sword")
icon_state = "sword[active ? sword_color : "0"]"
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 59fe688b69..cfe1afad5a 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -28,9 +28,17 @@
var/resistance_flags = NONE // INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ON_FIRE | UNACIDABLE | ACID_PROOF
var/persistence_replacement //have something WAY too amazing to live to the next round? Set a new path here. Overuse of this var will make me upset.
- var/current_skin //the item reskin
- var/list/unique_reskin //List of options to reskin.
+
+ //Reskin variables
+ /// The item reskin
+ var/current_skin
+ /// List of options to reskin.
+ var/list/unique_reskin
+ /// Can always be modified
var/always_reskinnable = FALSE
+ /// How to bring up the reskinning menu
+ var/reskin_binding = COMSIG_CLICK_ALT
+ //
// Access levels, used in modules\jobs\access.dm
var/list/req_access
@@ -76,6 +84,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 +352,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/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm
index 66cc5484e4..efa00ef888 100644
--- a/code/modules/clothing/gloves/miscellaneous.dm
+++ b/code/modules/clothing/gloves/miscellaneous.dm
@@ -86,8 +86,6 @@
/obj/item/clothing/gloves/fingerless/pugilist/crafted/reskin_obj(mob/M)
. = ..()
- if(!.)
- return
switch(current_skin)
if("Short")
item_state = "armwraps"
diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm
index 238b2ec86e..1b8b4d9e00 100644
--- a/code/modules/clothing/under/accessories.dm
+++ b/code/modules/clothing/under/accessories.dm
@@ -622,6 +622,4 @@
/obj/item/clothing/accessory/pride/reskin_obj(mob/M)
. = ..()
- if(!.)
- return
name = "[current_skin] pin"
diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm
index 19a6c9e832..0a7d6d05da 100644
--- a/code/modules/paperwork/pen.dm
+++ b/code/modules/paperwork/pen.dm
@@ -120,8 +120,6 @@
/obj/item/pen/fountain/captain/reskin_obj(mob/M)
. = ..()
- if(!.)
- return
desc = "It's an expensive [current_skin] fountain pen. The nib is quite sharp."
/obj/item/pen/attack_self(mob/living/carbon/user)
diff --git a/modular_sand/code/modules/clothing/spacesuits/hardsuit.dm b/modular_sand/code/modules/clothing/spacesuits/hardsuit.dm
index 789826f5be..d48f9b16a1 100644
--- a/modular_sand/code/modules/clothing/spacesuits/hardsuit.dm
+++ b/modular_sand/code/modules/clothing/spacesuits/hardsuit.dm
@@ -126,12 +126,20 @@
/obj/item/clothing/suit/space/hardsuit/mining
unique_reskin = list(
"Default" = list(
+ "name" = "mining hardsuit",
+ "desc" = "A special suit that protects against hazardous, low pressure environments. Has reinforced plating for wildlife encounters.",
"icon" = 'icons/obj/clothing/suits.dmi',
- "icon_state" = "hardsuit-mining"
+ "icon_state" = "hardsuit-mining",
+ "mob_overlay_icon" = null,
+ "anthro_mob_worn_overlay" = null
),
"Conscript" = list(
+ "name" = "Conscript suit",
+ "desc" = "..and so he left, with new orders and new questions.",
"icon" = 'modular_sand/icons/mob/clothing/suit.dmi',
- "icon_state" = "commando-armor"
+ "icon_state" = "commando-armor",
+ "mob_overlay_icon" = 'modular_sand/icons/mob/clothing/suit.dmi',
+ "anthro_mob_worn_overlay" = 'modular_sand/icons/mob/clothing/suit_digi.dmi'
)
)
@@ -152,8 +160,6 @@
/obj/item/clothing/suit/space/hardsuit/mining/reskin_obj(mob/M)
. = ..()
- if(!.)
- return
var/datum/component/armor_plate/armor_comp = GetComponent(/datum/component/armor_plate)
var/armor_level = 0
var/armor_max = 0
@@ -167,12 +173,9 @@
switch(current_skin)
if("Default")
- name = initial(name)
- desc = initial(desc)
if(armor_level != 0)
upgrade_icon(amount = armor_level, maxamount = armor_max)
- mob_overlay_icon = initial(mob_overlay_icon)
- anthro_mob_worn_overlay = initial(mob_overlay_icon)
+
if(mining_helmet)
mining_helmet.name = initial(mining_helmet.name)
mining_helmet.desc = initial(mining_helmet.desc)
@@ -185,12 +188,8 @@
/// Sprited by Dexxiol#3462 :)
if("Conscript")
- name = "Conscript suit"
- desc = "..and so he left, with new orders and new questions."
if(armor_level != 0)
upgrade_icon(amount = armor_level, maxamount = armor_max)
- mob_overlay_icon = 'modular_sand/icons/mob/clothing/suit.dmi'
- anthro_mob_worn_overlay = 'modular_sand/icons/mob/clothing/suit_digi.dmi'
if(mining_helmet)
mining_helmet.name = "Conscript helmet"
diff --git a/tgstation.dme b/tgstation.dme
index 47e18c3c82..39ee4abb73 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -741,6 +741,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"