diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 8f36b1d007..b1e131dd87 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -409,7 +409,7 @@ var/list/global/slot_flags_enumeration = list( /obj/item/proc/ui_action_click() attack_self(usr) -/obj/item/proc/IsShield() +/obj/item/proc/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/attack_text = "the attack") return 0 /obj/item/proc/get_loc_turf() diff --git a/code/game/objects/items/weapons/material/swords.dm b/code/game/objects/items/weapons/material/swords.dm index 8a93d59fe5..a907e2abdf 100644 --- a/code/game/objects/items/weapons/material/swords.dm +++ b/code/game/objects/items/weapons/material/swords.dm @@ -11,8 +11,22 @@ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") hitsound = 'sound/weapons/bladeslice.ogg' -/obj/item/weapon/material/sword/IsShield() - return 1 +/obj/item/weapon/material/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/attack_text = "the attack") + + //parry only melee attacks + if(istype(damage_source, /obj/item/projectile) || (attacker && get_dist(user, attacker) > 1)) + return 0 + + //block as long as they are not directly behind us + var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + if(prob(50)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) + return 1 + return 0 /obj/item/weapon/material/sword/suicide_act(mob/user) viewers(user) << "[user] is falling on the [src.name]! It looks like \he's trying to commit suicide." diff --git a/code/game/objects/items/weapons/material/twohanded.dm b/code/game/objects/items/weapons/material/twohanded.dm index b41a4175a7..e944ab743a 100644 --- a/code/game/objects/items/weapons/material/twohanded.dm +++ b/code/game/objects/items/weapons/material/twohanded.dm @@ -71,6 +71,26 @@ O.unwield() return unwield() +//Allow a small chance of parrying melee attacks when wielded - maybe generalize this to other weapons someday +/obj/item/weapon/material/twohanded/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/attack_text = "the attack") + if(!wielded) + return 0 + + //parry only melee attacks + if(istype(damage_source, /obj/item/projectile) || (attacker && get_dist(user, attacker) > 1)) + return 0 + + //block as long as they are not directly behind us + var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + if(prob(15)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) + return 1 + return 0 + /obj/item/weapon/material/twohanded/update_icon() icon_state = "[base_icon][wielded]" item_state = icon_state @@ -167,51 +187,7 @@ else if(istype(A,/obj/effect/plant)) var/obj/effect/plant/P = A P.die_off() - -/* -/* - * Double-Bladed Energy Swords - Cheridan - */ - // Not sure what to do with this one, it won't work nicely with the material system, - // but I don't want to copypaste all the twohanded procs.. -/obj/item/weapon/material/twohanded/dualsaber - icon_state = "dualsaber0" - base_icon = "dualsaber" - name = "double-bladed energy sword" - desc = "Handle with care." - force = 3 - throwforce = 5.0 - throw_speed = 1 - throw_range = 5 - w_class = 2.0 - force_wielded = 30 - wieldsound = 'sound/weapons/saberon.ogg' - unwieldsound = 'sound/weapons/saberoff.ogg' - origin_tech = list(TECH_MAGNET = 3, TECH_ILLEGAL = 4) - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - sharp = 1 - edge = 1 - applies_material_colour = 0 - -/obj/item/weapon/material/twohanded/dualsaber/attack(target as mob, mob/living/user as mob) - ..() - if((CLUMSY in user.mutations) && (wielded) &&prob(40)) - user << "You twirl around a bit before losing your balance and impaling yourself on \the [src]." - user.take_organ_damage(20,25) - return - if((wielded) && prob(50)) - spawn(0) - for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2)) - user.set_dir(i) - sleep(1) - -/obj/item/weapon/material/twohanded/dualsaber/IsShield() - if(wielded) - return 1 - else - return 0 -*/ - + //spears, bay edition /obj/item/weapon/material/twohanded/spear icon_state = "spearglass0" diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index 43acc06908..d1dee719e1 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -151,8 +151,26 @@ attack_verb = list() icon_state = initial(icon_state) -/obj/item/weapon/melee/energy/sword/IsShield() - if(active) +/obj/item/weapon/melee/energy/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/attack_text = "the attack") + if(!active) + return 0 + + //parry only melee attacks + if(istype(damage_source, /obj/item/projectile) || (attacker && get_dist(user, attacker) > 1)) + return 0 + + //block as long as they are not directly behind us + var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + if(prob(50)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) return 1 return 0 diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm index 02b412232d..15a1eaf96b 100644 --- a/code/game/objects/items/weapons/shields.dm +++ b/code/game/objects/items/weapons/shields.dm @@ -1,5 +1,18 @@ /obj/item/weapon/shield name = "shield" + var/base_block_chance = 50 + +/obj/item/weapon/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/attack_text = "the attack") + //block as long as they are not directly behind us + var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block + if(check_shield_arc(user, bad_arc, damage_source, attacker)) + if(prob(get_block_chance(user, damage, damage_source, attacker))) + user.visible_message("\The [user] blocks [attack_text] with \the [src]!") + return 1 + return 0 + +/obj/item/weapon/shield/proc/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) + return base_block_chance /obj/item/weapon/shield/riot name = "riot shield" @@ -18,17 +31,26 @@ attack_verb = list("shoved", "bashed") var/cooldown = 0 //shield bash cooldown. based on world.time - IsShield() - return 1 +/obj/item/weapon/shield/riot/handle_shield(mob/user) + . = ..() + if(.) playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1) - attackby(obj/item/weapon/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/melee/baton)) - if(cooldown < world.time - 25) - user.visible_message("[user] bashes [src] with [W]!") - playsound(user.loc, 'sound/effects/shieldbash.ogg', 50, 1) - cooldown = world.time - else - ..() +/obj/item/weapon/shield/riot/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) + if(istype(damage_source, /obj/item/projectile)) + var/obj/item/projectile/P = damage_source + //plastic shields do not stop bullets or lasers, even in space. Will block beanbags, rubber bullets, and stunshots just fine though. + if((is_sharp(P) && damage > 10) || istype(P, /obj/item/projectile/beam)) + return 0 + return base_block_chance + +/obj/item/weapon/shield/riot/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/melee/baton)) + if(cooldown < world.time - 25) + user.visible_message("[user] bashes [src] with [W]!") + playsound(user.loc, 'sound/effects/shieldbash.ogg', 50, 1) + cooldown = world.time + else + ..() /* * Energy Shield @@ -49,11 +71,23 @@ attack_verb = list("shoved", "bashed") var/active = 0 -/obj/item/weapon/shield/energy/IsShield() - if(active) - return 1 - else - return 0 +/obj/item/weapon/shield/energy/handle_shield(mob/user) + if(!active) + return 0 //turn it on first! + . = ..() + + if(.) + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) + +/obj/item/weapon/shield/energy/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) + if(istype(damage_source, /obj/item/projectile)) + var/obj/item/projectile/P = damage_source + if((is_sharp(P) && damage > 10) || istype(P, /obj/item/projectile/beam)) + return (base_block_chance - round(damage / 3)) //block bullets and beams using the old block chance + return base_block_chance /obj/item/weapon/shield/energy/attack_self(mob/living/user as mob) if ((CLUMSY in user.mutations) && prob(50)) @@ -82,9 +116,32 @@ add_fingerprint(user) return +//** Shield Helpers +//This is here in case it is useful for things like energy swords that want to pretend they are shields + +//bad_arc is the ABSOLUTE arc of directions from which we cannot block +/obj/item/proc/check_shield_arc(mob/user, var/bad_arc, atom/damage_source = null, mob/attacker = null) + //check attack direction + var/attack_dir = 0 //direction from the user to the source of the attack + if(istype(damage_source, /obj/item/projectile)) + var/obj/item/projectile/P = damage_source + attack_dir = get_dir(get_turf(user), P.starting) + else if(attacker) + attack_dir = get_dir(get_turf(user), get_turf(attacker)) + else if(damage_source) + attack_dir = get_dir(get_turf(user), get_turf(damage_source)) + + world << "attack_dir: [print_dir(attack_dir)]" + + if(!(attack_dir && (attack_dir & bad_arc))) + return 1 + return 0 + + + /obj/item/weapon/cloaking_device name = "cloaking device" - desc = "Use this to become invisible to the human eyesocket." + desc = "Use this to become invisible to the human eye." icon = 'icons/obj/device.dmi' icon_state = "shield0" var/active = 0.0 @@ -114,3 +171,4 @@ if(ismob(loc)) loc:update_icons() ..() + diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index 4bcefa5c15..7a9d6f3f20 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -113,8 +113,26 @@ slowdown = 1 armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) -/obj/item/clothing/suit/armor/reactive/IsShield() - if(active) +/obj/item/clothing/suit/armor/reactive/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/attack_text = "the attack") + if(prob(50)) + user.visible_message("The reactive teleport system flings [user] clear of the attack!") + var/list/turfs = new/list() + for(var/turf/T in orange(6, user)) + if(istype(T,/turf/space)) continue + if(T.density) continue + if(T.x>world.maxx-6 || T.x<6) continue + if(T.y>world.maxy-6 || T.y<6) continue + turfs += T + if(!turfs.len) turfs += pick(/turf in orange(6)) + var/turf/picked = pick(turfs) + if(!isturf(picked)) return + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(user.loc, "sparks", 50, 1) + + user.loc = picked return 1 return 0 diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index ce9ac0f225..fbd998fc52 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -252,8 +252,26 @@ New() item_color = "red" -/obj/item/weapon/holo/esword/IsShield() - if(active) +/obj/item/weapon/holo/esword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/attack_text = "the attack") + if(!active) + return 0 + + //parry only melee holo attacks + if(!istype(damage_source, /obj/item/weapon/holo) || (attacker && get_dist(user, attacker) > 1)) + return 0 + + //block as long as they are not directly behind us + var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block + if(!check_shield_arc(user, bad_arc, damage_source, attacker)) + return 0 + + if(prob(50)) + user.visible_message("\The [user] parries [attack_text] with \the [src]!") + + var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() + spark_system.set_up(5, 0, user.loc) + spark_system.start() + playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) return 1 return 0 diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index d9535b106c..e15f9a554d 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -19,8 +19,7 @@ // Should this all be in Touch()? if(istype(H)) - if((H != src) && check_shields(0, H.name)) - visible_message("\red [H] attempted to touch [src]!") + if(H != src && check_shields(0, null, H, H.name)) H.do_attack_animation(src) return 0 diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 5ecfa1807c..b66a610368 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -17,7 +17,7 @@ emp_act var/obj/item/organ/external/organ = get_organ() //Shields - if(check_shields(P.damage, "the [P.name]")) + if(check_shields(P.damage, P, null, "the [P.name]")) P.on_hit(src, 2, def_zone) return 2 @@ -148,32 +148,9 @@ emp_act return gear return null -/mob/living/carbon/human/proc/check_shields(var/damage = 0, var/attack_text = "the attack") - if(l_hand && istype(l_hand, /obj/item/weapon))//Current base is the prob(50-d/3) - var/obj/item/weapon/I = l_hand - if(I.IsShield() && (prob(50 - round(damage / 3)))) - visible_message("\red [src] blocks [attack_text] with the [l_hand.name]!") - return 1 - if(r_hand && istype(r_hand, /obj/item/weapon)) - var/obj/item/weapon/I = r_hand - if(I.IsShield() && (prob(50 - round(damage / 3)))) - visible_message("\red [src] blocks [attack_text] with the [r_hand.name]!") - return 1 - if(wear_suit && istype(wear_suit, /obj/item/)) - var/obj/item/I = wear_suit - if(I.IsShield() && (prob(35))) - visible_message("\red The reactive teleport system flings [src] clear of [attack_text]!") - var/list/turfs = new/list() - for(var/turf/T in orange(6)) - if(istype(T,/turf/space)) continue - if(T.density) continue - if(T.x>world.maxx-6 || T.x<6) continue - if(T.y>world.maxy-6 || T.y<6) continue - turfs += T - if(!turfs.len) turfs += pick(/turf in orange(6)) - var/turf/picked = pick(turfs) - if(!isturf(picked)) return - src.loc = picked +/mob/living/carbon/human/proc/check_shields(var/damage = 0, var/atom/damage_source = null, var/mob/attacker = null, var/attack_text = "the attack") + for(var/obj/item/shield in list(l_hand, r_hand, wear_suit)) + if(shield && shield.handle_shield(src, damage, damage_source, attacker, attack_text)) return 1 return 0 @@ -206,7 +183,7 @@ emp_act if(user.a_intent == "disarm") effective_force = round(I.force/2) var/hit_area = affecting.name - if((user != src) && check_shields(effective_force, "the [I.name]")) + if((user != src) && check_shields(effective_force, I, user, "the [I.name]")) return 0 if(istype(I,/obj/item/weapon/card/emag)) @@ -335,7 +312,7 @@ emp_act O.throwing = 0 //it hit, so stop moving - if ((O.thrower != src) && check_shields(throw_damage, "[O]")) + if ((O.thrower != src) && check_shields(throw_damage, O, thrower, "[O]")) return var/obj/item/organ/external/affecting = get_organ(zone) diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index c06b57d54c..19ac8c62fb 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -237,7 +237,7 @@ var/hit_area = affecting.name - if((user != target) && H.check_shields(7, "the [src.name]")) + if((user != target) && H.check_shields(7, src, user, "\the [src]")) return if (target != user && H.getarmor(target_zone, "melee") > 5 && prob(50))