diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index a3bfd5614792..273ceaeeded4 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -147,6 +147,11 @@ #define COMSIG_ITEM_HIT_REACT "item_hit_react" #define COMPONENT_HIT_REACTION_BLOCK (1<<0) +/// from /datum/component/cleave_attack/perform_sweep(): (atom/target, obj/item/item, mob/living/user, params) +#define COMSIG_ATOM_CLEAVE_ATTACK "atom_cleave_attack" + // allows cleave attack to hit things it normally wouldn't + #define ATOM_ALLOW_CLEAVE_ATTACK (1<<0) + /// Called before an item is embedded (mob/living/carbon/target = carbon that it is getting embedded into) #define COMSIG_ITEM_EMBEDDED "mob_carbon_embedded" // Prevents the embed diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index bc2cea177ca6..65475f68432e 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -670,6 +670,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_APC_SHOCKING "apc_shocking" /// Properly wielded two handed item #define TRAIT_WIELDED "wielded" +/// This item is currently performing a cleaving attack +#define TRAIT_CLEAVING "cleaving" /// A transforming item that is actively extended / transformed #define TRAIT_TRANSFORM_ACTIVE "active_transform" /// Buckling yourself to objects with this trait won't immobilize you diff --git a/code/datums/components/cleave_attack.dm b/code/datums/components/cleave_attack.dm new file mode 100644 index 000000000000..bd7911d30625 --- /dev/null +++ b/code/datums/components/cleave_attack.dm @@ -0,0 +1,146 @@ +/datum/component/cleave_attack + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + /// Size of the attack arc in degrees + var/arc_size + /// Make this TRUE for two-handed weapons like axes + var/requires_wielded + /// How much slower is it to swing + var/swing_speed_mod + /// Which effect should this use + var/cleave_effect + /// Whether this item is disallowed from hitting more than one target + var/no_multi_hit + /// Callback when the cleave attack is finished + var/datum/callback/cleave_end_callback + +/datum/component/cleave_attack/Initialize( + arc_size=90, + swing_speed_mod=1.25, + requires_wielded=FALSE, + no_multi_hit=FALSE, + datum/callback/cleave_end_callback, + cleave_effect, + ... + ) + + if(!isitem(parent)) + return COMPONENT_INCOMPATIBLE + + src.arc_size = arc_size + src.swing_speed_mod = swing_speed_mod + src.requires_wielded = requires_wielded + src.no_multi_hit = no_multi_hit + src.cleave_end_callback = cleave_end_callback + set_cleave_effect(cleave_effect) // set it based on arc size if an effect wasn't specified + +/datum/component/cleave_attack/InheritComponent( + datum/component/C, + i_am_original, + arc_size, + swing_speed_mod, + requires_wielded, + datum/callback/cleave_end_callback, + cleave_effect + ) + + if(!i_am_original) + return + if(arc_size) + src.arc_size = arc_size + if(swing_speed_mod) + src.swing_speed_mod = swing_speed_mod + if(requires_wielded) + src.requires_wielded = requires_wielded + if(no_multi_hit) + src.no_multi_hit = no_multi_hit + if(cleave_end_callback) + src.cleave_end_callback = cleave_end_callback + set_cleave_effect(cleave_effect) + +/// Sets the cleave effect to the specified effect, or based on arc size if one wasn't specified. +/datum/component/cleave_attack/proc/set_cleave_effect(new_effect) + if(new_effect) + cleave_effect = new_effect + return + switch(arc_size) + if(0 to 120) + cleave_effect = /obj/effect/temp_visual/dir_setting/firing_effect/sweep_attack + if(120 to 240) + cleave_effect = /obj/effect/temp_visual/dir_setting/firing_effect/sweep_attack/semicircle + else + cleave_effect = /obj/effect/temp_visual/dir_setting/firing_effect/sweep_attack/full_circle + +/datum/component/cleave_attack/RegisterWithParent() + RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) + RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, PROC_REF(on_afterattack)) + +/datum/component/cleave_attack/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_ATOM_EXAMINE, COMSIG_ITEM_AFTERATTACK)) + +/datum/component/cleave_attack/proc/on_examine(atom/examined_item, mob/user, list/examine_list) + var/arc_desc + switch(arc_size) + if(0 to 90) + arc_desc = "narrow arc" + if(90 to 180) + arc_desc = "wide arc" + if(180 to 270) + arc_desc = "very wide arc" + if(270 to INFINITY) + arc_desc = "full circle" + examine_list += "It can swing in a [arc_desc]." + +/datum/component/cleave_attack/proc/on_afterattack(obj/item/item, atom/target, mob/user, proximity_flag, click_parameters) + if(proximity_flag || user.a_intent != INTENT_HARM) + return // don't sweep on precise hits or non-harmful intents + perform_sweep(item, target, user, click_parameters) + +/datum/component/cleave_attack/proc/perform_sweep(obj/item/item, atom/target, mob/living/user, params) + if(user.next_move > world.time) + return // don't spam it + if(requires_wielded && !HAS_TRAIT(item, TRAIT_WIELDED)) + return // if it needs to be wielded, check to make sure it is + + // some information we're going to need later + var/turf/user_turf = get_turf(user) + var/turf/center_turf = get_turf_in_angle(get_angle(user, target), user_turf) + var/facing_dir = get_dir(user, center_turf) + var/swing_direction = (user.active_hand_index % 2) ? -1 : 1 + + // make a list of turfs to swing across + var/list/turf_list = list() + var/turfs_count = round(arc_size / 90, 1) + for(var/i in -min(turfs_count, 3) to min(turfs_count, 4)) // do NOT hit the same tile more than once + turf_list.Add(get_step(user_turf, turn(facing_dir, i * 45 * swing_direction))) + + // do some effects so everyone knows you're swinging a weapon + playsound(item, 'sound/weapons/punchmiss.ogg', 50, TRUE) + new cleave_effect(user_turf, facing_dir) + + // now swing across those turfs + ADD_TRAIT(item, TRAIT_CLEAVING, REF(src)) + for(var/turf/T as anything in turf_list) + if(hit_atoms_on_turf(item, target, user, T, params)) + break + REMOVE_TRAIT(item, TRAIT_CLEAVING, REF(src)) + + // do these last so they don't get overridden during the attack loop + cleave_end_callback?.Invoke(item, user) + user.do_attack_animation(center_turf, no_effect=TRUE) + user.changeNext_move(CLICK_CD_MELEE * item.weapon_stats[SWING_SPEED] * swing_speed_mod) + user.weapon_slow(item) + +/// Hits all possible atoms on a turf, returns TRUE if the swing should end early +/datum/component/cleave_attack/proc/hit_atoms_on_turf(obj/item/item, atom/target, mob/living/user, turf/hit_turf, params) + for(var/atom/movable/hit_atom in hit_turf) + if(hit_atom == user || hit_atom == target) + continue // why are you hitting yourself + if(!(SEND_SIGNAL(hit_atom, COMSIG_ATOM_CLEAVE_ATTACK, item, user) & ATOM_ALLOW_CLEAVE_ATTACK)) + if(hit_atom.pass_flags & LETPASSTHROW) + continue // if you can throw something over it, you can swing over it too + if(!hit_atom.density && hit_atom.uses_integrity) + continue + item.melee_attack_chain(user, hit_atom, params) + if(no_multi_hit && isliving(hit_atom)) + return TRUE + return FALSE diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm index 4f2530061adb..a4b161fb1961 100644 --- a/code/datums/martial/sleeping_carp.dm +++ b/code/datums/martial/sleeping_carp.dm @@ -186,6 +186,7 @@ AddComponent(/datum/component/two_handed, \ force_wielded = 14, \ ) + AddComponent(/datum/component/cleave_attack, arc_size=180, requires_wielded=TRUE) /obj/item/melee/bostaff/update_icon_state() . = ..() diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index dc85d046db5a..8af238c3c807 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -45,7 +45,7 @@ /// Effect on hitting something var/hit_effect = ATTACK_EFFECT_SLASH /// Effect of the cleave attack - var/cleave_effect = /obj/effect/temp_visual/dir_setting/firing_effect/mecha_swipe + var/cleave_effect = /obj/effect/temp_visual/dir_setting/firing_effect/sweep_attack /obj/item/mecha_parts/mecha_equipment/melee_weapon/can_attach(obj/mecha/M) if(!..()) @@ -210,8 +210,7 @@ O.visible_message(span_danger("[chassis.name] strikes [O] with a wide swing of [src]!")) //Don't really need to make a message for EVERY object, just important ones playsound(O,'sound/weapons/smash.ogg', 50) //metallic bonk noise - var/turf/cleave_effect_loc = get_step(get_turf(src), SOUTHWEST) - new cleave_effect(cleave_effect_loc, chassis.dir) + new cleave_effect(get_turf(src), chassis.dir) /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/precise_attack(atom/target) special_hit(target) @@ -649,8 +648,7 @@ playsound(chassis, attack_sound, 50, 1) for(var/turf/T in list(get_turf(chassis), get_step(chassis, chassis.dir), get_step(chassis, turn(chassis.dir, -45)), get_step(chassis, turn(chassis.dir, 45)))) do_mop(chassis, T, 3) // mop the floor with them! - var/turf/cleave_effect_loc = get_step(get_turf(src), SOUTHWEST) - new cleave_effect(cleave_effect_loc, chassis.dir) + new cleave_effect(get_turf(src), chassis.dir) /obj/item/mecha_parts/mecha_equipment/melee_weapon/flyswatter name = "comically large flyswatter" diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 124ba550dfcf..3c7795a7f01c 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -20,31 +20,31 @@ . = ..() if(set_color) color = set_color - var/target_pixel_x = 0 - var/target_pixel_y = 0 + var/target_pixel_x = pixel_x + var/target_pixel_y = pixel_y switch(set_dir) if(NORTH) - target_pixel_y = 16 + target_pixel_y += 16 if(SOUTH) - target_pixel_y = -16 + target_pixel_y += -16 layer = ABOVE_MOB_LAYER if(EAST) - target_pixel_x = 16 + target_pixel_x += 16 if(WEST) - target_pixel_x = -16 + target_pixel_x += -16 if(NORTHEAST) - target_pixel_x = 16 - target_pixel_y = 16 + target_pixel_x += 16 + target_pixel_y += 16 if(NORTHWEST) - target_pixel_x = -16 - target_pixel_y = 16 + target_pixel_x += -16 + target_pixel_y += 16 if(SOUTHEAST) - target_pixel_x = 16 - target_pixel_y = -16 + target_pixel_x += 16 + target_pixel_y += -16 layer = ABOVE_MOB_LAYER if(SOUTHWEST) - target_pixel_x = -16 - target_pixel_y = -16 + target_pixel_x += -16 + target_pixel_y += -16 layer = ABOVE_MOB_LAYER animate(src, pixel_x = target_pixel_x, pixel_y = target_pixel_y, alpha = 0, time = duration) @@ -71,14 +71,14 @@ switch(newdir) if(NORTH) layer = BELOW_MOB_LAYER - pixel_x = rand(-3,3) - pixel_y = rand(4,6) + pixel_x += rand(-3,3) + pixel_y += rand(4,6) if(SOUTH) - pixel_x = rand(-3,3) - pixel_y = rand(-1,1) + pixel_x += rand(-3,3) + pixel_y += rand(-1,1) else - pixel_x = rand(-1,1) - pixel_y = rand(-1,1) + pixel_x += rand(-1,1) + pixel_y += rand(-1,1) ..() /obj/effect/temp_visual/dir_setting/firing_effect/energy @@ -89,11 +89,20 @@ icon_state = "shieldsparkles" duration = 0.3 SECONDS -/obj/effect/temp_visual/dir_setting/firing_effect/mecha_swipe +/obj/effect/temp_visual/dir_setting/firing_effect/sweep_attack icon = 'icons/effects/96x96.dmi' icon_state = "big_slash" + pixel_x = -32 + pixel_y = -32 duration = 0.3 SECONDS +/obj/effect/temp_visual/dir_setting/firing_effect/sweep_attack/semicircle + icon_state = "big_slash_180" + +/obj/effect/temp_visual/dir_setting/firing_effect/sweep_attack/full_circle + icon_state = "big_slash_360" + duration = 0.4 SECONDS + /obj/effect/temp_visual/dir_setting/ninja name = "ninja shadow" icon = 'icons/mob/mob.dmi' diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm index 8e01632a70d4..3cf8adbfd8f2 100644 --- a/code/game/objects/items/holy_weapons.dm +++ b/code/game/objects/items/holy_weapons.dm @@ -147,6 +147,10 @@ menutab = MENU_WEAPON additional_desc = "An exceptionally large sword, capable of occasionally deflecting blows." +/obj/item/nullrod/claymore/Initialize(mapload) + . = ..() + AddComponent(/datum/component/cleave_attack) + /obj/item/nullrod/claymore/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) if(attack_type == PROJECTILE_ATTACK) final_block_chance = 0 //Don't bring a sword to a gunfight @@ -359,6 +363,10 @@ menutab = MENU_WEAPON additional_desc = "The weapon of choice for a devout monk. Block incoming blows while striking weak points until your opponent is too exhausted to continue." +/obj/item/nullrod/bostaff/Initialize(mapload) + . = ..() + AddComponent(/datum/component/cleave_attack, arc_size=180) + /obj/item/nullrod/bostaff/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) if(attack_type == PROJECTILE_ATTACK) final_block_chance = 0 //Don't bring a stick to a gunfight @@ -610,6 +618,7 @@ . = ..() ADD_TRAIT(src, TRAIT_NODROP, HAND_REPLACEMENT_TRAIT) AddComponent(/datum/component/butchering, 30, 100, 0, hitsound) + AddComponent(/datum/component/cleave_attack) /obj/item/nullrod/armblade name = "dark blessing" @@ -633,6 +642,7 @@ . = ..() ADD_TRAIT(src, TRAIT_NODROP, HAND_REPLACEMENT_TRAIT) AddComponent(/datum/component/butchering, 80, 70) + AddComponent(/datum/component/cleave_attack) /obj/item/nullrod/armblade/tentacle name = "unholy blessing" @@ -1040,6 +1050,10 @@ it also swaps back if it gets thrown into the chaplain, but the chaplain catches menutab = MENU_MISC additional_desc = "You feel an unwoken presence in this one." +/obj/item/nullrod/talking/Initialize(mapload) + . = ..() + AddComponent(/datum/component/cleave_attack) + /obj/item/nullrod/talking/relaymove(mob/user) return //stops buckled message spam for the ghost. @@ -1340,6 +1354,10 @@ it also swaps back if it gets thrown into the chaplain, but the chaplain catches menutab = MENU_MISC //banish it from being associated with proper weapons additional_desc = "Hey, God here. Asking you to pick literally anything else as your implement of justice." +/obj/item/nullrod/sord/Initialize(mapload) + . = ..() + AddComponent(/datum/component/cleave_attack) // i guess??? + //NOT CHAPLAIN SPAWNABLE /obj/item/nullrod/talking/chainsword name = "possessed chainsaw sword" diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm index bf0e97233bb1..62f568306497 100644 --- a/code/game/objects/items/melee/energy.dm +++ b/code/game/objects/items/melee/energy.dm @@ -105,6 +105,10 @@ block_chance = 50 saber_color = "green" +/obj/item/melee/transforming/energy/sword/Initialize(mapload) + . = ..() + AddComponent(/datum/component/cleave_attack) // very cool + /obj/item/melee/transforming/energy/sword/attackby(obj/item/I, mob/living/user, params) if(istype(I, /obj/item/melee/transforming/energy/sword)) if(HAS_TRAIT(I, TRAIT_NODROP) || HAS_TRAIT(src, TRAIT_NODROP)) diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index f6e5c5423699..02fb769b7ddf 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -80,8 +80,13 @@ hitsound = 'sound/weapons/rapierhit.ogg' materials = list(/datum/material/iron = 1000) +/obj/item/melee/cutlass/Initialize(mapload) + . = ..() + AddComponent(/datum/component/cleave_attack) + /obj/item/melee/sabre/Initialize(mapload) . = ..() + AddComponent(/datum/component/cleave_attack) // YES AddComponent(/datum/component/butchering, 30, 95, 5) //fast and effective, but as a sword, it might damage the results. /obj/item/melee/sabre/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) diff --git a/code/game/objects/items/singularityhammer.dm b/code/game/objects/items/singularityhammer.dm index d5d4ba24e585..a4f2fd4ae875 100644 --- a/code/game/objects/items/singularityhammer.dm +++ b/code/game/objects/items/singularityhammer.dm @@ -100,6 +100,7 @@ force_wielded = 20, \ icon_wielded = "[base_icon_state]1", \ ) + AddComponent(/datum/component/cleave_attack, arc_size=180, requires_wielded=TRUE) /obj/item/mjolnir/update_icon_state() . = ..() diff --git a/code/game/objects/items/tools/crowbar.dm b/code/game/objects/items/tools/crowbar.dm index 027d16615440..83429cb1cdb2 100644 --- a/code/game/objects/items/tools/crowbar.dm +++ b/code/game/objects/items/tools/crowbar.dm @@ -62,6 +62,10 @@ item_state = "crowbar" toolspeed = 0.7 +/obj/item/crowbar/large/Initialize(mapload) + . = ..() + AddComponent(/datum/component/cleave_attack, no_multi_hit=TRUE) // it's big + /obj/item/crowbar/cyborg name = "hydraulic crowbar" desc = "A hydraulic prying tool, compact but powerful. Designed to replace crowbar in construction cyborgs." diff --git a/code/game/objects/items/two_handed/baseball_bat.dm b/code/game/objects/items/two_handed/baseball_bat.dm index 2f6dbb3153c1..400a7bbcd90a 100644 --- a/code/game/objects/items/two_handed/baseball_bat.dm +++ b/code/game/objects/items/two_handed/baseball_bat.dm @@ -21,6 +21,7 @@ /obj/item/melee/baseball_bat/Initialize(mapload) . = ..() AddComponent(/datum/component/two_handed, require_twohands = TRUE) + AddComponent(/datum/component/cleave_attack, arc_size=90, requires_wielded=TRUE, no_multi_hit=TRUE) /obj/item/melee/baseball_bat/homerun name = "home run bat" diff --git a/code/game/objects/items/two_handed/chainsaw.dm b/code/game/objects/items/two_handed/chainsaw.dm index 9aa4aa9affe9..aa801f98c3eb 100644 --- a/code/game/objects/items/two_handed/chainsaw.dm +++ b/code/game/objects/items/two_handed/chainsaw.dm @@ -26,6 +26,7 @@ /obj/item/melee/chainsaw/Initialize(mapload) . = ..() + AddComponent(/datum/component/cleave_attack) AddComponent(/datum/component/two_handed, require_twohands = TRUE) AddComponent(/datum/component/butchering, 30, 100, 0, 'sound/weapons/chainsawhit.ogg', TRUE) diff --git a/code/game/objects/items/two_handed/dualsaber.dm b/code/game/objects/items/two_handed/dualsaber.dm index e369a884d41c..a91dedaf6052 100644 --- a/code/game/objects/items/two_handed/dualsaber.dm +++ b/code/game/objects/items/two_handed/dualsaber.dm @@ -57,6 +57,7 @@ wield_callback = CALLBACK(src, PROC_REF(on_wield)), \ unwield_callback = CALLBACK(src, PROC_REF(on_unwield)), \ ) + AddComponent(/datum/component/cleave_attack, arc_size=360, swing_speed_mod=1.5, requires_wielded=TRUE) // lol, lmao even /obj/item/melee/dualsaber/Destroy() STOP_PROCESSING(SSobj, src) diff --git a/code/game/objects/items/two_handed/fireaxe.dm b/code/game/objects/items/two_handed/fireaxe.dm index 16b1bc27484d..0d726252caac 100644 --- a/code/game/objects/items/two_handed/fireaxe.dm +++ b/code/game/objects/items/two_handed/fireaxe.dm @@ -29,6 +29,7 @@ force_wielded = force_wielded, \ icon_wielded = "[base_icon_state]1", \ ) + AddComponent(/datum/component/cleave_attack, arc_size=180, requires_wielded=TRUE) // YEAHHHHH AddComponent(/datum/component/butchering, 100, 80, 0 , hitsound) //axes are not known for being precision butchering tools /obj/item/fireaxe/update_icon_state() @@ -45,7 +46,7 @@ return if(QDELETED(A)) return - if(HAS_TRAIT(src, TRAIT_WIELDED)) //destroys shit faster, generally in 1-2 hits. + if(HAS_TRAIT(src, TRAIT_WIELDED) && !HAS_TRAIT(src, TRAIT_CLEAVING)) //destroys shit faster, generally in 1-2 hits. if(istype(A, /obj/structure/window)) var/obj/structure/window/W = A W.take_damage(W.max_integrity*2, BRUTE, MELEE, FALSE, null, armour_penetration) diff --git a/code/game/objects/items/two_handed/highfrequencyblade.dm b/code/game/objects/items/two_handed/highfrequencyblade.dm index 40cd8a0c1a18..c1341fcd7ae0 100644 --- a/code/game/objects/items/two_handed/highfrequencyblade.dm +++ b/code/game/objects/items/two_handed/highfrequencyblade.dm @@ -23,6 +23,7 @@ force_wielded = 20, \ icon_wielded = "[base_icon_state]1", \ ) + AddComponent(/datum/component/cleave_attack, requires_wielded=TRUE) AddComponent(/datum/component/butchering, 20, 105) /obj/item/vibro_weapon/update_icon_state() diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index cefc64e51f50..82681c1a609e 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -74,6 +74,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/claymore/Initialize(mapload) . = ..() + AddComponent(/datum/component/cleave_attack, arc_size=90) AddComponent(/datum/component/butchering, 40, 105) /obj/item/claymore/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) @@ -259,6 +260,10 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 100, ACID = 50) resistance_flags = FIRE_PROOF +/obj/item/katana/Initialize(mapload) + . = ..() + AddComponent(/datum/component/cleave_attack) + /obj/item/katana/basalt name = "basalt katana" desc = "A katana made of hardened basalt. Particularly damaging to lavaland fauna.
(Activate this item in hand to dodge roll in the direction you're facing)" @@ -584,6 +589,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/mounted_chainsaw/Initialize(mapload) . = ..() + AddComponent(/datum/component/cleave_attack) ADD_TRAIT(src, TRAIT_NODROP, HAND_REPLACEMENT_TRAIT) /obj/item/mounted_chainsaw/Destroy() diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index bca62264764c..17757fdf3a85 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -158,6 +158,9 @@ . = . || (mover.pass_flags & PASSGRILLE) /obj/structure/grille/attackby(obj/item/W, mob/user, params) + var/obj/structure/window/window = locate() in loc + if(window && window.fulltile && window.anchored) + return TRUE // don't attack grilles through windows, that's weird and causes too many problems user.changeNext_move(CLICK_CD_MELEE) add_fingerprint(user) if(W.tool_behaviour == TOOL_WIRECUTTER) diff --git a/code/modules/antagonists/blob/structures/_blob.dm b/code/modules/antagonists/blob/structures/_blob.dm index 1b8e79ee65fd..1a4a880c6d91 100644 --- a/code/modules/antagonists/blob/structures/_blob.dm +++ b/code/modules/antagonists/blob/structures/_blob.dm @@ -4,7 +4,7 @@ icon = 'icons/mob/blob.dmi' light_range = 2 desc = "A thick wall of writhing tendrils." - density = FALSE //this being false causes two bugs, being able to attack blob tiles behind other blobs and being unable to move on blob tiles in no gravity, but turning it to 1 causes the blob mobs to be unable to path through blobs, which is probably worse. + density = TRUE opacity = FALSE anchored = TRUE layer = BELOW_MOB_LAYER diff --git a/code/modules/antagonists/clockcult/clock_items/clock_weapons/battlehammer.dm b/code/modules/antagonists/clockcult/clock_items/clock_weapons/battlehammer.dm index 7c654e2a3fcf..b95d491e07da 100644 --- a/code/modules/antagonists/clockcult/clock_items/clock_weapons/battlehammer.dm +++ b/code/modules/antagonists/clockcult/clock_items/clock_weapons/battlehammer.dm @@ -12,6 +12,7 @@ /obj/item/clockwork/weapon/brass_battlehammer/Initialize(mapload) . = ..() AddComponent(/datum/component/two_handed, require_twohands = TRUE) + AddComponent(/datum/component/cleave_attack, arc_size=180, requires_wielded=TRUE, no_multi_hit=TRUE) // big hammer /obj/item/clockwork/weapon/brass_battlehammer/attack(mob/living/target, mob/living/carbon/human/user) . = ..() diff --git a/code/modules/antagonists/clockcult/clock_items/clock_weapons/longsword.dm b/code/modules/antagonists/clockcult/clock_items/clock_weapons/longsword.dm index 601c6219a627..0bd2c2bb853d 100644 --- a/code/modules/antagonists/clockcult/clock_items/clock_weapons/longsword.dm +++ b/code/modules/antagonists/clockcult/clock_items/clock_weapons/longsword.dm @@ -12,6 +12,10 @@ var/emp_cooldown = 0 var/cooldown_duration = 10 SECONDS +/obj/item/clockwork/weapon/brass_sword/Initialize(mapload, new_action) + . = ..() + AddComponent(/datum/component/cleave_attack) // slice and dice in the name of ratvar + /obj/item/clockwork/weapon/brass_sword/attack(mob/living/target, mob/living/carbon/human/user) . = ..() if(world.time > emp_cooldown && !is_servant_of_ratvar(target)) diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index 439ba4ffddfd..9cce3d6b51fd 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -288,6 +288,10 @@ COMSIG_ATOM_ENTERED = PROC_REF(on_entered), ) AddElement(/datum/element/connect_loc, loc_connections) + RegisterSignal(src, COMSIG_ATOM_CLEAVE_ATTACK, PROC_REF(on_cleave_attack)) + +/obj/structure/spacevine/proc/on_cleave_attack() + return ATOM_ALLOW_CLEAVE_ATTACK // vines don't have density but should still be cleavable /obj/structure/spacevine/examine(mob/user) . = ..() diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm index 97b78a364f38..c8eab1654462 100644 --- a/code/modules/hydroponics/hydroitemdefines.dm +++ b/code/modules/hydroponics/hydroitemdefines.dm @@ -142,6 +142,7 @@ /obj/item/scythe/Initialize(mapload) . = ..() + AddComponent(/datum/component/cleave_attack, arc_size=180) AddComponent(/datum/component/butchering, 90, 105) /obj/item/scythe/suicide_act(mob/user) @@ -154,20 +155,6 @@ playsound(src,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1) return (BRUTELOSS) -/obj/item/scythe/pre_attack(atom/A, mob/living/user, params) - if(swiping || !istype(A, /obj/structure/spacevine) || get_turf(A) == get_turf(user)) - return ..() - else - var/turf/user_turf = get_turf(user) - var/dir_to_target = get_dir(user_turf, get_turf(A)) - swiping = TRUE - var/static/list/scythe_slash_angles = list(0, 45, 90, -45, -90) - for(var/i in scythe_slash_angles) - var/turf/T = get_step(user_turf, turn(dir_to_target, i)) - for(var/obj/structure/spacevine/V in T) - if(user.Adjacent(V)) - melee_attack_chain(user, V) - swiping = FALSE // ************************************* // Nutrient defines for hydroponics diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index 7856adbc45d0..1826ff0baaeb 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -93,7 +93,7 @@ if(dodging && target && in_melee && isturf(loc) && isturf(target.loc)) var/datum/cb = CALLBACK(src, PROC_REF(sidestep)) if(sidestep_per_cycle > 1) //For more than one just spread them equally - this could changed to some sensible distribution later - var/sidestep_delay = SSnpcpool.wait / sidestep_per_cycle + var/sidestep_delay = round(SSnpcpool.wait / sidestep_per_cycle) for(var/i in 1 to sidestep_per_cycle) addtimer(cb, (i - 1)*sidestep_delay) else //Otherwise randomize it to make the players guessing. @@ -261,7 +261,7 @@ /mob/living/simple_animal/hostile/proc/MeleeAction(patience = TRUE) if(rapid_melee > 1) var/datum/callback/cb = CALLBACK(src, PROC_REF(CheckAndAttack)) - var/delay = SSnpcpool.wait / rapid_melee + var/delay = round(SSnpcpool.wait / rapid_melee) for(var/i in 1 to rapid_melee) addtimer(cb, (i - 1)*delay) else @@ -454,7 +454,7 @@ if(CanSmashTurfs(T)) T.attack_animal(src) for(var/obj/O in T) - if(O.density && environment_smash >= ENVIRONMENT_SMASH_STRUCTURES && !O.IsObscured()) + if(O.density && !O.CanAllowThrough(src) && environment_smash >= ENVIRONMENT_SMASH_STRUCTURES && !O.IsObscured()) O.attack_animal(src) return diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index 27c5a86f39bc..81ff251a72b6 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -412,7 +412,7 @@ Difficulty: Hard /obj/effect/decal/cleanable/blood/gibs/bubblegum/can_bloodcrawl_in() return TRUE -/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon) +/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect) if(!charging) ..() diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm index 985e0397a275..211f195938d6 100644 --- a/code/modules/surgery/tools.dm +++ b/code/modules/surgery/tools.dm @@ -241,6 +241,7 @@ /obj/item/circular_saw/Initialize(mapload) . = ..() + AddComponent(/datum/component/cleave_attack) AddComponent(/datum/component/butchering, 40 * toolspeed, 100, 5, 'sound/weapons/circsawhit.ogg') //saws are very accurate and fast at butchering /obj/item/circular_saw/attack(mob/living/M, mob/user) @@ -406,6 +407,9 @@ demolition_mod = 1.5 // lasers are good at cutting metal sharpness = SHARP_EDGED +/obj/item/scalpel/advanced/Initialize(mapload) + . = ..() + AddComponent(/datum/component/cleave_attack) // woe, angry medbay be upon ye /obj/item/scalpel/advanced/attack_self(mob/user) playsound(get_turf(user), 'sound/machines/click.ogg', 50, TRUE) diff --git a/icons/effects/96x96.dmi b/icons/effects/96x96.dmi index d7e81d7c86c2..8e4bd9e87d9a 100644 Binary files a/icons/effects/96x96.dmi and b/icons/effects/96x96.dmi differ diff --git a/yogstation.dme b/yogstation.dme index d0b5a90de459..732c65ffb3bc 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -579,6 +579,7 @@ #include "code\datums\components\butchering.dm" #include "code\datums\components\caltrop.dm" #include "code\datums\components\chasm.dm" +#include "code\datums\components\cleave_attack.dm" #include "code\datums\components\connect_containers.dm" #include "code\datums\components\connect_loc_behalf.dm" #include "code\datums\components\connect_mob_behalf.dm" diff --git a/yogstation/code/game/objects/items/wielded/big_spoon.dm b/yogstation/code/game/objects/items/wielded/big_spoon.dm index 107bce062e96..d6f0ad40c6ca 100644 --- a/yogstation/code/game/objects/items/wielded/big_spoon.dm +++ b/yogstation/code/game/objects/items/wielded/big_spoon.dm @@ -28,6 +28,7 @@ wield_callback = CALLBACK(src, PROC_REF(on_wield)), \ unwield_callback = CALLBACK(src, PROC_REF(on_unwield)), \ ) + AddComponent(/datum/component/cleave_attack, requires_wielded=TRUE, no_multi_hit=TRUE) /obj/item/bigspoon/proc/on_wield(atom/source, mob/living/user) hitsound = 'yogstation/sound/weapons/bat_hit.ogg' diff --git a/yogstation/code/game/objects/items/wielded/sledgehammer.dm b/yogstation/code/game/objects/items/wielded/sledgehammer.dm index 1822bd0a0198..750a91ccf359 100644 --- a/yogstation/code/game/objects/items/wielded/sledgehammer.dm +++ b/yogstation/code/game/objects/items/wielded/sledgehammer.dm @@ -33,6 +33,11 @@ require_twohands = TRUE, \ wielded_stats = list(SWING_SPEED = 1.5, ENCUMBRANCE = 0.5, ENCUMBRANCE_TIME = 1 SECONDS, REACH = 1, DAMAGE_LOW = 0, DAMAGE_HIGH = 0), \ ) + AddComponent(/datum/component/cleave_attack, \ + arc_size=180, \ + requires_wielded=TRUE, \ + no_multi_hit=TRUE, \ + ) // big and heavy hammer makes wide arc /obj/item/melee/sledgehammer/proc/on_wield(atom/source, mob/living/user) hitsound = "swing_hit" diff --git a/yogstation/code/game/objects/items/wielded/vxtvulhammer.dm b/yogstation/code/game/objects/items/wielded/vxtvulhammer.dm index 2aa35b57a80f..dcdf2f921549 100644 --- a/yogstation/code/game/objects/items/wielded/vxtvulhammer.dm +++ b/yogstation/code/game/objects/items/wielded/vxtvulhammer.dm @@ -46,6 +46,11 @@ force_wielded = force_wielded, \ unwield_callback = CALLBACK(src, PROC_REF(on_unwield)), \ ) + AddComponent(/datum/component/cleave_attack, \ + arc_size=180, \ + requires_wielded=TRUE, \ + cleave_end_callback=CALLBACK(src, PROC_REF(end_swing)), \ + ) /obj/item/melee/vxtvulhammer/Destroy() //Even though the hammer won't probably be destroyed, Everâ„¢ QDEL_NULL(spark_system) @@ -70,6 +75,10 @@ user.visible_message(span_warning("[user] flicks the hammer off!")) charging = FALSE +/obj/item/melee/vxtvulhammer/proc/end_swing(obj/item/weapon, mob/user) + if(supercharged) + supercharge() + /obj/item/melee/vxtvulhammer/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) if(attack_type == PROJECTILE_ATTACK || !HAS_TRAIT(src, TRAIT_WIELDED)) //Doesn't work against ranged or if it's not wielded final_block_chance = 0 //Please show me how you can block a bullet with an industrial hammer I would LOVE to see it @@ -143,7 +152,8 @@ K.color = color playsound(loc, 'sound/effects/powerhammerhit.ogg', 80, FALSE) //Mainly this sound playsound(loc, 'sound/effects/explosion3.ogg', 20, TRUE) //Bit of a reverb - supercharge() //At start so it doesn't give an unintentional message if you hit yourself + if(!HAS_TRAIT(src, TRAIT_CLEAVING)) // wait for the swing to end + supercharge() //At start so it doesn't give an unintentional message if you hit yourself if(ismecha(target) && !toy) user.visible_message(span_danger("The hammer thunders against [target], caving in part of its outer plating!"))