Soft reworks changeling Darkness Adaptation (#89767)

## About The Pull Request

Soft reworks Ling's Darkness Adaptation

- No longer permanently makes you transparent when active. Transparency
scales based on how dark your area is. Darker areas = more transparent.
- No longer permanently grants dark vision. Darker areas = more dark
vision.
- Now *costs* 15 chemicals rather than just *requiring* 10 chemicals.
Disabling the effect costs 0 chemicals.
- Toggling the effect imparts a click CD. CD is lower for disabling it. 


https://github.com/user-attachments/assets/d0384a07-1e23-46f7-8362-f42caa0b9c11

Also fixes a bug with digital camo

## Why It's Good For The Game

While this mutation was designed to be used in stealth shenanigans, it
was instead primarily used for murderboning with low visibility. This is
lame.

These changes will address that. 
- Activating the power imparts a click CD and costs chemicals so it is
less wise to use it when you get jumped and more wise to use it when
you're setting up an ambush.
- Flashlights and similar bright lights will disable the power, so it
will shine less (pun intended) when used in bright areas such as space
or, well, any area with a dark floor (most of Nebula Station)

It will also open up potential with other changeling powers:
- For example, EMP screech to disable lights or Disorienting screech to
break light fixtures.

## Changelog

🆑 Melbert
balance: Reworks Changeling's Darkness Adaptation. It's strength now
scales with how dark your environment is, rather than being static
transparency.
fix: Fixes Digital Camo's examine message
/🆑
This commit is contained in:
MrMelbert
2025-03-12 17:27:23 -04:00
committed by Roxy
parent 64c9f351af
commit eea0b14f4e
2 changed files with 136 additions and 54 deletions
+2 -2
View File
@@ -37,10 +37,10 @@
var/datum/atom_hud/silicon_hud = GLOB.huds[hud_type]
silicon_hud.unhide_single_atomhud_from(AI,target)
/datum/element/digitalcamo/proc/on_examine(datum/source, mob/M)
/datum/element/digitalcamo/proc/on_examine(datum/source, mob/M, list/examine_list)
SIGNAL_HANDLER
to_chat(M, span_warning("[source.p_their()] skin seems to be shifting like something is moving below it."))
examine_list += span_warning("[source.p_their()] skin seems to be shifting like something is moving below it.")
/datum/element/digitalcamo/proc/can_track(datum/source, mob/user)
SIGNAL_HANDLER
@@ -1,83 +1,165 @@
/datum/action/changeling/darkness_adaptation
name = "Darkness Adaptation"
desc = "Our skin pigmentation and eyes rapidly changes to suit the darkness. Needs 10 chemicals in-storage to toggle. Slows down our chemical regeneration by 15%"
desc = "Our skin pigmentation and eyes rapidly change to suit the darkness. \
Costs 15 chemicals to enable. Slows down chemical regeneration by 15% while active."
helptext = "Allows us to darken and change the translucency of our pigmentation, and adapt our eyes to see in dark conditions, \
The translucent effect works best in dark enviroments and garments. Can be toggled on and off."
The translucent effect works best in dark enviroments and garments. Can be toggled on and off."
button_icon_state = "darkness_adaptation"
dna_cost = 2
chemical_cost = 10
req_human = TRUE
//// is ability active (we are invisible)?
var/is_active = FALSE
chemical_cost = 15
/// How much we slow chemical regeneration while active, in chems per second
var/recharge_slowdown = 0.15
/datum/action/changeling/darkness_adaptation/on_purchase(mob/user, is_respec)
. = ..()
RegisterSignal(user, COMSIG_CARBON_GAIN_ORGAN, PROC_REF(eye_implanted))
RegisterSignal(user, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(eye_removed))
/datum/action/changeling/darkness_adaptation/sting_action(mob/living/carbon/human/cling) //SHOULD always be human, because req_human = TRUE
..()
is_active = !is_active
if(is_active)
enable_ability(cling)
else
if(cling.has_status_effect(/datum/status_effect/darkness_adapted))
disable_ability(cling)
cling.changeNext_move(CLICK_CD_MELEE * 0.5)
chemical_cost = initial(chemical_cost)
return FALSE
..()
enable_ability(cling)
chemical_cost = 0
return TRUE
/datum/action/changeling/darkness_adaptation/Remove(mob/living/carbon/human/cling)
..()
disable_ability(cling)
return ..()
/datum/action/changeling/darkness_adaptation/proc/enable_ability(mob/living/carbon/human/cling) //Enable the adaptation
animate(cling, alpha = 65,time = 3 SECONDS)
cling.visible_message(span_warning("[cling]'s skin suddenly starts becoming translucent!"), \
span_notice("We are now far more stealthy and better at seeing in the dark."))
animate(cling, color = COLOR_DARK, time = 3 SECONDS) // Darkens their overall appearance
/datum/action/changeling/darkness_adaptation/proc/enable_ability(mob/living/carbon/human/cling)
if(!cling.apply_status_effect(/datum/status_effect/darkness_adapted))
return
cling.visible_message(
span_warning("[cling]'s skin suddenly starts shifting and shimmering!"),
span_notice("We shift our skin to adapt to the darkness."),
)
var/datum/antagonist/changeling/changeling_data = cling.mind?.has_antag_datum(/datum/antagonist/changeling)
changeling_data?.chem_recharge_slowdown -= recharge_slowdown //Slows down chem regeneration
var/obj/item/organ/eyes/eyes = cling.get_organ_by_type(/obj/item/organ/eyes)
if(!istype(eyes))
return
eyes.lighting_cutoff = LIGHTING_CUTOFF_MEDIUM // Adds barely usable, kinda shit night vision
eyes.flash_protect = max(eyes.flash_protect += -1, FLASH_PROTECTION_HYPER_SENSITIVE) // Reduces flash protection by one level
cling.update_sight() // Update the display
/datum/action/changeling/darkness_adaptation/proc/disable_ability(mob/living/carbon/human/cling) //Restore the adaptation
animate(cling, alpha = 255, time = 3 SECONDS)
/datum/action/changeling/darkness_adaptation/proc/disable_ability(mob/living/carbon/human/cling)
if(!cling.remove_status_effect(/datum/status_effect/darkness_adapted))
return
cling.visible_message(
span_warning("[cling] appears from thin air!"),
span_notice("We are now appearing normal and lost the ability to see in the dark."),
span_warning("[cling]'s skin goes from shimmering to normal."),
span_notice("We stop adapting our skin to the darkness."),
)
animate(cling, color = null, time = 3 SECONDS)
var/datum/antagonist/changeling/changeling_data = cling.mind?.has_antag_datum(/datum/antagonist/changeling)
changeling_data?.chem_recharge_slowdown += recharge_slowdown
var/obj/item/organ/eyes/eyes = cling.get_organ_by_type(/obj/item/organ/eyes)
if(!istype(eyes))
return
eyes.lighting_cutoff = LIGHTING_CUTOFF_VISIBLE
eyes.flash_protect = max(eyes.flash_protect += 1, FLASH_PROTECTION_WELDER)
cling.update_sight()
/// Signal proc to grant the correct level of flash sensitivity
/datum/action/changeling/darkness_adaptation/proc/eye_implanted(mob/living/source, obj/item/organ/gained, special)
/// Makes the user harder to see in the dark (and makes the user see in the dark easier)
/datum/status_effect/darkness_adapted
id = "darkness_adapted"
tick_interval = 0.5 SECONDS
alert_type = null
/// Threshold before the dark color is applied
var/dark_color_threshold = 70
/// Tracks last eye strength to avoid unnecessary updates / eye nerfs
VAR_FINAL/last_eye_strength = 0
/// Tracks last alpha to avoid unnecessary updates
VAR_FINAL/last_alpha = 255
/// When we're moving around, skip any constant tick updates
COOLDOWN_DECLARE(skip_tick_update)
/datum/status_effect/darkness_adapted/on_apply()
RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(update_invis))
RegisterSignal(owner, COMSIG_CARBON_GAIN_ORGAN, PROC_REF(eye_implanted))
RegisterSignal(owner, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(eye_removed))
RegisterSignal(owner, COMSIG_ATOM_EXAMINE, PROC_REF(examine_mob))
update_eye_status()
update_invis()
return TRUE
/datum/status_effect/darkness_adapted/on_remove()
UnregisterSignal(owner, list(
COMSIG_CARBON_GAIN_ORGAN,
COMSIG_CARBON_LOSE_ORGAN,
COMSIG_MOVABLE_MOVED,
COMSIG_ATOM_EXAMINE,
))
if(last_eye_strength > 0)
nerf_eyes(owner.get_organ_by_type(/obj/item/organ/eyes))
if(last_alpha < 255)
nerf_invis()
/datum/status_effect/darkness_adapted/proc/examine_mob(datum/source, mob/user, list/examine_list)
SIGNAL_HANDLER
var/obj/item/organ/eyes/eyes = gained
if(last_alpha > dark_color_threshold)
examine_list += span_warning("[owner.p_Their()] skin is shimmering unnaturally in the light.")
/datum/status_effect/darkness_adapted/proc/get_darkness()
var/turf/owner_turf = get_turf(owner)
return istype(owner_turf) ? owner_turf.get_lumcount() : 1
/datum/status_effect/darkness_adapted/proc/get_alpha()
return clamp(65 + ((get_darkness() - LIGHTING_TILE_IS_DARK) * 255), 65, 255)
/datum/status_effect/darkness_adapted/proc/get_eye_strength()
return clamp(LIGHTING_CUTOFF_MEDIUM - ((get_darkness() - LIGHTING_TILE_IS_DARK) * LIGHTING_CUTOFF_MEDIUM + 5), LIGHTING_CUTOFF_REAL_LOW, LIGHTING_CUTOFF_MEDIUM + 5)
/datum/status_effect/darkness_adapted/proc/update_eye_status(obj/item/organ/eyes/eyes = owner.get_organ_by_type(/obj/item/organ/eyes))
if(!istype(eyes))
last_eye_strength = 0
return
if(is_active)
eyes.flash_protect = max(eyes.flash_protect += -1, FLASH_PROTECTION_HYPER_SENSITIVE)
else
var/new_eye_strength = get_eye_strength()
if(last_eye_strength == new_eye_strength)
return
buff_eyes(eyes, new_eye_strength)
last_eye_strength = new_eye_strength
/datum/status_effect/darkness_adapted/proc/buff_eyes(obj/item/organ/eyes/eyes, new_strength = get_eye_strength())
eyes.lighting_cutoff = new_strength
if(new_strength >= LIGHTING_CUTOFF_MEDIUM)
eyes.flash_protect = max(eyes.flash_protect += 1, FLASH_PROTECTION_WELDER)
else if(last_eye_strength >= LIGHTING_CUTOFF_MEDIUM)
eyes.flash_protect = max(eyes.flash_protect -= 1, FLASH_PROTECTION_HYPER_SENSITIVE)
owner.update_sight()
/// Signal proc to remove flash sensitivity when the eyes are removed
/datum/action/changeling/darkness_adaptation/proc/eye_removed(mob/living/source, obj/item/organ/removed, special)
/datum/status_effect/darkness_adapted/proc/nerf_eyes(obj/item/organ/eyes/eyes)
eyes.lighting_cutoff = initial(eyes.lighting_cutoff)
if(last_eye_strength >= LIGHTING_CUTOFF_MEDIUM)
eyes.flash_protect = max(eyes.flash_protect -= 1, FLASH_PROTECTION_HYPER_SENSITIVE)
owner.update_sight()
/datum/status_effect/darkness_adapted/proc/update_invis()
var/new_alpha = get_alpha()
if(last_alpha == new_alpha)
return
animate(owner, alpha = new_alpha, time = 0.5 SECONDS, flags = ANIMATION_PARALLEL)
if(new_alpha <= dark_color_threshold)
owner.add_atom_colour(COLOR_DARK, TEMPORARY_COLOUR_PRIORITY)
else
owner.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, COLOR_DARK)
last_alpha = new_alpha
/datum/status_effect/darkness_adapted/proc/nerf_invis()
animate(owner, alpha = 255, time = 1 SECONDS)
owner.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, COLOR_DARK)
/datum/status_effect/darkness_adapted/proc/on_move(...)
SIGNAL_HANDLER
var/obj/item/organ/eyes/eyes = removed
if(!istype(eyes))
update_invis()
update_eye_status()
COOLDOWN_START(src, skip_tick_update, 0.5 SECONDS)
/datum/status_effect/darkness_adapted/proc/eye_implanted(mob/living/source, obj/item/organ/gained, special)
SIGNAL_HANDLER
if(istype(gained, /obj/item/organ/eyes))
update_eye_status(gained)
/datum/status_effect/darkness_adapted/proc/eye_removed(mob/living/source, obj/item/organ/removed, special)
SIGNAL_HANDLER
if(istype(removed, /obj/item/organ/eyes) && last_eye_strength > 0)
nerf_eyes(removed)
last_eye_strength = 0
/datum/status_effect/darkness_adapted/tick(seconds_between_ticks)
if(!COOLDOWN_FINISHED(src, skip_tick_update))
return
eyes.flash_protect = initial(eyes.flash_protect)
// We don't need to bother about removing or adding night vision, fortunately, because they can't see anyways
update_eye_status()
update_invis()