From 4970675467c6c7aad09510cd886eacdf0c97032b Mon Sep 17 00:00:00 2001
From: kevinz000 <2003111+kevinz000@users.noreply.github.com>
Date: Sun, 15 Dec 2019 00:06:11 -0700
Subject: [PATCH] s
---
code/__DEFINES/misc.dm | 7 ++
code/__HELPERS/custom_holoforms.dm | 68 ++++++++++++++++++++
code/game/objects/items.dm | 2 +
code/modules/client/preferences.dm | 3 +
code/modules/client/verbs/custom_holoform.dm | 18 ++++++
code/modules/clothing/clothing.dm | 1 -
6 files changed, 98 insertions(+), 1 deletion(-)
create mode 100644 code/__HELPERS/custom_holoforms.dm
create mode 100644 code/modules/client/verbs/custom_holoform.dm
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 29ff0a8fae..1e585b8fff 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -427,6 +427,7 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
//Dummy mob reserve slots
#define DUMMY_HUMAN_SLOT_PREFERENCES "dummy_preference_preview"
+#define DUMMY_HUMAN_SLOT_HOLOFORM "dummy_holoform_generation"
#define DUMMY_HUMAN_SLOT_ADMIN "admintools"
#define DUMMY_HUMAN_SLOT_MANIFEST "dummy_manifest_generation"
@@ -528,3 +529,9 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
#define FOURSPACES " "
#define CRYOMOBS 'icons/obj/cryo_mobs.dmi'
+
+#define CUSTOM_HOLOFORM_DELAY 10 SECONDS //prevents spamming to make lag. it's pretty expensive to do this.
+
+#define HOLOFORM_FILTER_AI "FILTER_AI"
+#define HOLOFORM_FILTER_PAI "FILTER_PAI"
+#define HOLOFORM_FILTER_STATIC "FILTER_STATIC"
diff --git a/code/__HELPERS/custom_holoforms.dm b/code/__HELPERS/custom_holoforms.dm
new file mode 100644
index 0000000000..b20a41ea34
--- /dev/null
+++ b/code/__HELPERS/custom_holoforms.dm
@@ -0,0 +1,68 @@
+// Generates a holoform appearance
+// Equipment list is slot = path.
+/proc/generate_custom_holoform_from_prefs(datum/preferences/prefs, filter_type = HOLOFORM_FILTER_AI, list/equipment_by_slot, list/inhand_equipment)
+ ASSERT(prefs)
+ var/mob/living/carbon/human/dummy/mannequin = generate_or_wait_for_human_dummy(DUMMY_HUMAN_SLOT_HOLOFORM)
+ prefs.copy_to(mannequin)
+ if(length(equipment_by_slot))
+ LAZYINITLIST(spawned)
+ for(var/slot in equipment_by_slot)
+ var/obj/item/I = new equipment_by_slot[slot]
+ mannequin.equip_to_slot_if_possible(I, slot, TRUE, TRUE, TRUE, TRUE)
+ if(length(inhand_equipment))
+ LAZYINITLIST(spawned)
+ for(var/path in inhand_equipment)
+ var/obj/item/I = new path
+ mannequin.equip_to_slot_if_possible(I, SLOT_IN_HANDS, TRUE, TRUE, TRUE, TRUE)
+ var/icon/result = get_custom_holoform_icon(mannequin, filter_type)
+ unset_busy_human_dummy(DUMMY_HUMAN_SLOT_HOLOFORM)
+ return result
+
+/proc/get_custom_holoform_icon(atom/A, filter_type)
+ var/icon/I = getFlatIcon(A)
+ switch(filter_type)
+ if(HOLOFORM_FILTER_AI)
+ I = getHologramIcon(I)
+ if(HOLOFORM_FILTER_STATIC)
+ I = getStaticIcon(I)
+ if(HOLOFORM_FILTER_PAI)
+ I = getPAIHologramIcon(I)
+ return I
+
+//Errors go to user.
+/proc/generate_custom_holoform_from_prefs_safe(datum/preferences/prefs, filter_type, list/equipment_by_slot, list/inhand_equipment, mob/user)
+ if(user)
+ if(user.prefs.last_custom_holoform < world.time + CUSTOM_HOLOFORM_DELAY)
+ return
+ if(length(equipment_by_slot))
+ for(var/slot in equipment_by_slot)
+ var/path = equipment_by_slot[slot]
+ if(!ispath(path, /obj/item))
+ equipment_by_slot -= slot
+ to_chat(user, "[path] is not a valid path and has been removed.")
+ continue
+ else
+ var/obj/item/I = path
+ if(!initial(I.allow_virtual_spawn))
+ equipment_by_slot -= slot
+ to_chat(user, "[path] is not an allowed path and has been removed.")
+ continue
+ if(length(inhand_equipment))
+ for(var/path in inhand_equipment)
+ if(!ispath(path, /obj/item))
+ inhand_equipment -= path
+ to_chat(user, "[path] is not a valid path and has been removed.")
+ continue
+ else
+ var/obj/item/I = path
+ if(!initial(I.allow_virtual_spawn))
+ inhand_equipment -= slot
+ to_chat(user, "[path] is not an allowed path and has been removed.")
+ continue
+ return generate_custom_holoform_from_prefs(prefs, filter_type, equipment_by_slot, inhand_equipment)
+
+//Prompts this client for custom holoform parameters.
+/proc/user_interface_custom_holoform(client/C)
+ var/datum/preferences/target_prefs = C.prefs
+ ASSERT(target_prefs)
+
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index c5d198c05f..eca75def83 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -109,6 +109,8 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
var/list/grind_results //A reagent list containing the reagents this item produces when ground up in a grinder - this can be an empty list to allow for reagent transferring only
var/list/juice_results //A reagent list containing blah blah... but when JUICED in a grinder!
+ var/allow_virtual_spawn = TRUE //allow players to use this in holoforms, which will cause it to be instantiated in nullspace for long enough for us to grab the appearance/icons.
+
/obj/item/Initialize()
materials = typelist("materials", materials)
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index c476e5a310..70a287bf2a 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -21,6 +21,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/last_ip
var/last_id
+ var/mutable_appearance/custom_holoform
+ var/last_custom_holoform = 0
+
//Cooldowns for saving/loading. These are four are all separate due to loading code calling these one after another
var/saveprefcooldown
var/loadprefcooldown
diff --git a/code/modules/client/verbs/custom_holoform.dm b/code/modules/client/verbs/custom_holoform.dm
new file mode 100644
index 0000000000..0e63d83d82
--- /dev/null
+++ b/code/modules/client/verbs/custom_holoform.dm
@@ -0,0 +1,18 @@
+/client/verb/clear_custom_holoform()
+ set naame = "Clear Custom Holoform"
+ set desc = "Clear your current custom holoform"
+
+ prefs?.custom_holoform = null
+ to_chat(src, "Holoform removed.")
+
+/client/verb/set_custom_holoform()
+ set name = "Set Custom Holoform"
+ set desc = "Set your custom holoform using your current preferences slot and a specified set of gear."
+
+ if(prefs.last_custom_holoform < world.time + CUSTOM_HOLOFORM_DELAY)
+ to_chat(src, "You are attempting to change custom holoforms too fast!")
+ var/mutable_appearance/new_holoform = user_interface_custom_holoform(src)
+ if(new_holoform)
+ prefs.custom_holoform = new_holoform
+ prefs.last_custom_holoform = world.time
+ to_chat(src, "Holoform set.")
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index dc9e46c4b4..57d9238a5c 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -47,7 +47,6 @@
//Add a "exclude" string to do the opposite, making it only only species listed that can't wear it.
//You append this to clothing objects.
-
/obj/item/clothing/Initialize()
. = ..()
if(CHECK_BITFIELD(clothing_flags, VOICEBOX_TOGGLABLE))