diff --git a/code/game/objects/items/weapons/material/knives.dm b/code/game/objects/items/weapons/material/knives.dm
index f2528f57862..94bc9e8f692 100644
--- a/code/game/objects/items/weapons/material/knives.dm
+++ b/code/game/objects/items/weapons/material/knives.dm
@@ -99,15 +99,9 @@
icon_state = "machete"
force_divisor = 0.3 // 18 when hardness 60 (steel)
attack_verb = list("slashed", "chopped", "gouged", "ripped", "cut")
- var/should_cleave = TRUE //Now hatchets inherit from the machete, and thus knives. Tables turned.
+ can_cleave = TRUE //Now hatchets inherit from the machete, and thus knives. Tables turned.
slot_flags = SLOT_BELT
-// This cannot go into afterattack since some mobs delete themselves upon dying.
-/obj/item/weapon/material/knife/machete/pre_attack(var/mob/living/target, var/mob/living/user)
- if(should_cleave && istype(target))
- cleave(user, target)
- ..()
-
/obj/item/weapon/material/knife/tacknife/survival
name = "survival knife"
desc = "A hunting grade survival knife."
diff --git a/code/game/objects/items/weapons/material/misc.dm b/code/game/objects/items/weapons/material/misc.dm
index 87bfb0454d3..bbb8171b10b 100644
--- a/code/game/objects/items/weapons/material/misc.dm
+++ b/code/game/objects/items/weapons/material/misc.dm
@@ -28,7 +28,7 @@
icon = 'icons/obj/weapons.dmi'
icon_state = "unathiknife"
attack_verb = list("ripped", "torn", "cut")
- should_cleave = FALSE
+ can_cleave = FALSE
var hits = 0
/obj/item/weapon/material/knife/machete/hatchet/unathiknife/attack(mob/M as mob, mob/user as mob)
diff --git a/code/game/objects/items/weapons/material/twohanded.dm b/code/game/objects/items/weapons/material/twohanded.dm
index 6631ec25fa9..acc3132712b 100644
--- a/code/game/objects/items/weapons/material/twohanded.dm
+++ b/code/game/objects/items/weapons/material/twohanded.dm
@@ -94,6 +94,7 @@
force_wielded = 30
attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut")
applies_material_colour = 0
+ can_cleave = TRUE
/obj/item/weapon/material/twohanded/fireaxe/update_held_icon()
var/mob/living/M = loc
@@ -124,12 +125,6 @@
var/obj/effect/plant/P = A
P.die_off()
-// This cannot go into afterattack since some mobs delete themselves upon dying.
-/obj/item/weapon/material/twohanded/fireaxe/pre_attack(var/mob/living/target, var/mob/living/user)
- if(istype(target))
- cleave(user, target)
- ..()
-
/obj/item/weapon/material/twohanded/fireaxe/scythe
icon_state = "scythe0"
base_icon = "scythe"
diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm
index f7497ec3ff4..ad30eb0a57f 100644
--- a/code/game/objects/items/weapons/melee/energy.dm
+++ b/code/game/objects/items/weapons/melee/energy.dm
@@ -87,6 +87,7 @@
attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut")
sharp = 1
edge = 1
+ can_cleave = TRUE
/obj/item/weapon/melee/energy/axe/activate(mob/living/user)
..()
@@ -98,12 +99,6 @@
icon_state = initial(icon_state)
to_chat(user, "\The [src] is de-energised. It's just a regular axe now.")
-// This cannot go into afterattack since some mobs delete themselves upon dying.
-/obj/item/weapon/melee/energy/axe/pre_attack(var/mob/living/target, var/mob/living/user)
- if(istype(target))
- cleave(user, target)
- ..()
-
/obj/item/weapon/melee/energy/axe/suicide_act(mob/user)
var/datum/gender/TU = gender_datums[user.get_visible_gender()]
visible_message("\The [user] swings \the [src] towards [TU.his] head! It looks like [TU.he] [TU.is] trying to commit suicide.")
diff --git a/code/game/objects/weapons.dm b/code/game/objects/weapons.dm
index 7a619477eaf..c9a4babd9ef 100644
--- a/code/game/objects/weapons.dm
+++ b/code/game/objects/weapons.dm
@@ -2,6 +2,7 @@
name = "weapon"
icon = 'icons/obj/weapons.dmi'
hitsound = "swing_hit"
+ var/can_cleave = FALSE // If true, a 'cleaving' attack will occur.
var/cleaving = FALSE // Used to avoid infinite cleaving.
/obj/item/weapon/Bump(mob/M as mob)
@@ -16,14 +17,17 @@
)
// Attacks mobs (atm only simple ones due to friendly fire issues) that are adjacent to the target and user.
-/obj/item/weapon/proc/cleave(var/mob/living/user, var/mob/living/target)
+/obj/item/weapon/proc/cleave(mob/living/user, atom/target)
if(cleaving)
- return // We're busy.
+ return FALSE // We're busy.
+ if(!target.Adjacent(user))
+ return FALSE // Too far.
if(get_turf(user) == get_turf(target))
- return // Otherwise we would hit all eight surrounding tiles.
+ return FALSE // Otherwise we would hit all eight surrounding tiles.
+
cleaving = TRUE
var/hit_mobs = 0
- for(var/mob/living/simple_animal/SA in orange(get_turf(target), 1))
+ for(var/mob/living/simple_animal/SA in range(get_turf(target), 1))
if(SA.stat == DEAD) // Don't beat a dead horse.
continue
if(SA == user) // Don't hit ourselves. Simple mobs shouldn't be able to do this but that might change later to be able to hit all mob/living-s.
@@ -42,6 +46,13 @@
if(hit_mobs)
to_chat(user, "You used \the [src] to attack [hit_mobs] other thing\s!")
cleaving = FALSE // We're done now.
+ return hit_mobs > 0 // Returns TRUE if anything got hit.
+
+// This cannot go into afterattack since some mobs delete themselves upon dying.
+/obj/item/weapon/material/pre_attack(mob/living/target, mob/living/user)
+ if(can_cleave && istype(target))
+ cleave(user, target)
+ ..()
// This is purely the visual effect of cleaving.
/obj/item/weapon/proc/cleave_visual(var/mob/living/user, var/mob/living/target)
diff --git a/code/game/turfs/simulated/floor_attackby.dm b/code/game/turfs/simulated/floor_attackby.dm
index 5602eac320d..91371e7beb5 100644
--- a/code/game/turfs/simulated/floor_attackby.dm
+++ b/code/game/turfs/simulated/floor_attackby.dm
@@ -3,6 +3,12 @@
if(!C || !user)
return 0
+ if(isliving(user) && istype(C, /obj/item/weapon))
+ var/mob/living/L = user
+ if(L.a_intent != I_HELP)
+ attack_tile(C, L) // Be on help intent if you want to decon something.
+ return
+
if(flooring)
if(istype(C, /obj/item/weapon))
try_deconstruct_tile(C, user)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 100c3b5a31c..58da7d11291 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -86,6 +86,34 @@ turf/attackby(obj/item/weapon/W as obj, mob/user as mob)
S.gather_all(src, user)
return ..()
+// Hits a mob on the tile.
+/turf/proc/attack_tile(obj/item/weapon/W, mob/living/user)
+ if(!istype(W))
+ return FALSE
+
+ var/list/viable_targets = list()
+ var/success = FALSE // Hitting something makes this true. If its still false, the miss sound is played.
+
+ for(var/mob/living/L in contents)
+ if(L == user) // Don't hit ourselves.
+ continue
+ viable_targets += L
+
+ if(!viable_targets.len) // No valid targets on this tile.
+ if(W.can_cleave)
+ success = W.cleave(user, src)
+ else
+ var/mob/living/victim = pick(viable_targets)
+ success = W.resolve_attackby(victim, user)
+
+ user.setClickCooldown(user.get_attack_speed(W))
+ user.do_attack_animation(src, no_attack_icons = TRUE)
+
+ if(!success) // Nothing got hit.
+ user.visible_message("\The [user] swipes \the [W] over \the [src].")
+ playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
+ return success
+
/turf/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob)
var/turf/T = get_turf(user)
var/area/A = T.loc
diff --git a/code/modules/mob/animations.dm b/code/modules/mob/animations.dm
index 7f082e1ef08..5fe2122b8c8 100644
--- a/code/modules/mob/animations.dm
+++ b/code/modules/mob/animations.dm
@@ -201,8 +201,11 @@ note dizziness decrements automatically in the mob's Life() proc.
animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2)
animate(pixel_x = default_pixel_x, pixel_y = default_pixel_y, time = 2)
-/mob/living/do_attack_animation(atom/A)
+/mob/living/do_attack_animation(atom/A, no_attack_icons = FALSE)
..()
+ if(no_attack_icons)
+ return FALSE
+
//Check for clients with pref enabled
var/list/viewing = list()
for(var/m in viewers(A))