From 851c34337f6e95d97131357609b103fbf6987883 Mon Sep 17 00:00:00 2001 From: Geeves Date: Sun, 13 Oct 2024 23:16:01 +0200 Subject: [PATCH] Custom Item Limb Framework + Gracia's Aut'akh Arm (#20004) * You can now view your custom items from within the loadout. Also implements the framework necessary for custom items to be limbs, and to automatically replace the limb it's meant to replace. Implements the Aut'akh arm mentioned in this request: https://forums.aurorastation.org/topic/21092-gracias-autakh-arm --- code/__DEFINES/prefs.dm | 3 + .../preference_setup/general/03_body.dm | 1 + code/modules/customitems/item_defines.dm | 20 +++ code/modules/customitems/item_spawning.dm | 118 ++++++++++++------ .../abstract/new_player/preferences_setup.dm | 5 +- code/modules/organs/organ_external.dm | 24 +++- code/modules/organs/organ_icon.dm | 2 +- .../geeves-custom_item_limb_framework.yml | 6 + icons/mob/human_races/fluff/gracia_arm.dmi | Bin 0 -> 435 bytes 9 files changed, 132 insertions(+), 47 deletions(-) create mode 100644 html/changelogs/geeves-custom_item_limb_framework.yml create mode 100644 icons/mob/human_races/fluff/gracia_arm.dmi diff --git a/code/__DEFINES/prefs.dm b/code/__DEFINES/prefs.dm index 6725d511af1..d6497dc1716 100644 --- a/code/__DEFINES/prefs.dm +++ b/code/__DEFINES/prefs.dm @@ -15,6 +15,9 @@ /// Will show job suit if true, will hide if this is false or EQUIP_PREVIEW_JOB is false #define EQUIP_PREVIEW_JOB_SUIT BITFLAG(4) +/// Will show custom items if set, not set by default, to prevent overloading SQL requests +#define EQUIP_PREVIEW_CUSTOM_ITEMS BITFLAG(5) + /// The default EQUIP_PREVIEW bitflag, with this, all character creation items are shown #define EQUIP_PREVIEW_ALL (EQUIP_PREVIEW_LOADOUT|EQUIP_PREVIEW_JOB|EQUIP_PREVIEW_JOB_HAT|EQUIP_PREVIEW_JOB_UNIFORM|EQUIP_PREVIEW_JOB_SUIT) diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm index 9792e24319d..3b909ff38a1 100644 --- a/code/modules/client/preference_setup/general/03_body.dm +++ b/code/modules/client/preference_setup/general/03_body.dm @@ -300,6 +300,7 @@ var/global/list/valid_bloodtypes = list( out += "
[pref.equip_preview_mob & EQUIP_PREVIEW_JOB_HAT ? "Hide job hat" : "Show job hat"]" out += "
[pref.equip_preview_mob & EQUIP_PREVIEW_JOB_UNIFORM ? "Hide job uniform" : "Show job uniform"]" out += "
[pref.equip_preview_mob & EQUIP_PREVIEW_JOB_SUIT ? "Hide job suit" : "Show job suit"]" + out += "
[pref.equip_preview_mob & EQUIP_PREVIEW_CUSTOM_ITEMS ? "Hide custom items" : "Show custom items"]" out += "" var/tail_spacing = FALSE diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm index fab6fd39906..fec77804371 100644 --- a/code/modules/customitems/item_defines.dm +++ b/code/modules/customitems/item_defines.dm @@ -1971,3 +1971,23 @@ All custom items with worn sprites must follow the contained sprite system: http flag_path = "devorask_flag" flag_size = TRUE flag_item = /obj/item/flag/fluff/devorask_flag/l + +/obj/item/organ/external/arm/fluff/gracia_autakh // gracia's aut'akh left arm - Gracia Hiza - cometblaze + robotize_type = PROSTHETIC_AUTAKH + skin_color = FALSE + override_robotize_force_icon = 'icons/mob/human_races/fluff/gracia_arm.dmi' + override_robotize_painted = FALSE + robotize_children = FALSE + +/obj/item/organ/external/arm/fluff/gracia_autakh/Initialize(mapload) + . = ..() + // adding the hand to the child here means only the arm has to be added to the DB + // since the hand will be attached automatically + LAZYADD(children, new /obj/item/organ/external/hand/fluff/gracia_autakh(src)) + +/obj/item/organ/external/hand/fluff/gracia_autakh // gracia's aut'akh left hand - Gracia Hiza - cometblaze + robotize_type = PROSTHETIC_AUTAKH + skin_color = FALSE + override_robotize_force_icon = 'icons/mob/human_races/fluff/gracia_arm.dmi' + override_robotize_painted = FALSE + robotize_children = FALSE diff --git a/code/modules/customitems/item_spawning.dm b/code/modules/customitems/item_spawning.dm index 0488c76a7c4..3ff7978678b 100644 --- a/code/modules/customitems/item_spawning.dm +++ b/code/modules/customitems/item_spawning.dm @@ -213,68 +213,108 @@ return item -//gets the relevant list for the key from the listlist if it exists, check to make sure they are meant to have it and then calls the giving function -/proc/equip_custom_items(var/mob/living/carbon/human/M) +GLOBAL_LIST_EMPTY(character_id_to_custom_items_mapping) + +/// Gets the relevant custom items for the given target_mob, character_id, and player_ckey, and equips the target_mob with those custom items +/proc/equip_custom_items(var/mob/living/carbon/human/target_mob, var/character_id, var/player_ckey) + if(!character_id) + character_id = target_mob.character_id + + if(!player_ckey) + player_ckey = target_mob.ckey + + var/glob_character_id_key = "[player_ckey]-[character_id]" + //Fetch the custom items for the mob if(GLOB.config.sql_enabled) if(!establish_db_connection(GLOB.dbcon)) log_module_customitems("Unable to establish database connection while loading item. - Aborting") return - var/DBQuery/char_item_query = GLOB.dbcon.NewQuery("SELECT ss13_characters_custom_items.id, ss13_characters_custom_items.char_id, ss13_characters.ckey as usr_ckey, ss13_characters.name as usr_charname, item_path, item_data, req_titles, additional_data FROM ss13_characters_custom_items LEFT JOIN ss13_characters ON ss13_characters.id = ss13_characters_custom_items.char_id WHERE char_id = :char_id:") - char_item_query.Execute(list("char_id"=M.character_id)) - while(char_item_query.NextRow()) - CHECK_TICK - var/datum/custom_item/ci = new() - ci.id = text2num(char_item_query.item[1]) - ci.character_id = text2num(char_item_query.item[2]) - ci.usr_ckey = char_item_query.item[3] - ci.usr_charname = char_item_query.item[4] - ci.item_path = text2path(char_item_query.item[5]) - ci.item_data = json_decode(char_item_query.item[6]) //TODO: try/catch - ci.req_titles = json_decode(char_item_query.item[7]) //TODO: try/catch - ci.additional_data = char_item_query.item[8] + if(!GLOB.character_id_to_custom_items_mapping[glob_character_id_key]) + var/list/custom_items_list = list() - equip_custom_item_to_mob(ci,M) + var/DBQuery/char_item_query = GLOB.dbcon.NewQuery("SELECT ss13_characters_custom_items.id, ss13_characters_custom_items.char_id, ss13_characters.ckey as usr_ckey, ss13_characters.name as usr_charname, item_path, item_data, req_titles, additional_data FROM ss13_characters_custom_items LEFT JOIN ss13_characters ON ss13_characters.id = ss13_characters_custom_items.char_id WHERE char_id = :char_id:") + char_item_query.Execute(list("char_id"=character_id)) + while(char_item_query.NextRow()) + CHECK_TICK + var/datum/custom_item/ci = new() + ci.id = text2num(char_item_query.item[1]) + ci.character_id = text2num(char_item_query.item[2]) + ci.usr_ckey = char_item_query.item[3] + ci.usr_charname = char_item_query.item[4] + ci.item_path = text2path(char_item_query.item[5]) + ci.item_data = json_decode(char_item_query.item[6]) //TODO: try/catch + ci.req_titles = json_decode(char_item_query.item[7]) //TODO: try/catch + ci.additional_data = char_item_query.item[8] + + custom_items_list += ci + + GLOB.character_id_to_custom_items_mapping[glob_character_id_key] = custom_items_list else - for(var/item in custom_items) - CHECK_TICK - var/datum/custom_item/ci = item - if(lowertext(ci.usr_ckey) != lowertext(M.ckey)) - continue - if(lowertext(ci.usr_charname) != lowertext(M.real_name)) - continue - equip_custom_item_to_mob(ci,M) + if(!GLOB.character_id_to_custom_items_mapping[glob_character_id_key]) + var/list/custom_items_list = list() + + for(var/item in custom_items) + CHECK_TICK + var/datum/custom_item/ci = item + if(lowertext(ci.usr_ckey) != lowertext(player_ckey)) + continue + if(lowertext(ci.usr_charname) != lowertext(target_mob.real_name)) + continue + custom_items_list += ci + + GLOB.character_id_to_custom_items_mapping[glob_character_id_key] = custom_items_list + + for(var/datum/custom_item/ci as anything in GLOB.character_id_to_custom_items_mapping[glob_character_id_key]) + equip_custom_item_to_mob(ci, target_mob) -/proc/equip_custom_item_to_mob(var/datum/custom_item/citem, var/mob/living/carbon/human/M) +/proc/equip_custom_item_to_mob(var/datum/custom_item/citem, var/mob/living/carbon/human/target_mob) // Check for required job title. if(length(citem.req_titles)) var/has_title - var/current_title = M.mind.role_alt_title ? M.mind.role_alt_title : M.mind.assigned_role + var/current_title = target_mob.mind.role_alt_title ? target_mob.mind.role_alt_title : target_mob.mind.assigned_role for(var/title in citem.req_titles) if(title == current_title) has_title = 1 break if(!has_title) - to_chat(M, "A custom item could not be equipped as you have joined with the wrong role.") + to_chat(target_mob, "A custom item could not be equipped as you have joined with the wrong role.") return FALSE if(ispath(citem.item_path, /obj/item/organ/internal/augment/fluff)) - var/obj/item/organ/internal/augment/fluff/aug = citem.spawn_item(M) - var/obj/item/organ/external/affected = M.get_organ(aug.parent_organ) - aug.replaced(M, affected) - M.update_body() - M.updatehealth() - M.UpdateDamageIcon() + var/obj/item/organ/internal/augment/fluff/aug = citem.spawn_item(target_mob) + var/obj/item/organ/external/affected = target_mob.get_organ(aug.parent_organ) + aug.replaced(target_mob, affected) + target_mob.update_body() + target_mob.updatehealth() + target_mob.UpdateDamageIcon() + return + + if(ispath(citem.item_path, /obj/item/organ/external)) + var/obj/item/organ/external/dummy_limb = citem.item_path + var/obj/item/organ/external/parent_organ = target_mob.get_organ(initial(dummy_limb.parent_organ)) + if(parent_organ) + var/obj/item/organ/external/existing_limb = target_mob.get_organ(initial(dummy_limb.limb_name)) + if(existing_limb) + existing_limb.droplimb(TRUE) + qdel(existing_limb) + + var/obj/item/organ/external/custom_limb = citem.spawn_item(target_mob) + custom_limb.replaced(target_mob, parent_organ) + + target_mob.update_body() + target_mob.updatehealth() + target_mob.UpdateDamageIcon() return // ID cards and MCs are applied directly to the existing object rather than spawned fresh. var/obj/item/existing_item if(citem.item_path == /obj/item/card/id) - existing_item = locate(/obj/item/card/id) in M.get_contents() //TODO: Improve this ? + existing_item = locate(/obj/item/card/id) in target_mob.get_contents() //TODO: Improve this ? else if(citem.item_path == /obj/item/modular_computer) - existing_item = locate(/obj/item/modular_computer) in M.contents + existing_item = locate(/obj/item/modular_computer) in target_mob.contents // Spawn and equip the item. if(existing_item) @@ -283,12 +323,12 @@ else var/obj/item/newitem = citem.spawn_item() - if(M.equip_to_appropriate_slot(newitem)) + if(target_mob.equip_to_appropriate_slot(newitem)) return TRUE - if(M.equip_to_storage(newitem)) + if(target_mob.equip_to_storage(newitem)) return TRUE - newitem.forceMove(get_turf(M.loc)) - to_chat(M, "A custom item has been placed on the floor as there was no space for it on your mob.") + newitem.forceMove(get_turf(target_mob.loc)) + to_chat(target_mob, "A custom item has been placed on the floor as there was no space for it on your mob.") return TRUE diff --git a/code/modules/mob/abstract/new_player/preferences_setup.dm b/code/modules/mob/abstract/new_player/preferences_setup.dm index 5dab3c7d195..94bd19d83cb 100644 --- a/code/modules/mob/abstract/new_player/preferences_setup.dm +++ b/code/modules/mob/abstract/new_player/preferences_setup.dm @@ -217,7 +217,10 @@ var/list/leftovers = list() var/list/used_slots = list() - if((equip_preview_mob & EQUIP_PREVIEW_LOADOUT)) + if(equip_preview_mob & EQUIP_PREVIEW_CUSTOM_ITEMS) + equip_custom_items(mannequin, current_character, client.ckey) + + if(equip_preview_mob & EQUIP_PREVIEW_LOADOUT) SSjobs.EquipCustom(mannequin, previewJob, src, leftovers, null, used_slots) if((equip_preview_mob & EQUIP_PREVIEW_JOB) && previewJob) diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index 761ef90e9fd..b58339be8b6 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -68,6 +68,15 @@ var/gendered_icon = 0 var/force_icon + /// If set, will use this as the robotized force icon instead of the robotype + var/override_robotize_force_icon + + /// If set, will use tihs as the painted value instead of the robotype + var/override_robotize_painted + + /// Will robotize the children of this limb if set to true + var/robotize_children = TRUE + var/limb_name var/disfigured = 0 @@ -328,9 +337,12 @@ /obj/item/organ/external/replaced(var/mob/living/carbon/human/target) ..() + if(istype(owner)) owner.organs_by_name[limb_name] = src owner.organs |= src + if(!species) + species = owner.species for(var/obj/item/organ/organ in src) organ.replaced(owner,src) @@ -1217,8 +1229,8 @@ Note that amputating the affected organ does in fact remove the infection from t var/datum/robolimb/R = GLOB.all_robolimbs[company] if(R) - if(!force_skintone) - force_icon = R.icon + if(!force_skintone || override_robotize_force_icon) + force_icon = override_robotize_force_icon ? override_robotize_force_icon : R.icon if(R.lifelike) status |= ORGAN_LIFELIKE if(force_prosthetic_name) @@ -1231,8 +1243,8 @@ Note that amputating the affected organ does in fact remove the infection from t else name = "[R.company] [initial(name)]" desc = "[R.desc]" - if(R.paintable) - painted = 1 + if(R.paintable || !isnull(override_robotize_painted)) + painted = !isnull(override_robotize_painted) ? override_robotize_painted : TRUE brute_mod = R.brute_mod burn_mod = R.burn_mod robotize_type = company @@ -1242,8 +1254,8 @@ Note that amputating the affected organ does in fact remove the infection from t limb_flags &= ~ORGAN_CAN_BREAK get_icon() unmutate() - for (var/obj/item/organ/external/T in children) - if(T) + if(robotize_children) + for (var/obj/item/organ/external/T in children) T.robotize(company) /obj/item/organ/external/mechassist() diff --git a/code/modules/organs/organ_icon.dm b/code/modules/organs/organ_icon.dm index 73d1a909528..7c36614aeb9 100644 --- a/code/modules/organs/organ_icon.dm +++ b/code/modules/organs/organ_icon.dm @@ -18,7 +18,7 @@ var/limb_exception = FALSE if(robotize_type) var/datum/robolimb/R = GLOB.all_robolimbs[robotize_type] - if(R?.paintable) + if(R?.paintable && (isnull(override_robotize_painted) || override_robotize_painted)) limb_exception = TRUE if((status & ORGAN_ROBOT) && !limb_exception) return diff --git a/html/changelogs/geeves-custom_item_limb_framework.yml b/html/changelogs/geeves-custom_item_limb_framework.yml new file mode 100644 index 00000000000..f149b29704d --- /dev/null +++ b/html/changelogs/geeves-custom_item_limb_framework.yml @@ -0,0 +1,6 @@ +author: Geeves + +delete-after: True + +changes: + - rscadd: "You can now view your custom items from within the loadout." diff --git a/icons/mob/human_races/fluff/gracia_arm.dmi b/icons/mob/human_races/fluff/gracia_arm.dmi new file mode 100644 index 0000000000000000000000000000000000000000..b7880d36b4a2b739442222c66b2916bd011708af GIT binary patch literal 435 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeE3?v1%WpM*3l>na**8>L*C@Lz-magQC?h?(K zbN|WP`1tspdyi;pYC27{m}p@ERFD>O?mbXFV@Z%-FoVOh8)-mJVO2;(iA!p6a#3bM zNoIZ?14G4};IM+C((hk_3qF2*qUEitb?(gh;0>V$7mXi0(mC&=d6J>1r+0^iaggz4 zV=sj{k0u?N6rvEUscyGXBfPWQSatIXi6uaKt%jkw=n*rQu&EPgOmFg@xv6ROeC``% z3|4bh6=eFBT>)Btz|+MsB%(DrL4sA8$3RQ5aE1tHc0yUGLqW*g&J71e5?Lp&QWVVd zGH;4rm@?0yg{?2CRHHj~XA{R)9-c?82I5n>1(r4%b1qYytnrmcM*D)~;slYNra*%; zjuxG-BByeBTv_0>h^