Rewrites shield handling

Rewrites shield handling to be less hardcoded.
Shields now take into account the damage source and direction when blocking.
Riot shields no longer block most bullets, but are better at blocking melee and thrown items than they were previously.
Energy shields block projectiles with a similar probability as they did before, and block melee and thrown as well as riot shields do.
Shields no longer block from directly behind the player.
Weapons now only block melee attacks.
Cool effects when blocking with an energy shield or energy sword (including holoswords).
This commit is contained in:
mwerezak
2015-07-11 18:14:16 -04:00
parent 3cf542e5f1
commit 0a751322e3
10 changed files with 180 additions and 102 deletions

View File

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

View File

@@ -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()
/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("<span class='danger'>\The [user] parries [attack_text] with \the [src]!</span>")
playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1)
return 1
return 0
/obj/item/weapon/material/sword/suicide_act(mob/user)
viewers(user) << "<span class='danger'>[user] is falling on the [src.name]! It looks like \he's trying to commit suicide.</span>"

View File

@@ -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("<span class='danger'>\The [user] parries [attack_text] with \the [src]!</span>")
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
@@ -168,50 +188,6 @@
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 << "<span class='danger'>You twirl around a bit before losing your balance and impaling yourself on \the [src].</span>"
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"

View File

@@ -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("<span class='danger'>\The [user] parries [attack_text] with \the [src]!</span>")
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

View File

@@ -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("<span class='danger'>\The [user] blocks [attack_text] with \the [src]!</span>")
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,10 +31,19 @@
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)
/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("<span class='warning'>[user] bashes [src] with [W]!</span>")
@@ -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()
..()

View File

@@ -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("<span class='danger'>The reactive teleport system flings [user] clear of the attack!</span>")
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

View File

@@ -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("<span class='danger'>\The [user] parries [attack_text] with \the [src]!</span>")
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

View File

@@ -19,8 +19,7 @@
// Should this all be in Touch()?
if(istype(H))
if((H != src) && check_shields(0, H.name))
visible_message("\red <B>[H] attempted to touch [src]!</B>")
if(H != src && check_shields(0, null, H, H.name))
H.do_attack_animation(src)
return 0

View File

@@ -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 <B>[src] blocks [attack_text] with the [l_hand.name]!</B>")
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 <B>[src] blocks [attack_text] with the [r_hand.name]!</B>")
return 1
if(wear_suit && istype(wear_suit, /obj/item/))
var/obj/item/I = wear_suit
if(I.IsShield() && (prob(35)))
visible_message("\red <B>The reactive teleport system flings [src] clear of [attack_text]!</B>")
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)

View File

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