From 6ebc9119378f50a0e50a9fdddd1d6e7a20e9a226 Mon Sep 17 00:00:00 2001 From: necromanceranne <40847847+necromanceranne@users.noreply.github.com> Date: Tue, 16 Mar 2021 04:48:55 +1100 Subject: [PATCH] Batons knockdown and slowdown on rightclick, otherwise they stagger, but always do damage. Damage respects melee armor, but power cells grant armor pen based on max charge. --- code/__DEFINES/traits.dm | 1 + code/_globalvars/traits.dm | 2 +- code/game/objects/items/stunbaton.dm | 72 ++++++++++++++------- code/game/objects/items/teleprod.dm | 2 +- code/game/objects/structures/watercloset.dm | 2 +- 5 files changed, 53 insertions(+), 26 deletions(-) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 8e0886a30d..9b4e5a0ada 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -215,6 +215,7 @@ #define TRAIT_NO_STAMINA_BUFFER_REGENERATION "block_stamina_buffer_regen" /// Prevents stamina buffer regeneration #define TRAIT_NO_STAMINA_REGENERATION "block_stamina_regen" /// Prevents stamina regeneration #define TRAIT_ARMOR_BROKEN "armor_broken" //acts as if you are wearing no clothing when taking damage, does not affect non-clothing sources of protection +#define TRAIT_IWASBATONED "iwasbatoned" //some dastardly fellow has struck you with a baton and thought to use another to strike you again, the rogue /// forces update_density to make us not dense #define TRAIT_LIVING_NO_DENSITY "living_no_density" /// forces us to not render our overlays diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index ac6ea4e25c..26e0197deb 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -123,7 +123,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_HIGH_BLOOD" = TRAIT_HIGH_BLOOD, "TRAIT_EMPATH" = TRAIT_EMPATH, "TRAIT_FRIENDLY" = TRAIT_FRIENDLY, - "TRAIT_NICE_SHOT" = TRAIT_NICE_SHOT + "TRAIT_IWASBATONED" = TRAIT_IWASBATONED ), /obj/item/bodypart = list( "TRAIT_PARALYSIS" = TRAIT_PARALYSIS diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm index cc01e6aaee..ece3341eae 100644 --- a/code/game/objects/items/stunbaton.dm +++ b/code/game/objects/items/stunbaton.dm @@ -16,17 +16,20 @@ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 50, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) attack_speed = CLICK_CD_MELEE - var/stamforce = 35 + var/stamina_loss_amount = 35 var/turned_on = FALSE var/knockdown = TRUE var/obj/item/stock_parts/cell/cell var/hitcost = 750 var/throw_hit_chance = 35 var/preload_cell_type //if not empty the baton starts with this type of cell + var/cooldown_duration = 5 SECONDS //How long our baton rightclick goes on cooldown for after applying a knockdown + var/status_duration = 5 SECONDS //how long our status effects last for otherwise + COOLDOWN_DECLARE(shove_cooldown) /obj/item/melee/baton/examine(mob/user) . = ..() - . += "Right click attack while in combat mode to disarm instead of stun." + . += "Right click attack while in combat mode to knockdown, but only once per [cooldown_duration / 10] seconds." /obj/item/melee/baton/get_cell() . = cell @@ -74,6 +77,16 @@ //we're below minimum, turn off switch_status(FALSE) +///Check for our cell to determine how much penetration our weapon does. +/obj/item/melee/baton/proc/get_cell_zap_pen() + var/obj/item/stock_parts/cell/copper_top = get_cell() + if(copper_top) + var/chargepower = copper_top.maxcharge + var/zap_penetration = (chargepower/1000) //This is our effective penetration. Every 1000 max charge, we get 1 pen power. A high capacity cell is equal to 10 armor pen, as an example. + return zap_penetration + else + return 0 + /obj/item/melee/baton/proc/switch_status(new_status = FALSE, silent = FALSE) if(turned_on != new_status) turned_on = new_status @@ -101,6 +114,7 @@ var/obj/item/stock_parts/cell/copper_top = get_cell() if(copper_top) . += "\The [src] is [round(copper_top.percent())]% charged." + . += "\The [src] currently can penetrate [round(copper_top.maxcharge/1000)]% of enemy armor thanks to it's loaded cell." else . += "\The [src] does not have a power source installed." @@ -150,10 +164,10 @@ /obj/item/melee/baton/alt_pre_attack(atom/A, mob/living/user, params) if(!user.CheckActionCooldown(CLICK_CD_MELEE)) return - . = common_baton_melee(A, user, TRUE) //return true (attackchain interrupt) if this also returns true. no harm-disarming. + . = common_baton_melee(A, user, TRUE) //return true (attackchain interrupt) if this also returns true. no harm-shoving. //return TRUE to interrupt attack chain. -/obj/item/melee/baton/proc/common_baton_melee(mob/M, mob/living/user, disarming = FALSE) +/obj/item/melee/baton/proc/common_baton_melee(mob/M, mob/living/user, shoving = FALSE) if(iscyborg(M) || !isliving(M)) //can't baton cyborgs return FALSE if(turned_on && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50)) @@ -167,21 +181,26 @@ if(check_martial_counter(L, user)) return TRUE if(turned_on) - if(baton_stun(M, user, disarming)) + if(baton_stun(M, user, shoving)) user.do_attack_animation(M) else if(user.a_intent != INTENT_HARM) //they'll try to bash in the last proc. M.visible_message("[user] has prodded [M] with [src]. Luckily it was off.", \ "[user] has prodded you with [src]. Luckily it was off") - return disarming || (user.a_intent != INTENT_HARM) + return shoving || (user.a_intent != INTENT_HARM) -/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/living/user, disarming = FALSE) +/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/living/user, shoving = FALSE) var/list/return_list = list() if(L.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, return_list) & BLOCK_SUCCESS) //No message; check_shields() handles that playsound(L, 'sound/weapons/genhit.ogg', 50, 1) return FALSE - var/stunpwr = stamforce - stunpwr = block_calculate_resultant_damage(stunpwr, return_list) + var/final_stamina_loss_amount = stamina_loss_amount //Our stunning power for the baton + var/shoved = FALSE //Did we succeed on knocking our target over? + var/zap_penetration = get_cell_zap_pen() //Find out what kind of cell we have, and calculating the resultant armor pen we get from it + var/zap_block = L.run_armor_check(BODY_ZONE_CHEST, "melee", null, null, zap_penetration) //armor check, including calculation for armor penetration, for our attack + final_stamina_loss_amount = block_calculate_resultant_damage(final_stamina_loss_amount, return_list) + var/obj/item/stock_parts/cell/our_cell = get_cell() + if(!our_cell) switch_status(FALSE) return FALSE @@ -194,29 +213,34 @@ L.visible_message("[user] has prodded [L] with [src]. Luckily it was out of charge.", \ "[user] has prodded you with [src]. Luckily it was out of charge.") return FALSE - stunpwr *= round(stuncharge/hitcost, 0.1) + final_stamina_loss_amount *= round(stuncharge/hitcost, 0.1) if(user && !user.UseStaminaBuffer(getweight(user, STAM_COST_BATON_MOB_MULT), warn = TRUE)) return FALSE - if(!disarming) - if(knockdown) - L.DefaultCombatKnockdown(50, override_stamdmg = 0) //knockdown - L.adjustStaminaLoss(stunpwr) - else - L.drop_all_held_items() //no knockdown/stamina damage, instead disarm. + if(shoving && COOLDOWN_FINISHED(src, shove_cooldown) && !HAS_TRAIT(L, TRAIT_IWASBATONED)) //Rightclicking applies a knockdown, but only once every couple of seconds, based on the cooldown_duration var. If they were recently knocked down, they can't be knocked down again by a baton. + L.DefaultCombatKnockdown(50, override_stamdmg = 0) + L.apply_status_effect(STATUS_EFFECT_TASED_WEAK, status_duration) //Even if they shove themselves up, they're still slowed. + shoved = TRUE + COOLDOWN_START(src, shove_cooldown, cooldown_duration) + else //If we cannot/don't knock down the target, we apply a stagger, which keeps them from just running off + L.apply_status_effect(STATUS_EFFECT_STAGGERED, status_duration) - L.apply_effect(EFFECT_STUTTER, stamforce) + L.apply_damage (final_stamina_loss_amount, STAMINA, BODY_ZONE_CHEST, zap_block) + L.apply_effect(EFFECT_STUTTER, stamina_loss_amount) SEND_SIGNAL(L, COMSIG_LIVING_MINOR_SHOCK) if(user) L.lastattacker = user.real_name L.lastattackerckey = user.ckey - L.visible_message("[user] has [disarming? "disarmed" : "stunned"] [L] with [src]!", \ - "[user] has [disarming? "disarmed" : "stunned"] you with [src]!") - log_combat(user, L, disarming? "disarmed" : "stunned") + L.visible_message("[user] has [shoved ? "knocked to the floor" : "stunned"] [L] with [src]!", \ + "[user] has [shoved ? "knocked you to the floor" : "stunned you"] with [src]!") + log_combat(user, L, shoved ? "stunned and attempted knockdown" : "stunned") playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) + ADD_TRAIT(L, TRAIT_IWASBATONED, STATUS_EFFECT_TRAIT) //Prevents swapping to a new baton to avoid the cooldown by just acquiring more batons + addtimer(TRAIT_CALLBACK_REMOVE(L, TRAIT_IWASBATONED, STATUS_EFFECT_TRAIT), cooldown_duration) + if(ishuman(L)) var/mob/living/carbon/human/H = L H.forcesay(GLOB.hit_appends) @@ -228,7 +252,7 @@ user.visible_message("[user] accidentally hits [user.p_them()]self with [src]!", \ "You accidentally hit yourself with [src]!") SEND_SIGNAL(user, COMSIG_LIVING_MINOR_SHOCK) - user.DefaultCombatKnockdown(stamforce*6) + user.DefaultCombatKnockdown(stamina_loss_amount*6) playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) deductcharge(hitcost) @@ -306,16 +330,18 @@ w_class = WEIGHT_CLASS_BULKY force = 3 throwforce = 5 - stamforce = 25 + stamina_loss_amount = 25 hitcost = 1000 throw_hit_chance = 10 slot_flags = ITEM_SLOT_BACK + cooldown_duration = 7 SECONDS //It's a little on the weak side + status_duration = 2 //Slows someone for a tiny bit var/obj/item/assembly/igniter/sparkler /obj/item/melee/baton/cattleprod/Initialize() . = ..() sparkler = new (src) - sparkler.activate_cooldown = 5 + sparkler.activate_cooldown = 7 //Helps visualize the knockdown /obj/item/melee/baton/cattleprod/baton_stun() sparkler?.activate() diff --git a/code/game/objects/items/teleprod.dm b/code/game/objects/items/teleprod.dm index 63bde36976..ac853cae56 100644 --- a/code/game/objects/items/teleprod.dm +++ b/code/game/objects/items/teleprod.dm @@ -16,7 +16,7 @@ user.visible_message("[user] accidentally hits [user.p_them()]self with [src]!", \ "You accidentally hit yourself with [src]!") SEND_SIGNAL(user, COMSIG_LIVING_MINOR_SHOCK) - user.DefaultCombatKnockdown(stamforce * 6) + user.DefaultCombatKnockdown(stamina_loss_amount * 6) playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) if(do_teleport(user, get_turf(user), 50, channel = TELEPORT_CHANNEL_BLUESPACE)) deductcharge(hitcost) diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index dba8d5de45..01c62a7901 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -554,7 +554,7 @@ if(B.cell) if(B.cell.charge > 0 && B.turned_on) flick("baton_active", src) - var/stunforce = B.stamforce + var/stunforce = B.stamina_loss_amount user.DefaultCombatKnockdown(stunforce * 2) user.stuttering = stunforce/20 B.deductcharge(B.hitcost)