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))