Upstream 12 15 25 unique reskins fixes (#5019)

This branch needs help, tons of datums to make for the customizable
items.

---------

Co-authored-by: Alexis <catmc8565@gmail.com>
This commit is contained in:
nevimer
2026-01-10 13:14:26 -05:00
committed by GitHub
co-authored by Alexis
parent e82dd05cf1
commit a33fd49968
96 changed files with 4246 additions and 2843 deletions
-1
View File
@@ -9,7 +9,6 @@
strip_delay = 4 SECONDS
equip_delay_other = 4 SECONDS
visor_vars_to_toggle = NONE
unique_reskin_changes_base_icon_state = TRUE
var/adjusted_flags = null
///Did we install a filtering cloth?
+27 -11
View File
@@ -1,3 +1,28 @@
// Mask skins
/datum/atom_skin/joy_mask
abstract_type = /datum/atom_skin/joy_mask
change_base_icon_state = TRUE
/datum/atom_skin/joy_mask/joy
preview_name = "Joy"
new_icon_state = "joy"
/datum/atom_skin/joy_mask/flushed
preview_name = "Flushed"
new_icon_state = "flushed"
/datum/atom_skin/joy_mask/pensive
preview_name = "Pensive"
new_icon_state = "pensive"
/datum/atom_skin/joy_mask/angry
preview_name = "Angry"
new_icon_state = "angry"
/datum/atom_skin/joy_mask/pleading
preview_name = "Pleading"
new_icon_state = "pleading"
/obj/item/clothing/mask/joy
name = "emotion mask"
desc = "Express your happiness or hide your sorrows with this cultured cutout."
@@ -5,19 +30,10 @@
base_icon_state = "joy"
clothing_flags = MASKINTERNALS
flags_inv = HIDESNOUT
obj_flags = parent_type::obj_flags | INFINITE_RESKIN
unique_reskin = list(
"Joy" = "joy",
"Flushed" = "flushed",
"Pensive" = "pensive",
"Angry" = "angry",
"Pleading" = "pleading"
)
/obj/item/clothing/mask/joy/reskin_obj(mob/user)
/obj/item/clothing/mask/joy/Initialize(mapload)
. = ..()
user.update_worn_mask()
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/joy_mask, infinite = TRUE)
/obj/item/clothing/mask/mummy
name = "mummy mask"
-7
View File
@@ -73,13 +73,6 @@
. = ..()
dump_attachments()
/obj/item/clothing/under/setup_reskinning()
if(!check_setup_reskinning())
return
// We already register context in Initialize.
RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(on_click_alt_reskin))
/obj/item/clothing/under/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
. = ..()
@@ -31,13 +31,6 @@
. = ..()
register_context()
/obj/item/clothing/accessory/setup_reskinning()
if(!check_setup_reskinning())
return
// We already register context regardless in Initialize.
RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(on_click_alt_reskin))
/**
* Can we be attached to the passed clothing article?
*/
@@ -177,28 +177,51 @@
name = "Pre-Approved Cyborg Candidate dogtag"
display = "This employee has been screened for negative mental traits to an acceptable level of accuracy, and is approved for the NT Cyborg program as an alternative to medical resuscitation."
// Pride pin skins
/datum/atom_skin/pride_pin
abstract_type = /datum/atom_skin/pride_pin
/datum/atom_skin/pride_pin/gay
preview_name = "Rainbow Pride"
new_icon_state = "pride"
/datum/atom_skin/pride_pin/bi
preview_name = "Bisexual Pride"
new_icon_state = "pride_bi"
/datum/atom_skin/pride_pin/pan
preview_name = "Pansexual Pride"
new_icon_state = "pride_pan"
/datum/atom_skin/pride_pin/ace
preview_name = "Asexual Pride"
new_icon_state = "pride_ace"
/datum/atom_skin/pride_pin/enby
preview_name = "Non-binary Pride"
new_icon_state = "pride_enby"
/datum/atom_skin/pride_pin/trans
preview_name = "Transgender Pride"
new_icon_state = "pride_trans"
/datum/atom_skin/pride_pin/intersex
preview_name = "Intersex Pride"
new_icon_state = "pride_intersex"
/datum/atom_skin/pride_pin/lesbian
preview_name = "Lesbian Pride"
new_icon_state = "pride_lesbian"
/obj/item/clothing/accessory/pride
name = "pride pin"
desc = "A Nanotrasen Diversity & Inclusion Center-sponsored holographic pin to show off your pride, reminding the crew of their unwavering commitment to equity, diversity, and inclusion!"
icon_state = "pride"
obj_flags = UNIQUE_RENAME | INFINITE_RESKIN
unique_reskin = list(
"Rainbow Pride" = "pride",
"Bisexual Pride" = "pride_bi",
"Pansexual Pride" = "pride_pan",
"Asexual Pride" = "pride_ace",
"Non-binary Pride" = "pride_enby",
"Transgender Pride" = "pride_trans",
"Intersex Pride" = "pride_intersex",
"Lesbian Pride" = "pride_lesbian",
)
obj_flags = UNIQUE_RENAME
/obj/item/clothing/accessory/pride/setup_reskinning()
if(!check_setup_reskinning())
return
// We already register context regardless in Initialize.
RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(on_click_alt_reskin))
/obj/item/clothing/accessory/pride/Initialize(mapload)
. = ..()
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/pride_pin, infinite = TRUE)
/obj/item/clothing/accessory/deaf_pin
name = "deaf personnel pin"
+23 -7
View File
@@ -275,6 +275,26 @@
can_adjust = FALSE
resistance_flags = NONE
// Mech suit skins
/datum/atom_skin/mech_suit
abstract_type = /datum/atom_skin/mech_suit
/datum/atom_skin/mech_suit/red
preview_name = "Red"
new_icon_state = "red_mech_suit"
/datum/atom_skin/mech_suit/white
preview_name = "White"
new_icon_state = "white_mech_suit"
/datum/atom_skin/mech_suit/blue
preview_name = "Blue"
new_icon_state = "blue_mech_suit"
/datum/atom_skin/mech_suit/black
preview_name = "Black"
new_icon_state = "black_mech_suit"
/obj/item/clothing/under/costume/mech_suit
name = "mech pilot's suit"
desc = "A mech pilot's suit. Might make your butt look big."
@@ -286,13 +306,9 @@
alternate_worn_layer = GLOVES_LAYER //covers hands but gloves can go over it. This is how these things work in my head.
can_adjust = FALSE
unique_reskin = list(
"Red" = "red_mech_suit",
"White" = "white_mech_suit",
"Blue" = "blue_mech_suit",
"Black" = "black_mech_suit",
)
/obj/item/clothing/under/costume/mech_suit/Initialize(mapload)
. = ..()
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/mech_suit)
/obj/item/clothing/under/costume/russian_officer
name = "\improper Russian officer's uniform"
@@ -84,4 +84,4 @@
/datum/loadout_item/accessory/pride
name = "Pride Pin"
item_path = /obj/item/clothing/accessory/pride
loadout_flags = LOADOUT_FLAG_ALLOW_RESKIN
reskin_datum = /datum/atom_skin/pride_pin
+19 -28
View File
@@ -52,8 +52,9 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
/// Icon state for the UI to use for preview icons.
/// Set automatically if null
var/ui_icon_state
/// Reskin options of this item if it can be reskinned.
VAR_FINAL/list/cached_reskin_options
/// Base typepath to what reskin datum this item can use to reskin into
/// Doesn't verify that the item_path actually has these reskins
var/reskin_datum
/// A list of greyscale colors that are used for items that have greyscale support, but don't allow full customization.
/// This is an assoc list of /datum/job_department -> colors, or /datum/job -> colors, allowing for preset colors based on player chosen job.
/// Jobs are prioritized over departments.
@@ -100,15 +101,6 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
ui_icon = item_path::icon_preview || item_path::icon
ui_icon_state = item_path::icon_state_preview || item_path::icon_state
if(loadout_flags & LOADOUT_FLAG_ALLOW_RESKIN)
var/obj/item/dummy_item = new item_path()
if(!length(dummy_item.unique_reskin))
loadout_flags &= ~LOADOUT_FLAG_ALLOW_RESKIN
stack_trace("Loadout item [item_path] has LOADOUT_FLAG_ALLOW_RESKIN but has no unique reskins.")
else
cached_reskin_options = dummy_item.unique_reskin.Copy()
qdel(dummy_item)
// SKYRAT EDIT ADDITION
// Let's sanitize in case somebody inserted the player's byond name instead of ckey in canonical form
if(ckeywhitelist)
@@ -158,7 +150,7 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
// BUBBER EDIT ADDITION END
if("set_skin")
if(loadout_flags & LOADOUT_FLAG_ALLOW_RESKIN)
if(reskin_datum)
return set_skin(manager, user, params)
return TRUE
@@ -241,12 +233,7 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
/// Used for reskinning an item to an alt skin.
/datum/loadout_item/proc/set_skin(datum/preference_middleware/loadout/manager, mob/user, params)
var/reskin_to = params["skin"]
if(!cached_reskin_options[reskin_to])
return FALSE
if(!cached_reskin_options[reskin_to])
return FALSE
var/reskin_to = params["skin"] // sanity checking isn't necessary because it's all checked when equipped anyways
var/list/loadout = manager.get_current_loadout() // BUBBER EDIT: Multiple loadout presets: ORIGINAL: var/list/loadout = manager.preferences.read_preference(/datum/preference/loadout)
if(!loadout?[item_path])
@@ -333,11 +320,13 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
SEND_SIGNAL(equipped_item, COMSIG_NAME_CHANGED)
// BUBBER EDIT CHANGE END - Descriptions
if((loadout_flags & LOADOUT_FLAG_ALLOW_RESKIN) && item_details?[INFO_RESKIN])
if(reskin_datum && item_details?[INFO_RESKIN])
var/skin_chosen = item_details[INFO_RESKIN]
if(skin_chosen in equipped_item.unique_reskin)
equipped_item.current_skin = skin_chosen
equipped_item.icon_state = equipped_item.unique_reskin[skin_chosen]
for(var/datum/atom_skin/skin_path as anything in valid_subtypesof(reskin_datum))
if(skin_path::preview_name != skin_chosen)
continue
var/datum/atom_skin/skin_instance = GLOB.atom_skins[skin_path]
skin_instance.apply(equipped_item)
if(istype(equipped_item, /obj/item/clothing/accessory))
// Snowflake handing for accessories, because we need to update the thing it's attached to instead
if(isclothing(equipped_item.loc))
@@ -346,6 +335,7 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
update_flag |= (ITEM_SLOT_OCLOTHING|ITEM_SLOT_ICLOTHING)
else
update_flag |= equipped_item.slot_flags
break
return update_flag
@@ -400,7 +390,7 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
if((loadout_flags & LOADOUT_FLAG_GREYSCALING_ALLOWED) && !(loadout_flags & LOADOUT_FLAG_JOB_GREYSCALING))
displayed_text[FA_ICON_PALETTE] = "Recolorable"
if(loadout_flags & LOADOUT_FLAG_ALLOW_RESKIN)
if(reskin_datum)
displayed_text[FA_ICON_SWATCHBOOK] = "Reskinnable"
if(required_holiday)
@@ -473,16 +463,17 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
* Returns a list of options this item can be reskinned into.
*/
/datum/loadout_item/proc/get_reskin_options() as /list
if(!(loadout_flags & LOADOUT_FLAG_ALLOW_RESKIN))
if(!reskin_datum)
return null
var/list/reskins = list()
for(var/skin in cached_reskin_options)
for(var/datum/atom_skin/skin as anything in valid_subtypesof(reskin_datum))
UNTYPED_LIST_ADD(reskins, list(
"name" = skin,
"tooltip" = skin,
"skin_icon_state" = cached_reskin_options[skin],
"name" = skin::new_name || skin::preview_name,
"tooltip" = skin::preview_name,
"skin_icon" = skin::new_icon,
"skin_icon_state" = skin::new_icon_state,
))
return reskins
@@ -12,6 +12,7 @@
suicidal miners against local fauna."
icon = 'icons/obj/mining.dmi'
icon_state = "crusher"
base_icon_state = "crusher"
inhand_icon_state = "crusher0"
icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi'
@@ -56,25 +57,43 @@
var/detonation_damage = 50
/// Damage that the mark additionally does when hit by the crusher via backstab
var/backstab_bonus = 30
/// Used by retool kits when changing the crusher's appearance
var/current_inhand_icon_state = "crusher"
/// The file in which our projectile icon resides
var/projectile_icon = 'icons/obj/weapons/guns/projectiles.dmi'
/// Used by retool kits when changing the crusher's projectile sprite
var/projectile_icon_state = "pulse1"
/// Wielded damage we deal, aka our "real" damage
var/force_wielded = 20
/// Set to TRUE if the last projectile fired was point-blank at a living target
var/last_projectile_pb = FALSE
/obj/item/kinetic_crusher/Initialize(mapload)
. = ..()
AddComponent(/datum/component/butchering, \
AddComponent( \
/datum/component/butchering, \
speed = 6 SECONDS, \
effectiveness = 110, \
)
update_reskin(null)
//technically it's huge and bulky, but this provides an incentive to use it
update_wielding()
register_context()
/**
* Adds or updates the reskinning component on the crusher.
*
* * default_skin_typepath: The typepath of skin to apply by default.
* Passing null will either not apply a skin or will reset it to default if one is already applied.
* If a supplied skin is blacklisted, it will be un-blacklisted.
*/
/obj/item/kinetic_crusher/proc/update_reskin(datum/atom_skin/crusher_skin/default_skin_typepath)
AddComponent( \
/datum/component/reskinable_item, \
/datum/atom_skin/crusher_skin, \
infinite = TRUE, \
initial_skin = default_skin_typepath ? default_skin_typepath::preview_name : null, \
blacklisted_subtypes = subtypesof(/datum/atom_skin/crusher_skin/locked) - default_skin_typepath, \
)
/obj/item/kinetic_crusher/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = ..()
if(!held_item)
@@ -236,6 +255,7 @@
var/turf/proj_turf = user.loc
if(!isturf(proj_turf))
return
last_projectile_pb = get_dist(target, user) <= 1 && isliving(target)
var/obj/projectile/destabilizer/destabilizer = new(proj_turf)
SEND_SIGNAL(src, COMSIG_CRUSHER_FIRED_BLAST, target, user, destabilizer)
destabilizer.icon = projectile_icon
@@ -281,7 +301,7 @@
return TRUE
/obj/item/kinetic_crusher/update_icon_state()
inhand_icon_state = "[current_inhand_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]" // this is not icon_state and not supported by 2hcomponent
inhand_icon_state = "[base_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]" // this is not icon_state and not supported by 2hcomponent
return ..()
/obj/item/kinetic_crusher/update_overlays()
@@ -1,164 +1,95 @@
/*!
* Contains crusher trophies that are not obtained from fauna
*/
/// Cosmetic items for changing the crusher's look
/obj/item/crusher_trophy/retool_kit
name = "crusher retool kit"
desc = "A toolkit for changing the crusher's appearance without affecting the device's function."
icon = 'icons/obj/mining.dmi'
icon_state = "retool_kit"
denied_type = /obj/item/crusher_trophy/retool_kit
/// Currently picked crusher reskin
var/datum/crusher_skin/active_skin = /datum/crusher_skin/sword
/// If this kit forces some specific skin, or can pick between subtypes
var/forced_skin
/obj/item/crusher_trophy/retool_kit/Destroy(force)
if (istype(active_skin))
QDEL_NULL(active_skin)
return ..()
/obj/item/crusher_trophy/retool_kit/effect_desc()
return "the crusher to have the appearance of \a [active_skin::name]"
/obj/item/crusher_trophy/retool_kit/add_to(obj/item/kinetic_crusher/pkc, mob/user)
if (!forced_skin)
var/list/choices = list()
for (var/datum/crusher_skin/skin as anything in subtypesof(/datum/crusher_skin))
if (skin::normal_skin)
choices[skin] = icon(skin::retool_icon || 'icons/obj/mining.dmi', skin::retool_icon_state)
var/datum/crusher_skin/chosen_skin = show_radial_menu(user, src, choices, tooltips = TRUE, require_near = TRUE)
if (!chosen_skin)
return
active_skin = chosen_skin
else
active_skin = forced_skin
. = ..()
if(!.)
return
active_skin = new active_skin(pkc)
if (active_skin.retool_icon)
pkc.icon = active_skin.retool_icon
pkc.icon_state = active_skin.retool_icon_state
pkc.current_inhand_icon_state = active_skin.retool_inhand_icon
if (active_skin.retool_projectile_icon)
pkc.projectile_icon = active_skin.retool_projectile_icon
if (active_skin.retool_projectile_icon_state)
pkc.projectile_icon_state = active_skin.retool_projectile_icon_state
// Should either have both, or neither
if (active_skin.retool_lefthand_file)
pkc.lefthand_file = active_skin.retool_lefthand_file
pkc.righthand_file = active_skin.retool_righthand_file
if(active_skin.retool_worn_file)
pkc.worn_icon = active_skin.retool_worn_file
pkc.worn_icon_state = active_skin::retool_icon_state
if (active_skin.retool_inhand_x)
pkc.inhand_x_dimension = active_skin.retool_inhand_x
if (active_skin.retool_inhand_y)
pkc.inhand_y_dimension = active_skin.retool_inhand_y
pkc.update_appearance()
pkc.update_slot_icon()
/obj/item/crusher_trophy/retool_kit/remove_from(obj/item/kinetic_crusher/pkc)
var/skin_type = active_skin.type
qdel(active_skin)
active_skin = skin_type
pkc.icon = initial(pkc.icon)
pkc.icon_state = initial(pkc.icon_state)
pkc.current_inhand_icon_state = initial(pkc.current_inhand_icon_state)
pkc.projectile_icon = initial(pkc.projectile_icon)
pkc.projectile_icon_state = initial(pkc.projectile_icon_state)
pkc.lefthand_file = initial(pkc.lefthand_file)
pkc.righthand_file = initial(pkc.righthand_file)
pkc.worn_icon = initial(pkc.worn_icon)
pkc.worn_icon_state = initial(pkc.worn_icon_state)
pkc.inhand_x_dimension = initial(pkc.inhand_x_dimension)
pkc.inhand_y_dimension = initial(pkc.inhand_y_dimension)
pkc.update_appearance()
pkc.update_slot_icon()
return ..()
/// Alternate PKC skins
/datum/crusher_skin
/// Name of the modification
var/name = "error that should be reported to coders"
/// Specifies the icon file in which the crusher's new state is stored.
var/retool_icon = 'icons/obj/mining.dmi'
///Specifies the sprite/icon state which the crusher is changed to as an item. Should appear in the icons/obj/mining.dmi file with accompanying "lit" and "recharging" sprites
var/retool_icon_state = "ipickaxe"
///Specifies the icon state for the crusher's appearance in hand. Should appear in both retool_lefthand_file and retool_righthand_file.
var/retool_inhand_icon = "ipickaxe"
// Alternate PKC skins
/datum/atom_skin/crusher_skin
abstract_type = /datum/atom_skin/crusher_skin
change_base_icon_state = TRUE
new_icon = 'icons/obj/mining.dmi'
new_icon_state = "ipickaxe"
/// Specifies the icon state for the crusher's appearance in hand. Should appear in both new_lefthand_file and new_righthand_file.
var/new_inhand_icon = "ipickaxe"
/// Specifies the icon file in which the crusher's projectile sprite is located.
var/retool_projectile_icon = 'icons/obj/weapons/guns/projectiles.dmi'
///For if the retool kit changes the projectile's appearance.
var/retool_projectile_icon_state = null
var/new_projectile_icon = 'icons/obj/weapons/guns/projectiles.dmi'
/// For if the retool kit changes the projectile's appearance.
var/new_projectile_icon_state
/// Specifies the left hand inhand icon file. Don't forget to set the right hand file as well.
var/retool_lefthand_file = null
var/new_lefthand_file
/// Specifies the right hand inhand icon file. Don't forget to set the left hand file as well.
var/retool_righthand_file = null
var/new_righthand_file
/// Specifies the worn icon file.
var/retool_worn_file = null
var/new_worn_file
/// Specifies the X dimensions of the new inhand, only relevant with different inhand files.
var/retool_inhand_x = null
var/new_inhandx
/// Specifies the Y dimensions of the new inhand, only relevant with different inhand files.
var/retool_inhand_y = null
/// Can this skin be normally selected by a generic retool kit?
var/normal_skin = TRUE
/// Crusher this skin is attached to
var/obj/item/kinetic_crusher/crusher
var/new_inhandy
/datum/crusher_skin/New(obj/item/kinetic_crusher/new_crusher)
/datum/atom_skin/crusher_skin/apply(obj/item/kinetic_crusher/apply_to)
. = ..()
crusher = new_crusher
APPLY_VAR_OR_RESET_INITIAL(apply_to, inhand_icon_state, new_inhand_icon, reset_missing)
APPLY_VAR_OR_RESET_INITIAL(apply_to, projectile_icon, new_projectile_icon, reset_missing)
APPLY_VAR_OR_RESET_INITIAL(apply_to, projectile_icon_state, new_projectile_icon_state, reset_missing)
APPLY_VAR_OR_RESET_INITIAL(apply_to, lefthand_file, new_lefthand_file, reset_missing)
APPLY_VAR_OR_RESET_INITIAL(apply_to, righthand_file, new_righthand_file, reset_missing)
APPLY_VAR_OR_RESET_INITIAL(apply_to, worn_icon, new_worn_file, reset_missing)
APPLY_VAR_OR_RESET_INITIAL(apply_to, worn_icon_state, new_icon_state, reset_missing)
APPLY_VAR_OR_RESET_INITIAL(apply_to, inhand_x_dimension, new_inhandx, reset_missing)
APPLY_VAR_OR_RESET_INITIAL(apply_to, inhand_y_dimension, new_inhandy, reset_missing)
/datum/crusher_skin/Destroy(force)
crusher = null
return ..()
/datum/crusher_skin/sword
name = "sword"
retool_icon_state = "crusher_sword"
retool_inhand_icon = "crusher_sword"
/datum/crusher_skin/harpoon
name = "harpoon"
retool_icon_state = "crusher_harpoon"
retool_inhand_icon = "crusher_harpoon"
retool_projectile_icon_state = "pulse_harpoon"
/datum/crusher_skin/harpoon/New(obj/item/kinetic_crusher/new_crusher)
/datum/atom_skin/crusher_skin/clear_skin(obj/item/kinetic_crusher/clear_from)
. = ..()
RegisterSignal(crusher, COMSIG_ITEM_ATTACK_ANIMATION, PROC_REF(on_attack_animation))
RESET_INITIAL_IF_SET(clear_from, inhand_icon_state, new_inhand_icon)
RESET_INITIAL_IF_SET(clear_from, projectile_icon, new_projectile_icon)
RESET_INITIAL_IF_SET(clear_from, projectile_icon_state, new_projectile_icon_state)
RESET_INITIAL_IF_SET(clear_from, lefthand_file, new_lefthand_file)
RESET_INITIAL_IF_SET(clear_from, righthand_file, new_righthand_file)
RESET_INITIAL_IF_SET(clear_from, worn_icon, new_worn_file)
RESET_INITIAL_IF_SET(clear_from, worn_icon_state, new_icon_state)
RESET_INITIAL_IF_SET(clear_from, inhand_x_dimension, new_inhandx)
RESET_INITIAL_IF_SET(clear_from, inhand_y_dimension, new_inhandy)
/datum/crusher_skin/harpoon/Destroy(force)
UnregisterSignal(crusher, COMSIG_ITEM_ATTACK_ANIMATION)
return ..()
/datum/atom_skin/crusher_skin/sword
new_name = "proto-kinetic sword"
preview_name = "Sword"
new_icon_state = "crusher_sword"
new_inhand_icon = "crusher_sword"
/datum/crusher_skin/harpoon/proc/on_attack_animation(obj/item/source, atom/movable/attacker, atom/attacked_atom, animation_type, list/image_override, list/animation_override)
/datum/atom_skin/crusher_skin/harpoon
new_name = "proto-kinetic harpoon"
preview_name = "Harpoon"
new_icon_state = "crusher_harpoon"
new_inhand_icon = "crusher_harpoon"
new_projectile_icon_state = "pulse_harpoon"
/datum/atom_skin/crusher_skin/harpoon/apply(atom/apply_to)
. = ..()
RegisterSignal(apply_to, COMSIG_ITEM_ATTACK_ANIMATION, PROC_REF(on_attack_animation))
/datum/atom_skin/crusher_skin/harpoon/clear_skin(atom/clear_from)
. = ..()
UnregisterSignal(clear_from, COMSIG_ITEM_ATTACK_ANIMATION)
/datum/atom_skin/crusher_skin/harpoon/proc/on_attack_animation(obj/item/source, atom/movable/attacker, atom/attacked_atom, animation_type, list/image_override, list/animation_override)
SIGNAL_HANDLER
// If nothing is forcing an animation type, attack with a piercing animation because we're a harpoon
if (!animation_type)
animation_override += ATTACK_ANIMATION_PIERCE
/datum/crusher_skin/dagger
name = "dual dagger and blaster"
retool_icon_state = "crusher_dagger"
retool_inhand_icon = "crusher_dagger"
/// Are we doing a blaster animation right now?
var/blaster_strike = FALSE
/datum/atom_skin/crusher_skin/dagger
new_name = "proto-kinetic dual dagger and blaster"
preview_name = "Dagger and Blaster"
new_icon_state = "crusher_dagger"
new_inhand_icon = "crusher_dagger"
/datum/crusher_skin/dagger/New(obj/item/kinetic_crusher/new_crusher)
/datum/atom_skin/crusher_skin/dagger/apply(atom/apply_to)
. = ..()
RegisterSignal(crusher, COMSIG_ITEM_ATTACK_ANIMATION, PROC_REF(on_attack_animation))
RegisterSignal(crusher, COMSIG_CRUSHER_FIRED_BLAST, PROC_REF(on_fired_blast))
RegisterSignal(apply_to, COMSIG_ITEM_ATTACK_ANIMATION, PROC_REF(on_attack_animation))
RegisterSignal(apply_to, COMSIG_CRUSHER_FIRED_BLAST, PROC_REF(on_fired_blast))
/datum/crusher_skin/dagger/Destroy(force)
UnregisterSignal(crusher, list(COMSIG_ITEM_ATTACK_ANIMATION, COMSIG_CRUSHER_FIRED_BLAST))
return ..()
/datum/atom_skin/crusher_skin/dagger/clear_skin(atom/clear_from)
. = ..()
UnregisterSignal(clear_from, COMSIG_ITEM_ATTACK_ANIMATION)
UnregisterSignal(clear_from, COMSIG_CRUSHER_FIRED_BLAST)
/datum/crusher_skin/dagger/proc/on_attack_animation(obj/item/kinetic_crusher/source, atom/movable/attacker, atom/attacked_atom, animation_type, list/image_override, list/animation_override, list/angle_override)
/datum/atom_skin/crusher_skin/dagger/proc/on_attack_animation(obj/item/kinetic_crusher/source, atom/movable/attacker, atom/attacked_atom, animation_type, list/image_override, list/animation_override, list/angle_override)
SIGNAL_HANDLER
// If we've been forcefully assigned an animation type already, we shouldn't do the custom attack animation logic
@@ -167,11 +98,11 @@
if (isliving(attacked_atom))
var/mob/living/target = attacked_atom
if (blaster_strike)
if (source.last_projectile_pb)
image_override += image(icon = 'icons/obj/mining.dmi', icon_state = "crusher_dagger_blaster")
angle_override += 0
animation_override += ATTACK_ANIMATION_PIERCE
blaster_strike = FALSE
source.last_projectile_pb = FALSE
return
if (target.has_status_effect(/datum/status_effect/crusher_mark))
@@ -179,33 +110,62 @@
image_override += image(icon = 'icons/obj/mining.dmi', icon_state = "crusher_dagger_melee")
/datum/crusher_skin/dagger/proc/on_fired_blast(obj/item/kinetic_crusher/source, atom/target, mob/living/user, obj/projectile/destabilizer/destabilizer)
/datum/atom_skin/crusher_skin/dagger/proc/on_fired_blast(obj/item/kinetic_crusher/source, atom/target, mob/living/user, obj/projectile/destabilizer/destabilizer)
SIGNAL_HANDLER
if (isliving(target) && get_dist(target, user) <= 1)
blaster_strike = TRUE
user.do_item_attack_animation(target, used_item = source)
/datum/crusher_skin/glaive
name = "glaive"
retool_icon_state = "crusher_glaive"
retool_inhand_icon = "crusher_glaive"
retool_lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
retool_righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
retool_inhand_x = 64
retool_inhand_y = 64
/datum/atom_skin/crusher_skin/glaive
new_name = "proto-kinetic glaive"
preview_name = "Glaive"
new_icon_state = "crusher_glaive"
new_inhand_icon = "crusher_glaive"
new_lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
new_righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
new_inhandx = 64
new_inhandy = 64
// Locked skins that cannot be selected normally
/datum/atom_skin/crusher_skin/locked
abstract_type = /datum/atom_skin/crusher_skin/locked
/datum/atom_skin/crusher_skin/locked/ashen_skull
preview_name = "Skull"
new_icon_state = "crusher_skull"
new_inhand_icon = "crusher_skull"
new_projectile_icon_state = "pulse_skull"
/// Unlockable (or forced) skins
/obj/item/crusher_trophy/retool_kit
name = "crusher retool kit"
desc = "A toolkit for changing the crusher's appearance without affecting the device's function."
icon = 'icons/obj/mining.dmi'
icon_state = "retool_kit"
denied_type = /obj/item/crusher_trophy/retool_kit
/// What skin do we apply when attached
var/datum/atom_skin/crusher_skin/forced_skin
/obj/item/crusher_trophy/retool_kit/effect_desc()
return "the crusher to have the appearance of \a [forced_skin::preview_name]"
/obj/item/crusher_trophy/retool_kit/add_to(obj/item/kinetic_crusher/pkc, mob/user)
. = ..()
if(!.)
return
pkc.update_reskin(forced_skin)
/obj/item/crusher_trophy/retool_kit/remove_from(obj/item/kinetic_crusher/pkc)
pkc.update_reskin(null) // resets reskin component
return ..()
/obj/item/crusher_trophy/retool_kit/ashenskull
name = "ashen skull"
desc = "It burns with the flame of the necropolis, whispering in your ear. It demands to be bound to a suitable weapon."
icon_state = "retool_kit_skull"
forced_skin = /datum/crusher_skin/ashen_skull
forced_skin = /datum/atom_skin/crusher_skin/locked/ashen_skull
/obj/item/crusher_trophy/retool_kit/ashenskull/effect_desc()
return "the crusher to appear corrupted by infernal powers"
/datum/crusher_skin/ashen_skull
retool_icon_state = "crusher_skull"
retool_inhand_icon = "crusher_skull"
retool_projectile_icon_state = "pulse_skull"
normal_skin = FALSE
+18 -6
View File
@@ -1,3 +1,20 @@
// Clipboard skins
/datum/atom_skin/clipboard
abstract_type = /datum/atom_skin/clipboard
change_inhand_icon_state = TRUE
/datum/atom_skin/clipboard/brown
preview_name = "Brown"
new_icon_state = "clipboard"
/datum/atom_skin/clipboard/black
preview_name = "Black"
new_icon_state = "clipboard_black"
/datum/atom_skin/clipboard/white
preview_name = "White"
new_icon_state = "clipboard_white"
/**
* Clipboard
*/
@@ -15,12 +32,6 @@
resistance_flags = FLAMMABLE
custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT, /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT)
unique_reskin = list(
"Brown" = "clipboard",
"Black" = "clipboard_black",
"White" = "clipboard_white",
)
unique_reskin_changes_inhand = TRUE
/// The stored pen
var/obj/item/pen/pen
@@ -40,6 +51,7 @@
/obj/item/clipboard/Initialize(mapload)
update_appearance()
. = ..()
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/clipboard)
/obj/item/clipboard/Destroy()
QDEL_NULL(pen)
+46 -24
View File
@@ -177,6 +177,38 @@
time = 3 SECONDS
category = CAT_TOOLS
// Skins for captain's fountain pen
/datum/atom_skin/cap_pen
abstract_type = /datum/atom_skin/cap_pen
/datum/atom_skin/cap_pen/apply(atom/apply_to)
. = ..()
apply_to.desc = "It's an expensive [preview_name] fountain pen. The nib is quite sharp."
/datum/atom_skin/cap_pen/clear_skin(atom/clear_from)
. = ..()
clear_from.desc = initial(clear_from.desc)
/datum/atom_skin/cap_pen/oak
preview_name = "Oak"
new_icon_state = "pen-fountain-o"
/datum/atom_skin/cap_pen/gold
preview_name = "Gold"
new_icon_state = "pen-fountain-g"
/datum/atom_skin/cap_pen/rosewood
preview_name = "Rosewood"
new_icon_state = "pen-fountain-r"
/datum/atom_skin/cap_pen/black_silver
preview_name = "Black and Silver"
new_icon_state = "pen-fountain-b"
/datum/atom_skin/cap_pen/command_blue
preview_name = "Command Blue"
new_icon_state = "pen-fountain-cb"
/obj/item/pen/fountain/captain
name = "captain's fountain pen"
desc = "It's an expensive Oak fountain pen. The nib is quite sharp."
@@ -187,22 +219,15 @@
custom_materials = list(/datum/material/gold = SMALL_MATERIAL_AMOUNT*7.5)
sharpness = SHARP_EDGED
resistance_flags = FIRE_PROOF
unique_reskin = list(
"Oak" = "pen-fountain-o",
"Gold" = "pen-fountain-g",
"Rosewood" = "pen-fountain-r",
"Black and Silver" = "pen-fountain-b",
"Command Blue" = "pen-fountain-cb"
)
embed_type = /datum/embedding/pen/captain
dart_insert_casing_icon_state = "overlay_fountainpen_gold"
dart_insert_projectile_icon_state = "overlay_fountainpen_gold_proj"
var/list/overlay_reskin = list(
"Oak" = "overlay_fountainpen_gold",
"Gold" = "overlay_fountainpen_gold",
"Rosewood" = "overlay_fountainpen_gold",
"Black and Silver" = "overlay_fountainpen",
"Command Blue" = "overlay_fountainpen_gold"
/datum/atom_skin/cap_pen/black_silver::preview_name = "overlay_fountainpen",
/datum/atom_skin/cap_pen/command_blue::preview_name = "overlay_fountainpen_gold",
/datum/atom_skin/cap_pen/gold::preview_name = "overlay_fountainpen_gold",
/datum/atom_skin/cap_pen/oak::preview_name = "overlay_fountainpen_gold",
/datum/atom_skin/cap_pen/rosewood::preview_name = "overlay_fountainpen_gold",
)
/datum/embedding/pen/captain
@@ -210,24 +235,21 @@
/obj/item/pen/fountain/captain/Initialize(mapload)
. = ..()
AddComponent(/datum/component/butchering, \
speed = 20 SECONDS, \
effectiveness = 115, \
AddComponent( \
/datum/component/butchering, \
speed = 20 SECONDS, \
effectiveness = 115, \
)
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/cap_pen)
//the pen is mightier than the sword
RegisterSignal(src, COMSIG_DART_INSERT_PARENT_RESKINNED, PROC_REF(reskin_dart_insert))
/obj/item/pen/fountain/captain/reskin_obj(mob/M)
..()
if(current_skin)
desc = "It's an expensive [current_skin] fountain pen. The nib is quite sharp."
/obj/item/pen/fountain/captain/proc/reskin_dart_insert(datum/component/dart_insert/insert_comp)
/obj/item/pen/fountain/captain/proc/reskin_dart_insert(datum/component/dart_insert/insert_comp, skin)
SIGNAL_HANDLER
if(!istype(insert_comp)) //You really shouldn't be sending this signal from anything other than a dart_insert component
return
insert_comp.casing_overlay_icon_state = overlay_reskin[current_skin]
insert_comp.projectile_overlay_icon_state = "[overlay_reskin[current_skin]]_proj"
insert_comp.casing_overlay_icon_state = overlay_reskin[skin]
insert_comp.projectile_overlay_icon_state = "[overlay_reskin[skin]]_proj"
/obj/item/pen/item_ctrl_click(mob/living/carbon/user)
if(loc != user)
@@ -9,7 +9,6 @@
pickup_sound = 'sound/items/handling/gun/gun_pick_up.ogg'
drop_sound = 'sound/items/handling/gun/gun_drop.ogg'
sound_vary = TRUE
unique_reskin_changes_base_icon_state = TRUE
min_recoil = 0.1
@@ -97,8 +97,7 @@
. = ..()
var/live_ammo = get_ammo(FALSE, FALSE)
. += "[live_ammo ? live_ammo : "None"] of those are live rounds."
if (current_skin)
. += span_notice("It can be spun with [EXAMINE_HINT("alt-click")].")
. += span_notice("It can be spun with [EXAMINE_HINT("alt-click")].")
/obj/item/gun/ballistic/revolver/ignition_effect(atom/A, mob/user)
if(last_fire && last_fire + 15 SECONDS > world.time)
@@ -112,6 +111,47 @@
base_icon_state = "c38"
fire_sound = 'sound/items/weapons/gun/revolver/shot.ogg'
// 38 special skins
/datum/atom_skin/det_revolver
abstract_type = /datum/atom_skin/det_revolver
change_base_icon_state = TRUE
/datum/atom_skin/det_revolver/default
preview_name = "Default"
new_icon_state = "c38"
/datum/atom_skin/det_revolver/fitz_special
preview_name = "Fitz Special"
new_icon_state = "c38_fitz"
/datum/atom_skin/det_revolver/police_positive_special
preview_name = "Police Positive Special"
new_icon_state = "c38_police"
/datum/atom_skin/det_revolver/blued_steel
preview_name = "Blued Steel"
new_icon_state = "c38_blued"
/datum/atom_skin/det_revolver/stainless_steel
preview_name = "Stainless Steel"
new_icon_state = "c38_stainless"
/datum/atom_skin/det_revolver/gold_trim
preview_name = "Gold Trim"
new_icon_state = "c38_trim"
/datum/atom_skin/det_revolver/golden
preview_name = "Golden"
new_icon_state = "c38_gold"
/datum/atom_skin/det_revolver/peacemaker
preview_name = "The Peacemaker"
new_icon_state = "c38_peacemaker"
/datum/atom_skin/det_revolver/black_panther
preview_name = "Black Panther"
new_icon_state = "c38_panther"
/obj/item/gun/ballistic/revolver/c38/detective
name = "\improper Colt Detective Special"
desc = "A classic, if not outdated, law enforcement firearm. Uses .38 Special rounds. \nSome spread rumors that if you loosen the barrel with a wrench, you can \"improve\" it."
@@ -126,17 +166,10 @@
misfire_percentage_increment = 25 //about 1 in 4 rounds, which increases rapidly every shot
obj_flags = UNIQUE_RENAME
unique_reskin = list(
"Default" = "c38",
"Fitz Special" = "c38_fitz",
"Police Positive Special" = "c38_police",
"Blued Steel" = "c38_blued",
"Stainless Steel" = "c38_stainless",
"Gold Trim" = "c38_trim",
"Golden" = "c38_gold",
"The Peacemaker" = "c38_peacemaker",
"Black Panther" = "c38_panther"
)
/obj/item/gun/ballistic/revolver/c38/detective/Initialize(mapload)
. = ..()
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/det_revolver)
/obj/item/gun/ballistic/revolver/badass
name = "\improper Badass Revolver"
@@ -285,6 +285,35 @@
/obj/item/gun/ballistic/shotgun/bulldog/unrestricted
pin = /obj/item/firing_pin
// Double barrel shotgun skins
/datum/atom_skin/bar_shotgun
abstract_type = /datum/atom_skin/bar_shotgun
change_base_icon_state = TRUE
/datum/atom_skin/bar_shotgun/default
preview_name = "Standard Finish"
new_icon_state = "dshotgun"
/datum/atom_skin/bar_shotgun/dark_red
preview_name = "Dark Red Finish"
new_icon_state = "dshotgun_d"
/datum/atom_skin/bar_shotgun/ash
preview_name = "Ash"
new_icon_state = "dshotgun_f"
/datum/atom_skin/bar_shotgun/faded_grey
preview_name = "Faded Grey"
new_icon_state = "dshotgun_g"
/datum/atom_skin/bar_shotgun/maple
preview_name = "Maple"
new_icon_state = "dshotgun_l"
/datum/atom_skin/bar_shotgun/rosewood
preview_name = "Rosewood"
new_icon_state = "dshotgun_p"
/////////////////////////////
// DOUBLE BARRELED SHOTGUN //
/////////////////////////////
@@ -304,18 +333,15 @@
sawn_desc = "Omar's coming!"
obj_flags = UNIQUE_RENAME
rack_sound_volume = 0
unique_reskin = list("Default" = "dshotgun",
"Dark Red Finish" = "dshotgun_d",
"Ash" = "dshotgun_f",
"Faded Grey" = "dshotgun_g",
"Maple" = "dshotgun_l",
"Rosewood" = "dshotgun_p"
)
semi_auto = TRUE
bolt_type = BOLT_TYPE_NO_BOLT
can_be_sawn_off = TRUE
pb_knockback = 3 // it's a super shotgun!
/obj/item/gun/ballistic/shotgun/doublebarrel/Initialize(mapload)
. = ..()
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/bar_shotgun)
/obj/item/gun/ballistic/shotgun/doublebarrel/sawoff(mob/user)
. = ..()
if(.)
@@ -1,3 +1,31 @@
// Gel skins
/datum/atom_skin/med_gel
abstract_type = /datum/atom_skin/med_gel
/datum/atom_skin/med_gel/blue
preview_name = "Blue"
new_icon_state = "medigel_blue"
/datum/atom_skin/med_gel/cyan
preview_name = "Cyan"
new_icon_state = "medigel_cyan"
/datum/atom_skin/med_gel/green
preview_name = "Green"
new_icon_state = "medigel_green"
/datum/atom_skin/med_gel/red
preview_name = "Red"
new_icon_state = "medigel_red"
/datum/atom_skin/med_gel/orange
preview_name = "Orange"
new_icon_state = "medigel_orange"
/datum/atom_skin/med_gel/purple
preview_name = "Purple"
new_icon_state = "medigel_purple"
/obj/item/reagent_containers/medigel
name = "medical gel"
desc = "A medical gel applicator bottle, designed for precision application, with an unscrewable cap."
@@ -23,15 +51,11 @@
var/apply_method = "spray" //the thick gel is sprayed and then dries into patch like film.
var/self_delay = 30
custom_price = PAYCHECK_CREW * 2
unique_reskin = list(
"Blue" = "medigel_blue",
"Cyan" = "medigel_cyan",
"Green" = "medigel_green",
"Red" = "medigel_red",
"Orange" = "medigel_orange",
"Purple" = "medigel_purple"
)
/obj/item/reagent_containers/medigel/Initialize(mapload)
. = ..()
if(icon_state == "medigel") // oh yeah baby raw icon state check to make sure we can't reskin preset gels
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/med_gel)
/obj/item/reagent_containers/medigel/mode_change_message(mob/user)
var/squirt_mode = amount_per_transfer_from_this == initial(amount_per_transfer_from_this)
@@ -78,21 +102,18 @@
name = "medical gel (libital)"
desc = "A medical gel applicator bottle, designed for precision application, with an unscrewable cap. This one contains libital, for treating cuts and bruises. Libital does minor liver damage. Diluted with granibitaluri."
icon_state = "brutegel"
current_skin = "brutegel"
list_reagents = list(/datum/reagent/medicine/c2/libital = 24, /datum/reagent/medicine/granibitaluri = 36)
/obj/item/reagent_containers/medigel/aiuri
name = "medical gel (aiuri)"
desc = "A medical gel applicator bottle, designed for precision application, with an unscrewable cap. This one contains aiuri, useful for treating burns. Aiuri does minor eye damage. Diluted with granibitaluri."
icon_state = "burngel"
current_skin = "burngel"
list_reagents = list(/datum/reagent/medicine/c2/aiuri = 24, /datum/reagent/medicine/granibitaluri = 36)
/obj/item/reagent_containers/medigel/synthflesh
name = "medical gel (synthflesh)"
desc = "A medical gel applicator bottle, designed for precision application, with an unscrewable cap. This one contains synthflesh, a slightly toxic medicine capable of healing bruises, burns, and husks."
icon_state = "synthgel"
current_skin = "synthgel"
list_reagents = list(/datum/reagent/medicine/c2/synthflesh = 60)
list_reagents_purity = 1
amount_per_transfer_from_this = 60
@@ -117,6 +138,5 @@
name = "sterilizer gel"
desc = "gel bottle loaded with non-toxic sterilizer. Useful in preparation for surgery."
icon_state = "medigel_blue"
current_skin = "medigel_blue"
list_reagents = list(/datum/reagent/space_cleaner/sterilizine = 60)
custom_price = PAYCHECK_CREW * 2
@@ -420,6 +420,23 @@
. = ..()
icon_state = pick("sprayer_sus_1", "sprayer_sus_2", "sprayer_sus_3", "sprayer_sus_4", "sprayer_sus_5","sprayer_sus_6", "sprayer_sus_7", "sprayer_sus_8")
// Spray bottle skins
/datum/atom_skin/med_spray
abstract_type = /datum/atom_skin/med_spray
change_inhand_icon_state = TRUE
/datum/atom_skin/med_spray/red
preview_name = "Red"
new_icon_state = "sprayer_med_red"
/datum/atom_skin/med_spray/yellow
preview_name = "Yellow"
new_icon_state = "sprayer_med_yellow"
/datum/atom_skin/med_spray/blue
preview_name = "Blue"
new_icon_state = "sprayer_med_blue"
/obj/item/reagent_containers/spray/medical
name = "medical spray bottle"
icon = 'icons/obj/medical/chemical.dmi'
@@ -428,20 +445,10 @@
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
volume = 100
unique_reskin = list("Red" = "sprayer_med_red",
"Yellow" = "sprayer_med_yellow",
"Blue" = "sprayer_med_blue")
/obj/item/reagent_containers/spray/medical/reskin_obj(mob/M)
..()
switch(icon_state)
if("sprayer_med_red")
inhand_icon_state = "sprayer_med_red"
if("sprayer_med_yellow")
inhand_icon_state = "sprayer_med_yellow"
if("sprayer_med_blue")
inhand_icon_state = "sprayer_med_blue"
M.update_held_items()
/obj/item/reagent_containers/spray/medical/Initialize(mapload, vol)
. = ..()
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/med_spray)
/obj/item/reagent_containers/spray/hercuri
name = "medical spray (hercuri)"
+1
View File
@@ -270,6 +270,7 @@
#include "recycle_recycling.dm"
#include "required_map_items.dm"
#include "resist.dm"
#include "reskin_validation.dm"
#include "reta_system.dm"
#include "say.dm"
#include "screenshot_airlocks.dm"
@@ -0,0 +1,15 @@
/datum/unit_test/reskin_validation
/datum/unit_test/reskin_validation/Run()
var/list/known_names = list()
for(var/datum/atom_skin/skin as anything in valid_subtypesof(/datum/atom_skin))
if(isnull(skin::preview_name))
TEST_FAIL("Reskin [skin] is missing a preview_name.")
// preview names are bundled by abstract types
else if(known_names["[skin::preview_name]-[skin::abstract_type]"])
TEST_FAIL("Reskin [skin] has a duplicate preview_name [skin::preview_name].")
else
known_names["[skin::preview_name]-[skin::abstract_type]"] = TRUE
if(skin::new_icon && skin::new_icon_state && !icon_exists(skin::new_icon, skin::new_icon_state))
TEST_FAIL("Reskin [skin] has a new_icon_state [skin::new_icon_state] that does not exist in file [skin::new_icon].")