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