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"