From 40604ccde886fa745d89d021ac8ef4f59876b6d6 Mon Sep 17 00:00:00 2001
From: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com>
Date: Fri, 4 Oct 2024 16:16:46 +0100
Subject: [PATCH] Tonfa updates (#11200)
* Tonfa changes
* Update code/game/objects/items/melee/misc.dm
* Update misc.dm
* Update misc.dm
* Update code/game/objects/items/melee/misc.dm
* Review updates
---
code/__DEFINES/inventory.dm | 5 +
code/controllers/subsystem/traumas.dm | 1 +
code/game/objects/items/melee/misc.dm | 270 +++++++-----------
code/game/objects/items/shields.dm | 2 +-
code/game/objects/items/storage/belt.dm | 1 +
.../abductor/equipment/abduction_gear.dm | 1 +
code/modules/clothing/spacesuits/swat.dm | 1 +
code/modules/jobs/job_mail.dm | 1 +
code/modules/vending/security.dm | 2 +-
9 files changed, 109 insertions(+), 175 deletions(-)
diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm
index c739fdd12988f..b731118f29b82 100644
--- a/code/__DEFINES/inventory.dm
+++ b/code/__DEFINES/inventory.dm
@@ -153,6 +153,7 @@ GLOBAL_LIST_INIT(advanced_hardsuit_allowed, typecacheof(list(
/obj/item/flashlight,
/obj/item/gun,
/obj/item/melee/baton,
+ /obj/item/melee/tonfa,
/obj/item/reagent_containers/peppercloud_deployer,
/obj/item/restraints/handcuffs,
/obj/item/tank/internals)))
@@ -165,6 +166,7 @@ GLOBAL_LIST_INIT(security_hardsuit_allowed, typecacheof(list(
/obj/item/gun/energy,
/obj/item/gun/grenadelauncher,
/obj/item/melee/baton,
+ /obj/item/melee/tonfa,
/obj/item/reagent_containers/peppercloud_deployer,
/obj/item/restraints/handcuffs,
/obj/item/tank/internals)))
@@ -180,6 +182,7 @@ GLOBAL_LIST_INIT(detective_vest_allowed, typecacheof(list(
/obj/item/gun/grenadelauncher,
/obj/item/lighter,
/obj/item/melee/baton,
+ /obj/item/melee/tonfa,
/obj/item/melee/classic_baton/police,
/obj/item/reagent_containers/peppercloud_deployer,
/obj/item/restraints/handcuffs,
@@ -196,6 +199,7 @@ GLOBAL_LIST_INIT(security_vest_allowed, typecacheof(list(
/obj/item/gun/grenadelauncher,
/obj/item/knife/combat,
/obj/item/melee/baton,
+ /obj/item/melee/tonfa,
/obj/item/melee/classic_baton/police/telescopic,
/obj/item/reagent_containers/peppercloud_deployer,
/obj/item/restraints/handcuffs,
@@ -212,6 +216,7 @@ GLOBAL_LIST_INIT(security_wintercoat_allowed, typecacheof(list(
/obj/item/gun/grenadelauncher,
/obj/item/lighter,
/obj/item/melee/baton,
+ /obj/item/melee/tonfa,
/obj/item/melee/classic_baton/police/telescopic,
/obj/item/reagent_containers/peppercloud_deployer,
/obj/item/restraints/handcuffs,
diff --git a/code/controllers/subsystem/traumas.dm b/code/controllers/subsystem/traumas.dm
index 3cb9ddc494716..08cef4d647255 100644
--- a/code/controllers/subsystem/traumas.dm
+++ b/code/controllers/subsystem/traumas.dm
@@ -101,6 +101,7 @@ SUBSYSTEM_DEF(traumas)
/obj/item/clothing/under/rank/security/warden,
/obj/item/clothing/under/rank/security/head_of_security, /obj/item/clothing/under/rank/security/detective,
/obj/item/melee/baton, /obj/item/gun/energy/taser, /obj/item/restraints/handcuffs,
+ /obj/item/melee/tonfa,
/obj/machinery/door/airlock/security, /obj/effect/hallucination/simple/securitron)),
"clowns" = typecacheof(list(
diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm
index 7b54ef206b615..3d34bd08d1a72 100644
--- a/code/game/objects/items/melee/misc.dm
+++ b/code/game/objects/items/melee/misc.dm
@@ -181,7 +181,7 @@
var/stamina_damage = 55 // Do we deal stamina damage.
var/affect_silicon = FALSE // Does it stun silicons.
var/on_sound // "On" sound, played when switching between able to stun or not.
- var/on_stun_sound = "sound/effects/woodhit.ogg" // Default path to sound for when we stun.
+ var/on_stun_sound = 'sound/effects/woodhit.ogg' // Default path to sound for when we stun.
var/stun_animation = FALSE // Do we animate the "hit" when stunning.
var/on = TRUE // Are we on or off
@@ -299,6 +299,7 @@
if (H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
return
if(check_martial_counter(H, user))
+ log_combat(user, target, "attempted to attack", src, "(blocked by martial arts)")
return
var/list/desc = get_stun_description(target, user)
@@ -340,178 +341,6 @@
stamina_damage = 20
stun_animation = TRUE
-//Former Wooden Baton
-/obj/item/melee/classic_baton/police/tonfa
- name = "Police Tonfa"
- desc = "Favored by hot headed Security Officers who don't want to get in trouble with CentCom but still want to get that nostalgic feeling of beating some criminal scum upside the head with a chunk of wood."
- icon_state = "beater"
- item_state = "beater"
- lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
- force = 8
- throwforce = 7
- cooldown = 0
- stamina_damage = 30 // 4 hits to stamcrit < that was a lie
- stun_animation = TRUE
- /// Per-mob paralyze cooldowns.
- /// [mob] = [world.time where the cooldown ends]
- var/static/list/paralyze_cooldowns = list()
- /// Per-mob trip cooldowns.
- /// [mob] = [world.time where the cooldown ends]
- var/static/list/trip_cooldowns = list()
-
-/obj/item/melee/classic_baton/police/tonfa/attack(mob/living/target, mob/living/user)
- var/def_check = target.getarmor(type = MELEE, penetration = armour_penetration)
-
- add_fingerprint(user)
- if((HAS_TRAIT(user, TRAIT_CLUMSY)) && prob(50))
- to_chat(user, "You hit yourself over the head.")
- user.adjustStaminaLoss(stamina_damage)
-
- additional_effects_carbon(user) // user is the target here
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- H.apply_damage(2*force, BRUTE, BODY_ZONE_HEAD)
- else
- user.take_bodypart_damage(2*force)
- return
- if(!isliving(target))
- return
- if(iscyborg(target))
- if (user.a_intent != INTENT_HARM)
- playsound(get_turf(src), on_stun_sound, 75, 1, -1)
- user.do_attack_animation(target) // The attacker cuddles the Cyborg, awww. No damage here.
- return
- return ..()
- if (user.a_intent == INTENT_HARM)
- if(!..())
- target.apply_damage(force, STAMINA, blocked = def_check)
- return
- else if(cooldown_check > world.time)
- var/wait_desc = get_wait_description()
- if (wait_desc)
- to_chat(user, wait_desc)
- return
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- if (H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
- return
- if(check_martial_counter(H, user))
- return
-
- var/list/desc = get_stun_description(target, user)
- var/obj/item/bodypart/La = target.get_bodypart(BODY_ZONE_L_ARM)
- var/obj/item/bodypart/Ra = target.get_bodypart(BODY_ZONE_R_ARM)
- var/obj/item/bodypart/Ll = target.get_bodypart(BODY_ZONE_L_LEG)
- var/obj/item/bodypart/Rl = target.get_bodypart(BODY_ZONE_R_LEG)
- var/mob/living/carbon/human/T = target
-
- user.do_attack_animation(target)
- playsound(get_turf(src), on_stun_sound, 75, 1, -1)
- additional_effects_carbon(target, user)
- if(user.is_zone_selected(BODY_ZONE_CHEST) || user.is_zone_selected(BODY_ZONE_PRECISE_GROIN))
- target.apply_damage(stamina_damage, STAMINA, BODY_ZONE_CHEST, def_check)
- log_combat(user, target, "stunned", src)
- target.visible_message(desc["visiblestun"], desc["localstun"])
-
- else if(user.is_zone_selected(BODY_ZONE_HEAD) || user.is_zone_selected(BODY_ZONE_PRECISE_EYES) || user.is_zone_selected(BODY_ZONE_PRECISE_MOUTH))
- target.apply_damage(stamina_damage*0.8, STAMINA, BODY_ZONE_HEAD, def_check) // 90 : 5 = 18 , 5 hits to KnockOut
-
- if(target.staminaloss > 89 && !target.has_status_effect(STATUS_EFFECT_PARALYZED) && (!paralyze_cooldowns[target] || COOLDOWN_FINISHED(src, paralyze_cooldowns[target])))
- T.force_say(user)
- target.balloon_alert_to_viewers("Knock-Down!")
- if(!target.has_status_effect(STATUS_EFFECT_PARALYZED))
- target.Paralyze(80)
- target.setStaminaLoss(0)
- playsound(usr.loc, "sound/machines/bellsound.ogg", 15, 1)
- log_combat(user, target, "Knocked-Down", src)
- if(CHECK_BITFIELD(target.mobility_flags, MOBILITY_STAND)) //this is here so the "falls" message doesn't appear if the target is already on the floor
- target.visible_message("[T] [pick(list("falls limp like a bag of bricks.","falls to the ground, unresponsive.","lays down on the ground.","got [T.p_their()] dome rung in."))]")
- else
- target.visible_message("[T] [pick(list("goes limp.","falls flat."))]")
- COOLDOWN_START(src, paralyze_cooldowns[target], 16 SECONDS)
- else
- log_combat(user, target, "stunned", src)
- target.visible_message(desc["visiblestun"], desc["localstun"])
-
- else if(user.is_zone_selected(BODY_ZONE_L_LEG))
- log_combat(user, target, "stunned", src)
- target.visible_message(desc["visibleleg"], desc["localleg"])
- if (Rl.get_staminaloss() < 26 && Ra.get_staminaloss() < 26 && La.get_staminaloss() < 26)
- target.apply_damage(stamina_damage, STAMINA, BODY_ZONE_L_LEG, def_check)
- else
- target.apply_damage(stamina_damage*0.5, STAMINA, BODY_ZONE_L_LEG, def_check)
- if (Ll.get_staminaloss() == 50)
- target.apply_damage(stamina_damage*0.5, STAMINA, BODY_ZONE_CHEST, def_check)
- else
- target.apply_damage(stamina_damage*0.2, STAMINA, BODY_ZONE_CHEST, def_check)
-
- if(Ll.get_staminaloss() == 50 && CHECK_BITFIELD(target.mobility_flags, MOBILITY_STAND) && (!trip_cooldowns[target] || COOLDOWN_FINISHED(src, trip_cooldowns[target])))
- target.visible_message("[T] [pick(list("falls down.","falls face first into the floor.","gets viciously tripped.","got clumsy."))]")
- target.balloon_alert_to_viewers("Tripped!")
- target.Knockdown(7)
- log_combat(user, target, "tripped", src)
- target.visible_message(desc["visibletrip"], desc["localtrip"])
- playsound(usr.loc, "sound/misc/slip.ogg", 30, 1)
- COOLDOWN_START(src, trip_cooldowns[target], 3 SECONDS)
-
- else if(user.is_zone_selected(BODY_ZONE_R_LEG))
- log_combat(user, target, "stunned", src)
- target.visible_message(desc["visibleleg"], desc["localleg"])
- if (Ll.get_staminaloss() < 26 && Ra.get_staminaloss() < 26 && La.get_staminaloss() < 26)
- target.apply_damage(stamina_damage, STAMINA, BODY_ZONE_R_LEG, def_check)
- else
- target.apply_damage(stamina_damage*0.5, STAMINA, BODY_ZONE_R_LEG, def_check)
- if (Rl.get_staminaloss() == 50)
- target.apply_damage(stamina_damage*0.5, STAMINA, BODY_ZONE_CHEST, def_check)
- else
- target.apply_damage(stamina_damage*0.2, STAMINA, BODY_ZONE_CHEST, def_check)
-
- if(Rl.get_staminaloss() == 50 && CHECK_BITFIELD(target.mobility_flags, MOBILITY_STAND) && (!trip_cooldowns[target] || COOLDOWN_FINISHED(src, trip_cooldowns[target])))
- target.visible_message("[T] [pick(list("falls down.","falls face first into the floor.","gets viciously tripped.","got clumsy."))]")
- target.balloon_alert_to_viewers("Tripped!")
- target.Knockdown(7)
- log_combat(user, target, "tripped", src)
- playsound(usr.loc, "sound/misc/slip.ogg", 30, 1)
- target.visible_message(desc["visibletrip"], desc["localtrip"])
- COOLDOWN_START(src, trip_cooldowns[target], 3 SECONDS)
-
- else if(user.is_zone_selected(BODY_ZONE_L_ARM))
- if(!La.get_staminaloss() == 50)
- log_combat(user, target, "stunned", src)
- target.visible_message(desc["visiblearm"], desc["localarm"])
- else
- log_combat(user, target, "disarmed", src)
- target.visible_message(desc["visibledisarm"], desc["localdisarm"])
- if (Ra.get_staminaloss() < 26 && Ll.get_staminaloss() < 26 && Rl.get_staminaloss() < 26)
- target.apply_damage(stamina_damage*0.8, STAMINA, BODY_ZONE_L_ARM, def_check)
- else
- target.apply_damage(stamina_damage*0.2, STAMINA, BODY_ZONE_L_ARM, def_check)
- if (La.get_staminaloss() == 50)
- target.apply_damage(stamina_damage*0.5, STAMINA, BODY_ZONE_CHEST, def_check)
- else
- target.apply_damage(stamina_damage*0.2, STAMINA, BODY_ZONE_CHEST, def_check)
-
- else if(user.is_zone_selected(BODY_ZONE_R_ARM))
- if(!Ra.get_staminaloss() == 50)
- log_combat(user, target, "stunned", src)
- target.visible_message(desc["visiblearm"], desc["localarm"])
- else
- log_combat(user, target, "disarmed", src)
- target.visible_message(desc["visibledisarm"], desc["localdisarm"])
- if (La.get_staminaloss() < 26 && Ll.get_staminaloss() < 26 && Rl.get_staminaloss() < 26)
- target.apply_damage(stamina_damage*0.8, STAMINA, BODY_ZONE_R_ARM, def_check)
- else
- target.apply_damage(stamina_damage*0.2, STAMINA, BODY_ZONE_R_ARM, def_check)
- if (Ra.get_staminaloss() == 50)
- target.apply_damage(stamina_damage*0.5, STAMINA, BODY_ZONE_CHEST, def_check)
- else
- target.apply_damage(stamina_damage*0.2, STAMINA, BODY_ZONE_CHEST, def_check)
-
- add_fingerprint(user)
-
- COOLDOWN_START(src, cooldown_check, cooldown)
-
//Telescopic Baton
/obj/item/melee/classic_baton/police/telescopic
name = "telescopic baton"
@@ -711,6 +540,7 @@
if (H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
return
if(check_martial_counter(H, user))
+ log_combat(user, target, "attempted to attack", src, "(blocked by martial arts)")
return
var/list/desc = get_stun_description(target, user)
@@ -1074,3 +904,97 @@
target.throw_at(throw_at, throw_range, 3)
cooldown = world.time + 15
+
+//Former Wooden Baton
+/obj/item/melee/tonfa
+ name = "Police Tonfa"
+ desc = "A traditional police baton for gaining the submission of an uncooperative target without the use of lethal-force. \
+ As with all traditional weapons, the target will find themselves bruised, but alive. It has proven to be effective in preventing \
+ repeat offenses and has brought employment to lawyers for decades."
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "beater"
+ item_state = "beater"
+ worn_icon_state = "classic_baton"
+ lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
+ force = 12
+ throwforce = 7
+ slot_flags = ITEM_SLOT_BELT
+ w_class = WEIGHT_CLASS_LARGE
+ hitsound = 'sound/effects/woodhit.ogg'
+ /// Damage dealt while on help intent
+ var/non_harm_force = 3
+ /// Stamina damage dealt
+ var/stamina_force = 25
+
+// #11200 Review - TEMP: Hacky code to deal with force string for this item.
+/obj/item/melee/tonfa/openTip(location, control, params, mob/user)
+ if (user != null && user.a_intent != INTENT_HARM)
+ force = non_harm_force
+ else
+ force = initial(force)
+ return ..()
+
+/obj/item/melee/tonfa/attack(mob/living/target, mob/living/user)
+ var/target_zone = user.get_combat_bodyzone(target)
+ var/armour_level = target.getarmor(target_zone, STAMINA, penetration = armour_penetration - 15)
+
+ add_fingerprint(user)
+ if((HAS_TRAIT(user, TRAIT_CLUMSY)) && prob(50))
+ to_chat(user, "You hit yourself over the head.")
+ user.adjustStaminaLoss(stamina_force)
+
+ // Deal full damage
+ force = initial(force)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ H.apply_damage(2*force, BRUTE, BODY_ZONE_HEAD)
+ else
+ user.take_bodypart_damage(2*force)
+ return
+ if(!isliving(target))
+ return ..()
+ if(iscyborg(target))
+ if (user.a_intent != INTENT_HARM)
+ playsound(get_turf(src), hitsound, 75, 1, -1)
+ user.do_attack_animation(target) // The attacker cuddles the Cyborg, awww. No damage here.
+ return
+ if (user.a_intent != INTENT_HARM)
+ force = non_harm_force
+ else
+ force = initial(force)
+ if(ishuman(target))
+ var/mob/living/carbon/human/H = target
+ if (H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
+ return
+ if(check_martial_counter(H, user))
+ log_combat(user, target, "attempted to attack", src, "(blocked by martial arts)")
+ return
+
+ var/mob/living/carbon/human/T = target
+
+ target.visible_message("[user] strikes [target] in the [parse_zone(target_zone)].", "You strike [target] in the [parse_zone(target_zone)].")
+ log_combat(user, target, "attacked", src)
+
+ // If the target has a lot of stamina loss, knock them down
+ if ((user.is_zone_selected(BODY_ZONE_L_LEG) || user.is_zone_selected(BODY_ZONE_R_LEG)) && target.getStaminaLoss() > 22)
+ var/effectiveness = CLAMP01((target.getStaminaLoss() - 22) / 50)
+ log_combat(user, target, "knocked-down", src, "(additional effect)")
+ // Move the target back upon knockdown, to give them some time to recover
+ var/shove_dir = get_dir(user.loc, target.loc)
+ var/turf/target_shove_turf = get_step(target.loc, shove_dir)
+ var/mob/living/carbon/human/target_collateral_human = locate(/mob/living/carbon) in target_shove_turf.contents
+ if (target_collateral_human && target_shove_turf != get_turf(user))
+ target.Knockdown(max(0.5 SECONDS, effectiveness * 4 SECONDS * (100-armour_level)/100))
+ target_collateral_human.Knockdown(0.5 SECONDS)
+ else
+ target.Knockdown(effectiveness * 4 SECONDS * (100-armour_level)/100)
+ target.Move(target_shove_turf, shove_dir)
+ if (user.is_zone_selected(BODY_ZONE_L_LEG) || user.is_zone_selected(BODY_ZONE_R_LEG) || user.is_zone_selected(BODY_ZONE_L_ARM) || user.is_zone_selected(BODY_ZONE_R_ARM))
+ // 4-5 hits on an unarmoured target
+ target.apply_damage(stamina_force*0.6, STAMINA, target_zone, armour_level)
+ else
+ // 4-5 hits on an unarmoured target
+ target.apply_damage(stamina_force, STAMINA, target_zone, armour_level)
+
+ return ..()
diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm
index 86000c43e0ecf..89f5ab3cae700 100644
--- a/code/game/objects/items/shields.dm
+++ b/code/game/objects/items/shields.dm
@@ -104,7 +104,7 @@
transparent = TRUE
/obj/item/shield/riot/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/melee/baton))
+ if(istype(W, /obj/item/melee) && W.sharpness == IS_BLUNT)
if(cooldown < world.time - 25)
user.visible_message("[user] bashes [src] with [W]!")
playsound(user.loc, 'sound/effects/shieldbash.ogg', 50, 1)
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 20589f52313a8..e247252257742 100644
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -310,6 +310,7 @@
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
var/static/list/can_hold = typecacheof(list(
/obj/item/melee/baton,
+ /obj/item/melee/tonfa,
/obj/item/melee/classic_baton/police,
/obj/item/grenade,
/obj/item/reagent_containers/peppercloud_deployer,
diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
index 7a234eb8613c7..daaaee3acf59f 100644
--- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm
+++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
@@ -19,6 +19,7 @@
/obj/item/abductor,
/obj/item/abductor/baton,
/obj/item/melee/baton,
+ /obj/item/melee/tonfa,
/obj/item/gun/energy,
/obj/item/restraints/handcuffs
)
diff --git a/code/modules/clothing/spacesuits/swat.dm b/code/modules/clothing/spacesuits/swat.dm
index 0d9d3929d15eb..4982cc85b9156 100644
--- a/code/modules/clothing/spacesuits/swat.dm
+++ b/code/modules/clothing/spacesuits/swat.dm
@@ -8,6 +8,7 @@
/obj/item/ammo_box,
/obj/item/ammo_casing,
/obj/item/melee/baton,
+ /obj/item/melee/tonfa,
/obj/item/restraints/handcuffs,
/obj/item/tank/internals,
/obj/item/knife/combat
diff --git a/code/modules/jobs/job_mail.dm b/code/modules/jobs/job_mail.dm
index 2618a19e5b6aa..3e2e6525e4a18 100644
--- a/code/modules/jobs/job_mail.dm
+++ b/code/modules/jobs/job_mail.dm
@@ -186,6 +186,7 @@
/obj/effect/spawner/mail/donut = 20,
/obj/effect/spawner/mail/rdonut = 15,
/obj/item/melee/baton = 1,
+ /obj/item/melee/tonfa,
)
//DETECTIVE
diff --git a/code/modules/vending/security.dm b/code/modules/vending/security.dm
index cb21eeb0ecf44..8d37864f0eb94 100644
--- a/code/modules/vending/security.dm
+++ b/code/modules/vending/security.dm
@@ -16,7 +16,7 @@
/obj/item/holosign_creator/security = 3,
/obj/item/restraints/legcuffs/bola/energy = 7,
/obj/item/club = 5,
- /obj/item/melee/classic_baton/police/tonfa = 5)
+ /obj/item/melee/tonfa = 5)
contraband = list(/obj/item/clothing/glasses/sunglasses/advanced = 2)
premium = list(/obj/item/storage/belt/security/webbing = 5,
/obj/item/storage/backpack/duffelbag/sec/deputy = 4,