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 00000000000..b7880d36b4a
Binary files /dev/null and b/icons/mob/human_races/fluff/gracia_arm.dmi differ