diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index b7466e7b4ff..24f1f4616cd 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -488,7 +488,7 @@ msg_stage++ -/// Prevent clicks for the duration of the ability +/// Prevent clicks for the "duration" of the status /datum/status_effect/debuff/clickcd id = "clickcd" alert_type = /atom/movable/screen/alert/status_effect/debuff/clickcd @@ -538,7 +538,7 @@ /datum/status_effect/debuff/feinted id = "feinted" alert_type = /atom/movable/screen/alert/status_effect/debuff/feinted - duration = 30 SECONDS + duration = 20 SECONDS /atom/movable/screen/alert/status_effect/debuff/feinted name = "Feinted" diff --git a/code/game/objects/items/weapons/melee/axes.dm b/code/game/objects/items/weapons/melee/axes.dm index 943a4cb0810..26127159c35 100644 --- a/code/game/objects/items/weapons/melee/axes.dm +++ b/code/game/objects/items/weapons/melee/axes.dm @@ -23,6 +23,7 @@ grid_height = 64 grid_width = 32 + weapon_special = /datum/special_intent/axe_swing //................ Stone Axe ............... // /obj/item/weapon/axe/stone @@ -393,6 +394,8 @@ grid_height = 96 grid_width = 64 + weapon_special = /datum/special_intent/axe_swing + /obj/item/weapon/greataxe/getonmobprop(tag) . = ..() if(tag) diff --git a/code/game/objects/items/weapons/melee/blunt.dm b/code/game/objects/items/weapons/melee/blunt.dm index fbca4626815..db13f995fb7 100644 --- a/code/game/objects/items/weapons/melee/blunt.dm +++ b/code/game/objects/items/weapons/melee/blunt.dm @@ -429,6 +429,8 @@ parrysound = "parrywood" sellprice = 35 + weapon_special = /datum/special_intent/ground_smash + /obj/item/weapon/mace/goden/getonmobprop(tag) . = ..() if(tag) diff --git a/code/game/objects/items/weapons/melee/flail.dm b/code/game/objects/items/weapons/melee/flail.dm index bfdac4901e2..f6af779eb35 100644 --- a/code/game/objects/items/weapons/melee/flail.dm +++ b/code/game/objects/items/weapons/melee/flail.dm @@ -29,6 +29,8 @@ grid_width = 32 grid_height = 96 + weapon_special = /datum/special_intent/flail_sweep + /obj/item/weapon/flail/getonmobprop(tag) . = ..() if(tag) diff --git a/code/game/objects/items/weapons/melee/knives.dm b/code/game/objects/items/weapons/melee/knives.dm index b1c45d262ed..57bbbd66ecd 100644 --- a/code/game/objects/items/weapons/melee/knives.dm +++ b/code/game/objects/items/weapons/melee/knives.dm @@ -215,6 +215,8 @@ possible_item_intents = list(DAGGER_CUT, DAGGER_THRUST) sellprice = 12 + weapon_special = /datum/special_intent/triple_stab + /obj/item/weapon/knife/dagger/jile name = "iron jile" desc = "A curved iron dagger from the fallen east." diff --git a/code/game/objects/items/weapons/melee/special.dm b/code/game/objects/items/weapons/melee/special.dm index 9b32290ff26..dfe9cb09582 100644 --- a/code/game/objects/items/weapons/melee/special.dm +++ b/code/game/objects/items/weapons/melee/special.dm @@ -338,6 +338,8 @@ grid_width = 64 grid_height = 32 + weapon_special = /datum/special_intent/upper_cut + /obj/item/weapon/knuckles/getonmobprop(tag) . = ..() if(tag) diff --git a/code/game/objects/items/weapons/melee/swords.dm b/code/game/objects/items/weapons/melee/swords.dm index cf44aeb39e6..a14e53b876b 100644 --- a/code/game/objects/items/weapons/melee/swords.dm +++ b/code/game/objects/items/weapons/melee/swords.dm @@ -121,6 +121,8 @@ icon_state = "sword1" sellprice = 30 + weapon_special = /datum/special_intent/shin_swipe + /obj/item/weapon/sword/arming/Initialize() . = ..() if(icon_state == "sword1") @@ -470,6 +472,8 @@ SET_BASE_PIXEL(-16, -16) dropshrink = 0.8 + weapon_special = /datum/special_intent/piercing_lunge + /obj/item/weapon/sword/rapier/getonmobprop(tag) . = ..() if(tag) @@ -674,6 +678,8 @@ grid_height = 96 grid_width = 64 + weapon_special = /datum/special_intent/side_sweep + /obj/item/weapon/sword/long/shotel name = "steel shotel" icon_state = "shotel_steel" @@ -989,6 +995,8 @@ slot_flags = ITEM_SLOT_BACK sellprice = 90 + weapon_special = /datum/special_intent/greatsword_swing + /obj/item/weapon/sword/long/greatsword/getonmobprop(tag) . = ..() if(tag) diff --git a/code/game/objects/items/weapons/melee/whips.dm b/code/game/objects/items/weapons/melee/whips.dm index 86e73c2c5bf..78353c0370a 100644 --- a/code/game/objects/items/weapons/melee/whips.dm +++ b/code/game/objects/items/weapons/melee/whips.dm @@ -26,6 +26,8 @@ grid_width = 32 grid_height = 64 + weapon_special = /datum/special_intent/whip_coil + /obj/item/weapon/whip/getonmobprop(tag) . = ..() if(tag) diff --git a/code/game/objects/items/weapons/rmb_intents.dm b/code/game/objects/items/weapons/rmb_intents.dm index 7191d46d6f6..4fab4efa5e3 100644 --- a/code/game/objects/items/weapons/rmb_intents.dm +++ b/code/game/objects/items/weapons/rmb_intents.dm @@ -109,7 +109,6 @@ var/theirskill = 0 var/skill_factor = 0 - if(attacker_item?.associated_skill) ourskill = user.get_skill_level(attacker_item.associated_skill) diff --git a/code/modules/combat/_special_intent.dm b/code/modules/combat/_special_intent.dm index 6f635623d7d..b1c1fa76ff1 100644 --- a/code/modules/combat/_special_intent.dm +++ b/code/modules/combat/_special_intent.dm @@ -7,6 +7,12 @@ var/desc = "bad coders" var/icon = 'icons/effects/effects.dmi' + /// The main place where we can draw out the pattern. Every tile entry is a list with two numbers. + /// The origin (0,0) is one step forward from the dir the owner is facing. + /// This can be modified, though it's best be done before [build_affected_turfs]. + /// A third list value can be specified for a custom delay. + var/list/tile_coordinates = null + /// State to show before the attack var/pre_icon_state = "blip" /// Sound to play before the attack @@ -29,11 +35,6 @@ /// Base cooldown time of the special, success OR failure var/cooldown = 30 SECONDS - /// The main place where we can draw out the pattern. Every tile entry is a list with two numbers. - /// The origin (0,0) is one step forward from the dir the owner is facing. - /// This can be modified, though it's best be done before [build_affected_turfs]. - var/list/tile_coordinates = null - /// Whether to have the howner pass through a doafter for the delay rather than it being on every turf. /// Default code here does not allow for dir switching during the do after. var/use_doafter = FALSE @@ -51,10 +52,17 @@ ///If the datum is using multi-timed turfs, only the FIRST one's adjacency is checked ONCE. var/respect_adjacency = TRUE + /// Check for presence on the starting tile instead of adjacency + var/check_starting_loc = TRUE + /// The only reference we will hold, and only if [respect_adjacency] is TRUE /// If the users loc isn't this when we end, they moved. var/turf/starting_loc = null + /// If true we do various things to prevent the user from moving or clicking in [start_attack] + /// At that point we know that there are target turfs + var/immobilize_user = FALSE + /datum/special_intent/Destroy(force, ...) starting_loc = null return ..() @@ -112,7 +120,7 @@ ///Main pipeline. Note that _delay() calls post_delay() after a timer. /datum/special_intent/proc/process_attack(mob/living/user, obj/item/parent, turf/target) - SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_OVERRIDE(TRUE) if(user.ckey) user.log_message(span_danger("Used the Special [name] on [key_name(target)]."), LOG_ATTACK) @@ -121,13 +129,15 @@ var/list/turf/affected = build_affected_turfs(user, target) - after_creation(user, parent, affected) + start_cooldown(user) + + if(!after_creation(user, parent, affected)) + return start_attack(user, parent, affected) - start_cooldown(user) - /// Do stuff before creating the grid +/// Change temporary values and coordinates here /datum/special_intent/proc/pre_creation(mob/living/user, obj/item/parent, turf/target) return @@ -136,9 +146,10 @@ var/turf/origin = start_override ? start_override : get_step(user, user.dir) var/list/turf/affected_turfs = list() - for(var/list/coords in tile_coordinates) - var/coord_x = coords[1] - var/coord_y = coords[2] + for(var/list/values in tile_coordinates) + var/coord_x = LAZYACCESS(values, 1) + var/coord_y = LAZYACCESS(values, 2) + var/delay = LAZYACCESS(values, 3) if(respect_dir) switch(user.dir) @@ -156,13 +167,16 @@ var/turf/affected = locate(origin.x + coord_x, origin.y + coord_y, origin.z) if(affected && !affected.is_blocked_turf(TRUE)) - affected_turfs += affected + // Map each turf to a list of delays on it + // This allows for multiple effects on the same turf without duplicate turf entries + LAZYADDASSOCLIST(affected_turfs, affected, delay) return affected_turfs /// Called after the affected turfs are processed. +/// Return FALSE to cancel the attack /datum/special_intent/proc/after_creation(mob/living/user, obj/item/parent, list/turfs) - return + return TRUE /// Start the attack, executing it after the delay /datum/special_intent/proc/start_attack(mob/living/user, obj/item/parent, list/turfs) @@ -182,11 +196,17 @@ /datum/special_intent/proc/pre_delay(mob/living/user, obj/item/parent, list/turfs) SHOULD_CALL_PARENT(TRUE) + if(immobilize_user) + user.Immobilize(attack_delay) + user.apply_status_effect(/datum/status_effect/debuff/clickcd, attack_delay) + for(var/turf/affected as anything in turfs) - var/obj/effect/temp_visual/duration_setting/effect = new(affected, attack_delay) - effect.icon = icon - effect.icon_state = pre_icon_state - effect.plane = GAME_PLANE_FOV_HIDDEN + for(var/delay in turfs[affected]) + var/duration = attack_delay + delay + var/obj/effect/temp_visual/duration_setting/effect = new(affected, duration) + effect.icon = icon + effect.icon_state = pre_icon_state + effect.plane = GAME_PLANE_FOV_HIDDEN if(pre_sound) playsound(user, pre_sound, 100, TRUE) @@ -195,7 +215,7 @@ /// It performs any needed adjacency checks and will try to draw the "post" visuals on any valid turfs. /// It calls apply_hit() after where most of the logic for any on-hit effects should go. /datum/special_intent/proc/post_delay(mob/living/user, obj/item/parent, list/turfs) - SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_OVERRIDE(TRUE) if(QDELETED(user) || QDELETED(parent)) return @@ -204,23 +224,41 @@ user.balloon_alert(user, "dropped weapon!") return - if(starting_loc && get_turf(user) != starting_loc) - starting_loc = null - user.balloon_alert(user, "moved!") - return + if(starting_loc) + if(check_starting_loc && get_turf(user) != starting_loc) + starting_loc = null + user.balloon_alert(user, "moved!") + return + else if(!user.TurfAdjacent(starting_loc)) + starting_loc = null + user.balloon_alert(user, "too far!") + return for(var/turf/affected as anything in turfs) - if(post_icon_state) - var/obj/effect/temp_visual/duration_setting/effect = new(affected, fade_delay) - effect.icon = icon - effect.icon_state = post_icon_state - effect.plane = GAME_PLANE_FOV_HIDDEN - - apply_hit(user, parent, affected) + for(var/delay in turfs[affected]) + if(isnum(delay)) + addtimer(CALLBACK(src, PROC_REF(post_delay_apply), user, parent, affected), delay) + else + post_delay_apply(user, parent, affected) if(post_sound) playsound(user, post_sound, 100, TRUE) +/// Apply individual tile effects +/datum/special_intent/proc/post_delay_apply(mob/living/user, obj/item/parent, turf/target) + SHOULD_CALL_PARENT(TRUE) + + if(QDELETED(parent) || QDELETED(user)) + return // Incredibly unlikely an open turf will change + + if(post_icon_state) + var/obj/effect/temp_visual/duration_setting/effect = new(target, fade_delay) + effect.icon = icon + effect.icon_state = post_icon_state + effect.plane = GAME_PLANE_FOV_HIDDEN + + apply_hit(user, parent, target) + /// Main proc where stuff should happen. This is called immediately after the post_delay of the intent. /datum/special_intent/proc/apply_hit(mob/living/user, obj/item/parent, turf/target) SHOULD_CALL_PARENT(FALSE) diff --git a/code/modules/combat/special_intents.dm b/code/modules/combat/special_intents.dm index c42084a0ae5..ad149f82815 100644 --- a/code/modules/combat/special_intents.dm +++ b/code/modules/combat/special_intents.dm @@ -1,9 +1,19 @@ +// Defines for common attack types +/// Single turf only +#define SPECIAL_ATTACK_SINGLE (list(list(0, 0))) +/// First three turfs directly in front +#define SPECIAL_ATTACK_SWIPE (list(list(0, 0), list(1, 0), list(-1, 0))) +/// Two turfs in front +#define SPECIAL_ATTACK_POKE (list(list(0, 0), list(0, 1))) + /datum/special_intent/side_sweep name = "Distracting Swipe" desc = "Swing tall at your primary flank in a distracting fashion. \ Anyone caught in it will be exposed for a short while. \ However, those on the floor will not be struck." + tile_coordinates = list(list(0, 0), list(1, 0), list(1, -1)) //L shape that hugs our -right- flank. + pre_icon_state = "trap" post_icon_state = "sweep_fx" post_sound = 'sound/combat/sidesweep_hit.ogg' @@ -13,8 +23,6 @@ attack_delay = 0.6 SECONDS cooldown = 17 SECONDS - tile_coordinates = list(list(0,0), list(1,0), list(1,-1)) //L shape that hugs our -right- flank. - /// Damage we deal var/damage = 20 /// Users intent when we started the attack, instead of checking it when it happens @@ -22,9 +30,9 @@ /datum/special_intent/side_sweep/pre_creation(mob/living/user, obj/item/parent, turf/target) if(user.used_hand == 1) //We invert it if it's the left arm. - tile_coordinates = list(list(0,0), list(-1,0), list(-1,-1)) + tile_coordinates = list(list(0, 0), list(-1, 0), list(-1, -1)) else - tile_coordinates = list(list(0,0), list(1,0), list(1,-1)) + tile_coordinates = list(list(0, 0), list(1, 0), list(1, -1)) var/statmod = max(user.STASTR, user.STASPD, user.STAPER) //It's a versatile weapon, so the scaling is versatile, too if(parent) @@ -48,7 +56,7 @@ if(victim.body_position == LYING_DOWN) continue victim.apply_status_effect(/datum/status_effect/debuff/exposed, 4 SECONDS) - apply_generic_weapon_damage(user, parent, victim, damage, SLASH, target_zone, damage_class = BCLASS_CUT) + apply_generic_weapon_damage(user, parent, victim, damage, SLASH, target_zone, BCLASS_CUT) /datum/special_intent/shin_swipe name = "Shin Prod" @@ -56,6 +64,8 @@ Slows down the opponent if hit. \ However, those on the floor will not be struck" + tile_coordinates = SPECIAL_ATTACK_SWIPE + pre_icon_state = "trap" post_icon_state = "sweep_fx" post_sound = 'sound/combat/shin_swipe.ogg' @@ -65,8 +75,6 @@ attack_delay = 0.5 SECONDS cooldown = 20 SECONDS - tile_coordinates = list(list(0,0), list(1,0), list(-1,0)) - var/damage /datum/special_intent/shin_swipe/pre_creation(mob/living/user, obj/item/parent, turf/target) @@ -80,5 +88,347 @@ continue victim.Slowdown(5) victim.apply_status_effect(/datum/status_effect/debuff/hobbled) - apply_generic_weapon_damage(user, parent, victim, damage, STAB, pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG), damage_class = BCLASS_CUT) + apply_generic_weapon_damage(user, parent, victim, damage, STAB, pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG), BCLASS_CUT) + +/datum/special_intent/piercing_lunge + name = "Piercing Lunge" + desc = "A planned attack at the chest, extending ourselves. \ + Pierces our enemy's armor and knocks the wind from them. \ + However, those on the floor will not be struck" + + tile_coordinates = SPECIAL_ATTACK_POKE + + pre_icon_state = "trap" + post_icon_state = "stab" + post_sound = 'sound/combat/parry/bladed/bladedsmall (3).ogg' + + stamina_cost = 20 + + attack_delay = 0.5 SECONDS + cooldown = 20 SECONDS + + var/damage + +/datum/special_intent/piercing_lunge/pre_creation(mob/living/user, obj/item/parent, turf/target) + damage = parent.force * max((1 + (((user.STASPD - 10) + (user.STAPER - 10)) / 10)), 0.1) + +/datum/special_intent/piercing_lunge/apply_hit(mob/living/user, obj/item/parent, turf/target) + for(var/mob/living/victim in target) + if(victim == user) + continue + if(victim.body_position == LYING_DOWN) + continue + victim.adjust_stamina(30) + apply_generic_weapon_damage(user, parent, victim, damage, STAB, BODY_ZONE_CHEST, BCLASS_STAB) + +/datum/special_intent/ground_smash + name = "Ground Smash" + desc = "Swings downward, leaving a traveling quake for a few tiles. \ + Anyone struck by it will be slowed and offbalanced, or knocked down if they're already off-balanced." + + tile_coordinates = list(list(0, 0), list(0, 1, 0.1 SECONDS), list(0, 2, 0.2 SECONDS)) + + pre_icon_state = "trap" + pre_sound = 'sound/combat/ground_smash_start.ogg' + post_icon_state = "kick_fx" + post_sound = list('sound/combat/ground_smash1.ogg', 'sound/combat/ground_smash2.ogg', 'sound/combat/ground_smash3.ogg') + + stamina_cost = 30 + + attack_delay = 0.7 SECONDS + cooldown = 25 SECONDS + + immobilize_user = TRUE + +/datum/special_intent/ground_smash/apply_hit(mob/living/user, obj/item/parent, turf/target) + for(var/mob/living/victim in target) + if(victim == user) + continue + + var/victim_dir = get_dir(victim, user) + var/throw_dir = turn(victim_dir, pick(90, 270)) + var/turf/turf_target = get_ranged_target_turf(victim, throw_dir, rand(1, 3)) + victim.safe_throw_at(turf_target, rand(1, 3), 1, user, force = MOVE_FORCE_STRONG) + + victim.Slowdown(5) + victim.apply_status_effect(/datum/status_effect/debuff/exposed, 2.5 SECONDS) + + if(victim.IsOffBalanced()) + victim.Knockdown(2 SECONDS) + else + victim.OffBalance(5 SECONDS) + + apply_generic_weapon_damage(user, parent, victim, parent.force * 2, BLUNT, BODY_ZONE_CHEST, BCLASS_BLUNT) + +/datum/special_intent/flail_sweep + name = "Flail Sweep" + desc = "Swings in a perfect circle all around you, pushing people aside. \ + However, those on the floor will not be struck" + + pre_icon_state = "trap" + post_icon_state = "sweep_fx" + post_sound = 'sound/combat/flail_sweep.ogg' + + stamina_cost = 30 + + attack_delay = 0.7 SECONDS + cooldown = 25 SECONDS + + immobilize_user = TRUE + +/datum/special_intent/flail_sweep/build_affected_turfs(mob/living/user, turf/start_override) + var/list/affected = list() + + for(var/turf/adjacent in orange(1, user)) + if(adjacent.is_blocked_turf(TRUE)) + continue + LAZYADDASSOCLIST(affected, adjacent, 0) + + return affected + +/datum/special_intent/flail_sweep/apply_hit(mob/living/user, obj/item/parent, turf/target) + for(var/mob/living/victim in target) + if(victim == user) + continue + if(victim.body_position == LYING_DOWN) + continue + var/turf/throw_target = get_edge_target_turf(user, get_dir(user, get_step_away(victim, user))) + victim.safe_throw_at(throw_target, rand(1, 2), 1, user, force = MOVE_FORCE_STRONG) + victim.Immobilize(1 SECONDS) + victim.apply_status_effect(/datum/status_effect/debuff/exposed, 3 SECONDS) + apply_generic_weapon_damage(user, parent, victim, parent.force * 1.5, STAB, BODY_ZONE_CHEST, BCLASS_STAB) + +#define GREAT_OUTER_DELAY 0.7 SECONDS + +/datum/special_intent/greatsword_swing + name = "Great Swing" + desc = "Swing your greatsword all around you in a ring of Judgement." + + tile_coordinates = list( + list(0, 0), + list(1, 0), + list(1, -1), + list(1, -2), + list(0, -2), + list(-1, -2), + list(-1, -1), + list(-1, 0), + list(0, 1, GREAT_OUTER_DELAY), + list(1, 1, GREAT_OUTER_DELAY), + list(-1, 1, GREAT_OUTER_DELAY), + list(1, -3, GREAT_OUTER_DELAY), + list(0, -3, GREAT_OUTER_DELAY), + list(-1, -3, GREAT_OUTER_DELAY), + list(-2, 0, GREAT_OUTER_DELAY), + list(-2, -1, GREAT_OUTER_DELAY), + list(-2, -2, GREAT_OUTER_DELAY), + list(2, 0, GREAT_OUTER_DELAY), + list(2, -1, GREAT_OUTER_DELAY), + list(2, -2, GREAT_OUTER_DELAY), + ) + + pre_icon_state = "fx_trap_long" + pre_sound = 'sound/combat/rend_hit.ogg' + post_icon_state = "sweep_fx" + post_sound = 'sound/combat/wooshes/bladed/wooshlarge (3).ogg' + + stamina_cost = 35 + + attack_delay = 0.7 SECONDS + cooldown = 30 SECONDS + + immobilize_user = TRUE + +/datum/special_intent/greatsword_swing/apply_hit(mob/living/user, obj/item/parent, turf/target) + for(var/mob/living/victim in target) + if(victim == user) + continue + if(victim.body_position == LYING_DOWN) + continue + apply_generic_weapon_damage(user, parent, victim, parent.force * 1.5, SLASH, BODY_ZONE_CHEST, BCLASS_CUT) + +#undef GREAT_OUTER_DELAY + +#define AXE_SWING_GRID_DEFAULT list(list(-1,0), list(0,0, 0.2 SECONDS), list(1,0, 0.4 SECONDS)) +#define AXE_SWING_GRID_MIRROR list(list(-1,0, 0.4 SECONDS), list(0,0, 0.2 SECONDS), list(1,0)) + +/datum/special_intent/axe_swing + name = "Hefty Swing" + desc = "Swings from left to right. Anyone caught in the swing get immobilized and exposed." + + tile_coordinates = AXE_SWING_GRID_DEFAULT + + pre_icon_state = "trap" + pre_sound = 'sound/combat/rend_start.ogg' + post_icon_state = "sweep_fx" + post_sound = list('sound/combat/sp_axe_swing1.ogg', 'sound/combat/sp_axe_swing2.ogg', 'sound/combat/sp_axe_swing3.ogg') + + stamina_cost = 20 + + attack_delay = 0.5 SECONDS + cooldown = 25 SECONDS + + immobilize_user = TRUE + + var/damage = 20 + +/datum/special_intent/axe_swing/pre_creation(mob/living/user, obj/item/parent, turf/target) + damage = (parent.force * (user.STASTR / 10)) + 10 + + if(user.used_hand == 1) //We mirror it if it's the left arm. + tile_coordinates += AXE_SWING_GRID_MIRROR + else + tile_coordinates += AXE_SWING_GRID_DEFAULT //Initial() doesn't work with lists so we copy paste the original + +/datum/special_intent/axe_swing/apply_hit(mob/living/user, obj/item/parent, turf/target) + for(var/mob/living/victim in target) + if(victim == user) + continue + if(victim.body_position == LYING_DOWN) + continue + victim.apply_status_effect(/datum/status_effect/debuff/exposed, 6 SECONDS) + victim.Immobilize(2.5 SECONDS) + apply_generic_weapon_damage(user, parent, victim, damage, SLASH, pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG), BCLASS_CHOP) + +#undef AXE_SWING_GRID_DEFAULT +#undef AXE_SWING_GRID_MIRROR + +/datum/special_intent/backstep + name = "Backstep" + desc = "A defensive attack used to quickly gain distance, shoving back any pursuer backwards, slowing and exposing them." + + tile_coordinates = SPECIAL_ATTACK_SWIPE + + pre_icon_state = "fx_trap_long" + pre_sound = 'sound/combat/polearm_woosh.ogg' + post_icon_state = "sweep_fx" + post_sound = 'sound/combat/rend_hit.ogg' + + stamina_cost = 20 + + attack_delay = 0.5 SECONDS + cooldown = 15 SECONDS + + check_starting_loc = FALSE + + var/push_dir + +/datum/special_intent/backstep/after_creation(mob/living/user, obj/item/parent, list/turfs) + . = ..() + + var/turf/throw_target = get_edge_target_turf(user, get_dir(user, get_step_away(user, get_step(get_turf(user), user.dir)))) + push_dir = user.dir + user.safe_throw_at(throw_target, 1, 1, user, force = MOVE_FORCE_EXTREMELY_STRONG) + + if(user.z != starting_loc.z) + return FALSE // we got thrown off a cliff or something + + if(user.body_position != STANDING_UP) + return FALSE // we got thrown at a wall and fell over :( + +/datum/special_intent/backstep/apply_hit(mob/living/user, obj/item/parent, turf/target) + for(var/mob/living/victim in target) + if(victim == user) + continue + if(victim.body_position == LYING_DOWN) + continue + + victim.Slowdown(5) + + var/turf/throw_target = get_edge_target_turf(user, push_dir) + victim.safe_throw_at(throw_target, 1, 1, user, force = MOVE_FORCE_EXTREMELY_STRONG) + victim.apply_status_effect(/datum/status_effect/debuff/exposed, 4.5 SECONDS) + + apply_generic_weapon_damage(user, parent, victim, parent.force, BLUNT, BODY_ZONE_CHEST, BCLASS_BLUNT) + +/datum/special_intent/upper_cut + name = "Upper Cut" + desc = "Charge up a devastating strike infront of you, if the target is Exposed they will fall over and be flung back with tremendous damage, if not exposed they will be pushed slightly back.." + + tile_coordinates = SPECIAL_ATTACK_SINGLE + + pre_icon_state = "trap" + pre_sound = 'sound/combat/ground_smash_start.ogg' + post_icon_state = "kick_fx" + post_sound = 'sound/combat/hits/punch/punch_hard (2).ogg' + + stamina_cost = 20 + + attack_delay = 0.5 SECONDS + cooldown = 15 SECONDS + + var/damage = 30 + +/datum/special_intent/upper_cut/apply_hit(mob/living/user, obj/item/parent, turf/target) + var/mob/living/victim = locate() in target // Only one + if(!victim || victim == user) + return + + var/turf/throw_target = get_edge_target_turf(user, get_dir(user, get_step_away(victim, user))) + var/throw_dist = 1 + var/target_zone = BODY_ZONE_CHEST + + if(victim.has_status_effect(/datum/status_effect/debuff/exposed)) + victim.Knockdown(1 SECONDS) + throw_dist = rand(2, 4) + damage = 120 + target_zone = BODY_ZONE_HEAD + + victim.safe_throw_at(throw_target, throw_dist, 1, user, force = MOVE_FORCE_EXTREMELY_STRONG) + apply_generic_weapon_damage(user, parent, target, damage, BLUNT, target_zone, BCLASS_BLUNT) + +/datum/special_intent/whip_coil + name = "Whip Coil" + desc = "A long-range lash that coils around the ankles of the target, trying pulling them off their feet." + + tile_coordinates = SPECIAL_ATTACK_SINGLE + + pre_icon_state = "trap" + pre_sound = 'sound/combat/sp_whip_start.ogg' + post_icon_state = "strike" + + stamina_cost = 20 + + attack_delay = 0.4 SECONDS + cooldown = 15 SECONDS + + use_clickloc = TRUE + range = 3 + +/datum/special_intent/whip_coil/apply_hit(mob/living/user, obj/item/parent, turf/target) + var/mob/living/victim = locate() in target // Only one + if(!victim || victim == user) + playsound(target, 'sound/combat/sp_whip_whiff.ogg', 100, TRUE) + return + + if(victim.body_position == STANDING_UP && victim.IsOffBalanced()) + victim.Knockdown(1 SECONDS) + else + victim.Immobilize(3 SECONDS) + + playsound(target, 'sound/combat/sp_whip_hit.ogg', 100, TRUE) + +/datum/special_intent/triple_stab + name = "Triple Stab" + desc = "Stab for the chest three times in quick succession." + + tile_coordinates = list(list(0, 0), list(0, 0, 0.3 SECONDS), list(0, 0, 0.6 SECONDS)) + + pre_icon_state = "trap" + post_icon_state = "stab" + + stamina_cost = 30 + + attack_delay = 0.4 SECONDS + cooldown = 20 SECONDS + +/datum/special_intent/triple_stab/apply_hit(mob/living/user, obj/item/parent, turf/target) + var/mob/living/victim = locate() in target // Only one + if(!victim || victim == user) + return + + apply_generic_weapon_damage(user, parent, victim, parent.force * 0.6, STAB, BODY_ZONE_CHEST, BCLASS_STAB) +#undef SPECIAL_ATTACK_SINGLE +#undef SPECIAL_ATTACK_SWIPE +#undef SPECIAL_ATTACK_POKE diff --git a/sound/combat/flail_sweep.ogg b/sound/combat/flail_sweep.ogg new file mode 100644 index 00000000000..b40d0ada579 Binary files /dev/null and b/sound/combat/flail_sweep.ogg differ diff --git a/sound/combat/ground_smash1.ogg b/sound/combat/ground_smash1.ogg new file mode 100644 index 00000000000..416db9b8d50 Binary files /dev/null and b/sound/combat/ground_smash1.ogg differ diff --git a/sound/combat/ground_smash2.ogg b/sound/combat/ground_smash2.ogg new file mode 100644 index 00000000000..49a0701adab Binary files /dev/null and b/sound/combat/ground_smash2.ogg differ diff --git a/sound/combat/ground_smash3.ogg b/sound/combat/ground_smash3.ogg new file mode 100644 index 00000000000..d531fe7e587 Binary files /dev/null and b/sound/combat/ground_smash3.ogg differ diff --git a/sound/combat/ground_smash_start.ogg b/sound/combat/ground_smash_start.ogg new file mode 100644 index 00000000000..cf0d6b7eb50 Binary files /dev/null and b/sound/combat/ground_smash_start.ogg differ diff --git a/sound/combat/polearm_woosh.ogg b/sound/combat/polearm_woosh.ogg new file mode 100644 index 00000000000..b886d12c1cb Binary files /dev/null and b/sound/combat/polearm_woosh.ogg differ diff --git a/sound/combat/rend_hit.ogg b/sound/combat/rend_hit.ogg new file mode 100644 index 00000000000..f60b03ad868 Binary files /dev/null and b/sound/combat/rend_hit.ogg differ diff --git a/sound/combat/rend_start.ogg b/sound/combat/rend_start.ogg new file mode 100644 index 00000000000..ccdb1305d33 Binary files /dev/null and b/sound/combat/rend_start.ogg differ diff --git a/sound/combat/sp_axe_swing1.ogg b/sound/combat/sp_axe_swing1.ogg new file mode 100644 index 00000000000..96437b938cd Binary files /dev/null and b/sound/combat/sp_axe_swing1.ogg differ diff --git a/sound/combat/sp_axe_swing2.ogg b/sound/combat/sp_axe_swing2.ogg new file mode 100644 index 00000000000..648ad9fc8f8 Binary files /dev/null and b/sound/combat/sp_axe_swing2.ogg differ diff --git a/sound/combat/sp_axe_swing3.ogg b/sound/combat/sp_axe_swing3.ogg new file mode 100644 index 00000000000..d5f9dbe8748 Binary files /dev/null and b/sound/combat/sp_axe_swing3.ogg differ diff --git a/sound/combat/sp_gsword_hit.ogg b/sound/combat/sp_gsword_hit.ogg new file mode 100644 index 00000000000..7f33ef99454 Binary files /dev/null and b/sound/combat/sp_gsword_hit.ogg differ diff --git a/sound/combat/sp_whip_hit.ogg b/sound/combat/sp_whip_hit.ogg new file mode 100644 index 00000000000..1e947088d7a Binary files /dev/null and b/sound/combat/sp_whip_hit.ogg differ diff --git a/sound/combat/sp_whip_start.ogg b/sound/combat/sp_whip_start.ogg new file mode 100644 index 00000000000..ec55b69ad07 Binary files /dev/null and b/sound/combat/sp_whip_start.ogg differ diff --git a/sound/combat/sp_whip_whiff.ogg b/sound/combat/sp_whip_whiff.ogg new file mode 100644 index 00000000000..2ee2cdb9093 Binary files /dev/null and b/sound/combat/sp_whip_whiff.ogg differ