[MIRROR] Refactored the on_hit_effect into an element. (#29028)

* Refactored the on_hit_effect into an element. (#85034)

## About The Pull Request
on_hit_effect is now an element, which uses signals instead of
callbacks.

## Why It's Good For The Game
Easy peasy almost lemon squeezy. It's a good thing that I had made the
AddElementTrait proc so we can manage multiple sources of a common
element. It's much better to use signals than callbacks for components
and datums with several possible sources, kinda like how it's been donw
with the relay_attackers element.

## Changelog
N/A

* Refactored the on_hit_effect into an element.

---------

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
This commit is contained in:
SkyratBot
2024-07-24 22:37:21 +05:30
committed by GitHub
co-authored by Ghom
parent b9bf9f040e
commit bd96f4221a
9 changed files with 116 additions and 135 deletions
@@ -45,3 +45,6 @@
///from /datum/bank_account/pay_debt(), after a portion or all the debt has been paid.
#define COMSIG_BANK_ACCOUNT_DEBT_PAID "bank_account_debt_paid"
///from /datum/component/on_hit_effect/send_signal(): (user, target, hit_zone)
#define COMSIG_ON_HIT_EFFECT "comsig_on_hit_effect"
+2
View File
@@ -701,6 +701,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_HONKSPAMMING "trait_honkspamming"
/// Required by the waddling element since there are multiple sources of it.
#define TRAIT_WADDLING "trait_waddling"
/// Required by the on_hit_effect element, which is in turn added by other elements.
#define TRAIT_ON_HIT_EFFECT "trait_on_hit_effect"
///Used for managing KEEP_TOGETHER in [/atom/var/appearance_flags]
#define TRAIT_KEEP_TOGETHER "keep-together"
+1
View File
@@ -59,6 +59,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NOT_ENGRAVABLE" = TRAIT_NOT_ENGRAVABLE,
"TRAIT_SPELLS_TRANSFER_TO_LOC" = TRAIT_SPELLS_TRANSFER_TO_LOC,
"TRAIT_ODD_CUSTOMIZABLE_FOOD_INGREDIENT" = TRAIT_ODD_CUSTOMIZABLE_FOOD_INGREDIENT,
"TRAIT_ON_HIT_EFFECT" = TRAIT_ON_HIT_EFFECT,
"TRAIT_RUNECHAT_HIDDEN" = TRAIT_RUNECHAT_HIDDEN,
"TRAIT_SECLUDED_LOCATION" = TRAIT_SECLUDED_LOCATION,
"TRAIT_SNOWSTORM_IMMUNE" = TRAIT_SNOWSTORM_IMMUNE,
-93
View File
@@ -1,93 +0,0 @@
/**
* ## On Hit Effect Component!
*
* Component for other elements/components to rely on for on-hit effects without duplicating the on-hit code.
* See Lifesteal, or bane for examples.
*
* THIS COULD EASILY SUPPORT COMPONENT_DUPE_ALLOWED but the getcomponent makes it throw errors. if you can figure that out feel free to readd the dupe types
*/
/datum/component/on_hit_effect
///callback used by other components to apply effects
var/datum/callback/on_hit_callback
///callback optionally used for more checks
var/datum/callback/extra_check_callback
///optionally should we also apply the effect if thrown at something?
var/thrown_effect
/datum/component/on_hit_effect/Initialize(on_hit_callback, extra_check_callback, thrown_effect = FALSE)
src.on_hit_callback = on_hit_callback
src.extra_check_callback = extra_check_callback
if(!(ismachinery(parent) || isstructure(parent) || isgun(parent) || isprojectilespell(parent) || isitem(parent) || isanimal_or_basicmob(parent) || isprojectile(parent)))
return ELEMENT_INCOMPATIBLE
src.thrown_effect = thrown_effect
/datum/component/on_hit_effect/Destroy(force)
on_hit_callback = null
extra_check_callback = null
return ..()
/datum/component/on_hit_effect/RegisterWithParent()
if(ismachinery(parent) || isstructure(parent) || isgun(parent) || isprojectilespell(parent))
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, PROC_REF(on_projectile_hit))
else if(isitem(parent))
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, PROC_REF(item_afterattack))
else if(isanimal_or_basicmob(parent))
RegisterSignal(parent, COMSIG_HOSTILE_POST_ATTACKINGTARGET, PROC_REF(hostile_attackingtarget))
else if(isprojectile(parent))
RegisterSignal(parent, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(on_projectile_self_hit))
if(thrown_effect)
RegisterSignal(parent, COMSIG_MOVABLE_IMPACT, PROC_REF(on_thrown_hit))
/datum/component/on_hit_effect/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_PROJECTILE_ON_HIT,
COMSIG_ITEM_AFTERATTACK,
COMSIG_HOSTILE_POST_ATTACKINGTARGET,
COMSIG_PROJECTILE_SELF_ON_HIT,
COMSIG_MOVABLE_IMPACT,
))
/datum/component/on_hit_effect/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
SIGNAL_HANDLER
if(!proximity_flag)
return
if(extra_check_callback)
if(!extra_check_callback.Invoke(user, target, source))
return
on_hit_callback.Invoke(source, user, target, user.zone_selected)
/datum/component/on_hit_effect/proc/hostile_attackingtarget(mob/living/attacker, atom/target, success)
SIGNAL_HANDLER
if(!success)
return
if(extra_check_callback)
if(!extra_check_callback.Invoke(attacker, target))
return
on_hit_callback.Invoke(attacker, attacker, target, attacker.zone_selected)
/datum/component/on_hit_effect/proc/on_projectile_hit(datum/fired_from, atom/movable/firer, atom/target, angle, body_zone)
SIGNAL_HANDLER
if(extra_check_callback)
if(!extra_check_callback.Invoke(firer, target))
return
on_hit_callback.Invoke(fired_from, firer, target, body_zone)
/datum/component/on_hit_effect/proc/on_projectile_self_hit(datum/source, mob/firer, atom/target, angle, body_zone)
SIGNAL_HANDLER
if(extra_check_callback)
if(!extra_check_callback.Invoke(firer, target))
return
on_hit_callback.Invoke(source, firer, target, body_zone)
/datum/component/on_hit_effect/proc/on_thrown_hit(datum/source, atom/hit_atom, datum/thrownthing/throwingdatum)
SIGNAL_HANDLER
if(extra_check_callback && !extra_check_callback.Invoke(source, hit_atom))
return
on_hit_callback.Invoke(source, source, hit_atom, null)
+27 -28
View File
@@ -27,40 +27,20 @@
src.added_damage = added_damage
src.requires_combat_mode = requires_combat_mode
src.mob_biotypes = mob_biotypes
target.AddComponent(/datum/component/on_hit_effect, CALLBACK(src, PROC_REF(do_bane)), CALLBACK(src, PROC_REF(check_bane)))
target.AddElementTrait(TRAIT_ON_HIT_EFFECT, REF(src), /datum/element/on_hit_effect)
RegisterSignal(target, COMSIG_ON_HIT_EFFECT, PROC_REF(do_bane))
/datum/element/bane/Detach(datum/target)
qdel(target.GetComponent(/datum/component/on_hit_effect))
/datum/element/bane/Detach(datum/source)
UnregisterSignal(source, COMSIG_ON_HIT_EFFECT)
REMOVE_TRAIT(source, TRAIT_ON_HIT_EFFECT, REF(src))
return ..()
/datum/element/bane/proc/check_bane(bane_applier, target, bane_weapon)
if(!check_biotype_path(bane_applier, target))
/datum/element/bane/proc/do_bane(datum/element_owner, mob/living/bane_applier, mob/living/baned_target, hit_zone, throw_hit)
if(!check_biotype_path(bane_applier, baned_target))
return
var/atom/movable/atom_owner = bane_weapon
if(SEND_SIGNAL(atom_owner, COMSIG_OBJECT_PRE_BANING, target) & COMPONENT_CANCEL_BANING)
if(SEND_SIGNAL(element_owner, COMSIG_OBJECT_PRE_BANING, baned_target) & COMPONENT_CANCEL_BANING)
return
return TRUE
/**
* Checks typepaths and the mob's biotype, returning TRUE if correct and FALSE if wrong.
* Additionally checks if combat mode is required, and if so whether it's enabled or not.
*/
/datum/element/bane/proc/check_biotype_path(mob/living/bane_applier, atom/target)
if(!isliving(target))
return FALSE
var/mob/living/living_target = target
if(bane_applier)
if(requires_combat_mode && !bane_applier.combat_mode)
return FALSE
var/is_correct_biotype = living_target.mob_biotypes & mob_biotypes
if(mob_biotypes && !(is_correct_biotype))
return FALSE
if(ispath(target_type, /mob/living))
return istype(living_target, target_type)
else //species type
return is_species(living_target, target_type)
/datum/element/bane/proc/do_bane(datum/element_owner, mob/living/bane_applier, mob/living/baned_target, hit_zone)
var/force_boosted
var/applied_dam_type
@@ -91,3 +71,22 @@
baned_target.apply_damage(extra_damage, applied_dam_type, hit_zone)
SEND_SIGNAL(baned_target, COMSIG_LIVING_BANED, bane_applier, baned_target) // for extra effects when baned.
SEND_SIGNAL(element_owner, COMSIG_OBJECT_ON_BANING, baned_target)
/**
* Checks typepaths and the mob's biotype, returning TRUE if correct and FALSE if wrong.
* Additionally checks if combat mode is required, and if so whether it's enabled or not.
*/
/datum/element/bane/proc/check_biotype_path(mob/living/bane_applier, atom/target)
if(!isliving(target))
return FALSE
var/mob/living/living_target = target
if(bane_applier)
if(requires_combat_mode && !bane_applier.combat_mode)
return FALSE
var/is_correct_biotype = living_target.mob_biotypes & mob_biotypes
if(mob_biotypes && !(is_correct_biotype))
return FALSE
if(ispath(target_type, /mob/living))
return istype(living_target, target_type)
else //species type
return is_species(living_target, target_type)
+7 -4
View File
@@ -13,13 +13,16 @@
/datum/element/lifesteal/Attach(datum/target, flat_heal = 10)
. = ..()
src.flat_heal = flat_heal
target.AddComponent(/datum/component/on_hit_effect, CALLBACK(src, PROC_REF(do_lifesteal)))
target.AddElementTrait(TRAIT_ON_HIT_EFFECT, REF(src), /datum/element/on_hit_effect)
RegisterSignal(target, COMSIG_ON_HIT_EFFECT, PROC_REF(do_lifesteal))
/datum/element/lifesteal/Detach(datum/target)
qdel(target.GetComponent(/datum/component/on_hit_effect))
/datum/element/lifesteal/Detach(datum/source)
UnregisterSignal(source, COMSIG_ON_HIT_EFFECT)
REMOVE_TRAIT(source, TRAIT_ON_HIT_EFFECT, REF(src))
return ..()
/datum/element/lifesteal/proc/do_lifesteal(datum/element_owner, atom/heal_target, atom/damage_target, hit_zone)
/datum/element/lifesteal/proc/do_lifesteal(datum/source, atom/heal_target, atom/damage_target, hit_zone, throw_hit)
SIGNAL_HANDLER
if(isliving(heal_target) && isliving(damage_target))
var/mob/living/healing = heal_target
var/mob/living/damaging = damage_target
+65
View File
@@ -0,0 +1,65 @@
/**
* ## On Hit Effect Component!
*
* Component for other elements/components to rely on for on-hit effects without duplicating the on-hit code.
* See Lifesteal, or bane for examples.
*/
/datum/element/on_hit_effect
/datum/element/on_hit_effect/Attach(datum/target)
. = ..()
if(!HAS_TRAIT(target, TRAIT_ON_HIT_EFFECT))
stack_trace("[type] added to [target] without adding TRAIT_ON_HIT_EFFECT first. Please use AddElementTrait instead.")
if(ismachinery(target) || isstructure(target) || isgun(target) || isprojectilespell(target))
RegisterSignal(target, COMSIG_PROJECTILE_ON_HIT, PROC_REF(on_projectile_hit))
else if(isitem(target))
RegisterSignal(target, COMSIG_ITEM_AFTERATTACK, PROC_REF(item_afterattack))
else if(isanimal_or_basicmob(target))
RegisterSignal(target, COMSIG_HOSTILE_POST_ATTACKINGTARGET, PROC_REF(hostile_attackingtarget))
else if(isprojectile(target))
RegisterSignal(target, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(on_projectile_self_hit))
else
return ELEMENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_MOVABLE_IMPACT, PROC_REF(on_thrown_hit))
/datum/element/on_hit_effect/Detach(datum/source)
UnregisterSignal(source, list(
COMSIG_PROJECTILE_ON_HIT,
COMSIG_ITEM_AFTERATTACK,
COMSIG_HOSTILE_POST_ATTACKINGTARGET,
COMSIG_PROJECTILE_SELF_ON_HIT,
COMSIG_MOVABLE_IMPACT,
))
return ..()
/datum/element/on_hit_effect/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
SIGNAL_HANDLER
if(!proximity_flag)
return
on_hit(source, user, target, user.zone_selected)
/datum/element/on_hit_effect/proc/hostile_attackingtarget(mob/living/attacker, atom/target, success)
SIGNAL_HANDLER
if(!success)
return
on_hit(attacker, attacker, target, attacker.zone_selected)
/datum/element/on_hit_effect/proc/on_projectile_hit(datum/fired_from, atom/movable/firer, atom/target, angle, body_zone)
SIGNAL_HANDLER
on_hit(fired_from, firer, target, body_zone)
/datum/element/on_hit_effect/proc/on_projectile_self_hit(datum/source, mob/firer, atom/target, angle, body_zone)
SIGNAL_HANDLER
on_hit(source, firer, target, body_zone)
/datum/element/on_hit_effect/proc/on_thrown_hit(datum/source, atom/hit_atom, datum/thrownthing/throwingdatum)
SIGNAL_HANDLER
on_hit(source, source, hit_atom, null, TRUE)
/datum/element/on_hit_effect/proc/on_hit(atom/source, atom/movable/attacker, atom/target, body_zone, throw_hit = FALSE)
SEND_SIGNAL(source, COMSIG_ON_HIT_EFFECT, attacker, target, body_zone, throw_hit)
+10 -9
View File
@@ -12,24 +12,25 @@
var/injection_flags
///How much of the reagent added. if it's a list, it'll pick a range with the range being list(lower_value, upper_value)
var/list/amount_added
///Does this trigger when thrown?
var/thrown_effect = FALSE
/datum/element/venomous/Attach(datum/target, poison_type, amount_added, injection_flags = NONE, thrown_effect = FALSE)
. = ..()
src.poison_type = poison_type
src.amount_added = amount_added
src.injection_flags = injection_flags
target.AddComponent(\
/datum/component/on_hit_effect,\
on_hit_callback = CALLBACK(src, PROC_REF(do_venom)),\
thrown_effect = thrown_effect,\
)
src.thrown_effect = thrown_effect
target.AddElementTrait(TRAIT_ON_HIT_EFFECT, REF(src), /datum/element/on_hit_effect)
RegisterSignal(target, COMSIG_ON_HIT_EFFECT, PROC_REF(do_venom))
/datum/element/venomous/Detach(datum/target)
qdel(target.GetComponent(/datum/component/on_hit_effect))
/datum/element/venomous/Detach(datum/source)
UnregisterSignal(source, COMSIG_ON_HIT_EFFECT)
REMOVE_TRAIT(source, TRAIT_ON_HIT_EFFECT, REF(src))
return ..()
/datum/element/venomous/proc/do_venom(datum/element_owner, atom/venom_source, mob/living/target, hit_zone)
if(!istype(target))
/datum/element/venomous/proc/do_venom(datum/element_owner, atom/venom_source, mob/living/target, hit_zone, throw_hit)
if((throw_hit && !thrown_effect) || !istype(target))
return
if(target.stat == DEAD)
return
+1 -1
View File
@@ -1249,7 +1249,6 @@
#include "code\datums\components\nuclear_bomb_operator.dm"
#include "code\datums\components\object_possession.dm"
#include "code\datums\components\omen.dm"
#include "code\datums\components\on_hit_effect.dm"
#include "code\datums\components\onwear_mood.dm"
#include "code\datums\components\orbiter.dm"
#include "code\datums\components\overlay_lighting.dm"
@@ -1594,6 +1593,7 @@
#include "code\datums\elements\noisy_movement.dm"
#include "code\datums\elements\noticable_organ.dm"
#include "code\datums\elements\obj_regen.dm"
#include "code\datums\elements\on_hit_effect.dm"
#include "code\datums\elements\only_pull_living.dm"
#include "code\datums\elements\openspace_item_click_handler.dm"
#include "code\datums\elements\ore_collecting.dm"