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))