Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MIRROR] Generalize chameleon extension setup #1734

Merged
merged 1 commit into from
Dec 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions code/__defines/flags.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions code/_helpers/game.dm
Original file line number Diff line number Diff line change
@@ -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
Expand Down
22 changes: 22 additions & 0 deletions code/datums/extensions/chameleon.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
2 changes: 2 additions & 0 deletions code/game/objects/items.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
66 changes: 11 additions & 55 deletions code/modules/clothing/chameleon.dm
Original file line number Diff line number Diff line change
Expand Up @@ -5,119 +5,79 @@
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"
icon_state = "greysoft"
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"
icon_state = "armor"
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"
icon_state = "black"
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"
icon_state = "backpack"
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"
icon_state = "black"
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"
icon_state = "fullgas"
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"
icon_state = "meson"
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"
icon_state = "headset"
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"
icon_state = "tie"
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"
Expand All @@ -126,15 +86,11 @@
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'
projectile_type = /obj/item/projectile/chameleon
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)
5 changes: 1 addition & 4 deletions code/modules/holodeck/HolodeckControl.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
18 changes: 18 additions & 0 deletions code/unit_tests/unique_tests.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down