mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-23 21:18:37 +01:00
Refactors unique_reskin, deletes retool kit (#93775)
## About The Pull Request Closes #93635 `unique_reskin` is no longer a list on `/item`, now `/datum/atom_skin` The actual reskinning behavior has been moved out to `/datum/component/reskinable_item` PKC reskinning is now handled via alt-click reskin, rather than via the retooling kit. The retooling kit has been removed. There's no limit on how many times you can reskin your PKC (though perhaps we limit it to one reskin and keep the retooling kit as a way to allow a miner to reskin it a second time?) The Ashen Skull unique reskin is still a trophy, and instead unlocks its unique reskin option in the alt-click radial. ## Why It's Good For The Game I'm unsure why the retooling kit exists on its own, when it's relatively cheap and just performs the behavior of alt-click reskinning. So to keep it consistent with all other forms of reskinning I've just made it baseline. To accomplish that I refactored reskinning. The new form of reskinning allows for greater potential in adding reskins, allowing far more than just an icon state change. Also we can put it on turfs and mobs and structures now which is cool I guess There's also the added benefit of being able to see an item's reskins without needing to instantiate it, which the loadout menu uses to great effect. ## Changelog 🆑 Melbert refactor: Refactored item reskinning (the alt-click way), report any oddities with that del: Deleted the crusher retool kit, now you can just reskin your crusher with alt-click. The Skull skin is still locked behind having the Ashen Skull trophy applied. fix: Stunswords no longer have an incorrect lore blurb fix: Fixed loadout item reskinning's UI /🆑
This commit is contained in:
@@ -60,9 +60,9 @@
|
||||
add_to_dart(dart, user)
|
||||
return COMPONENT_CANCEL_ATTACK_CHAIN
|
||||
|
||||
/datum/component/dart_insert/proc/on_reskin(datum/source, mob/user, skin)
|
||||
/datum/component/dart_insert/proc/on_reskin(datum/source, skin)
|
||||
SIGNAL_HANDLER
|
||||
SEND_SIGNAL(parent, COMSIG_DART_INSERT_PARENT_RESKINNED)
|
||||
SEND_SIGNAL(parent, COMSIG_DART_INSERT_PARENT_RESKINNED, skin)
|
||||
|
||||
/datum/component/dart_insert/proc/add_to_dart(obj/item/ammo_casing/dart, mob/user)
|
||||
var/obj/projectile/dart_projectile = dart.loaded_projectile
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* ### Atom skin singleton datum
|
||||
*
|
||||
* Simple datum which holds information about a skin that can be applied to an atom.
|
||||
*/
|
||||
/datum/atom_skin
|
||||
abstract_type = /datum/atom_skin
|
||||
|
||||
/// Required, name shown in the radial menu
|
||||
var/preview_name
|
||||
|
||||
/// If true, changing the reskin also changes the base_icon_state of the atom
|
||||
var/change_base_icon_state = FALSE
|
||||
/// If true, changing the reskin also changes the inhand_icon_state of the atom
|
||||
var/change_inhand_icon_state = FALSE
|
||||
/// If true, unset vars are reset to their original values when applying this skin
|
||||
var/reset_missing = TRUE
|
||||
|
||||
/// Optional, name to change the atom to when applied
|
||||
var/new_name
|
||||
/// Optional, description to change the atom to when applied
|
||||
var/new_desc
|
||||
/// Optional, icon to change the atom to when applied
|
||||
var/new_icon
|
||||
/// Optional, icon_state to change the atom to when applied
|
||||
var/new_icon_state
|
||||
|
||||
/**
|
||||
* Applies all relevant skin changes to the given atom
|
||||
* Can be overridden to add additional behavior, such as registering signals or altering other vars.
|
||||
*
|
||||
* * apply_to: The atom to apply the skin to
|
||||
*/
|
||||
/datum/atom_skin/proc/apply(atom/apply_to)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
APPLY_VAR_OR_RESET_INITIAL(apply_to, name, new_name, reset_missing)
|
||||
APPLY_VAR_OR_RESET_INITIAL(apply_to, desc, new_desc, reset_missing)
|
||||
APPLY_VAR_OR_RESET_INITIAL(apply_to, icon, new_icon, reset_missing)
|
||||
APPLY_VAR_OR_RESET_TO(apply_to, icon_state, new_icon_state, reset_missing, initial(apply_to.post_init_icon_state) || initial(apply_to.icon_state))
|
||||
if(change_base_icon_state)
|
||||
APPLY_VAR_OR_RESET_INITIAL(apply_to, base_icon_state, new_icon_state, reset_missing)
|
||||
if(change_inhand_icon_state && isitem(apply_to))
|
||||
var/obj/item/item_apply_to = apply_to
|
||||
APPLY_VAR_OR_RESET_INITIAL(item_apply_to, inhand_icon_state, new_icon_state, reset_missing)
|
||||
|
||||
/**
|
||||
* Resets all changes this skin would have made to the given atom
|
||||
* Does not verify that the skin was actually applied to the atom beforehand.
|
||||
* Can be overridden to add additional behavior, such as unregistering signals or altering other vars.
|
||||
*
|
||||
* * clear_from: The atom to clear the skin from
|
||||
*/
|
||||
/datum/atom_skin/proc/clear_skin(atom/clear_from)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
RESET_INITIAL_IF_SET(clear_from, name, new_name)
|
||||
RESET_INITIAL_IF_SET(clear_from, desc, new_desc)
|
||||
RESET_INITIAL_IF_SET(clear_from, icon, new_icon)
|
||||
RESET_TO_IF_SET(clear_from, icon_state, new_icon_state, initial(clear_from.post_init_icon_state) || initial(clear_from.icon_state))
|
||||
if(change_base_icon_state)
|
||||
RESET_INITIAL_IF_SET(clear_from, base_icon_state, new_icon_state)
|
||||
if(change_inhand_icon_state && isitem(clear_from))
|
||||
var/obj/item/item_clear_from = clear_from
|
||||
RESET_INITIAL_IF_SET(item_clear_from, inhand_icon_state, new_icon_state)
|
||||
|
||||
/**
|
||||
* ### Reskinnable atoms
|
||||
*
|
||||
* Simple component which lets an atom be alt-clicked to open a radial menu to choose a new skin to apply.
|
||||
*/
|
||||
/datum/component/reskinable_item
|
||||
dupe_mode = COMPONENT_DUPE_SELECTIVE
|
||||
/// Base reskin type to pull options from - all subtypes except those blacklisted are valid options
|
||||
VAR_PRIVATE/base_reskin_type
|
||||
/// If TRUE, the reskin option is infinite-use. If FALSE, the component is deleted on use (so you're stuck with that skin).
|
||||
VAR_PRIVATE/infinite_reskin = FALSE
|
||||
/// List of subtypes of /datum/atom_skin that are not allowed to be used for this item
|
||||
VAR_PRIVATE/list/blacklisted_subtypes
|
||||
|
||||
/// Currently applied skin preview_name
|
||||
VAR_PRIVATE/current_skin
|
||||
|
||||
/datum/component/reskinable_item/Initialize(base_reskin_type, infinite = FALSE, initial_skin, list/blacklisted_subtypes = list())
|
||||
if(!isatom(parent) || isarea(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.base_reskin_type = base_reskin_type
|
||||
src.infinite_reskin = infinite
|
||||
src.blacklisted_subtypes = blacklisted_subtypes
|
||||
|
||||
if(initial_skin)
|
||||
set_skin_by_name(initial_skin)
|
||||
|
||||
var/atom/atom_parent = parent
|
||||
atom_parent.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1
|
||||
|
||||
/datum/component/reskinable_item/RegisterWithParent()
|
||||
RegisterSignal(parent, COMSIG_CLICK_ALT, PROC_REF(on_click_alt_reskin))
|
||||
RegisterSignal(parent, COMSIG_ATOM_EXAMINE_TAGS, PROC_REF(add_tags))
|
||||
RegisterSignal(parent, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, PROC_REF(add_context))
|
||||
|
||||
/datum/component/reskinable_item/UnregisterFromParent()
|
||||
UnregisterSignal(parent, COMSIG_CLICK_ALT)
|
||||
UnregisterSignal(parent, COMSIG_ATOM_EXAMINE_TAGS)
|
||||
UnregisterSignal(parent, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM)
|
||||
|
||||
/datum/component/reskinable_item/CheckDupeComponent(datum/component/comp, base_reskin_type, infinite = FALSE, initial_skin, list/blacklisted_subtypes = list())
|
||||
if(src.base_reskin_type != base_reskin_type)
|
||||
return FALSE // new comp - though the alt-click behavior will collide
|
||||
|
||||
src.infinite_reskin = infinite
|
||||
src.blacklisted_subtypes = blacklisted_subtypes
|
||||
|
||||
set_skin_by_name(initial_skin)
|
||||
return TRUE // same comp
|
||||
|
||||
/datum/component/reskinable_item/proc/get_skins_by_name()
|
||||
var/list/reskin_options = list()
|
||||
for(var/datum/atom_skin/reskin_option as anything in valid_subtypesof(base_reskin_type) - blacklisted_subtypes)
|
||||
reskin_options[reskin_option::preview_name] = reskin_option
|
||||
|
||||
return reskin_options
|
||||
|
||||
/datum/component/reskinable_item/proc/set_skin_by_name(input_name)
|
||||
var/list/reskin_options = get_skins_by_name()
|
||||
if(current_skin)
|
||||
var/datum/atom_skin/previous_skin = GLOB.atom_skins[reskin_options[current_skin]]
|
||||
previous_skin.clear_skin(parent)
|
||||
|
||||
if(input_name)
|
||||
var/datum/atom_skin/reskin_to_apply = GLOB.atom_skins[reskin_options[input_name]]
|
||||
reskin_to_apply.apply(parent)
|
||||
|
||||
current_skin = input_name
|
||||
|
||||
var/atom/atom_parent = parent
|
||||
atom_parent.update_appearance()
|
||||
|
||||
if(isitem(parent))
|
||||
var/obj/item/item_parent = parent
|
||||
item_parent.update_slot_icon()
|
||||
|
||||
SEND_SIGNAL(parent, COMSIG_OBJ_RESKIN, input_name)
|
||||
|
||||
/datum/component/reskinable_item/proc/add_context(atom/source, list/context, obj/item/held_item, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
context[SCREENTIP_CONTEXT_ALT_LMB] = "Reskin"
|
||||
return CONTEXTUAL_SCREENTIP_SET
|
||||
|
||||
/datum/component/reskinable_item/proc/add_tags(atom/source, mob/user, list/tags)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
tags["reskinnable"] = "This item is able to be reskinned! Alt-Click to do so!"
|
||||
|
||||
/// Called when alt clicked and the item has unique reskin options
|
||||
/datum/component/reskinable_item/proc/on_click_alt_reskin(datum/source, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!user.can_perform_action(parent, NEED_DEXTERITY))
|
||||
return NONE
|
||||
|
||||
INVOKE_ASYNC(src, PROC_REF(reskin_obj), user)
|
||||
return CLICK_ACTION_SUCCESS
|
||||
|
||||
/**
|
||||
* Reskins object based on a user's choice
|
||||
*
|
||||
* Arguments:
|
||||
* * user The mob choosing a reskin option
|
||||
*/
|
||||
/datum/component/reskinable_item/proc/reskin_obj(mob/user)
|
||||
var/atom/atom_parent = parent
|
||||
|
||||
var/list/items = list()
|
||||
for(var/reskin_name, reskin_typepath in get_skins_by_name())
|
||||
var/datum/atom_skin/reskin = GLOB.atom_skins[reskin_typepath]
|
||||
items[reskin_name] = image(icon = reskin.new_icon || atom_parent.icon, icon_state = reskin.new_icon_state || atom_parent.icon_state)
|
||||
|
||||
sort_list(items)
|
||||
|
||||
var/pick = show_radial_menu(user, parent, items, custom_check = CALLBACK(src, PROC_REF(check_reskin_menu), user), radius = 38, require_near = TRUE)
|
||||
if(!pick || !items[pick])
|
||||
return
|
||||
|
||||
set_skin_by_name(pick)
|
||||
to_chat(user, span_info("[parent] is now skinned as '[pick].'"))
|
||||
|
||||
if(!infinite_reskin)
|
||||
qdel(src)
|
||||
|
||||
/**
|
||||
* Checks if we are allowed to interact with a radial menu for reskins
|
||||
*
|
||||
* Arguments:
|
||||
* * user The mob interacting with the menu
|
||||
*/
|
||||
/datum/component/reskinable_item/proc/check_reskin_menu(mob/user)
|
||||
if(QDELETED(parent))
|
||||
return FALSE
|
||||
if(user.incapacitated)
|
||||
return FALSE
|
||||
return TRUE
|
||||
Reference in New Issue
Block a user