Merge branch 'master' of https://github.com/tgstation/tgstation into upstream-23-10-2025

This commit is contained in:
Roxy
2025-10-23 17:38:23 -04:00
453 changed files with 20635 additions and 15897 deletions
+57 -31
View File
@@ -183,14 +183,22 @@
/// Sounds when this bodypart is used in an umarmed attack
var/sound/unarmed_attack_sound = 'sound/items/weapons/punch1.ogg'
var/sound/unarmed_miss_sound = 'sound/items/weapons/punchmiss.ogg'
///Lowest possible punch damage this bodypart can give. If this is set to 0, unarmed attacks will always miss.
/// Lowest possible punch damage this bodypart can give. If this is set to 0, unarmed attacks will always miss.
var/unarmed_damage_low = 1
///Highest possible punch damage this bodypart can ive.
/// Highest possible punch damage this bodypart can ive.
var/unarmed_damage_high = 1
///Determines the accuracy bonus, armor penetration and knockdown probability.
/// Determines the accuracy bonus, armor penetration and knockdown probability.
var/unarmed_effectiveness = 10
/// Multiplier applied to effectiveness and damage when attacking a grabbed target.
var/unarmed_pummeling_bonus = 1
/// If this limb is used to grab (which is only arms right now), how much more effective is the limb? Every integer above or below 0 is one effective grab level. Higher is a bonus, lower is a malus.
/// This is more powerful than adjusting grab escape chance. If the arm has both an increased grab level and a chance reduction, it is very hard to escape a grab.
var/unarmed_grab_state_bonus = 0
/// If this limb is used to grab (which is only arms right now), how much additional damage does it deal to the grabbed individual when they fail to escape the grab?
var/unarmed_grab_damage_bonus = 0
/// If this limb is used to grab (which is only arms right now), how much more difficult is it to escape the grab, before accounting for grab state? Values above 0 is a malus, values below 0 is a bonus.
/// This is less powerful than adjusting grab state. If the arm has both an increased grab level and a chance reduction, it is very hard to escape a grab.
var/unarmed_grab_escape_chance_bonus = 0
/// The 'sharpness' of the limb. Could indicate claws, teeth or spines. Should default to NONE, or blunt.
var/unarmed_sharpness = NONE
@@ -1129,42 +1137,43 @@
if(dropped && dmg_overlay_type)
if(brutestate)
// divided into two overlays: one that gets colored and one that doesn't.
var/image/brute_blood_overlay = image('icons/mob/effects/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_[brutestate]0", -DAMAGE_LAYER)
var/image/brute_blood_overlay = image('icons/mob/effects/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_[brutestate]0", -DAMAGE_LAYER, dir = SOUTH)
brute_blood_overlay.color = get_color_from_blood_list(update_on ? update_on.get_blood_dna_list() : blood_dna_info) // living mobs can just get it fresh, dropped limbs use blood_dna_info
var/mutable_appearance/brute_damage_overlay = mutable_appearance('icons/mob/effects/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_[brutestate]0_overlay", -DAMAGE_LAYER, appearance_flags = RESET_COLOR)
if(brute_damage_overlay)
brute_blood_overlay.overlays += brute_damage_overlay
. += brute_blood_overlay
if(burnstate)
. += image('icons/mob/effects/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_0[burnstate]", -DAMAGE_LAYER)
. += image('icons/mob/effects/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_0[burnstate]", -DAMAGE_LAYER, dir = SOUTH)
var/image/limb = image(layer = -BODYPARTS_LAYER)
var/image/aux
var/image_dir = null
if (dropped)
image_dir = SOUTH
// Handles invisibility (not alpha or actual invisibility but invisibility)
if(is_invisible)
limb.icon = icon_invisible
limb.icon_state = "invisible_[body_zone]"
. += limb
. += image(icon_invisible, "invisible_[body_zone]", -BODYPARTS_LAYER, dir = image_dir)
return .
// Normal non-husk handling
// This is the MEAT of limb icon code
limb.icon = icon_greyscale
// This is the MEAT of limb icon code
var/used_icon = icon_greyscale
if(!should_draw_greyscale || !icon_greyscale)
limb.icon = icon_static
used_icon = icon_static
if(is_dimorphic) //Does this type of limb have sexual dimorphism?
limb.icon_state = "[limb_id]_[body_zone]_[limb_gender]"
else
limb.icon_state = "[limb_id]_[body_zone]"
var/used_state = "[limb_id]_[body_zone]"
if(is_dimorphic) // Does this type of limb have sexual dimorphism?
used_state = "[limb_id]_[body_zone]_[limb_gender]"
var/image/limb = image(used_icon, used_state, -BODYPARTS_LAYER, dir = image_dir)
var/image/aux = null
icon_exists_or_scream(limb.icon, limb.icon_state) //Prints a stack trace on the first failure of a given iconstate.
. += limb
if(aux_zone) //Hand shit
aux = image(limb.icon, "[limb_id]_[aux_zone]", -aux_layer)
aux = image(limb.icon, "[limb_id]_[aux_zone]", -aux_layer, dir = image_dir)
. += aux
if(is_husked)
@@ -1181,50 +1190,67 @@
if(aux_zone)
aux.color = limb_color // SKYRAT EDIT CHANGE - ORIGINAL: aux.color = "[draw_color]"
//EMISSIVE CODE START
// For some reason this was applied as an overlay on the aux image and limb image before.
// I am very sure that this is unnecessary, and i need to treat it as part of the return list
// to be able to mask it proper in case this limb is a leg.
var/atom/location = loc || owner || src
if(blocks_emissive != EMISSIVE_BLOCK_NONE)
var/mutable_appearance/limb_em_block = emissive_blocker(limb.icon, limb.icon_state, location, layer = limb.layer, alpha = limb.alpha)
if (dropped)
limb_em_block = image(limb_em_block, dir = SOUTH)
. += limb_em_block
if(aux_zone)
var/mutable_appearance/aux_em_block = emissive_blocker(aux.icon, aux.icon_state, location, layer = aux.layer, alpha = aux.alpha)
if (dropped)
aux_em_block = image(aux_em_block, dir = SOUTH)
. += aux_em_block
if(!is_husked && is_emissive)
var/mutable_appearance/limb_em = emissive_appearance(limb.icon, "[limb.icon_state]_e", location, layer = limb.layer, alpha = limb.alpha)
if (dropped)
limb_em = image(limb_em, dir = SOUTH)
. += limb_em
if(aux_zone)
var/mutable_appearance/aux_em = emissive_appearance(aux.icon, "[aux.icon_state]_e", location, layer = aux.layer, alpha = aux.alpha)
if (dropped)
aux_em = image(aux_em, dir = SOUTH)
. += aux_em
//EMISSIVE CODE END
//No need to handle leg layering if dropped, we only face south anyways
// No need to handle leg layering if dropped, we only face south anyways
if(!dropped && ((body_zone == BODY_ZONE_R_LEG) || (body_zone == BODY_ZONE_L_LEG)))
//Legs are a bit goofy in regards to layering, and we will need two images instead of one to fix that
// Legs are a bit goofy in regards to layering, and we will need two images instead of one to fix that
var/obj/item/bodypart/leg/leg_source = src
for(var/image/limb_image in .)
//remove the old, unmasked image
// Remove the old, unmasked image
. -= limb_image
//add two masked images based on the old one
// Add two masked images based on the old one
. += leg_source.generate_masked_leg(limb_image)
// And finally put bodypart_overlays on if not husked
if(is_husked)
return .
//Draw external organs like horns and frills
// Draw external organs like horns and frills
for(var/datum/bodypart_overlay/overlay as anything in bodypart_overlays)
if(!overlay.can_draw_on_bodypart(src, owner))
continue
//Some externals have multiple layers for background, foreground and between
// Some externals have multiple layers for background, foreground and between
for(var/external_layer in overlay.all_layers)
if(overlay.layers & external_layer)
. += overlay.get_overlay(external_layer, src)
if(!(overlay.layers & external_layer))
continue
var/external_overlay = overlay.get_overlay(external_layer, src)
if (!dropped)
. += external_overlay
continue
if (!islist(external_overlay))
. += image(external_overlay, dir = SOUTH)
continue
for (var/mutable_appearance/actual_overlay as anything in external_overlay)
. += image(actual_overlay, dir = SOUTH)
for(var/datum/layer in .)
overlay.modify_bodypart_appearance(layer)
// SKYRAT EDIT ADDITION BEGIN - MARKINGS CODE
+32 -42
View File
@@ -173,56 +173,46 @@
/obj/item/bodypart/head/get_limb_icon(dropped, mob/living/carbon/update_on)
. = ..()
. += get_hair_and_lips_icon(dropped)
// We need to get the eyes if we are dropped (ugh)
if(dropped)
var/obj/item/organ/eyes/eyes = locate(/obj/item/organ/eyes) in src
// This is a bit of copy/paste code from eyes.dm:generate_body_overlay
if(eyes?.eye_icon_state && (head_flags & HEAD_EYESPRITES))
var/image/eye_left = image('icons/mob/human/human_face.dmi', "[eyes.eye_icon_state]_l", -EYES_LAYER, SOUTH)
var/image/eye_right = image('icons/mob/human/human_face.dmi', "[eyes.eye_icon_state]_r", -EYES_LAYER, SOUTH)
if(head_flags & HEAD_EYECOLOR)
if(eyes.eye_color_left)
eye_left.color = eyes.eye_color_left
if(eyes.eye_color_right)
eye_right.color = eyes.eye_color_right
if(eyes.overlay_ignore_lighting)
eye_left.overlays += emissive_appearance(eye_left.icon, eye_left.icon_state, src, alpha = eye_left.alpha)
eye_right.overlays += emissive_appearance(eye_right.icon, eye_right.icon_state, src, alpha = eye_right.alpha)
else if(blocks_emissive != EMISSIVE_BLOCK_NONE)
var/atom/location = loc || owner || src
eye_left.overlays += emissive_blocker(eye_left.icon, eye_left.icon_state, location, alpha = eye_left.alpha)
eye_right.overlays += emissive_blocker(eye_right.icon, eye_right.icon_state, location, alpha = eye_right.alpha)
if(worn_face_offset)
worn_face_offset.apply_offset(eye_left)
worn_face_offset.apply_offset(eye_right)
if(!dropped)
return
// SKYRAT EDIT ADDITION START - Customization (Emissives and synths)
if (eyes.is_emissive) // Because it was done all weird up there.
var/mutable_appearance/emissive_left = emissive_appearance(eye_left.icon, eye_left.icon_state, src, -BODY_LAYER, eye_left.alpha)
var/mutable_appearance/emissive_right = emissive_appearance(eye_right.icon, eye_right.icon_state, src, -BODY_LAYER, eye_right.alpha)
var/obj/item/organ/eyes/eyes = locate(/obj/item/organ/eyes) in src
if(!eyes)
if (!(head_flags & HEAD_EYEHOLES))
return
var/image/no_eyes = image('icons/mob/human/human_face.dmi', "eyes_missing", -EYES_LAYER, SOUTH)
worn_face_offset?.apply_offset(no_eyes)
. += no_eyes
return
emissive_left.appearance_flags &= ~RESET_TRANSFORM
emissive_right.appearance_flags &= ~RESET_TRANSFORM
if(!eyes.eye_icon_state || !(head_flags & HEAD_EYESPRITES))
return
if(worn_face_offset)
worn_face_offset.apply_offset(emissive_right)
worn_face_offset.apply_offset(emissive_left)
// This is a bit of copy/paste code from eyes.dm:generate_body_overlay
var/image/eye_left = image('icons/mob/human/human_face.dmi', "[eyes.eye_icon_state]_l", -EYES_LAYER, SOUTH)
var/image/eye_right = image('icons/mob/human/human_face.dmi', "[eyes.eye_icon_state]_r", -EYES_LAYER, SOUTH)
if(head_flags & HEAD_EYECOLOR)
if(eyes.eye_color_left)
eye_left.color = eyes.eye_color_left
if(eyes.eye_color_right)
eye_right.color = eyes.eye_color_right
eye_left.overlays += emissive_left
eye_right.overlays += emissive_right
var/list/emissive_overlays = eyes.get_emissive_overlays(eye_left, eye_right, src)
if(length(emissive_overlays))
eye_left.overlays += image(emissive_overlays[1], dir = SOUTH)
eye_right.overlays += image(emissive_overlays[2], dir = SOUTH)
else if(blocks_emissive != EMISSIVE_BLOCK_NONE)
eye_left.overlays += image(emissive_blocker(eye_left.icon, eye_left.icon_state, src, alpha = eye_left.alpha), dir = SOUTH)
eye_right.overlays += image(emissive_blocker(eye_right.icon, eye_right.icon_state, src, alpha = eye_right.alpha), dir = SOUTH)
// SKYRAT EDIT END
if(worn_face_offset)
worn_face_offset.apply_offset(eye_left)
worn_face_offset.apply_offset(eye_right)
. += eye_left
. += eye_right
else if(!eyes && (head_flags & HEAD_EYEHOLES))
var/image/no_eyes = image('icons/mob/human/human_face.dmi', "eyes_missing", -EYES_LAYER, SOUTH)
worn_face_offset?.apply_offset(no_eyes)
. += no_eyes
return
. += eye_left
. += eye_right
/obj/item/bodypart/head/Initialize(mapload)
. = ..()
@@ -10,7 +10,7 @@
facial_hair_hidden = FALSE
LAZYNULL(hair_masks)
if(human_head_owner)
for(var/obj/item/worn_item in human_head_owner.get_equipped_items())
for(var/obj/item/worn_item in human_head_owner.get_equipped_items(INCLUDE_ABSTRACT))
if(worn_item.hair_mask)
LAZYSET(hair_masks, worn_item.hair_mask, TRUE)
hair_hidden = !!(human_head_owner.obscured_slots & HIDEHAIR)
@@ -62,16 +62,16 @@
. = list()
var/atom/location = loc || owner || src
var/image_dir = null
if (dropped)
image_dir = SOUTH
var/datum/sprite_accessory/sprite_accessory
if(!facial_hair_hidden && lip_style && (head_flags & HEAD_LIPS))
//not a sprite accessory, don't ask
//Overlay
var/image/lip_overlay = image('icons/mob/human/human_face.dmi', "lips_[lip_style]", -BODY_LAYER)
var/image/lip_overlay = image('icons/mob/human/human_face.dmi', "lips_[lip_style]", -BODY_LAYER, dir = image_dir)
lip_overlay.color = lip_color
//Emissive blocker
if(blocks_emissive != EMISSIVE_BLOCK_NONE)
lip_overlay.overlays += emissive_blocker(lip_overlay.icon, lip_overlay.icon_state, location, alpha = facial_hair_alpha)
//Offsets
worn_face_offset?.apply_offset(lip_overlay)
. += lip_overlay
@@ -81,11 +81,14 @@
sprite_accessory = SSaccessories.facial_hairstyles_list[facial_hairstyle]
if(sprite_accessory)
//Overlay
facial_hair_overlay = image(sprite_accessory.icon, sprite_accessory.icon_state, -HAIR_LAYER)
facial_hair_overlay = image(sprite_accessory.icon, sprite_accessory.icon_state, -HAIR_LAYER, dir = image_dir)
facial_hair_overlay.alpha = facial_hair_alpha
//Emissive blocker
if(blocks_emissive != EMISSIVE_BLOCK_NONE)
facial_hair_overlay.overlays += emissive_blocker(facial_hair_overlay.icon, facial_hair_overlay.icon_state, location, alpha = facial_hair_alpha)
var/mutable_appearance/em_block = emissive_blocker(facial_hair_overlay.icon, facial_hair_overlay.icon_state, location, alpha = facial_hair_alpha)
if (dropped)
em_block = image(em_block, dir = SOUTH)
facial_hair_overlay.overlays += em_block
//Offsets
worn_face_offset?.apply_offset(facial_hair_overlay)
. += facial_hair_overlay
@@ -93,7 +96,7 @@
var/facial_hair_gradient_style = gradient_styles[GRADIENT_FACIAL_HAIR_KEY]
if(facial_hair_gradient_style != "None")
var/facial_hair_gradient_color = gradient_colors[GRADIENT_FACIAL_HAIR_KEY]
var/image/facial_hair_gradient_overlay = get_gradient_overlay(icon(sprite_accessory.icon, sprite_accessory.icon_state), -HAIR_LAYER, SSaccessories.facial_hair_gradients_list[facial_hair_gradient_style], facial_hair_gradient_color)
var/image/facial_hair_gradient_overlay = get_gradient_overlay(icon(sprite_accessory.icon, sprite_accessory.icon_state), -HAIR_LAYER, SSaccessories.facial_hair_gradients_list[facial_hair_gradient_style], facial_hair_gradient_color, dropped)
. += facial_hair_gradient_overlay
var/list/all_hair_overlays = list()
@@ -103,7 +106,7 @@
//Hair masks
var/icon/base_icon = icon(hair_sprite_accessory.getCachedIcon(hair_masks))
//Overlay
all_hair_overlays += image(base_icon, layer=-HAIR_LAYER)
all_hair_overlays += image(base_icon, layer=-HAIR_LAYER, dir = image_dir)
//If we have any hair appendages (ponytails, etc.) sticking out on a particular side, we need to add an additional hair layer to go above hats/helmets for the sides they stick out on
if(LAZYLEN(hair_sprite_accessory.hair_appendages_outer))
var/strictly_masked_zones = NONE
@@ -112,7 +115,7 @@
for(var/appendage_icon_state in hair_sprite_accessory.hair_appendages_outer)
var/appendage_zone = hair_sprite_accessory.hair_appendages_outer[appendage_icon_state]
if(!(appendage_zone & strictly_masked_zones)) // if there are no strict masks in this zone
all_hair_overlays += image(hair_sprite_accessory.icon, icon_state=appendage_icon_state, layer=-OUTER_HAIR_LAYER)
all_hair_overlays += image(hair_sprite_accessory.icon, icon_state=appendage_icon_state, layer=-OUTER_HAIR_LAYER, dir = image_dir)
for(var/image/hair_overlay as anything in all_hair_overlays)
hair_overlay.alpha = hair_alpha
hair_overlay.pixel_z = hair_sprite_accessory.y_offset
@@ -123,7 +126,10 @@
// SKYRAT EDIT ADDITION END
//Emissive blocker
if(blocks_emissive != EMISSIVE_BLOCK_NONE)
hair_overlay.overlays += emissive_blocker(hair_overlay.icon, hair_overlay.icon_state, location, alpha = hair_alpha)
var/mutable_appearance/em_block = emissive_blocker(hair_overlay.icon, hair_overlay.icon_state, location, alpha = hair_alpha)
if (dropped)
em_block = image(em_block, dir = SOUTH)
hair_overlay.overlays += em_block
//Offsets
worn_face_offset?.apply_offset(hair_overlay)
. += hair_overlay
@@ -131,7 +137,7 @@
var/hair_gradient_style = gradient_styles[GRADIENT_HAIR_KEY]
if(hair_gradient_style != "None")
var/hair_gradient_color = gradient_colors[GRADIENT_HAIR_KEY]
var/image/hair_gradient_overlay = get_gradient_overlay(base_icon, hair_overlay.layer, SSaccessories.hair_gradients_list[hair_gradient_style], hair_gradient_color)
var/image/hair_gradient_overlay = get_gradient_overlay(base_icon, hair_overlay.layer, SSaccessories.hair_gradients_list[hair_gradient_style], hair_gradient_color, dropped)
hair_gradient_overlay.pixel_z = hair_sprite_accessory.y_offset
// SKYRAT EDIT ADDITION - Species hair offset
if(LAZYFIND(owner?.dna?.species?.offset_features, OFFSET_HAIR))
@@ -141,10 +147,10 @@
. += hair_gradient_overlay
if(show_debrained && (head_flags & HEAD_DEBRAIN))
. += get_debrain_overlay()
. += get_debrain_overlay(dropped)
if(show_eyeless && (head_flags & HEAD_EYEHOLES))
. += get_eyeless_overlay()
. += get_eyeless_overlay(dropped)
//HAIR COLOR START
if(override_hair_color)
@@ -166,7 +172,7 @@
#undef SET_OVERLAY_VALUE
/// Returns an appropriate debrained overlay
/obj/item/bodypart/head/proc/get_debrain_overlay()
/obj/item/bodypart/head/proc/get_debrain_overlay(dropped)
RETURN_TYPE(/image)
var/debrain_icon = 'icons/mob/human/human_face.dmi'
var/debrain_icon_state = "debrained"
@@ -181,21 +187,25 @@
debrain_icon_state = "debrained"
var/image/debrain_overlay = mutable_appearance(debrain_icon, debrain_icon_state, -HAIR_LAYER)
if (dropped)
debrain_overlay = image(debrain_overlay, dir = SOUTH)
worn_face_offset?.apply_offset(debrain_overlay)
return debrain_overlay
/// Returns an appropriate missing eyes overlay
/obj/item/bodypart/head/proc/get_eyeless_overlay()
/obj/item/bodypart/head/proc/get_eyeless_overlay(dropped)
RETURN_TYPE(/image)
var/eyeless_icon = 'icons/mob/human/human_face.dmi'
var/eyeless_icon_state = "eyes_missing"
var/image/eyeless_overlay = mutable_appearance(eyeless_icon, eyeless_icon_state, -HAIR_LAYER)
if (dropped)
eyeless_overlay = image(eyeless_overlay, dir = SOUTH)
worn_face_offset?.apply_offset(eyeless_overlay)
return eyeless_overlay
/// Returns an appropriate hair/facial hair gradient overlay
/obj/item/bodypart/head/proc/get_gradient_overlay(icon/base_icon, layer, datum/sprite_accessory/gradient, grad_color)
/obj/item/bodypart/head/proc/get_gradient_overlay(icon/base_icon, layer, datum/sprite_accessory/gradient, grad_color, dropped)
RETURN_TYPE(/mutable_appearance)
var/mutable_appearance/gradient_overlay = mutable_appearance(layer = layer)
@@ -204,6 +214,8 @@
temp.Blend(temp_hair, ICON_ADD)
gradient_overlay.icon = temp
gradient_overlay.color = grad_color
if (dropped)
gradient_overlay = image(gradient_overlay, dir = SOUTH)
worn_face_offset?.apply_offset(gradient_overlay)
return gradient_overlay
+3 -1
View File
@@ -45,8 +45,10 @@
span_notice("[user] completes the surgery on [target]'s ears."),
)
display_pain(target, "Your head swims, but it seems like you can feel your hearing coming back!")
target_ears.deaf = (20) //deafness works off ticks, so this should work out to about 30-40s
target_ears.set_organ_damage(0)
///makes you temporarily deaf for a duration post-surgery
var/deaf_change = 40 SECONDS - target_ears.temporary_deafness
target_ears.adjust_temporary_deafness(deaf_change)
return ..()
/datum/surgery_step/fix_ears/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+61 -23
View File
@@ -117,7 +117,7 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
/// Add a Trait to an organ that it will give its owner.
/obj/item/organ/proc/add_organ_trait(trait)
LAZYADD(organ_traits, trait)
LAZYOR(organ_traits, trait)
if(isnull(owner))
return
ADD_TRAIT(owner, trait, REF(src))
@@ -251,14 +251,9 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
damage = clamp(damage + damage_amount, 0, maximum)
SEND_SIGNAL(src, COMSIG_ORGAN_ADJUST_DAMAGE, damage_amount, maximum, required_organ_flag)
. = (prev_damage - damage) // return net damage
var/message = check_damage_thresholds(owner)
var/message = check_damage_thresholds()
prev_damage = damage
if(damage >= maxHealth)
organ_flags |= ORGAN_FAILING
else
organ_flags &= ~ORGAN_FAILING
if(message && owner && owner.stat <= SOFT_CRIT)
to_chat(owner, message)
@@ -272,24 +267,66 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
* description: By checking our current damage against our previous damage, we can decide whether we've passed an organ threshold.
* If we have, send the corresponding threshold message to the owner, if such a message exists.
*/
/obj/item/organ/proc/check_damage_thresholds(mob/organ_owner)
/obj/item/organ/proc/check_damage_thresholds()
SHOULD_CALL_PARENT(TRUE)
if(damage == prev_damage)
return
var/delta = damage - prev_damage
var/message = ""
if(delta > 0)
if(damage >= maxHealth)
return now_failing
if(damage > high_threshold && prev_damage <= high_threshold)
return high_threshold_passed
if(damage > low_threshold && prev_damage <= low_threshold)
return low_threshold_passed
else
if(prev_damage > low_threshold && damage <= low_threshold)
return low_threshold_cleared
if(prev_damage > high_threshold && damage <= high_threshold)
return high_threshold_cleared
if(prev_damage == maxHealth)
return now_fixed
on_low_damage_received()
message = low_threshold_passed
if(damage > high_threshold && prev_damage <= high_threshold)
on_high_damage_received()
message = high_threshold_passed
if(damage >= maxHealth)
organ_flags |= ORGAN_FAILING
on_begin_failure()
message = now_failing
return message
if(prev_damage == maxHealth)
organ_flags &= ~ORGAN_FAILING
on_failure_recovery()
message = now_fixed
if(prev_damage > high_threshold && damage <= high_threshold)
on_high_damage_healed()
message = high_threshold_cleared
if(prev_damage > low_threshold && damage <= low_threshold)
on_low_damage_healed()
message = low_threshold_cleared
return message
/**
* Called when the damage surpasses the low damage threshold.
*
* This and other procs like this one merely exist to make it easier to keep a standard on
* damage thresholds for organs. This doesn't mean you cannot make custom thresholds for various stuff,
* and you're more than welcome to improve or refactor any portion of the code around these mechanics
*/
/obj/item/organ/proc/on_low_damage_received()
return
///Called when the damage goes below the low damage threshold
/obj/item/organ/proc/on_low_damage_healed()
return
///Called when the damage surpasses the high damage threshold
/obj/item/organ/proc/on_high_damage_received()
return
///Called when the damage goes below the high damage threshold
/obj/item/organ/proc/on_high_damage_healed()
return
///Called when the organ enters failing stage
/obj/item/organ/proc/on_begin_failure()
return
///Called when the organ recovers from failing stage
/obj/item/organ/proc/on_failure_recovery()
return
//Looking for brains?
//Try code/modules/mob/living/carbon/brain/brain_item.dm
@@ -313,9 +350,9 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
dna.species.regenerate_organs(src, replace_current = FALSE)
set_heartattack(FALSE)
// Ears have aditional vаr "deaf", need to update it too
// Ears have aditional var "deaf", need to update it too
var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS)
ears?.adjustEarDamage(0, -INFINITY) // full heal ears deafness
ears.adjust_temporary_deafness(-INFINITY)
return
@@ -352,7 +389,8 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
if(!ears)
ears = new()
ears.Insert(src)
ears.adjustEarDamage(-INFINITY, -INFINITY) // actually do: set_organ_damage(0) and deaf = 0
ears.set_organ_damage(0)
ears.adjust_temporary_deafness(-INFINITY)
///Organs don't die instantly, and neither should you when you get fucked up
/obj/item/organ/proc/handle_failing_organs(seconds_per_tick)
@@ -438,25 +438,31 @@
)
aug_overlay = "strongarm"
///The amount of damage the implant adds to our unarmed attacks.
var/punch_damage = 5
///Biotypes we apply an additional amount of damage too
var/biotype_bonus_targets = MOB_BEAST | MOB_SPECIAL | MOB_MINING
///Extra damage dealt to our targeted mobs
/// The amount of damage the implant adds to the lower punching force of our arm.
var/lower_punch_damage = 0
/// The amount of damage the implant adds to the upper punching force of our arm.
var/upper_punch_damage = 2
/// The amount of punch effectiveness (AKA accuracy and crit potential) the implant adds to our arm
var/punch_effectiveness_added = 10
/// How much extra damage does our implant allow the implanted while grabbing someone and they are unable to break the grapple?
var/bonus_grab_damage = 20
/// Biotypes we apply an additional amount of damage too
var/biotype_bonus_targets = MOB_SPECIAL | MOB_MINING
/// Extra damage dealt to our targeted mobs
var/biotype_bonus_damage = 20
///IF true, the throw attack will not smash people into walls
/// IF true, the throw attack will not smash people into walls
var/non_harmful_throw = TRUE
///How far away your attack will throw your oponent
/// How far away your attack will throw your oponent
var/attack_throw_range = 1
///Minimum throw power of the attack
/// Minimum throw power of the attack
var/throw_power_min = 1
///Maximum throw power of the attack
/// Maximum throw power of the attack
var/throw_power_max = 4
///How long will the implant malfunction if it is EMP'd
/// How long will the implant malfunction if it is EMP'd
var/emp_base_duration = 9 SECONDS
///How long before we get another slam punch; consider that these usually come in pairs of two
/// How long before we get another slam punch against a human; consider that these usually come in pairs
var/slam_cooldown_duration = 5 SECONDS
///Tracks how soon we can perform another slam attack
/// Tracks how soon we can perform another slam attack
COOLDOWN_DECLARE(slam_cooldown)
/obj/item/organ/cyberimp/arm/strongarm/Initialize(mapload)
@@ -472,6 +478,20 @@
. = ..()
UnregisterSignal(arm_owner, COMSIG_LIVING_EARLY_UNARMED_ATTACK)
/obj/item/organ/cyberimp/arm/strongarm/on_bodypart_insert(obj/item/bodypart/arm)
. = ..()
arm.unarmed_damage_low += lower_punch_damage
arm.unarmed_damage_high += upper_punch_damage
arm.unarmed_effectiveness += punch_effectiveness_added
arm.unarmed_grab_damage_bonus += bonus_grab_damage
/obj/item/organ/cyberimp/arm/strongarm/on_bodypart_remove(obj/item/bodypart/arm)
. = ..()
arm.unarmed_damage_low += lower_punch_damage
arm.unarmed_damage_high += upper_punch_damage
arm.unarmed_effectiveness += punch_effectiveness_added
arm.unarmed_grab_damage_bonus += bonus_grab_damage
/obj/item/organ/cyberimp/arm/strongarm/emp_act(severity)
. = ..()
if((organ_flags & ORGAN_FAILING) || . & EMP_PROTECT_SELF)
@@ -495,13 +515,12 @@
return NONE
if(HAS_TRAIT(source, TRAIT_HULK)) //NO HULK
return NONE
if(!COOLDOWN_FINISHED(src, slam_cooldown))
if(!COOLDOWN_FINISHED(src, slam_cooldown) && ishuman(target))
return NONE
if(!source.can_unarmed_attack())
return COMPONENT_SKIP_ATTACK
var/mob/living/living_target = target
source.changeNext_move(CLICK_CD_MELEE)
var/picked_hit_type = pick("punch", "smash", "pummel", "bash", "slam")
if(organ_flags & ORGAN_FAILING)
@@ -514,22 +533,28 @@
source.Paralyze(1 SECONDS)
return COMPONENT_CANCEL_ATTACK_CHAIN
if(ishuman(target))
var/mob/living/carbon/human/human_target = target
if(human_target.check_block(source, punch_damage, "[source]'s' [picked_hit_type]"))
source.do_attack_animation(target)
playsound(living_target.loc, 'sound/items/weapons/punchmiss.ogg', 25, TRUE, -1)
log_combat(source, target, "attempted to [picked_hit_type]", "muscle implant")
return COMPONENT_CANCEL_ATTACK_CHAIN
var/ground_bounce = FALSE // funny flavor. if you hit someone who's floored you slam them into the ground, breaking tiles
var/turf/target_turf = get_turf(living_target)
/* Damage calculations operate on the same math used in /datum/species/proc/harm():
* Unarmed damage is randomized between an upper and lower value.
* The lower value is determined by taking the damage value from the limb, and then increasing that value based on athletics level (min upper value)
* The upper value is determiend by taking the damage value from the limb, and then seeing if we have the strength trait, providing extra damage.
* The end result is that the more investment into athletics, the more precise the damage is, without necessarily increasing the potential damage.
*/
// Our attacking limb
var/obj/item/bodypart/attacking_bodypart = hand
var/potential_damage = punch_damage
// The upper damage, calculated first as it will be used to cap our potential lower damage.
var/potential_upper_damage = attacking_bodypart.unarmed_damage_high + (HAS_TRAIT(source, TRAIT_STRENGTH) ? 2 : 0)
// The lower damage, which is capped by potential_upper_damage
var/potential_lower_damage = min(attacking_bodypart.unarmed_damage_low + (source.mind?.get_skill_level(/datum/skill/athletics) || 0), potential_upper_damage)
// Finally, we determine the actual damage roll.
var/potential_damage = rand(potential_lower_damage, potential_upper_damage)
// This value is used to determine armour penetration.
var/potential_effectiveness = attacking_bodypart.unarmed_effectiveness
// This is a damage and penetration multiplier if our target is grabbed when we deliver our punch.
var/potential_pummel_bonus = attacking_bodypart.unarmed_pummeling_bonus
potential_damage += rand(attacking_bodypart.unarmed_damage_low, attacking_bodypart.unarmed_damage_high)
if(living_target.pulledby && living_target.pulledby.grab_state >= GRAB_AGGRESSIVE) // get pummeled idiot
potential_damage *= potential_pummel_bonus
@@ -541,21 +566,18 @@
if(biotype_bonus_targets && is_correct_biotype) //If we are punching one of our special biotype targets, increase the damage floor by a factor of two.
potential_damage += biotype_bonus_damage
if(ishuman(target))
var/mob/living/carbon/human/human_target = target
if(human_target.check_block(source, potential_damage, "[source]'s' [picked_hit_type]"))
source.do_attack_animation(target)
playsound(living_target.loc, 'sound/items/weapons/punchmiss.ogg', 25, TRUE, -1)
log_combat(source, target, "attempted to [picked_hit_type]", "muscle implant")
return COMPONENT_CANCEL_ATTACK_CHAIN
source.do_attack_animation(target, ATTACK_EFFECT_SMASH)
playsound(living_target.loc, 'sound/items/weapons/punch1.ogg', 25, TRUE, -1)
var/target_zone = living_target.get_random_valid_zone(source.zone_selected)
var/armor_block = living_target.run_armor_check(target_zone, MELEE, armour_penetration = potential_effectiveness)
living_target.apply_damage(potential_damage * 2, attacking_bodypart.attack_type, target_zone, armor_block)
if(source.body_position != LYING_DOWN) //Throw them if we are standing
var/atom/throw_target = get_edge_target_turf(living_target, source.dir)
living_target.throw_at(throw_target, attack_throw_range, rand(throw_power_min,throw_power_max), source, gentle = non_harmful_throw)
if(ground_bounce)
if(isfloorturf(target_turf))
var/turf/open/floor/crunched = target_turf
crunched.crush() // crunch
// Some mobs gib when killed, so we're logging early. At this point, we're definitely hitting, so...
living_target.visible_message(
span_danger("[source] [picked_hit_type]ed [living_target][ground_bounce ? " into [target_turf]" : ""]!"),
span_userdanger("You're [picked_hit_type]ed by [source][ground_bounce ? " into [target_turf]" : ""]!"),
@@ -568,7 +590,24 @@
log_combat(source, target, "[picked_hit_type]ed", "muscle implant")
COOLDOWN_START(src, slam_cooldown, slam_cooldown_duration)
if(ishuman(target))
COOLDOWN_START(src, slam_cooldown, slam_cooldown_duration)
var/target_zone = living_target.get_random_valid_zone(source.zone_selected)
var/armor_block = living_target.run_armor_check(target_zone, MELEE, armour_penetration = potential_effectiveness)
living_target.apply_damage(potential_damage * 2, attacking_bodypart.attack_type, target_zone, armor_block)
if(source.body_position != LYING_DOWN && !QDELETED(living_target)) //Throw them if we are standing and we didn't somehow just completely obliterate the target
var/atom/throw_target = get_edge_target_turf(living_target, source.dir)
living_target.throw_at(throw_target, attack_throw_range, rand(throw_power_min,throw_power_max), source, gentle = non_harmful_throw)
if(ground_bounce)
if(isfloorturf(target_turf))
var/turf/open/floor/crunched = target_turf
crunched.crush() // crunch
else if (ground_bounce) // Just in case our target mob somehow evaporated during this process, we still leave an obliterated tile in their wake
if(isfloorturf(target_turf))
var/turf/open/floor/crunched = target_turf
crunched.crush() // again, crunch
return COMPONENT_CANCEL_ATTACK_CHAIN
@@ -18,32 +18,36 @@
/// Eyecolor from the HUD
var/hud_color = "#3CB8A5"
/obj/item/organ/cyberimp/eyes/hud/Initialize(mapload)
. = ..()
if(toggled_on)
for(var/hud_trait in HUD_traits)
add_organ_trait(hud_trait)
/obj/item/organ/cyberimp/eyes/hud/proc/toggle_hud(mob/living/carbon/human/eye_owner)
if(toggled_on)
toggled_on = FALSE
eye_owner.remove_traits(HUD_traits, ORGAN_TRAIT)
for(var/hud_trait in HUD_traits)
remove_organ_trait(hud_trait)
balloon_alert(eye_owner, "hud disabled")
if(hud_color)
eye_owner.remove_eye_color(EYE_COLOR_HUD_PRIORITY)
return
toggled_on = TRUE
eye_owner.add_traits(HUD_traits, ORGAN_TRAIT)
for(var/hud_trait in HUD_traits)
add_organ_trait(hud_trait)
balloon_alert(eye_owner, "hud enabled")
if(hud_color)
eye_owner.add_eye_color_right(hud_color, EYE_COLOR_HUD_PRIORITY)
/obj/item/organ/cyberimp/eyes/hud/on_mob_insert(mob/living/carbon/human/eye_owner, special = FALSE, movement_flags)
. = ..()
eye_owner.add_traits(HUD_traits, ORGAN_TRAIT)
toggled_on = TRUE
if(hud_color)
if(toggled_on && hud_color)
eye_owner.add_eye_color_right(hud_color, EYE_COLOR_HUD_PRIORITY, !special)
/obj/item/organ/cyberimp/eyes/hud/on_mob_remove(mob/living/carbon/human/eye_owner, special, movement_flags)
. = ..()
eye_owner.remove_traits(HUD_traits, ORGAN_TRAIT)
toggled_on = FALSE
if(hud_color)
if(toggled_on && hud_color)
eye_owner.remove_eye_color(EYE_COLOR_HUD_PRIORITY, !special)
/obj/item/organ/cyberimp/eyes/hud/medical
@@ -14,8 +14,8 @@
now_fixed = span_info("Noise slowly begins filling your ears once more.")
low_threshold_cleared = span_info("The ringing in your ears has died down.")
/// `deaf` measures "ticks" of deafness. While > 0, the person is unable to hear anything.
var/deaf = 0
/// temporary deafness, measured in seconds. While > 0, the person is unable to hear anything.
var/temporary_deafness = 0
// `damage` in this case measures long term damage to the ears, if too high,
// the person will not have either `deaf` or `ear_damage` decrease
@@ -39,23 +39,20 @@
// no healing if failing
if(organ_flags & ORGAN_FAILING)
return
adjustEarDamage(0, -0.5 * seconds_per_tick)
adjust_temporary_deafness(-seconds_per_tick SECONDS)
if((damage > low_threshold) && SPT_PROB(damage / 60, seconds_per_tick))
adjustEarDamage(0, 4)
adjust_temporary_deafness(4 SECONDS)
SEND_SOUND(owner, sound('sound/items/weapons/flash_ring.ogg'))
/obj/item/organ/ears/apply_organ_damage(damage_amount, maximum, required_organ_flag)
. = ..()
update_temp_deafness()
/obj/item/organ/ears/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
update_temp_deafness()
if(temporary_deafness)
on_deafened()
/obj/item/organ/ears/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
UnregisterSignal(organ_owner, COMSIG_MOB_SAY)
REMOVE_TRAIT(organ_owner, TRAIT_DEAF, EAR_DAMAGE)
if(temporary_deafness)
on_undeafened(organ_owner)
/obj/item/organ/ears/get_status_appendix(advanced, add_tooltips)
if(owner.stat == DEAD || !HAS_TRAIT(owner, TRAIT_DEAF))
@@ -65,7 +62,7 @@
return conditional_tooltip("Subject is permanently deaf.", "Irreparable under normal circumstances.", add_tooltips)
if(HAS_TRAIT_FROM(owner, TRAIT_DEAF, GENETIC_MUTATION))
return conditional_tooltip("Subject is genetically deaf.", "Use medication such as [/datum/reagent/medicine/mutadone::name].", add_tooltips)
if(HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
if((organ_flags & ORGAN_FAILING) || HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
return conditional_tooltip("Subject is [(organ_flags & ORGAN_FAILING) ? "permanently": "temporarily"] deaf from ear damage.", "Repair surgically, use medication such as [/datum/reagent/medicine/inacusiate::name], or protect ears with earmuffs.", add_tooltips)
return "Subject is deaf."
@@ -73,52 +70,43 @@
// Always show if we have an appendix
return ..() || (owner.stat != DEAD && HAS_TRAIT(owner, TRAIT_DEAF))
/**
* Snowflake proc to handle temporary deafness
*
* * ddmg: Handles normal organ damage
* * ddeaf: Handles temporary deafness, 1 ddeaf = 2 seconds of deafness, by default (with no multiplier)
*/
/obj/item/organ/ears/proc/adjustEarDamage(ddmg = 0, ddeaf = 0)
if(HAS_TRAIT(owner, TRAIT_GODMODE))
update_temp_deafness()
///Adjust the temporary deafness of the person, up or down
/obj/item/organ/ears/proc/adjust_temporary_deafness(amount)
// organ failure makes us permanently deafened. Also, doesn't do anything if not in someone or during godmode
if(amount > 0 && owner && HAS_TRAIT(owner, TRAIT_GODMODE))
return
var/mod_damage = ddmg > 0 ? (ddmg * damage_multiplier) : ddmg
if(mod_damage)
apply_organ_damage(mod_damage)
var/mod_deaf = ddeaf > 0 ? (ddeaf * damage_multiplier) : ddeaf
if(mod_deaf)
deaf = max(deaf + mod_deaf, 0)
update_temp_deafness()
temporary_deafness = max(temporary_deafness + amount * damage_multiplier, 0)
/// Updates status of deafness
/obj/item/organ/ears/proc/update_temp_deafness()
// if we're failing we always have at least some deaf stacks (and thus deafness)
if(organ_flags & ORGAN_FAILING)
deaf = max(deaf, 1 * damage_multiplier)
if(isnull(owner))
if(!owner)
return
if(HAS_TRAIT(owner, TRAIT_GODMODE))
deaf = 0
if(temporary_deafness && !HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
on_deafened()
else if(!temporary_deafness && HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
on_undeafened()
if(deaf > 0)
if(!HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE))
RegisterSignal(owner, COMSIG_MOB_SAY, PROC_REF(adjust_speech))
ADD_TRAIT(owner, TRAIT_DEAF, EAR_DAMAGE)
else
REMOVE_TRAIT(owner, TRAIT_DEAF, EAR_DAMAGE)
UnregisterSignal(owner, COMSIG_MOB_SAY)
///Called when temporary deafness begins
/obj/item/organ/ears/proc/on_deafened()
RegisterSignal(owner, COMSIG_MOB_SAY, PROC_REF(adjust_speech))
ADD_TRAIT(owner, TRAIT_DEAF, EAR_DAMAGE)
///Called when temporary deafness reaches zero. Has to have an 'organ_owner' arg, because by the time it's called on 'on_mob_remove', owner is already null
/obj/item/organ/ears/proc/on_undeafened(mob/living/organ_owner = owner)
REMOVE_TRAIT(organ_owner, TRAIT_DEAF, EAR_DAMAGE)
UnregisterSignal(organ_owner, COMSIG_MOB_SAY)
/obj/item/organ/ears/on_begin_failure()
add_organ_trait(TRAIT_DEAF)
/obj/item/organ/ears/on_failure_recovery()
remove_organ_trait(TRAIT_DEAF)
/// Being deafened by loud noises makes you shout
/obj/item/organ/ears/proc/adjust_speech(datum/source, list/speech_args)
SIGNAL_HANDLER
if(HAS_TRAIT_NOT_FROM(source, TRAIT_DEAF, EAR_DAMAGE))
return
if(HAS_TRAIT(source, TRAIT_SIGN_LANG))
if(HAS_TRAIT_NOT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE) || HAS_TRAIT(owner, TRAIT_SIGN_LANG))
return
var/message = speech_args[SPEECH_MESSAGE]
@@ -240,16 +228,9 @@
desc = "Allows the user to more easily hear whispers. The user becomes extra vulnerable to loud noises, however"
// Same sensitivity as felinid ears
damage_multiplier = 2
// The original idea was to use signals to do this not traits. Unfortunately, the star effect used for whispers applies before any relevant signals
// This seems like the least invasive solution
/obj/item/organ/ears/cybernetic/whisper/on_mob_insert(mob/living/carbon/ear_owner)
. = ..()
ADD_TRAIT(ear_owner, TRAIT_GOOD_HEARING, ORGAN_TRAIT)
/obj/item/organ/ears/cybernetic/whisper/on_mob_remove(mob/living/carbon/ear_owner)
. = ..()
REMOVE_TRAIT(ear_owner, TRAIT_GOOD_HEARING, ORGAN_TRAIT)
// The original idea was to use signals to do this not traits. Unfortunately, the star effect used for whispers applies before any relevant signals
// This seems like the least invasive solution
organ_traits = list(TRAIT_GOOD_HEARING)
/obj/item/organ/ears/cybernetic/volume
name = "volume-adjusting cybernetic ears"
@@ -265,14 +246,7 @@
desc = "Through the power of modern engineering, allows the user to hear speech through walls. The user becomes extra vulnerable to loud noises, however"
// Same sensitivity as felinid ears
damage_multiplier = 2
/obj/item/organ/ears/cybernetic/xray/on_mob_insert(mob/living/carbon/ear_owner)
. = ..()
ADD_TRAIT(ear_owner, TRAIT_XRAY_HEARING, ORGAN_TRAIT)
/obj/item/organ/ears/cybernetic/xray/on_mob_remove(mob/living/carbon/ear_owner)
. = ..()
REMOVE_TRAIT(ear_owner, TRAIT_XRAY_HEARING, ORGAN_TRAIT)
organ_traits = list(TRAIT_XRAY_HEARING)
/obj/item/organ/ears/cybernetic/emp_act(severity)
. = ..()
@@ -22,8 +22,6 @@
/// Sight flags this eye pair imparts on its user.
var/sight_flags = NONE
/// changes how the eyes overlay is applied, makes it apply over the lighting layer
var/overlay_ignore_lighting = FALSE
/// How much innate tint these eyes have
var/tint = 0
/// How much innare flash protection these eyes have, usually paired with tint
@@ -52,8 +50,6 @@
/// Glasses cannot be worn over these eyes. Currently unused
var/no_glasses = FALSE
/// indication that the eyes are undergoing some negative effect
var/damaged = FALSE
/// Native FOV that will be applied if a config is enabled
var/native_fov = FOV_90_DEGREES
/// Scarring on this organ
@@ -82,8 +78,13 @@
/obj/item/organ/eyes/on_mob_insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(organ_flags & ORGAN_FAILING)
receiver.become_blind(EYE_DAMAGE)
if(damage >= low_threshold)
receiver.assign_nearsightedness(EYE_DAMAGE, damage >= high_threshold ? 3 : 2, TRUE)
receiver.cure_blind(NO_EYES)
apply_damaged_eye_effects()
// Ensures that non-player mobs get their eye colors assigned, as players get them from prefs
if (ishuman(receiver))
var/mob/living/carbon/human/as_human = receiver
@@ -91,9 +92,17 @@
eye_color_left = as_human.eye_color_left
if (!eye_color_right)
eye_color_right = as_human.eye_color_right
RegisterSignals(receiver, list(
SIGNAL_ADDTRAIT(TRAIT_LUMINESCENT_EYES),
SIGNAL_REMOVETRAIT(TRAIT_LUMINESCENT_EYES),
SIGNAL_ADDTRAIT(TRAIT_REFLECTIVE_EYES),
SIGNAL_REMOVETRAIT(TRAIT_REFLECTIVE_EYES),
), PROC_REF(on_shiny_eyes_trait_update))
refresh(receiver, call_update = TRUE)
RegisterSignal(receiver, COMSIG_ATOM_BULLET_ACT, PROC_REF(on_bullet_act))
RegisterSignal(receiver, COMSIG_COMPONENT_CLEAN_FACE_ACT, PROC_REF(on_face_wash))
if (scarring)
apply_scarring_effects()
@@ -139,20 +148,38 @@
if(!special)
human_owner.update_body()
// become blind (if not special)
if(!special)
organ_owner.become_blind(NO_EYES)
// Cure blindness from eye damage
organ_owner.cure_blind(EYE_DAMAGE)
organ_owner.cure_nearsighted(EYE_DAMAGE)
// Eye blind and temp blind go to, even if this is a bit of cheesy way to clear blindness
organ_owner.remove_status_effect(/datum/status_effect/eye_blur)
organ_owner.remove_status_effect(/datum/status_effect/temporary_blindness)
// Then become blind anyways (if not special)
if(!special)
organ_owner.become_blind(NO_EYES)
if (scarring)
owner.cure_nearsighted(TRAIT_RIGHT_EYE_SCAR)
owner.cure_nearsighted(TRAIT_LEFT_EYE_SCAR)
owner.cure_blind(EYE_SCARRING_TRAIT)
organ_owner.update_tint()
organ_owner.update_sight()
is_emissive = FALSE // SKYRAT EDIT ADDITION
UnregisterSignal(organ_owner, list(COMSIG_ATOM_BULLET_ACT, COMSIG_COMPONENT_CLEAN_FACE_ACT))
UnregisterSignal(organ_owner, list(
COMSIG_ATOM_BULLET_ACT,
COMSIG_COMPONENT_CLEAN_FACE_ACT,
SIGNAL_ADDTRAIT(TRAIT_LUMINESCENT_EYES),
SIGNAL_REMOVETRAIT(TRAIT_LUMINESCENT_EYES),
SIGNAL_ADDTRAIT(TRAIT_REFLECTIVE_EYES),
SIGNAL_REMOVETRAIT(TRAIT_REFLECTIVE_EYES),
))
///Called whenever the luminescent and/or reflective eyes traits are added or removed
/obj/item/organ/eyes/proc/on_shiny_eyes_trait_update(mob/living/carbon/human/source)
SIGNAL_HANDLER
source.update_eyes()
/obj/item/organ/eyes/update_atom_colour()
. = ..()
@@ -290,9 +317,8 @@
var/list/overlays = list(eye_left, eye_right)
if(overlay_ignore_lighting && !(parent.obscured_slots & HIDEEYES))
overlays += emissive_appearance(eye_left.icon, eye_left.icon_state, parent, -EYES_LAYER, alpha = eye_left.alpha)
overlays += emissive_appearance(eye_right.icon, eye_right.icon_state, parent, -EYES_LAYER, alpha = eye_right.alpha)
if(!(parent.obscured_slots & HIDEEYES))
overlays += get_emissive_overlays(eye_left, eye_right, parent)
var/obj/item/bodypart/head/my_head = parent.get_bodypart(BODY_ZONE_HEAD)
@@ -331,6 +357,19 @@
return overlays
///Returns the two emissive overlays built for the left and right eyes, in order.
/obj/item/organ/eyes/proc/get_emissive_overlays(mutable_appearance/eye_left, mutable_appearance/eye_right, atom/spokesman)
var/list/return_list = list()
var/emissive_effect
if((owner && HAS_TRAIT(owner, TRAIT_LUMINESCENT_EYES)) || (TRAIT_LUMINESCENT_EYES in organ_traits))
emissive_effect = EMISSIVE_BLOOM
else if((owner && HAS_TRAIT(owner, TRAIT_REFLECTIVE_EYES)) || (TRAIT_REFLECTIVE_EYES in organ_traits))
emissive_effect = EMISSIVE_SPECULAR
if(emissive_effect)
return_list += emissive_appearance(eye_left.icon, eye_left.icon_state, spokesman, -EYES_LAYER, alpha = eye_left.alpha, effect_type = emissive_effect)
return_list += emissive_appearance(eye_right.icon, eye_right.icon_state, spokesman, -EYES_LAYER, alpha = eye_right.alpha, effect_type = emissive_effect)
return return_list
/obj/item/organ/eyes/update_overlays()
. = ..()
if (scarring & RIGHT_EYE_SCAR)
@@ -390,13 +429,6 @@
owner.cure_blind(EYE_SCARRING_TRAIT)
owner.update_body()
/obj/item/organ/eyes/on_mob_remove(mob/living/carbon/eye_owner)
. = ..()
if (scarring)
eye_owner.cure_nearsighted(TRAIT_RIGHT_EYE_SCAR)
eye_owner.cure_nearsighted(TRAIT_LEFT_EYE_SCAR)
eye_owner.cure_blind(EYE_SCARRING_TRAIT)
#undef OFFSET_X
#undef OFFSET_Y
@@ -406,38 +438,28 @@
eye_color_left = initial(eye_color_left)
eye_color_right = initial(eye_color_right)
/obj/item/organ/eyes/apply_organ_damage(damage_amount, maximum = maxHealth, required_organ_flag)
. = ..()
if(!owner)
return FALSE
apply_damaged_eye_effects()
/// Applies effects to our owner based on how damaged our eyes are
/obj/item/organ/eyes/proc/apply_damaged_eye_effects()
// we're in healthy threshold, either try to heal (if damaged) or do nothing
if(damage <= low_threshold)
if(damaged)
damaged = FALSE
// clear nearsightedness from damage
owner.cure_nearsighted(EYE_DAMAGE)
// and cure blindness from damage
owner.cure_blind(EYE_DAMAGE)
/obj/item/organ/eyes/on_low_damage_received()
if(damage >= high_threshold)
return
owner?.assign_nearsightedness(EYE_DAMAGE, 2, TRUE)
//various degrees of "oh fuck my eyes", from "point a laser at your eye" to "staring at the Sun" intensities
// 50 - blind
// 49-31 - nearsighted (2 severity)
// 30-20 - nearsighted (1 severity)
if(organ_flags & ORGAN_FAILING)
// become blind from damage
owner.become_blind(EYE_DAMAGE)
/obj/item/organ/eyes/on_high_damage_received()
owner?.assign_nearsightedness(EYE_DAMAGE, 3, TRUE)
else
// become nearsighted from damage
var/severity = damage > high_threshold ? 3 : 2
owner.assign_nearsightedness(EYE_DAMAGE, severity, TRUE)
/obj/item/organ/eyes/on_begin_failure()
owner?.become_blind(EYE_DAMAGE)
damaged = TRUE
/obj/item/organ/eyes/on_failure_recovery()
owner?.cure_blind(EYE_DAMAGE)
/obj/item/organ/eyes/on_high_damage_healed()
if(damage <= low_threshold)
return
owner?.assign_nearsightedness(EYE_DAMAGE, 2, TRUE)
/obj/item/organ/eyes/on_low_damage_healed()
// clear nearsightedness from damage
owner?.cure_nearsighted(EYE_DAMAGE)
/obj/item/organ/eyes/feel_for_damage(self_aware)
// Eye damage has visual effects, so we don't really need to "feel" it when self-examining
@@ -715,16 +737,9 @@
eye_color_left = "#3cb8a5"
eye_color_right = "#3cb8a5"
sight_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS
organ_traits = list(TRAIT_XRAY_VISION)
penlight_message = "replaced by small radiation emitters and detectors"
/obj/item/organ/eyes/robotic/xray/on_mob_insert(mob/living/carbon/eye_owner)
. = ..()
ADD_TRAIT(eye_owner, TRAIT_XRAY_VISION, ORGAN_TRAIT)
/obj/item/organ/eyes/robotic/xray/on_mob_remove(mob/living/carbon/eye_owner)
. = ..()
REMOVE_TRAIT(eye_owner, TRAIT_XRAY_VISION, ORGAN_TRAIT)
/obj/item/organ/eyes/robotic/thermals
name = "thermal eyes"
desc = "These cybernetic eye implants will give you thermal vision. Vertical slit pupil included."
@@ -1051,17 +1066,25 @@
if(QDELETED(eye_owner) || !ishuman(eye_owner)) //Other carbon mobs don't have eye color.
return
if(!eye.light_on)
eye_icon_state = initial(eye_icon_state)
overlay_ignore_lighting = FALSE
else
overlay_ignore_lighting = TRUE
eye_icon_state = base_eye_state
var/obj/item/bodypart/head/head = eye_owner.get_bodypart(BODY_ZONE_HEAD) //if we have eyes we definently have a head anyway
var/previous_flags = head.head_flags
head.head_flags = previous_flags | HEAD_EYECOLOR
eye_owner.dna.species.handle_body(eye_owner)
head.head_flags |= HEAD_EYECOLOR
///enabling and disabling the TRAIT_LUMINESCENT_EYES trait already calls handle_eyes(), in that case, let's skip that call
var/skip_call = FALSE
if(!eye.light_on)
eye_icon_state = initial(eye_icon_state)
skip_call = HAS_TRAIT_FROM_ONLY(eye_owner, TRAIT_LUMINESCENT_EYES, REF(src))
remove_organ_trait(TRAIT_LUMINESCENT_EYES)
else
skip_call = !HAS_TRAIT(eye_owner, TRAIT_LUMINESCENT_EYES)
add_organ_trait(TRAIT_LUMINESCENT_EYES)
eye_icon_state = base_eye_state
if(!skip_call && ishuman(eye_owner))
var/mob/living/carbon/human/humie = eye_owner
humie.update_eyes()
head.head_flags = previous_flags
#undef MATCH_LIGHT_COLOR
@@ -1187,7 +1210,7 @@
eye_color_right = "#f74a4d"
eye_icon_state = "eyes_glow"
iris_overlay = null
overlay_ignore_lighting = TRUE
organ_traits = list(TRAIT_UNNATURAL_RED_GLOWY_EYES, TRAIT_LUMINESCENT_EYES)
flash_protect = FLASH_PROTECTION_HYPER_SENSITIVE
low_light_cutoff = list(5, 12, 20)
medium_light_cutoff = list(15, 20, 30)
@@ -1200,10 +1223,6 @@
apply_organ_damage(20 * examtool.light_power) //that's 0.5 lightpower for a penlight, so one penlight shining is equivalent to two seconds in a lit area
return span_danger("[owner.p_Their()] eyes [penlight_message].")
/obj/item/organ/eyes/night_vision/maintenance_adapted/on_mob_insert(mob/living/carbon/eye_owner)
. = ..()
ADD_TRAIT(eye_owner, TRAIT_UNNATURAL_RED_GLOWY_EYES, ORGAN_TRAIT)
/obj/item/organ/eyes/night_vision/maintenance_adapted/on_life(seconds_per_tick, times_fired)
if(!owner.is_blind() && isturf(owner.loc) && owner.has_light_nearby(light_amount=0.5)) //we allow a little more than usual so we can produce light from the adapted eyes
to_chat(owner, span_danger("Your eyes! They burn in the light!"))
@@ -1213,10 +1232,6 @@
apply_organ_damage(-10) //heal quickly
. = ..()
/obj/item/organ/eyes/night_vision/maintenance_adapted/on_mob_remove(mob/living/carbon/unadapted, special = FALSE, movement_flags)
REMOVE_TRAIT(unadapted, TRAIT_UNNATURAL_RED_GLOWY_EYES, ORGAN_TRAIT)
return ..()
/obj/item/organ/eyes/pod
name = "pod eyes"
desc = "Strangest salad you've ever seen."
@@ -1226,3 +1241,9 @@
// iris_overlay = null BUBBER EDIT - ALLOWS EYE COLOR TO BE CHANGED AGAIN
foodtype_flags = PODPERSON_ORGAN_FOODTYPES
penlight_message = "are green and plant-like"
/obj/item/organ/eyes/felinid
name = "felinid eyes"
desc = "A pair of highly reflective eyes with slit pupils, like those of a cat."
pupils_name = "slit pupils"
penlight_message = "shine under the pearly light"
@@ -536,7 +536,7 @@
emp_vulnerability = 20
metabolism_efficiency = 0.1
/obj/item/organ/stomach/cybernetic/tier2/stomach_acid_power(atom/movable/nomnom)
/obj/item/organ/stomach/cybernetic/tier3/stomach_acid_power(atom/movable/nomnom)
if (isliving(nomnom))
return 0
if (IsEdible(nomnom))
@@ -8,6 +8,7 @@
attack_verb_continuous = list("licks", "slobbers", "slaps", "frenches", "tongues")
attack_verb_simple = list("lick", "slobber", "slap", "french", "tongue")
voice_filter = ""
organ_traits = list(TRAIT_SPEAKS_CLEARLY)
/**
* A cached list of paths of all the languages this tongue is capable of speaking
*
@@ -51,6 +52,8 @@
// - then we cache it via string list
// this results in tongues with identical possible languages sharing a cached list instance
languages_possible = string_list(get_possible_languages())
if(!sense_of_taste)
add_organ_trait(TRAIT_AGEUSIA)
/obj/item/organ/tongue/examine(mob/user)
. = ..()
@@ -137,40 +140,24 @@
* ageusia from having a non-tasting tongue.
*/
REMOVE_TRAIT(receiver, TRAIT_AGEUSIA, NO_TONGUE_TRAIT)
apply_tongue_effects()
/obj/item/organ/tongue/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
temp_say_mod = ""
UnregisterSignal(organ_owner, COMSIG_MOB_SAY)
REMOVE_TRAIT(organ_owner, TRAIT_SPEAKS_CLEARLY, SPEAKING_FROM_TONGUE)
REMOVE_TRAIT(organ_owner, TRAIT_AGEUSIA, ORGAN_TRAIT)
// Carbons by default start with NO_TONGUE_TRAIT caused TRAIT_AGEUSIA
ADD_TRAIT(organ_owner, TRAIT_AGEUSIA, NO_TONGUE_TRAIT)
organ_owner.voice_filter = initial(organ_owner.voice_filter)
/obj/item/organ/tongue/apply_organ_damage(damage_amount, maximum = maxHealth, required_organ_flag)
. = ..()
if(!owner)
return FALSE
apply_tongue_effects()
/obj/item/organ/tongue/on_begin_failure()
remove_organ_trait(TRAIT_SPEAKS_CLEARLY)
add_organ_trait(TRAIT_AGEUSIA)
/// Applies effects to our owner based on how damaged our tongue is
/obj/item/organ/tongue/proc/apply_tongue_effects()
/obj/item/organ/tongue/on_failure_recovery()
add_organ_trait(TRAIT_SPEAKS_CLEARLY)
if(sense_of_taste)
//tongues can't taste food when they are failing
if(organ_flags & ORGAN_FAILING)
ADD_TRAIT(owner, TRAIT_AGEUSIA, ORGAN_TRAIT)
else
REMOVE_TRAIT(owner, TRAIT_AGEUSIA, ORGAN_TRAIT)
else
//tongues can't taste food when they lack a sense of taste
ADD_TRAIT(owner, TRAIT_AGEUSIA, ORGAN_TRAIT)
if(organ_flags & ORGAN_FAILING)
REMOVE_TRAIT(owner, TRAIT_SPEAKS_CLEARLY, SPEAKING_FROM_TONGUE)
else
ADD_TRAIT(owner, TRAIT_SPEAKS_CLEARLY, SPEAKING_FROM_TONGUE)
remove_organ_trait(TRAIT_AGEUSIA)
/obj/item/organ/tongue/could_speak_language(datum/language/language_path)
return (language_path in languages_possible)
@@ -579,7 +566,7 @@
attack_verb_simple = list("beep", "boop")
modifies_speech = TRUE
taste_sensitivity = 25 // not as good as an organic tongue
organ_traits = list(TRAIT_SILICON_EMOTES_ALLOWED)
organ_traits = list(TRAIT_SPEAKS_CLEARLY, TRAIT_SILICON_EMOTES_ALLOWED)
voice_filter = "alimiter=0.9,acompressor=threshold=0.2:ratio=20:attack=10:release=50:makeup=2,highpass=f=1000"
/obj/item/organ/tongue/robot/could_speak_language(datum/language/language_path)
@@ -645,7 +632,7 @@
say_mod = "meows"
liked_foodtypes = SEAFOOD | ORANGES | BUGS | GORE
disliked_foodtypes = GROSS | CLOTH | RAW
organ_traits = list(TRAIT_WOUND_LICKER, TRAIT_FISH_EATER, TRAIT_CARPOTOXIN_IMMUNE)
organ_traits = list(TRAIT_SPEAKS_CLEARLY, TRAIT_WOUND_LICKER, TRAIT_FISH_EATER, TRAIT_CARPOTOXIN_IMMUNE)
languages_native = list(/datum/language/nekomimetic)
actions_types = list(/datum/action/item_action/organ_action/go_feral)
var/feral_mode = FALSE
@@ -229,7 +229,8 @@
moveToNullspace()
bodypart_owner = null
on_bodypart_remove(limb)
if(!isnull(limb))
on_bodypart_remove(limb)
return TRUE
+10 -7
View File
@@ -93,22 +93,25 @@
// Only followers of Asclepius have the ability to use Healing Touch and perform miracle feats of surgery.
// Prevents people from performing multiple simultaneous surgeries unless they're holding a Rod of Asclepius.
surgery.step_in_progress = TRUE
var/speed_mod = 1
var/fail_prob = 0//100 - fail_prob = success_prob
var/advance = FALSE
var/interaction_key = HAS_TRAIT(user, TRAIT_HIPPOCRATIC_OATH) ? target : DOAFTER_SOURCE_SURGERY
if(DOING_INTERACTION(user, interaction_key))
user.balloon_alert(user, "already doing surgery!")
return FALSE
if(!chem_check(target))
user.balloon_alert(user, "missing [LOWER_TEXT(get_chem_list())]!")
to_chat(user, span_warning("[target] is missing the [LOWER_TEXT(get_chem_list())] required to perform this surgery step!"))
surgery.step_in_progress = FALSE
return FALSE
if(preop(user, target, target_zone, tool, surgery) == SURGERY_STEP_FAIL)
update_surgery_mood(target, SURGERY_STATE_FAILURE)
surgery.step_in_progress = FALSE
return FALSE
surgery.step_in_progress = TRUE
var/speed_mod = 1
var/fail_prob = 0//100 - fail_prob = success_prob
var/advance = FALSE
update_surgery_mood(target, SURGERY_STATE_STARTED)
play_preop_sound(user, target, target_zone, tool, surgery) // Here because most steps overwrite preop
@@ -162,7 +165,7 @@
var/was_sleeping = (target.stat != DEAD && target.IsSleeping())
if(do_after(user, modded_time, target = target, interaction_key = user.has_status_effect(/datum/status_effect/hippocratic_oath) ? target : DOAFTER_SOURCE_SURGERY)) //If we have the hippocratic oath, we can perform one surgery on each target, otherwise we can only do one surgery in total.
if(do_after(user, modded_time, target = target, interaction_key = interaction_key)) //If we have the hippocratic oath, we can perform one surgery on each target, otherwise we can only do one surgery in total.
if((prob(100-fail_prob) || (iscyborg(user) && !silicons_obey_prob)) && !try_to_fail)
if(success(user, target, target_zone, tool, surgery))