mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-23 13:10:02 +01:00
[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:
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user