Files
kiwistation/code/_onclick/item_attack.dm
T
James fffb74bdcf [READY] Welders consume fuel when they destroy an object during attack (#46842)
About The Pull Request

Welders didn't actually consume fuel if their attack deleted an object. This was first noticed when welding space vines, in that the welder didn't consume any fuel to do so. Further testing showed that this was the case for any object that gets destroyed on hit, including APCs, Air Alarms, Girders etc.

Problem was that the code for actually consuming fuel after an attack is in the afterattack proc which doesn't actually get called if the target is QDELETED. PR moves that code to the attack proc, along with (regrettably) implementing attack_obj too. This feels hacky and reeks of duplicate code, so if anyone has a better option for how I could implement this fix I'm all ears.
Why It's Good For The Game

Fixes unintentional behavior
Changelog

cl
fix: Welders now consume fuel when they destroy an object
/cl
2019-10-13 16:41:14 +13:00

150 lines
5.9 KiB
Plaintext

/**
*This is the proc that handles the order of an item_attack.
*The order of procs called is:
*tool_act on the target. If it returns TRUE, the chain will be stopped.
*pre_attack() on src. If this returns TRUE, the chain will be stopped.
*attackby on the target. If it returns TRUE, the chain will be stopped.
*and lastly
*afterattack. The return value does not matter.
*/
/obj/item/proc/melee_attack_chain(mob/user, atom/target, params)
if(tool_behaviour && target.tool_act(user, src, tool_behaviour))
return
if(pre_attack(target, user, params))
return
if(target.attackby(src,user, params))
return
if(QDELETED(src) || QDELETED(target))
attack_qdeleted(target, user, TRUE, params)
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.
/obj/item/proc/attack_self(mob/user)
if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user) & COMPONENT_NO_INTERACT)
return
interact(user)
/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_NO_ATTACK)
return TRUE
return FALSE //return TRUE 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
/obj/attackby(obj/item/I, mob/living/user, params)
return ..() || ((obj_flags & CAN_BE_HIT) && I.attack_obj(src, user))
/mob/living/attackby(obj/item/I, mob/living/user, params)
if(..())
return TRUE
user.changeNext_move(CLICK_CD_MELEE)
return I.attack(src, user)
/obj/item/proc/attack(mob/living/M, mob/living/user)
if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, M, user) & COMPONENT_ITEM_NO_ATTACK)
return
SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK, M, user)
if(item_flags & NOBLUDGEON)
return
if(force && HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, "<span class='warning'>You don't want to harm other living beings!</span>")
return
if(!force)
playsound(loc, 'sound/weapons/tap.ogg', get_clamped_volume(), TRUE, -1)
else if(hitsound)
playsound(loc, hitsound, get_clamped_volume(), TRUE, -1)
M.lastattacker = user.real_name
M.lastattackerckey = user.ckey
user.do_attack_animation(M)
M.attacked_by(src, user)
log_combat(user, M, "attacked", src.name, "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])")
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)
if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_OBJ, O, user) & COMPONENT_NO_ATTACK_OBJ)
return
if(item_flags & NOBLUDGEON)
return
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(O)
O.attacked_by(src, user)
/atom/movable/proc/attacked_by()
return
/obj/attacked_by(obj/item/I, mob/living/user)
if(I.force)
user.visible_message("<span class='danger'>[user] hits [src] with [I]!</span>", \
"<span class='danger'>You hit [src] with [I]!</span>", null, COMBAT_MESSAGE_RANGE)
//only witnesses close by and the victim see a hit message.
log_combat(user, src, "attacked", I)
take_damage(I.force, I.damtype, "melee", 1)
/mob/living/attacked_by(obj/item/I, mob/living/user)
send_item_attack_message(I, user)
if(I.force)
apply_damage(I.force, I.damtype)
if(I.damtype == BRUTE)
if(prob(33))
I.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)
return TRUE //successful attack
/mob/living/simple_animal/attacked_by(obj/item/I, mob/living/user)
if(I.force < force_threshold || I.damtype == STAMINA)
playsound(loc, 'sound/weapons/tap.ogg', I.get_clamped_volume(), TRUE, -1)
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)
SEND_SIGNAL(user, COMSIG_MOB_ITEM_AFTERATTACK, target, user, proximity_flag, click_parameters)
// 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)
SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK_QDELETED, target, user, proximity_flag, click_parameters)
/obj/item/proc/get_clamped_volume()
if(w_class)
if(force)
return CLAMP((force + w_class) * 4, 30, 100)// Add the item's force to its weight class and multiply by 4, then clamp the value between 30 and 100
else
return CLAMP(w_class * 6, 10, 100) // Multiply the item's weight class by 6, then clamp the value between 10 and 100
/mob/living/proc/send_item_attack_message(obj/item/I, mob/living/user, hit_area)
var/message_verb = "attacked"
if(I.attack_verb && I.attack_verb.len)
message_verb = "[pick(I.attack_verb)]"
else if(!I.force)
return
var/message_hit_area = ""
if(hit_area)
message_hit_area = " in the [hit_area]"
var/attack_message = "[src] is [message_verb][message_hit_area] with [I]!"
var/attack_message_local = "You're [message_verb][message_hit_area] with [I]!"
if(user in viewers(src, null))
attack_message = "[user] [message_verb] [src][message_hit_area] with [I]!"
attack_message_local = "[user] [message_verb] you[message_hit_area] with [I]!"
visible_message("<span class='danger'>[attack_message]</span>",\
"<span class='userdanger'>[attack_message_local]</span>", null, COMBAT_MESSAGE_RANGE)
return 1