Files
Bubberstation/code/datums/status_effects/_status_effect_helpers.dm
MrMelbert c3a1f21c1a Converts blindness and nearsightedness to status effects, scratches some VERY dumb blindness handling that resulted in mobs becoming "incurably" blind (#72267)
## About The Pull Request

- Nearsighted is now a grouped status effect.
- Blindness is now a grouped status effect.
   - Eye handling of blindness has improved. 
- When eyes are removed, they now cause you to become blind, rather than
handling it in `update_tint`.
- Being ahealed no longer blinds you for one tick, meaning that black
overlay on aheal is gone.
- Temporary Blindness is now a status effect.
- Both Nearsightedness and Blindness have been exorcised from mob vars
and life chains. This means that we've finally cut 2 procs from life,
`handle_status_effect` and `handle_traits`, and moved both to event
based processing. Wooo optimizations.
- Swapped pacifism status effect to use apply and set helpers. 
- Removed an unused admin toggle that disabled welding helmet tint but
also tint from every clothing item and also blindness from losing your
eyes.
- Clothes now generally all blind their mob more consistently.
- Oculine, eye surgery, and sensory restoration are now no longer the
only way to fix blindness from eye damage. If your eyes are healed
through any other means, it will also heal your blindness.
- Some things that made you blind, such as ling blind sting, no longer
just flat made you blind from eye damage forever. They now cause eye
damage directly, which in turn makes you blind from eye damage, as
expected.
- Pacifists can't eyestab anymore. Eyestabs now have a limit on the
amount of blur applied.
- Refactored some `is_x_covered` procs to accept flags rather than have
a lot of arguments for some silly reason.
- Unit tests for blindness. 

## Why It's Good For The Game

Blindness was exceptionally poorly handled prior, primarily due to the
fact that it was tied to the mob instead of separated out

On top of that the system put a LOT of faith in proper handling of
blindness on the coder's end which was misplaced evidently. Many places
didn't update or handle blindness correctly, or just let people
perma-blind.

Deferring it to a status effect improves this a lot

## Changelog

🆑 Melbert
refactor: Refactored blindness and nearsightedness. Important to note is
that all mobs are naturally blind until their eyes are actually created.
refactor: Refactored "is covered" procs
fix: Less sources of blindness now cause permanent blindness. Includes
the "Blind" Spell and "Blind Sting" from changelings.
admin: Ahealing someone no longer flashes the blind overlay for 1 tick.
admin: I removed an unused (sort of) inaccessible admin verb that
allowed you to toggle the tint from all welding helmets (and clothing)
(and lack of eyes) in existence, let me know if you want similar back
balance: Changeling "Blind Sting" now causes eye damage (enough to
blind) rather than arbitrarily forcing blindness.
balance: Visionloss virus symptom now causes eye damage (enough to
blind) rather than arbitrarily forcing blindness.
balance: Oculine has been reworked slightly. Prior, Oculine arbitrarily
healed blindness and nearsightedness from eye damage reagrdless of how
damaged the eyes were, and applied blur on success. Now, Oculine just
heals eye damage, and blindness / nearsightedness is restored in the
process. There is now a probability every tick that eye blur is applied
based on how pure the oculine is while healing very damaged eyes.
balance: Pacifists can no longer eyestab.
balance: Any clothing item that covers your eyes contributes to getting
the bonus while sleeping, and to removing temporary blindness faster
/🆑
2023-01-15 23:01:40 -08:00

137 lines
4.7 KiB
Plaintext

// Status effect helpers for living mobs
/**
* Applies a given status effect to this mob.
*
* new_effect - TYPEPATH of a status effect to apply.
* Additional status effect arguments can be passed.
*
* Returns the instance of the created effected, if successful.
* Returns 'null' if unsuccessful.
*/
/mob/living/proc/apply_status_effect(datum/status_effect/new_effect, ...)
RETURN_TYPE(/datum/status_effect)
// The arguments we pass to the start effect. The 1st argument is this mob.
var/list/arguments = args.Copy()
arguments[1] = src
// If the status effect we're applying doesn't allow multiple effects, we need to handle it
if(initial(new_effect.status_type) != STATUS_EFFECT_MULTIPLE)
for(var/datum/status_effect/existing_effect as anything in status_effects)
if(existing_effect.id != initial(new_effect.id))
continue
switch(existing_effect.status_type)
// Multiple are allowed, continue as normal. (Not normally reachable)
if(STATUS_EFFECT_MULTIPLE)
break
// Only one is allowed of this type - early return
if(STATUS_EFFECT_UNIQUE)
return
// Replace the existing instance (deletes it).
if(STATUS_EFFECT_REPLACE)
existing_effect.be_replaced()
// Refresh the existing type, then early return
if(STATUS_EFFECT_REFRESH)
existing_effect.refresh(arglist(arguments))
return
// Create the status effect with our mob + our arguments
var/datum/status_effect/new_instance = new new_effect(arguments)
if(!QDELETED(new_instance))
return new_instance
/**
* Removes all instances of a given status effect from this mob
*
* removed_effect - TYPEPATH of a status effect to remove.
* Additional status effect arguments can be passed - these are passed into before_remove.
*
* Returns TRUE if at least one was removed.
*/
/mob/living/proc/remove_status_effect(datum/status_effect/removed_effect, ...)
var/list/arguments = args.Copy(2)
. = FALSE
for(var/datum/status_effect/existing_effect as anything in status_effects)
if(existing_effect.id == initial(removed_effect.id) && existing_effect.before_remove(arguments))
qdel(existing_effect)
. = TRUE
return .
/**
* Checks if this mob has a status effect that shares the passed effect's ID
*
* checked_effect - TYPEPATH of a status effect to check for. Checks for its ID, not it's typepath
*
* Returns an instance of a status effect, or NULL if none were found.
*/
/mob/proc/has_status_effect(datum/status_effect/checked_effect)
// Yes I'm being cringe and putting this on the mob level even though status effects only apply to the living level
// There's quite a few places (namely examine and, bleh, cult code) where it's easier to not need to cast to living before checking
// for an effect such as blindness
return null
/mob/living/has_status_effect(datum/status_effect/checked_effect)
RETURN_TYPE(/datum/status_effect)
for(var/datum/status_effect/present_effect as anything in status_effects)
if(present_effect.id == initial(checked_effect.id))
return present_effect
return null
/**
* Checks if this mob has a status effect that shares the passed effect's ID
* and has the passed sources are in its list of sources (ONLY works for grouped efects!)
*
* checked_effect - TYPEPATH of a status effect to check for. Checks for its ID, not it's typepath
*
* Returns an instance of a status effect, or NULL if none were found.
*/
/mob/proc/has_status_effect_from_source(datum/status_effect/grouped/checked_effect, sources)
// See [/mob/proc/has_status_effect] for reason behind having this on the mob level
return null
/mob/living/has_status_effect_from_source(datum/status_effect/grouped/checked_effect, sources)
RETURN_TYPE(/datum/status_effect)
if(!ispath(checked_effect))
CRASH("has_status_effect_from_source passed with an improper status effect path.")
if(!islist(sources))
sources = list(sources)
for(var/datum/status_effect/grouped/present_effect in status_effects)
if(present_effect.id != initial(checked_effect.id))
continue
var/list/matching_sources = present_effect.sources & sources
if(length(matching_sources))
return present_effect
return null
/**
* Returns a list of all status effects that share the passed effect type's ID
*
* checked_effect - TYPEPATH of a status effect to check for. Checks for its ID, not it's typepath
*
* Returns a list
*/
/mob/proc/has_status_effect_list(datum/status_effect/checked_effect)
// See [/mob/proc/has_status_effect] for reason behind having this on the mob level
return null
/mob/living/has_status_effect_list(datum/status_effect/checked_effect)
RETURN_TYPE(/list)
var/list/effects_found = list()
for(var/datum/status_effect/present_effect as anything in status_effects)
if(present_effect.id == initial(checked_effect.id))
effects_found += present_effect
return effects_found