Files
Bubberstation/code/datums/components/healing_touch.dm
Jacquerel 7867d14fd4 Lightgeists won't try to heal toxin damage (#73467)
## About The Pull Request

Fixes #73439
Lightgeists use `heal_overall_damage` to heal, but despite the name that
actually only heals burn, brute, and stamina damage.
It will now prompt an error message if you try to heal someone who
doesn't have any of those damage types.
Simpler mobs can't take toxin or oxyloss damage so this has no effect on
spiders (the other user of this component).

Also during my conversion I forgot to set the lightgeist healing time to
0, so I did that too.

I thought about maybe changing this so that they _can_ heal toxin damage
but they've existed without being able to do it for so long that I don't
think it would be a "bug fix" at that point, I'll maybe adjust the
component behaviour further (to use `heal_ordered_damage` instead
probably) after this month. Until then I technically can't do it without
affecting Spider balance a little bit.

## Why It's Good For The Game

Makes it a _little_ clearer why someone's health doesn't increase when
they are clicked on.
Restores lost functionality.

## Changelog

🆑
fix: Lightgeists won't try to heal any damage which isn't burn or brute,
because they can't.
fix: Lightgeists don't need to perform an action bar in order to heal
someone, which was added by mistake.
/🆑
2023-02-17 15:03:57 -07:00

139 lines
5.1 KiB
Plaintext

#define MEND_REPLACE_KEY_SOURCE "%SOURCE%"
#define MEND_REPLACE_KEY_TARGET "%TARGET%"
/**
* # Healing Touch component
*
* A mob with this component will be able to heal certain targets by attacking them.
* This intercepts the attack and starts a do_after if the target is in its allowed type list.
*/
/datum/component/healing_touch
/// How much brute damage to heal
var/heal_brute
/// How much burn damage to heal
var/heal_burn
/// How much stamina damage to heal
var/heal_stamina
/// Interaction will use this key, and be blocked while this key is in use
var/interaction_key
/// Any extra conditions which need to be true to permit healing. Returning TRUE permits the healing, FALSE or null cancels it.
var/datum/callback/extra_checks
/// Time it takes to perform the healing action
var/heal_time
/// Typecache of mobs we can heal
var/list/valid_targets_typecache
/// How targetting yourself works, expects one of HEALING_TOUCH_ANYONE, HEALING_TOUCH_NOT_SELF, or HEALING_TOUCH_SELF_ONLY
var/self_targetting
/// Text to print when action starts, replaces %SOURCE% with healer and %TARGET% with healed mob
var/action_text
/// Text to print when action completes, replaces %SOURCE% with healer and %TARGET% with healed mob
var/complete_text
/datum/component/healing_touch/Initialize(
heal_brute = 20,
heal_burn = 20,
heal_stamina = 0,
heal_time = 2 SECONDS,
interaction_key = DOAFTER_SOURCE_HEAL_TOUCH,
datum/callback/extra_checks = null,
list/valid_targets_typecache = list(),
self_targetting = HEALING_TOUCH_NOT_SELF,
action_text = "%SOURCE% begins healing %TARGET%",
complete_text = "%SOURCE% finishes healing %TARGET%",
)
if (!isliving(parent))
return COMPONENT_INCOMPATIBLE
src.heal_brute = heal_brute
src.heal_burn = heal_burn
src.heal_stamina = heal_stamina
src.heal_time = heal_time
src.interaction_key = interaction_key
src.extra_checks = extra_checks
src.valid_targets_typecache = valid_targets_typecache.Copy()
src.self_targetting = self_targetting
src.action_text = action_text
src.complete_text = complete_text
RegisterSignal(parent, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(try_healing)) // Players
RegisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(try_healing)) // NPCs
/datum/component/healing_touch/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HOSTILE_PRE_ATTACKINGTARGET))
return ..()
/datum/component/healing_touch/Destroy(force, silent)
QDEL_NULL(extra_checks)
return ..()
/// Validate our target, and interrupt the attack chain to start healing it if it is allowed
/datum/component/healing_touch/proc/try_healing(mob/living/healer, atom/target)
SIGNAL_HANDLER
if (!isliving(target))
return
if (!is_type_in_typecache(target, valid_targets_typecache))
return // Fall back to attacking it
if (extra_checks && !extra_checks.Invoke(healer, target))
return COMPONENT_CANCEL_ATTACK_CHAIN
if (DOING_INTERACTION(healer, interaction_key))
healer.balloon_alert(healer, "busy!")
return COMPONENT_CANCEL_ATTACK_CHAIN
switch (self_targetting)
if (HEALING_TOUCH_NOT_SELF)
if (target == healer)
healer.balloon_alert(healer, "can't heal yourself!")
return COMPONENT_CANCEL_ATTACK_CHAIN
if (HEALING_TOUCH_SELF_ONLY)
if (target != healer)
healer.balloon_alert(healer, "can only heal yourself!")
return COMPONENT_CANCEL_ATTACK_CHAIN
var/mob/living/living_target = target
if (living_target.health >= living_target.maxHealth)
target.balloon_alert(healer, "not hurt!")
return COMPONENT_CANCEL_ATTACK_CHAIN
if (!has_healable_damage(living_target))
target.balloon_alert(healer, "can't heal that!")
return COMPONENT_CANCEL_ATTACK_CHAIN
if (living_target.stat == DEAD)
target.balloon_alert(healer, "they're dead!")
return COMPONENT_CANCEL_ATTACK_CHAIN
INVOKE_ASYNC(src, PROC_REF(heal_target), healer, target)
return COMPONENT_CANCEL_ATTACK_CHAIN
/// Returns true if the target has a kind of damage which we can heal
/datum/component/healing_touch/proc/has_healable_damage(mob/living/target)
return (target.getBruteLoss() > 0 && heal_brute) || (target.getFireLoss() > 0 && heal_burn) || (target.getStaminaLoss() > 0 && heal_stamina)
/// Perform a do_after and then heal our target
/datum/component/healing_touch/proc/heal_target(mob/living/healer, mob/living/target)
if (action_text)
healer.visible_message(span_notice("[format_string(action_text, healer, target)]"))
if (heal_time && !do_after(healer, heal_time, target = target, interaction_key = interaction_key))
healer.balloon_alert(healer, "interrupted!")
return
if (complete_text)
healer.visible_message(span_notice("[format_string(complete_text, healer, target)]"))
target.heal_overall_damage(brute = heal_brute, burn = heal_burn, stamina = heal_stamina)
new /obj/effect/temp_visual/heal(get_turf(target), COLOR_HEALING_CYAN)
/// Reformats the passed string with the replacetext keys
/datum/component/healing_touch/proc/format_string(string, atom/source, atom/target)
var/final_message = replacetext(string, MEND_REPLACE_KEY_SOURCE, "[source]")
final_message = replacetext(final_message, MEND_REPLACE_KEY_TARGET, "[target]")
return final_message
#undef MEND_REPLACE_KEY_SOURCE
#undef MEND_REPLACE_KEY_TARGET