From 33ca41c0bdf38ba58cda384e5134cb61103e67c7 Mon Sep 17 00:00:00 2001
From: Mothblocks <35135081+Jared-Fogle@users.noreply.github.com>
Date: Fri, 5 Feb 2021 16:23:09 -0800
Subject: [PATCH] Add alternate attack procs (#56663)
Adds alternate attack procs for right-clicking. Updates the gunpoint code to use this new proc as an example (though I suspect this is being changed soon, so oops).
This makes it easier to add new behavior to right click, which we should be doing.
---
code/__DEFINES/combat.dm | 11 ++++++
code/_onclick/item_attack.dm | 65 ++++++++++++++++++++++++++++++++-
code/modules/projectiles/gun.dm | 16 ++++----
3 files changed, 82 insertions(+), 10 deletions(-)
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index ffc6a428bbd..4ffc526cedd 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -278,3 +278,14 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
/// If a carbon is thrown at a speed faster than normal and impacts something solid, they take extra damage for every extra speed up to this number (see [/mob/living/carbon/proc/throw_impact])
#define CARBON_MAX_IMPACT_SPEED_BONUS 5
+
+/// Alternate attack defines. Return these at the end of procs like afterattack_alt.
+/// Calls the normal attack proc. For example, if returned in afterattack_alt, will call afterattack.
+/// Will continue the chain depending on the return value of the non-alternate proc, like with normal attacks.
+#define ALT_ATTACK_CALL_NORMAL 1
+
+/// Cancels the attack chain entirely.
+#define ALT_ATTACK_CANCEL_ATTACK_CHAIN 2
+
+/// Proceed with the attack chain, but don't call the normal methods.
+#define ALT_ATTACK_CONTINUE_CHAIN 3
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 4b229ddfd1d..a82d69eea18 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -8,15 +8,41 @@
* * [/obj/item/proc/afterattack]. The return value does not matter.
*/
/obj/item/proc/melee_attack_chain(mob/user, atom/target, params)
+ var/is_right_clicking = params2list(params)["right"]
+
if(tool_behaviour && target.tool_act(user, src, tool_behaviour))
return TRUE
+
if(pre_attack(target, user, params))
return TRUE
- if(target.attackby(src,user, params))
+
+ var/attackby_result
+
+ if (is_right_clicking)
+ switch (target.attackby_alt(src, user, params))
+ if (ALT_ATTACK_CALL_NORMAL)
+ attackby_result = target.attackby(src, user, params)
+ if (ALT_ATTACK_CANCEL_ATTACK_CHAIN)
+ return TRUE
+ if (null)
+ CRASH("attackby_alt must return an ALT_ATTACK_* define, please consult code/__DEFINES/combat.dm")
+ else
+ attackby_result = target.attackby(src, user, params)
+
+ if (attackby_result)
return TRUE
+
if(QDELETED(src) || QDELETED(target))
attack_qdeleted(target, user, TRUE, params)
return TRUE
+
+ if (is_right_clicking)
+ var/after_attack_alt_result = afterattack_alt(target, user, TRUE, params)
+
+ // There's no chain left to continue at this point, so CANCEL_ATTACK_CHAIN and CONTINUE_CHAIN are functionally the same.
+ if (after_attack_alt_result == ALT_ATTACK_CANCEL_ATTACK_CHAIN || after_attack_alt_result == ALT_ATTACK_CONTINUE_CHAIN)
+ return TRUE
+
return afterattack(target, user, TRUE, params)
/// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
@@ -55,6 +81,19 @@
return TRUE
return FALSE
+/**
+ * Called on an object being right-clicked on by an item
+ *
+ * Arguments:
+ * * obj/item/weapon - The item hitting this atom
+ * * mob/user - The wielder of this item
+ * * params - click params such as alt/shift etc
+ *
+ * See: [/obj/item/proc/melee_attack_chain]
+ */
+/atom/proc/attackby_alt(obj/item/weapon, mob/user, params)
+ return ALT_ATTACK_CALL_NORMAL
+
/obj/attackby(obj/item/I, mob/living/user, params)
return ..() || ((obj_flags & CAN_BE_HIT) && I.attack_obj(src, user))
@@ -64,6 +103,15 @@
user.changeNext_move(CLICK_CD_MELEE)
return I.attack(src, user, params)
+/mob/living/attackby_alt(obj/item/weapon, mob/living/user, params)
+ var/result = weapon.attack_alt(src, user, params)
+
+ // Normal attackby updates click cooldown, so we have to make up for it
+ if (result != ALT_ATTACK_CALL_NORMAL)
+ user.changeNext_move(CLICK_CD_MELEE)
+
+ return result
+
/**
* Called from [/mob/living/proc/attackby]
*
@@ -110,6 +158,9 @@
log_combat(user, M, "attacked", src.name, "(COMBAT MODE: [uppertext(user.combat_mode)]) (DAMTYPE: [uppertext(damtype)])")
add_fingerprint(user)
+/// The equivalent of [/obj/item/proc/attack] but for alternate attacks, AKA right clicking
+/obj/item/proc/attack_alt(mob/living/victim, mob/living/user, params)
+ return ALT_ATTACK_CALL_NORMAL
/// The equivalent of the standard version of [/obj/item/proc/attack] but for object targets.
/obj/item/proc/attack_obj(obj/O, mob/living/user)
@@ -165,6 +216,18 @@
SEND_SIGNAL(src, COMSIG_ITEM_AFTERATTACK, target, user, proximity_flag, click_parameters)
SEND_SIGNAL(user, COMSIG_MOB_ITEM_AFTERATTACK, target, user, proximity_flag, click_parameters)
+/**
+ * Called at the end of the attack chain if the user right-clicked.
+ *
+ * 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_alt(atom/target, mob/user, proximity_flag, click_parameters)
+ return ALT_ATTACK_CALL_NORMAL
+
/// Called if the target gets deleted by our attack
/obj/item/proc/attack_qdeleted(atom/target, mob/user, proximity_flag, click_parameters)
SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_QDELETED, target, user, proximity_flag, click_parameters)
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index b35091e8b36..f433747c4bc 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -193,15 +193,13 @@
for(var/obj/O in contents)
O.emp_act(severity)
-/obj/item/gun/attack(mob/M, mob/living/user, params)
- var/list/modifiers = params2list(params)
- if(ismob(M) && modifiers && modifiers["right"]) //Right click to hold someone up
- if(user.GetComponent(/datum/component/gunpoint))
- to_chat(user, "You are already holding someone up!")
- return
- user.AddComponent(/datum/component/gunpoint, M, src)
- return TRUE
- return ..()
+/obj/item/gun/attack_alt(mob/living/victim, mob/living/user, params)
+ if (user.GetComponent(/datum/component/gunpoint))
+ to_chat(user, "You are already holding someone up!")
+ return ALT_ATTACK_CANCEL_ATTACK_CHAIN
+
+ user.AddComponent(/datum/component/gunpoint, victim, src)
+ return ALT_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/gun/afterattack(atom/target, mob/living/user, flag, params)
. = ..()