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) . = ..()