diff --git a/code/__defines/flags.dm b/code/__defines/flags.dm index 65181b94d63b6..dc6ae948143a4 100644 --- a/code/__defines/flags.dm +++ b/code/__defines/flags.dm @@ -52,6 +52,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define ITEM_FLAG_NOCUFFS FLAG(13) // Gloves that have this flag prevent cuffs being applied #define ITEM_FLAG_CAN_HIDE_IN_SHOES FLAG(14) // Items that can be hidden in shoes that permit it #define ITEM_FLAG_WASHER_ALLOWED FLAG(15) // Items that can be washed in washing machines +#define ITEM_FLAG_IS_CHAMELEON_ITEM FLAG(16) // Setups the chameleon extension on init. Throws an exception if there is no compatible extension subtype. // Flags for pass_flags. #define PASS_FLAG_TABLE FLAG(0) diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index 82aa26c9dc618..c9d3ddc17d0dd 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -1,5 +1,9 @@ //This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 +#define IS_SUBTYPE(child_type, parent_type) (child_type != parent_type && istype(child_type, parent_type)) + +#define IS_SUBPATH(child_path, parent_path) (child_path != parent_path && ispath(child_path, parent_path)) + /proc/is_on_same_plane_or_station(z1, z2) if(z1 == z2) return 1 diff --git a/code/datums/extensions/chameleon.dm b/code/datums/extensions/chameleon.dm index 6c0eb2c799fc1..4b5bb7ba9fcd1 100644 --- a/code/datums/extensions/chameleon.dm +++ b/code/datums/extensions/chameleon.dm @@ -338,3 +338,25 @@ return outfit.l_ear if (ispath(outfit.r_ear, expected_type)) return outfit.r_ear + +/*************** +* Setup Helper * +****************/ +/obj/proc/SetupChameleonExtension(throw_runtime) + var/best_found_expected_type + var/best_found_extension + for (var/datum/extension/chameleon/chameleon_extension_type as anything in typesof(/datum/extension/chameleon)) + var/expected_type = initial(chameleon_extension_type.expected_type) + + if (istype(src, expected_type)) // If the type of src is a type expected by the extension then.. + // Check if the expected type is a better match than the previously found best expected type (if any) + if (!best_found_expected_type || IS_SUBPATH(expected_type, best_found_expected_type)) + best_found_expected_type = expected_type + best_found_extension = chameleon_extension_type + + if (best_found_extension) + set_extension(src, best_found_extension) + return + + if (throw_runtime) + CRASH("The type [type] does not have a compatible chameleon extension.") diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 21b85ea8ba878..d374e5202dbbf 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -114,6 +114,8 @@ if(armor[type]) // Don't set it if it gives no armor anyway, which is many items. set_extension(src, armor_type, armor, armor_degradation_speed) break + if (item_flags & ITEM_FLAG_IS_CHAMELEON_ITEM) + SetupChameleonExtension(TRUE) /obj/item/Destroy() QDEL_NULL(hidden_uplink) diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index 01fad96a891e3..474811ab4261b 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -5,11 +5,7 @@ worn_state = "jumpsuit" desc = "It's a plain jumpsuit. It seems to have a small dial on the wrist." origin_tech = list(TECH_ESOTERIC = 3) - item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON - -/obj/item/clothing/under/chameleon/Initialize() - . = ..() - set_extension(src,/datum/extension/chameleon/clothing/under) + item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM /obj/item/clothing/head/chameleon name = "cap" @@ -17,11 +13,7 @@ desc = "It looks like a plain hat, but upon closer inspection, there's an advanced holographic array installed inside. It seems to have a small dial inside." origin_tech = list(TECH_ESOTERIC = 3) body_parts_covered = 0 - item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON - -/obj/item/clothing/head/chameleon/Initialize() - . = ..() - set_extension(src, /datum/extension/chameleon/clothing/head) + item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM /obj/item/clothing/suit/chameleon name = "armor" @@ -29,11 +21,7 @@ item_state = "armor" desc = "It appears to be a vest of standard armor, except this is embedded with a hidden holographic cloaker, allowing it to change it's appearance, but offering no protection.. It seems to have a small dial inside." origin_tech = list(TECH_ESOTERIC = 3) - item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON - -/obj/item/clothing/suit/chameleon/Initialize() - . = ..() - set_extension(src, /datum/extension/chameleon/clothing/suit) + item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM /obj/item/clothing/shoes/chameleon name = "shoes" @@ -41,11 +29,7 @@ item_state = "black" desc = "They're comfy black shoes, with clever cloaking technology built in. It seems to have a small dial on the back of each shoe." origin_tech = list(TECH_ESOTERIC = 3) - item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON - -/obj/item/clothing/shoes/chameleon/Initialize() - . = ..() - set_extension(src, /datum/extension/chameleon/clothing/shoes) + item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM /obj/item/storage/backpack/chameleon name = "backpack" @@ -53,11 +37,7 @@ item_state = "backpack" desc = "A backpack outfitted with cloaking tech. It seems to have a small dial inside, kept away from the storage." origin_tech = list(TECH_ESOTERIC = 3) - item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON - -/obj/item/storage/backpack/chameleon/Initialize() - . = ..() - set_extension(src,/datum/extension/chameleon/backpack,/obj/item/storage/backpack) + item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM /obj/item/clothing/gloves/chameleon name = "gloves" @@ -65,11 +45,7 @@ item_state = "bgloves" desc = "It looks like a pair of gloves, but it seems to have a small dial inside." origin_tech = list(TECH_ESOTERIC = 3) - item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON - -/obj/item/clothing/gloves/chameleon/Initialize() - . = ..() - set_extension(src, /datum/extension/chameleon/clothing/gloves) + item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM /obj/item/clothing/mask/chameleon name = "mask" @@ -77,11 +53,7 @@ item_state = "gas_alt" desc = "It looks like a plain gask mask, but on closer inspection, it seems to have a small dial inside." origin_tech = list(TECH_ESOTERIC = 3) - item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON - -/obj/item/clothing/mask/chameleon/Initialize() - . = ..() - set_extension(src, /datum/extension/chameleon/clothing/mask) + item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM /obj/item/clothing/glasses/chameleon name = "goggles" @@ -89,11 +61,7 @@ item_state = "glasses" desc = "It looks like a plain set of mesons, but on closer inspection, it seems to have a small dial inside." origin_tech = list(TECH_ESOTERIC = 3) - item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON - -/obj/item/clothing/glasses/chameleon/Initialize() - . = ..() - set_extension(src, /datum/extension/chameleon/clothing/glasses) + item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM /obj/item/device/radio/headset/chameleon name = "radio headset" @@ -101,11 +69,7 @@ item_state = "headset" desc = "An updated, modular intercom that fits over the head. This one seems to have a small dial on it." origin_tech = list(TECH_ESOTERIC = 3) - item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON - -/obj/item/device/radio/headset/chameleon/Initialize() - . = ..() - set_extension(src,/datum/extension/chameleon/headset) + item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM /obj/item/clothing/accessory/chameleon name = "tie" @@ -113,11 +77,7 @@ item_state = "" desc = "A neosilk clip-on tie. It seems to have a small dial on its back." origin_tech = list(TECH_ESOTERIC = 3) - item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON - -/obj/item/clothing/accessory/chameleon/Initialize() - . = ..() - set_extension(src,/datum/extension/chameleon/clothing/accessory) + item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM /obj/item/gun/energy/chameleon name = "chameleon gun" @@ -126,7 +86,7 @@ icon_state = "revolver" w_class = ITEM_SIZE_SMALL origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2, TECH_ESOTERIC = 8) - item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON + item_flags = ITEM_FLAG_INVALID_FOR_CHAMELEON | ITEM_FLAG_IS_CHAMELEON_ITEM matter = list() fire_sound = 'sound/weapons/gunshot/gunshot_pistol.ogg' @@ -134,7 +94,3 @@ charge_meter = 0 charge_cost = 20 //uses next to no power, since it's just holograms max_shots = 50 - -/obj/item/gun/energy/chameleon/Initialize() - . = ..() - set_extension(src,/datum/extension/chameleon/gun) diff --git a/code/modules/holodeck/HolodeckControl.dm b/code/modules/holodeck/HolodeckControl.dm index 6ca3270901560..66b34ec267f84 100644 --- a/code/modules/holodeck/HolodeckControl.dm +++ b/code/modules/holodeck/HolodeckControl.dm @@ -281,10 +281,7 @@ for(var/obj/holo_obj in holographic_objs) holo_obj.alpha *= 0.8 //give holodeck objs a slight transparency holo_obj.holographic = TRUE - if(istype(holo_obj,/obj/item/storage)) - set_extension(holo_obj,/datum/extension/chameleon/backpack) - if(istype(holo_obj,/obj/item/clothing)) - set_extension(holo_obj,/datum/extension/chameleon/clothing) + holo_obj.SetupChameleonExtension() if(HP.ambience) linkedholodeck.forced_ambience = HP.ambience.Copy() diff --git a/code/unit_tests/unique_tests.dm b/code/unit_tests/unique_tests.dm index f8fe770e46e40..d6acc5bf84acc 100644 --- a/code/unit_tests/unique_tests.dm +++ b/code/unit_tests/unique_tests.dm @@ -151,6 +151,24 @@ pass("All space suit modifiers have unique names.") return 1 +// Purpose: /proc/SetupChameleonExtension() attempts to find the best chameleon extension for a given type +// Having multiple extensions expect the same type can technically lead to inconsistencies between compilations (if the types are moved around, etc.) +// Can be worked around by, for example, adding a flag that adds/removes a given extension from the list of possible extensions in the proc above +/datum/unit_test/chameleon_extensions_shall_have_unique_expected_types + name = "UNIQUENESS: Chameleon Extensions Shall Have Unique Expected Types" + +/datum/unit_test/chameleon_extensions_shall_have_unique_expected_types/start_test() + var/list/expected_types_by_extension = list() + for (var/datum/extension/chameleon/chameleon_extension_type as anything in typesof(/datum/extension/chameleon)) + group_by(expected_types_by_extension, initial(chameleon_extension_type.expected_type), chameleon_extension_type) + + var/number_of_issues = number_of_issues(expected_types_by_extension, "Chameleon Extensions - Expected Types") + if(number_of_issues) + fail("[number_of_issues] duplicate expected type\s found.") + else + pass("All chameleon extensions have unique expected types.") + return 1 + /datum/unit_test/proc/number_of_issues(list/entries, type, feedback = /singleton/noi_feedback) var/issues = 0 for(var/key in entries)