diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm index b1f7eebab1fb..ebc29681cd9e 100644 --- a/code/datums/components/butchering.dm +++ b/code/datums/components/butchering.dm @@ -54,7 +54,7 @@ if (!source.get_sharpness() && !can_be_blunt) return - if (!user.combat_mode) + if (user.combat_mode) //DARKPACK EDIT CHANGE- quality of life for melee weapons, doesnt make sense you couldnt do combos in combat mode return // Can we butcher it? @@ -334,6 +334,7 @@ var/static/list/butcher_spots = typecacheof(list( /obj/structure/table, /obj/structure/bed, + /obj/structure/chair, //DARKPACK EDIT BUTCHERING: Chairs added because of prevelence and the general non-supporting of construction mechanics /obj/machinery/stasis, /obj/structure/kitchenspike, )) @@ -345,7 +346,7 @@ break if (!found_spot) - to_chat(user, span_warning("You need a better spot to butcher [victim]!")) + to_chat(user, span_warning("You need to butcher [victim] on a table/chair!")) //DARKPACK EDIT BUTCHERING ORIGINAL: to_chat(user, span_warning("You need a better spot to butcher [victim]!")) return var/obj/item/bodypart/limb = victim.get_bodypart(deprecise_zone(user.zone_selected)) diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index 73a526361f0d..1eed059d86a0 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -96,7 +96,7 @@ show_eyeless = FALSE /// Can this head be dismembered normally? - can_dismember = FALSE + can_dismember = TRUE //DARKPACK EDIT- For QOL it's much easier to leave it enabled by default, undismemberable heads are silly TGcode stuff /obj/item/bodypart/head/Initialize(mapload) . = ..() @@ -164,7 +164,7 @@ if (!can_dismember) return FALSE - if(!HAS_TRAIT(owner, TRAIT_CURSED) && owner.stat < HARD_CRIT) + if (owner.stat < HARD_CRIT) return FALSE return ..() diff --git a/modular_darkpack/modules/martial/artdefines.dm b/modular_darkpack/modules/martial/artdefines.dm new file mode 100644 index 000000000000..38e40c14dac8 --- /dev/null +++ b/modular_darkpack/modules/martial/artdefines.dm @@ -0,0 +1,3 @@ +#define MARTIALART_DARKPACK_KUNGFU "kungfu" +#define MARTIALART_DARKPACK_CQB "cqb" +#define MARTIALART_DARKPACK_BOXING "streetboxing" diff --git a/modular_darkpack/modules/martial/cqb.dm b/modular_darkpack/modules/martial/cqb.dm new file mode 100644 index 000000000000..890fceb39954 --- /dev/null +++ b/modular_darkpack/modules/martial/cqb.dm @@ -0,0 +1,366 @@ +#define SLAM_COMBO "GH" +#define KICK_COMBO "HH" +#define RESTRAIN_COMBO "GG" +#define PRESSURE_COMBO "DG" + +/datum/martial_art/darkpack_cqb + name = "CQC" + id = MARTIALART_DARKPACK_CQB + help_verb = /mob/living/proc/CQC_help + smashes_tables = TRUE + display_combos = TRUE + /// Weakref to a mob we're currently restraining (with grab-grab combo) + VAR_PRIVATE/datum/weakref/restraining_mob + +/datum/martial_art/darkpack_cqb/activate_style(mob/living/new_holder) + . = ..() + if (iscarbon(new_holder)) + var/list/obj/item/bodypart/affected_bodyparts + var/mob/living/carbon/human/carbon_owner = new_holder + for (var/obj/item/bodypart/limb as anything in carbon_owner.bodyparts) + if (!istype(limb, /obj/item/bodypart/arm) && !istype(limb, /obj/item/bodypart/leg)) + continue + + LAZYADD(affected_bodyparts, limb) + + //limb.unarmed_damage_low += 5 Unsure on this one + //limb.unarmed_damage_high += 5 + limb.unarmed_attack_effect = null + limb.unarmed_attack_sound = 'sound/items/weapons/cqchit1.ogg' + +/datum/martial_art/darkpack_cqb/deactivate_style(mob/living/remove_from) + return ..() + + +/datum/martial_art/darkpack_cqb/reset_streak(mob/living/new_target) + if(!IS_WEAKREF_OF(new_target, restraining_mob)) + restraining_mob = null + return ..() + +/datum/martial_art/darkpack_cqb/proc/check_streak(mob/living/attacker, mob/living/defender) + if(findtext(streak, SLAM_COMBO)) + reset_streak() + return Slam(attacker, defender) + if(findtext(streak, KICK_COMBO)) + reset_streak() + return Kick(attacker, defender) + if(findtext(streak, RESTRAIN_COMBO)) + reset_streak() + return Restrain(attacker, defender) + if(findtext(streak, PRESSURE_COMBO)) + reset_streak() + return Pressure(attacker, defender) + return FALSE + +/datum/martial_art/darkpack_cqb/proc/Slam(mob/living/attacker, mob/living/defender) + if(defender.body_position != STANDING_UP) + return FALSE + + attacker.do_attack_animation(defender) //Potentially change the flow of this combo to describe a failed slam if the Stam vs Str difference is high for optimal brick wall gameplay. Potentially 6 Str?? + defender.visible_message( + span_danger("[attacker] slams [defender] into the ground!"), + span_userdanger("You're slammed into the ground by [attacker]!"), + span_hear("You hear a sickening sound of flesh hitting flesh!"), + null, + attacker, + ) + to_chat(attacker, span_danger("You slam [defender] into the ground!")) + playsound(attacker, 'sound/items/weapons/slam.ogg', 50, TRUE, -1) + defender.apply_damage(10, BRUTE) + defender.Knockdown(3 SECONDS) + var/stun_time = SSroll.storyteller_roll((attacker.st_get_stat(STAT_STRENGTH) + attacker.st_get_stat(STAT_BRAWL)), difficulty = 6, numerical = TRUE, roller = attacker) //In THEORY this should be attacker weighted, with high stamima users shrugging it off. + var/defended_time = SSroll.storyteller_roll(defender.st_get_stat(STAT_STAMINA) * 2, difficulty = (attacker.st_get_stat(STAT_STRENGTH) + attacker.st_get_stat(STAT_BRAWL)), numerical = TRUE, roller = defender) + stun_time = clamp((stun_time-defended_time), 0, 10) + defender.Paralyze(stun_time SECONDS) + log_combat(attacker, defender, "slammed (CQB)") + return TRUE + +/datum/martial_art/darkpack_cqb/proc/Kick(mob/living/attacker, mob/living/defender) + if(defender.stat != CONSCIOUS) + return FALSE + + attacker.do_attack_animation(defender) + if(defender.body_position == LYING_DOWN && !defender.IsUnconscious() && !iskindred(defender) && defender.get_stamina_loss() >= 0) //This is EXTREMELY conditional to make sure it's not abused to shit + log_combat(attacker, defender, "knocked out (Head kick)(CQC)") + defender.visible_message( + span_danger("[attacker] kicks [defender]'s head, knocking [defender.p_them()] out!"), + span_userdanger("You're knocked unconscious by [attacker]!"), + span_hear("You hear a sickening sound of flesh hitting flesh!"), + null, + attacker, + ) + to_chat(attacker, span_danger("You kick [defender]'s head, knocking [defender.p_them()] out!")) + playsound(attacker, 'sound/items/weapons/genhit1.ogg', 50, TRUE, -1) + + var/helmet_protection = (defender.run_armor_check(BODY_ZONE_HEAD, MELEE) + (defender.st_get_stat(STAT_STAMINA) * 5)) + defender.apply_effect(20 SECONDS, EFFECT_KNOCKDOWN, helmet_protection) + defender.apply_effect(10 SECONDS, EFFECT_UNCONSCIOUS, helmet_protection) + //defender.adjust_organ_loss(ORGAN_SLOT_BRAIN, 15, 150) brain damage is the WORST + + else + defender.visible_message( + span_danger("[attacker] kicks [defender] back!"), + span_userdanger("You're kicked back by [attacker]!"), + span_hear("You hear a sickening sound of flesh hitting flesh!"), + COMBAT_MESSAGE_RANGE, + attacker, + ) + to_chat(attacker, span_danger("You kick [defender] back!")) + playsound(attacker, 'sound/items/weapons/cqchit1.ogg', 50, TRUE, -1) + var/atom/throw_target = get_edge_target_turf(defender, attacker.dir) + defender.throw_at(throw_target, 1, 14, attacker) + defender.apply_damage(10, attacker.get_attack_type()) + if(defender.body_position == LYING_DOWN && !defender.IsUnconscious()) + defender.adjust_stamina_loss(45) + log_combat(attacker, defender, "kicked (CQC)") + + return TRUE + +/datum/martial_art/darkpack_cqb/proc/Pressure(mob/living/attacker, mob/living/defender) + attacker.do_attack_animation(defender) + log_combat(attacker, defender, "pressured (CQC)") + defender.visible_message( + span_danger("[attacker] punches [defender]'s neck!"), + span_userdanger("Your neck is punched by [attacker]!"), + span_hear("You hear a sickening sound of flesh hitting flesh!"), + COMBAT_MESSAGE_RANGE, + attacker, + ) + to_chat(attacker, span_danger("You punch [defender]'s neck!")) + defender.adjust_dizzy_up_to((10-defender.st_get_stat(STAT_STAMINA)) SECONDS, 10 SECONDS) + defender.adjust_stamina_loss(50) + playsound(attacker, 'sound/items/weapons/cqchit1.ogg', 50, TRUE, -1) + return TRUE + +/datum/martial_art/darkpack_cqb/proc/Restrain(mob/living/attacker, mob/living/defender) + if(restraining_mob?.resolve()) + return FALSE + if(defender.stat != CONSCIOUS) + return FALSE + var/stun_time = SSroll.storyteller_roll((attacker.st_get_stat(STAT_STRENGTH) + attacker.st_get_stat(STAT_BRAWL)), difficulty = 6, numerical = TRUE, roller = attacker) //In THEORY this should be attacker weighted, with high stamima users shrugging it off. + var/defended_time = SSroll.storyteller_roll(defender.st_get_stat(STAT_STAMINA) + defender.st_get_stat(STAT_ATHLETICS), difficulty = (attacker.st_get_stat(STAT_STRENGTH) + attacker.st_get_stat(STAT_BRAWL)), numerical = TRUE, roller = defender) + var/total_time = stun_time - defended_time + if(total_time <= 0) + log_combat(attacker, defender, "failed restrained (CQB)") + defender.visible_message( + span_warning("[attacker] failes to get [defender] into a restraining hold!"), + span_userdanger("You've managed to resist a restraining hold by [attacker]!"), + span_hear("You hear shuffling and a muffled groan!"), + null, + attacker, + ) + to_chat(attacker, span_danger("You fail to lock [defender] into a restraining position!")) + defender.adjust_stamina_loss(20) + return TRUE + log_combat(attacker, defender, "restrained (CQB)") + defender.visible_message( + span_warning("[attacker] locks [defender] into a restraining position!"), + span_userdanger("You're locked into a restraining position by [attacker]!"), + span_hear("You hear shuffling and a muffled groan!"), + null, + attacker, + ) + to_chat(attacker, span_danger("You lock [defender] into a restraining position!")) + defender.adjust_stamina_loss(20) + defender.Stun(total_time SECONDS) + restraining_mob = WEAKREF(defender) + addtimer(VARSET_CALLBACK(src, restraining_mob, null), 5 SECONDS, TIMER_UNIQUE) + return TRUE + +/datum/martial_art/darkpack_cqb/grab_act(mob/living/attacker, mob/living/defender) + if(attacker == defender) + return MARTIAL_ATTACK_INVALID + if(defender.check_block(attacker, 0, attacker.name, UNARMED_ATTACK)) + return MARTIAL_ATTACK_FAIL + + add_to_streak("G", defender) + if(check_streak(attacker, defender)) //if a combo is made no grab upgrade is done + return MARTIAL_ATTACK_SUCCESS + if(attacker.body_position == LYING_DOWN) + return MARTIAL_ATTACK_INVALID + + var/old_grab_state = attacker.grab_state + defender.grabbedby(attacker, TRUE) + if(old_grab_state == GRAB_PASSIVE) + defender.drop_all_held_items() + attacker.setGrabState(GRAB_AGGRESSIVE) //Instant aggressive grab if on grab intent + log_combat(attacker, defender, "grabbed", addition="aggressively") + defender.visible_message( + span_warning("[attacker] violently grabs [defender]!"), + span_userdanger("You're grabbed violently by [attacker]!"), + span_hear("You hear sounds of aggressive fondling!"), + COMBAT_MESSAGE_RANGE, + attacker, + ) + to_chat(attacker, span_danger("You violently grab [defender]!")) + return MARTIAL_ATTACK_SUCCESS + +/datum/martial_art/darkpack_cqb/harm_act(mob/living/attacker, mob/living/defender) + if(attacker.grab_state == GRAB_KILL \ + && attacker.zone_selected == BODY_ZONE_HEAD \ + && attacker.pulling == defender \ + && defender.stat != DEAD \ + ) + var/obj/item/bodypart/head = defender.get_bodypart(BODY_ZONE_HEAD) + if(!isnull(head)) + if(!do_after(attacker, (20 - (attacker.st_get_stat(STAT_DEXTERITY) + attacker.st_get_stat(STAT_MEDICINE))) , defender)) + defender.balloon_alert(attacker, "failed to necksnap!") + to_chat(attacker, span_warning("Your hands slip off [defender]'s neck!")) + return MARTIAL_ATTACK_FAIL + var/snappower = clamp((1 + attacker.st_get_stat(STAT_STRENGTH) - defender.st_get_stat(STAT_STAMINA)), 0, 10) + if(snappower == 0) + defender.balloon_alert(attacker, "not strong enough to necksnap!") + defender.visible_message( + span_notice("[attacker] fails to snap the neck of [defender]."), + span_userdanger("[attacker]'s weak hands were unable to snap your neck'."), + ) + return MARTIAL_ATTACK_FAIL + playsound(defender, 'sound/effects/wounds/crack1.ogg', 100) + defender.visible_message( + span_danger("[attacker] snaps the neck of [defender]!"), + span_userdanger("Your neck is snapped by [attacker]!"), + span_hear("You hear a sickening snap!"), + ignored_mobs = attacker + ) + to_chat(attacker, span_danger("In a swift motion, you snap the neck of [defender]!")) + log_combat(attacker, defender, "snapped neck") //I would call kill() for normal humans but necksnapping a NPC with Str 5 already does it + defender.apply_damage(snappower LETHAL_TTRPG_DAMAGE, BRUTE, BODY_ZONE_HEAD, wound_bonus=CANT_WOUND) + return MARTIAL_ATTACK_SUCCESS + + if(defender.check_block(attacker, 10, attacker.name, UNARMED_ATTACK)) //it's impossible to miss the neck of someone you're already strangling to death + return MARTIAL_ATTACK_FAIL + + if(attacker.resting && defender.stat != DEAD && defender.body_position == STANDING_UP) //This probably needs to go, unsure currently + defender.visible_message( + span_danger("[attacker] leg sweeps [defender]!"), + span_userdanger("Your legs are sweeped by [attacker]!"), + span_hear("You hear a sickening sound of flesh hitting flesh!"), + null, + attacker, + ) + to_chat(attacker, span_danger("You leg sweep [defender]!")) + playsound(attacker, 'sound/effects/hit_kick.ogg', 50, TRUE, -1) + attacker.do_attack_animation(defender) + defender.apply_damage(10, BRUTE) + defender.Knockdown(5 SECONDS) + log_combat(attacker, defender, "sweeped (CQC)") + reset_streak() + return MARTIAL_ATTACK_SUCCESS + + add_to_streak("H", defender) + if(check_streak(attacker, defender)) + return MARTIAL_ATTACK_SUCCESS + if(defender.body_position == LYING_DOWN) + var/obj/item/bodypart/arm/active_arm = attacker.get_active_hand() + var/stomp_force = ((active_arm.unarmed_damage_high + attacker.st_get_stat(STAT_STRENGTH))*(1 + ((attacker.st_get_stat(STAT_STRENGTH) - 2) / 5))) //I would prefer to move the different stomp effects into the attack chain properly, but that's a jankier solution then doing this + var/picked_hit_type = pick("kick", "stomp") + defender.apply_damage(stomp_force, BRUTE) + playsound(defender, (picked_hit_type == "kick" || picked_hit_type == "stomp") ? 'sound/items/weapons/cqchit2.ogg' : 'sound/items/weapons/cqchit1.ogg', 50, TRUE, -1) + + defender.visible_message( + span_danger("[attacker] [picked_hit_type]ed [defender]!"), + span_userdanger("You're [picked_hit_type]ed by [attacker]!"), + span_hear("You hear a sickening sound of flesh hitting flesh!"), + COMBAT_MESSAGE_RANGE, + attacker, + ) + to_chat(attacker, span_danger("You [picked_hit_type] [defender]!")) + log_combat(attacker, defender, "attacked ([picked_hit_type]'d)(CQC)") + return MARTIAL_ATTACK_SUCCESS + + return MARTIAL_ATTACK_INVALID //Outside these two exceptions, punching logic operates normally + +/datum/martial_art/darkpack_cqb/disarm_act(mob/living/attacker, mob/living/defender) + if(defender.check_block(attacker, 0, attacker.name, UNARMED_ATTACK)) + return MARTIAL_ATTACK_FAIL + + add_to_streak("D", defender) + if(check_streak(attacker, defender)) + return MARTIAL_ATTACK_SUCCESS + + if(IS_WEAKREF_OF(attacker.pulling, restraining_mob)) + log_combat(attacker, defender, "disarmed (CQC)", addition = "knocked out (CQC Chokehold)") + defender.visible_message( + span_danger("[attacker] puts [defender] into a chokehold!"), + span_userdanger("You're put into a chokehold by [attacker]!"), + span_hear("You hear shuffling and a muffled groan!"), + null, + attacker, + ) + to_chat(attacker, span_danger("You put [defender] into a chokehold!")) + //this is gonna be a tricky one to figure out + defender.SetSleeping(40 SECONDS) + restraining_mob = null + if(attacker.grab_state < GRAB_NECK && !HAS_TRAIT(attacker, TRAIT_PACIFISM)) + attacker.setGrabState(GRAB_NECK) + return MARTIAL_ATTACK_SUCCESS + + attacker.do_attack_animation(defender, ATTACK_EFFECT_DISARM) + if(prob(65) && (defender.stat == CONSCIOUS || !defender.IsParalyzed() || !restraining_mob?.resolve())) + var/obj/item/disarmed_item = defender.get_active_held_item() + if(disarmed_item && defender.temporarilyRemoveItemFromInventory(disarmed_item)) + attacker.put_in_hands(disarmed_item) + else + disarmed_item = null + + defender.visible_message( + span_danger("[attacker] strikes [defender]'s jaw with their hand[disarmed_item ? ", disarming [defender.p_them()] of [disarmed_item]" : ""]!"), + span_userdanger("[attacker] strikes your jaw,[disarmed_item ? " disarming you of [disarmed_item] and" : ""] leaving you disoriented!"), + span_hear("You hear a sickening sound of flesh hitting flesh!"), + COMBAT_MESSAGE_RANGE, + attacker, + ) + to_chat(attacker, span_danger("You strike [defender]'s jaw,[disarmed_item ? " disarming [defender.p_them()] of [disarmed_item] and" : ""] leaving [defender.p_them()] disoriented!")) + playsound(defender, 'sound/items/weapons/cqchit1.ogg', 50, TRUE, -1) + defender.set_jitter_if_lower(4 SECONDS) + defender.apply_damage(5, attacker.get_attack_type()) + log_combat(attacker, defender, "disarmed (CQC)", addition = disarmed_item ? "(disarmed of [disarmed_item])" : null) + return MARTIAL_ATTACK_SUCCESS + + defender.visible_message( + span_danger("[attacker] fails to disarm [defender]!"), \ + span_userdanger("You're nearly disarmed by [attacker]!"), + span_hear("You hear a swoosh!"), + COMBAT_MESSAGE_RANGE, + attacker, + ) + to_chat(attacker, span_warning("You fail to disarm [defender]!")) + playsound(defender, 'sound/items/weapons/punchmiss.ogg', 25, TRUE, -1) + log_combat(attacker, defender, "failed to disarm (CQC)") + return MARTIAL_ATTACK_FAIL + + +/mob/living/proc/CQB_help() + set name = "Remember The Basics" + set desc = "You try to remember some of the basics of CQB." + set category = "Martial Art" + to_chat(usr, "You try to remember some of the basics of CQB.") + + to_chat(usr, "[span_notice("Slam")]: Grab Punch. Slam opponent into the ground, knocking them down.") + to_chat(usr, "[span_notice("CQB Kick")]: Punch Punch. Knocks opponent away. Knocks out stunned opponents and does stamina damage.") + to_chat(usr, "[span_notice("Restrain")]: Grab Grab. Locks opponents into a restraining position, disarm to knock them out with a chokehold.") + to_chat(usr, "[span_notice("Pressure")]: Shove Grab. Decent stamina damage.") + + to_chat(usr, "In addition, by having your throw mode on when being attacked, you enter an active defense mode where you have a chance to block and sometimes even counter attacks done to you.") + + + +#undef SLAM_COMBO +#undef KICK_COMBO +#undef RESTRAIN_COMBO +#undef PRESSURE_COMBO + +/obj/item/clothing/gloves/cqb_gloves + name = "Debugging Gloves" + desc = "Delete at some point" + icon_state = "black" + greyscale_colors = COLOR_BLACK + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT + resistance_flags = NONE + +/obj/item/clothing/gloves/cqb_gloves/Initialize(mapload) + . = ..() + AddComponent(/datum/component/martial_art_giver, /datum/martial_art/darkpack_cqb) diff --git a/modular_darkpack/modules/martial/kungfu.dm b/modular_darkpack/modules/martial/kungfu.dm new file mode 100644 index 000000000000..6f1c5312b74a --- /dev/null +++ b/modular_darkpack/modules/martial/kungfu.dm @@ -0,0 +1,269 @@ +#define LAUNCH_KICK_COMBO "HD" +#define DROP_KICK_COMBO "DD" +#define KNEE_STOMACH_COMBO "GH" + +/datum/martial_art/darkpack_kungfu + name = "Kung Fu" + id = MARTIALART_DARKPACK_KUNGFU + help_verb = /mob/living/proc/kungfu_help + display_combos = TRUE + grab_state_modifier = 1 + + +/datum/martial_art/darkpack_kungfu/activate_style(mob/living/new_holder) + . = ..() + //RegisterSignal(new_holder, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby)) + RegisterSignal(new_holder, COMSIG_ATOM_PRE_BULLET_ACT, PROC_REF(hit_by_projectile)) + RegisterSignal(new_holder, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(check_dodge)) + if (iscarbon(new_holder)) + var/list/obj/item/bodypart/affected_bodyparts + var/mob/living/carbon/human/carbon_owner = new_holder + for (var/obj/item/bodypart/limb as anything in carbon_owner.bodyparts) + if (!istype(limb, /obj/item/bodypart/arm) && !istype(limb, /obj/item/bodypart/leg)) + continue + + LAZYADD(affected_bodyparts, limb) + + //limb.unarmed_damage_low += 5 Unsure on this one + //limb.unarmed_damage_high += 5 + limb.unarmed_attack_effect = null + limb.unarmed_attack_sound = 'modular_darkpack/modules/martial/sounds/harmboxing.ogg' + +/datum/martial_art/darkpack_kungfu/deactivate_style(mob/living/remove_from) + UnregisterSignal(remove_from, list(COMSIG_ATOM_ATTACKBY, COMSIG_ATOM_PRE_BULLET_ACT, COMSIG_LIVING_CHECK_BLOCK)) + return ..() + +/datum/martial_art/darkpack_kungfu/proc/check_streak(mob/living/attacker, mob/living/defender) + + if(findtext(streak,LAUNCH_KICK_COMBO)) + reset_streak() + return launch_kick(attacker, defender) + + if(findtext(streak,DROP_KICK_COMBO)) + reset_streak() + return drop_kick(attacker, defender) + + if(findtext(streak,KNEE_STOMACH_COMBO)) + reset_streak() + return knee_stomach(attacker, defender) + + return FALSE + +/// Frontal Kick: Harm Disarm combo, knocks back relative to Attacker Str - Defender Fort +/datum/martial_art/darkpack_kungfu/proc/launch_kick(mob/living/attacker, mob/living/defender) + attacker.do_attack_animation(defender, ATTACK_EFFECT_KICK) + defender.visible_message( + span_warning("[attacker] kicks [defender] square in the chest, sending them flying!"), + span_userdanger("You are kicked square in the chest by [attacker], sending you flying!"), + span_hear("You hear a sickening sound of flesh hitting flesh!"), + COMBAT_MESSAGE_RANGE, + attacker, + ) + to_chat(attacker, span_danger("You kick [defender] square in the chest, sending them flying!")) + playsound(attacker, 'modular_darkpack/modules/martial/sounds/frontalkick.ogg', 50, TRUE, -1) + var/atom/throw_target = get_edge_target_turf(defender, attacker.dir) + var/throw_distance = clamp((attacker.st_get_stat(STAT_STRENGTH) - defender.st_get_stat(STAT_STAMINA)), 1, 3) + defender.throw_at(throw_target, throw_distance, 4, attacker) + defender.apply_damage(15, attacker.get_attack_type(), BODY_ZONE_CHEST) + log_combat(attacker, defender, "Frontal Kicked (Kungfu)") + return TRUE + +/// Roundhouse Kick: Disarm Disarm combo, knocks people down and deals substantial stamina damage, and also discombobulates them. Knocks objects out of their hands if they're already on the ground. +/datum/martial_art/darkpack_kungfu/proc/drop_kick(mob/living/attacker, mob/living/defender) + attacker.do_attack_animation(defender, ATTACK_EFFECT_KICK) + playsound(attacker, 'modular_darkpack/modules/martial/sounds/roundhousekick.ogg', 50, TRUE, -1) + var/kickpower = SSroll.storyteller_roll((attacker.st_get_stat(STAT_STRENGTH) + attacker.st_get_stat(STAT_BRAWL)), difficulty = 7, numerical = TRUE, roller = attacker) + if(defender.body_position == STANDING_UP && (kickpower >= 0)) + defender.Knockdown(kickpower SECONDS) + defender.visible_message(span_warning("[attacker] kicks [defender] in the head, sending them face first into the floor!"), \ + span_userdanger("You are kicked in the head by [attacker], sending you crashing to the floor!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker) + else + defender.drop_all_held_items() + defender.visible_message(span_warning("[attacker] kicks [defender] in the head!"), \ + span_userdanger("You are kicked in the head by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker) + defender.apply_damage(10, attacker.get_attack_type()) + defender.apply_damage(40, STAMINA) + defender.adjust_dizzy_up_to(10 SECONDS, 10 SECONDS) + log_combat(attacker, defender, "Roundhoused (KungFu)") + return TRUE + +/// Flying Knee: Grab Harm combo, causes them to be silenced and briefly stunned, as well as doing a moderate amount of stamina damage. +/datum/martial_art/darkpack_kungfu/proc/knee_stomach(mob/living/attacker, mob/living/defender) + attacker.do_attack_animation(defender, ATTACK_EFFECT_KICK) + playsound(attacker, 'modular_darkpack/modules/martial/sounds/grabbed.ogg', 70, TRUE, -1) + defender.visible_message( + span_warning("[attacker] violently slams [attacker.p_their()] knee into [defender]!"), + span_userdanger("You slam your knee straight into [defender]!"), + span_hear("You hear a sickening sound of flesh hitting flesh!"), + COMBAT_MESSAGE_RANGE, + attacker, + ) + var/roll_success = SSroll.storyteller_roll((attacker.st_get_stat(STAT_STRENGTH) + attacker.st_get_stat(STAT_BRAWL)), difficulty = 8, roller = attacker) + if(roll_success) + defender.Knockdown(3 SECONDS) + defender.apply_damage(40, STAMINA) + defender.adjust_silence_up_to(5 SECONDS, 5 SECONDS) + log_combat(attacker, defender, "kneed in the stomach (Kung-Fu)") + return TRUE + +/datum/martial_art/darkpack_kungfu/grab_act(mob/living/attacker, mob/living/defender) + if(!can_deflect(attacker)) //allows for deniability + return MARTIAL_ATTACK_INVALID + + if(defender.check_block(attacker, 0, "[attacker]'s grab", UNARMED_ATTACK)) + return MARTIAL_ATTACK_FAIL + + add_to_streak("G", defender) + if(check_streak(attacker, defender)) + return MARTIAL_ATTACK_SUCCESS + + var/grab_log_description = "grabbed" + attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH) + playsound(defender, 'modular_darkpack/modules/martial/sounds/frontalkick2.ogg', 25, TRUE, -1) + defender.apply_damage(20, STAMINA) + log_combat(attacker, defender, "[grab_log_description] (Sleeping Carp)") + return MARTIAL_ATTACK_INVALID // normal grab + +/datum/martial_art/darkpack_kungfu/harm_act(mob/living/attacker, mob/living/defender) + if(defender.check_block(attacker, 10, attacker.name, UNARMED_ATTACK)) + return MARTIAL_ATTACK_FAIL + + add_to_streak("H", defender) + if(check_streak(attacker, defender)) + return MARTIAL_ATTACK_SUCCESS + + return MARTIAL_ATTACK_INVALID // normal punch + +/datum/martial_art/darkpack_kungfu/disarm_act(mob/living/attacker, mob/living/defender) + if(!can_deflect(attacker)) //allows for deniability + return MARTIAL_ATTACK_INVALID + if(defender.check_block(attacker, 0, attacker.name, UNARMED_ATTACK)) + return MARTIAL_ATTACK_FAIL + + add_to_streak("D", defender) + if(check_streak(attacker, defender)) + return MARTIAL_ATTACK_SUCCESS + + attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH) + playsound(defender, 'sound/items/weapons/punch1.ogg', 25, TRUE, -1) + defender.apply_damage(20, STAMINA) + log_combat(attacker, defender, "disarmed (Sleeping Carp)") + return MARTIAL_ATTACK_INVALID // normal disarm + +/datum/martial_art/darkpack_kungfu/proc/can_deflect(mob/living/user) + if(!can_use(user) || !user.combat_mode) + return FALSE + if(INCAPACITATED_IGNORING(user, INCAPABLE_GRAB)) //NO STUN + return FALSE + if(!(user.mobility_flags & MOBILITY_USE)) //NO UNABLE TO USE + return FALSE + if(HAS_TRAIT(user, TRAIT_HULK)) //NO HULK + return FALSE + if(!isturf(user.loc)) //NO MOTHERFLIPPIN MECHS! + return FALSE + return TRUE + +/datum/martial_art/darkpack_kungfu/proc/reset_animation(mob/living/user, fadein) + if(fadein) + animate(user, alpha = 225, time = 0.1 SECONDS) + return + else + animate(user, pixel_x = 0, pixel_y = 0, time = 0.1 SECONDS) + +/datum/martial_art/darkpack_kungfu/proc/hit_by_projectile(mob/living/user, obj/projectile/hitting_projectile, def_zone) + SIGNAL_HANDLER + + if(!user.is_clan(/datum/vampire_clan/true_brujah)) + return NONE //No, you cant dodge bullets normally, bum. + + var/determine_avoidance = ((user.st_get_stat(STAT_ATHLETICS) + user.st_get_stat(STAT_DEXTERITY)) * 7) + + if(!can_deflect(user)) + return NONE + + if(!prob(determine_avoidance)) + return NONE + + user.visible_message( + span_danger("[user] effortlessly dodges the [hitting_projectile]! [user.p_They()] is unnaturally fast!"), + span_userdanger("You dodge [hitting_projectile]!"), + ) + playsound(user, SFX_BULLET_MISS, 75, TRUE) + hitting_projectile.firer = user + var/mob/living/carbon/human/dodger = user + if(dodger.is_clan(/datum/vampire_clan/true_brujah)) + animate(user, alpha = 0, time = 0.2 SECONDS) + new /obj/effect/temporis/weskar(user.loc, user) + addtimer(CALLBACK(src, PROC_REF(reset_animation), user, TRUE), 0.1 SECONDS) + else + animate(user, pixel_x = rand(-16,16), pixel_y = rand(-16,16), time = 0.2 SECONDS) + addtimer(CALLBACK(src, PROC_REF(reset_animation), user, FALSE), 0.1 SECONDS) + //hitting_projectile.set_angle(rand(0, 360)) theoretically it should go straight through if you avoided + return COMPONENT_BULLET_PIERCED + +/// If our user has committed to being as martial arty as they can be, they may be able to avoid incoming attacks. +/datum/martial_art/darkpack_kungfu/proc/check_dodge(mob/living/user, atom/movable/hitby, damage, attack_text, attack_type, ...) + SIGNAL_HANDLER + + var/determine_avoidance = ((user.st_get_stat(STAT_ATHLETICS) + user.st_get_stat(STAT_DEXTERITY))) + + if(!can_deflect(user)) + return + + if(user.throw_mode) + determine_avoidance *= 2 + + if(attack_type == PROJECTILE_ATTACK || attack_type == THROWN_PROJECTILE_ATTACK) + return NONE + + if(!prob(determine_avoidance)) + return NONE + + user.visible_message( + span_danger("[user] cleanly avoids [attack_text] with incredible speed!"), + span_userdanger("You dodge [attack_text]"), + ) + playsound(user.loc, 'sound/items/weapons/punchmiss.ogg', 25, TRUE, -1) + var/mob/living/carbon/human/dodger = user + if(dodger.is_clan(/datum/vampire_clan/true_brujah)) + animate(user, alpha = 0, time = 0.3 SECONDS) + new /obj/effect/temporis/weskar(user.loc, user) + addtimer(CALLBACK(src, PROC_REF(reset_animation), user, TRUE), 0.1 SECONDS) + else + animate(user, pixel_x = rand(-16,16), pixel_y = rand(-16,16), time = 0.1 SECONDS) + addtimer(CALLBACK(src, PROC_REF(reset_animation), user, FALSE), 0.1 SECONDS) + + + + return SUCCESSFUL_BLOCK + +/mob/living/proc/kungfu_help() + set name = "Recall Teachings" + set desc = "Remember the martial techniques of the Kung-Fu" + set category = "Martial Arts" + + to_chat(usr, span_info("You retreat inward and recall your past training")) + to_chat(usr, "[span_notice("Frontal Kick")]: Punch Shove. Launch your opponent away from you with incredible force!") + to_chat(usr, "[span_notice("Roundhouse Kick")]: Shove Shove. Nonlethally kick an opponent to the floor, knocking them down, discombobulating them and dealing substantial stamina damage. If they're already prone, disarm them as well.") + to_chat(usr, "[span_notice("Flying Knee")]: Grab Punch. Deliver a knee jab into the opponent, dealing high stamina damage, as well as briefly stunning them, winding them and making it difficult for them to speak.") + to_chat(usr, "[span_notice("Grabs and Shoves")]: While in combat mode, your typical grab and shove do decent stamina damage, and your grabs harder to break. If you grab someone who has substantial amounts of stamina damage, you knock them out!") + + +#undef LAUNCH_KICK_COMBO +#undef DROP_KICK_COMBO +#undef KNEE_STOMACH_COMBO + +/obj/item/clothing/gloves/kungfu_gloves + name = "Debugging Gloves" + desc = "Delete at some point" + icon_state = "black" + greyscale_colors = COLOR_BLACK + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT + resistance_flags = NONE + +/obj/item/clothing/gloves/kungfu_gloves/Initialize(mapload) + . = ..() + AddComponent(/datum/component/martial_art_giver, /datum/martial_art/darkpack_kungfu) diff --git a/modular_darkpack/modules/martial/sounds/bell.ogg b/modular_darkpack/modules/martial/sounds/bell.ogg new file mode 100644 index 000000000000..6a23df1f7f2f Binary files /dev/null and b/modular_darkpack/modules/martial/sounds/bell.ogg differ diff --git a/modular_darkpack/modules/martial/sounds/cross.ogg b/modular_darkpack/modules/martial/sounds/cross.ogg new file mode 100644 index 000000000000..39afbaf6b905 Binary files /dev/null and b/modular_darkpack/modules/martial/sounds/cross.ogg differ diff --git a/modular_darkpack/modules/martial/sounds/frontalkick.ogg b/modular_darkpack/modules/martial/sounds/frontalkick.ogg new file mode 100644 index 000000000000..4e21014060d5 Binary files /dev/null and b/modular_darkpack/modules/martial/sounds/frontalkick.ogg differ diff --git a/modular_darkpack/modules/martial/sounds/frontalkick2.ogg b/modular_darkpack/modules/martial/sounds/frontalkick2.ogg new file mode 100644 index 000000000000..ba37073c466d Binary files /dev/null and b/modular_darkpack/modules/martial/sounds/frontalkick2.ogg differ diff --git a/modular_darkpack/modules/martial/sounds/grabbed.ogg b/modular_darkpack/modules/martial/sounds/grabbed.ogg new file mode 100644 index 000000000000..7596d3288e2f Binary files /dev/null and b/modular_darkpack/modules/martial/sounds/grabbed.ogg differ diff --git a/modular_darkpack/modules/martial/sounds/harmboxing.ogg b/modular_darkpack/modules/martial/sounds/harmboxing.ogg new file mode 100644 index 000000000000..c3e98e3fa1d7 Binary files /dev/null and b/modular_darkpack/modules/martial/sounds/harmboxing.ogg differ diff --git a/modular_darkpack/modules/martial/sounds/hook.ogg b/modular_darkpack/modules/martial/sounds/hook.ogg new file mode 100644 index 000000000000..ac00ebf1e02f Binary files /dev/null and b/modular_darkpack/modules/martial/sounds/hook.ogg differ diff --git a/modular_darkpack/modules/martial/sounds/jabcombo.ogg b/modular_darkpack/modules/martial/sounds/jabcombo.ogg new file mode 100644 index 000000000000..1cfbaddb342d Binary files /dev/null and b/modular_darkpack/modules/martial/sounds/jabcombo.ogg differ diff --git a/modular_darkpack/modules/martial/sounds/kneeing.ogg b/modular_darkpack/modules/martial/sounds/kneeing.ogg new file mode 100644 index 000000000000..1f79ebc6ffbe Binary files /dev/null and b/modular_darkpack/modules/martial/sounds/kneeing.ogg differ diff --git a/modular_darkpack/modules/martial/sounds/mainpunch.ogg b/modular_darkpack/modules/martial/sounds/mainpunch.ogg new file mode 100644 index 000000000000..e81ba852dc64 Binary files /dev/null and b/modular_darkpack/modules/martial/sounds/mainpunch.ogg differ diff --git a/modular_darkpack/modules/martial/sounds/mainpunch2.ogg b/modular_darkpack/modules/martial/sounds/mainpunch2.ogg new file mode 100644 index 000000000000..2c0f7b2b8cdd Binary files /dev/null and b/modular_darkpack/modules/martial/sounds/mainpunch2.ogg differ diff --git a/modular_darkpack/modules/martial/sounds/roundhousekick.ogg b/modular_darkpack/modules/martial/sounds/roundhousekick.ogg new file mode 100644 index 000000000000..5eba90a28516 Binary files /dev/null and b/modular_darkpack/modules/martial/sounds/roundhousekick.ogg differ diff --git a/modular_darkpack/modules/martial/sounds/swipe.ogg b/modular_darkpack/modules/martial/sounds/swipe.ogg new file mode 100644 index 000000000000..83f0b64084fb Binary files /dev/null and b/modular_darkpack/modules/martial/sounds/swipe.ogg differ diff --git a/modular_darkpack/modules/martial/sounds/uppercut.ogg b/modular_darkpack/modules/martial/sounds/uppercut.ogg new file mode 100644 index 000000000000..08ab21295d4b Binary files /dev/null and b/modular_darkpack/modules/martial/sounds/uppercut.ogg differ diff --git a/modular_darkpack/modules/martial/streetboxing.dm b/modular_darkpack/modules/martial/streetboxing.dm new file mode 100644 index 000000000000..eba8ce0b2c29 --- /dev/null +++ b/modular_darkpack/modules/martial/streetboxing.dm @@ -0,0 +1,278 @@ +#define UPPERCUT_COMBO "HDH" +#define JAB_COMBO "GHH" +#define CROSS_COMBO "DHH" +#define DIRTY_COMBO "GD" + +/datum/martial_art/darkpack_boxing + name = "Kung Fu" + id = MARTIALART_DARKPACK_BOXING + help_verb = /mob/living/proc/streetboxing_help + display_combos = TRUE + grab_state_modifier = 1 + + +/datum/martial_art/darkpack_boxing/activate_style(mob/living/new_holder) + . = ..() + //RegisterSignal(new_holder, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby)) + RegisterSignal(new_holder, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(check_dodge)) + if (iscarbon(new_holder)) + var/list/obj/item/bodypart/affected_bodyparts + var/mob/living/carbon/human/carbon_owner = new_holder + for (var/obj/item/bodypart/limb as anything in carbon_owner.bodyparts) + if (!istype(limb, /obj/item/bodypart/arm) && !istype(limb, /obj/item/bodypart/leg)) + continue + + LAZYADD(affected_bodyparts, limb) + + //limb.unarmed_damage_low += 5 Unsure on this one + //limb.unarmed_damage_high += 5 + //limb.unarmed_attack_sound = 'modular_darkpack/modules/martial/sounds/harmboxing.ogg' + +/datum/martial_art/darkpack_boxing/deactivate_style(mob/living/remove_from) + UnregisterSignal(remove_from, list(COMSIG_LIVING_CHECK_BLOCK)) + return ..() + +/datum/martial_art/darkpack_boxing/proc/check_streak(mob/living/attacker, mob/living/defender) + + if(findtext(streak,UPPERCUT_COMBO)) + reset_streak() + return uppercut(attacker, defender) + + if(findtext(streak,JAB_COMBO)) + reset_streak() + return jab_combo(attacker, defender) + + if(findtext(streak,CROSS_COMBO)) + reset_streak() + return cross_punch(attacker, defender) + + if(findtext(streak,DIRTY_COMBO)) + reset_streak() + return dirty_hit(attacker, defender) + + return FALSE + +/// Frontal Kick: Harm Disarm combo, knocks back relative to Attacker Str - Defender Fort +/datum/martial_art/darkpack_boxing/proc/uppercut(mob/living/attacker, mob/living/defender) + attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH) + uppercut_animation(attacker) + addtimer(CALLBACK(src, PROC_REF(reset_animation), attacker, FALSE), 0.1 SECONDS) + defender.visible_message( + span_warning("[attacker] uppercuts [defender] square in the jaw, sending you flying"), + span_userdanger("You are uppercut in the jaw by [attacker], sending you flying!"), + span_hear("You hear a sickening sound of flesh hitting flesh!"), + COMBAT_MESSAGE_RANGE, + attacker, + ) + + playsound(attacker, 'modular_darkpack/modules/martial/sounds/uppercut.ogg', 50, TRUE, -1) + var/atom/throw_target = get_edge_target_turf(defender, attacker.dir) + var/throw_distance = clamp((attacker.st_get_stat(STAT_STRENGTH) - defender.st_get_stat(STAT_STAMINA)), 1, 3) + defender.throw_at(throw_target, throw_distance, 4, attacker) + defender.apply_damage(6 * attacker.st_get_stat(STAT_STRENGTH), attacker.get_attack_type(), BODY_ZONE_HEAD) + log_combat(attacker, defender, "Frontal Kicked (Kungfu)") + return TRUE + +/// Jab Combo: Hit the other guy twice in quick succession +/datum/martial_art/darkpack_boxing/proc/jab_combo(mob/living/attacker, mob/living/defender) + attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH) + playsound(attacker, 'modular_darkpack/modules/martial/sounds/jabcombo.ogg', 50, TRUE, -1) + defender.visible_message(span_warning("[attacker] rapidly jabs [defender]'s head!"), \ + span_userdanger("You are jabbed in the head by [attacker], leaving you disoriented!"), span_hear("You hear a sickening sound of knuckles hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker) + defender.adjust_stamina_loss(45) + defender.apply_damage(20, attacker.get_attack_type(), BODY_ZONE_HEAD) + defender.adjust_dizzy_up_to(10 SECONDS, 10 SECONDS) + log_combat(attacker, defender, "Jab Combo'd (Boxing)") + return TRUE + +/// Cross Punch: Figure out +/datum/martial_art/darkpack_boxing/proc/cross_punch(mob/living/attacker, mob/living/defender) + attacker.do_attack_animation(defender, ATTACK_EFFECT_KICK) + playsound(attacker, 'modular_darkpack/modules/martial/sounds/grabbed.ogg', 70, TRUE, -1) + defender.visible_message( + span_warning("[attacker] violently slams [attacker.p_their()] knee into [defender]!"), + span_userdanger("You slam your knee straight into [defender]!"), + span_hear("You hear a sickening sound of flesh hitting flesh!"), + COMBAT_MESSAGE_RANGE, + attacker, + ) + var/roll_success = SSroll.storyteller_roll((attacker.st_get_stat(STAT_STRENGTH) + attacker.st_get_stat(STAT_BRAWL)), difficulty = 8, roller = attacker) + if(roll_success) + defender.Knockdown(3 SECONDS) + defender.apply_damage(40, STAMINA) + defender.adjust_silence_up_to(5 SECONDS, 5 SECONDS) + log_combat(attacker, defender, "kneed in the stomach (Kung-Fu)") + return TRUE + +/// Dirty Move: Liver Punch or something +/datum/martial_art/darkpack_boxing/proc/dirty_hit(mob/living/attacker, mob/living/defender) + attacker.do_attack_animation(defender, ATTACK_EFFECT_KICK) + playsound(attacker, 'modular_darkpack/modules/martial/sounds/grabbed.ogg', 70, TRUE, -1) + defender.visible_message( + span_warning("[attacker] violently slams [attacker.p_their()] knee into [defender]!"), + span_userdanger("You slam your knee straight into [defender]!"), + span_hear("You hear a sickening sound of flesh hitting flesh!"), + COMBAT_MESSAGE_RANGE, + attacker, + ) + var/roll_success = SSroll.storyteller_roll((attacker.st_get_stat(STAT_STRENGTH) + attacker.st_get_stat(STAT_BRAWL)), difficulty = 8, roller = attacker) + if(roll_success) + defender.Knockdown(3 SECONDS) + defender.apply_damage(40, STAMINA) + defender.adjust_silence_up_to(5 SECONDS, 5 SECONDS) + log_combat(attacker, defender, "kneed in the stomach (Kung-Fu)") + return TRUE + +/datum/martial_art/darkpack_boxing/grab_act(mob/living/attacker, mob/living/defender) + if(defender.check_block(attacker, 0, "[attacker]'s grab", UNARMED_ATTACK)) + return MARTIAL_ATTACK_FAIL + + add_to_streak("G", defender) + if(check_streak(attacker, defender)) + return MARTIAL_ATTACK_SUCCESS + defender.apply_damage(15, STAMINA) + return MARTIAL_ATTACK_INVALID //Boxing is not known for holds at all + +/datum/martial_art/darkpack_boxing/harm_act(mob/living/attacker, mob/living/defender) + if(defender.check_block(attacker, 10, attacker.name, UNARMED_ATTACK)) + return MARTIAL_ATTACK_FAIL + + add_to_streak("H", defender) + if(check_streak(attacker, defender)) + return MARTIAL_ATTACK_SUCCESS + + return MARTIAL_ATTACK_INVALID //You're gonna punch someone in the face normally and like it + +/datum/martial_art/darkpack_boxing/disarm_act(mob/living/attacker, mob/living/defender) + if(!can_deflect(attacker)) //you arent swiping at someone on the ground + return MARTIAL_ATTACK_INVALID + if(defender.check_block(attacker, 0, attacker.name, UNARMED_ATTACK)) + return MARTIAL_ATTACK_FAIL + + add_to_streak("D", defender) + if(check_streak(attacker, defender)) + return MARTIAL_ATTACK_SUCCESS + + //playsound(defender, 'modular_darkpack/modules/martial/sounds/swipe.ogg', 25, TRUE, -1) + defender.apply_damage(20, STAMINA) + return MARTIAL_ATTACK_INVALID //Essentially taking a swipe at their face + +/datum/martial_art/darkpack_boxing/proc/can_deflect(mob/living/user) + if(!can_use(user) || !user.combat_mode) + return FALSE + if(INCAPACITATED_IGNORING(user, INCAPABLE_GRAB)) //NO STUN + return FALSE + if(!(user.mobility_flags & MOBILITY_USE)) //NO UNABLE TO USE + return FALSE + if(HAS_TRAIT(user, TRAIT_HULK)) //NO HULK + return FALSE + if(!isturf(user.loc)) //NO MOTHERFLIPPIN MECHS! + return FALSE + return TRUE + +/datum/martial_art/darkpack_boxing/proc/reset_animation(mob/living/user, fadein) + if(fadein) + animate(user, alpha = 225, time = 0.1 SECONDS) + return + else + animate(user, alpha = 225, pixel_x = 0, pixel_y = 0, time = 0.1 SECONDS) + +/datum/martial_art/darkpack_boxing/proc/dodge_animation(mob/living/user) + var/new_pixel_x + var/new_pixel_y + var/userdir = user.dir + switch(userdir) + if(EAST) + new_pixel_x = rand(-16, -8) + new_pixel_y = rand(-24, 24) + if(WEST) + new_pixel_x = rand(8, 16) + new_pixel_y = rand(-24, 24) + if(NORTH) + new_pixel_x = rand(-16,16) + new_pixel_y = rand(-16, -8) + if(SOUTH) + new_pixel_x = rand(-16,16) + new_pixel_y = rand(8, 16) + animate(user, alpha = 200, pixel_x = new_pixel_x, pixel_y = new_pixel_y, time = 0.2 SECONDS) + +/datum/martial_art/darkpack_boxing/proc/uppercut_animation(mob/living/user) + var/new_pixel_x + var/new_pixel_y + var/userdir = user.dir + switch(userdir) + if(EAST) + new_pixel_x = rand(0, 4) + new_pixel_y = rand(0, 12) + if(WEST) + new_pixel_x = rand(-4, 0) + new_pixel_y = rand(0, 12) + if(NORTH) + new_pixel_y = rand(0, 12) + if(SOUTH) + new_pixel_y = rand(0, 12) + animate(user, alpha = 200, pixel_x = new_pixel_x, pixel_y = new_pixel_y, time = 0.2 SECONDS) + +/// Like the hit game "Punch Out!!"" (1984) you can dodge incoming punches at you.. melee weapons and projectiles? not so lucky +/datum/martial_art/darkpack_boxing/proc/check_dodge(mob/living/user, atom/movable/hitby, damage, attack_text, attack_type, ...) + SIGNAL_HANDLER + + var/determine_avoidance = ((user.st_get_stat(STAT_ATHLETICS) + user.st_get_stat(STAT_DEXTERITY) + user.st_get_stat(STAT_BRAWL)) * 2) //Theoretical max of 40% with Cel 5 + + if(!can_deflect(user)) + return + + if(user.throw_mode) //Theoretical max of 80% with Cel 5 + determine_avoidance *= 2 + + if(attack_type != UNARMED_ATTACK) + return NONE + + if(!prob(determine_avoidance)) + return NONE + + user.visible_message( + span_danger("[user] cleanly dodges [attack_text] with incredible speed!"), + span_userdanger("You dodge [attack_text]"), + ) + playsound(user.loc, 'sound/items/weapons/punchmiss.ogg', 25, TRUE, -1) + var/mob/living/attacker = GET_ASSAILANT(hitby) + user.setDir(turn(attacker.dir, 180)) + var/mob/living/carbon/human/dodger = user + dodge_animation(dodger) + addtimer(CALLBACK(src, PROC_REF(reset_animation), dodger, FALSE), 0.1 SECONDS) + + + return SUCCESSFUL_BLOCK + +/mob/living/proc/streetboxing_help() + set name = "Recall Teachings" + set desc = "Remember the martial techniques of the Kung-Fu" + set category = "Martial Arts" + + to_chat(usr, span_info("You retreat inward and recall your past training")) + to_chat(usr, "[span_notice("Frontal Kick")]: Punch Shove. Launch your opponent away from you with incredible force!") + to_chat(usr, "[span_notice("Roundhouse Kick")]: Shove Shove. Nonlethally kick an opponent to the floor, knocking them down, discombobulating them and dealing substantial stamina damage. If they're already prone, disarm them as well.") + to_chat(usr, "[span_notice("Flying Knee")]: Grab Punch. Deliver a knee jab into the opponent, dealing high stamina damage, as well as briefly stunning them, winding them and making it difficult for them to speak.") + to_chat(usr, "[span_notice("Grabs and Shoves")]: While in combat mode, your typical grab and shove do decent stamina damage, and your grabs harder to break. If you grab someone who has substantial amounts of stamina damage, you knock them out!") + +#undef UPPERCUT_COMBO +#undef JAB_COMBO +#undef CROSS_COMBO +#undef DIRTY_COMBO + +/obj/item/clothing/gloves/boxing_gloves + name = "Debugging Gloves" + desc = "Delete at some point" + icon_state = "black" + greyscale_colors = COLOR_BLACK + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT + resistance_flags = NONE + +/obj/item/clothing/gloves/boxing_gloves/Initialize(mapload) + . = ..() + AddComponent(/datum/component/martial_art_giver, /datum/martial_art/darkpack_boxing) + diff --git a/modular_darkpack/modules/martial/swords.dm b/modular_darkpack/modules/martial/swords.dm new file mode 100644 index 000000000000..2b56a2dfb38c --- /dev/null +++ b/modular_darkpack/modules/martial/swords.dm @@ -0,0 +1,115 @@ +/* +#define ATTACK_STRIKE "Hilt Strike" +#define ATTACK_SLICE "Wide Slice" +#define ATTACK_CUT "Tendon Cut" + +/obj/item/testing_katana + name = "testing katana" + desc = "It's a regular Katana.. like one of those japanese animes." + icon = 'icons/obj/mining_zones/artefacts.dmi' + icon_state = "cursed_katana" + icon_angle = -45 + lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' + force = 15 + armour_penetration = 30 + block_chance = 30 + block_sound = 'sound/items/weapons/parry.ogg' + sharpness = SHARP_EDGED + w_class = WEIGHT_CLASS_HUGE + attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts") + attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut") + hitsound = 'sound/items/weapons/bladeslice.ogg' + resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | FREEZE_PROOF + var/static/list/combo_list = list( + ATTACK_STRIKE = list(COMBO_STEPS = list(LEFT_ATTACK, LEFT_ATTACK, RIGHT_ATTACK), COMBO_PROC = PROC_REF(strike)), + ATTACK_SLICE = list(COMBO_STEPS = list(RIGHT_ATTACK, LEFT_ATTACK, LEFT_ATTACK), COMBO_PROC = PROC_REF(slice)), + ATTACK_CUT = list(COMBO_STEPS = list(RIGHT_ATTACK, RIGHT_ATTACK, LEFT_ATTACK), COMBO_PROC = PROC_REF(cut)), + ) + var/list/alt_continuous = list("stabs", "pierces", "impales") + var/list/alt_simple = list("stab", "pierce", "impale") + +/obj/item/testing_katana/Initialize(mapload) + . = ..() + alt_continuous = string_list(alt_continuous) + alt_simple = string_list(alt_simple) + AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple) + AddComponent( \ + /datum/component/combo_attacks, \ + combos = combo_list, \ + max_combo_length = 3, \ + examine_message = span_notice("This seems to be a skilled weapon... perhaps I could use my experience?"), \ + reset_message = "you return to neutral stance", \ + can_attack_callback = CALLBACK(src, PROC_REF(can_combo_attack)) \ + ) + +/obj/item/testing_katana/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) + var/datum/storyteller_roll/melee_block/parry_roll = new() + var/puller_result = parry_roll.st_roll(owner, src) + final_block_chance = puller_result * 5 //I dont see what can go wrong with allowing a theoretical 100% parry chance for an individual hit. There was infact something wrong + return ..() + +/obj/item/testing_katana/proc/can_combo_attack(mob/living/carbon/user, mob/living/target) //prevents the unworthy from properly using blades + return target.stat != DEAD && target != user && (user.st_get_stat(STAT_MELEE) >= 4) + +/obj/item/testing_katana/proc/strike(mob/living/target, mob/user) + user.visible_message(span_warning("[user] strikes [target] with [src]'s hilt!"), + span_notice("You hilt strike [target]!")) + to_chat(target, span_userdanger("You've been struck by [user]!")) + playsound(src, 'sound/items/weapons/genhit3.ogg', 50, TRUE) + RegisterSignal(target, COMSIG_MOVABLE_IMPACT, PROC_REF(strike_throw_impact)) + var/atom/throw_target = get_edge_target_turf(target, user.dir) + target.throw_at(throw_target, 5, 3, user, FALSE, gentle = TRUE) + target.apply_damage(damage = 17, exposed_wound_bonus = 10) + to_chat(target, span_userdanger("You've been struck by [user]!")) + user.do_attack_animation(target, ATTACK_EFFECT_PUNCH) + +/obj/item/testing_katana/proc/strike_throw_impact(mob/living/source, atom/hit_atom, datum/thrownthing/thrownthing) + SIGNAL_HANDLER + + UnregisterSignal(source, COMSIG_MOVABLE_IMPACT) + if(isclosedturf(hit_atom)) + source.apply_damage(damage = 5) + if(ishostile(source)) + var/mob/living/simple_animal/hostile/target = source + target.ranged_cooldown += 5 SECONDS + else if(iscarbon(source)) + var/mob/living/carbon/target = source + target.set_confusion_if_lower(8 SECONDS) + return NONE + +/obj/item/testing_katana/proc/slice(mob/living/target, mob/user) + user.visible_message(span_warning("[user] does a wide slice!"), + span_notice("You do a wide slice!")) + playsound(src, 'sound/items/weapons/bladeslice.ogg', 50, TRUE) + user.do_item_attack_animation(target, used_item = src, animation_type = ATTACK_ANIMATION_SLASH) + var/turf/user_turf = get_turf(user) + var/dir_to_target = get_dir(user_turf, get_turf(target)) + var/static/list/testing_katana_slice_angles = list(0, -45, 45, -90, 90) //so that the animation animates towards the target clicked and not towards a side target + for(var/iteration in testing_katana_slice_angles) + var/turf/turf = get_step(user_turf, turn(dir_to_target, iteration)) + user.do_attack_animation(turf, ATTACK_EFFECT_SLASH) + for(var/mob/living/additional_target in turf) + if(user.Adjacent(additional_target) && additional_target.density) + additional_target.apply_damage(damage = 15, sharpness = SHARP_EDGED, exposed_wound_bonus = 10) + to_chat(additional_target, span_userdanger("You've been sliced by [user]!")) + target.apply_damage(damage = 5, sharpness = SHARP_EDGED, wound_bonus = 10) + +/obj/item/testing_katana/proc/cut(mob/living/target, mob/user) + user.visible_message(span_warning("[user] cuts [target]'s tendons!"), + span_notice("You tendon cut [target]!")) + to_chat(target, span_userdanger("Your tendons have been cut by [user]!")) + user.do_item_attack_animation(target, used_item = src, animation_type = ATTACK_ANIMATION_SLASH) + target.apply_damage(damage = 15, sharpness = SHARP_EDGED, wound_bonus = 15) + user.do_attack_animation(target, ATTACK_EFFECT_DISARM) + playsound(src, 'sound/items/weapons/rapierhit.ogg', 50, TRUE) + var/datum/status_effect/stacking/saw_bleed/bloodletting/status = target.has_status_effect(/datum/status_effect/stacking/saw_bleed/bloodletting) + if(!status) + target.apply_status_effect(/datum/status_effect/stacking/saw_bleed/bloodletting, 6) + else + status.add_stacks(6) + +#undef ATTACK_STRIKE +#undef ATTACK_SLICE +#undef ATTACK_CUT +*/ diff --git a/modular_darkpack/modules/powers/code/discipline/temporis/temporis_effects.dm b/modular_darkpack/modules/powers/code/discipline/temporis/temporis_effects.dm index d1be6f5b3570..401e9716f752 100644 --- a/modular_darkpack/modules/powers/code/discipline/temporis/temporis_effects.dm +++ b/modular_darkpack/modules/powers/code/discipline/temporis/temporis_effects.dm @@ -22,3 +22,7 @@ /obj/effect/temporis/clothos_gift effect_alpha = 155 effect_fadeout = 0.5 SECONDS + +/obj/effect/temporis/weskar + effect_alpha = 155 + effect_fadeout = 0.3 SECONDS diff --git a/tgstation.dme b/tgstation.dme index 4eb831f486e7..85c40ff3921b 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -7296,6 +7296,11 @@ #include "modular_darkpack\modules\looc\code\verbs.dm" #include "modular_darkpack\modules\mannequin\code\mannequin_subtypes.dm" #include "modular_darkpack\modules\mapping_helpers\code\viewport_helper.dm" +#include "modular_darkpack\modules\martial\artdefines.dm" +#include "modular_darkpack\modules\martial\cqb.dm" +#include "modular_darkpack\modules\martial\kungfu.dm" +#include "modular_darkpack\modules\martial\streetboxing.dm" +#include "modular_darkpack\modules\martial\swords.dm" #include "modular_darkpack\modules\masquerade\code\blood_hunt_skull.dm" #include "modular_darkpack\modules\masquerade\code\human.dm" #include "modular_darkpack\modules\masquerade\code\logging_machine.dm"