From 593f7699db71ff23ff1b41560563cc73339c375c Mon Sep 17 00:00:00 2001 From: mwerezak Date: Sat, 25 Jul 2015 03:09:36 -0400 Subject: [PATCH] Refactors item_attack.dm Refactors and cleans up item_attack.dm and related mob attack code Conflicts: code/_onclick/item_attack.dm code/game/objects/items.dm code/modules/mob/living/bot/bot.dm code/modules/mob/living/carbon/human/human_defense.dm code/modules/mob/living/living_defense.dm code/modules/mob/living/simple_animal/simple_animal.dm --- code/_onclick/item_attack.dm | 85 +++----- code/game/objects/items.dm | 3 - code/modules/hydroponics/grown.dm | 41 ++-- code/modules/mob/living/bot/bot.dm | 1 + .../mob/living/carbon/carbon_defense.dm | 98 +++++++++ .../living/carbon/human/human_attackhand.dm | 17 -- .../mob/living/carbon/human/human_defense.dm | 187 ++++++++++-------- code/modules/mob/living/living_defense.dm | 86 ++++---- code/modules/mob/living/silicon/silicon.dm | 3 - .../mob/living/simple_animal/simple_animal.dm | 6 +- polaris.dme | 1 + 11 files changed, 275 insertions(+), 253 deletions(-) create mode 100644 code/modules/mob/living/carbon/carbon_defense.dm diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 093975051b..94f9b7774d 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -3,6 +3,10 @@ /obj/item/proc/attack_self(mob/user) return +/obj/item/proc/resolve_attackby(atom/A, mob/source) + add_fingerprint(source) + return A.attackby(src,source) + // No comment /atom/proc/attackby(obj/item/W, mob/user) return @@ -12,29 +16,23 @@ visible_message("[src] has been hit by [user] with [W].") /mob/living/attackby(obj/item/I, mob/user) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - if(istype(I) && ismob(user)) - I.attack(src, user) - + if(!ismob(user)) + return 0 + if(can_operate(src) && do_surgery(src,user,I)) //Surgery + return 1 + if(istype(I)) + return I.attack(src, user, user.zone_sel.selecting) // 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) return -//TODO: refactor mob attack code. -/* -Busy writing something else that I don't want to get mixed up in a general attack code, and I don't want to forget this so leaving a note here. -leave attackby() as handling the general case of "using an item on a mob" -attackby() will decide to call attacked_by() or not. -attacked_by() will be made a living level proc and handle the specific case of "attacking with an item to cause harm" -attacked_by() will then call attack() so that stunbatons and other weapons that have special attack effects can do their thing. -attacked_by() will handle hitting/missing/logging as it does now, and will call attack() to apply the attack effects (damage) instead of the other way around (as it is now). -*/ - -/obj/item/proc/attack(mob/living/M as mob, mob/living/user as mob, def_zone) - - if(!istype(M) || (can_operate(M) && do_surgery(M,user,src))) return 0 +/obj/item/proc/attack(mob/living/M, mob/living/user, def_zone) + if(!istype(M)) + return 0 + if(!force || (flags & NOBLUDGEON)) + return 0 ///////////////////////// user.lastattacked = M @@ -46,50 +44,19 @@ attacked_by() will handle hitting/missing/logging as it does now, and will call msg_admin_attack("[key_name(user)] attacked [key_name(M)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" ) ///////////////////////// - // Attacking someone with a weapon while they are neck-grabbed - if(user.a_intent == I_HURT) - for(var/obj/item/weapon/grab/G in M.grabbed_by) - if(G.assailant == user && G.state >= GRAB_NECK) - M.attack_throat(src, G, user) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(M) + M.attacked_with_item(src, user, def_zone) + return 1 + +//Called when a weapon is used to make a successful melee attack on a mob. +/obj/item/proc/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) + if(hitsound) + playsound(loc, hitsound, 50, 1, -1) + var/power = force if(HULK in user.mutations) power *= 2 + return target.hit_with_weapon(src, user, power, hit_zone) - // TODO: needs to be refactored into a mob/living level attacked_by() proc. ~Z - user.do_attack_animation(M) - user.break_cloak() - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - - // Handle striking to cripple. - var/dislocation_str - if(user.a_intent == I_DISARM) - dislocation_str = H.attack_joint(src, user, def_zone) - if(H.attacked_by(src, user, def_zone) && hitsound) - playsound(loc, hitsound, 50, 1, -1) - spawn(1) //ugh I hate this but I don't want to root through human attack procs to print it after this call resolves. - if(dislocation_str) user.visible_message("[dislocation_str]") - return 1 - return 0 - else - if(attack_verb.len) - user.visible_message("[M] has been [pick(attack_verb)] with [src] by [user]!") - else - user.visible_message("[M] has been attacked with [src] by [user]!") - - if (hitsound) - playsound(loc, hitsound, 50, 1, -1) - switch(damtype) - if("brute") - M.take_organ_damage(power) - if(prob(33)) // Added blood for whacking non-humans too - var/turf/simulated/location = get_turf(M) - if(istype(location)) location.add_blood_floor(M) - if("fire") - if (!(COLD_RESISTANCE in M.mutations)) - M.take_organ_damage(0, power) - M << "Aargh it burns!" - M.updatehealth() - add_fingerprint(user) - return 1 diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 2493e29609..9b40cf4e8c 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -633,6 +633,3 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. /obj/item/proc/pwr_drain() return 0 // Process Kill -/obj/item/proc/resolve_attackby(atom/A, mob/source) - return A.attackby(src,source) - diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 7c17c58467..8528033f52 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -242,40 +242,23 @@ if(user.a_intent == I_HURT) - // This is being copypasted here because reagent_containers (WHY DOES FOOD DESCEND FROM THAT) overrides it completely. - // TODO: refactor all food paths to be less horrible and difficult to work with in this respect. ~Z - if(!istype(M) || (can_operate(M) && do_surgery(M,user,src))) return 0 + if(!istype(M)) + return 0 + if(!force || (flags & NOBLUDGEON)) + return 0 + ///////////////////////// user.lastattacked = M M.lastattacker = user - user.attack_log += "\[[time_stamp()]\] Attacked [M.name] ([M.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" - M.attack_log += "\[[time_stamp()]\] Attacked by [user.name] ([user.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" - msg_admin_attack("[key_name(user)] attacked [key_name(M)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" ) - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/hit = H.attacked_by(src, user, def_zone) - if(hit && hitsound) - playsound(loc, hitsound, 50, 1, -1) - return hit - else - if(attack_verb.len) - user.visible_message("[M] has been [pick(attack_verb)] with [src] by [user]!") - else - user.visible_message("[M] has been attacked with [src] by [user]!") + if(!no_attack_log) + user.attack_log += "\[[time_stamp()]\] Attacked [M.name] ([M.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" + M.attack_log += "\[[time_stamp()]\] Attacked by [user.name] ([user.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" + msg_admin_attack("[key_name(user)] attacked [key_name(M)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" ) + ///////////////////////// - if (hitsound) - playsound(loc, hitsound, 50, 1, -1) - switch(damtype) - if("brute") - M.take_organ_damage(force) - if(prob(33)) - var/turf/simulated/location = get_turf(M) - if(istype(location)) location.add_blood_floor(M) - if("fire") - if (!(COLD_RESISTANCE in M.mutations)) - M.take_organ_damage(0, force) - M.updatehealth() + user.do_attack_animation(M) + M.attacked_with_item(src, user, def_zone) if(seed && seed.get_trait(TRAIT_STINGS)) if(!reagents || reagents.total_volume <= 0) diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm index 5679cc1a46..5eceb7c7ba 100644 --- a/code/modules/mob/living/bot/bot.dm +++ b/code/modules/mob/living/bot/bot.dm @@ -337,3 +337,4 @@ //if((dir & EAST ) && (D.dir & (NORTH|SOUTH))) return !D.check_access(ID) else return !D.check_access(ID) // it's a real, air blocking door return 0 + diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm new file mode 100644 index 0000000000..933ec801ca --- /dev/null +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -0,0 +1,98 @@ + +//Called when the mob is hit with an item in combat. +/mob/living/carbon/hit_with_weapon(obj/item/I, mob/living/user, var/effective_force, var/hit_zone) + if(check_attack_throat(I, user)) + return + ..() + +/mob/living/carbon/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/hit_zone) + if(!effective_force || blocked >= 2) + return 0 + + //Hulk modifier + if(HULK in user.mutations) + effective_force *= 2 + + //Apply weapon damage + var/weapon_sharp = is_sharp(I) + var/weapon_edge = has_edge(I) + if(prob(getarmor(hit_zone, "melee"))) //melee armour provides a chance to turn sharp/edge weapon attacks into blunt ones + weapon_sharp = 0 + weapon_edge = 0 + + apply_damage(effective_force, I.damtype, hit_zone, blocked, sharp=weapon_sharp, edge=weapon_edge, used_weapon=I) + + //Melee weapon embedded object code. + if (I && I.damtype == BRUTE && !I.anchored && !is_robot_module(I)) + var/damage = effective_force + if (blocked) + damage /= blocked+1 + + //blunt objects should really not be embedding in things unless a huge amount of force is involved + var/embed_chance = weapon_sharp? damage/I.w_class : damage/(I.w_class*3) + var/embed_threshold = weapon_sharp? 5*I.w_class : 15*I.w_class + + //Sharp objects will always embed if they do enough damage. + if((weapon_sharp && damage > (10*I.w_class)) || (damage > embed_threshold && prob(embed_chance))) + src.embed(I, hit_zone) + + return 1 + +// Attacking someone with a weapon while they are neck-grabbed +/mob/living/carbon/proc/check_attack_throat(obj/item/W, mob/user) + if(user.a_intent == I_HURT) + for(var/obj/item/weapon/grab/G in src.grabbed_by) + if(G.assailant == user && G.state >= GRAB_NECK) + if(attack_throat(W, G, user)) + return 1 + return 0 + +// Knifing +/mob/living/carbon/proc/attack_throat(obj/item/W, obj/item/weapon/grab/G, mob/user) + + if(!W.edge || !W.force || W.damtype != BRUTE) + return 0 //unsuitable weapon + + user.visible_message("\The [user] begins to slit [src]'s throat with \the [W]!") + + user.next_move = world.time + 20 //also should prevent user from triggering this repeatedly + if(!do_after(user, 20)) + return 0 + if(!(G && G.assailant == user && G.affecting == src)) //check that we still have a grab + return 0 + + var/damage_mod = 1 + //presumably, if they are wearing a helmet that stops pressure effects, then it probably covers the throat as well + var/obj/item/clothing/head/helmet = get_equipped_item(slot_head) + if(istype(helmet) && (helmet.body_parts_covered & HEAD) && (helmet.flags & STOPPRESSUREDAMAGE)) + //we don't do an armor_check here because this is not an impact effect like a weapon swung with momentum, that either penetrates or glances off. + damage_mod = 1.0 - (helmet.armor["melee"]/100) + + var/total_damage = 0 + for(var/i in 1 to 3) + var/damage = min(W.force*1.5, 20)*damage_mod + apply_damage(damage, W.damtype, "head", 0, sharp=W.sharp, edge=W.edge) + total_damage += damage + + var/oxyloss = total_damage + if(total_damage >= 40) //threshold to make someone pass out + oxyloss = 60 // Brain lacks oxygen immediately, pass out + + adjustOxyLoss(min(oxyloss, 100 - getOxyLoss())) //don't put them over 100 oxyloss + + if(total_damage) + if(oxyloss >= 40) + user.visible_message("\The [user] slit [src]'s throat open with \the [W]!") + else + user.visible_message("\The [user] cut [src]'s neck with \the [W]!") + + if(W.hitsound) + playsound(loc, W.hitsound, 50, 1, -1) + + G.last_action = world.time + flick(G.hud.icon_state, G.hud) + + user.attack_log += "\[[time_stamp()]\] Knifed [name] ([ckey]) with [W.name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(W.damtype)])" + src.attack_log += "\[[time_stamp()]\] Got knifed by [user.name] ([user.ckey]) with [W.name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(W.damtype)])" + msg_admin_attack("[key_name(user)] knifed [key_name(src)] with [W.name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(W.damtype)])" ) + return 1 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index a38e516acf..1d61cd6d0e 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -310,23 +310,6 @@ updatehealth() return 1 -/mob/living/carbon/human/proc/attack_joint(var/obj/item/W, var/mob/living/user, var/def_zone) - if(!def_zone) def_zone = user.zone_sel.selecting - var/target_zone = get_zone_with_miss_chance(check_zone(def_zone), src) - - if(user == src) // Attacking yourself can't miss - target_zone = user.zone_sel.selecting - if(!target_zone) - return null - var/obj/item/organ/external/organ = get_organ(check_zone(target_zone)) - if(!organ || (organ.dislocated == 2) || (organ.dislocated == -1)) - return null - var/dislocation_str - if(prob(W.force)) - dislocation_str = "[src]'s [organ.joint] [pick("gives way","caves in","crumbles","collapses")]!" - organ.dislocate(1) - return dislocation_str - //Used to attack a joint through grabbing /mob/living/carbon/human/proc/grab_joint(var/mob/living/user, var/def_zone) var/has_grab = 0 diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 105d7be9bd..accfed5513 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -139,67 +139,75 @@ emp_act if(.) return return 0 -//Returns 1 if the attack hit, 0 if it missed. -/mob/living/carbon/human/proc/attacked_by(var/obj/item/I, var/mob/living/user, var/def_zone) - if(!I || !user) return 0 +/mob/living/carbon/human/emp_act(severity) + for(var/obj/O in src) + if(!O) continue + O.emp_act(severity) + ..() - var/target_zone = def_zone? check_zone(def_zone) : get_zone_with_miss_chance(user.zone_sel.selecting, src) +/mob/living/carbon/human/attacked_with_item(obj/item/I, mob/living/user, var/target_zone) + if(!I || !user) + return - if(user == src) // Attacking yourself can't miss - target_zone = user.zone_sel.selecting - if(!target_zone) - visible_message("[user] misses [src] with \the [I]!") - return 0 + if(check_attack_throat(I, user)) + return - var/obj/item/organ/external/affecting = get_organ(target_zone) + var/hit_zone = target_zone + if(user != src) // Attacking yourself can't miss + hit_zone = get_zone_with_miss_chance(target_zone, src) + if(!hit_zone) + visible_message("\The [user] misses [src] with \the [I]!") + return + + if(check_shields(I.force, I, user, target_zone, "the [I.name]")) + return + + var/obj/item/organ/external/affecting = get_organ(hit_zone) if (!affecting || affecting.is_stump()) user << "They are missing that limb!" return 0 - var/effective_force = I.force - if(user.a_intent == "disarm") effective_force = round(I.force/2) - var/hit_area = affecting.name + I.apply_hit_effect(src, user, target_zone) - if((user != src) && check_shields(effective_force, I, user, target_zone, "the [I.name]")) +/mob/living/carbon/human/hit_with_weapon(obj/item/I, mob/living/user, var/effective_force, var/hit_zone) + var/obj/item/organ/external/affecting = get_organ(hit_zone) + if(!affecting) + return //should be prevented by attacked_with_item() but for sanity. + + visible_message("[src] has been [I.attack_verb.len? pick(I.attack_verb) : "attacked"] in the [affecting.name] with [I.name] by [user]!") + + var/blocked = run_armor_check(hit_zone, "melee", I.armor_penetration, "Your armor has protected your [affecting.name].", "Your armor has softened the blow to your [affecting.name].") + standard_weapon_hit_effects(I, user, effective_force, blocked, hit_zone) + + return 1 + +/mob/living/carbon/human/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/hit_zone) + var/obj/item/organ/external/affecting = get_organ(hit_zone) + if(!affecting) return 0 - if(istype(I,/obj/item/weapon/card/emag)) - if(!(affecting.status & ORGAN_ROBOT)) - user << "\red That limb isn't robotic." - return - if(affecting.sabotaged) - user << "\red [src]'s [affecting.name] is already sabotaged!" + // Handle striking to cripple. + if(user.a_intent == I_DISARM) + effective_force /= 2 + if(..(I, user effective_force, blocked, hit_zone)) + attack_joint(affecting, I, blocked) else - user << "\red You sneakily slide [I] into the dataport on [src]'s [affecting.name] and short out the safeties." - var/obj/item/weapon/card/emag/emag = I - emag.uses-- - affecting.sabotaged = 1 - return 1 + return 0 + else if(!..()) + return 0 - if(I.attack_verb.len) - visible_message("\red [src] has been [pick(I.attack_verb)] in the [hit_area] with [I.name] by [user]!") - else - visible_message("\red [src] has been attacked in the [hit_area] with [I.name] by [user]!") + if(effective_force > 10 || effective_force >= 5 && prob(33)) + forcesay(hit_appends) //forcesay checks stat already - var/armor = run_armor_check(affecting, "melee", I.armor_penetration, "Your armor has protected your [hit_area].", "Your armor has softened hit to your [hit_area].") - var/weapon_sharp = is_sharp(I) - var/weapon_edge = has_edge(I) - if ((weapon_sharp || weapon_edge) && prob(getarmor(target_zone, "melee"))) - weapon_sharp = 0 - weapon_edge = 0 + if(prob(25 + (effective_force * 2))) + if(!((I.damtype == BRUTE) || (I.damtype == HALLOSS))) + return - if(armor >= 100) return 0 - if(!effective_force) return 0 - var/Iforce = effective_force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords) + if(!(I.flags & NOBLOODY)) + I.add_blood(src) - apply_damage(effective_force, I.damtype, affecting, armor, sharp=weapon_sharp, edge=weapon_edge, used_weapon=I) - - var/bloody = 0 - if(((I.damtype == BRUTE) || (I.damtype == HALLOSS)) && prob(25 + (effective_force * 2))) - I.add_blood(src) //Make the weapon bloody, not the person. -// if(user.hand) user.update_inv_l_hand() //updates the attacker's overlay for the (now bloodied) weapon -// else user.update_inv_r_hand() //removed because weapons don't have on-mob blood overlays + var/bloody = 0 if(prob(33)) bloody = 1 var/turf/location = loc @@ -212,51 +220,56 @@ emp_act H.bloody_hands(src) if(!stat) - if(headcheck(hit_area)) - //Harder to score a stun but if you do it lasts a bit longer - if(prob(effective_force)) - apply_effect(20, PARALYZE, armor) - visible_message("[src] [species.get_knockout_message(src)]") - else - //Easier to score a stun but lasts less time - if(prob(effective_force + 10)) - apply_effect(6, WEAKEN, armor) - visible_message("[src] has been knocked down!") + switch(hit_zone) + if("head")//Harder to score a stun but if you do it lasts a bit longer + if(prob(effective_force)) + apply_effect(20, PARALYZE, blocked) + visible_message("\The [src] has been knocked unconscious!") + if(bloody)//Apply blood + if(wear_mask) + wear_mask.add_blood(src) + update_inv_wear_mask(0) + if(head) + head.add_blood(src) + update_inv_head(0) + if(glasses && prob(33)) + glasses.add_blood(src) + update_inv_glasses(0) + if("chest")//Easier to score a stun but lasts less time + if(prob(effective_force + 10)) + apply_effect(6, WEAKEN, blocked) + visible_message("\The [src] has been knocked down!") + if(bloody) + bloody_body(src) - //Apply blood - if(bloody) - switch(hit_area) - if(BP_HEAD) - if(wear_mask) - wear_mask.add_blood(src) - update_inv_wear_mask(0) - if(head) - head.add_blood(src) - update_inv_head(0) - if(glasses && prob(33)) - glasses.add_blood(src) - update_inv_glasses(0) - if(BP_TORSO) - bloody_body(src) - - if(Iforce > 10 || Iforce >= 5 && prob(33)) - forcesay(hit_appends) //forcesay checks stat already - - //Melee weapon embedded object code. - if (I && I.damtype == BRUTE && !I.anchored && !is_robot_module(I)) - var/damage = effective_force - if (armor) - damage /= armor+1 - - //blunt objects should really not be embedding in things unless a huge amount of force is involved - var/embed_chance = weapon_sharp? damage/I.w_class : damage/(I.w_class*3) - var/embed_threshold = weapon_sharp? 5*I.w_class : 15*I.w_class - - //Sharp objects will always embed if they do enough damage. - if((weapon_sharp && damage > (10*I.w_class)) || (damage > embed_threshold && prob(embed_chance))) - affecting.embed(I) return 1 +/mob/living/carbon/human/proc/attack_joint(var/obj/item/organ/external/organ, var/obj/item/W, var/blocked) + if(!organ || (organ.dislocated == 2) || (organ.dislocated == -1) || blocked >= 2) + return 0 + if(prob(W.force / (blocked+1))) + visible_message("[src]'s [organ.joint] [pick("gives way","caves in","crumbles","collapses")]!") + organ.dislocate(1) + return 1 + return 0 + +//*** TODO + +/* TODO Reimplement pending cleanup +if(istype(I,/obj/item/weapon/card/emag)) + if(!(affecting.status & ORGAN_ROBOT)) + user << "\red That limb isn't robotic." + return + if(affecting.sabotaged) + user << "\red [src]'s [affecting.name] is already sabotaged!" + else + user << "\red You sneakily slide [I] into the dataport on [src]'s [affecting.name] and short out the safeties." + var/obj/item/weapon/card/emag/emag = I + emag.uses-- + affecting.sabotaged = 1 + return 1 +*/ + //this proc handles being hit by a thrown atom /mob/living/carbon/human/hitby(atom/movable/AM as mob|obj,var/speed = THROWFORCE_SPEED_DIVISOR) if(istype(AM,/obj/)) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 3966f60a20..5f96e48b8d 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -144,6 +144,41 @@ O.emp_act(severity) ..() +//Whereas attackby() handles the general case of using items on mobs, this handles the specific case of attacking a mob with an item as a weapon. +/mob/living/proc/attacked_with_item(obj/item/I, mob/living/user, var/target_zone) + I.apply_hit_effect(src, user, target_zone) + +//Called when the mob is hit with an item in combat. +/mob/living/proc/hit_with_weapon(obj/item/I, mob/living/user, var/effective_force, var/hit_zone) + visible_message("[src] has been [I.attack_verb.len? pick(I.attack_verb) : "attacked"] with [I.name] by [user]!") + + var/blocked = run_armor_check(hit_zone, "melee") + standard_weapon_hit_effects(I, user, effective_force, blocked, hit_zone) + + if(I.damtype == BRUTE && prob(33)) // Added blood for whacking non-humans too + var/turf/simulated/location = get_turf(src) + if(istype(location)) location.add_blood_floor(src) + +//returns 0 if the effects failed to apply for some reason, 1 otherwise. +/mob/living/proc/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/hit_zone) + if(!effective_force || blocked >= 2) + return 0 + + //Hulk modifier + if(HULK in user.mutations) + effective_force *= 2 + + //Apply weapon damage + var/weapon_sharp = is_sharp(I) + var/weapon_edge = has_edge(I) + if(prob(max(getarmor(hit_zone, "melee") - I.armor_penetration, 0))) //melee armour provides a chance to turn sharp/edge weapon attacks into blunt ones + weapon_sharp = 0 + weapon_edge = 0 + + apply_damage(effective_force, I.damtype, hit_zone, blocked, sharp=weapon_sharp, edge=weapon_edge, used_weapon=I) + + return 1 + //this proc handles being hit by a thrown atom /mob/living/hitby(atom/movable/AM as mob|obj,var/speed = THROWFORCE_SPEED_DIVISOR)//Standardization and logging -Sieve if(istype(AM,/obj/)) @@ -370,54 +405,3 @@ hud_used.hide_actions_toggle.screen_loc = hud_used.ButtonNumberToScreenCoords(button_number+1) //hud_used.SetButtonCoords(hud_used.hide_actions_toggle,button_number+1) client.screen += hud_used.hide_actions_toggle - - -//If simple_animals are ever moved under carbon, then this can probably be moved to carbon as well -/mob/living/proc/attack_throat(obj/item/W, obj/item/weapon/grab/G, mob/user) - - // Knifing - if(!W.edge || !W.force || W.damtype != BRUTE) return //unsuitable weapon - - user.visible_message("\The [user] begins to slit [src]'s throat with \the [W]!") - - user.next_move = world.time + 20 //also should prevent user from triggering this repeatedly - if(!do_after(user, 20)) - return - if(!(G && G.assailant == user && G.affecting == src)) //check that we still have a grab - return - - var/damage_mod = 1 - //presumably, if they are wearing a helmet that stops pressure effects, then it probably covers the throat as well - var/obj/item/clothing/head/helmet = get_equipped_item(slot_head) - if(istype(helmet) && (helmet.body_parts_covered & HEAD) && (helmet.item_flags & STOPPRESSUREDAMAGE)) - //we don't do an armor_check here because this is not an impact effect like a weapon swung with momentum, that either penetrates or glances off. - damage_mod = 1.0 - (helmet.armor["melee"]/100) - - var/total_damage = 0 - for(var/i in 1 to 3) - var/damage = min(W.force*1.5, 20)*damage_mod - apply_damage(damage, W.damtype, BP_HEAD, 0, sharp=W.sharp, edge=W.edge) - total_damage += damage - - var/oxyloss = total_damage - if(total_damage >= 40) //threshold to make someone pass out - oxyloss = 60 // Brain lacks oxygen immediately, pass out - - adjustOxyLoss(min(oxyloss, 100 - getOxyLoss())) //don't put them over 100 oxyloss - - if(total_damage) - if(oxyloss >= 40) - user.visible_message("\The [user] slit [src]'s throat open with \the [W]!") - else - user.visible_message("\The [user] cut [src]'s neck with \the [W]!") - - if(W.hitsound) - playsound(loc, W.hitsound, 50, 1, -1) - - G.last_action = world.time - flick(G.hud.icon_state, G.hud) - - user.attack_log += "\[[time_stamp()]\] Knifed [name] ([ckey]) with [W.name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(W.damtype)])" - src.attack_log += "\[[time_stamp()]\] Got knifed by [user.name] ([user.ckey]) with [W.name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(W.damtype)])" - msg_admin_attack("[key_name(user)] knifed [key_name(src)] with [W.name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(W.damtype)])" ) - return diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 0d0a83b7d1..d670e17ee3 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -132,9 +132,6 @@ updatehealth() return 1*/ -/mob/living/silicon/attack_throat() - return - /proc/islinked(var/mob/living/silicon/robot/bot, var/mob/living/silicon/ai/ai) if(!istype(bot) || !istype(ai)) return 0 diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 2fbad9deee..d0a7ba2ba3 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -287,11 +287,9 @@ if(istype(O, /obj/item/weapon/material/knife) || istype(O, /obj/item/weapon/material/knife/butch)) harvest(user) else - attacked_with_item(O, user) + attacked_with_item(O, user, user.zone_sel.selecting) -//TODO: refactor mob attackby(), attacked_by(), and friends. -/mob/living/simple_animal/proc/attacked_with_item(var/obj/item/O, var/mob/user) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) +/mob/living/simple_animal/attacked_with_item(obj/item/O, mob/living/user, var/target_zone) if(!O.force) visible_message("[user] gently taps [src] with \the [O].") return diff --git a/polaris.dme b/polaris.dme index 82459576ff..ab2cacb4f0 100644 --- a/polaris.dme +++ b/polaris.dme @@ -1305,6 +1305,7 @@ #include "code\modules\mob\living\bot\secbot.dm" #include "code\modules\mob\living\carbon\breathe.dm" #include "code\modules\mob\living\carbon\carbon.dm" +#include "code\modules\mob\living\carbon\carbon_defense.dm" #include "code\modules\mob\living\carbon\carbon_defines.dm" #include "code\modules\mob\living\carbon\carbon_powers.dm" #include "code\modules\mob\living\carbon\give.dm"