diff --git a/code/__defines/bodytype.dm b/code/__defines/bodytype.dm
index 0fefad2a52b..5a720df0564 100644
--- a/code/__defines/bodytype.dm
+++ b/code/__defines/bodytype.dm
@@ -1,8 +1,10 @@
#define BODY_FLAG_EXCLUDE BITFLAG(0)
#define BODY_FLAG_HUMANOID BITFLAG(1)
#define BODY_FLAG_MONKEY BITFLAG(2)
+#define BODY_FLAG_QUADRUPED BITFLAG(3)
#define BODYTYPE_HUMANOID "humanoid body"
+#define BODYTYPE_QUADRUPED "quadruped body"
#define BODYTYPE_OTHER "alien body"
#define BODYTYPE_MONKEY "small humanoid body"
diff --git a/code/__defines/damage_organs.dm b/code/__defines/damage_organs.dm
index f585d4a0836..3b786a9c2c9 100644
--- a/code/__defines/damage_organs.dm
+++ b/code/__defines/damage_organs.dm
@@ -71,6 +71,12 @@
#define ORGAN_FLAG_CAN_DISLOCATE BITFLAG(7) // The organ can be dislocated.
#define ORGAN_FLAG_SKELETAL BITFLAG(8) // The organ has been skeletonized.
+// Organ category defines.
+/// Limb contributes only to stance damage calculation (foot)
+#define ORGAN_CATEGORY_STANCE "stance"
+/// Limb is considered the 'root' of a given stance limb (leg) - also counted for stance damage a la ORGAN_CATEGORY_STANCE
+#define ORGAN_CATEGORY_STANCE_ROOT "stance_root"
+
// Droplimb types.
#define DISMEMBER_METHOD_EDGE 0
#define DISMEMBER_METHOD_BLUNT 1
diff --git a/code/__defines/flags.dm b/code/__defines/flags.dm
index cc9efae8d51..925e8fea4d4 100644
--- a/code/__defines/flags.dm
+++ b/code/__defines/flags.dm
@@ -65,6 +65,7 @@ The latter will result in a linter warning and will not work correctly.
#define OBJ_FLAG_NOFALL BITFLAG(3) // Will prevent mobs from falling
#define OBJ_FLAG_MOVES_UNSUPPORTED BITFLAG(4) // Object moves with shuttle transition even if turf below is a background turf.
#define OBJ_FLAG_HOLLOW BITFLAG(5) // Modifies initial matter values to be lower than w_class normally sets.
+#define OBJ_FLAG_SUPPORT_MOB BITFLAG(6) // Object can be used to prop up a mob with stance damage (broken legs)
// Item-level flags (/obj/item/item_flags)
#define ITEM_FLAG_NO_BLUDGEON BITFLAG(0) // When an item has this it produces no "X has been hit by Y with Z" message with the default handler.
diff --git a/code/__defines/languages.dm b/code/__defines/languages.dm
index c6ae4185206..58b0a9277e2 100644
--- a/code/__defines/languages.dm
+++ b/code/__defines/languages.dm
@@ -10,4 +10,3 @@
#define LANG_FLAG_NO_STUTTER BITFLAG(8) // No stuttering, slurring, or other speech problems
#define LANG_FLAG_ALT_TRANSMIT BITFLAG(9) // Language is not based on vision or sound (Todo: add this into the say code and use it for the rootspeak languages)
#define LANG_FLAG_FORBIDDEN BITFLAG(10) // Language is not to be granted to a mob under any circumstances.
-
diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm
index 648edce4f73..140bc68961f 100644
--- a/code/__defines/mobs.dm
+++ b/code/__defines/mobs.dm
@@ -309,9 +309,9 @@
#define DEXTERITY_KEYBOARDS BITFLAG(4)
#define DEXTERITY_TOUCHSCREENS BITFLAG(5)
// TODO: actually get grab code to check this one.
-#define DEXTERITY_GRAPPLE BITFLAG(6)
-#define DEXTERITY_WEAPONS BITFLAG(7)
-#define DEXTERITY_COMPLEX_TOOLS BITFLAG(8)
+#define DEXTERITY_GRAPPLE BITFLAG(6) // Can the mob grab other mobs?
+#define DEXTERITY_WEAPONS BITFLAG(7) // Can the mob use guns?
+#define DEXTERITY_COMPLEX_TOOLS BITFLAG(8) // Can the mob use complex items like flashlights, handcuffs, etc?
#define DEXTERITY_BASE (DEXTERITY_SIMPLE_MACHINES|DEXTERITY_HOLD_ITEM|DEXTERITY_WIELD_ITEM|DEXTERITY_EQUIP_ITEM)
#define DEXTERITY_FULL (DEXTERITY_BASE|DEXTERITY_KEYBOARDS|DEXTERITY_TOUCHSCREENS|DEXTERITY_GRAPPLE|DEXTERITY_WEAPONS|DEXTERITY_COMPLEX_TOOLS)
diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index e92fb83744d..1d1291dfaeb 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -669,7 +669,6 @@ Turf and target are seperate in case you want to teleport some distance from a t
mobs += M
return mobs
-
/proc/parse_zone(zone)
var/static/list/zone_to_descriptor_mapping = list(
BP_R_HAND = "right hand",
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index c58d371347b..fec45f64dc1 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -217,7 +217,7 @@
inv_box = sublist[2]
inv_box.screen_loc = "CENTER:[world.icon_size/2],BOTTOM:[hand_y_offset]"
hand_y_offset += world.icon_size
- if(mymob.client)
+ if(mymob.client && length(hand_hud_objects))
mymob.client.screen |= hand_hud_objects
// Make sure all held items are on the screen and set to the correct screen loc.
@@ -232,14 +232,15 @@
if(mymob.client)
mymob.client.screen |= held // just to make sure it's visible post-login
- var/hand_x_offset = -(world.icon_size/2)
- for(var/i = 1 to length(swaphand_hud_objects))
- var/obj/swap_elem = swaphand_hud_objects[i]
- swap_elem.screen_loc = "CENTER:[hand_x_offset],BOTTOM:[hand_y_offset]"
- if(i > 1) // first two elems share a slot
- hand_x_offset += world.icon_size
- if(mymob.client)
- mymob.client.screen |= swap_elem
+ if(length(swaphand_hud_objects))
+ var/hand_x_offset = -(world.icon_size/2)
+ for(var/i = 1 to length(swaphand_hud_objects))
+ var/obj/swap_elem = swaphand_hud_objects[i]
+ swap_elem.screen_loc = "CENTER:[hand_x_offset],BOTTOM:[hand_y_offset]"
+ if(i > 1) // first two elems share a slot
+ hand_x_offset += world.icon_size
+ if(mymob.client)
+ mymob.client.screen |= swap_elem
/datum/hud/proc/BuildInventoryUI()
diff --git a/code/game/jobs/job/_job.dm b/code/game/jobs/job/_job.dm
index 1bbf9d6b13c..b8395bee03e 100644
--- a/code/game/jobs/job/_job.dm
+++ b/code/game/jobs/job/_job.dm
@@ -89,11 +89,12 @@
return title
/datum/job/proc/equip_job(var/mob/living/carbon/human/H, var/alt_title, var/datum/mil_branch/branch, var/datum/mil_rank/grade)
+ H.add_language(/decl/language/human/common)
if (required_language)
H.add_language(required_language)
H.set_default_language(required_language)
- H.add_language(/decl/language/human/common)
- H.set_default_language(/decl/language/human/common)
+ else
+ H.set_default_language(/decl/language/human/common)
var/decl/hierarchy/outfit/outfit = get_outfit(H, alt_title, branch, grade)
if(outfit)
diff --git a/code/game/objects/item_mob_overlay.dm b/code/game/objects/item_mob_overlay.dm
index d86eaaf4d29..990860e2276 100644
--- a/code/game/objects/item_mob_overlay.dm
+++ b/code/game/objects/item_mob_overlay.dm
@@ -60,38 +60,64 @@ var/global/list/icon_state_cache = list()
/obj/item/proc/get_mob_overlay(mob/user_mob, slot, bodypart, use_fallback_if_icon_missing = TRUE, skip_adjustment = FALSE)
var/state_modifier = user_mob?.get_overlay_state_modifier()
+
if(!use_single_icon)
var/mob_state = "[item_state || icon_state][state_modifier]"
var/mob_icon = global.default_onmob_icons[slot]
var/decl/bodytype/root_bodytype = user_mob.get_bodytype()
if(istype(root_bodytype))
var/use_slot = (bodypart in root_bodytype.equip_adjust) ? bodypart : slot
- return root_bodytype.get_offset_overlay_image(mob_icon, mob_state, color, use_slot)
+ return root_bodytype.get_offset_overlay_image(user_mob, mob_icon, mob_state, color, use_slot)
return overlay_image(mob_icon, mob_state, color, RESET_COLOR)
var/bodytype = user_mob?.get_bodytype_category() || BODYTYPE_HUMANOID
var/useicon = get_icon_for_bodytype(bodytype)
- var/use_state = "[bodytype]-[slot][state_modifier]"
+ var/use_state = "[bodytype]-[slot]"
+ if(state_modifier)
+ use_state = "[use_state][state_modifier]"
var/is_not_held_slot = !(slot in global.all_hand_slots)
if(bodytype != BODYTYPE_HUMANOID && !check_state_in_icon(use_state, useicon) && use_fallback_if_icon_missing)
+
var/fallback = is_not_held_slot && get_fallback_slot(slot)
- if(fallback && fallback != slot && check_state_in_icon("[bodytype]-[fallback][state_modifier]", useicon))
- slot = fallback
+ if(fallback && fallback != slot)
+ if(state_modifier)
+ if(check_state_in_icon("[bodytype]-[fallback][state_modifier]", useicon))
+ slot = fallback
+ else if(check_state_in_icon("[bodytype]-[fallback]", useicon))
+ slot = fallback
else
bodytype = BODYTYPE_HUMANOID
useicon = get_icon_for_bodytype(bodytype)
- use_state = "[bodytype]-[slot][state_modifier]"
+
+ if(state_modifier)
+ use_state = "[bodytype]-[slot][state_modifier]"
+ if(!check_state_in_icon(use_state, useicon))
+ use_state = "[bodytype]-[slot]"
+ else
+ use_state = "[bodytype]-[slot]"
if(!check_state_in_icon(use_state, useicon) && global.bodypart_to_slot_lookup_table[slot])
- use_state = "[bodytype]-[global.bodypart_to_slot_lookup_table[slot]][state_modifier]"
+
+ var/lookup_slot = global.bodypart_to_slot_lookup_table[slot]
+ if(state_modifier)
+ use_state = "[bodytype]-[lookup_slot][state_modifier]"
+ if(!check_state_in_icon(use_state, useicon))
+ use_state = "[bodytype]-[lookup_slot]"
+ else
+ use_state = "[bodytype]-[lookup_slot]"
if(!check_state_in_icon(use_state, useicon))
var/fallback = use_fallback_if_icon_missing && is_not_held_slot && get_fallback_slot(slot)
if(!fallback)
return new /image
slot = fallback
- use_state = "[bodytype]-[slot][state_modifier]"
+ if(state_modifier)
+ use_state = "[bodytype]-[slot][state_modifier]"
+ if(!check_state_in_icon(use_state, useicon))
+ use_state = "[bodytype]-[slot]"
+ else
+ use_state = "[bodytype]-[slot]"
if(!check_state_in_icon(use_state, useicon))
return new /image
@@ -115,10 +141,10 @@ var/global/list/icon_state_cache = list()
var/decl/bodytype/root_bodytype = user_mob?.get_bodytype()
if(root_bodytype && root_bodytype.bodytype_category != bodytype)
var/list/overlays_to_offset = overlay.overlays
- overlay = root_bodytype.get_offset_overlay_image(overlay.icon, overlay.icon_state, color, (bodypart || slot))
+ overlay = root_bodytype.get_offset_overlay_image(user_mob, overlay.icon, overlay.icon_state, color, (bodypart || slot))
for(var/thing in overlays_to_offset)
var/image/I = thing // Technically an appearance but don't think we can cast to those
- var/image/adjusted_overlay = root_bodytype.get_offset_overlay_image(I.icon, I.icon_state, I.color, (bodypart || slot))
+ var/image/adjusted_overlay = root_bodytype.get_offset_overlay_image(user_mob, I.icon, I.icon_state, I.color, (bodypart || slot))
adjusted_overlay.appearance_flags = I.appearance_flags
adjusted_overlay.plane = I.plane
adjusted_overlay.layer = I.layer
diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm
index 00484cf0e65..a59f168c170 100644
--- a/code/game/objects/items/__item.dm
+++ b/code/game/objects/items/__item.dm
@@ -134,6 +134,7 @@
return origin_tech
/obj/item/Initialize(var/ml, var/material_key)
+
if(isnull(current_health))
current_health = max_health //Make sure to propagate max_health to health var before material setup, for consistency
if(!ispath(material_key, /decl/material))
@@ -143,6 +144,8 @@
. = ..()
+ setup_sprite_sheets()
+
if(islist(armor))
for(var/type in armor)
if(armor[type]) // Don't set it if it gives no armor anyway, which is many items.
@@ -1109,3 +1112,6 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out.
/obj/item/proc/loadout_setup(mob/wearer, metadata)
return
+
+/obj/item/proc/setup_sprite_sheets()
+ return
diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm
index f471fd99a37..f69317716b5 100644
--- a/code/game/objects/items/weapons/cards_ids.dm
+++ b/code/game/objects/items/weapons/cards_ids.dm
@@ -287,7 +287,7 @@ var/global/const/NO_EMAG_ACT = -50
dat += text("Blood Type: []
\n", blood_type)
dat += text("DNA Hash: []
\n", dna_hash)
if(front && side)
- dat +="
Photo:
| "
+ dat +="Photo:
| "
dat += ""
return jointext(dat,null)
diff --git a/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm
index 46ae59f4b49..0dac0481e0e 100644
--- a/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm
@@ -24,6 +24,7 @@
parts_amount = 2
parts_type = /obj/item/stack/material/strut
user_comfort = 1
+ obj_flags = OBJ_FLAG_SUPPORT_MOB
var/base_icon = "bed"
var/padding_color
diff --git a/code/game/objects/structures/stool_bed_chair_nest_sofa/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/chairs.dm
index 910e18bdade..7524c9a29aa 100644
--- a/code/game/objects/structures/stool_bed_chair_nest_sofa/chairs.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/chairs.dm
@@ -6,7 +6,7 @@
color = "#666666"
buckle_dir = 0
buckle_lying = 0 //force people to sit up in chairs when buckled
- obj_flags = OBJ_FLAG_ROTATABLE
+ obj_flags = OBJ_FLAG_ROTATABLE | OBJ_FLAG_SUPPORT_MOB
base_icon = "chair"
user_comfort = 0.5
diff --git a/code/game/objects/structures/stool_bed_chair_nest_sofa/stools.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/stools.dm
index 7f55c87c922..4c1bcb3cf12 100644
--- a/code/game/objects/structures/stool_bed_chair_nest_sofa/stools.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/stools.dm
@@ -1,15 +1,16 @@
//Todo: add leather and cloth for arbitrary coloured stools.
/obj/item/stool
- name = "stool"
- desc = "Apply butt."
- icon = 'icons/obj/furniture.dmi'
- icon_state = "stool_preview" //set for the map
- item_state = "stool"
- randpixel = 0
- force = 10
- throwforce = 10
- w_class = ITEM_SIZE_HUGE
- material = DEFAULT_FURNITURE_MATERIAL
+ name = "stool"
+ desc = "Apply butt."
+ icon = 'icons/obj/furniture.dmi'
+ icon_state = "stool_preview" //set for the map
+ item_state = "stool"
+ randpixel = 0
+ force = 10
+ throwforce = 10
+ w_class = ITEM_SIZE_HUGE
+ material = DEFAULT_FURNITURE_MATERIAL
+ obj_flags = OBJ_FLAG_SUPPORT_MOB | OBJ_FLAG_ROTATABLE
var/base_icon = "stool"
var/padding_color
var/decl/material/padding_material
diff --git a/code/modules/clothing/_clothing.dm b/code/modules/clothing/_clothing.dm
index e2e7cad5224..84328ea6da7 100644
--- a/code/modules/clothing/_clothing.dm
+++ b/code/modules/clothing/_clothing.dm
@@ -30,6 +30,8 @@
/obj/item/clothing/Initialize()
. = ..()
+ setup_equip_flags()
+
if(accessory_slot)
if(isnull(accessory_removable))
accessory_removable = TRUE
@@ -62,6 +64,13 @@
/obj/item/clothing/proc/is_accessory()
return istype(loc, /obj/item/clothing)
+/obj/item/clothing/proc/setup_equip_flags()
+ if(!isnull(bodytype_equip_flags))
+ if(bodytype_equip_flags & BODY_FLAG_EXCLUDE)
+ bodytype_equip_flags |= BODY_FLAG_QUADRUPED
+ else
+ bodytype_equip_flags &= ~BODY_FLAG_QUADRUPED
+
/obj/item/clothing/can_contaminate()
return TRUE
diff --git a/code/modules/clothing/head/_head.dm b/code/modules/clothing/head/_head.dm
index 17fa36be7bd..02e9d76f03d 100644
--- a/code/modules/clothing/head/_head.dm
+++ b/code/modules/clothing/head/_head.dm
@@ -51,7 +51,7 @@
if(overlay && on && check_state_in_icon("[overlay.icon_state]_light", overlay.icon))
var/light_overlay
if(user_mob.get_bodytype_category() != bodytype)
- light_overlay = user_mob.get_bodytype()?.get_offset_overlay_image(overlay.icon, "[overlay.icon_state]_light", null, slot)
+ light_overlay = user_mob.get_bodytype()?.get_offset_overlay_image(user_mob, overlay.icon, "[overlay.icon_state]_light", null, slot)
if(!light_overlay)
light_overlay = image(overlay.icon, "[overlay.icon_state]_light")
overlay.overlays += light_overlay
diff --git a/code/modules/clothing/suits/cloaks.dm b/code/modules/clothing/suits/cloaks.dm
index 20b2dd5d7f6..a1cb8f3d802 100644
--- a/code/modules/clothing/suits/cloaks.dm
+++ b/code/modules/clothing/suits/cloaks.dm
@@ -35,8 +35,8 @@
var/bodyicon = get_icon_for_bodytype(bodytype)
var/decl/bodytype/root_bodytype = user_mob.get_bodytype()
if(user_mob && bodytype != root_bodytype.bodytype_category)
- underlay = root_bodytype.get_offset_overlay_image(bodyicon, "[bodytype]-underlay", color, slot)
- cloverlay = root_bodytype.get_offset_overlay_image(bodyicon, "[bodytype]-overlay", color, slot)
+ underlay = root_bodytype.get_offset_overlay_image(user_mob, bodyicon, "[bodytype]-underlay", color, slot)
+ cloverlay = root_bodytype.get_offset_overlay_image(user_mob, bodyicon, "[bodytype]-overlay", color, slot)
else
underlay = image(bodyicon, "[bodytype]-underlay")
cloverlay = image(bodyicon, "[bodytype]-overlay")
diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm
index 25aef1b9729..90c30e87287 100644
--- a/code/modules/mob/living/carbon/carbon_defines.dm
+++ b/code/modules/mob/living/carbon/carbon_defines.dm
@@ -26,5 +26,6 @@
var/list/organs_by_tag
var/tmp/list/internal_organs
var/tmp/list/external_organs
+ var/list/organs_by_category
var/player_triggered_sleeping = 0
diff --git a/code/modules/mob/living/carbon/carbon_organs.dm b/code/modules/mob/living/carbon/carbon_organs.dm
index 4fd1c5d27c1..5e7378e8747 100644
--- a/code/modules/mob/living/carbon/carbon_organs.dm
+++ b/code/modules/mob/living/carbon/carbon_organs.dm
@@ -19,12 +19,18 @@
/mob/living/carbon/has_internal_organs()
return LAZYLEN(internal_organs) > 0
+/mob/living/carbon/get_organs_by_categories(var/list/categories)
+ for(var/organ_cat in categories)
+ if(organ_cat in organs_by_category)
+ LAZYDISTINCTADD(., organs_by_category[organ_cat])
+
//Deletes all references to organs
/mob/living/carbon/delete_organs()
..()
- organs_by_tag = null
- internal_organs = null
- external_organs = null
+ organs_by_tag = null
+ internal_organs = null
+ external_organs = null
+ organs_by_category = null
/mob/living/carbon/add_organ(obj/item/organ/O, obj/item/organ/external/affected, in_place, update_icon, detached, skip_health_update = FALSE)
var/obj/item/organ/existing = LAZYACCESS(organs_by_tag, O.organ_tag)
@@ -41,6 +47,12 @@
else if(!O.is_internal())
LAZYSET(organs_by_tag, O.organ_tag, O)
LAZYDISTINCTADD(external_organs, O)
+
+ // Update our organ category lists, if neeed.
+ if(O.organ_category)
+ LAZYINITLIST(organs_by_category)
+ LAZYDISTINCTADD(organs_by_category[O.organ_category], O)
+
. = ..()
/mob/living/carbon/remove_organ(var/obj/item/organ/O, var/drop_organ = TRUE, var/detach = TRUE, var/ignore_children = FALSE, var/in_place = FALSE, var/update_icon = TRUE, var/skip_health_update = FALSE)
@@ -54,6 +66,12 @@
else
LAZYREMOVE(external_organs, O)
+ // Update our organ category lists, if neeed.
+ if(O.organ_category && islist(organs_by_category))
+ organs_by_category[O.organ_category] -= O
+ if(LAZYLEN(organs_by_category[O.organ_category]) <= 0)
+ LAZYREMOVE(organs_by_category, O.organ_category)
+
/mob/living/carbon/get_bodytype()
RETURN_TYPE(/decl/bodytype)
// If the root organ ever changes/isn't always the chest, this will need to be changed.
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 140ebcf8964..e4f2f3ac462 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -970,14 +970,6 @@
. = global.using_map.default_cultural_info[token]
PRINT_STACK_TRACE("get_cultural_value() tried to return a non-instance value for token '[token]' - full culture list: [json_encode(cultural_info)] default species culture list: [json_encode(global.using_map.default_cultural_info)]")
-/mob/living/carbon/human/needs_wheelchair()
- var/stance_damage = 0
- for(var/limb_tag in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT))
- var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(src, limb_tag)
- if(!E || !E.is_usable())
- stance_damage += 2
- return stance_damage >= 4
-
/mob/living/carbon/human/get_digestion_product()
return species.get_digestion_product(src)
@@ -1325,3 +1317,6 @@
volume = round(volume)
if(volume > 0 && range > 0)
playsound(T, footsound, volume, 1, range)
+
+/mob/living/get_overlay_state_modifier()
+ return null
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index de81177ac58..709b7616ed5 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -50,8 +50,6 @@
var/list/equipment_overlays = list()
var/datum/mil_branch/char_branch = null
var/datum/mil_rank/char_rank = null
- /// Whether this mob's ability to stand has been affected
- var/stance_damage = 0
/// default unarmed attack
var/decl/natural_attack/default_attack
/// machine that is currently applying visual effects to this mob. Only used for camera monitors currently.
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index 483589d994b..87fa5de044d 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -59,8 +59,6 @@
if (root_bodytype && bodytemperature < root_bodytype.cold_discomfort_level)
tally += (root_bodytype.cold_discomfort_level - bodytemperature) / 10 * 1.75
- tally += max(2 * stance_damage, 0) //damaged/missing feet or legs is slow
-
if(mRun in mutations)
tally = 0
@@ -141,7 +139,6 @@
return (stamina > 0)
/mob/living/carbon/human/UpdateLyingBuckledAndVerbStatus()
- var/old_buckled_lying = !!buckled?.buckle_lying
var/old_lying = lying
. = ..()
if(!buckled)
@@ -150,6 +147,5 @@
var/mob/M = buckled
M.unbuckle_mob()
var/decl/bodytype/B = get_bodytype()
- playsound(loc, B.bodyfall_sounds, 50, TRUE, -1)
- else if(!lying && !old_buckled_lying)
- handle_stance() // Force an immediate stance update.
+ if(B)
+ playsound(loc, B.bodyfall_sounds, 50, TRUE, -1)
diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm
index 0d09afd17d5..38a0fca364e 100644
--- a/code/modules/mob/living/carbon/human/human_organs.dm
+++ b/code/modules/mob/living/carbon/human/human_organs.dm
@@ -42,8 +42,6 @@
else
vital_organ_missing_time = null
-
-
//processing internal organs is pretty cheap, do that first.
for(var/obj/item/organ/I in internal_organs)
I.Process()
@@ -55,9 +53,6 @@
for(var/obj/item/organ/external/Ex in get_external_organs())
LAZYDISTINCTADD(bad_external_organs, Ex)
- handle_stance()
- handle_grasp()
-
if(!force_process && !LAZYLEN(bad_external_organs))
return
@@ -82,169 +77,6 @@
if (W.infection_check())
W.germ_level += 1
-/mob/living/carbon/human/proc/Check_Proppable_Object()
- for(var/turf/T in RANGE_TURFS(src, 1)) //we only care for non-space turfs
- if(T.density && T.simulated) //walls work
- return 1
-
- for(var/obj/O in orange(1, src))
- if(O && O.density && O.anchored)
- return 1
-
- return 0
-
-/mob/living/carbon/human/proc/handle_stance()
- set waitfor = FALSE // Can sleep in emotes.
- // Don't need to process any of this if they aren't standing anyways
- // unless their stance is damaged, and we want to check if they should stay down
- if (!stance_damage && (lying || resting) && (life_tick % 4) != 0)
- return
-
- stance_damage = 0
-
- // Buckled to a bed/chair. Stance damage is forced to 0 since they're sitting on something solid
- if (istype(buckled, /obj/structure/bed))
- return
-
- // Can't fall if nothing pulls you down
- if(!has_gravity())
- return
-
- var/limb_pain
- for(var/limb_tag in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT))
- var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(src, limb_tag)
- if(!E || !E.is_usable())
- stance_damage += 2 // let it fail even if just foot&leg
- else if (E.is_malfunctioning())
- //malfunctioning only happens intermittently so treat it as a missing limb when it procs
- stance_damage += 2
- if(prob(10))
- visible_message("\The [src]'s [E.name] [pick("twitches", "shudders")] and sparks!")
- spark_at(src, amount = 5, holder = src)
- else if (E.is_broken())
- stance_damage += 1
- else if (E.is_dislocated())
- stance_damage += 0.5
-
- if(E) limb_pain = E.can_feel_pain()
-
- // Canes and crutches help you stand (if the latter is ever added)
- // One cane mitigates a broken leg+foot, or a missing foot.
- // Two canes are needed for a lost leg. If you are missing both legs, canes aren't gonna help you.
- for(var/obj/item/cane/C in get_held_items())
- stance_damage -= 2
-
- if(MOVING_DELIBERATELY(src)) //you don't suffer as much if you aren't trying to run
- var/working_pair = 2
- var/obj/item/organ/external/LF = GET_EXTERNAL_ORGAN(src, BP_L_FOOT)
- var/obj/item/organ/external/LL = GET_EXTERNAL_ORGAN(src, BP_L_LEG)
- var/obj/item/organ/external/RF = GET_EXTERNAL_ORGAN(src, BP_R_FOOT)
- var/obj/item/organ/external/RL = GET_EXTERNAL_ORGAN(src, BP_R_LEG)
- if(!LL || !LF) //are we down a limb?
- working_pair -= 1
- else if((!LL.is_usable()) || (!LF.is_usable())) //if not, is it usable?
- working_pair -= 1
- if(!RL || !RF)
- working_pair -= 1
- else if((!RL.is_usable()) || (!RF.is_usable()))
- working_pair -= 1
- if(working_pair >= 1)
- stance_damage -= 1
- if(Check_Proppable_Object()) //it helps to lean on something if you've got another leg to stand on
- stance_damage -= 1
-
- var/list/objects_to_sit_on = list(
- /obj/item/stool,
- /obj/structure/bed,
- )
-
- for(var/type in objects_to_sit_on) //things that can't be climbed but can be propped-up-on
- if(locate(type) in src.loc)
- return
-
- // standing is poor
- if(stance_damage >= 4 || (stance_damage >= 2 && prob(2)) || (stance_damage >= 3 && prob(8)))
- if(!(lying || resting))
- if(limb_pain)
- emote(/decl/emote/audible/scream)
- custom_emote(VISIBLE_MESSAGE, "collapses!")
- SET_STATUS_MAX(src, STAT_WEAK, 3) //can't emote while weakened, apparently.
-
-/mob/living/carbon/human/proc/handle_grasp()
- for(var/hand_slot in get_held_item_slots())
- var/datum/inventory_slot/inv_slot = get_inventory_slot_datum(hand_slot)
- if(!inv_slot?.requires_organ_tag)
- continue
- var/holding = inv_slot?.get_equipped_item()
- if(holding)
- var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(src, inv_slot.requires_organ_tag)
- if((!E || !E.is_usable() || E.is_parent_dislocated()) && try_unequip(holding))
- grasp_damage_disarm(E)
-
-/mob/living/carbon/human/proc/stance_damage_prone(var/obj/item/organ/external/affected)
-
- if(affected && (!BP_IS_PROSTHETIC(affected) || affected.is_robotic()))
- switch(affected.body_part)
- if(SLOT_FOOT_LEFT, SLOT_FOOT_RIGHT)
- if(!BP_IS_PROSTHETIC(affected))
- to_chat(src, SPAN_WARNING("You lose your footing as your [affected.name] spasms!"))
- else
- to_chat(src, SPAN_WARNING("You lose your footing as your [affected.name] [pick("twitches", "shudders")]!"))
- if(SLOT_LEG_LEFT, SLOT_LEG_RIGHT)
- if(!BP_IS_PROSTHETIC(affected))
- to_chat(src, SPAN_WARNING("Your [affected.name] buckles from the shock!"))
- else
- to_chat(src, SPAN_WARNING("You lose your balance as [affected.name] [pick("malfunctions", "freezes","shudders")]!"))
- else
- return
- SET_STATUS_MAX(src, STAT_WEAK, 4)
-
-/mob/living/carbon/human/proc/grasp_damage_disarm(var/obj/item/organ/external/affected)
-
- var/list/drop_held_item_slots
- if(istype(affected))
- for(var/grasp_tag in (list(affected.organ_tag) | affected.children))
- var/datum/inventory_slot/inv_slot = get_inventory_slot_datum(grasp_tag)
- if(inv_slot?.get_equipped_item())
- LAZYDISTINCTADD(drop_held_item_slots, inv_slot)
- else if(istype(affected, /datum/inventory_slot))
- drop_held_item_slots = list(affected)
-
- if(!LAZYLEN(drop_held_item_slots))
- return
-
- for(var/datum/inventory_slot/inv_slot in drop_held_item_slots)
- if(!try_unequip(inv_slot.get_equipped_item()))
- continue
- var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(src, inv_slot.slot_id)
- if(!E)
- continue
- if(E.is_robotic())
- var/decl/pronouns/G = get_pronouns()
- visible_message("\The [src] drops what [G.he] [G.is] holding, [G.his] [E.name] malfunctioning!")
- spark_at(src, 5, holder=src)
- continue
-
- var/grasp_name = E.name
- if((E.body_part in list(SLOT_ARM_LEFT, SLOT_ARM_RIGHT)) && LAZYLEN(E.children))
- var/obj/item/organ/external/hand = pick(E.children)
- grasp_name = hand.name
-
- if(E.can_feel_pain())
- var/emote_scream = pick("screams in pain", "lets out a sharp cry", "cries out")
- var/emote_scream_alt = pick("scream in pain", "let out a sharp cry", "cry out")
- visible_message(
- "\The [src] [emote_scream] and drops what they were holding in their [grasp_name]!",
- null,
- "You hear someone [emote_scream_alt]!"
- )
- custom_pain("The sharp pain in your [E.name] forces you to drop what you were holding in your [grasp_name]!", 30)
- else
- visible_message("\The [src] drops what they were holding in their [grasp_name]!")
-
-/mob/living/proc/is_asystole()
- return FALSE
-
/mob/living/carbon/human/is_asystole()
if(isSynthetic())
var/obj/item/organ/internal/cell/C = get_organ(BP_CELL, /obj/item/organ/internal/cell)
diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm
index b36bf38379a..61ceec9d9c1 100644
--- a/code/modules/mob/living/carbon/human/say.dm
+++ b/code/modules/mob/living/carbon/human/say.dm
@@ -132,11 +132,16 @@
/mob/living/carbon/human/can_speak(decl/language/speaking)
if(ispath(speaking, /decl/language))
speaking = GET_DECL(speaking)
- if(species && speaking && (speaking.name in species.assisted_langs))
- for(var/obj/item/organ/internal/voicebox/I in get_internal_organs())
- if(I.is_usable() && I.assists_languages[speaking])
- return TRUE
- return FALSE
+ if(!istype(speaking))
+ return ..()
+ if(species)
+ if(speaking.type in species.assisted_langs)
+ for(var/obj/item/organ/internal/voicebox/I in get_internal_organs())
+ if(I.is_usable() && I.assists_languages[speaking])
+ return TRUE
+ return FALSE
+ else if(speaking.type in species.unspeakable_langs)
+ return FALSE
. = ..()
/mob/living/carbon/human/parse_language(var/message)
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index c2d87414f96..3e41420a44a 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -211,7 +211,7 @@ Please contact me on #coderbus IRC. ~Carn x
var/turn_angle
var/matrix/M = matrix()
M.Scale(desired_scale_x, desired_scale_y)
- if(lying)
+ if(lying && get_bodytype()?.rotate_on_prone)
// This locate is very bad but trying to get it to respect the buckled dir is proving tricky.
if((dir & EAST) || (isturf(loc) && (locate(/obj/structure/bed) in loc)))
turn_angle = 90
@@ -343,7 +343,7 @@ Please contact me on #coderbus IRC. ~Carn x
var/image/I
if(UW.slot_offset_str && LAZYACCESS(root_bodytype.equip_adjust, UW.slot_offset_str))
- I = root_bodytype.get_offset_overlay_image(UW.icon, UW.icon_state, UW.color, UW.slot_offset_str)
+ I = root_bodytype.get_offset_overlay_image(src, UW.icon, UW.icon_state, UW.color, UW.slot_offset_str)
else
I = image(icon = UW.icon, icon_state = UW.icon_state)
I.color = UW.color
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index dc6dfd29cbb..c4d7649012e 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -37,11 +37,66 @@
handle_fire()
handle_actions()
UpdateLyingBuckledAndVerbStatus()
+ handle_grasp()
+ handle_stance()
handle_regular_hud_updates()
handle_status_effects()
-
return 1
+/mob/living/proc/handle_grasp()
+ for(var/hand_slot in get_held_item_slots())
+ var/datum/inventory_slot/inv_slot = get_inventory_slot_datum(hand_slot)
+ if(!inv_slot?.requires_organ_tag)
+ continue
+ var/holding = inv_slot?.get_equipped_item()
+ if(holding)
+ var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(src, inv_slot.requires_organ_tag)
+ if((!E || !E.is_usable() || E.is_parent_dislocated()) && try_unequip(holding))
+ grasp_damage_disarm(E)
+
+/mob/living/proc/grasp_damage_disarm(var/obj/item/organ/external/affected)
+
+ var/list/drop_held_item_slots
+ if(istype(affected))
+ for(var/grasp_tag in (list(affected.organ_tag) | affected.children))
+ var/datum/inventory_slot/inv_slot = get_inventory_slot_datum(grasp_tag)
+ if(inv_slot?.get_equipped_item())
+ LAZYDISTINCTADD(drop_held_item_slots, inv_slot)
+ else if(istype(affected, /datum/inventory_slot))
+ drop_held_item_slots = list(affected)
+
+ if(!LAZYLEN(drop_held_item_slots))
+ return
+
+ for(var/datum/inventory_slot/inv_slot in drop_held_item_slots)
+ if(!try_unequip(inv_slot.get_equipped_item()))
+ continue
+ var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(src, inv_slot.slot_id)
+ if(!E)
+ continue
+ if(E.is_robotic())
+ var/decl/pronouns/G = get_pronouns()
+ visible_message("\The [src] drops what [G.he] [G.is] holding, [G.his] [E.name] malfunctioning!")
+ spark_at(src, 5, holder=src)
+ continue
+
+ var/grasp_name = E.name
+ if((E.body_part in list(SLOT_ARM_LEFT, SLOT_ARM_RIGHT)) && LAZYLEN(E.children))
+ var/obj/item/organ/external/hand = pick(E.children)
+ grasp_name = hand.name
+
+ if(E.can_feel_pain())
+ var/emote_scream = pick("screams in pain", "lets out a sharp cry", "cries out")
+ var/emote_scream_alt = pick("scream in pain", "let out a sharp cry", "cry out")
+ visible_message(
+ "\The [src] [emote_scream] and drops what they were holding in their [grasp_name]!",
+ null,
+ "You hear someone [emote_scream_alt]!"
+ )
+ custom_pain("The sharp pain in your [E.name] forces you to drop what you were holding in your [grasp_name]!", 30)
+ else
+ visible_message("\The [src] drops what they were holding in their [grasp_name]!")
+
/mob/living/proc/handle_living_non_stasis_processes()
SHOULD_CALL_PARENT(TRUE)
// hungy
@@ -507,3 +562,133 @@
SET_STATUS_MAX(src, STAT_WEAK, current_size)
apply_damage(current_size * 3, IRRADIATE, damage_flags = DAM_DISPERSED)
return ..()
+
+#define LIMB_UNUSABLE 2
+#define LIMB_DAMAGED 1
+#define LIMB_IMPAIRED 0.5
+
+/mob/living/proc/handle_stance()
+ set waitfor = FALSE // Can sleep in emotes.
+ // Don't need to process any of this if they aren't standing anyways
+ // unless their stance is damaged, and we want to check if they should stay down
+ if (!stance_damage && (lying || resting) && (life_tick % 4) != 0)
+ return
+
+ stance_damage = 0
+
+ // Buckled to a bed/chair. Stance damage is forced to 0 since they're sitting on something solid
+ if (istype(buckled, /obj/structure/bed))
+ return
+
+ // Can't fall if nothing pulls you down
+ if(!has_gravity())
+ return
+
+ // If we don't have a bodytype, all the limb checking below is going to be nonsensical.
+ var/decl/bodytype/root_bodytype = get_bodytype()
+ if(!root_bodytype)
+ return
+
+ var/static/list/all_stance_limbs = list(ORGAN_CATEGORY_STANCE, ORGAN_CATEGORY_STANCE_ROOT)
+ var/expected_limbs_for_bodytype = root_bodytype.get_expected_organ_count_for_categories(all_stance_limbs)
+ if(expected_limbs_for_bodytype <= 0)
+ return // we don't care about stance for whatever reason.
+
+ // Is there something in our loc we can prop ourselves on?
+ if(length(loc?.contents))
+ for(var/obj/thing in loc.contents)
+ if(thing.obj_flags & OBJ_FLAG_SUPPORT_MOB)
+ return
+
+ var/found_limbs = 0
+ var/had_limb_pain = FALSE
+ for(var/obj/item/organ/external/limb in get_organs_by_categories(all_stance_limbs))
+ found_limbs++
+ var/add_stance_damage = 0
+ if(limb.is_malfunctioning())
+ // malfunctioning only happens intermittently so treat it as a missing limb when it procs
+ add_stance_damage = LIMB_UNUSABLE
+ if(prob(10))
+ visible_message("\The [src]'s [limb.name] [pick("twitches", "shudders")] and sparks!")
+ spark_at(src, amount = 5, holder = src)
+ else if(!limb.is_usable())
+ add_stance_damage = LIMB_UNUSABLE
+ else if (limb.is_broken())
+ add_stance_damage = LIMB_DAMAGED
+ else if (limb.is_dislocated())
+ add_stance_damage = LIMB_IMPAIRED
+
+ if(add_stance_damage > 0)
+ // Keep track of if any of our limbs can feel pain and has failed,
+ // so we don't scream if it's a prosthetic that has broken.
+ had_limb_pain = had_limb_pain || limb.can_feel_pain()
+ stance_damage += add_stance_damage
+
+ // Add missing limbs as unusable.
+ stance_damage += max(0, expected_limbs_for_bodytype - found_limbs) * LIMB_UNUSABLE
+
+ // Canes and crutches help you stand (if the latter is ever added)
+ // One cane mitigates a broken leg+foot, or a missing foot.
+ // Two canes are needed for a lost leg. If you are missing both legs, canes aren't gonna help you.
+ for(var/obj/item/cane/C in get_held_items())
+ stance_damage -= LIMB_UNUSABLE // Counts for a single functional limb.
+
+ // Calculate the expected and actual number of functioning legs we have.
+ var/has_sufficient_working_legs = TRUE
+ var/list/root_limb_tags = root_bodytype.organ_tags_by_category[ORGAN_CATEGORY_STANCE_ROOT]
+ var/minimum_working_legs = CEILING(length(root_limb_tags) * 0.5)
+ if(minimum_working_legs > 0)
+ var/leg_count = 0
+ has_sufficient_working_legs = FALSE
+ for(var/organ_tag in root_limb_tags)
+ var/obj/item/organ/external/stance_root = GET_EXTERNAL_ORGAN(src, organ_tag)
+ if(!stance_root || !stance_root.is_usable())
+ continue
+ if(!length(stance_root.children))
+ continue
+ // In theory a leg may have multiple children in the future; this
+ // will need to be revisited for fork-legged insect people or whatever.
+ var/has_usable_child = FALSE
+ for(var/child_tag in stance_root.children)
+ var/obj/item/organ/external/stance_child = GET_EXTERNAL_ORGAN(src, child_tag)
+ if(stance_child?.is_usable())
+ has_usable_child = TRUE
+ break
+ if(has_usable_child)
+ leg_count++
+ if(leg_count >= minimum_working_legs)
+ has_sufficient_working_legs = TRUE
+ break
+
+ // Having half or more of our expected number of working legs allows us to mitigate some stance damage.
+ if(has_sufficient_working_legs)
+ if(find_mob_supporting_object()) //it helps to lean on something if you've got another leg to stand on
+ stance_damage -= LIMB_UNUSABLE
+ else
+ stance_damage -= LIMB_DAMAGED
+
+ // standing is poor
+ if(stance_damage >= expected_limbs_for_bodytype || (!MOVING_DELIBERATELY(src) && ((stance_damage >= (expected_limbs_for_bodytype*0.75) && prob(8)) || (stance_damage >= (expected_limbs_for_bodytype*0.5) && prob(2)))))
+ if(!(lying || resting))
+ if(had_limb_pain)
+ emote(/decl/emote/audible/scream)
+ custom_emote(VISIBLE_MESSAGE, "collapses!")
+ SET_STATUS_MAX(src, STAT_WEAK, 3) //can't emote while weakened, apparently.
+
+/mob/living/proc/stance_damage_prone(var/obj/item/organ/external/affected)
+
+ if(affected && (!BP_IS_PROSTHETIC(affected) || affected.is_robotic()))
+ switch(affected.body_part)
+ if(SLOT_FOOT_LEFT, SLOT_FOOT_RIGHT)
+ if(!BP_IS_PROSTHETIC(affected))
+ to_chat(src, SPAN_WARNING("You lose your footing as your [affected.name] spasms!"))
+ else
+ to_chat(src, SPAN_WARNING("You lose your footing as your [affected.name] [pick("twitches", "shudders")]!"))
+ if(SLOT_LEG_LEFT, SLOT_LEG_RIGHT)
+ if(!BP_IS_PROSTHETIC(affected))
+ to_chat(src, SPAN_WARNING("Your [affected.name] buckles from the shock!"))
+ else
+ to_chat(src, SPAN_WARNING("You lose your balance as [affected.name] [pick("malfunctions", "freezes","shudders")]!"))
+ else
+ return
+ SET_STATUS_MAX(src, STAT_WEAK, 4)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 836017a3baa..9058e3b9c91 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -604,6 +604,7 @@ default behaviour is:
resting = !resting
UpdateLyingBuckledAndVerbStatus()
update_icon()
+ update_tail_showing()
to_chat(src, SPAN_NOTICE("You are now [resting ? "resting" : "getting up"]."))
//called when the mob receives a bright flash
@@ -772,7 +773,12 @@ default behaviour is:
fluids.trans_to_holder(touching_reagents, saturation)
/mob/living/proc/needs_wheelchair()
- return FALSE
+ var/tmp_stance_damage = 0
+ for(var/limb_tag in list(BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT))
+ var/obj/item/organ/external/E = GET_EXTERNAL_ORGAN(src, limb_tag)
+ if(!E || !E.is_usable())
+ tmp_stance_damage += 2
+ return tmp_stance_damage >= 4
/mob/living/proc/seizure()
set waitfor = 0
@@ -1448,3 +1454,20 @@ default behaviour is:
/mob/living/proc/handle_footsteps()
return
+
+/mob/living/get_movement_delay(var/travel_dir)
+ . = ..()
+ if(stance_damage)
+ . += max(2 * stance_damage, 0) //damaged/missing feet or legs is slow
+
+/mob/living/proc/find_mob_supporting_object()
+ for(var/turf/T in RANGE_TURFS(src, 1))
+ if(T.density && T.simulated)
+ return TRUE
+ for(var/obj/O in orange(1, src))
+ if((O.obj_flags & OBJ_FLAG_SUPPORT_MOB) || (O.density && O.anchored))
+ return TRUE
+ return FALSE
+
+/mob/living/proc/is_asystole()
+ return FALSE
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 8339892a1cf..7c98e8264ba 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -72,3 +72,6 @@
// Used to track appearance descriptor datums.
// Currently only on humans due to the spaghetti code involved, TODO: generalize.
var/list/appearance_descriptors
+
+ /// Whether this mob's ability to stand has been affected
+ var/stance_damage = 0
diff --git a/code/modules/mob/living/living_organs.dm b/code/modules/mob/living/living_organs.dm
index 704e2833f5a..f31461bcb1d 100644
--- a/code/modules/mob/living/living_organs.dm
+++ b/code/modules/mob/living/living_organs.dm
@@ -10,6 +10,9 @@
/mob/living/proc/get_internal_organs()
return
+/mob/living/proc/get_organs_by_categories(var/category)
+ return
+
//Those are meant to be overriden with optimizations
/mob/living/proc/has_organs()
return LAZYLEN(get_organs()) > 0
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 8f29b8589b3..070c658c826 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1061,6 +1061,7 @@
return gripper.get_dexterity(silent)
// If this slot requires an organ, do the appropriate organ checks.
+ check_slot = gripper.requires_organ_tag
var/obj/item/organ/external/active_hand = GET_EXTERNAL_ORGAN(src, check_slot)
if(!active_hand)
if(!silent)
@@ -1078,7 +1079,6 @@
return (active_hand.get_manual_dexterity() & ~dex_malus)
return active_hand.get_manual_dexterity()
-
/mob/proc/check_dexterity(var/dex_level = DEXTERITY_FULL, var/silent = FALSE)
. = (get_dexterity(silent) & dex_level) == dex_level
if(!. && !silent)
diff --git a/code/modules/organs/external/_external.dm b/code/modules/organs/external/_external.dm
index cb7e8912473..bf1211d44bf 100644
--- a/code/modules/organs/external/_external.dm
+++ b/code/modules/organs/external/_external.dm
@@ -36,8 +36,9 @@
var/skin_colour // skin colour
var/skin_blend = ICON_ADD // How the skin colour is applied.
var/hair_colour // hair colour
- var/render_alpha = 255
+ var/render_alpha = 255 // Alpha value to use for rendering the icon (slime transparency)
var/skip_body_icon_draw = FALSE // Set to true to skip including this organ on the human body sprite.
+ var/icon_state_modifier // String modifier to icon_state used for prone icons, etc.
/// Sprite accessories like hair and markings to apply to the organ icon and owner.
VAR_PRIVATE/list/_sprite_accessories
diff --git a/code/modules/organs/external/_external_icons.dm b/code/modules/organs/external/_external_icons.dm
index a0018a762e3..35a24a3948e 100644
--- a/code/modules/organs/external/_external_icons.dm
+++ b/code/modules/organs/external/_external_icons.dm
@@ -252,9 +252,10 @@ var/global/list/organ_icon_cache = list()
. = ..()
// Update our cache key and refresh or create our base icon.
+ var/next_state = owner ? "[organ_tag][owner.get_overlay_state_modifier()]" : organ_tag
update_limb_icon_file()
- if(icon_state != organ_tag)
- icon_state = organ_tag
+ if(icon_state != next_state)
+ icon_state = next_state
_icon_cache_key = jointext(get_icon_cache_key_components(), null)
var/icon/mob_icon = global.organ_icon_cache[_icon_cache_key] || generate_mob_icon()
diff --git a/code/modules/organs/external/head.dm b/code/modules/organs/external/head.dm
index fb9684bd260..f2fe70f3fc7 100644
--- a/code/modules/organs/external/head.dm
+++ b/code/modules/organs/external/head.dm
@@ -102,9 +102,10 @@
/obj/item/organ/external/head/generate_mob_icon()
var/icon/ret = ..()
- var/icon/eyes_icon = get_eyes_organ()?.get_onhead_icon()
- if(eyes_icon)
- ret.Blend(eyes_icon, ICON_OVERLAY)
+ if(ret)
+ var/icon/eyes_icon = get_eyes_organ()?.get_onhead_icon()
+ if(eyes_icon)
+ ret.Blend(eyes_icon, ICON_OVERLAY)
return ret
/obj/item/organ/external/head/get_mob_overlays()
@@ -112,3 +113,12 @@
var/image/eye_glow = get_organ_eyes_overlay()
if(eye_glow)
LAZYADD(., eye_glow)
+
+/obj/item/organ/external/head/gripper/do_install(mob/living/carbon/human/target, affected, in_place, update_icon, detached)
+ . = ..()
+ if(. && owner)
+ owner.add_held_item_slot(new /datum/inventory_slot/gripper/mouth)
+
+/obj/item/organ/external/head/gripper/do_uninstall(in_place, detach, ignore_children, update_icon)
+ owner?.remove_held_item_slot(BP_MOUTH)
+ . = ..()
diff --git a/code/modules/organs/external/quadruped.dm b/code/modules/organs/external/quadruped.dm
new file mode 100644
index 00000000000..1411f8e91e1
--- /dev/null
+++ b/code/modules/organs/external/quadruped.dm
@@ -0,0 +1,51 @@
+/obj/item/organ/external/leg/quadruped
+ name = "left hindleg"
+ joint = "rear left knee"
+
+/obj/item/organ/external/leg/right/quadruped
+ name = "right hindleg"
+ joint = "rear right knee"
+
+/obj/item/organ/external/foot/quadruped
+ name = "left hindpaw"
+ joint = "rear left ankle"
+
+/obj/item/organ/external/foot/right/quadruped
+ name = "right hindpaw"
+ joint = "rear right ankle"
+
+/obj/item/organ/external/arm/quadruped
+ name = "left foreleg"
+ joint = "front left knee"
+ amputation_point = "front left knee"
+ tendon_name = "cruciate ligament"
+ artery_name = "femoral artery"
+ organ_category = ORGAN_CATEGORY_STANCE_ROOT
+ limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE
+
+/obj/item/organ/external/arm/right/quadruped
+ name = "right foreleg"
+ joint = "front right knee"
+ amputation_point = "front right knee"
+ tendon_name = "cruciate ligament"
+ artery_name = "femoral artery"
+ organ_category = ORGAN_CATEGORY_STANCE_ROOT
+ limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE
+
+/obj/item/organ/external/hand/quadruped
+ name = "left forepaw"
+ joint = "front left ankle"
+ amputation_point = "front left ankle"
+ tendon_name = "Achilles tendon"
+ limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE
+ organ_category = ORGAN_CATEGORY_STANCE
+ gripper_type = null
+
+/obj/item/organ/external/hand/right/quadruped
+ name = "right forepaw"
+ joint = "front right ankle"
+ amputation_point = "front right ankle"
+ tendon_name = "Achilles tendon"
+ limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE
+ organ_category = ORGAN_CATEGORY_STANCE
+ gripper_type = null
diff --git a/code/modules/organs/external/standard.dm b/code/modules/organs/external/standard.dm
index e86b0ea97a7..8579c7690da 100644
--- a/code/modules/organs/external/standard.dm
+++ b/code/modules/organs/external/standard.dm
@@ -92,6 +92,7 @@
tendon_name = "cruciate ligament"
artery_name = "femoral artery"
arterial_bleed_severity = 0.75
+ organ_category = ORGAN_CATEGORY_STANCE_ROOT
limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE
/obj/item/organ/external/leg/right
@@ -116,6 +117,7 @@
tendon_name = "Achilles tendon"
arterial_bleed_severity = 0.5
limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE
+ organ_category = ORGAN_CATEGORY_STANCE
/obj/item/organ/external/foot/right
organ_tag = BP_R_FOOT
@@ -148,7 +150,7 @@
owner.add_held_item_slot(new gripper_type)
/obj/item/organ/external/hand/do_uninstall(in_place, detach, ignore_children, update_icon)
- if(!in_place)
+ if(gripper_type)
owner?.remove_held_item_slot(organ_tag)
. = ..()
diff --git a/code/modules/organs/external/tail.dm b/code/modules/organs/external/tail.dm
index fc711d6b8c3..55fff675056 100644
--- a/code/modules/organs/external/tail.dm
+++ b/code/modules/organs/external/tail.dm
@@ -41,7 +41,8 @@
owner.update_tail_showing(FALSE)
/obj/item/organ/external/tail/proc/get_tail()
- return tail
+ var/modifier = owner?.get_overlay_state_modifier()
+ . = modifier ? "[tail][modifier]" : tail
/obj/item/organ/external/tail/proc/get_tail_icon()
return tail_icon
diff --git a/code/modules/organs/internal/eyes.dm b/code/modules/organs/internal/eyes.dm
index bdb11a3dd1f..ee53e0c9666 100644
--- a/code/modules/organs/internal/eyes.dm
+++ b/code/modules/organs/internal/eyes.dm
@@ -37,12 +37,14 @@
verbs |= /obj/item/organ/internal/eyes/proc/toggle_eye_glow
/obj/item/organ/internal/eyes/proc/get_onhead_icon()
+ var/modifier = owner?.get_overlay_state_modifier()
+ var/eye_state = modifier ? "eyes[modifier]" : "eyes"
+ last_eye_cache_key = "[type]-[bodytype.eye_icon]-[last_cached_eye_colour]-[bodytype.eye_offset]-[eye_state]"
last_cached_eye_colour = eye_colour
- last_eye_cache_key = "[type]-[bodytype.eye_icon]-[last_cached_eye_colour]-[bodytype.eye_offset]"
if(!bodytype.eye_icon)
return
if(!global.eye_icon_cache[last_eye_cache_key])
- var/icon/eyes_icon = icon(icon = bodytype.eye_icon, icon_state = "")
+ var/icon/eyes_icon = icon(icon = bodytype.eye_icon, icon_state = eye_state)
if(bodytype.eye_offset)
eyes_icon.Shift(NORTH, bodytype.eye_offset)
if(bodytype.apply_eye_colour)
diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm
index c52d846c819..589b252a7a8 100644
--- a/code/modules/organs/organ.dm
+++ b/code/modules/organs/organ.dm
@@ -10,6 +10,7 @@
// Strings.
var/organ_tag = "organ" // Unique identifier.
+ var/organ_category // Identifier for use in organ collections, unused if unset. Would be nice to make this a list, but bodytypes rely on initial() with it.
var/parent_organ = BP_CHEST // Organ holding this object.
// Status tracking.
diff --git a/code/modules/projectiles/targeting/targeting_mob.dm b/code/modules/projectiles/targeting/targeting_mob.dm
index 1dd13a1aa6d..08cc73e28aa 100644
--- a/code/modules/projectiles/targeting/targeting_mob.dm
+++ b/code/modules/projectiles/targeting/targeting_mob.dm
@@ -24,6 +24,10 @@
aiming.cancel_aiming(no_message)
/mob/living/UpdateLyingBuckledAndVerbStatus()
+ var/old_lying = lying
+ var/old_buckled = buckled
..()
if(lying)
stop_aiming(no_message=1)
+ else if(old_buckled && !buckled && old_lying)
+ handle_stance() // Force an immediate stance update.
diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm
index 9569352c12d..7b4b2c03e64 100644
--- a/code/modules/species/species.dm
+++ b/code/modules/species/species.dm
@@ -68,7 +68,8 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200
var/age_descriptor = /datum/appearance_descriptor/age
// Speech vars.
- var/assisted_langs = list() // The languages the species can't speak without an assisted organ.
+ var/assisted_langs = list() // The languages the species can't speak without an assisted organ.
+ var/unspeakable_langs = list() // The languages the species can't speak at all.
var/list/speech_sounds // A list of sounds to potentially play when speaking.
var/list/speech_chance // The likelihood of a speech sound playing.
diff --git a/code/modules/species/species_bodytype.dm b/code/modules/species/species_bodytype.dm
index 515795bcc81..206e119561f 100644
--- a/code/modules/species/species_bodytype.dm
+++ b/code/modules/species/species_bodytype.dm
@@ -44,6 +44,10 @@ var/global/list/bodytypes_by_category = list()
var/manual_dexterity = null
/// Determines how the limb behaves with regards to manual attachment/detachment.
var/modular_limb_tier = MODULAR_BODYPART_INVALID
+ // Expected organ types per category, used only for stance checking at time of writing.
+ var/list/organs_by_category = list()
+ // Expected organ tags per category, used only for stance checking at time of writing.
+ var/list/organ_tags_by_category = list()
var/list/onmob_state_modifiers
var/health_hud_intensity = 1
@@ -193,6 +197,9 @@ var/global/list/bodytypes_by_category = list()
"Your chilly flesh stands out in goosebumps."
)
+ /// Set to FALSE if the mob will update prone icon based on state rather than transform.
+ var/rotate_on_prone = TRUE
+
/decl/bodytype/Initialize()
. = ..()
icon_deformed ||= icon_base
@@ -222,10 +229,27 @@ var/global/list/bodytypes_by_category = list()
has_limbs[ltag] = list("path" = override_limb_types[ltag])
//Build organ descriptors
- for(var/limb_type in has_limbs)
- var/list/organ_data = has_limbs[limb_type]
- var/obj/item/organ/limb_path = organ_data["path"]
- organ_data["descriptor"] = initial(limb_path.name)
+ for(var/organ_tag in has_limbs)
+ var/list/organ_data = has_limbs[organ_tag]
+ var/obj/item/organ/organ = organ_data["path"]
+ organ_data["descriptor"] = initial(organ.name)
+ var/organ_cat = initial(organ.organ_category)
+ if(organ_cat)
+ LAZYADD(organs_by_category[organ_cat], organ)
+ LAZYADD(organ_tags_by_category[organ_cat], organ_tag)
+
+ for(var/organ_tag in has_organ)
+ var/obj/item/organ/organ = has_organ[organ_tag]
+ var/organ_cat = initial(organ.organ_category)
+ if(organ_cat)
+ LAZYADD(organs_by_category[organ_cat], organ)
+ LAZYADD(organ_tags_by_category[organ_cat], organ_tag)
+
+/decl/bodytype/proc/get_expected_organ_count_for_categories(var/list/categories)
+ . = 0
+ for(var/category in categories)
+ if(category && (category in organs_by_category))
+ . += length(organs_by_category[category])
/decl/bodytype/proc/apply_limb_colouration(var/obj/item/organ/external/E, var/icon/applying)
return applying
diff --git a/code/modules/species/species_bodytype_offsets.dm b/code/modules/species/species_bodytype_offsets.dm
index 2a7505551a7..9e599406f47 100644
--- a/code/modules/species/species_bodytype_offsets.dm
+++ b/code/modules/species/species_bodytype_offsets.dm
@@ -21,16 +21,21 @@ The slots that you can use are found in items_clothing.dm and are the inventory
var/list/equip_adjust = list()
var/list/equip_overlays = list()
-/decl/bodytype/proc/get_offset_overlay_image(var/mob_icon, var/mob_state, var/color, var/slot)
+/decl/bodytype/proc/get_equip_adjust(mob/mob)
+ return equip_adjust
+
+/decl/bodytype/proc/get_offset_overlay_image(mob/mob, mob_icon, mob_state, color, slot)
// If we don't actually need to offset this, don't bother with any of the generation/caching.
- if(length(equip_adjust) && equip_adjust[slot] && length(equip_adjust[slot]))
+ var/list/use_equip_adjust = get_equip_adjust(mob)
+ if(length(use_equip_adjust) && use_equip_adjust[slot] && length(use_equip_adjust[slot]))
// Check the cache for previously made icons.
- var/image_key = "[mob_icon]-[mob_state]-[color]-[slot]"
+ var/modifier = mob?.get_overlay_state_modifier()
+ var/image_key = modifier ? "[modifier]-[mob_icon]-[mob_state]-[color]-[slot]" : "generic-[mob_icon]-[mob_state]-[color]-[slot]"
if(!equip_overlays[image_key])
var/icon/final_I = new(icon_template)
- var/list/shifts = equip_adjust[slot]
+ var/list/shifts = use_equip_adjust[slot]
// Apply all pixel shifts for each direction.
for(var/shift_facing in shifts)
diff --git a/code/modules/species/species_bodytype_quadruped.dm b/code/modules/species/species_bodytype_quadruped.dm
new file mode 100644
index 00000000000..c703ef0991a
--- /dev/null
+++ b/code/modules/species/species_bodytype_quadruped.dm
@@ -0,0 +1,18 @@
+/decl/bodytype/quadruped
+ abstract_type = /decl/bodytype/quadruped
+ rotate_on_prone = FALSE
+ bodytype_category = BODYTYPE_QUADRUPED
+ bodytype_flag = BODY_FLAG_QUADRUPED
+ has_limbs = list(
+ BP_CHEST = list("path" = /obj/item/organ/external/chest),
+ BP_GROIN = list("path" = /obj/item/organ/external/groin),
+ BP_HEAD = list("path" = /obj/item/organ/external/head/gripper),
+ BP_L_ARM = list("path" = /obj/item/organ/external/arm/quadruped),
+ BP_R_ARM = list("path" = /obj/item/organ/external/arm/right/quadruped),
+ BP_L_LEG = list("path" = /obj/item/organ/external/leg/quadruped),
+ BP_R_LEG = list("path" = /obj/item/organ/external/leg/right/quadruped),
+ BP_L_HAND = list("path" = /obj/item/organ/external/hand/quadruped),
+ BP_R_HAND = list("path" = /obj/item/organ/external/hand/right/quadruped),
+ BP_L_FOOT = list("path" = /obj/item/organ/external/foot/quadruped),
+ BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right/quadruped)
+ )
diff --git a/code/modules/sprite_accessories/_accessory.dm b/code/modules/sprite_accessories/_accessory.dm
index b7aeafd58ad..34f3802ee8a 100644
--- a/code/modules/sprite_accessories/_accessory.dm
+++ b/code/modules/sprite_accessories/_accessory.dm
@@ -142,16 +142,21 @@
if(!icon_state)
return null
LAZYINITLIST(cached_icons[organ.bodytype])
- LAZYINITLIST(cached_icons[organ.bodytype][organ.organ_tag])
- var/icon/accessory_icon = cached_icons[organ.bodytype][organ.organ_tag][color]
+ LAZYINITLIST(cached_icons[organ.bodytype][organ.icon_state])
+ var/icon/accessory_icon = cached_icons[organ.bodytype][organ.icon_state][color]
if(!accessory_icon)
- accessory_icon = icon(get_accessory_icon(organ), icon_state) // make a new one to avoid mutating the base
+ // make a new one to avoid mutating the base
+ var/marking_modifier = organ.owner?.get_overlay_state_modifier()
+ if(marking_modifier)
+ accessory_icon = icon(get_accessory_icon(organ), "[icon_state][marking_modifier]")
+ else
+ accessory_icon = icon(get_accessory_icon(organ), icon_state)
if(!accessory_icon)
- cached_icons[organ.bodytype][organ.organ_tag][color] = null
+ cached_icons[organ.bodytype][organ.icon_state][color] = null
return null
if(mask_to_bodypart)
accessory_icon.Blend(get_limb_mask_for(organ), ICON_MULTIPLY)
if(!isnull(color) && !isnull(color_blend))
accessory_icon.Blend(color, color_blend)
- cached_icons[organ.bodytype][organ.organ_tag][color] = accessory_icon
+ cached_icons[organ.bodytype][organ.icon_state][color] = accessory_icon
return accessory_icon
diff --git a/mods/content/corporate/clothing/suit/captain.dm b/mods/content/corporate/clothing/suit/captain.dm
index ca9e02c84c2..55150d96b98 100644
--- a/mods/content/corporate/clothing/suit/captain.dm
+++ b/mods/content/corporate/clothing/suit/captain.dm
@@ -27,4 +27,3 @@
/obj/item/clothing/suit/armor/captain/Initialize()
. = ..()
LAZYSET(slowdown_per_slot, slot_wear_suit_str, 1.5)
-
diff --git a/mods/species/ascent/mobs/nymph/_nymph.dm b/mods/species/ascent/mobs/nymph/_nymph.dm
index 8bcd4c410bf..da48aabf82e 100644
--- a/mods/species/ascent/mobs/nymph/_nymph.dm
+++ b/mods/species/ascent/mobs/nymph/_nymph.dm
@@ -50,4 +50,4 @@
set_extension(src, /datum/extension/base_icon_state, icon_state)
/mob/living/simple_animal/alien/kharmaan/get_dexterity(var/silent)
- return (DEXTERITY_EQUIP_ITEM|DEXTERITY_HOLD_ITEM)
+ return (DEXTERITY_EQUIP_ITEM)
diff --git a/mods/species/bayliens/tajaran/_tajaran.dm b/mods/species/bayliens/tajaran/_tajaran.dm
index 78e6c821d4b..6024c4bca0d 100644
--- a/mods/species/bayliens/tajaran/_tajaran.dm
+++ b/mods/species/bayliens/tajaran/_tajaran.dm
@@ -3,7 +3,7 @@
#define BODYTYPE_FELINE "feline body"
#define BODY_FLAG_FELINE BITFLAG(7)
-/obj/item/clothing/Initialize()
+/obj/item/clothing/setup_equip_flags()
. = ..()
if(bodytype_equip_flags & BODY_FLAG_EXCLUDE)
bodytype_equip_flags |= BODY_FLAG_FELINE
diff --git a/mods/species/bayliens/tajaran/machinery/suit_cycler.dm b/mods/species/bayliens/tajaran/machinery/suit_cycler.dm
index 58eb45c24c7..af8842c7b19 100644
--- a/mods/species/bayliens/tajaran/machinery/suit_cycler.dm
+++ b/mods/species/bayliens/tajaran/machinery/suit_cycler.dm
@@ -2,70 +2,70 @@
LAZYDISTINCTADD(available_bodytypes, BODYTYPE_FELINE)
. = ..()
-/obj/item/clothing/suit/space/void/merc/Initialize()
+/obj/item/clothing/suit/space/void/merc/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/merc/suit.dmi')
-/obj/item/clothing/suit/space/void/swat/Initialize()
+/obj/item/clothing/suit/space/void/swat/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/deathsquad/suit.dmi')
-/obj/item/clothing/suit/space/void/engineering/Initialize()
+/obj/item/clothing/suit/space/void/engineering/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/engineering/suit.dmi')
-/obj/item/clothing/suit/space/void/mining/Initialize()
+/obj/item/clothing/suit/space/void/mining/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/mining/suit.dmi')
-/obj/item/clothing/suit/space/void/medical/Initialize()
+/obj/item/clothing/suit/space/void/medical/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/medical/suit.dmi')
-/obj/item/clothing/suit/space/void/security/Initialize()
+/obj/item/clothing/suit/space/void/security/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/sec/suit.dmi')
-/obj/item/clothing/suit/space/void/atmos/Initialize()
+/obj/item/clothing/suit/space/void/atmos/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/atmos/suit.dmi')
-/obj/item/clothing/suit/space/void/engineering/alt/Initialize()
+/obj/item/clothing/suit/space/void/engineering/alt/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/engineering_alt/suit.dmi')
-/obj/item/clothing/suit/space/void/mining/alt/Initialize()
+/obj/item/clothing/suit/space/void/mining/alt/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/mining_alt/suit.dmi')
-/obj/item/clothing/suit/space/void/medical/alt/Initialize()
+/obj/item/clothing/suit/space/void/medical/alt/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/medical_alt/suit.dmi')
-/obj/item/clothing/suit/space/void/security/alt/Initialize()
+/obj/item/clothing/suit/space/void/security/alt/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/sec_alt/suit.dmi')
-/obj/item/clothing/suit/space/void/atmos/alt/Initialize()
+/obj/item/clothing/suit/space/void/atmos/alt/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/atmos_alt/suit.dmi')
-/obj/item/clothing/suit/space/void/engineering/salvage/Initialize()
+/obj/item/clothing/suit/space/void/engineering/salvage/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/salvage/suit.dmi')
-/obj/item/clothing/suit/space/void/expedition/Initialize()
+/obj/item/clothing/suit/space/void/expedition/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/pilot/suit.dmi')
-/obj/item/clothing/suit/space/void/Initialize()
+/obj/item/clothing/suit/space/void/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/nasa/suit.dmi')
-/obj/item/clothing/suit/space/void/wizard/Initialize()
+/obj/item/clothing/suit/space/void/wizard/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/wizard/suit.dmi')
-/obj/item/clothing/suit/space/void/excavation/Initialize()
+/obj/item/clothing/suit/space/void/excavation/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_FELINE, 'mods/species/bayliens/tajaran/icons/clothing/excavation/suit.dmi')
diff --git a/mods/species/neoavians/clothing.dm b/mods/species/neoavians/clothing.dm
index 4d8c7d09df5..4ad16cfebf4 100644
--- a/mods/species/neoavians/clothing.dm
+++ b/mods/species/neoavians/clothing.dm
@@ -1,21 +1,20 @@
-
-
//Shoes
-
-/obj/item/clothing/shoes/magboots/Initialize()
+/obj/item/clothing/shoes/magboots/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/feet/magboots.dmi')
-/obj/item/clothing/shoes/galoshes/Initialize()
+/obj/item/clothing/shoes/galoshes/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/feet/galoshes.dmi')
//Gloves
-
-/obj/item/clothing/gloves/Initialize()
+/obj/item/clothing/gloves/setup_equip_flags()
. = ..()
if(!isnull(bodytype_equip_flags) && !(bodytype_equip_flags & BODY_FLAG_EXCLUDE))
bodytype_equip_flags |= BODY_FLAG_AVIAN
+
+/obj/item/clothing/gloves/setup_sprite_sheets()
+ . = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/gloves.dmi')
//Backpacks & tanks
@@ -26,20 +25,20 @@
//Radsuits (theyre essential?)
-/obj/item/clothing/head/radiation/Initialize()
+/obj/item/clothing/head/radiation/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/head/rad_helm.dmi')
-/obj/item/clothing/suit/radiation/Initialize()
+/obj/item/clothing/suit/radiation/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/suit/rad_suit.dmi')
//cloaks
-/obj/item/clothing/suit/cloak/Initialize()
+/obj/item/clothing/suit/cloak/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/accessory/cloak.dmi')
-/obj/item/clothing/suit/cloak/hide/Initialize()
+/obj/item/clothing/suit/cloak/hide/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/accessory/cloak_hide.dmi')
diff --git a/mods/species/neoavians/machinery/suit_cycler.dm b/mods/species/neoavians/machinery/suit_cycler.dm
index 5fa82cf3034..9b368c30577 100644
--- a/mods/species/neoavians/machinery/suit_cycler.dm
+++ b/mods/species/neoavians/machinery/suit_cycler.dm
@@ -4,86 +4,86 @@
//mining
-/obj/item/clothing/suit/space/void/mining/Initialize()
+/obj/item/clothing/suit/space/void/mining/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/suit.dmi')
-/obj/item/clothing/head/helmet/space/void/mining/Initialize()
+/obj/item/clothing/head/helmet/space/void/mining/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/helmet.dmi')
//excavation
-/obj/item/clothing/suit/space/void/excavation/Initialize()
+/obj/item/clothing/suit/space/void/excavation/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/suit.dmi')
-/obj/item/clothing/head/helmet/space/void/excavation/Initialize()
+/obj/item/clothing/head/helmet/space/void/excavation/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/mining/helmet.dmi')
//engineering
-/obj/item/clothing/head/helmet/space/void/engineering/Initialize()
+/obj/item/clothing/head/helmet/space/void/engineering/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/engineering/helmet.dmi')
-/obj/item/clothing/suit/space/void/engineering/Initialize()
+/obj/item/clothing/suit/space/void/engineering/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/engineering/suit.dmi')
-/obj/item/clothing/head/helmet/space/void/atmos/Initialize()
+/obj/item/clothing/head/helmet/space/void/atmos/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/atmos/helmet.dmi')
-/obj/item/clothing/suit/space/void/atmos/Initialize()
+/obj/item/clothing/suit/space/void/atmos/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/atmos/suit.dmi')
//medical
-/obj/item/clothing/suit/space/void/medical/Initialize()
+/obj/item/clothing/suit/space/void/medical/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/medical/suit.dmi')
-/obj/item/clothing/head/helmet/space/void/medical/Initialize()
+/obj/item/clothing/head/helmet/space/void/medical/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/medical/helmet.dmi')
//security
-/obj/item/clothing/head/helmet/space/void/security/Initialize()
+/obj/item/clothing/head/helmet/space/void/security/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/sec/helmet.dmi')
-/obj/item/clothing/suit/space/void/security/Initialize()
+/obj/item/clothing/suit/space/void/security/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/sec/suit.dmi')
//salvage
-/obj/item/clothing/head/helmet/space/void/engineering/salvage/Initialize()
+/obj/item/clothing/head/helmet/space/void/engineering/salvage/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/salvage/helmet.dmi')
-/obj/item/clothing/suit/space/void/engineering/salvage/Initialize()
+/obj/item/clothing/suit/space/void/engineering/salvage/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/salvage/suit.dmi')
//pilot
-/obj/item/clothing/head/helmet/space/void/expedition/Initialize()
+/obj/item/clothing/head/helmet/space/void/expedition/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/pilot/helmet.dmi')
-/obj/item/clothing/suit/space/void/expedition/Initialize()
+/obj/item/clothing/suit/space/void/expedition/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/pilot/suit.dmi')
//merc
-/obj/item/clothing/head/helmet/space/void/merc/Initialize()
+/obj/item/clothing/head/helmet/space/void/merc/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/merc/helmet.dmi')
-/obj/item/clothing/suit/space/void/merc/Initialize()
+/obj/item/clothing/suit/space/void/merc/setup_sprite_sheets()
. = ..()
LAZYSET(sprite_sheets, BODYTYPE_AVIAN, 'mods/species/neoavians/icons/clothing/spacesuit/void/merc/suit.dmi')
\ No newline at end of file
diff --git a/mods/species/vox/gear/gear_head.dm b/mods/species/vox/gear/gear_head.dm
index 24d6676478f..6d96034223a 100644
--- a/mods/species/vox/gear/gear_head.dm
+++ b/mods/species/vox/gear/gear_head.dm
@@ -1,4 +1,4 @@
-/obj/item/clothing/head/helmet/space/void/Initialize()
+/obj/item/clothing/head/helmet/space/void/setup_equip_flags()
. = ..()
if(bodytype_equip_flags & BODY_FLAG_EXCLUDE)
bodytype_equip_flags |= BODY_FLAG_VOX
diff --git a/mods/species/vox/gear/gear_suit.dm b/mods/species/vox/gear/gear_suit.dm
index 99856c36fad..9760a804b45 100644
--- a/mods/species/vox/gear/gear_suit.dm
+++ b/mods/species/vox/gear/gear_suit.dm
@@ -1,4 +1,4 @@
-/obj/item/clothing/suit/space/void/Initialize()
+/obj/item/clothing/suit/space/void/setup_equip_flags()
. = ..()
if(bodytype_equip_flags & BODY_FLAG_EXCLUDE)
bodytype_equip_flags |= BODY_FLAG_VOX
diff --git a/nebula.dme b/nebula.dme
index 5ad67597f52..991c94141e2 100644
--- a/nebula.dme
+++ b/nebula.dme
@@ -3156,6 +3156,7 @@
#include "code\modules\organs\external\diagnostics.dm"
#include "code\modules\organs\external\head.dm"
#include "code\modules\organs\external\insectoid.dm"
+#include "code\modules\organs\external\quadruped.dm"
#include "code\modules\organs\external\standard.dm"
#include "code\modules\organs\external\tail.dm"
#include "code\modules\organs\external\unbreakable.dm"
@@ -3579,6 +3580,7 @@
#include "code\modules\species\species_bodytype.dm"
#include "code\modules\species\species_bodytype_helpers.dm"
#include "code\modules\species\species_bodytype_offsets.dm"
+#include "code\modules\species\species_bodytype_quadruped.dm"
#include "code\modules\species\species_bodytype_random.dm"
#include "code\modules\species\species_crystalline_bodytypes.dm"
#include "code\modules\species\species_getters.dm"