Files
Bubberstation/code/datums/components/mob_chain.dm
SkyratBot df08743c14 [MIRROR] Separates COMSIG_LIVING_ADJUST_DAMAGE into 6 different signals [MDB IGNORE] (#24179)
* Separates COMSIG_LIVING_ADJUST_DAMAGE into 6 different signals (#78814)

## About The Pull Request

As potato requested after the merge of #78744 these should have separate
concerns even if they are very similar.
In addition I improved how mob chains react to glide size changes, and
swapped PAIs to using these signals instead of an override of
`onBruteLoss` just because I can.

I tested it but the unit tests will catch me if i did it wrong

## Changelog

🆑
fix: Flesh Worms will move smoothly more consistently.
/🆑

* Separates COMSIG_LIVING_ADJUST_DAMAGE into 6 different signals

---------

Co-authored-by: Jacquerel <hnevard@gmail.com>
2023-10-07 17:30:09 -04:00

225 lines
7.7 KiB
Plaintext

/**
* Component allowing you to create a linked list of mobs.
* These mobs will follow each other and attack as one, as well as sharing damage taken.
*/
/datum/component/mob_chain
/// If true then damage we take is passed backwards along the line
var/pass_damage_back
/// If true then we will set our icon state based on line position
var/vary_icon_state
/// Mob in front of us in the chain
var/mob/living/front
/// Mob behind us in the chain
var/mob/living/back
/datum/component/mob_chain/Initialize(mob/living/front, pass_damage_back = TRUE, vary_icon_state = FALSE)
. = ..()
if (!isliving(parent))
return COMPONENT_INCOMPATIBLE
src.front = front
src.pass_damage_back = pass_damage_back
src.vary_icon_state = vary_icon_state
if (!isnull(front))
SEND_SIGNAL(front, COMSIG_MOB_GAINED_CHAIN_TAIL, parent)
parent.AddComponent(/datum/component/leash, owner = front, distance = 1) // Handles catching up gracefully
var/mob/living/living_parent = parent
living_parent.set_glide_size(front.glide_size)
/datum/component/mob_chain/Destroy(force, silent)
if (!isnull(front))
SEND_SIGNAL(front, COMSIG_MOB_LOST_CHAIN_TAIL, parent)
front = null
back = null
return ..()
/datum/component/mob_chain/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOB_GAINED_CHAIN_TAIL, PROC_REF(on_gained_tail))
RegisterSignal(parent, COMSIG_MOB_LOST_CHAIN_TAIL, PROC_REF(on_lost_tail))
RegisterSignal(parent, COMSIG_MOB_CHAIN_CONTRACT, PROC_REF(on_contracted))
RegisterSignal(parent, COMSIG_LIVING_DEATH, PROC_REF(on_death))
RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(on_deletion))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
RegisterSignal(parent, COMSIG_ATOM_CAN_BE_PULLED, PROC_REF(on_pulled))
RegisterSignals(parent, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HUMAN_EARLY_UNARMED_ATTACK, COMSIG_MOB_ATTACK_RANGED), PROC_REF(on_attack))
RegisterSignal(parent, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, PROC_REF(on_glide_size_changed))
if (vary_icon_state)
RegisterSignal(parent, COMSIG_ATOM_UPDATE_ICON_STATE, PROC_REF(on_update_icon_state))
update_mob_appearance()
if (pass_damage_back)
RegisterSignals(parent, COMSIG_LIVING_ADJUST_STANDARD_DAMAGE_TYPES, PROC_REF(on_adjust_damage))
RegisterSignal(parent, COMSIG_LIVING_ADJUST_STAMINA_DAMAGE, PROC_REF(on_adjust_stamina))
RegisterSignal(parent, COMSIG_CARBON_LIMB_DAMAGED, PROC_REF(on_limb_damage))
var/datum/action/cooldown/worm_contract/shrink = new(parent)
shrink.Grant(parent)
/datum/component/mob_chain/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_ATOM_CAN_BE_PULLED,
COMSIG_ATOM_UPDATE_ICON_STATE,
COMSIG_CARBON_LIMB_DAMAGED,
COMSIG_HUMAN_EARLY_UNARMED_ATTACK,
COMSIG_LIVING_ADJUST_BRUTE_DAMAGE,
COMSIG_LIVING_ADJUST_BURN_DAMAGE,
COMSIG_LIVING_ADJUST_CLONE_DAMAGE,
COMSIG_LIVING_DEATH,
COMSIG_LIVING_ADJUST_OXY_DAMAGE,
COMSIG_LIVING_ADJUST_STAMINA_DAMAGE,
COMSIG_LIVING_ADJUST_TOX_DAMAGE,
COMSIG_LIVING_UNARMED_ATTACK,
COMSIG_MOB_ATTACK_RANGED,
COMSIG_MOB_CHAIN_CONTRACT,
COMSIG_MOB_GAINED_CHAIN_TAIL,
COMSIG_MOB_LOST_CHAIN_TAIL,
COMSIG_MOVABLE_MOVED,
COMSIG_MOVABLE_UPDATE_GLIDE_SIZE,
COMSIG_QDELETING,
))
qdel(parent.GetComponent(/datum/component/leash))
var/mob/living/living_parent = parent
var/datum/action/cooldown/worm_contract/shrink = locate() in living_parent.actions
qdel(shrink)
/// Update how we look
/datum/component/mob_chain/proc/update_mob_appearance()
if (!vary_icon_state)
return
var/mob/living/body = parent
body.update_appearance(UPDATE_ICON_STATE)
/// Called when something sets us as IT'S front
/datum/component/mob_chain/proc/on_gained_tail(mob/living/body, mob/living/tail)
SIGNAL_HANDLER
back = tail
update_mob_appearance()
/// Called when our tail loses its chain component
/datum/component/mob_chain/proc/on_lost_tail()
SIGNAL_HANDLER
back = null
update_mob_appearance()
/// Called when our tail gets pulled up to our body
/datum/component/mob_chain/proc/on_contracted(mob/living/shrinking)
SIGNAL_HANDLER
if (isnull(back))
return
back.forceMove(shrinking.loc)
var/datum/action/cooldown/worm_contract/shrink = locate() in back.actions
if (isnull(shrink))
return
INVOKE_ASYNC(shrink, TYPE_PROC_REF(/datum/action, Trigger))
/// If we die so does the guy behind us, then stop following the leader
/datum/component/mob_chain/proc/on_death()
SIGNAL_HANDLER
back?.death()
qdel(src)
/// If we get deleted so does the guy behind us
/datum/component/mob_chain/proc/on_deletion()
SIGNAL_HANDLER
QDEL_NULL(back)
front?.update_appearance(UPDATE_ICON)
/// Pull our tail behind us when we move
/datum/component/mob_chain/proc/on_moved(mob/living/mover, turf/old_loc)
SIGNAL_HANDLER
if(isnull(back) || back.loc == old_loc)
return
back.Move(old_loc)
/// Update our visuals based on if we have someone in front and behind
/datum/component/mob_chain/proc/on_update_icon_state(mob/living/our_mob)
SIGNAL_HANDLER
var/current_icon_state = our_mob.base_icon_state
if(isnull(front))
current_icon_state = "[current_icon_state]_start"
else if(isnull(back))
current_icon_state = "[current_icon_state]_end"
else
current_icon_state = "[current_icon_state]_mid"
our_mob.icon_state = current_icon_state
if (isanimal_or_basicmob(our_mob))
var/mob/living/basic/basic_parent = our_mob
basic_parent.icon_living = current_icon_state
/// Do not allow someone to be pulled out of the chain
/datum/component/mob_chain/proc/on_pulled(mob/living/our_mob)
SIGNAL_HANDLER
if (!isnull(front))
return COMSIG_ATOM_CANT_PULL
/// Tell our tail to attack too
/datum/component/mob_chain/proc/on_attack(mob/living/our_mob, atom/target)
SIGNAL_HANDLER
if (target == back || target == front)
return COMPONENT_CANCEL_ATTACK_CHAIN
if (isnull(back) || QDELETED(target))
return
INVOKE_ASYNC(back, TYPE_PROC_REF(/mob, ClickOn), target)
/// Maintain glide size backwards
/datum/component/mob_chain/proc/on_glide_size_changed(mob/living/our_mob, new_size)
SIGNAL_HANDLER
back?.set_glide_size(new_size)
/// On gain or lose stamina, adjust our tail too
/datum/component/mob_chain/proc/on_adjust_stamina(mob/living/our_mob, type, amount, forced)
SIGNAL_HANDLER
if (forced)
return
back?.adjustStaminaLoss(amount, forced = forced)
/// On damage or heal, affect our furthest segment
/datum/component/mob_chain/proc/on_adjust_damage(mob/living/our_mob, type, amount, forced)
SIGNAL_HANDLER
if (isnull(back) || forced)
return
switch (type)
if(BRUTE)
back.adjustBruteLoss(amount, forced = forced)
if(BURN)
back.adjustFireLoss(amount, forced = forced)
if(TOX)
back.adjustToxLoss(amount, forced = forced)
if(OXY) // If all segments are suffocating we pile damage backwards until our ass starts dying forwards
back.adjustOxyLoss(amount, forced = forced)
if(CLONE)
back.adjustCloneLoss(amount, forced = forced)
return COMPONENT_IGNORE_CHANGE
/// Special handling for if damage is delegated to a mob's limbs instead of its overall damage
/datum/component/mob_chain/proc/on_limb_damage(mob/living/our_mob, limb, brute, burn)
SIGNAL_HANDLER
if (isnull(back))
return
if (brute != 0)
back.adjustBruteLoss(brute, updating_health = FALSE)
if (burn != 0)
back.adjustFireLoss(burn, updating_health = FALSE)
if (brute != 0 || burn != 0)
back.updatehealth()
return COMPONENT_PREVENT_LIMB_DAMAGE
/**
* Shrink the chain of mobs into one tile.
*/
/datum/action/cooldown/worm_contract
name = "Force Contract"
desc = "Forces your body to contract onto a single tile."
background_icon_state = "bg_heretic"
overlay_icon_state = "bg_heretic_border"
button_icon = 'icons/mob/actions/actions_ecult.dmi'
button_icon_state = "worm_contract"
cooldown_time = 30 SECONDS
melee_cooldown_time = 0 SECONDS
/datum/action/cooldown/worm_contract/Activate(atom/target)
SEND_SIGNAL(owner, COMSIG_MOB_CHAIN_CONTRACT)
StartCooldown()