From f3537f38e6be30b8f553d6ce67cb405aac2785b1 Mon Sep 17 00:00:00 2001 From: kevinz000 <2003111+kevinz000@users.noreply.github.com> Date: Sat, 11 Apr 2020 07:09:50 -0700 Subject: [PATCH] shield bashing --- code/__DEFINES/flags/shields.dm | 18 ++++ code/_globalvars/bitfields.dm | 10 +++ code/game/objects/items/shields.dm | 127 +++++++++++++++++++++++++-- icons/effects/96x96_attack_sweep.dmi | Bin 0 -> 3701 bytes tgstation.dme | 1 + 5 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 code/__DEFINES/flags/shields.dm create mode 100644 icons/effects/96x96_attack_sweep.dmi diff --git a/code/__DEFINES/flags/shields.dm b/code/__DEFINES/flags/shields.dm new file mode 100644 index 0000000000..301f19a35c --- /dev/null +++ b/code/__DEFINES/flags/shields.dm @@ -0,0 +1,18 @@ +/// Transparent, let beams pass +#define SHIELD_TRANSPARENT (1<<0) +/// Can shield bash +#define SHIELD_CAN_BASH (1<<1) +/// Shield bash knockdown on wall hit +#define SHIELD_BASH_WALL_KNOCKDOWN (1<<2) +/// Shield bash always knockdown +#define SHIELD_BASH_ALWAYS_KNOCKDOWN (1<<3) +/// Shield bash disarm on wall hit +#define SHIELD_BASH_WALL_DISARM (1<<4) +/// Shield bash always disarm +#define SHIELD_BASH_ALWAYS_DISARM (1<<5) +/// You can shieldbash target someone on the ground for ground slam +#define SHIELD_BASH_GROUND_SLAM (1<<6) +/// Shield bashing someone on the ground will disarm +#define SHIELD_BASH_GROUND_SLAM_DISARM (1<<7) + +#define SHIELD_FLAGS_DEFAULT (SHIELD_CAN_BASH | SHIELD_BASH_WALL_KNOCKDOWN | SHIELD_BASH_WALL_DISARM | SHIELD_BASH_GROUND_SLAM | SHIELD_BASH_GROUND_SLAM_DISARM) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 5367322d8e..78d6df83e5 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -249,5 +249,15 @@ GLOBAL_LIST_INIT(bitfields, list( "COMBAT_FLAG_SOFT_STAMCRIT" = COMBAT_FLAG_SOFT_STAMCRIT, "COMBAT_FLAG_INTENTIONALLY_RESTING" = COMBAT_FLAG_INTENTIONALLY_RESTING, "COMBAT_FLAG_RESISTING_REST" = COMBAT_FLAG_RESISTING_REST + ), + "shield_flags" = list( + "SHIELD_TRANSPARENT" = SHIELD_TRANSPARENT, + "SHIELD_CAN_BASH" = SHIELD_CAN_BASH, + "SHIELD_BASH_WALL_KNOCKDOWN" = SHIELD_BASH_WALL_KNOCKDOWN, + "SHIELD_BASH_ALWAYS_KNOCKDOWN" = SHIELD_BASH_ALWAYS_KNOCKDOWN, + "SHIELD_BASH_WALL_DISARM" = SHIELD_BASH_WALL_DISARM, + "SHIELD_BASH_ALWAYS_DISARM" = SHIELD_BASH_ALWAYS_DISARM, + "SHIELD_BASH_GROUND_SLAM" = SHIELD_BASH_GROUND_SLAM, + "SHIELD_BASH_GROUND_SLAM_DISARM" = SHIELD_BASH_GROUND_SLAM_DISARM ) )) diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 37dfc417d5..8f3a6fd479 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -3,11 +3,124 @@ icon = 'icons/obj/items_and_weapons.dmi' block_chance = 50 armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) - var/transparent = FALSE // makes beam projectiles pass through the shield + /// Shield flags + var/shield_flags = SHIELD_FLAGS_DEFAULT + /// Last shieldbash world.time + var/last_shieldbash = 0 + /// Shieldbashing cooldown + var/shieldbash_cooldown = 5 SECONDS + /// Shieldbashing stamina cost + var/shieldbash_stamcost = 7.5 + /// Shieldbashing knockback + var/shieldbash_knockback = 2 + /// Shield bashing brute damage + var/shieldbash_brutedamage = 5 + /// Shield bashing stamina damage + var/shieldbash_stamdmg = 15 + /// Shield bashing daze duration + var/shieldbash_daze_duration = 3.5 SECONDS + +/obj/item/shield/examine(mob/user) + . = ..() + if(shield_flags & SHIELD_CAN_BASH) + . += "Right click on combat mode attack with [src] to shield bash!" + if(shield_flags & SHIELD_BASH_GROUND_SLAM) + . += "Directly rightclicking on a downed target with [src] will slam them instead of bashing." /obj/item/shield/proc/on_shield_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance) return TRUE +/obj/item/shield/alt_pre_attack(atom/A, mob/living/user, params) + user_shieldbash(user, A) + return TRUE + +/obj/item/shield/proc/do_shieldbash_effect(mob/living/user, dir) + var/px = 0 + var/py = 0 + switch(dir) + if(NORTH) + py = 12 + if(SOUTH) + py = -12 + if(EAST) + px = 12 + if(WEST) + px = -12 + var/oldpx = user.pixel_x + var/oldpy = user.pixel_y + animate(user, pixel_x = px, pixel_y = py, time = 1, easing = SINE_EASING | EASE_OUT) + animate(user, pixel_x = oldpx, pixel_y = oldpy, time = 1.5) + user.visible_message("[user] charges forwards with [src]!") + animate(new /obj/effect/temp_visual/dir_setting/shieldbash(user.loc, dir), alpha = 0, pixel_x = px + 4, pixel_y = py + 4, time = 3) + +/obj/item/shield/proc/bash_target(mob/living/user, mob/living/target) + if(!(target.status_flags & CANKNOCKDOWN) || HAS_TRAIT(src, TRAIT_STUNIMMUNE)) // should probably add stun absorption check at some point I guess.. + // unified stun absorption system when lol + target.visible_message("[user] slams [target] with [src], but [target] doesn't falter!", "[user] slams you with [src], but it barely fazes you!") + return FALSE + var/target_downed = !CHECK_MOBILITY(target, MOBILITY_STAND) + var/away_dir = get_dir(user, target) + var/wallhit = !(away_dir & (away_dir - 1)) && isclosedturf(get_step_away(target, user)) //no diagonals + var/disarming = (target_downed && (shield_flags & SHIELD_BASH_GROUND_SLAM_DISARM)) || (shield_flags & SHIELD_BASH_ALWAYS_DISARM) || (wallhit && (shield_flags & SHIELD_BASH_WALL_DISARM)) + var/knockdown = !target_downed && ((shield_flags & SHIELD_BASH_ALWAYS_KNOCKDOWN) || (wallhit && (shield_flags & SHIELD_BASH_WALL_KNOCKDOWN))) + target.visible_message("[target_downed? "[user] slams [src] into [target]" : "[user] bashes [target] with [src]"][shieldbash_daze_duration? ", dazing them":""][knockdown? " and knocking them down":""]!", + "[target_downed? "[user] slams [src] into you" : "[user] bashes you with [src]"][shieldbash_daze_duration? ", dazing you":""][knockdown? " and knocking you down":""]!") + if(knockdown) + target.KnockToFloor(disarming) + else if(disarming) + target.drop_all_held_items() + target.apply_damage(shieldbash_stamdmg, STAMINA, BODY_ZONE_CHEST) + target.apply_damage(shieldbash_brutedamage, BRUTE, BODY_ZONE_CHEST) + target.Daze(shieldbash_daze_duration) + if(!target_downed && shieldbash_knockback) + var/turf/targetturf = get_turf(target) + var/turf/userturf = get_turf(user) + var/dx = CLAMP(targetturf.x - userturf.x, -1, 1) + var/dy = CLAMP(targetturf.y - userturf.y, -1, 1) + dx *= shieldbash_knockback + dy *= shieldbash_knockback + var/turf/knockback_target = locate(CLAMP(userturf.x + dx, 1, world.maxx), CLAMP(userturf.y + dy, 1, world.maxy), z) + if(knockback_target) + for(var/i in 1 to shieldbash_knockback) + step(target, get_dir(target, knockback_target)) + return TRUE + +/obj/item/shield/proc/user_shieldbash(mob/living/user, atom/target) + if(!(shield_flags & SHIELD_CAN_BASH)) + to_chat(user, "[src] can't be used to shield bash!") + return FALSE + if(world.time < last_shieldbash + shieldbash_cooldown) + to_chat(user, "You can't bash with [src] again so soon!") + return FALSE + if(isliving(target)) //GROUND SLAAAM + if(!(shield_flags & SHIELD_BASH_GROUND_SLAM)) + to_chat(user, "You can't ground slam with [src]!") + return FALSE + bash_target(user, target) + last_shieldbash = world.time + user.adjustStaminaLossBuffered(shieldbash_stamcost) + return 1 + // Directional sweep! + last_shieldbash = world.time + user.adjustStaminaLossBuffered(shieldbash_stamcost) + // Since we are in combat mode, we can probably safely use the user's dir instead of getting their mouse pointing cardinal dir. + do_shieldbash_effect(user, user.dir) + var/list/checking = list(get_step(user, user.dir), get_step(user, turn(user.dir, 45)), get_step(user, turn(user.dir, -45))) + var/list/victims = list() + for(var/i in checking) + var/turf/T = i + for(var/mob/living/L in T) + victims += L + if(length(victims)) + for(var/i in victims) + bash_target(user, target) + return length(victims) + +/obj/effect/temp_visual/dir_setting/shield_bash + icon = 'icon/effects/96x96_attack_sweep.dmi' + icon_state = "shieldbash" + duration = 3 + /obj/item/shield/riot name = "riot shield" desc = "A shield adept at blocking blunt objects from connecting with the torso of the shield wielder." @@ -23,13 +136,13 @@ custom_materials = list(/datum/material/glass=7500, /datum/material/iron=1000) attack_verb = list("shoved", "bashed") var/cooldown = 0 //shield bash cooldown. based on world.time - transparent = TRUE + shield_flags = SHIELD_FLAGS_DEFAULT | SHIELD_TRANSPARENT max_integrity = 75 /obj/item/shield/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if(ismovableatom(object)) var/atom/movable/AM = object - if(transparent && (AM.pass_flags & PASSGLASS)) + if(CHECK_BITFIELD(shield_flags, SHIELD_TRANSPARENT) && (AM.pass_flags & PASSGLASS)) return BLOCK_NONE if(attack_type & ATTACK_TYPE_THROWN) final_block_chance += 30 @@ -88,7 +201,7 @@ item_state = "roman_shield" lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi' - transparent = FALSE + shield_flags = SHIELD_FLAGS_DEFAULT max_integrity = 65 /obj/item/shield/riot/roman/fake @@ -111,7 +224,7 @@ custom_materials = null resistance_flags = FLAMMABLE block_chance = 30 - transparent = FALSE + shield_flags = SHIELD_FLAGS_DEFAULT max_integrity = 55 /obj/item/shield/riot/buckler/shatter(mob/living/carbon/human/owner) @@ -238,7 +351,7 @@ throwforce = 15 //Massive pice of metal w_class = WEIGHT_CLASS_HUGE item_flags = SLOWS_WHILE_IN_HAND - transparent = FALSE + shield_flags = SHIELD_FLAGS_DEFAULT /obj/item/shield/riot/implant name = "riot tower shield" @@ -248,7 +361,7 @@ icon = 'icons/obj/items_and_weapons.dmi' block_chance = 30 //May be big but hard to move around to block. slowdown = 1 - transparent = FALSE + shield_flags = SHIELD_FLAGS_DEFAULT item_flags = SLOWS_WHILE_IN_HAND /obj/item/shield/riot/implant/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) diff --git a/icons/effects/96x96_attack_sweep.dmi b/icons/effects/96x96_attack_sweep.dmi new file mode 100644 index 0000000000000000000000000000000000000000..25547822f1b8f2fe3298f1df20a36c3e26902aca GIT binary patch literal 3701 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 I)6rl42NWaOVE_OC literal 0 HcmV?d00001 diff --git a/tgstation.dme b/tgstation.dme index 2f9323c5d2..c45807a533 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -121,6 +121,7 @@ #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\signals.dm" +#include "code\__DEFINES\flags\shields.dm" #include "code\__HELPERS\_cit_helpers.dm" #include "code\__HELPERS\_lists.dm" #include "code\__HELPERS\_logging.dm"