diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e110a110584e3..20ca21d4a9005 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -54,6 +54,7 @@ jobs:
python tools/ci/no_duplicate_definitions.py
python -m tools.ci.check_icon_conflicts
python -m tools.ci.check_icon_dupenames
+ python -m tools.ci.check_legacy_attack_chain
python -m tools.maplint.source --github
- name: Run DreamChecker
@@ -95,7 +96,14 @@ jobs:
strategy:
fail-fast: false # Let all map tests run to completion
matrix:
- maptype: ['/datum/map/boxstation', '/datum/map/deltastation', '/datum/map/metastation', '/datum/map/cerestation', '/datum/map/emeraldstation']
+ maptype:
+ [
+ '/datum/map/boxstation',
+ '/datum/map/deltastation',
+ '/datum/map/metastation',
+ '/datum/map/cerestation',
+ '/datum/map/emeraldstation',
+ ]
byondtype: ['STABLE']
services:
mariadb:
diff --git a/.github/workflows/devdocs.yml b/.github/workflows/devdocs.yml
index 7819a3690e24f..4baa9e38f51aa 100644
--- a/.github/workflows/devdocs.yml
+++ b/.github/workflows/devdocs.yml
@@ -15,7 +15,7 @@ jobs:
- name: Build docs
run: |
- python -m pip install mkdocs==1.6.0 mkdocs-material[imaging]==9.5.31 mkdocs-github-admonitions-plugin==0.0.2
+ python -m pip install mkdocs==1.6.0 mkdocs-material[imaging]==9.5.31 mkdocs-github-admonitions-plugin==0.0.2 mkdocs-glightbox==0.4.0
python -m mkdocs build
- name: Deploy docs
diff --git a/code/__DEFINES/dcs/atom_signals.dm b/code/__DEFINES/dcs/atom_signals.dm
index 9c358c6854f8e..d1dbbf2ee1ab7 100644
--- a/code/__DEFINES/dcs/atom_signals.dm
+++ b/code/__DEFINES/dcs/atom_signals.dm
@@ -8,10 +8,6 @@
// from SSatoms InitAtom - Only if the atom was not deleted or failed initialization
#define COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE "atom_init_success"
-///from base of atom/attackby(): (/obj/item, /mob/living, params)
-#define COMSIG_PARENT_ATTACKBY "atom_attackby"
-///Return this in response if you don't want afterattack to be called
- #define COMPONENT_NO_AFTERATTACK (1<<0)
///from base of atom/attack_hulk(): (/mob/living/carbon/human)
#define COMSIG_ATOM_HULK_ATTACK "hulk_attack"
///from base of atom/examine(): (examining_user, examine_list)
@@ -99,13 +95,6 @@
#define ATOM_PREHIT_SUCCESS (1<<0)
#define ATOM_PREHIT_FAILURE (1<<1)
-// Attack signals. These should share the returned flags, to standardize the attack chain.
-// The chain currently works like:
-// tool_act -> pre_attack -> target.attackby (item.attack) -> afterattack
-// You can use these signal responses to cancel the attack chain at a certain point from most attack signal types.
- /// This response cancels the attack chain entirely. If sent early, it might cause some later effects to be skipped.
- #define COMPONENT_CANCEL_ATTACK_CHAIN (1<<0)
-
/// Called from atom/Initialize() of target: (atom/target)
#define COMSIG_ATOM_INITIALIZED_ON "atom_initialized_on"
diff --git a/code/__DEFINES/dcs/attack_chain_signals.dm b/code/__DEFINES/dcs/attack_chain_signals.dm
new file mode 100644
index 0000000000000..f0d6f716b0953
--- /dev/null
+++ b/code/__DEFINES/dcs/attack_chain_signals.dm
@@ -0,0 +1,67 @@
+// MARK: Item Interactions
+
+// Return values for non-attack interactions.
+#define ITEM_INTERACT_SUCCESS (1<<0) //! Cancel the rest of the attack chain, indicating success.
+#define ITEM_INTERACT_BLOCKING (1<<1) //! Cancel the rest of the attack chain, without indicating success.
+#define ITEM_INTERACT_SKIP_TO_ATTACK (1<<2) //! Skip the rest of the interaction chain, going straight to the attack phase.
+
+/// Combination return value for any item interaction that cancels the rest of the attack chain.
+#define ITEM_INTERACT_ANY_BLOCKER (ITEM_INTERACT_SUCCESS | ITEM_INTERACT_BLOCKING)
+
+/// Sent when this atom is clicked on by a mob with an item.
+///
+/// [/atom/proc/item_interaction] -> mob/living/user, obj/item/tool, list/modifiers
+#define COMSIG_INTERACT_TARGET "interact_target"
+
+/// Sent to a mob clicking on an atom with an item.
+///
+/// [/atom/proc/item_interaction] -> atom/target, obj/item/tool, list/modifiers
+#define COMSIG_INTERACT_USER "interact_user"
+
+/// Sent to an item clicking on an atom.
+///
+/// [/atom/proc/item_interaction] -> mob/living/user, atom/target, list/modifiers
+#define COMSIG_INTERACTING "interacting"
+
+#define COMSIG_INTERACT_RANGED "interact_ranged" //! [/atom/proc/ranged_item_interaction]
+#define COMSIG_INTERACTING_RANGED "interacting_ranged" //! [/atom/proc/ranged_item_interaction]
+
+#define COMSIG_ACTIVATE_SELF "activate_self" //! [/obj/item/proc/activate_self] -> mob/user
+
+// Attack signals. These should share the returned flags, to standardize the attack chain.
+// The chain currently works like:
+// tool_act -> pre_attack -> target.attackby (item.attack) -> afterattack
+// You can use these signal responses to cancel the attack chain at a certain point from most attack signal types.
+
+// MARK: Attack Chain
+
+// Signal interceptors for short-circuiting parts of the attack chain.
+
+#define COMPONENT_CANCEL_ATTACK_CHAIN (1<<0) //! Cancel the attack chain entirely.
+#define COMPONENT_SKIP_ATTACK (1<<1) //! Skip this attack step, continuing for the next one to happen.
+#define COMPONENT_SKIP_AFTERATTACK (1<<2) //! Skip after_attacks (while allowing for e.g. attack_by).
+
+#define COMSIG_PRE_ATTACK "pre_attack" //! [/obj/item/proc/pre_attack] -> atom/target, mob/user, params
+
+#define COMSIG_ATTACK "attack" //! [/obj/item/proc/attack] -> mob/living/target, mob/living/user
+#define COMSIG_ATTACK_OBJ "attack_obj" //! [/obj/item/proc/attack_obj] -> obj/attacked, mob/user
+#define COMSIG_ATTACK_OBJ_LIVING "attack_obj_living" //! [/obj/item/proc/attack_obj] -> obj/attacked
+#define COMSIG_ATTACK_BY "attack_by" //! [/atom/proc/attackby] -> obj/item/weapon, mob/living/user, params
+
+#define COMSIG_AFTER_ATTACK "item_after_attack" //! [/obj/item/proc/afterattack] -> atom/target, mob/user, params
+#define COMSIG_AFTER_ATTACKED_BY "after_attacked_by" //! [/obj/item/proc/afterattack] -> obj/item/weapon, mob/user, proximity_flag, params
+
+// Return values for directing the control of the attack chain. Distinct from
+// signal interceptors because they're not meant to be combined, and to mesh better with
+// historical use of return values in attack chain procs.
+
+#define CONTINUE_ATTACK 0 //! Continue the attack chain, i.e. allow other signals to respond.
+#define FINISH_ATTACK 1 //! Do not continue the attack chain.
+
+// Legacy-only, do not use in new code
+
+#define COMSIG_TOOL_ATTACK "tool_attack" //! [/obj/item/proc/tool_attack_chain] -> atom/tool, mob/user
+#define COMPONENT_NO_ATTACK (1<<0)
+#define COMPONENT_NO_INTERACT (1<<0)
+#define COMPONENT_NO_ATTACK_OBJ (1<<0)
+#define COMPONENT_CANCEL_TOOLACT (1<<0)
diff --git a/code/__DEFINES/dcs/item_signals.dm b/code/__DEFINES/dcs/item_signals.dm
index 475c33a538c45..922682352c742 100644
--- a/code/__DEFINES/dcs/item_signals.dm
+++ b/code/__DEFINES/dcs/item_signals.dm
@@ -6,21 +6,7 @@
// /obj/item
-///from base of obj/item/attack(): (/mob/living/target, /mob/living/user)
-#define COMSIG_ITEM_ATTACK "item_attack"
-///from base of obj/item/attack_self(): (/mob)
-#define COMSIG_ITEM_ATTACK_SELF "item_attack_self"
- #define COMPONENT_NO_INTERACT (1<<0)
-///from base of obj/item/attack_obj(): (/obj, /mob)
-#define COMSIG_ITEM_ATTACK_OBJ "item_attack_obj"
- #define COMPONENT_NO_ATTACK_OBJ (1<<0)
-///from base of obj/item/pre_attack(): (atom/target, mob/user, params)
-#define COMSIG_ITEM_PRE_ATTACK "item_pre_attack"
- #define COMPONENT_NO_ATTACK (1<<0)
-///from base of obj/item/pre_attack(): (atom/target, mob/user, params)
-#define COMSIG_ITEM_BEING_ATTACKED "item_being_attacked"
-///from base of obj/item/afterattack(): (atom/target, mob/user, params)
-#define COMSIG_ITEM_AFTERATTACK "item_afterattack"
+
///called on [/obj/item] before unequip from base of [/mob/proc/unEquip]: (force, atom/newloc, no_move, invdrop, silent)
#define COMSIG_ITEM_PRE_UNEQUIP "item_pre_unequip"
///only the pre unequip can be cancelled
@@ -41,9 +27,6 @@
#define COMPONENT_BLOCK_SHARPEN_BLOCKED (1<<1)
#define COMPONENT_BLOCK_SHARPEN_ALREADY (1<<2)
#define COMPONENT_BLOCK_SHARPEN_MAXED (1<<3)
-///from base of [/obj/item/proc/tool_attack_chain]: (atom/tool, mob/user)
-#define COMSIG_TOOL_ATTACK "tool_attack"
- #define COMPONENT_CANCEL_TOOLACT (1<<0)
/// Called by [/obj/item/assembly/proc/pulse]
#define COMSIG_ASSEMBLY_PULSED "item_assembly_pulsed"
///from [/mob/living/carbon/human/proc/Move]: ()
@@ -129,7 +112,7 @@
// other items
-/// from base of /obj/item/slimepotion/speed/afterattack(): (obj/target, /obj/src, mob/user)
+/// from base of /obj/item/slimepotion/speed/afterattack__legacy__attackchain(): (obj/target, /obj/src, mob/user)
#define COMSIG_SPEED_POTION_APPLIED "speed_potion"
#define SPEED_POTION_STOP (1<<0)
diff --git a/code/__DEFINES/dcs/mob_signals.dm b/code/__DEFINES/dcs/mob_signals.dm
index 2922c06f9f9a9..3d0406f583ead 100644
--- a/code/__DEFINES/dcs/mob_signals.dm
+++ b/code/__DEFINES/dcs/mob_signals.dm
@@ -36,7 +36,7 @@
#define COMPONENT_ITEM_NO_ATTACK (1<<0)
///from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone)
#define COMSIG_MOB_APPLY_DAMAGE "mob_apply_damage"
-///from base of obj/item/afterattack(): (atom/target, mob/user, proximity_flag, click_parameters)
+///from base of obj/item/afterattack__legacy__attackchain(): (atom/target, mob/user, proximity_flag, click_parameters)
#define COMSIG_MOB_ITEM_AFTERATTACK "mob_item_afterattack"
///from base of mob/RangedAttack(): (atom/A, params)
#define COMSIG_MOB_ATTACK_RANGED "mob_attack_ranged"
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 0c1bda08b3ae4..c108b54e38cf5 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -44,7 +44,7 @@
The most common are:
* mob/UnarmedAttack(atom,adjacent) - used here only when adjacent, with no item in hand; in the case of humans, checks gloves
* atom/attackby(item,user) - used only when adjacent
- * item/afterattack(atom,user,adjacent,params) - used both ranged and adjacent
+ * item/afterattack__legacy__attackchain(atom,user,adjacent,params) - used both ranged and adjacent
* mob/RangedAttack(atom,params) - used only ranged, only used for tk and laser eyes but could be changed
*/
/mob/proc/ClickOn(atom/A, params)
@@ -123,7 +123,10 @@
var/obj/item/W = get_active_hand()
if(W == A)
- W.attack_self(src)
+ if(W.new_attack_chain)
+ W.activate_self(src)
+ else
+ W.attack_self__legacy__attackchain(src)
if(hand)
update_inv_l_hand()
else
@@ -150,10 +153,12 @@
if(ismob(A))
changeNext_move(CLICK_CD_MELEE)
UnarmedAttack(A, 1)
-
else
if(W)
- W.afterattack(A, src, 0, params) // 0: not Adjacent
+ if(W.new_attack_chain)
+ A.base_ranged_item_interaction(src, W, params)
+ else
+ W.afterattack__legacy__attackchain(A, src, 0, params) // 0: not Adjacent
else
RangedAttack(A, params)
diff --git a/code/_onclick/click_override.dm b/code/_onclick/click_override.dm
index 36674ed70987c..c59506c654e1a 100644
--- a/code/_onclick/click_override.dm
+++ b/code/_onclick/click_override.dm
@@ -25,7 +25,7 @@
icon_state = "book"
var/datum/middle_click_override/clickBehavior = new /datum/middle_click_override/badmin_clicker
-/obj/item/badmin_book/attack_self(mob/living/user as mob)
+/obj/item/badmin_book/attack_self__legacy__attackchain(mob/living/user as mob)
if(user.middleClickOverride)
to_chat(user, "You try to draw power from [src], but you cannot hold the power at this time!")
return
diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm
index 39e0b5138ab22..7e5da141d5a10 100644
--- a/code/_onclick/cyborg.dm
+++ b/code/_onclick/cyborg.dm
@@ -83,7 +83,11 @@
return
if(W == A)
- W.attack_self(src)
+ if(W.new_attack_chain)
+ W.activate_self(src)
+ else
+ W.attack_self__legacy__attackchain(src)
+
return
// cyborgs are prohibited from using storage items so we can I think safely remove (A.loc in contents)
@@ -98,7 +102,7 @@
if(can_reach(A, W))
W.melee_attack_chain(src, A, params)
return
- W.afterattack(A, src, 0, params)
+ W.afterattack__legacy__attackchain(A, src, 0, params)
return
/mob/living/silicon/robot/MiddleShiftControlClickOn(atom/A)
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 4b0dbab47b08f..16a7a09b9ba9f 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -76,7 +76,7 @@
/atom/movable/screen/grab/attack_hand()
return
-/atom/movable/screen/grab/attackby()
+/atom/movable/screen/grab/attackby__legacy__attackchain()
return
/atom/movable/screen/act_intent
name = "intent"
@@ -170,7 +170,7 @@
if(master)
var/obj/item/I = usr.get_active_hand()
if(I)
- master.attackby(I, usr, params)
+ master.attackby__legacy__attackchain(I, usr, params)
return TRUE
/atom/movable/screen/storage/proc/is_item_accessible(obj/item/I, mob/user)
@@ -226,7 +226,7 @@
S.orient2hud(user)
S.show_to(user)
else // If it's not in the storage, try putting it inside
- S.attackby(I, user)
+ S.attackby__legacy__attackchain(I, user)
return TRUE
/atom/movable/screen/zone_sel
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 02b314ad6140c..94836bb8663e6 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -1,35 +1,71 @@
-/obj/item/proc/melee_attack_chain(mob/user, atom/target, params)
- if(!tool_attack_chain(user, target) && pre_attack(target, user, params))
- // Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example)
- var/resolved = target.attackby(src, user, params)
- if(!resolved && target && !QDELETED(src))
- afterattack(target, user, 1, params) // 1: clicking something Adjacent
-
-//Checks if the item can work as a tool, calling the appropriate tool behavior on the target
-//Note that if tool_act returns TRUE, then the tool won't call attack_by.
-/obj/item/proc/tool_attack_chain(mob/user, atom/target)
- if(!tool_behaviour)
- return FALSE
- if(SEND_SIGNAL(target, COMSIG_TOOL_ATTACK, src, user) & COMPONENT_CANCEL_TOOLACT)
- return FALSE
+/**
+ * This is the proc that handles the order of an item_attack.
+ *
+ * The order of procs called is:
+ * * [/atom/proc/base_item_interaction] on the target. If it returns ITEM_INTERACT_SUCCESS or ITEM_INTERACT_BLOCKING, the chain will be stopped.
+ * * [/obj/item/proc/pre_attack] on `src`. If this returns FINISH_ATTACK, the chain will be stopped.
+ * * [/atom/proc/attack_by] on the target. If it returns FINISH_ATTACK, the chain will be stopped.
+ * * [/obj/item/proc/after_attack]. The return value does not matter.
+ *
+ * Currently the return value of this proc is not checked anywhere, and is only used when short-circuiting the rest of the item attack.
+ */
+/obj/item/proc/melee_attack_chain(mob/user, atom/target, params, proximity_flag = 1)
+ // TODO: Look into whether proxy attackers are worth porting from /tg/: https://github.com/tgstation/tgstation/pull/83860
+ var/list/modifiers = params2list(params)
+
+ var/item_interact_result = target.base_item_interaction(user, src, modifiers)
+ if(item_interact_result & ITEM_INTERACT_SUCCESS)
+ return
+ if(item_interact_result & ITEM_INTERACT_BLOCKING)
+ return
- return target.tool_act(user, src, tool_behaviour)
+ // Attack phase
-// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
-/obj/item/proc/attack_self(mob/user)
- var/signal_ret = SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user)
- if(signal_ret & COMPONENT_NO_INTERACT)
+ if(pre_attack(target, user, params))
return
- if(signal_ret & COMPONENT_CANCEL_ATTACK_CHAIN)
- return TRUE
-/obj/item/proc/pre_attack(atom/A, mob/living/user, params) //do stuff before attackby!
- if(SEND_SIGNAL(src, COMSIG_ITEM_PRE_ATTACK, A, user, params) & COMPONENT_CANCEL_ATTACK_CHAIN)
- return TRUE
+ var/resolved = target.new_attack_chain \
+ ? target.attack_by(src, user, params) \
+ : target.attackby__legacy__attackchain(src, user, params)
+
+ // We were asked to cancel the rest of the attack chain.
+ if(resolved)
+ return
+
+ // At this point it means the attack was "successful", or at least
+ // handled, in some way. This can mean nothing happened, this can mean the
+ // target took damage, etc.
+ if(target.new_attack_chain)
+ target.attacked_by(src, user)
+ after_attack(target, user, proximity_flag, params)
+ else
+ afterattack__legacy__attackchain(target, user, proximity_flag, params)
- if(SEND_SIGNAL(A, COMSIG_ITEM_BEING_ATTACKED, src, user, params) & COMPONENT_CANCEL_ATTACK_CHAIN)
+/// Called when the item is in the active hand, and clicked; alternately, there
+/// is an 'activate held object' verb or you can hit pagedown.
+/obj/item/proc/activate_self(mob/user)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(SEND_SIGNAL(src, COMSIG_ACTIVATE_SELF, user) & COMPONENT_CANCEL_ATTACK_CHAIN)
+ return FINISH_ATTACK
+
+/**
+ * Called on ourselves before we hit something. Return TRUE to cancel the remainder of the attack chain.
+ *
+ * Arguments:
+ * * atom/A - The atom about to be hit
+ * * mob/living/user - The mob doing the htting
+ * * params - click params such as alt/shift etc
+ *
+ * See: [/obj/item/proc/melee_attack_chain]
+ */
+/obj/item/proc/pre_attack(atom/A, mob/living/user, params)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(SEND_SIGNAL(src, COMSIG_PRE_ATTACK, A, user, params) & COMPONENT_CANCEL_ATTACK_CHAIN)
return TRUE
+ // TODO: Turn this into a component and have a sane implementation instead of extra-specific behavior in a core proc
var/temperature = get_heat()
if(temperature && A.reagents && !ismob(A) && !istype(A, /obj/item/clothing/mask/cigarette))
var/reagent_temp = A.reagents.chem_temp
@@ -37,119 +73,179 @@
if(do_after_once(user, time, TRUE, user, TRUE, attempt_cancel_message = "You stop heating up [A]."))
to_chat(user, "You heat [A] with [src].")
A.reagents.temperature_reagents(temperature)
- return TRUE //return FALSE to avoid calling attackby after this proc does stuff
-// No comment
-/atom/proc/attackby(obj/item/W, mob/user, params)
- if(SEND_SIGNAL(src, COMSIG_PARENT_ATTACKBY, W, user, params) & COMPONENT_NO_AFTERATTACK)
- return TRUE
- return FALSE
+/**
+ * Called when mob `user` is hitting us with an item `attacking`.
+ * Part of the [/obj/item/proc/melee_attack_chain].
+ *
+ * Handles calling [/atom/proc/attack] or [/obj/item/proc/attack_obj] as necessary.
+ *
+ * Arguments:
+ * * obj/item/attacking_item - The item hitting this atom
+ * * mob/user - The wielder of this item
+ * * params - click params such as alt/shift etc
+ *
+ * Handles [COMSIG_ATTACK_BY] returning [COMPONENT_SKIP_AFTERATTACK].
+ * Returns [FINISH_ATTACK] if the attack chain should stop here.
+ */
+/atom/proc/attack_by(obj/item/attacking, mob/user, params)
+ SHOULD_CALL_PARENT(TRUE)
-/obj/attackby(obj/item/I, mob/living/user, params)
- return ..() || (can_be_hit && I.attack_obj(src, user, params))
+ if(SEND_SIGNAL(src, COMSIG_ATTACK_BY, attacking, user, params) & COMPONENT_SKIP_AFTERATTACK)
+ return FINISH_ATTACK
-/mob/living/attackby(obj/item/I, mob/living/user, params)
- user.changeNext_move(CLICK_CD_MELEE)
- if(attempt_harvest(I, user))
- return TRUE
- return I.attack(src, user)
+/obj/attack_by(obj/item/attacking, mob/user, params)
+ . = ..()
-/obj/item/proc/attack(mob/living/M, mob/living/user, def_zone)
- if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, M, user) & COMPONENT_CANCEL_ATTACK_CHAIN)
+ if(.)
+ return FINISH_ATTACK
+
+ if(!can_be_hit)
+ return FINISH_ATTACK
+
+ return attacking.attack_obj(src, user, params)
+
+/mob/living/attack_by(obj/item/attacking, mob/living/user, params)
+ if(..())
return TRUE
- SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK, M, user)
+ user.changeNext_move(CLICK_CD_MELEE)
+ return attacking.attack(src, user, params)
+
+/**
+ * Called when we are used by `user` to attack the living `target`.
+ *
+ * Returns `TRUE` if the rest of the attack chain should be cancelled. This may occur if the attack failed for some reason.
+ * Returns `FALSE` if the attack was "successful" or "handled" in some way, and the rest of the attack chain should still fire.
+ */
+/obj/item/proc/attack(mob/living/target, mob/living/user, params)
+ SHOULD_CALL_PARENT(TRUE)
+
+ var/signal_return = SEND_SIGNAL(src, COMSIG_ATTACK, target, user, params) \
+ || SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK, target, user, params)
+
+ if(signal_return & COMPONENT_CANCEL_ATTACK_CHAIN)
+ return FINISH_ATTACK
+
+ if(signal_return & COMPONENT_SKIP_ATTACK)
+ return FALSE
+
+ . = __attack_core(target, user)
+
+ if(!target.new_attack_chain)
+ return target.attacked_by__legacy__attackchain(src, user, /* def_zone */ null)
+
+/obj/item/proc/__attack_core(mob/living/target, mob/living/user)
+ PRIVATE_PROC(TRUE)
+
if(flags & (NOBLUDGEON))
return FALSE
- if((is_surgery_tool_by_behavior(src) || is_organ(src) || tool_behaviour) && user.a_intent == INTENT_HELP && on_operable_surface(M) && M != user)
+ // TODO: Migrate all of this to the proper objects so it's not clogging up a core proc and called at irrelevant times
+ if((is_surgery_tool_by_behavior(src) || is_organ(src) || tool_behaviour) && user.a_intent == INTENT_HELP && on_operable_surface(target) && target != user)
to_chat(user, "You don't want to harm the person you're trying to help!")
- return
+ return FALSE
if(force && HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, "You don't want to harm other living beings!")
- return
+ return FALSE
if(!force)
playsound(loc, 'sound/weapons/tap.ogg', get_clamped_volume(), TRUE, -1)
else
- SEND_SIGNAL(M, COMSIG_ITEM_ATTACK)
- add_attack_logs(user, M, "Attacked with [name] ([uppertext(user.a_intent)]) ([uppertext(damtype)])", (M.ckey && force > 0 && damtype != STAMINA) ? null : ATKLOG_ALMOSTALL)
+ SEND_SIGNAL(target, COMSIG_ATTACK)
+ add_attack_logs(user, target, "Attacked with [name] ([uppertext(user.a_intent)]) ([uppertext(damtype)])", (target.ckey && force > 0 && damtype != STAMINA) ? null : ATKLOG_ALMOSTALL)
if(hitsound)
playsound(loc, hitsound, get_clamped_volume(), TRUE, extrarange = stealthy_audio ? SILENCED_SOUND_EXTRARANGE : -1, falloff_distance = 0)
- M.lastattacker = user.real_name
- M.lastattackerckey = user.ckey
-
- user.do_attack_animation(M)
- . = M.attacked_by(src, user, def_zone)
+ target.lastattacker = user.real_name
+ target.lastattackerckey = user.ckey
+ user.do_attack_animation(target)
add_fingerprint(user)
-//the equivalent of the standard version of attack() but for object targets.
-/obj/item/proc/attack_obj(obj/O, mob/living/user, params)
- if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_OBJ, O, user) & COMPONENT_NO_ATTACK_OBJ)
- return
- if(flags & (NOBLUDGEON))
- return
- user.changeNext_move(CLICK_CD_MELEE)
- user.do_attack_animation(O)
- O.attacked_by(src, user)
+/// The equivalent of the standard version of [/obj/item/proc/attack] but for non mob targets.
+/obj/item/proc/attack_obj(obj/attacked_obj, mob/living/user, params)
+ var/signal_return = SEND_SIGNAL(src, COMSIG_ATTACK_OBJ, attacked_obj, user) | SEND_SIGNAL(user, COMSIG_ATTACK_OBJ_LIVING, attacked_obj)
+ if(signal_return & COMPONENT_SKIP_ATTACK)
+ return TRUE
+ if(signal_return & COMPONENT_CANCEL_ATTACK_CHAIN)
+ return FALSE
+ if(flags & NOBLUDGEON)
+ return FALSE
-/atom/movable/proc/attacked_by()
+ if(!attacked_obj.new_attack_chain)
+ attacked_obj.attacked_by__legacy__attackchain(src, user)
+
+/**
+ * Called *after* we have been attacked with the item `attacker` by `user`.
+ *
+ * Return value is ignored for purposes of the attack chain.
+ */
+/atom/proc/attacked_by(obj/item/attacker, mob/living/user)
return
-/obj/attacked_by(obj/item/I, mob/living/user)
- var/damage = I.force
- if(I.force)
+/obj/attacked_by(obj/item/attacker, mob/living/user)
+ var/damage = attacker.force
+ if(attacker.force)
user.visible_message(
- "[user] has hit [src] with [I]!",
- "You hit [src] with [I]!",
+ "[user] has hit [src] with [attacker]!",
+ "You hit [src] with [attacker]!",
"You hear something being struck by a weapon!"
)
if(ishuman(user))
var/mob/living/carbon/human/H = user
damage += H.physiology.melee_bonus
- take_damage(damage, I.damtype, MELEE, 1)
+ take_damage(damage, attacker.damtype, MELEE, 1)
-/mob/living/attacked_by(obj/item/I, mob/living/user, def_zone)
- send_item_attack_message(I, user)
- if(I.force)
+/mob/living/attacked_by(obj/item/attacker, mob/living/user, def_zone)
+ send_item_attack_message(attacker, user)
+ if(attacker.force)
var/bonus_damage = 0
if(ishuman(user))
var/mob/living/carbon/human/H = user
bonus_damage = H.physiology.melee_bonus
- apply_damage(I.force + bonus_damage, I.damtype, def_zone)
- if(I.damtype == BRUTE)
+ apply_damage(attacker.force + bonus_damage, attacker.damtype, def_zone)
+ if(attacker.damtype == BRUTE)
if(prob(33))
- I.add_mob_blood(src)
+ attacker.add_mob_blood(src)
var/turf/location = get_turf(src)
add_splatter_floor(location)
if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood
user.add_mob_blood(src)
-/mob/living/simple_animal/attacked_by(obj/item/I, mob/living/user)
- if(!I.force)
+/mob/living/simple_animal/attacked_by(obj/item/attacker, mob/living/user)
+ if(!attacker.force)
user.visible_message(
- "[user] gently taps [src] with [I].",
+ "[user] gently taps [src] with [attacker].",
"This weapon is ineffective, it does no damage!",
"You hear a gentle tapping."
)
- else if(I.force < force_threshold || I.damtype == STAMINA)
+ else if(attacker.force < force_threshold || attacker.damtype == STAMINA)
visible_message(
- "[I] bounces harmlessly off of [src].",
- "[I] bounces harmlessly off of [src]!",
+ "[attacker] bounces harmlessly off of [src].",
+ "[attacker] bounces harmlessly off of [src]!",
"You hear something being struck by a weapon!"
)
else
return ..()
-// Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person.
-// Click parameters is the params string from byond Click() code, see that documentation.
-/obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
- SEND_SIGNAL(src, COMSIG_ITEM_AFTERATTACK, target, user, proximity_flag, click_parameters)
+/**
+ * Last proc in the [/obj/item/proc/melee_attack_chain].
+ *
+ * Arguments:
+ * * atom/target - The thing that was hit
+ * * mob/user - The mob doing the hitting
+ * * proximity_flag - is 1 if this afterattack was called on something adjacent, in your square, or on your person.
+ * * click_parameters - is the params string from byond [/atom/proc/Click] code, see that documentation.
+ */
+/obj/item/proc/after_attack(atom/target, mob/user, proximity_flag, click_parameters)
+ SHOULD_CALL_PARENT(TRUE)
+
+ SEND_SIGNAL(src, COMSIG_AFTER_ATTACK, target, user, proximity_flag, click_parameters)
+ SEND_SIGNAL(target, COMSIG_AFTER_ATTACKED_BY, src, user, proximity_flag, click_parameters)
/obj/item/proc/get_clamped_volume()
if(w_class)
diff --git a/code/_onclick/item_attack_legacy.dm b/code/_onclick/item_attack_legacy.dm
new file mode 100644
index 0000000000000..828925008da28
--- /dev/null
+++ b/code/_onclick/item_attack_legacy.dm
@@ -0,0 +1,115 @@
+/**
+ * Called after `user` has attacked us with item `W`.
+ *
+ * New uses of this proc are prohibited! Used [/atom/proc/attacked_by].
+ * If you are modifiying an existing implementation of this proc, it is expected that you replace it with the proper alternative!
+ */
+/atom/proc/attacked_by__legacy__attackchain(obj/item/W, mob/living/user)
+ return
+
+/atom/movable/attacked_by__legacy__attackchain()
+ return
+
+/obj/attacked_by__legacy__attackchain(obj/item/I, mob/living/user)
+ return attacked_by(I, user)
+
+/mob/living/attacked_by__legacy__attackchain(obj/item/I, mob/living/user, def_zone)
+ return attacked_by(I, user, def_zone)
+
+/mob/living/simple_animal/attacked_by__legacy__attackchain(obj/item/I, mob/living/user)
+ return attacked_by(I, user)
+
+/obj/item/proc/attack__legacy__attackchain(mob/living/M, mob/living/user, def_zone)
+ if(SEND_SIGNAL(src, COMSIG_ATTACK, M, user) & COMPONENT_CANCEL_ATTACK_CHAIN)
+ return TRUE
+
+ SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK, M, user)
+
+ . = __attack_core(M, user)
+
+ if(!M.new_attack_chain)
+ M.attacked_by__legacy__attackchain(src, user, def_zone)
+
+/**
+ * Called when `user` attacks us with item `W`.
+ *
+ * Handles [COMSIG_ATTACK_BY] returning [COMPONENT_SKIP_AFTERATTACK].
+ * Returns TRUE if afterattack should not be called, FALSE otherwise.
+ *
+ * New uses of this proc are prohibited! Use [/atom/proc/attackby] or [/atom/proc/base_item_interaction] instead!
+ * If you are modifiying an existing implementation of this proc, it is expected that you replace it with the proper alternative!
+ */
+/atom/proc/attackby__legacy__attackchain(obj/item/W, mob/user, params)
+ if(SEND_SIGNAL(src, COMSIG_ATTACK_BY, W, user, params) & COMPONENT_SKIP_AFTERATTACK)
+ return TRUE
+ return FALSE
+
+/obj/attackby__legacy__attackchain(obj/item/I, mob/living/user, params)
+ return ..() || (can_be_hit && I.new_attack_chain \
+ ? I.attack_obj(src, user, params) \
+ : I.attack_obj__legacy__attackchain(src, user, params))
+
+/mob/living/attackby__legacy__attackchain(obj/item/I, mob/living/user, params)
+ user.changeNext_move(CLICK_CD_MELEE)
+ if(attempt_harvest(I, user))
+ return TRUE
+
+ if(I.new_attack_chain)
+ return I.attack(src, user, params)
+
+ return I.attack__legacy__attackchain(src, user)
+
+/**
+ * Called when `user` attacks us with object `O`.
+ *
+ * Handles [COMSIG_ATTACK_OBJ] returning [COMPONENT_NO_ATTACK_OBJ].
+ * Returns FALSE if the attack isn't valid.
+ *
+ * New uses of this proc are prohibited! Use [/obj/item/proc/interact_with_atom]
+ * or [/atom/proc/base_item_interaction] if this is not meant to be an attack, and
+ * [/obj/item/proc/attack_obj] if it is. If you are modifiying an existing
+ * implementation of this proc, it is expected that you replace it with the proper
+ * alternative!
+ */
+/obj/item/proc/attack_obj__legacy__attackchain(obj/O, mob/living/user, params)
+ if(SEND_SIGNAL(src, COMSIG_ATTACK_OBJ, O, user) & COMPONENT_NO_ATTACK_OBJ)
+ return FALSE
+ if(flags & (NOBLUDGEON))
+ return FALSE
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(O)
+
+ if(!O.new_attack_chain)
+ O.attacked_by__legacy__attackchain(src, user)
+
+/**
+ * Called when `user` has us in the active hand, and has clicked on us.
+ *
+ * Handles [COMSIG_ACTIVATE_SELF] returning [COMPONENT_NO_INTERACT].
+ * Returns TRUE if a listener has requested the attack chain be cancelled.
+ *
+ * New uses of this proc are prohibited! Use [/obj/item/proc/activate_self].
+ * If you are modifiying an existing implementation of this proc, it is expected that you replace it with the proper alternative!
+ */
+/obj/item/proc/attack_self__legacy__attackchain(mob/user)
+ var/signal_ret = SEND_SIGNAL(src, COMSIG_ACTIVATE_SELF, user)
+ if(signal_ret & COMPONENT_NO_INTERACT)
+ return
+ if(signal_ret & COMPONENT_CANCEL_ATTACK_CHAIN)
+ return TRUE
+
+/**
+ * Last proc in the [/obj/item/proc/melee_attack_chain].
+ *
+ * Sends [COMSIG_AFTER_ATTACK] and [COMSIG_AFTER_ATTACKED_BY], handling no responses.
+ * New uses of this proc are prohibited! attack() calls on mobs and objects handle sending these signals.
+ *
+ * Arguments:
+ * * atom/target - The thing that was hit
+ * * mob/user - The mob doing the hitting
+ * * proximity_flag - is 1 if this afterattack was called on something adjacent, in your square, or on your person.
+ * * click_parameters - is the params string from byond [/atom/proc/Click] code, see that documentation.
+ */
+/obj/item/proc/afterattack__legacy__attackchain(atom/target, mob/user, proximity_flag, params)
+ SEND_SIGNAL(src, COMSIG_AFTER_ATTACK, target, user, proximity_flag, params)
+ SEND_SIGNAL(target, COMSIG_AFTER_ATTACKED_BY, src, user, proximity_flag, params)
diff --git a/code/_onclick/telekinesis.dm b/code/_onclick/telekinesis.dm
index 6963ca58801cc..4bd96084e7871 100644
--- a/code/_onclick/telekinesis.dm
+++ b/code/_onclick/telekinesis.dm
@@ -100,15 +100,15 @@
qdel(src)
-/obj/item/tk_grab/attack_self(mob/user)
+/obj/item/tk_grab/attack_self__legacy__attackchain(mob/user)
if(focus)
focus.attack_self_tk(user)
/obj/item/tk_grab/override_throw(mob/user, atom/target)
- afterattack(target, user)
+ afterattack__legacy__attackchain(target, user)
return TRUE
-/obj/item/tk_grab/afterattack(atom/target , mob/living/user, proximity, params)
+/obj/item/tk_grab/afterattack__legacy__attackchain(atom/target , mob/living/user, proximity, params)
if(!target || !user)
return
if(last_throw + TK_COOLDOWN > world.time)
@@ -140,9 +140,9 @@
if(isitem(focus) && target.Adjacent(focus) && !user.in_throw_mode)
var/obj/item/I = focus
- var/resolved = target.attackby(I, user, params)
+ var/resolved = target.attackby__legacy__attackchain(I, user, params)
if(!resolved && target && I)
- I.afterattack(target,user,1) // for splashing with beakers
+ I.afterattack__legacy__attackchain(target,user,1) // for splashing with beakers
else
@@ -156,7 +156,7 @@
focus.throw_at(target, 10, 1, user)
last_throw = world.time
-/obj/item/tk_grab/attack(mob/living/M, mob/living/user, def_zone)
+/obj/item/tk_grab/attack__legacy__attackchain(mob/living/M, mob/living/user, def_zone)
return
/obj/item/tk_grab/is_equivalent(obj/item/I)
diff --git a/code/datums/components/defibrillator.dm b/code/datums/components/defibrillator.dm
index fa734c62f90d1..a33c4f0a36fb5 100644
--- a/code/datums/components/defibrillator.dm
+++ b/code/datums/components/defibrillator.dm
@@ -72,7 +72,7 @@
var/effect_target = isnull(actual_unit) ? parent : actual_unit
- RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(trigger_defib))
+ RegisterSignal(parent, COMSIG_ATTACK, PROC_REF(trigger_defib))
RegisterSignal(effect_target, COMSIG_ATOM_EMAG_ACT, PROC_REF(on_emag))
RegisterSignal(effect_target, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp))
@@ -119,7 +119,7 @@
* Start the defibrillation process when triggered by a signal.
*/
/datum/component/defib/proc/trigger_defib(obj/item/paddles, mob/living/carbon/human/target, mob/living/user)
- SIGNAL_HANDLER // COMSIG_ITEM_ATTACK
+ SIGNAL_HANDLER // COMSIG_ATTACK
// This includes some do-afters, so we have to pass it off asynchronously
INVOKE_ASYNC(src, PROC_REF(defibrillate), user, target)
return TRUE
diff --git a/code/datums/components/ducttape.dm b/code/datums/components/ducttape.dm
index 516b34e347b64..fa749cbbc50eb 100644
--- a/code/datums/components/ducttape.dm
+++ b/code/datums/components/ducttape.dm
@@ -12,7 +12,7 @@
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(add_tape_text))
x_offset = x
y_offset = y
- RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, PROC_REF(afterattack))
+ RegisterSignal(parent, COMSIG_AFTER_ATTACK, PROC_REF(afterattack__legacy__attackchain))
RegisterSignal(parent, COMSIG_ITEM_PICKUP, PROC_REF(pick_up))
I.update_icon() //Do this first so the action button properly shows the icon
if(!hide_tape) //the tape can no longer be removed if TRUE
@@ -44,7 +44,7 @@
I.remove_tape()
qdel(src)
-/datum/component/ducttape/proc/afterattack(obj/item/I, atom/target, mob/user, proximity, params)
+/datum/component/ducttape/proc/afterattack__legacy__attackchain(obj/item/I, atom/target, mob/user, proximity, params)
if(!proximity)
return
if(!isturf(target))
diff --git a/code/datums/components/label.dm b/code/datums/components/label.dm
index c76e2ca97210e..dcc7a3fc73bbf 100644
--- a/code/datums/components/label.dm
+++ b/code/datums/components/label.dm
@@ -22,12 +22,12 @@
apply_label()
/datum/component/label/RegisterWithParent()
- RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, PROC_REF(on_attack_by))
+ RegisterSignal(parent, COMSIG_ATTACK_BY, PROC_REF(on_attack_by))
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
RegisterSignal(parent, COMSIG_ATOM_UPDATE_NAME, PROC_REF(on_update_name))
/datum/component/label/UnregisterFromParent()
- UnregisterSignal(parent, list(COMSIG_PARENT_ATTACKBY, COMSIG_PARENT_EXAMINE, COMSIG_ATOM_UPDATE_NAME))
+ UnregisterSignal(parent, list(COMSIG_ATTACK_BY, COMSIG_PARENT_EXAMINE, COMSIG_ATOM_UPDATE_NAME))
/**
This proc will fire after the parent is hit by a hand labeler which is trying to apply another label.
@@ -53,7 +53,7 @@
* user: The mob who is wielding the attacking object.
*/
/datum/component/label/proc/on_attack_by(datum/source, obj/item/attacker, mob/user)
- SIGNAL_HANDLER // COMSIG_PARENT_ATTACKBY
+ SIGNAL_HANDLER // COMSIG_ATTACK_BY
// If the attacking object is not a hand labeler or it's not off (has a label ready to apply), return.
// The hand labeler should be off in order to remove a label.
var/obj/item/hand_labeler/labeler = attacker
diff --git a/code/datums/components/material_container.dm b/code/datums/components/material_container.dm
index 6041460d068c8..e35d9379578b8 100644
--- a/code/datums/components/material_container.dm
+++ b/code/datums/components/material_container.dm
@@ -37,7 +37,7 @@
precondition = _precondition
after_insert = _after_insert
- RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, PROC_REF(OnAttackBy))
+ RegisterSignal(parent, COMSIG_ATTACK_BY, PROC_REF(OnAttackBy))
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(OnExamine))
var/list/possible_mats = list()
@@ -74,7 +74,7 @@
if((I.flags_2 & (HOLOGRAM_2 | NO_MAT_REDEMPTION_2)) || (tc && !is_type_in_typecache(I, tc)))
to_chat(user, "[parent] won't accept [I]!")
return
- . = COMPONENT_NO_AFTERATTACK
+ . = COMPONENT_SKIP_AFTERATTACK
var/datum/callback/pc = precondition
if(pc && !pc.Invoke(user))
return
diff --git a/code/datums/components/paintable.dm b/code/datums/components/paintable.dm
index e77e05147190f..f0a7187149a8e 100644
--- a/code/datums/components/paintable.dm
+++ b/code/datums/components/paintable.dm
@@ -2,7 +2,7 @@
var/current_paint
/datum/component/spraycan_paintable/Initialize()
- RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, PROC_REF(Repaint))
+ RegisterSignal(parent, COMSIG_ATTACK_BY, PROC_REF(Repaint))
/datum/component/spraycan_paintable/Destroy()
RemoveCurrentCoat()
@@ -15,7 +15,7 @@
/datum/component/spraycan_paintable/proc/Repaint(datum/source, obj/item/toy/crayon/spraycan/spraycan, mob/living/user)
if(!istype(spraycan) || user.a_intent == INTENT_HARM)
return
- . = COMPONENT_NO_AFTERATTACK
+ . = COMPONENT_SKIP_AFTERATTACK
if(spraycan.capped)
to_chat(user, "Take the cap off first!")
return
diff --git a/code/datums/components/radioactive.dm b/code/datums/components/radioactive.dm
index ba240898e34fb..6652b4ed6460f 100644
--- a/code/datums/components/radioactive.dm
+++ b/code/datums/components/radioactive.dm
@@ -22,8 +22,8 @@
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(rad_examine))
RegisterSignal(parent, COMSIG_ADMIN_DECONTAMINATE, PROC_REF(admin_decontaminate))
if(isitem(parent))
- RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(rad_attack))
- RegisterSignal(parent, COMSIG_ITEM_ATTACK_OBJ, PROC_REF(rad_attack))
+ RegisterSignal(parent, COMSIG_ATTACK, PROC_REF(rad_attack))
+ RegisterSignal(parent, COMSIG_ATTACK_OBJ, PROC_REF(rad_attack))
if(strength > RAD_MINIMUM_CONTAMINATION)
SSradiation.warn(src)
//Let's make er glow
diff --git a/code/datums/components/scope.dm b/code/datums/components/scope.dm
index 1506d2851a688..4ad8faa83d656 100644
--- a/code/datums/components/scope.dm
+++ b/code/datums/components/scope.dm
@@ -101,7 +101,7 @@
SIGNAL_HANDLER // COMSIG_GUN_TRY_FIRE
if(!tracker?.given_turf || target == get_target(tracker.given_turf))
return NONE
- INVOKE_ASYNC(source, TYPE_PROC_REF(/obj/item, afterattack), get_target(tracker.given_turf), user)
+ INVOKE_ASYNC(source, TYPE_PROC_REF(/obj/item, afterattack__legacy__attackchain), get_target(tracker.given_turf), user)
return COMPONENT_CANCEL_GUN_FIRE
/datum/component/scope/proc/on_examine(datum/source, mob/user, list/examine_list)
diff --git a/code/datums/components/shelved.dm b/code/datums/components/shelved.dm
index 29ba697d01bb3..3aa678d2efed7 100644
--- a/code/datums/components/shelved.dm
+++ b/code/datums/components/shelved.dm
@@ -25,7 +25,7 @@
/datum/component/shelver/RegisterWithParent()
RegisterSignal(parent, COMSIG_SHELF_ATTEMPT_PICKUP, PROC_REF(on_shelf_attempt_pickup))
- RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, PROC_REF(on_attackby))
+ RegisterSignal(parent, COMSIG_ATTACK_BY, PROC_REF(on_attackby))
RegisterSignal(parent, COMSIG_SHELF_ITEM_REMOVED, PROC_REF(on_shelf_item_removed))
RegisterSignal(parent, COMSIG_SHELF_ADDED_ON_MAPLOAD, PROC_REF(prepare_autoshelf))
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine))
@@ -117,18 +117,18 @@
O.update_appearance(UPDATE_ICON)
/datum/component/shelver/proc/on_attackby(datum/source, obj/item/attacker, mob/user, params)
- SIGNAL_HANDLER // COMSIG_PARENT_ATTACKBY
+ SIGNAL_HANDLER // COMSIG_ATTACK_BY
if(isrobot(user))
- return COMPONENT_NO_AFTERATTACK
+ return COMPONENT_SKIP_AFTERATTACK
if(attacker.flags & ABSTRACT)
- return COMPONENT_NO_AFTERATTACK
+ return COMPONENT_SKIP_AFTERATTACK
if(user.a_intent == INTENT_HARM)
return
if(length(allowed_types) && !(attacker.type in allowed_types))
to_chat(user, "[attacker] won't fit on [parent]!")
- return COMPONENT_NO_AFTERATTACK
+ return COMPONENT_SKIP_AFTERATTACK
var/list/PL = params2list(params)
var/icon_x = text2num(PL["icon-x"])
@@ -140,7 +140,7 @@
if(icon_x >= coords[1] && icon_x <= coords[3] && icon_y >= coords[2] && icon_y <= coords[4])
if(used_places[i])
to_chat(user, "There's already something there on [parent].")
- return COMPONENT_NO_AFTERATTACK
+ return COMPONENT_SKIP_AFTERATTACK
var/position_details = placement_zones[coords]
if(user.drop_item())
@@ -149,7 +149,7 @@
"[user] places [attacker] on [parent].",
"You place [attacker] on [parent].",
)
- return COMPONENT_NO_AFTERATTACK
+ return COMPONENT_SKIP_AFTERATTACK
/**
* Add an item to the shelf.
diff --git a/code/datums/components/spooky.dm b/code/datums/components/spooky.dm
index 99f7ba0581162..3ec7ab5bbe68e 100644
--- a/code/datums/components/spooky.dm
+++ b/code/datums/components/spooky.dm
@@ -2,7 +2,7 @@
var/too_spooky = TRUE //will it spawn a new instrument?
/datum/component/spooky/Initialize()
- RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(spectral_attack))
+ RegisterSignal(parent, COMSIG_ATTACK, PROC_REF(spectral_attack))
/datum/component/spooky/proc/spectral_attack(datum/source, mob/living/carbon/C, mob/user)
if(ishuman(user)) //this weapon wasn't meant for mortals.
diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm
index 47a94d7e69d2f..bfb939ed10362 100644
--- a/code/datums/components/squeak.dm
+++ b/code/datums/components/squeak.dm
@@ -22,7 +22,7 @@
/datum/component/squeak/Initialize(custom_sounds, volume_override, chance_override, step_delay_override, use_delay_override, squeak_on_move, extrarange, falloff_exponent, fallof_distance)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
- RegisterSignal(parent, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_BLOB_ACT, COMSIG_ATOM_HULK_ATTACK, COMSIG_PARENT_ATTACKBY), PROC_REF(play_squeak))
+ RegisterSignal(parent, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_BLOB_ACT, COMSIG_ATOM_HULK_ATTACK, COMSIG_ATTACK_BY), PROC_REF(play_squeak))
if(ismovable(parent))
RegisterSignal(parent, list(COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_IMPACT), PROC_REF(play_squeak))
RegisterSignal(parent, COMSIG_MOVABLE_CROSSED, PROC_REF(play_squeak_crossed))
@@ -30,8 +30,8 @@
if(squeak_on_move)
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(play_squeak))
if(isitem(parent))
- RegisterSignal(parent, list(COMSIG_ITEM_ATTACK, COMSIG_ITEM_ATTACK_OBJ, COMSIG_ITEM_HIT_REACT), PROC_REF(play_squeak))
- RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, PROC_REF(use_squeak))
+ RegisterSignal(parent, list(COMSIG_ATTACK, COMSIG_ATTACK_OBJ, COMSIG_ITEM_HIT_REACT), PROC_REF(play_squeak))
+ RegisterSignal(parent, COMSIG_ACTIVATE_SELF, PROC_REF(use_squeak))
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip))
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_drop))
if(istype(parent, /obj/item/clothing/shoes))
diff --git a/code/datums/components/sticky.dm b/code/datums/components/sticky.dm
index 83b2d7544f961..9b1c625903f5b 100644
--- a/code/datums/components/sticky.dm
+++ b/code/datums/components/sticky.dm
@@ -25,11 +25,11 @@
return ..()
/datum/component/sticky/RegisterWithParent()
- RegisterSignal(parent, COMSIG_ITEM_PRE_ATTACK, PROC_REF(stick_to_it))
+ RegisterSignal(parent, COMSIG_PRE_ATTACK, PROC_REF(stick_to_it))
RegisterSignal(parent, COMSIG_MOVABLE_IMPACT, PROC_REF(stick_to_it_throwing))
/datum/component/sticky/UnregisterFromParent()
- UnregisterSignal(parent, COMSIG_ITEM_PRE_ATTACK)
+ UnregisterSignal(parent, COMSIG_PRE_ATTACK)
UnregisterSignal(parent, COMSIG_MOVABLE_IMPACT)
/datum/component/sticky/proc/stick_to_it(obj/item/I, atom/target, mob/user, params)
diff --git a/code/datums/components/surgery_initiator.dm b/code/datums/components/surgery_initiator.dm
index 9f476ba089da8..7a7f5966c8975 100644
--- a/code/datums/components/surgery_initiator.dm
+++ b/code/datums/components/surgery_initiator.dm
@@ -48,12 +48,12 @@
src.forced_surgery = forced_surgery
/datum/component/surgery_initiator/RegisterWithParent()
- RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(initiate_surgery_moment))
+ RegisterSignal(parent, COMSIG_ATTACK, PROC_REF(initiate_surgery_moment))
RegisterSignal(parent, COMSIG_ATOM_UPDATE_SHARPNESS, PROC_REF(on_parent_sharpness_change))
RegisterSignal(parent, COMSIG_PARENT_EXAMINE_MORE, PROC_REF(on_parent_examine_more))
/datum/component/surgery_initiator/UnregisterFromParent()
- UnregisterSignal(parent, COMSIG_ITEM_ATTACK)
+ UnregisterSignal(parent, COMSIG_ATTACK)
UnregisterSignal(parent, COMSIG_ATOM_UPDATE_SHARPNESS)
UnregisterSignal(parent, COMSIG_PARENT_EXAMINE_MORE)
@@ -72,7 +72,7 @@
/// Does the surgery initiation.
/datum/component/surgery_initiator/proc/initiate_surgery_moment(datum/source, atom/target, mob/user)
- SIGNAL_HANDLER // COMSIG_ITEM_ATTACK
+ SIGNAL_HANDLER // COMSIG_ATTACK
if(!isliving(user))
return
var/mob/living/L = target
diff --git a/code/datums/components/two_handed.dm b/code/datums/components/two_handed.dm
index 63c3108efe3e2..c5d8143341a49 100644
--- a/code/datums/components/two_handed.dm
+++ b/code/datums/components/two_handed.dm
@@ -104,8 +104,8 @@
/datum/component/two_handed/RegisterWithParent()
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip))
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_drop))
- RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, PROC_REF(on_attack_self))
- RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(on_attack))
+ RegisterSignal(parent, COMSIG_ACTIVATE_SELF, PROC_REF(on_attack_self))
+ RegisterSignal(parent, COMSIG_ATTACK, PROC_REF(on_attack))
RegisterSignal(parent, COMSIG_ATOM_UPDATE_ICON, PROC_REF(on_update_icon))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
RegisterSignal(parent, COMSIG_ITEM_SHARPEN_ACT, PROC_REF(on_sharpen))
@@ -116,8 +116,8 @@
/datum/component/two_handed/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED,
COMSIG_ITEM_DROPPED,
- COMSIG_ITEM_ATTACK_SELF,
- COMSIG_ITEM_ATTACK,
+ COMSIG_ACTIVATE_SELF,
+ COMSIG_ATTACK,
COMSIG_ATOM_UPDATE_ICON,
COMSIG_MOVABLE_MOVED,
COMSIG_ITEM_SHARPEN_ACT))
@@ -151,7 +151,7 @@
/// Triggered on attack self of the item containing the component
/datum/component/two_handed/proc/on_attack_self(datum/source, mob/user)
- SIGNAL_HANDLER // COMSIG_ITEM_ATTACK_SELF
+ SIGNAL_HANDLER // COMSIG_ACTIVATE_SELF
if(require_twohands)
return
@@ -311,7 +311,7 @@
* on_attack triggers on attack with the parent item
*/
/datum/component/two_handed/proc/on_attack(obj/item/source, mob/living/target, mob/living/user)
- SIGNAL_HANDLER // COMSIG_ITEM_ATTACK
+ SIGNAL_HANDLER // COMSIG_ATTACK
if(wielded && attacksound)
var/obj/item/parent_item = parent
playsound(parent_item.loc, attacksound, 50, TRUE)
diff --git a/code/datums/ores.dm b/code/datums/ores.dm
index 7774a485cff32..07e7ae6090038 100644
--- a/code/datums/ores.dm
+++ b/code/datums/ores.dm
@@ -136,7 +136,7 @@
else
log_game("An explosion has triggered a gibtonite deposit reaction at [AREACOORD(source)].")
- RegisterSignal(source, COMSIG_PARENT_ATTACKBY, PROC_REF(on_parent_attackby))
+ RegisterSignal(source, COMSIG_ATTACK_BY, PROC_REF(on_attackby))
detonate_start_time = world.time
explosion_callback = addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/ore/gibtonite, detonate), source), detonate_time, TIMER_STOPPABLE)
@@ -164,13 +164,13 @@
return MINERAL_PREVENT_DIG
-/datum/ore/gibtonite/proc/on_parent_attackby(turf/source, obj/item/attacker, mob/user)
- SIGNAL_HANDLER // COMSIG_PARENT_ATTACKBY
+/datum/ore/gibtonite/proc/on_attackby(turf/source, obj/item/attacker, mob/user)
+ SIGNAL_HANDLER // COMSIG_ATTACK_BY
if(istype(attacker, /obj/item/mining_scanner) || istype(attacker, /obj/item/t_scanner/adv_mining_scanner) && stage == GIBTONITE_ACTIVE)
user.visible_message("[user] holds [attacker] to [src]...", "You use [attacker] to locate where to cut off the chain reaction and attempt to stop it...")
defuse(source)
- return COMPONENT_NO_AFTERATTACK
+ return COMPONENT_SKIP_AFTERATTACK
/datum/ore/gibtonite/proc/detonate(turf/simulated/mineral/source)
if(stage == GIBTONITE_STABLE)
diff --git a/code/datums/outfits/outfit_debug.dm b/code/datums/outfits/outfit_debug.dm
index ccce96c4c0cfd..559c9b8b9a21e 100644
--- a/code/datums/outfits/outfit_debug.dm
+++ b/code/datums/outfits/outfit_debug.dm
@@ -63,7 +63,7 @@
for(var/channel in SSradio.radiochannels)
channels[channel] = 1 // yeah, all channels, sure, probably fine
-/obj/item/encryptionkey/syndicate/all_channels/attack_self(mob/user, pickupfireoverride)
+/obj/item/encryptionkey/syndicate/all_channels/attack_self__legacy__attackchain(mob/user, pickupfireoverride)
change_voice = !change_voice
to_chat(user, "You switch [src] to [change_voice ? "" : "not "]change your voice on syndicate communications.")
@@ -157,7 +157,7 @@
. = ..()
. += "Alt-Click to toggle mind-activation on spawning."
-/obj/item/debug/human_spawner/afterattack(atom/target, mob/user, proximity)
+/obj/item/debug/human_spawner/afterattack__legacy__attackchain(atom/target, mob/user, proximity)
..()
if(!isturf(target))
return
@@ -167,7 +167,7 @@
if(activate_mind)
H.mind_initialize()
-/obj/item/debug/human_spawner/attack_self(mob/user)
+/obj/item/debug/human_spawner/attack_self__legacy__attackchain(mob/user)
..()
var/choice = input("Select a species", "Human Spawner", null) in GLOB.all_species
selected_species = GLOB.all_species[choice]
@@ -199,7 +199,7 @@
desc = "A wonder of modern medicine. This tool functions as any other sort of surgery tool, and finishes in only a fraction of the time. Hey, how'd you get your hands on this, anyway?"
toolspeed = 0.01
-/obj/item/scalpel/laser/manager/debug/attack_self(mob/user)
+/obj/item/scalpel/laser/manager/debug/attack_self__legacy__attackchain(mob/user)
. = ..()
toolspeed = toolspeed == 0.5 ? 0.01 : 0.5
to_chat(user, "[src] is now set to toolspeed [toolspeed]")
diff --git a/code/datums/spells/alien_spells/build_resin_structure.dm b/code/datums/spells/alien_spells/build_resin_structure.dm
index c38dcceec66d5..b64925e0b2638 100644
--- a/code/datums/spells/alien_spells/build_resin_structure.dm
+++ b/code/datums/spells/alien_spells/build_resin_structure.dm
@@ -58,7 +58,7 @@
desc = "The hunger..."
icon_state = "alien_acid"
-/obj/item/melee/touch_attack/alien/consume_resin/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/touch_attack/alien/consume_resin/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
if(target == user)
to_chat(user, "You stop trying to consume resin.")
..()
diff --git a/code/datums/spells/alien_spells/corrosive_acid_spit.dm b/code/datums/spells/alien_spells/corrosive_acid_spit.dm
index 2f32c2dcb0499..20593bdd37569 100644
--- a/code/datums/spells/alien_spells/corrosive_acid_spit.dm
+++ b/code/datums/spells/alien_spells/corrosive_acid_spit.dm
@@ -11,7 +11,7 @@
desc = "A fistfull of death."
icon_state = "alien_acid"
-/obj/item/melee/touch_attack/alien/corrosive_acid/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/touch_attack/alien/corrosive_acid/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
if(target == user)
to_chat(user, "You withdraw your readied acid.")
..()
@@ -45,7 +45,7 @@
desc = "The air warps around your hand, somehow the heat doesn't hurt."
icon_state = "alien_acid"
-/obj/item/melee/touch_attack/alien/burning_touch/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/touch_attack/alien/burning_touch/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
if(target == user)
to_chat(user, "You cool down your boiled aid.")
..()
diff --git a/code/datums/spells/banana_touch.dm b/code/datums/spells/banana_touch.dm
index 2c289884a91cd..052e657e807d7 100644
--- a/code/datums/spells/banana_touch.dm
+++ b/code/datums/spells/banana_touch.dm
@@ -24,13 +24,13 @@
/obj/item/melee/touch_attack/banana/apprentice
-/obj/item/melee/touch_attack/banana/apprentice/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/touch_attack/banana/apprentice/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
if(iswizard(target) && target != user)
to_chat(user, "Seriously?! Honk THEM, not me!")
return
..()
-/obj/item/melee/touch_attack/banana/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/touch_attack/banana/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
if(!proximity || target == user || !ishuman(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED))
return
diff --git a/code/datums/spells/mime.dm b/code/datums/spells/mime.dm
index e1229dbcc1612..cb8c1a4c182fe 100644
--- a/code/datums/spells/mime.dm
+++ b/code/datums/spells/mime.dm
@@ -134,7 +134,7 @@
desc = "It contains various pictures of mimes mid-performance, aswell as some illustrated tutorials."
icon_state = "bookmime"
-/obj/item/spellbook/oneuse/mime/attack_self(mob/user)
+/obj/item/spellbook/oneuse/mime/attack_self__legacy__attackchain(mob/user)
var/datum/spell/S = new spell
for(var/datum/spell/knownspell in user.mind.spell_list)
if(knownspell.type == S.type)
diff --git a/code/datums/spells/mime_malaise.dm b/code/datums/spells/mime_malaise.dm
index 209863ada380f..8ce906e055a71 100644
--- a/code/datums/spells/mime_malaise.dm
+++ b/code/datums/spells/mime_malaise.dm
@@ -19,7 +19,7 @@
icon_state = "fleshtostone"
item_state = "fleshtostone"
-/obj/item/melee/touch_attack/mime_malaise/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/touch_attack/mime_malaise/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
if(!proximity || target == user || !ishuman(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED))
return
diff --git a/code/datums/spells/spacetime_dist.dm b/code/datums/spells/spacetime_dist.dm
index e487936fe03fe..e7cee2a416a0d 100644
--- a/code/datums/spells/spacetime_dist.dm
+++ b/code/datums/spells/spacetime_dist.dm
@@ -110,7 +110,7 @@
if(!cant_teleport)
walk_link(AM)
-/obj/effect/cross_action/spacetime_dist/attackby(obj/item/W, mob/user, params)
+/obj/effect/cross_action/spacetime_dist/attackby__legacy__attackchain(obj/item/W, mob/user, params)
if(user.drop_item(W))
walk_link(W)
else
diff --git a/code/game/atom/atom_tool_acts.dm b/code/game/atom/atom_tool_acts.dm
new file mode 100644
index 0000000000000..6c56e5972da84
--- /dev/null
+++ b/code/game/atom/atom_tool_acts.dm
@@ -0,0 +1,166 @@
+/**
+ * ## Item interaction
+ *
+ * Handles non-combat iteractions of a tool on this atom,
+ * such as using a tool on a wall to deconstruct it,
+ * or scanning someone with a health analyzer
+ */
+/atom/proc/base_item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ SHOULD_CALL_PARENT(TRUE)
+ PROTECTED_PROC(TRUE)
+
+ // We do not have a combat mode or secondary actions like /tg/, so instead
+ // I'm unilaterally deciding it here: If you are not on harm intent, tool
+ // interactions are not attacks. Shit like the autolathe accepting
+ // screwdrivers on harm intent is unintuitive and needs to go away, and there
+ // are dozens of ${TOOL}_act procs that do constant harm intent checks.
+ var/tool_return = tool_act(user, tool, modifiers)
+ if(tool_return)
+ return tool_return
+
+ var/early_sig_return = NONE
+ /*
+ * This is intentionally using `||` instead of `|` to short-circuit the signal calls
+ * This is because we want to return early if ANY of these signals return a value
+ *
+ * This puts priority on the atom's signals, then the tool's signals, then the user's signals
+ */
+ early_sig_return = SEND_SIGNAL(src, COMSIG_INTERACT_TARGET, user, tool, modifiers) \
+ || SEND_SIGNAL(tool, COMSIG_INTERACTING, user, src, modifiers) \
+ || SEND_SIGNAL(user, COMSIG_INTERACT_USER, src, tool, modifiers)
+
+ if(early_sig_return)
+ return early_sig_return
+
+ if(new_attack_chain)
+ var/self_interaction = item_interaction(user, tool, modifiers)
+ if(self_interaction)
+ return self_interaction
+
+ if(tool.new_attack_chain)
+ var/interact_return = tool.interact_with_atom(src, user, modifiers)
+ if(interact_return)
+ return interact_return
+
+ return NONE
+
+/**
+ *
+ * ## Tool Act
+ *
+ * Handles using specific tools on this atom directly.
+ *
+ * Handles the tool_acts in particular, such as wrenches and screwdrivers.
+ *
+ * This can be overriden to handle unique "tool interactions"
+ * IE using an item like a tool (when it's not actually one)
+ * but otherwise does nothing that [item_interaction] doesn't already do.
+ *
+ * In other words, use sparingly. It's harder to use (correctly) than [item_interaction].
+ */
+/atom/proc/tool_act(mob/living/user, obj/item/tool, list/modifiers)
+ SHOULD_CALL_PARENT(TRUE)
+ PROTECTED_PROC(TRUE)
+
+ if(SEND_SIGNAL(src, COMSIG_TOOL_ATTACK, tool, user) & COMPONENT_CANCEL_TOOLACT)
+ return FALSE
+
+ var/tool_type = tool.tool_behaviour
+ if(!tool_type)
+ return NONE
+
+ var/act_result = NONE // or FALSE, or null, as some things may return
+
+ switch(tool_type)
+ if(TOOL_CROWBAR)
+ act_result = crowbar_act(user, tool)
+ if(TOOL_MULTITOOL)
+ act_result = multitool_act(user, tool)
+ if(TOOL_SCREWDRIVER)
+ act_result = screwdriver_act(user, tool)
+ if(TOOL_WRENCH)
+ act_result = wrench_act(user, tool)
+ if(TOOL_WIRECUTTER)
+ act_result = wirecutter_act(user, tool)
+ if(TOOL_WELDER)
+ act_result = welder_act(user, tool)
+
+ if(!act_result)
+ return NONE
+
+ return act_result
+
+/**
+ * Called when this atom has an item used on it.
+ * IE, a mob is clicking on this atom with an item.
+ *
+ * Return an ITEM_INTERACT_ flag in the event the interaction was handled, to cancel further interaction code.
+ * Return NONE to allow default interaction / tool handling.
+ */
+/atom/proc/item_interaction(mob/living/user, obj/item/used, list/modifiers)
+ return NONE
+
+/**
+ * Called when this item is being used to interact with an atom,
+ * IE, a mob is clicking on an atom with this item.
+ *
+ * Return an ITEM_INTERACT_ flag in the event the interaction was handled, to cancel further interaction code.
+ * Return NONE to allow default interaction / tool handling.
+ */
+/obj/item/proc/interact_with_atom(atom/target, mob/living/user, list/modifiers)
+ return NONE
+
+/**
+ * ## Ranged item interaction
+ *
+ * Handles non-combat ranged interactions of a tool on this atom,
+ * such as shooting a gun in the direction of someone*,
+ * having a scanner you can point at someone to scan them at any distance,
+ * or pointing a laser pointer at something.
+ *
+ * *While this intuitively sounds combat related, it is not,
+ * because a "combat use" of a gun is gun-butting.
+ */
+/atom/proc/base_ranged_item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ SHOULD_CALL_PARENT(TRUE)
+ PROTECTED_PROC(TRUE)
+
+ // See [base_item_interaction] for defails on why this is using `||` (TL;DR it's short circuiting)
+ var/early_sig_return = SEND_SIGNAL(src, COMSIG_INTERACT_RANGED, user, tool, modifiers) \
+ || SEND_SIGNAL(tool, COMSIG_INTERACTING_RANGED, user, src, modifiers)
+
+ if(early_sig_return)
+ return early_sig_return
+
+ var/self_interaction = ranged_item_interaction(user, tool, modifiers)
+ if(self_interaction)
+ return self_interaction
+
+ var/interact_return = tool.ranged_interact_with_atom(src, user, modifiers)
+ if(interact_return)
+ return interact_return
+
+ return NONE
+
+/**
+ * Called when this atom has an item used on it from a distance.
+ * IE, a mob is clicking on this atom with an item and is not adjacent.
+ *
+ * Does NOT include Telekinesis users, they are considered adjacent generally.
+ *
+ * Return an ITEM_INTERACT_ flag in the event the interaction was handled, to cancel further interaction code.
+ */
+/atom/proc/ranged_item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ return NONE
+
+/**
+ * Called when this item is being used to interact with an atom from a distance,
+ * IE, a mob is clicking on an atom with this item and is not adjacent.
+ *
+ * Does NOT include Telekinesis users, they are considered adjacent generally
+ * (so long as this item is adjacent to the atom).
+ *
+ * Return an ITEM_INTERACT_ flag in the event the interaction was handled, to cancel further interaction code.
+ */
+/obj/item/proc/ranged_interact_with_atom(atom/target, mob/living/user, list/modifiers)
+ return NONE
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 838de5d24408d..0f77644ea66c1 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -129,6 +129,9 @@
///When a projectile ricochets off this atom, it deals the normal damage * this modifier to this atom
var/receive_ricochet_damage_coeff = 0.33
+ /// Whether this atom is using the new attack chain.
+ var/new_attack_chain = FALSE
+
/atom/New(loc, ...)
SHOULD_CALL_PARENT(TRUE)
if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New()
@@ -549,23 +552,6 @@
if(reagents)
reagents.temperature_reagents(exposed_temperature)
-/// If it returns TRUE, attack chain stops
-/atom/proc/tool_act(mob/living/user, obj/item/I, tool_type)
- switch(tool_type)
- if(TOOL_CROWBAR)
- return crowbar_act(user, I)
- if(TOOL_MULTITOOL)
- return multitool_act(user, I)
- if(TOOL_SCREWDRIVER)
- return screwdriver_act(user, I)
- if(TOOL_WRENCH)
- return wrench_act(user, I)
- if(TOOL_WIRECUTTER)
- return wirecutter_act(user, I)
- if(TOOL_WELDER)
- return welder_act(user, I)
-
-
// Tool-specific behavior procs. To be overridden in subtypes.
/atom/proc/crowbar_act(mob/living/user, obj/item/I)
return
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index ba00d87662230..0af9086620762 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -504,9 +504,9 @@
verbs.Cut()
return
-/atom/movable/overlay/attackby(a, b, c)
+/atom/movable/overlay/attackby__legacy__attackchain(a, b, c)
if(master)
- return master.attackby(a, b, c)
+ return master.attackby__legacy__attackchain(a, b, c)
/atom/movable/overlay/attack_hand(a, b, c)
if(master)
diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm
index 3e140fca048e2..c85ff6a05c3b1 100644
--- a/code/game/dna/dna_modifier.dm
+++ b/code/game/dna/dna_modifier.dm
@@ -190,7 +190,7 @@
QDEL_LIST_CONTENTS(L.grabbed_by)
return TRUE
-/obj/machinery/dna_scannernew/attackby(obj/item/I, mob/user, params)
+/obj/machinery/dna_scannernew/attackby__legacy__attackchain(obj/item/I, mob/user, params)
if(istype(I, /obj/item/reagent_containers/glass))
if(beaker)
to_chat(user, "A beaker is already loaded into the machine.")
@@ -327,7 +327,7 @@
idle_power_consumption = 10
active_power_consumption = 400
-/obj/machinery/computer/scan_consolenew/attackby(obj/item/I, mob/user, params)
+/obj/machinery/computer/scan_consolenew/attackby__legacy__attackchain(obj/item/I, mob/user, params)
if(istype(I, /obj/item/disk/data)) //INSERT SOME diskS
if(!disk)
user.drop_item()
diff --git a/code/game/gamemodes/cult/blood_magic.dm b/code/game/gamemodes/cult/blood_magic.dm
index 348345a6aee57..6f97e380e15eb 100644
--- a/code/game/gamemodes/cult/blood_magic.dm
+++ b/code/game/gamemodes/cult/blood_magic.dm
@@ -410,10 +410,10 @@
/obj/item/melee/blood_magic/customised_abstract_text(mob/living/carbon/owner)
return "[owner.p_their(TRUE)] [owner.l_hand == src ? "left hand" : "right hand"] is burning in blood-red fire."
-/obj/item/melee/blood_magic/attack_self(mob/living/user)
- afterattack(user, user, TRUE)
+/obj/item/melee/blood_magic/attack_self__legacy__attackchain(mob/living/user)
+ attackby__legacy__attackchain(user, user, TRUE)
-/obj/item/melee/blood_magic/attack(mob/living/M, mob/living/carbon/user)
+/obj/item/melee/blood_magic/attack__legacy__attackchain(mob/living/M, mob/living/carbon/user)
if(!iscarbon(user) || !IS_CULTIST(user))
uses = 0
qdel(src)
@@ -421,7 +421,7 @@
add_attack_logs(user, M, "used a cult spell ([src]) on")
M.lastattacker = user.real_name
-/obj/item/melee/blood_magic/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/blood_magic/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
. = ..()
if(invocation)
user.whisper(invocation)
@@ -443,7 +443,7 @@
color = RUNE_COLOR_RED
invocation = "Fuu ma'jin!"
-/obj/item/melee/blood_magic/stun/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/blood_magic/stun/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
if(!isliving(target) || !proximity)
return
var/mob/living/L = target
@@ -489,7 +489,7 @@
desc = "Will teleport a cultist to a teleport rune on contact."
invocation = "Sas'so c'arta forbici!"
-/obj/item/melee/blood_magic/teleport/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/blood_magic/teleport/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
if(user.holy_check())
return
var/list/potential_runes = list()
@@ -563,7 +563,7 @@
invocation = "In'totum Lig'abis!"
color = "#000000" // black
-/obj/item/melee/blood_magic/shackles/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/blood_magic/shackles/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
if(user.holy_check())
return
if(iscarbon(target) && proximity)
@@ -626,7 +626,7 @@
[METAL_TO_CONSTRUCT_SHELL_CONVERSION] metal into a construct shell\n
Airlocks into brittle runed airlocks after a delay (harm intent)"}
-/obj/item/melee/blood_magic/construction/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+/obj/item/melee/blood_magic/construction/afterattack__legacy__attackchain(atom/target, mob/user, proximity_flag, click_parameters)
if(user.holy_check())
return
if(proximity_flag)
@@ -682,7 +682,7 @@
desc = "Will equipt cult combat gear onto a cultist on contact."
color = "#33cc33" // green
-/obj/item/melee/blood_magic/armor/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/blood_magic/armor/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
if(user.holy_check())
return
if(iscarbon(target) && proximity)
@@ -707,7 +707,7 @@
color = "#9c0651"
has_source = FALSE //special, only availible for a blood cost.
-/obj/item/melee/blood_magic/empower/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+/obj/item/melee/blood_magic/empower/afterattack__legacy__attackchain(atom/target, mob/user, proximity_flag, click_parameters)
if(user.holy_check())
return
if(proximity_flag)
@@ -857,7 +857,7 @@
new /obj/effect/temp_visual/cult/sparks(get_turf(H))
// This should really be split into multiple procs
-/obj/item/melee/blood_magic/manipulator/afterattack(atom/target, mob/living/carbon/human/user, proximity)
+/obj/item/melee/blood_magic/manipulator/afterattack__legacy__attackchain(atom/target, mob/living/carbon/human/user, proximity)
if(user.holy_check())
return
if(!proximity)
@@ -912,7 +912,7 @@
to_chat(user, "Your blood rite has gained [temp] charge\s from blood sources around you!")
uses += max(1, temp)
-/obj/item/melee/blood_magic/manipulator/attack_self(mob/living/user)
+/obj/item/melee/blood_magic/manipulator/attack_self__legacy__attackchain(mob/living/user)
if(user.holy_check())
return
var/list/options = list("Blood Orb (50)" = image(icon = 'icons/obj/cult.dmi', icon_state = "summoning_orb"),
diff --git a/code/game/gamemodes/cult/cult_actions.dm b/code/game/gamemodes/cult/cult_actions.dm
index 315350410ebb7..83c1b3155bbbe 100644
--- a/code/game/gamemodes/cult/cult_actions.dm
+++ b/code/game/gamemodes/cult/cult_actions.dm
@@ -123,6 +123,6 @@
if(D)
owner.remove_from_mob(D)
owner.put_in_hands(D)
- D.attack_self(owner)
+ D.activate_self(owner)
else
to_chat(usr, "You do not seem to carry a ritual dagger to draw a rune with. If you need a new one, prepare and use the Summon Dagger spell.")
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index f05682e9d1b17..053ecf814bff2 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -23,6 +23,7 @@
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
sprite_sheets_inhand = list("Skrell" = 'icons/mob/clothing/species/skrell/held.dmi') // To stop skrell stabbing themselves in the head
+ new_attack_chain = TRUE
/obj/item/melee/cultblade/Initialize(mapload)
. = ..()
@@ -33,7 +34,10 @@
. = ..()
. += "This blade is a powerful weapon, capable of severing limbs easily. Nonbelievers are unable to use this weapon. Striking a nonbeliever after downing them with your cult magic will stun them completely."
-/obj/item/melee/cultblade/attack(mob/living/target, mob/living/carbon/human/user)
+/obj/item/melee/cultblade/pre_attack(atom/target, mob/living/user, params)
+ if(..())
+ return FINISH_ATTACK
+
if(!IS_CULTIST(user))
user.Weaken(10 SECONDS)
user.unEquip(src, 1)
@@ -44,12 +48,17 @@
H.apply_damage(rand(force/2, force), BRUTE, pick("l_arm", "r_arm"))
else
user.adjustBruteLoss(rand(force/2, force))
- return
+
+ return FINISH_ATTACK
+
+/obj/item/melee/cultblade/attack(mob/living/target, mob/living/carbon/human/user)
+ if(..())
+ return FINISH_ATTACK
+
if(!IS_CULTIST(target))
var/datum/status_effect/cult_stun_mark/S = target.has_status_effect(STATUS_EFFECT_CULT_STUN)
if(S)
S.trigger()
- ..()
/obj/item/melee/cultblade/pickup(mob/living/user)
. = ..()
@@ -241,7 +250,7 @@
/obj/item/whetstone/cult/update_icon_state()
icon_state = "cult_sharpener[used ? "_used" : ""]"
-/obj/item/whetstone/cult/attackby(obj/item/I, mob/user, params)
+/obj/item/whetstone/cult/attackby__legacy__attackchain(obj/item/I, mob/user, params)
..()
if(used)
to_chat(user, "[src] crumbles to ashes.")
@@ -281,7 +290,7 @@
icon_state ="shuttlecurse"
var/global/curselimit = 0
-/obj/item/shuttle_curse/attack_self(mob/living/user)
+/obj/item/shuttle_curse/attack_self__legacy__attackchain(mob/living/user)
if(!IS_CULTIST(user))
user.unEquip(src, 1)
user.Weaken(10 SECONDS)
@@ -330,7 +339,7 @@
pulled.forceMove(turf_behind)
. = pulled
-/obj/item/cult_shift/attack_self(mob/user)
+/obj/item/cult_shift/attack_self__legacy__attackchain(mob/user)
if(!uses || !iscarbon(user))
to_chat(user, "[src] is dull and unmoving in your hands.")
@@ -625,7 +634,7 @@
playsound(T, 'sound/effects/glassbr3.ogg', 100)
qdel(src)
-/obj/item/cult_spear/attack(mob/living/M, mob/living/user, def_zone)
+/obj/item/cult_spear/attack__legacy__attackchain(mob/living/M, mob/living/user, def_zone)
. = ..()
var/datum/status_effect/cult_stun_mark/S = M.has_status_effect(STATUS_EFFECT_CULT_STUN)
if(S && HAS_TRAIT(src, TRAIT_WIELDED))
@@ -671,7 +680,7 @@
fire_sound = 'sound/magic/wand_teleport.ogg'
flags = NOBLUDGEON | DROPDEL
-/obj/item/gun/projectile/shotgun/boltaction/enchanted/arcane_barrage/blood/afterattack(atom/target, mob/living/user, flag, params)
+/obj/item/gun/projectile/shotgun/boltaction/enchanted/arcane_barrage/blood/afterattack__legacy__attackchain(atom/target, mob/living/user, flag, params)
if(user.holy_check())
return
..()
@@ -722,7 +731,7 @@
w_class = WEIGHT_CLASS_SMALL
-/obj/item/portal_amulet/afterattack(atom/O, mob/user, proximity)
+/obj/item/portal_amulet/afterattack__legacy__attackchain(atom/O, mob/user, proximity)
. = ..()
if(!IS_CULTIST(user))
if(!iscarbon(user))
@@ -802,7 +811,7 @@
if(target)
exit = new /obj/effect/cult_portal_exit(target)
-/obj/effect/portal/cult/attackby(obj/I, mob/user, params)
+/obj/effect/portal/cult/attackby__legacy__attackchain(obj/I, mob/user, params)
if(istype(I, /obj/item/melee/cultblade/dagger) && IS_CULTIST(user) || istype(I, /obj/item/nullrod) && HAS_MIND_TRAIT(user, TRAIT_HOLY))
to_chat(user, "You close the portal with your [I].")
playsound(src, 'sound/magic/magic_missile.ogg', 100, TRUE)
diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm
index eea528a729b45..b948bad9da635 100644
--- a/code/game/gamemodes/cult/cult_structures.dm
+++ b/code/game/gamemodes/cult/cult_structures.dm
@@ -52,7 +52,7 @@
. += "The magic in [src] is weak, it will be ready to use again in [get_ETA()]."
. += "[src] is [anchored ? "":"not "]secured to the floor."
-/obj/structure/cult/functional/attackby(obj/item/I, mob/user, params)
+/obj/structure/cult/functional/attackby__legacy__attackchain(obj/item/I, mob/user, params)
if(istype(I, /obj/item/melee/cultblade/dagger) && IS_CULTIST(user))
if(user.holy_check())
return
@@ -180,7 +180,7 @@
. = ..()
icon_state = GET_CULT_DATA(forge_icon_state, "forge")
-/obj/structure/cult/functional/forge/attackby(obj/item/I, mob/user, params)
+/obj/structure/cult/functional/forge/attackby__legacy__attackchain(obj/item/I, mob/user, params)
if(istype(I, /obj/item/grab))
var/obj/item/grab/G = I
if(!iscarbon(G.affecting))
diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm
index e20ec8dba5e33..fd1e6f3f1438b 100644
--- a/code/game/gamemodes/cult/ritual.dm
+++ b/code/game/gamemodes/cult/ritual.dm
@@ -32,28 +32,31 @@
. += "Striking another cultist with it will purge holy water from them."
. += "Striking a noncultist will tear their flesh, additionally, if you recently downed them with cult magic it will stun them completely."
-/obj/item/melee/cultblade/dagger/attack(mob/living/M, mob/living/user)
- if(IS_CULTIST(M))
- if(M.reagents && M.reagents.has_reagent("holywater")) //allows cultists to be rescued from the clutches of ordained religion
- if(M == user) // Targeting yourself
+/obj/item/melee/cultblade/dagger/pre_attack(atom/target, mob/living/user, params)
+ if(..())
+ return FINISH_ATTACK
+
+ if(IS_CULTIST(target))
+ if(target.reagents && target.reagents.has_reagent("holywater")) //allows cultists to be rescued from the clutches of ordained religion
+ if(target == user) // Targeting yourself
to_chat(user, "You can't remove holy water from yourself!")
+
else // Targeting someone else
- to_chat(user, "You remove the taint from [M].")
- to_chat(M, "[user] removes the taint from your body.")
- M.reagents.del_reagent("holywater")
- add_attack_logs(user, M, "Hit with [src], removing the holy water from them")
- return FALSE
- else
- var/datum/status_effect/cult_stun_mark/S = M.has_status_effect(STATUS_EFFECT_CULT_STUN)
- if(S)
- S.trigger()
- . = ..()
+ to_chat(user, "You remove the taint from [target].")
+ to_chat(target, "[user] removes the taint from your body.")
+ target.reagents.del_reagent("holywater")
+ add_attack_logs(user, target, "Hit with [src], removing the holy water from them")
-/obj/item/melee/cultblade/dagger/attack_self(mob/user)
- if(!IS_CULTIST(user))
- to_chat(user, "[src] is covered in unintelligible shapes and markings.")
+ return FINISH_ATTACK
+
+/obj/item/melee/cultblade/dagger/activate_self(mob/user)
+ if(..())
return
- scribe_rune(user)
+
+ if(IS_CULTIST(user))
+ scribe_rune(user)
+ else
+ to_chat(user, "[src] is covered in unintelligible shapes and markings.")
/obj/item/melee/cultblade/dagger/proc/narsie_rune_check(mob/living/user, area/A)
var/datum/game_mode/gamemode = SSticker.mode
@@ -97,7 +100,6 @@
return FALSE
return TRUE
-
/obj/item/melee/cultblade/dagger/proc/scribe_rune(mob/living/user)
var/list/shields = list()
var/list/possible_runes = list()
diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm
index 55674d5770570..ebe886ff10c6e 100644
--- a/code/game/gamemodes/cult/runes.dm
+++ b/code/game/gamemodes/cult/runes.dm
@@ -75,7 +75,7 @@ To draw a rune, use a ritual dagger.
if(req_keyword && keyword)
. += "Keyword: [keyword]"
-/obj/effect/rune/attackby(obj/I, mob/user, params)
+/obj/effect/rune/attackby__legacy__attackchain(obj/I, mob/user, params)
if(istype(I, /obj/item/melee/cultblade/dagger) && IS_CULTIST(user))
if(!can_dagger_erase_rune(user))
return
@@ -1098,7 +1098,7 @@ structure_check() searches for nearby cultist structures required for the invoca
sleep(40)
new /obj/singularity/narsie/large(T) //Causes Nar'Sie to spawn even if the rune has been removed
-/obj/effect/rune/narsie/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal.
+/obj/effect/rune/narsie/attackby__legacy__attackchain(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal.
if((istype(I, /obj/item/melee/cultblade/dagger) && IS_CULTIST(user)))
log_game("Summon Narsie rune erased by [key_name(user)] with a cult dagger")
message_admins("[key_name_admin(user)] erased a Narsie rune with a cult dagger")
diff --git a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
index 235cab74862eb..02bc96a04065a 100644
--- a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
+++ b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
@@ -191,12 +191,12 @@ CONTENTS:
item_state = "silencer"
origin_tech = "materials=4;programming=7;abductor=3"
-/obj/item/abductor/silencer/attack(mob/living/M, mob/user)
+/obj/item/abductor/silencer/attack__legacy__attackchain(mob/living/M, mob/user)
if(!AbductorCheck(user))
return
radio_off(M, user)
-/obj/item/abductor/silencer/afterattack(atom/target, mob/living/user, flag, params)
+/obj/item/abductor/silencer/afterattack__legacy__attackchain(atom/target, mob/living/user, flag, params)
if(flag)
return
if(!AbductorCheck(user))
@@ -284,7 +284,7 @@ CONTENTS:
icon_state = "wonderprodProbe"
item_state = "wonderprodProbe"
-/obj/item/abductor_baton/attack(mob/target, mob/living/user)
+/obj/item/abductor_baton/attack__legacy__attackchain(mob/target, mob/living/user)
if(!isabductor(user))
return
@@ -316,7 +316,7 @@ CONTENTS:
if(BATON_PROBE)
ProbeAttack(L,user)
-/obj/item/abductor_baton/attack_self(mob/living/user)
+/obj/item/abductor_baton/attack_self__legacy__attackchain(mob/living/user)
toggle(user)
if(ishuman(user))
var/mob/living/carbon/human/H = user
@@ -449,7 +449,7 @@ CONTENTS:
var/mob/living/marked = null
var/obj/machinery/abductor/console/console
-/obj/item/abductor/gizmo/attack_self(mob/user)
+/obj/item/abductor/gizmo/attack_self__legacy__attackchain(mob/user)
if(!ScientistCheck(user))
return
if(!console)
@@ -464,7 +464,7 @@ CONTENTS:
icon_state = "gizmo_scan"
to_chat(user, "You switch the device to [mode==GIZMO_SCAN? "SCAN": "MARK"] MODE")
-/obj/item/abductor/gizmo/attack(mob/living/M, mob/user)
+/obj/item/abductor/gizmo/attack__legacy__attackchain(mob/living/M, mob/user)
if(!ScientistCheck(user))
return
if(!console)
@@ -477,7 +477,7 @@ CONTENTS:
if(GIZMO_MARK)
mark(M, user)
-/obj/item/abductor/gizmo/afterattack(atom/target, mob/living/user, flag, params)
+/obj/item/abductor/gizmo/afterattack__legacy__attackchain(atom/target, mob/living/user, flag, params)
if(flag)
return
if(!ScientistCheck(user))
@@ -531,7 +531,7 @@ CONTENTS:
item_state = "silencer"
var/mode = MIND_DEVICE_MESSAGE
-/obj/item/abductor/mind_device/attack_self(mob/user)
+/obj/item/abductor/mind_device/attack_self__legacy__attackchain(mob/user)
if(!ScientistCheck(user))
return
@@ -543,7 +543,7 @@ CONTENTS:
icon_state = "mind_device_message"
to_chat(user, "You switch the device to [mode == MIND_DEVICE_MESSAGE ? "TRANSMISSION" : "COMMAND"] MODE")
-/obj/item/abductor/mind_device/afterattack(atom/target, mob/living/user, flag, params)
+/obj/item/abductor/mind_device/afterattack__legacy__attackchain(atom/target, mob/living/user, flag, params)
if(!ScientistCheck(user))
return
diff --git a/code/game/gamemodes/miniantags/abduction/machinery/console.dm b/code/game/gamemodes/miniantags/abduction/machinery/console.dm
index db6453bcb7f9d..f1539a9929176 100644
--- a/code/game/gamemodes/miniantags/abduction/machinery/console.dm
+++ b/code/game/gamemodes/miniantags/abduction/machinery/console.dm
@@ -218,7 +218,7 @@
vest = V
return TRUE
-/obj/machinery/abductor/console/attackby(obj/O, mob/user, params)
+/obj/machinery/abductor/console/attackby__legacy__attackchain(obj/O, mob/user, params)
if(istype(O, /obj/item/abductor/gizmo) && AddGizmo(O))
to_chat(user, "You link the tool to the console.")
else if(istype(O, /obj/item/clothing/suit/armor/abductor/vest) && AddVest(O))
diff --git a/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm b/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm
index 9d13aa35d047a..9d70325835a9f 100644
--- a/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm
+++ b/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm
@@ -82,7 +82,7 @@
return
ui_interact(user)
-/obj/machinery/abductor/gland_dispenser/attackby(obj/item/W, mob/user, params)
+/obj/machinery/abductor/gland_dispenser/attackby__legacy__attackchain(obj/item/W, mob/user, params)
if(istype(W, /obj/item/organ/internal/heart/gland))
if(!user.drop_item())
return
diff --git a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
index 1ee6822d206bd..339d4f4e77372 100644
--- a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
+++ b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm
@@ -174,7 +174,7 @@
H.clear_restraints()
return
-/obj/machinery/abductor/experiment/attackby(obj/item/G, mob/user)
+/obj/machinery/abductor/experiment/attackby__legacy__attackchain(obj/item/G, mob/user)
if(istype(G, /obj/item/grab))
var/obj/item/grab/grabbed = G
if(!ishuman(grabbed.affecting))
diff --git a/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm b/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm
index df69ab5e4d211..df5df3aae74a1 100644
--- a/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm
+++ b/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm
@@ -230,7 +230,7 @@
desc = "It still beats furiously, emitting an aura of fear."
color = COLOR_BLACK
-/obj/item/organ/internal/heart/demon/shadow/attack_self(mob/living/user)
+/obj/item/organ/internal/heart/demon/shadow/attack_self__legacy__attackchain(mob/living/user)
. = ..()
user.drop_item()
insert(user)
diff --git a/code/game/gamemodes/miniantags/demons/slaughter_demon/slaughter.dm b/code/game/gamemodes/miniantags/demons/slaughter_demon/slaughter.dm
index beb1b7ae99c84..67e9eca6a599a 100644
--- a/code/game/gamemodes/miniantags/demons/slaughter_demon/slaughter.dm
+++ b/code/game/gamemodes/miniantags/demons/slaughter_demon/slaughter.dm
@@ -220,7 +220,7 @@
/obj/item/organ/internal/heart/demon/prepare_eat()
return // Just so people don't accidentally waste it
-/obj/item/organ/internal/heart/demon/attack_self(mob/living/user)
+/obj/item/organ/internal/heart/demon/attack_self__legacy__attackchain(mob/living/user)
user.visible_message("[user] raises [src] to [user.p_their()] mouth and tears into it with [user.p_their()] teeth!", \
"An unnatural hunger consumes you. You raise [src] to your mouth and devour it!")
playsound(user, 'sound/misc/demon_consume.ogg', 50, 1)
@@ -230,7 +230,7 @@
//The loot from killing a slaughter demon - can be consumed to allow the user to blood crawl
/// SLAUGHTER DEMON HEART
-/obj/item/organ/internal/heart/demon/slaughter/attack_self(mob/living/user)
+/obj/item/organ/internal/heart/demon/slaughter/attack_self__legacy__attackchain(mob/living/user)
..()
// Eating the heart for the first time. Gives basic bloodcrawling. This is the only time we need to insert the heart.
diff --git a/code/game/gamemodes/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm
index 77eb510f82d27..83e47bff1c163 100644
--- a/code/game/gamemodes/miniantags/guardian/guardian.dm
+++ b/code/game/gamemodes/miniantags/guardian/guardian.dm
@@ -279,7 +279,7 @@
"Blue" = "#0000FF")
var/name_list = list("Aries", "Leo", "Sagittarius", "Taurus", "Virgo", "Capricorn", "Gemini", "Libra", "Aquarius", "Cancer", "Scorpio", "Pisces")
-/obj/item/guardiancreator/attack_self(mob/living/user)
+/obj/item/guardiancreator/attack_self__legacy__attackchain(mob/living/user)
if(has_guardian(user))
to_chat(user, "You already have a [mob_name]!")
return
diff --git a/code/game/gamemodes/miniantags/guardian/types/explosive_guardian.dm b/code/game/gamemodes/miniantags/guardian/types/explosive_guardian.dm
index cd90466c93f15..8ed033b7f9cbb 100644
--- a/code/game/gamemodes/miniantags/guardian/types/explosive_guardian.dm
+++ b/code/game/gamemodes/miniantags/guardian/types/explosive_guardian.dm
@@ -90,7 +90,7 @@
user.Stun(3 SECONDS)//A bomb went off in your hands. Actually lets people follow up with it if they bait someone, right now it is unreliable.
qdel(src)
-/obj/item/guardian_bomb/attackby(obj/item/W, mob/living/user)
+/obj/item/guardian_bomb/attackby__legacy__attackchain(obj/item/W, mob/living/user)
detonate(user)
/obj/item/guardian_bomb/attack_hand(mob/user)
diff --git a/code/game/gamemodes/miniantags/guardian/types/protector.dm b/code/game/gamemodes/miniantags/guardian/types/protector.dm
index 2be939f8cff59..e385fd1e329d4 100644
--- a/code/game/gamemodes/miniantags/guardian/types/protector.dm
+++ b/code/game/gamemodes/miniantags/guardian/types/protector.dm
@@ -137,7 +137,7 @@
P.on_hit(src, 0)
return FALSE
-/obj/effect/guardianshield/attacked_by(obj/item/I, mob/living/user)
+/obj/effect/guardianshield/attacked_by__legacy__attackchain(obj/item/I, mob/living/user)
if(I.force)
user.visible_message("[user] has hit [src] with [I]!", "You hit [src] with [I]!")
linked_guardian.apply_damage(I.force, I.damtype)
diff --git a/code/game/gamemodes/miniantags/morph/morph.dm b/code/game/gamemodes/miniantags/morph/morph.dm
index 1379ec4abfec9..8e05021c240ce 100644
--- a/code/game/gamemodes/miniantags/morph/morph.dm
+++ b/code/game/gamemodes/miniantags/morph/morph.dm
@@ -220,7 +220,7 @@
#define MORPH_ATTACKED if((. = ..()) && morphed) mimic_spell.restore_form(src)
-/mob/living/simple_animal/hostile/morph/attackby(obj/item/O, mob/living/user)
+/mob/living/simple_animal/hostile/morph/attackby__legacy__attackchain(obj/item/O, mob/living/user)
if(user.a_intent == INTENT_HELP && ambush_prepared)
to_chat(user, "You try to use [O] on [src]... it seems different than no-")
ambush_attack(user, TRUE)
diff --git a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm
index 6a768f9d6bacc..c8ef3b159adbc 100644
--- a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm
+++ b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon.dm
@@ -745,7 +745,7 @@
visible_message("[M] [response_harm] [src].")
try_attack_mob(M)
-/mob/living/simple_animal/demon/pulse_demon/attackby(obj/item/O, mob/living/user)
+/mob/living/simple_animal/demon/pulse_demon/attackby__legacy__attackchain(obj/item/O, mob/living/user)
if(is_under_tile())
to_chat(user, "You can't interact with something that's under the floor!")
return
@@ -821,7 +821,7 @@
. = ..()
set_light(13, 2, "#bbbb00")
-/obj/item/organ/internal/heart/demon/pulse/attack_self(mob/living/user)
+/obj/item/organ/internal/heart/demon/pulse/attack_self__legacy__attackchain(mob/living/user)
. = ..()
user.drop_item()
insert(user)
diff --git a/code/game/gamemodes/miniantags/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm
index 1bf8400c4142c..3719b54a9881f 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant.dm
@@ -228,7 +228,7 @@
ghostize()
qdel(src)
-/mob/living/simple_animal/revenant/attackby(obj/item/W, mob/living/user, params)
+/mob/living/simple_animal/revenant/attackby__legacy__attackchain(obj/item/W, mob/living/user, params)
if(istype(W, /obj/item/nullrod))
visible_message("[src] violently flinches!", \
"As \the [W] passes through you, you feel your essence draining away!")
diff --git a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm
index b170895fd6f36..0a167ec7724c4 100644
--- a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm
+++ b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm
@@ -378,12 +378,12 @@
possessed_object.escape_chance = 100 // We cannot be contained
ADD_TRAIT(possessed_object, TRAIT_DODGE_ALL_OBJECTS, "Revenant")
- addtimer(CALLBACK(src, PROC_REF(attack), possessed_object, user), 1 SECONDS, TIMER_UNIQUE) // Short warm-up for floaty ambience
- attack_timers.Add(addtimer(CALLBACK(src, PROC_REF(attack), possessed_object, user), 4 SECONDS, TIMER_UNIQUE|TIMER_LOOP|TIMER_STOPPABLE)) // 5 second looping attacks
+ addtimer(CALLBACK(src, PROC_REF(attack__legacy__attackchain), possessed_object, user), 1 SECONDS, TIMER_UNIQUE) // Short warm-up for floaty ambience
+ attack_timers.Add(addtimer(CALLBACK(src, PROC_REF(attack__legacy__attackchain), possessed_object, user), 4 SECONDS, TIMER_UNIQUE|TIMER_LOOP|TIMER_STOPPABLE)) // 5 second looping attacks
addtimer(CALLBACK(possessed_object, TYPE_PROC_REF(/mob/living/simple_animal/possessed_object, death)), 70 SECONDS, TIMER_UNIQUE) // De-haunt the object
/// Handles finding a valid target and throwing us at it
-/datum/spell/aoe/revenant/haunt_object/proc/attack(mob/living/simple_animal/possessed_object/possessed_object, mob/living/simple_animal/revenant/user)
+/datum/spell/aoe/revenant/haunt_object/proc/attack__legacy__attackchain(mob/living/simple_animal/possessed_object/possessed_object, mob/living/simple_animal/revenant/user)
var/list/potential_victims = list()
for(var/turf/turf_to_search in spiral_range_turfs(aoe_range, get_turf(possessed_object)))
for(var/mob/living/carbon/potential_victim in turf_to_search)
diff --git a/code/game/gamemodes/nuclear/nuclear_challenge.dm b/code/game/gamemodes/nuclear/nuclear_challenge.dm
index 59906b7316587..e0ce8b2b03864 100644
--- a/code/game/gamemodes/nuclear/nuclear_challenge.dm
+++ b/code/game/gamemodes/nuclear/nuclear_challenge.dm
@@ -16,7 +16,7 @@
var/declaring_war = FALSE
var/total_tc = 0 //Total amount of telecrystals shared between nuke ops
-/obj/item/nuclear_challenge/attack_self(mob/living/user)
+/obj/item/nuclear_challenge/attack_self__legacy__attackchain(mob/living/user)
if(!check_allowed(user))
return
diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm
index 39456a29c8cc4..62423bd03a4c2 100644
--- a/code/game/gamemodes/nuclear/nuclearbomb.dm
+++ b/code/game/gamemodes/nuclear/nuclearbomb.dm
@@ -168,7 +168,7 @@ GLOBAL_VAR(bomb_set)
if(NUKE_CORE_FULLY_EXPOSED)
. += core ? "nukecore3" : "nukecore4"
-/obj/machinery/nuclearbomb/attackby(obj/item/O as obj, mob/user as mob, params)
+/obj/machinery/nuclearbomb/attackby__legacy__attackchain(obj/item/O as obj, mob/user as mob, params)
if(istype(O, /obj/item/disk/nuclear))
if(extended)
if(auth)
diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm
index a8106a2d21076..c316b9a78cd66 100644
--- a/code/game/gamemodes/nuclear/pinpointer.dm
+++ b/code/game/gamemodes/nuclear/pinpointer.dm
@@ -55,7 +55,7 @@
else if(mode == MODE_NUKE)
workbomb()
-/obj/item/pinpointer/attack_self(mob/user)
+/obj/item/pinpointer/attack_self__legacy__attackchain(mob/user)
if(mode == PINPOINTER_MODE_DET)
return
cycle(user)
@@ -396,7 +396,7 @@
///Var to track the linked detective gun
var/linked_gun_UID
-/obj/item/pinpointer/crew/attackby(obj/item/I, mob/living/user)
+/obj/item/pinpointer/crew/attackby__legacy__attackchain(obj/item/I, mob/living/user)
. = ..()
if(istype(I, /obj/item/gun/energy/detective))
link_gun(I.UID())
diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm
index cd7d2b48e911f..d9162487b06fa 100644
--- a/code/game/gamemodes/wizard/artefact.dm
+++ b/code/game/gamemodes/wizard/artefact.dm
@@ -59,7 +59,7 @@
dust_if_respawnable(C)
-/obj/item/contract/attack_self(mob/user as mob)
+/obj/item/contract/attack_self__legacy__attackchain(mob/user as mob)
if(..())
return
@@ -88,7 +88,7 @@
var/activate_descriptor = "reality"
var/rend_desc = "You should run now."
-/obj/item/veilrender/attack_self(mob/user as mob)
+/obj/item/veilrender/attack_self__legacy__attackchain(mob/user as mob)
if(charged)
new /obj/effect/rend(get_turf(user), spawn_type, spawn_amt, rend_desc)
charged = 0
@@ -128,7 +128,7 @@
if(spawn_amt_left <= 0)
qdel(src)
-/obj/effect/rend/attackby(obj/item/I as obj, mob/user as mob)
+/obj/effect/rend/attackby__legacy__attackchain(obj/item/I as obj, mob/user as mob)
if(istype(I, /obj/item/nullrod))
user.visible_message("[user] seals \the [src] with \the [I].")
qdel(src)
@@ -213,7 +213,7 @@
current_owner.update_sight()
current_owner.update_icons()
-/obj/item/scrying/attack_self(mob/user as mob)
+/obj/item/scrying/attack_self__legacy__attackchain(mob/user as mob)
if(in_use)
return
in_use = TRUE
@@ -286,13 +286,13 @@ GLOBAL_LIST_EMPTY(multiverse)
GLOB.multiverse.Remove(src)
return ..()
-/obj/item/multisword/attack(mob/living/M as mob, mob/living/user as mob) //to prevent accidental friendly fire or out and out grief.
+/obj/item/multisword/attack__legacy__attackchain(mob/living/M as mob, mob/living/user as mob) //to prevent accidental friendly fire or out and out grief.
if(M.real_name == user.real_name)
to_chat(user, "[src] detects benevolent energies in your target and redirects your attack!")
return
..()
-/obj/item/multisword/attack_self(mob/user)
+/obj/item/multisword/attack_self__legacy__attackchain(mob/user)
if(user.mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE)
to_chat(user, "You know better than to touch your teacher's stuff.")
return
@@ -689,7 +689,7 @@ GLOBAL_LIST_EMPTY(multiverse)
if(cooldown_time_left)
. += "[src] is being strained by the amount of risen skeletons thralls. It cannot be used to rise another skeleton thrall for [cooldown_time_left / 10] seconds."
-/obj/item/necromantic_stone/attack(mob/living/carbon/human/victim, mob/living/carbon/human/necromancer)
+/obj/item/necromantic_stone/attack__legacy__attackchain(mob/living/carbon/human/victim, mob/living/carbon/human/necromancer)
if(!istype(victim) || !istype(necromancer))
return ..()
@@ -855,7 +855,7 @@ GLOBAL_LIST_EMPTY(multiverse)
desc = "An enchanted mug which can be filled with any of various liquids on command."
icon_state = "evermug"
-/obj/item/reagent_containers/drinks/everfull/attack_self(mob/user)
+/obj/item/reagent_containers/drinks/everfull/attack_self__legacy__attackchain(mob/user)
var/static/list/options = list("Omnizine" = image(icon = 'icons/obj/storage.dmi', icon_state = "firstaid"),
"Ale" = image(icon = 'icons/obj/drinks.dmi', icon_state = "alebottle"),
"Wine" = image(icon = 'icons/obj/drinks.dmi', icon_state = "wineglass"),
diff --git a/code/game/gamemodes/wizard/godhand.dm b/code/game/gamemodes/wizard/godhand.dm
index 592de13f2b91e..7481db80dc9e9 100644
--- a/code/game/gamemodes/wizard/godhand.dm
+++ b/code/game/gamemodes/wizard/godhand.dm
@@ -29,7 +29,7 @@
/obj/item/melee/touch_attack/customised_abstract_text(mob/living/carbon/owner)
return "[owner.p_their(TRUE)] [owner.l_hand == src ? "left hand" : "right hand"] is burning in magic fire."
-/obj/item/melee/touch_attack/attack(mob/target, mob/living/carbon/user)
+/obj/item/melee/touch_attack/attack__legacy__attackchain(mob/target, mob/living/carbon/user)
if(!iscarbon(user)) //Look ma, no hands
return
if(HAS_TRAIT(user, TRAIT_HANDS_BLOCKED))
@@ -37,7 +37,7 @@
return
..()
-/obj/item/melee/touch_attack/afterattack(atom/target, mob/user, proximity)
+/obj/item/melee/touch_attack/afterattack__legacy__attackchain(atom/target, mob/user, proximity)
if(catchphrase)
user.say(catchphrase)
playsound(get_turf(user), on_use_sound, 50, 1)
@@ -53,7 +53,7 @@
icon_state = "disintegrate"
item_state = "disintegrate"
-/obj/item/melee/touch_attack/disintegrate/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/touch_attack/disintegrate/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
if(!proximity || target == user || !ismob(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //exploding after touching yourself would be bad
return
var/mob/M = target
@@ -69,7 +69,7 @@
icon_state = "fleshtostone"
item_state = "fleshtostone"
-/obj/item/melee/touch_attack/fleshtostone/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/touch_attack/fleshtostone/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
if(!proximity || target == user || !isliving(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //getting hard after touching yourself would also be bad
return
var/mob/living/L = target
@@ -86,7 +86,7 @@
item_state = "disintegrate"
needs_permit = FALSE
-/obj/item/melee/touch_attack/fake_disintegrate/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/touch_attack/fake_disintegrate/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
if(!proximity || target == user || !ismob(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //not exploding after touching yourself would be bad
return
do_sparks(4, 0, target.loc)
@@ -101,7 +101,7 @@
icon_state = "cluwnecurse"
item_state = "cluwnecurse"
-/obj/item/melee/touch_attack/cluwne/afterattack(atom/target, mob/living/carbon/user, proximity)
+/obj/item/melee/touch_attack/cluwne/afterattack__legacy__attackchain(atom/target, mob/living/carbon/user, proximity)
if(!proximity || target == user || !ishuman(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //clowning around after touching yourself would unsurprisingly, be bad
return
diff --git a/code/game/gamemodes/wizard/magic_tarot.dm b/code/game/gamemodes/wizard/magic_tarot.dm
index 9eed402337ce6..5f7eed073e965 100644
--- a/code/game/gamemodes/wizard/magic_tarot.dm
+++ b/code/game/gamemodes/wizard/magic_tarot.dm
@@ -16,7 +16,7 @@
maximum_cards = 5
our_card_cooldown_time = 12 SECONDS // A minute for a full hand of 5 cards
-/obj/item/tarot_generator/attack_self(mob/user)
+/obj/item/tarot_generator/attack_self__legacy__attackchain(mob/user)
if(!COOLDOWN_FINISHED(src, card_cooldown))
to_chat(user, "[src]'s magic is still recovering from the last card, wait [round(COOLDOWN_TIMELEFT(src, card_cooldown) / 10)] more second\s!")
return
@@ -50,7 +50,7 @@
///How many cards in a pack. 3 in base, 5 in jumbo, 7 in mega
var/cards = 3
-/obj/item/tarot_card_pack/attack_self(mob/user)
+/obj/item/tarot_card_pack/attack_self__legacy__attackchain(mob/user)
user.visible_message("[user] tears open [src].", \
"You tear open [src]!")
playsound(loc, 'sound/items/poster_ripped.ogg', 50, TRUE)
@@ -92,7 +92,7 @@
else
. += "We have the Ink... Could you provide your Vision instead?"
-/obj/item/blank_tarot_card/attack_self(mob/user)
+/obj/item/blank_tarot_card/attack_self__legacy__attackchain(mob/user)
if(!ishuman(user))
return
if(!let_people_choose)
@@ -181,7 +181,7 @@
if(!face_down)
. += "[src] [our_tarot.extended_desc]"
-/obj/item/magic_tarot_card/attack_self(mob/user)
+/obj/item/magic_tarot_card/attack_self__legacy__attackchain(mob/user)
poof()
if(has_been_activated)
return
diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm
index 7e3c863c14817..3d45da2bab258 100644
--- a/code/game/gamemodes/wizard/soulstone.dm
+++ b/code/game/gamemodes/wizard/soulstone.dm
@@ -87,7 +87,7 @@
animate(offset = 0, time = 10 SECONDS)
//////////////////////////////Capturing////////////////////////////////////////////////////////
-/obj/item/soulstone/attack(mob/living/carbon/human/M, mob/living/user)
+/obj/item/soulstone/attack__legacy__attackchain(mob/living/carbon/human/M, mob/living/user)
if(M == user)
return
@@ -169,7 +169,7 @@
transfer_soul("VICTIM", M, user)
return
-/obj/item/soulstone/attackby(obj/item/O, mob/user)
+/obj/item/soulstone/attackby__legacy__attackchain(obj/item/O, mob/user)
if(istype(O, /obj/item/storage/bible) && !IS_CULTIST(user) && HAS_MIND_TRAIT(user, TRAIT_HOLY))
if(purified)
return
@@ -221,7 +221,7 @@
else
..()
-/obj/item/soulstone/attack_self(mob/living/user)
+/obj/item/soulstone/attack_self__legacy__attackchain(mob/living/user)
var/mob/living/simple_animal/shade/S = locate(/mob/living/simple_animal/shade) in contents
if(!in_range(src, user) || !S)
return
@@ -287,7 +287,7 @@
. += "A Wraith, which does high damage and can jaunt through walls, though it is quite fragile."
. += "A Juggernaut, which is very hard to kill and can produce temporary walls, but is slow."
-/obj/structure/constructshell/attackby(obj/item/I, mob/living/user, params)
+/obj/structure/constructshell/attackby__legacy__attackchain(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/soulstone))
var/obj/item/soulstone/SS = I
if(!SS.can_use(user))
diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm
index 4df54ff12eef7..60b8ac422dd8b 100644
--- a/code/game/gamemodes/wizard/spellbook.dm
+++ b/code/game/gamemodes/wizard/spellbook.dm
@@ -734,7 +734,7 @@
..()
initialize()
-/obj/item/spellbook/attackby(obj/item/O as obj, mob/user as mob, params)
+/obj/item/spellbook/attackby__legacy__attackchain(obj/item/O as obj, mob/user as mob, params)
if(istype(O, /obj/item/contract))
var/obj/item/contract/contract = O
if(contract.used)
@@ -850,7 +850,7 @@
dat += {"[content]