mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-24 13:38:41 +01:00
Extends atom_reskin to be more modular/adds support for greyscale reskins in the loadout menu (#94466)
Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
/// Global list of available atom skins
|
||||
GLOBAL_LIST_INIT_TYPED(atom_skins, /datum/atom_skin, init_subtypes_w_path_keys(/datum/atom_skin))
|
||||
GLOBAL_LIST_EMPTY_TYPED(atom_skins, /datum/atom_skin)
|
||||
|
||||
/proc/get_atom_skins()
|
||||
if(!length(GLOB.atom_skins))
|
||||
GLOB.atom_skins = init_subtypes_w_path_keys(/datum/atom_skin)
|
||||
return GLOB.atom_skins
|
||||
|
||||
/// Sets the atom's varname to newvalue if newvalue is not null, otherwise resets it to its initial value if resetcondition is true
|
||||
#define APPLY_VAR_OR_RESET_INITIAL(atom, varname, newvalue, resetcondition) \
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
abstracts = list()
|
||||
for(var/datum/sometype as anything in subtypesof(/datum))
|
||||
if(sometype == sometype::abstract_type)
|
||||
abstracts |= sometype::abstract_type
|
||||
abstracts[sometype::abstract_type] = TRUE
|
||||
|
||||
return abstracts
|
||||
|
||||
/// Like subtypesof, but automatically excludes abstract typepaths
|
||||
@@ -16,3 +17,47 @@
|
||||
/// Like typesof, but automatically excludes abstract typepaths
|
||||
/proc/valid_typesof(datum/sometype)
|
||||
return typesof(sometype) - get_abstract_types()
|
||||
|
||||
/// Returns a list of concrete types under abstract sub-branches of `root`
|
||||
/proc/get_abstract_branch_descendants(datum/root)
|
||||
var/list/abstracts = get_abstract_types()
|
||||
var/list/to_remove = list()
|
||||
var/list/seen_abstract_parents = list()
|
||||
|
||||
for (var/datum/sometype as anything in subtypesof(root))
|
||||
if (abstracts[sometype])
|
||||
continue
|
||||
|
||||
var/datum/parenttype = sometype.parent_type
|
||||
while (parenttype && parenttype != root)
|
||||
if (seen_abstract_parents[parenttype] || (abstracts[parenttype]))
|
||||
seen_abstract_parents[parenttype] = TRUE
|
||||
to_remove += sometype
|
||||
break
|
||||
parenttype = parenttype.parent_type
|
||||
|
||||
return to_remove
|
||||
|
||||
/// Like valid_subtypesof(), but excludes concrete descendants of abstract sub-branches
|
||||
/proc/valid_direct_subtypesof(datum/root)
|
||||
var/list/result = subtypesof(root)
|
||||
|
||||
// Remove all abstract types
|
||||
result -= get_abstract_types()
|
||||
|
||||
// Remove concrete types under abstract sub-branches
|
||||
result -= get_abstract_branch_descendants(root)
|
||||
|
||||
return result
|
||||
|
||||
/// Like valid_typesof(), but excludes concrete descendants of abstract sub-branches
|
||||
/proc/valid_direct_typesof(datum/root)
|
||||
var/list/result = typesof(root)
|
||||
|
||||
// Remove all abstract types
|
||||
result -= get_abstract_types()
|
||||
|
||||
// Remove concrete types under abstract sub-branches
|
||||
result -= get_abstract_branch_descendants(root)
|
||||
|
||||
return result
|
||||
|
||||
@@ -16,13 +16,76 @@ SUBSYSTEM_DEF(greyscale_previews)
|
||||
if(!CONFIG_GET(flag/generate_assets_in_init))
|
||||
return SS_INIT_SUCCESS
|
||||
#endif
|
||||
|
||||
ExportMapPreviews()
|
||||
return SS_INIT_SUCCESS
|
||||
|
||||
/// Sets up the list of types to process for organizing icons into their respective .dmi.
|
||||
/datum/controller/subsystem/greyscale_previews/proc/build_type_category_map(list/types_that_get_their_own_file)
|
||||
var/list/type_to_filename = list()
|
||||
|
||||
for (var/filename in types_that_get_their_own_file)
|
||||
var/root = types_that_get_their_own_file[filename]
|
||||
|
||||
for (var/atom/atom_type as anything in typesof(root))
|
||||
// First match wins (prevents /obj from overwriting clothing buckets, etc.)
|
||||
if (isnull(type_to_filename[atom_type]))
|
||||
type_to_filename[atom_type] = filename
|
||||
|
||||
return type_to_filename
|
||||
|
||||
/// Builds a worklist of all the item types to try making a GAGS preview icon for.
|
||||
/datum/controller/subsystem/greyscale_previews/proc/build_preview_worklists(list/types_that_get_their_own_file)
|
||||
var/list/type_to_filename = build_type_category_map(types_that_get_their_own_file)
|
||||
|
||||
/// filename => list(entries)
|
||||
var/list/worklists = list()
|
||||
|
||||
for (var/filename in types_that_get_their_own_file)
|
||||
worklists[filename] = list()
|
||||
|
||||
worklists["unsorted"] = list()
|
||||
|
||||
/// ---- atom skins ----
|
||||
for (var/skin_path, atom_skin in get_atom_skins())
|
||||
var/datum/atom_skin/skin = atom_skin
|
||||
var/atom/typepath = skin.greyscale_item_path
|
||||
if (!GLOB.all_loadout_datums[typepath]) // We don't need reskin previews for non-loadout items
|
||||
continue
|
||||
if (isnull(skin.new_icon_state)) // This is the same as the default icon, which we will be generating below.
|
||||
continue
|
||||
if (!typepath::greyscale_config || !typepath::greyscale_colors)
|
||||
continue
|
||||
if (typepath::flags_1 & NO_NEW_GAGS_PREVIEW_1)
|
||||
continue
|
||||
|
||||
var/filename = type_to_filename[typepath] || "unsorted"
|
||||
worklists[filename] += skin
|
||||
|
||||
var/list/seen_typepaths = list()
|
||||
/// ---- base atom types ----
|
||||
for (var/filename, path in types_that_get_their_own_file)
|
||||
var/atom/root = path
|
||||
var/list/filename_worklist = worklists[filename]
|
||||
|
||||
for (var/atom/typepath as anything in valid_typesof(root))
|
||||
if (seen_typepaths[typepath])
|
||||
continue
|
||||
|
||||
seen_typepaths[typepath] = TRUE
|
||||
|
||||
if (!typepath::greyscale_config || !typepath::greyscale_colors)
|
||||
continue
|
||||
if (typepath::flags_1 & NO_NEW_GAGS_PREVIEW_1)
|
||||
continue
|
||||
|
||||
filename_worklist += typepath
|
||||
|
||||
return worklists
|
||||
|
||||
/// Goes through all the valid GAGS item types in subtypes that fall under the types specified in types_that_get_their_own_file, creating a .dmi for each.
|
||||
/datum/controller/subsystem/greyscale_previews/proc/ExportMapPreviews()
|
||||
// Put subtypes before their parent or the parent file will take all the generated icons
|
||||
var/static/list/types_that_get_their_own_file = list(
|
||||
var/list/types_that_get_their_own_file = list(
|
||||
"turfs" = /turf, // None of these yet but it's harmless to be prepared
|
||||
"mobs" = /mob, // Ditto
|
||||
"clothing/accessory" = /obj/item/clothing/accessory,
|
||||
@@ -45,27 +108,23 @@ SUBSYSTEM_DEF(greyscale_previews)
|
||||
)
|
||||
|
||||
#ifdef UNIT_TESTS
|
||||
if(!check_map_previews_filepath_order(types_that_get_their_own_file))
|
||||
if (!check_map_previews_filepath_order(types_that_get_their_own_file))
|
||||
CRASH("The list 'types_that_get_their_own_file', used by ExportMapPreviews, is invalid. Please ensure that subtypes come BEFORE parent types in the list order.")
|
||||
#endif
|
||||
|
||||
var/list/handled_types = list()
|
||||
for(var/filename in types_that_get_their_own_file)
|
||||
var/type_to_export = types_that_get_their_own_file[filename]
|
||||
handled_types += ExportMapPreviewsForType(filename, type_to_export, handled_types)
|
||||
|
||||
ExportMapPreviewsForType("unsorted", /atom, handled_types)
|
||||
var/list/worklists = build_preview_worklists(types_that_get_their_own_file)
|
||||
for (var/filename in worklists)
|
||||
ExportMapPreviewsForType(filename, worklists[filename])
|
||||
|
||||
/// Checks that we do not have any parent types coming before subtypes in the types_that_get_their_own_file list (which is an assoc list (filepath, typepath))
|
||||
/datum/controller/subsystem/greyscale_previews/proc/check_map_previews_filepath_order(list/our_list)
|
||||
var/list/type_paths_to_check = list()
|
||||
for(var/filepath in our_list)
|
||||
for (var/filepath in our_list)
|
||||
type_paths_to_check += our_list[filepath]
|
||||
|
||||
if(!length(type_paths_to_check))
|
||||
if (!length(type_paths_to_check))
|
||||
return TRUE
|
||||
|
||||
for(var/i = 1 to length(type_paths_to_check))
|
||||
for (var/i = 1 to length(type_paths_to_check))
|
||||
var/path_i = type_paths_to_check[i]
|
||||
for(var/j = i+1 to length(type_paths_to_check))
|
||||
var/path_j = type_paths_to_check[j]
|
||||
@@ -74,31 +133,49 @@ SUBSYSTEM_DEF(greyscale_previews)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/controller/subsystem/greyscale_previews/proc/ExportMapPreviewsForType(filename, atom/atom_typepath, list/type_blacklist)
|
||||
var/list/handled_types = list()
|
||||
/datum/controller/subsystem/greyscale_previews/proc/ExportMapPreviewsForType(filename, list/entries)
|
||||
var/list/icons = list()
|
||||
for(var/atom/atom_type as anything in typesof(atom_typepath))
|
||||
if(type_blacklist && type_blacklist[atom_type])
|
||||
|
||||
for (var/entry in entries)
|
||||
var/atom/typepath
|
||||
var/icon_state
|
||||
var/reskin_icon_state
|
||||
|
||||
if (istype(entry, /datum/atom_skin))
|
||||
var/datum/atom_skin/skin = entry
|
||||
typepath = skin.greyscale_item_path
|
||||
icon_state = skin.new_icon_state
|
||||
reskin_icon_state = TRUE
|
||||
else
|
||||
typepath = entry
|
||||
icon_state = typepath::post_init_icon_state
|
||||
|
||||
if (!typepath)
|
||||
continue
|
||||
handled_types[atom_type] = TRUE
|
||||
var/greyscale_config = atom_type::greyscale_config
|
||||
var/greyscale_colors = atom_type::greyscale_colors
|
||||
if(!greyscale_config || !greyscale_colors || atom_type::flags_1 & NO_NEW_GAGS_PREVIEW_1)
|
||||
|
||||
var/greyscale_config = typepath::greyscale_config
|
||||
var/greyscale_colors = typepath::greyscale_colors
|
||||
|
||||
if (!greyscale_config || !greyscale_colors || (typepath::flags_1 & NO_NEW_GAGS_PREVIEW_1))
|
||||
continue
|
||||
|
||||
// This is what the actual icon state will be in the map_icon .dmi
|
||||
var/key = reskin_icon_state ? "[typepath]--[icon_state]" : "[typepath]"
|
||||
|
||||
#ifdef CHECK_SPRITESHEET_ICON_VALIDITY
|
||||
var/icon/map_icon = icon(SSgreyscale.GetColoredIconByType(greyscale_config, greyscale_colors))
|
||||
if((map_icon.Height() > 32) || (map_icon.Width() > 32)) // No large icons, use icon_preview and icon_preview_state instead.
|
||||
stack_trace("GAGS configuration is trying to generate a map preview graphic for '[atom_type]', which has a large icon. This is not suppoorted; implement icon_preview instead.")
|
||||
var/icon/map_icon = icon(SSgreyscale.GetColoredIconByType(greyscale_config, greyscale_colors)) // No large icons, use icon_preview and icon_preview_state instead.
|
||||
if (map_icon.Width() > 32 || map_icon.Height() > 32)
|
||||
stack_trace("GAGS configuration is trying to generate a map preview graphic for '[typepath]' (icon state: [icon_state]), which has a large icon. This is not suppoorted; implement icon_preview instead.")
|
||||
continue
|
||||
if(!(atom_type::post_init_icon_state in map_icon.IconStates()))
|
||||
stack_trace("GAGS configuration missing icon state needed to generate map preview graphic for '[atom_type]'. Make sure the right greyscale_config is set up.")
|
||||
if (!(icon_state in map_icon.IconStates()))
|
||||
stack_trace("GAGS configuration missing icon state ([icon_state]) needed to generate map preview graphic for '[typepath]'. Make sure the right greyscale_config is set up.")
|
||||
continue
|
||||
map_icon = icon(map_icon, atom_type::post_init_icon_state)
|
||||
icons["[atom_type]"] = map_icon
|
||||
map_icon = icon(map_icon, icon_state)
|
||||
icons[key] = map_icon
|
||||
#else // will be updated to use iconforge's new .dmi spritesheet generation instead
|
||||
var/icon/map_icon = icon(SSgreyscale.GetColoredIconByType(greyscale_config, greyscale_colors))
|
||||
map_icon = icon(map_icon, atom_type::post_init_icon_state)
|
||||
icons["[atom_type]"] = map_icon
|
||||
map_icon = icon(map_icon, icon_state)
|
||||
icons[map_icon_key] = map_icon
|
||||
#endif
|
||||
|
||||
var/icon/holder = icon('icons/testing/greyscale_error.dmi')
|
||||
@@ -115,7 +192,6 @@ SUBSYSTEM_DEF(greyscale_previews)
|
||||
if(old_md5 != new_md5)
|
||||
stack_trace("Generated map icons were different than what is currently saved. If you see this in a CI run it means you need to run the game once through initialization and commit the resulting files in 'icons/map_icons/'")
|
||||
#endif
|
||||
return handled_types
|
||||
|
||||
#ifdef CHECK_SPRITESHEET_ICON_VALIDITY
|
||||
#undef CHECK_SPRITESHEET_ICON_VALIDITY
|
||||
|
||||
@@ -24,6 +24,44 @@
|
||||
var/new_icon
|
||||
/// Optional, icon_state to change the atom to when applied
|
||||
var/new_icon_state
|
||||
/// Specifies the icon state for the atom's appearance in hand. Should appear in both new_lefthand_file and new_righthand_file.
|
||||
var/new_inhand_icon_state
|
||||
/// Optional, specifies the left hand inhand icon file. Don't forget to set the right hand file as well.
|
||||
var/new_lefthand_file
|
||||
/// Optional, specifies the right hand inhand icon file. Don't forget to set the left hand file as well.
|
||||
var/new_righthand_file
|
||||
/// Optional, specifies the worn icon file.
|
||||
var/new_worn_icon
|
||||
/// When set true, will allow concrete subtypes of abstract subtypes (such as for organizational purposes) to be selectable as reskins in the loadout menu.
|
||||
var/allow_all_subtypes_in_loadout
|
||||
/// Mandatory for GAGs items. The path to the greyscale item this is to be applied to.
|
||||
var/atom/greyscale_item_path
|
||||
/// Auto populated. The greyscale_config from greyscale_item_path.
|
||||
VAR_FINAL/greyscale_config
|
||||
/// Auto populated. The greyscale_colors from greyscale_item_path.
|
||||
VAR_FINAL/greyscale_colors
|
||||
|
||||
/datum/atom_skin/New()
|
||||
. = ..()
|
||||
if(isnull(greyscale_item_path))
|
||||
return
|
||||
|
||||
// Populate the fields required for GAGS previews
|
||||
greyscale_config = greyscale_item_path::greyscale_config
|
||||
greyscale_colors = greyscale_item_path::greyscale_colors
|
||||
|
||||
// The icon isn't 'new'. So we don't make an extra icon in map_icons.
|
||||
if(greyscale_item_path::post_init_icon_state == new_icon_state)
|
||||
new_icon_state = null
|
||||
|
||||
/// Returns the correct preview icon state for this atom skin, whether it be a map_preview or a normal icon. This is assumed from our var population above.
|
||||
/datum/atom_skin/proc/get_preview_icon_state()
|
||||
if(isnull(greyscale_item_path)) // Not a GAGs icon
|
||||
return new_icon_state
|
||||
if(isnull(new_icon_state)) // The base GAGS item icon
|
||||
return "[greyscale_item_path]"
|
||||
|
||||
return "[greyscale_item_path]--[new_icon_state]" // A GAGs reskin of the item icon
|
||||
|
||||
/**
|
||||
* Applies all relevant skin changes to the given atom
|
||||
@@ -31,17 +69,26 @@
|
||||
*
|
||||
* * apply_to: The atom to apply the skin to
|
||||
*/
|
||||
/datum/atom_skin/proc/apply(atom/apply_to)
|
||||
/datum/atom_skin/proc/apply(atom/apply_to, mob/user)
|
||||
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)
|
||||
if(!HAS_TRAIT(apply_to, TRAIT_WAS_RENAMED))
|
||||
APPLY_VAR_OR_RESET_INITIAL(apply_to, name, new_name, reset_missing)
|
||||
APPLY_VAR_OR_RESET_INITIAL(apply_to, desc, new_desc, reset_missing)
|
||||
if(!apply_to.greyscale_config) // Never want to be resetting a GAGS icon back to initial.
|
||||
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))
|
||||
if(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)
|
||||
if(!item_apply_to.greyscale_config_worn)
|
||||
APPLY_VAR_OR_RESET_INITIAL(item_apply_to, worn_icon, new_worn_icon, reset_missing)
|
||||
if(!item_apply_to.greyscale_config_inhand_left)
|
||||
APPLY_VAR_OR_RESET_INITIAL(item_apply_to, lefthand_file, new_lefthand_file, reset_missing)
|
||||
APPLY_VAR_OR_RESET_INITIAL(item_apply_to, righthand_file, new_righthand_file, reset_missing)
|
||||
APPLY_VAR_OR_RESET_INITIAL(item_apply_to, worn_icon_state, new_icon_state, reset_missing)
|
||||
if(change_inhand_icon_state || new_inhand_icon_state)
|
||||
APPLY_VAR_OR_RESET_INITIAL(item_apply_to, inhand_icon_state, new_inhand_icon_state || new_icon_state, reset_missing)
|
||||
|
||||
/**
|
||||
* Resets all changes this skin would have made to the given atom
|
||||
@@ -54,13 +101,20 @@
|
||||
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)
|
||||
var/is_greyscale = clear_from.greyscale_config || clear_from.greyscale_colors
|
||||
if(!is_greyscale)
|
||||
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))
|
||||
if(isitem(clear_from))
|
||||
var/obj/item/item_clear_from = clear_from
|
||||
RESET_INITIAL_IF_SET(item_clear_from, inhand_icon_state, new_icon_state)
|
||||
RESET_INITIAL_IF_SET(item_clear_from, worn_icon, new_worn_icon)
|
||||
RESET_INITIAL_IF_SET(item_clear_from, inhand_icon_state, change_inhand_icon_state ? new_inhand_icon_state : new_icon_state)
|
||||
if(!is_greyscale)
|
||||
RESET_INITIAL_IF_SET(item_clear_from, worn_icon_state, new_icon_state)
|
||||
RESET_INITIAL_IF_SET(item_clear_from, lefthand_file, new_lefthand_file)
|
||||
RESET_INITIAL_IF_SET(item_clear_from, righthand_file, new_righthand_file)
|
||||
|
||||
/// Gets a preview image for this skin based on the given atom's icon and icon_state
|
||||
/datum/atom_skin/proc/get_preview_icon(atom/for_atom)
|
||||
@@ -82,14 +136,24 @@
|
||||
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())
|
||||
/datum/component/reskinable_item/Initialize(base_reskin_type, infinite = FALSE, initial_skin, list/blacklisted_subtypes)
|
||||
if(!isatom(parent) || isarea(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
var/atom/atom_parent = parent
|
||||
#ifdef UNIT_TESTS
|
||||
if(atom_parent.greyscale_config && (atom_parent.type in GLOB.all_loadout_datums)) // We only care about these when they're in the loadout
|
||||
var/datum/atom_skin/reskin_type = base_reskin_type
|
||||
if(reskin_type && isnull(reskin_type::greyscale_item_path)) // greyscale_item_path unset
|
||||
stack_trace("[type] added to a greyscale item without setting the greyscale_item_path! In [reskin_type], add 'greyscale_item_path = [atom_parent::type]'.")
|
||||
else if(atom_parent.type != reskin_type::greyscale_item_path) // greyscale_item_path set but doesn't match the item it's being added to
|
||||
stack_trace("[type] added to an invalid item type, [atom_parent.type]. [reskin_type] is set up to only be added to: [reskin_type::greyscale_item_path]. \
|
||||
Either fix its greyscale_item_path if this is incorrect, or apply a different skin.")
|
||||
#endif
|
||||
|
||||
src.base_reskin_type = base_reskin_type
|
||||
src.infinite_reskin = infinite
|
||||
src.blacklisted_subtypes = blacklisted_subtypes
|
||||
@@ -97,7 +161,10 @@
|
||||
if(initial_skin)
|
||||
set_skin_by_name(initial_skin)
|
||||
|
||||
var/atom/atom_parent = parent
|
||||
var/list/reskin_options = get_skins_by_name()
|
||||
if(length(reskin_options) <= 1) // Check that we actually have reskin options - if not there's no point to existing past this point.
|
||||
return COMPONENT_REDUNDANT
|
||||
|
||||
atom_parent.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1
|
||||
|
||||
/datum/component/reskinable_item/RegisterWithParent()
|
||||
@@ -110,15 +177,20 @@
|
||||
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
|
||||
|
||||
/datum/component/reskinable_item/CheckDupeComponent(datum/component/comp, base_reskin_type, infinite = FALSE, initial_skin, list/blacklisted_subtypes)
|
||||
// Always absorb added components
|
||||
src.base_reskin_type = base_reskin_type
|
||||
src.infinite_reskin = infinite
|
||||
src.blacklisted_subtypes = blacklisted_subtypes
|
||||
|
||||
set_skin_by_name(initial_skin)
|
||||
return TRUE // same comp
|
||||
if(initial_skin)
|
||||
set_skin_by_name(initial_skin)
|
||||
|
||||
var/list/reskin_options = get_skins_by_name()
|
||||
if(length(reskin_options) <= 1) // Check that we actually have reskin options - if not there's no point to existing past this point.
|
||||
qdel(src)
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/component/reskinable_item/proc/get_skins_by_name()
|
||||
var/list/reskin_options = list()
|
||||
@@ -127,15 +199,16 @@
|
||||
|
||||
return reskin_options
|
||||
|
||||
/datum/component/reskinable_item/proc/set_skin_by_name(input_name)
|
||||
/datum/component/reskinable_item/proc/set_skin_by_name(input_name, mob/user)
|
||||
var/list/reskin_options = get_skins_by_name()
|
||||
var/list/atom_skins = get_atom_skins()
|
||||
if(current_skin)
|
||||
var/datum/atom_skin/previous_skin = GLOB.atom_skins[reskin_options[current_skin]]
|
||||
previous_skin.clear_skin(parent)
|
||||
var/datum/atom_skin/previous_skin = atom_skins[reskin_options[current_skin]]
|
||||
previous_skin.clear_skin(parent, user)
|
||||
|
||||
if(input_name)
|
||||
var/datum/atom_skin/reskin_to_apply = GLOB.atom_skins[reskin_options[input_name]]
|
||||
reskin_to_apply.apply(parent)
|
||||
var/datum/atom_skin/reskin_to_apply = atom_skins[reskin_options[input_name]]
|
||||
reskin_to_apply.apply(parent, user)
|
||||
|
||||
current_skin = input_name
|
||||
|
||||
@@ -179,8 +252,10 @@
|
||||
var/atom/atom_parent = parent
|
||||
|
||||
var/list/items = list()
|
||||
var/list/atom_skins = get_atom_skins()
|
||||
for(var/reskin_name, reskin_typepath in get_skins_by_name())
|
||||
items[reskin_name] = GLOB.atom_skins[reskin_typepath].get_preview_icon(atom_parent)
|
||||
var/datum/atom_skin/reskin = 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)
|
||||
|
||||
@@ -188,7 +263,7 @@
|
||||
if(!pick || !items[pick])
|
||||
return
|
||||
|
||||
set_skin_by_name(pick)
|
||||
set_skin_by_name(pick, user)
|
||||
to_chat(user, span_info("[parent] is now skinned as '[pick].'"))
|
||||
|
||||
if(!infinite_reskin)
|
||||
|
||||
@@ -311,8 +311,7 @@
|
||||
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
|
||||
resistance_flags = NONE
|
||||
|
||||
/obj/item/clothing/gloves/kaza_ruk/sec/Initialize(mapload)
|
||||
. = ..()
|
||||
/obj/item/clothing/gloves/kaza_ruk/sec/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/kaza_ruk, infinite = TRUE)
|
||||
|
||||
/obj/item/clothing/gloves/kaza_ruk/combatglovesplus
|
||||
|
||||
@@ -977,3 +977,7 @@
|
||||
if(pass_info.pass_flags & pass_flags_self)
|
||||
return TRUE
|
||||
. = !density
|
||||
|
||||
/// Logic for adding reskin components goes here. Override for atom-specific reskin setups.
|
||||
/atom/proc/setup_reskins()
|
||||
return
|
||||
|
||||
@@ -154,6 +154,8 @@
|
||||
if(ispath(ai_controller))
|
||||
ai_controller = new ai_controller(src)
|
||||
|
||||
setup_reskins()
|
||||
|
||||
return INITIALIZE_HINT_NORMAL
|
||||
|
||||
/**
|
||||
|
||||
@@ -83,15 +83,14 @@
|
||||
var/sticker_icon_state = STARTING_STICKER
|
||||
/// Custom description
|
||||
var/custom_description
|
||||
/// Whether this disk can be reskinned
|
||||
var/reskin_allowed = TRUE
|
||||
|
||||
/obj/item/disk/Initialize(mapload)
|
||||
. = ..()
|
||||
if(reskin_allowed)
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/floppy_disk, infinite = FALSE)
|
||||
update_appearance(UPDATE_OVERLAYS)
|
||||
|
||||
/obj/item/disk/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/floppy_disk, infinite = TRUE)
|
||||
|
||||
/obj/item/disk/update_overlays()
|
||||
. = ..()
|
||||
if(sticker_icon_state)
|
||||
|
||||
@@ -794,8 +794,7 @@
|
||||
|
||||
obj_flags = UNIQUE_RENAME
|
||||
|
||||
/obj/item/melee/baton/security/stunsword/Initialize(mapload)
|
||||
. = ..()
|
||||
/obj/item/melee/baton/security/stunsword/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/stunsword)
|
||||
|
||||
/obj/item/melee/baton/security/stunsword/add_deep_lore()
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
read_only = TRUE
|
||||
read_only_locked = TRUE
|
||||
reskin_allowed = FALSE
|
||||
/// Whether we're a real nuke disk or not.
|
||||
var/fake = FALSE
|
||||
|
||||
@@ -30,6 +29,9 @@
|
||||
// Ensure fake disks still have examine text, but dont actually do anything
|
||||
AddComponent(/datum/component/keep_me_secure)
|
||||
|
||||
/obj/item/disk/nuclear/setup_reskins()
|
||||
return
|
||||
|
||||
/obj/item/disk/nuclear/proc/secured_process(last_move)
|
||||
var/turf/new_turf = get_turf(src)
|
||||
var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
base_icon_state = "datadisk"
|
||||
icon_state = "datadisk0"
|
||||
sticker_icon_state = "o_code"
|
||||
reskin_allowed = FALSE
|
||||
/// Name of the choice made
|
||||
var/choice_made
|
||||
|
||||
@@ -24,6 +23,9 @@
|
||||
load_callback = CALLBACK(src, PROC_REF(load_onto_avatar)), \
|
||||
)
|
||||
|
||||
/obj/item/disk/bitrunning/setup_reskins()
|
||||
return
|
||||
|
||||
/obj/item/disk/bitrunning/examine(mob/user)
|
||||
. = ..()
|
||||
. += span_infoplain("This disk must be carried on your person into a netpod to be used.")
|
||||
|
||||
@@ -796,4 +796,6 @@
|
||||
desc = "This disk provides a firmware update to the Express Supply Console, granting the use of Nanotrasen's Bluespace Drop Pods to the supply department."
|
||||
icon_state = "datadisk12"
|
||||
sticker_icon_state = "o_cargopod"
|
||||
reskin_allowed = FALSE
|
||||
|
||||
/obj/item/disk/cargo/bluespace_pod/setup_reskins()
|
||||
return
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
/obj/item/clothing/glasses/Initialize(mapload)
|
||||
. = ..()
|
||||
if(glass_colour_type)
|
||||
AddElement(/datum/element/wearable_client_colour, glass_colour_type, ITEM_SLOT_EYES, GLASSES_TRAIT, forced = forced_glass_color)
|
||||
AddElement(/datum/element/wearable_client_colour, glass_colour_type, ITEM_SLOT_EYES, GLASSES_TRAIT, forced = forced_glass_color, comsig_toggle = COMSIG_CLICK_ALT_SECONDARY)
|
||||
|
||||
/obj/item/clothing/glasses/suicide_act(mob/living/carbon/user)
|
||||
user.visible_message(span_suicide("[user] is stabbing \the [src] into [user.p_their()] eyes! It looks like [user.p_theyre()] trying to commit suicide!"))
|
||||
|
||||
@@ -167,7 +167,6 @@
|
||||
desc = "Used to see the boundaries of shuttle regions."
|
||||
modes = list(MODE_NONE, MODE_SHUTTLE)
|
||||
|
||||
|
||||
/obj/item/clothing/glasses/meson/engine/atmos_imaging
|
||||
name = "atmospheric thermal imaging goggles"
|
||||
desc = "Goggles used by Atmospheric Technicians to see the thermal energy of gasses in open areas."
|
||||
|
||||
@@ -31,8 +31,7 @@
|
||||
clothing_flags = MASKINTERNALS
|
||||
flags_inv = HIDESNOUT
|
||||
|
||||
/obj/item/clothing/mask/joy/Initialize(mapload)
|
||||
. = ..()
|
||||
/obj/item/clothing/mask/joy/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/joy_mask, infinite = TRUE)
|
||||
|
||||
/obj/item/clothing/mask/mummy
|
||||
|
||||
@@ -219,8 +219,7 @@
|
||||
icon_state = "pride"
|
||||
obj_flags = UNIQUE_RENAME
|
||||
|
||||
/obj/item/clothing/accessory/pride/Initialize(mapload)
|
||||
. = ..()
|
||||
/obj/item/clothing/accessory/pride/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/pride_pin, infinite = TRUE)
|
||||
|
||||
/obj/item/clothing/accessory/deaf_pin
|
||||
|
||||
@@ -306,8 +306,7 @@
|
||||
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
|
||||
|
||||
/obj/item/clothing/under/costume/mech_suit/Initialize(mapload)
|
||||
. = ..()
|
||||
/obj/item/clothing/under/costume/mech_suit/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/mech_suit)
|
||||
|
||||
/obj/item/clothing/under/costume/russian_officer
|
||||
|
||||
@@ -54,7 +54,7 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
|
||||
var/ui_icon_state
|
||||
/// 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
|
||||
var/datum/atom_skin/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.
|
||||
@@ -276,10 +276,13 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
|
||||
|
||||
if(reskin_datum && item_details?[INFO_RESKIN])
|
||||
var/skin_chosen = item_details[INFO_RESKIN]
|
||||
var/list/atom_skins = get_atom_skins()
|
||||
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]
|
||||
if(skin_path::preview_name != skin_chosen)
|
||||
continue
|
||||
var/datum/atom_skin/skin_instance = 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
|
||||
@@ -394,13 +397,20 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
|
||||
return null
|
||||
|
||||
var/list/reskins = list()
|
||||
var/list/atom_skins = get_atom_skins()
|
||||
var/list/reskin_choices
|
||||
if(reskin_datum::allow_all_subtypes_in_loadout)
|
||||
reskin_choices = valid_subtypesof(reskin_datum)
|
||||
else
|
||||
reskin_choices = valid_direct_subtypesof(reskin_datum)
|
||||
|
||||
for(var/datum/atom_skin/skin as anything in valid_subtypesof(reskin_datum))
|
||||
for(var/datum/atom_skin/skin_path as anything in reskin_choices)
|
||||
var/datum/atom_skin/atom_skin = atom_skins[skin_path]
|
||||
UNTYPED_LIST_ADD(reskins, list(
|
||||
"name" = skin::new_name || skin::preview_name,
|
||||
"tooltip" = skin::preview_name,
|
||||
"skin_icon" = skin::new_icon,
|
||||
"skin_icon_state" = skin::new_icon_state,
|
||||
"name" = skin_path::new_name || skin_path::preview_name,
|
||||
"tooltip" = skin_path::preview_name,
|
||||
"skin_icon" = skin_path::new_icon,
|
||||
"skin_icon_state" = atom_skin?.get_preview_icon_state() || skin_path::new_icon
|
||||
))
|
||||
|
||||
return reskins
|
||||
|
||||
@@ -4,18 +4,11 @@
|
||||
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"
|
||||
new_inhand_icon_state = "ipickaxe"
|
||||
/// Specifies the icon file in which the crusher's projectile sprite is located.
|
||||
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/new_lefthand_file
|
||||
/// Specifies the right hand inhand icon file. Don't forget to set the left hand file as well.
|
||||
var/new_righthand_file
|
||||
/// Specifies the worn icon file.
|
||||
var/new_worn_file
|
||||
/// Specifies the X dimensions of the new inhand, only relevant with different inhand files.
|
||||
var/new_inhandx
|
||||
/// Specifies the Y dimensions of the new inhand, only relevant with different inhand files.
|
||||
@@ -23,25 +16,15 @@
|
||||
|
||||
/datum/atom_skin/crusher_skin/apply(obj/item/kinetic_crusher/apply_to)
|
||||
. = ..()
|
||||
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/atom_skin/crusher_skin/clear_skin(obj/item/kinetic_crusher/clear_from)
|
||||
. = ..()
|
||||
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)
|
||||
|
||||
@@ -49,20 +32,20 @@
|
||||
new_name = "proto-kinetic sword"
|
||||
preview_name = "Sword"
|
||||
new_icon_state = "crusher_sword"
|
||||
new_inhand_icon = "crusher_sword"
|
||||
new_inhand_icon_state = "crusher_sword"
|
||||
|
||||
/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_inhand_icon_state = "crusher_harpoon"
|
||||
new_projectile_icon_state = "pulse_harpoon"
|
||||
|
||||
/datum/atom_skin/crusher_skin/harpoon/apply(atom/apply_to)
|
||||
/datum/atom_skin/crusher_skin/harpoon/apply(atom/apply_to, mob/user)
|
||||
. = ..()
|
||||
RegisterSignal(apply_to, COMSIG_ITEM_ATTACK_ANIMATION, PROC_REF(on_attack_animation))
|
||||
|
||||
/datum/atom_skin/crusher_skin/harpoon/clear_skin(atom/clear_from)
|
||||
/datum/atom_skin/crusher_skin/harpoon/clear_skin(atom/clear_from, mob/user)
|
||||
. = ..()
|
||||
UnregisterSignal(clear_from, COMSIG_ITEM_ATTACK_ANIMATION)
|
||||
|
||||
@@ -77,14 +60,14 @@
|
||||
new_name = "proto-kinetic dual dagger and blaster"
|
||||
preview_name = "Dagger and Blaster"
|
||||
new_icon_state = "crusher_dagger"
|
||||
new_inhand_icon = "crusher_dagger"
|
||||
new_inhand_icon_state = "crusher_dagger"
|
||||
|
||||
/datum/atom_skin/crusher_skin/dagger/apply(atom/apply_to)
|
||||
/datum/atom_skin/crusher_skin/dagger/apply(atom/apply_to, mob/user)
|
||||
. = ..()
|
||||
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/atom_skin/crusher_skin/dagger/clear_skin(atom/clear_from)
|
||||
/datum/atom_skin/crusher_skin/dagger/clear_skin(atom/clear_from, mob/user)
|
||||
. = ..()
|
||||
UnregisterSignal(clear_from, COMSIG_ITEM_ATTACK_ANIMATION)
|
||||
UnregisterSignal(clear_from, COMSIG_CRUSHER_FIRED_BLAST)
|
||||
@@ -120,7 +103,7 @@
|
||||
new_name = "proto-kinetic glaive"
|
||||
preview_name = "Glaive"
|
||||
new_icon_state = "crusher_glaive"
|
||||
new_inhand_icon = "crusher_glaive"
|
||||
new_inhand_icon_state = "crusher_glaive"
|
||||
new_lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
|
||||
new_righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
|
||||
new_inhandx = 64
|
||||
@@ -133,7 +116,7 @@
|
||||
/datum/atom_skin/crusher_skin/locked/ashen_skull
|
||||
preview_name = "Skull"
|
||||
new_icon_state = "crusher_skull"
|
||||
new_inhand_icon = "crusher_skull"
|
||||
new_inhand_icon_state = "crusher_skull"
|
||||
new_projectile_icon_state = "pulse_skull"
|
||||
|
||||
/// Unlockable (or forced) skins
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
/obj/item/clipboard/Initialize(mapload)
|
||||
update_appearance()
|
||||
. = ..()
|
||||
|
||||
/obj/item/clipboard/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/clipboard)
|
||||
|
||||
/obj/item/clipboard/Destroy()
|
||||
|
||||
@@ -185,11 +185,12 @@
|
||||
/datum/atom_skin/cap_pen
|
||||
abstract_type = /datum/atom_skin/cap_pen
|
||||
|
||||
/datum/atom_skin/cap_pen/apply(atom/apply_to)
|
||||
/datum/atom_skin/cap_pen/apply(atom/apply_to, mob/user)
|
||||
. = ..()
|
||||
apply_to.desc = "It's an expensive [preview_name] fountain pen. The nib is quite sharp."
|
||||
apply_to.update_desc()
|
||||
|
||||
/datum/atom_skin/cap_pen/clear_skin(atom/clear_from)
|
||||
/datum/atom_skin/cap_pen/clear_skin(atom/clear_from, mob/user)
|
||||
. = ..()
|
||||
clear_from.desc = initial(clear_from.desc)
|
||||
|
||||
@@ -244,10 +245,12 @@
|
||||
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/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/cap_pen)
|
||||
|
||||
/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
|
||||
|
||||
@@ -167,8 +167,7 @@
|
||||
|
||||
obj_flags = UNIQUE_RENAME
|
||||
|
||||
/obj/item/gun/ballistic/revolver/c38/detective/Initialize(mapload)
|
||||
. = ..()
|
||||
/obj/item/gun/ballistic/revolver/c38/detective/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/det_revolver)
|
||||
|
||||
/obj/item/gun/ballistic/revolver/badass
|
||||
|
||||
@@ -338,8 +338,7 @@
|
||||
can_be_sawn_off = TRUE
|
||||
pb_knockback = 3 // it's a super shotgun!
|
||||
|
||||
/obj/item/gun/ballistic/shotgun/doublebarrel/Initialize(mapload)
|
||||
. = ..()
|
||||
/obj/item/gun/ballistic/shotgun/doublebarrel/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/bar_shotgun)
|
||||
|
||||
/obj/item/gun/ballistic/shotgun/doublebarrel/sawoff(mob/user)
|
||||
|
||||
@@ -52,8 +52,7 @@
|
||||
var/self_delay = 30
|
||||
custom_price = PAYCHECK_CREW * 2
|
||||
|
||||
/obj/item/reagent_containers/medigel/Initialize(mapload)
|
||||
. = ..()
|
||||
/obj/item/reagent_containers/medigel/setup_reskins()
|
||||
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)
|
||||
|
||||
|
||||
@@ -435,8 +435,7 @@
|
||||
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
|
||||
volume = 100
|
||||
|
||||
/obj/item/reagent_containers/spray/medical/Initialize(mapload, vol)
|
||||
. = ..()
|
||||
/obj/item/reagent_containers/spray/medical/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/med_spray)
|
||||
|
||||
/obj/item/reagent_containers/spray/hercuri
|
||||
|
||||
@@ -2,14 +2,37 @@
|
||||
|
||||
/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))
|
||||
for(var/atom_skin_path, atom_skin in get_atom_skins())
|
||||
var/datum/atom_skin/skin = 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 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
|
||||
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].")
|
||||
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].")
|
||||
|
||||
var/atom/greyscale_item_path = skin.greyscale_item_path
|
||||
var/datum/greyscale_config/greyscale_config = skin.greyscale_config
|
||||
var/greyscale_colors = skin.greyscale_colors
|
||||
var/field_count = 0
|
||||
if(greyscale_item_path) // If any of these are set, they should all be set
|
||||
field_count++
|
||||
if(greyscale_config)
|
||||
field_count++
|
||||
if(greyscale_colors)
|
||||
field_count++
|
||||
if(!field_count)
|
||||
continue
|
||||
if(field_count != 3)
|
||||
TEST_FAIL("[skin]: incomplete greyscale definition: item_path: [greyscale_item_path], \
|
||||
config: [greyscale_config], \
|
||||
colors: [greyscale_colors]")
|
||||
|
||||
if(isnull(greyscale_item_path::greyscale_config) || isnull(greyscale_item_path::greyscale_colors))
|
||||
TEST_FAIL("[skin]: greyscale_item_path is set to an item that does not have a greyscale config or greyscale_colors set! \
|
||||
Either set those up for the item, or set all the greyscale fields on the skin to null.")
|
||||
|
||||
@@ -35,13 +35,13 @@ type SpawnSearchData = {
|
||||
type SpawnAtomData = {
|
||||
// Type -> Name
|
||||
types: Record<string, string>;
|
||||
abstractTypes: Array<string>;
|
||||
abstractTypes: Record<string, boolean>
|
||||
fancyTypes: Record<string, string>;
|
||||
};
|
||||
|
||||
type AtomPathData = {
|
||||
types: Array<AtomTypeData>;
|
||||
abstractTypes: Array<string>;
|
||||
abstractTypes: Record<string, boolean>
|
||||
fancyTypes: Record<string, string>;
|
||||
};
|
||||
|
||||
@@ -56,7 +56,7 @@ export const SpawnSearch = () => {
|
||||
data;
|
||||
const [atomData, setAtomData] = useState<AtomPathData>({
|
||||
types: [],
|
||||
abstractTypes: [],
|
||||
abstractTypes: {},
|
||||
fancyTypes: {},
|
||||
});
|
||||
const [selected, setSelected] = useState<number>(0);
|
||||
@@ -129,7 +129,7 @@ export const SpawnSearch = () => {
|
||||
(type: AtomTypeData) =>
|
||||
(searchLambda(type.typepath) ||
|
||||
(searchNames && searchLambda(type.name))) &&
|
||||
(includeAbstracts || !atomData.abstractTypes.includes(type.typepath)),
|
||||
(includeAbstracts || !atomData.abstractTypes[type.typepath]),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -313,7 +313,7 @@ export const SpawnSearch = () => {
|
||||
>
|
||||
<span
|
||||
style={
|
||||
atomData.abstractTypes.includes(item.typepath)
|
||||
atomData.abstractTypes[item.typepath]
|
||||
? { opacity: 0.75, color: '#FFA246' }
|
||||
: {}
|
||||
}
|
||||
@@ -344,7 +344,7 @@ export const SpawnSearch = () => {
|
||||
>
|
||||
{item.name}
|
||||
</span>
|
||||
{!!atomData.abstractTypes.includes(item.typepath) && (
|
||||
{!!atomData.abstractTypes[item.typepath] && (
|
||||
<span
|
||||
style={{
|
||||
float: 'right',
|
||||
|
||||
Reference in New Issue
Block a user