diff --git a/code/__DEFINES/melee.dm b/code/__DEFINES/melee.dm index 79a42444763f..0fa552661fe4 100644 --- a/code/__DEFINES/melee.dm +++ b/code/__DEFINES/melee.dm @@ -12,6 +12,9 @@ #define MARTIALART_FLYINGFANG "flying fang" #define MARTIALART_HUNTERFU "hunterfu" #define MARTIALART_FRENZYGRAB "frenzy grabbing" +#define MARTIALART_PRETERNISSTEALTH "preternis stealth" +#define MARTIALART_EXPLOSIVEFIST "explosive fist" +#define MARTIALART_GARDENWARFARE "garden warfare" //Weapon stat defines diff --git a/code/_onclick/hud/blob_overmind.dm b/code/_onclick/hud/blob_overmind.dm index e028a89b4772..e2c1c0a5712d 100644 --- a/code/_onclick/hud/blob_overmind.dm +++ b/code/_onclick/hud/blob_overmind.dm @@ -175,3 +175,4 @@ using = new /obj/screen/blob/RelocateCore() using.screen_loc = ui_storage2 static_inventory += using + diff --git a/code/datums/martial.dm b/code/datums/martial.dm index 056d232b7704..0bb1ffb25dc4 100644 --- a/code/datums/martial.dm +++ b/code/datums/martial.dm @@ -209,4 +209,4 @@ /datum/martial_art/proc/on_remove(mob/living/carbon/human/H) if(help_verb) remove_verb(H, help_verb) - return + return \ No newline at end of file diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm index e3575748d999..5c3190d74e3e 100644 --- a/code/game/objects/items/granters.dm +++ b/code/game/objects/items/granters.dm @@ -419,6 +419,72 @@ name = "empty scroll" icon_state = "blankscroll" +/obj/item/book/granter/martial/preternis_stealth + martial = /datum/martial_art/stealth + name = "strange electronic board" + martialname = "Stealth" + desc = "A strange electronic board, containing some sort of software." + greet = "You have uploaded some combat modules into yourself. Your combos will now have special effects on your enemies, and mostly are not obvious to other people. \ + You can check what combos can you do, and their effect by using Refresh Data verb in Combat Modules tab." + icon = 'icons/obj/module.dmi' + icon_state = "cyborg_upgrade" + remarks = list("Processing data...") + +/obj/item/book/granter/martial/preternis_stealth/already_known(mob/user) + if(!ispreternis(user)) + to_chat(user, span_warning("You don't understand what to do with this strange electronic device.")) + return TRUE + return ..() + +/obj/item/book/granter/martial/preternis_stealth/onlearned(mob/living/carbon/user) + ..() + if(oneuse == TRUE) + desc = "It looks like it doesn't contain any data no more." + +/obj/item/book/granter/martial/garden_warfare + martial = /datum/martial_art/gardern_warfare + name = "mysterious scroll" + martialname = "Garden Warfare" + desc = "A scroll, filled with a tone of text. Looks like it says something about combat and... plants?" + greet = "You know the martial art of Garden Warfare! Now you control your body better, then other phytosians do, allowing you to extend vines from your body and impale people with splinters. \ + You can check what combos can you do, and their effect by using Remember the basics verb in Garden Warfare tab." + icon = 'icons/obj/wizard.dmi' + icon_state = "scroll2" + remarks = list("I didn't know that my body grows sprinklers...", "I am able to snatch people with vines? Interesting.", "Wow, strangling people is brutal.") ///Kill me please for this cringe + +/obj/item/book/granter/martial/garden_warfare/already_known(mob/user) + if(!ispodperson(user)) + to_chat(user, span_warning("You see that this scroll says something about natural abilitites of podpeople, but, unfortunately, you are not one of them.")) + return TRUE + return ..() + +/obj/item/book/granter/martial/garden_warfare/onlearned(mob/living/carbon/user) + ..() + if(oneuse == TRUE) + desc = "It's completely blank." + +/obj/item/book/granter/martial/explosive_fist + martial = /datum/martial_art/explosive_fist + name = "burnt scroll" + martialname = "Explosive Fist" + desc = "A burnt scroll, that glorifies plasmamen, and also says a lot things of explosions." + greet = "You know the martial art of Explosive Fist. Now your attacks deal brute and burn damage, while your combos are able to set people on fire, explode them, or all at once. \ + You can check what combos can you do, and their effect by using Remember the basics verb in Explosive Fist tab." + icon = 'icons/obj/wizard.dmi' + icon_state = "scroll2" + remarks = list("Set them on fire...", "Show the punny humans who is here the supreme race...", "Make them burn...", "Explosion are cool!") + +/obj/item/book/granter/martial/explosive_fist/already_known(mob/user) + if(!isplasmaman(user)) + to_chat(user, span_warning("It says about very dangerous things, that you would prefer not to know.")) + return TRUE + return ..() + +/obj/item/book/granter/martial/explosive_fist/onlearned(mob/living/carbon/user) + ..() + if(oneuse == TRUE) + desc = "It's completely blank." + // I did not include mushpunch's grant, it is not a book and the item does it just fine. diff --git a/yogstation.dme b/yogstation.dme index e40c9563d2f8..ef5d80f4c445 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -3351,6 +3351,9 @@ #include "yogstation\code\datums\diseases\advance\symptoms\confusion.dm" #include "yogstation\code\datums\diseases\advance\symptoms\heal.dm" #include "yogstation\code\datums\looping_sounds\darkspawn.dm" +#include "yogstation\code\datums\martial\explosive_fist.dm" +#include "yogstation\code\datums\martial\garden_warfare.dm" +#include "yogstation\code\datums\martial\stealth.dm" #include "yogstation\code\datums\mood_events\generic_positive_events.dm" #include "yogstation\code\datums\mutations\alcohol.dm" #include "yogstation\code\datums\mutations\extendoarm.dm" diff --git a/yogstation/code/datums/martial/explosive_fist.dm b/yogstation/code/datums/martial/explosive_fist.dm new file mode 100644 index 000000000000..ed008ab136bb --- /dev/null +++ b/yogstation/code/datums/martial/explosive_fist.dm @@ -0,0 +1,330 @@ +#define EXPLOSIVE_DISARM_COMBO "DD" + +#define DETONATE_COMBO "HHDH" +#define PRE_DETONATE_COMBO "HH" +#define ALMOST_DETONATE_COMBO "HHD" + +#define LIFEFORCE_TRADE_COMBO "DGDG" +#define PRE_LIFEFORCE_TRADE_COMBO "DG" +#define ALMOST_LIFEFORCE_TRADE_COMBO "DGD" + +#define IMMOLATE_COMBO "DHDG" +#define PRE_IMMOLATE_COMBO "DH" +#define ALMOST_IMMOLATE_COMBO "DHD" + + +/datum/martial_art/explosive_fist + name = "Explosive Fist" + id = MARTIALART_EXPLOSIVEFIST + help_verb = /mob/living/carbon/human/proc/explosive_fist_help + +/datum/martial_art/explosive_fist/can_use(mob/living/carbon/human/H) + return isplasmaman(H) + +/datum/martial_art/explosive_fist/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(A.a_intent == INTENT_GRAB && A!=D && (can_use(A))) // A!=D prevents grabbing yourself + add_to_streak("G",D) + if(check_streak(A,D)) //if a combo is made no grab upgrade is done + return TRUE + return FALSE + else + return FALSE + +/datum/martial_art/explosive_fist/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!can_use(A)) + return FALSE + add_to_streak("H",D) + if(check_streak(A,D)) + return TRUE + var/selected_zone = A.zone_selected + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected)) + var/brute_block = D.run_armor_check(affecting, MELEE, 0) + var/burn_block = D.run_armor_check(affecting, BOMB, 0) + A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) + playsound(get_turf(D), 'sound/effects/explosion1.ogg', 50, TRUE, -1) + D.apply_damage(10, BRUTE, selected_zone, brute_block) + D.apply_damage(10, BURN, selected_zone, burn_block) + D.visible_message(span_danger("[A] [A.dna.species.attack_verb]s [D]!"), \ + span_userdanger("[A] [A.dna.species.attack_verb]s you!")) + log_combat(A, D, "[A.dna.species.attack_verb]s(Explosive Fist)") + + +/datum/martial_art/explosive_fist/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!(can_use(A))) + return FALSE + add_to_streak("D",D) + if(check_streak(A,D)) + return TRUE + return FALSE + +/datum/martial_art/explosive_fist/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!can_use(A)) + return + if(findtext(streak, EXPLOSIVE_DISARM_COMBO)) + explosive_disarm(A,D) + return TRUE + if(findtext(streak, PRE_DETONATE_COMBO)) + detonate(A,D) + return TRUE + if(findtext(streak, PRE_LIFEFORCE_TRADE_COMBO)) + lifeforce_trade(A,D) + if(findtext(streak,PRE_IMMOLATE_COMBO)) + return immolate(A,D) + +/datum/martial_art/explosive_fist/proc/explosive_disarm(mob/living/carbon/human/A, mob/living/carbon/human/D) + var/selected_zone = A.zone_selected + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected)) + var/armor_block = D.run_armor_check(affecting, BOMB, 0) + D.apply_damage(18, BURN, selected_zone, armor_block) + + var/obj/item/bodypart/affecting_p = A.get_bodypart(BODY_ZONE_CHEST) // p - plasmamen + var/armor_block_p = A.run_armor_check(affecting_p, BOMB) + A.apply_damage(10, BURN, BODY_ZONE_CHEST, armor_block_p) + + D.Knockdown(3 SECONDS) + playsound(D, 'sound/effects/explosion1.ogg', 50, TRUE, -1) + A.do_attack_animation(D, ATTACK_EFFECT_DISARM) + log_combat(A, D, "blasts(Explosive Fist)") + D.visible_message(span_danger("[A] blasts [D]!"), \ + span_userdanger("[A] blasts you!")) + var/atom/throw_target = get_edge_target_turf(D, get_dir(A,D)) + D.throw_at(throw_target, rand(1,2), 7, A) + streak = "" + +/datum/martial_art/explosive_fist/proc/detonate(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(findtext(streak, DETONATE_COMBO)) + A.do_attack_animation(D, ATTACK_EFFECT_SMASH) + log_combat(A, D, "detonates(Explosive Fist)") + D.visible_message(span_danger("[A] detonates [D]!"), \ + span_userdanger("[A] detonates you!")) + explosion(get_turf(D), -1, 0, 2, 0, 0, 2) + D.IgniteMob() + playsound(D, 'sound/effects/explosion1.ogg', 50, TRUE, -1) + + var/obj/item/bodypart/affecting = A.get_bodypart(BODY_ZONE_CHEST) + var/armor_block = A.run_armor_check(affecting, BOMB) + A.apply_damage(15, BRUTE, BODY_ZONE_CHEST, armor_block) + streak = "" + + else if(findtext(streak, ALMOST_DETONATE_COMBO)) + var/selected_zone = A.zone_selected + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected)) + var/armor_block = D.run_armor_check(affecting, MELEE, 0) + A.do_attack_animation(D, ATTACK_EFFECT_DISARM) + playsound(D, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1) + var/current_stamina_damage = D.getStaminaLoss() + var/damage_to_deal = 55 + + if(current_stamina_damage > 50) ///We apply a stamina slowdown on our target, our do nothing! + damage_to_deal = 0 + D.apply_damage(damage_to_deal, STAMINA, selected_zone, armor_block) + D.visible_message(span_danger("[A] activates [D]!"), \ + span_userdanger("[A] activates you!")) + log_combat(A, D, "activates(Explosive Fist)") + D.adjust_fire_stacks(4) + + else + var/selected_zone = A.zone_selected + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected)) + var/brute_block = D.run_armor_check(affecting, MELEE, 0) + var/burn_block = D.run_armor_check(affecting, BOMB, 0) + A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) + playsound(D, 'sound/effects/explosion1.ogg', 50, TRUE, -1) + D.apply_damage(12, BRUTE, selected_zone, brute_block) + D.apply_damage(12, BURN, selected_zone, burn_block) + D.adjust_fire_stacks(2) + D.visible_message(span_danger("[A] primes [D]!"), \ + span_userdanger("[A] primes you!")) + log_combat(A, D, "primes(Explosive Fist)") + +/datum/martial_art/explosive_fist/proc/lifeforce_trade(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(findtext(streak, LIFEFORCE_TRADE_COMBO)) + if(A.get_item_by_slot(ITEM_SLOT_HEAD)) + A.do_attack_animation(D, ATTACK_EFFECT_SMASH) + playsound(get_turf(D), 'sound/weapons/cqchit2.ogg', 50, 1, -1) + + var/selected_zone = A.zone_selected + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected)) + var/brute_block = D.run_armor_check(affecting, MELEE, 0) + var/burn_block = D.run_armor_check(affecting, BOMB, 0) + D.apply_damage(25, BRUTE, selected_zone, brute_block) + D.apply_damage(25, BURN, selected_zone, burn_block) + + var/obj/item/bodypart/affecting_p = A.get_bodypart(BODY_ZONE_CHEST) + var/brute_block_p = A.run_armor_check(affecting_p, MELEE) + var/burn_block_p = A.run_armor_check(affecting_p, BOMB) + A.apply_damage(5, BRUTE, BODY_ZONE_CHEST, brute_block_p) + A.apply_damage(5, BURN, BODY_ZONE_CHEST, burn_block_p) + + D.visible_message(span_danger("[A] headbutts [D]!"), \ + span_userdanger("[A] headbutts you!")) + log_combat(A, D, "headbutts(Explosive Fist)") + streak = "" + else + if(A.grab_state < GRAB_NECK) + A.grab_state = GRAB_NECK + if(!(A.pulling == D)) + D.grabbedby(A, 1) + D.visible_message(span_danger("[A] violently grabs [D]'s neck!"), \ + span_userdanger("[A] violently grabs your neck!")) + log_combat(A, D, "grabs by the neck(Explosive Fist)") + playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, TRUE, -1) + streak = "" + A.adjust_fire_stacks(3) + D.adjust_fire_stacks(3) + A.IgniteMob() + D.IgniteMob() + proceed_lifeforce_trade(A, D) + + else if(findtext(streak, ALMOST_LIFEFORCE_TRADE_COMBO)) + A.do_attack_animation(D, ATTACK_EFFECT_DISARM) + playsound(get_turf(D), 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1) + + D.visible_message(span_danger("[A] staggers [D]!"), \ + span_userdanger("[A] staggers you!")) + log_combat(A, D, "staggers(Explosive Fist)") + + var/selected_zone = A.zone_selected + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(selected_zone)) + var/stamina_block = D.run_armor_check(affecting, MELEE, 0) + var/burn_block = D.run_armor_check(affecting, BOMB, 0) + D.apply_damage(20, STAMINA, selected_zone, stamina_block) + D.apply_damage(5, BURN, selected_zone, burn_block) + + if(!D.has_movespeed_modifier(MOVESPEED_ID_SHOVE)) /// We apply a more long shove slowdown if our target doesn't already have one + D.add_movespeed_modifier(MOVESPEED_ID_SHOVE, multiplicative_slowdown = SHOVE_SLOWDOWN_STRENGTH) + addtimer(CALLBACK(D, /mob/living/carbon/human/proc/clear_shove_slowdown), 4 SECONDS) + + ADD_TRAIT(D, TRAIT_POOR_AIM, "martial") + addtimer(CALLBACK(src, .proc/remove_stagger, D), 2 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE) + else + A.do_attack_animation(D, ATTACK_EFFECT_DISARM) + + var/selected_zone = A.zone_selected + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected)) + var/armor_block = D.run_armor_check(affecting, BOMB, 0) + D.apply_damage(20, BURN, selected_zone, armor_block) + + var/obj/item/bodypart/affecting_p = A.get_bodypart(BODY_ZONE_CHEST) + var/armor_block_p = A.run_armor_check(affecting_p, BOMB) + A.apply_damage(5, BURN, BODY_ZONE_CHEST, armor_block_p) + + D.visible_message(span_danger("[A] burns [D]!"), \ + span_userdanger("[A] burns you!")) + log_combat(A, D, "burns(Explosive Fist)") + +/datum/martial_art/explosive_fist/proc/immolate(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(findtext(streak,IMMOLATE_COMBO)) + if(A.get_item_by_slot(ITEM_SLOT_HEAD)) //No helmets??? + streak = "" + return FALSE + else + for(var/mob/living/target in view_or_range(2, A, "range")) + if(target == A) + continue + if(get_dist(get_turf(A), get_turf(target)) <= 1) + target.IgniteMob() ///If we are close, we ignite, if not - take 30 burn damage + else + target.adjustFireLoss(30) + + var/obj/item/bodypart/hed = D.get_bodypart(BODY_ZONE_HEAD) + var/armor_block = D.run_armor_check(hed, BOMB) + D.apply_damage(10, BURN, BODY_ZONE_HEAD, armor_block) + D.emote("scream") + D.blur_eyes(4) + + var/obj/item/bodypart/affecting_p = A.get_bodypart(BODY_ZONE_CHEST) + var/armor_block_p = A.run_armor_check(affecting_p, BOMB) + A.apply_damage(15, BURN, BODY_ZONE_CHEST, armor_block_p) + + A.visible_message(span_danger("[A] explodes violently!"), \ + span_userdanger("You unleash the flames from yourself!")) + log_combat(A, D, "immolates(Explosive Fist)") + playsound(get_turf(A), 'sound/effects/explosion1.ogg', 50, TRUE, -1) + + else if(findtext(streak,ALMOST_IMMOLATE_COMBO)) + for(var/mob/living/target in view_or_range(2, A, "range")) + target.adjust_fire_stacks(5) + var/selected_zone = A.zone_selected + var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(A.zone_selected)) + var/burn_block = target.run_armor_check(affecting, BOMB, 0) + var/brute_block = target.run_armor_check(affecting, MELEE, 0) + target.apply_damage(10, BURN, selected_zone, burn_block) + target.apply_damage(5, BRUTE, selected_zone, brute_block) + D.visible_message(span_danger("[A] primes [D]!"), \ + span_userdanger("[A] primes you!")) + log_combat(A, D, "primes(Explosive Fist)") + playsound(get_turf(D), 'sound/effects/explosion1.ogg', 50, TRUE, -1) + + + else + A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) + playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1) + + var/selected_zone = A.zone_selected + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected)) + var/armor_block = D.run_armor_check(affecting, BOMB, 0) + D.apply_damage(25, BURN, selected_zone, armor_block) + + var/obj/item/bodypart/affecting_p = A.get_bodypart(BODY_ZONE_CHEST) // p - plasmamen + var/armor_block_p = A.run_armor_check(affecting_p, BOMB) + A.apply_damage(5, BURN, BODY_ZONE_CHEST, armor_block_p) + + D.visible_message(span_danger("[A] burns [D]!"), \ + span_userdanger("[A] burns you!")) + log_combat(A, D, "burns(Explosive Fist)") + + return TRUE + +/datum/martial_art/explosive_fist/proc/proceed_lifeforce_trade(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!can_suck_life(A, D)) + return + if(!do_mob(A, D, 1 SECONDS)) + return + if(!can_suck_life(A, D)) + return + if(prob(35)) + var/message = pick("You feel your life force being drained!", "It hurts!", "You stare into [A]'s expressionless skull and see only fire and death.") + to_chat(D, span_userdanger(message)) + if(prob(25)) + D.emote("scream") + var/dam = 2 + D.adjustFireLoss(dam) + var/bruteloss = D.getBruteLoss() + var/fireloss = D.getFireLoss() + A.heal_overall_damage(bruteloss/2, fireloss/2, 0, CONSCIOUS, TRUE) + to_chat(A, span_notice("You drain lifeforce from [D]")) + proceed_lifeforce_trade(A, D) + +/datum/martial_art/explosive_fist/proc/can_suck_life(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!can_use(A)) + return + if(A.get_item_by_slot(ITEM_SLOT_HEAD)) + return FALSE + if(!A.pulling) + return FALSE + if(!(A.pulling == D)) + return FALSE + if(A.grab_state < GRAB_NECK) + return FALSE + if(A.stat == DEAD || A.stat == UNCONSCIOUS) + return FALSE + if(D.stat == DEAD || D.stat == UNCONSCIOUS) + return FALSE + return TRUE + +/datum/martial_art/explosive_fist/proc/remove_stagger(mob/living/carbon/human/D) + REMOVE_TRAIT(D, TRAIT_POOR_AIM, "martial") + +/mob/living/carbon/human/proc/explosive_fist_help() + set name = "Remember the basics" + set desc = "You try to remember some basic actions from the explosive fist art." + set category = "Explosive Fist" + to_chat(usr, "You try to remember some basic actions from the explosive fist art.") + + to_chat(usr, span_notice("Harm Intent Will deal 10 burn and 10 brute damage to people who you hit.")) + + to_chat(usr, "[span_notice("Explosive disarm")]: Disarm Disarm. Finishing this combo will deal 10 damage to you and 18 to your target, aswell as throwing your target away and knocking down for three seconds.") + to_chat(usr, "[span_notice("Detonate")]: Harm Harm Disarm Harm. Second strike will deal 12/12 brute/burn and apply 2 fire stacks to the target. Third strike will apply 4 fire stacks and deal some stamina damage if the target has less then 50 stamina damage. The final strike will ignite the target, make a light explosion and deal 15 damage to you.") + to_chat(usr, "[span_notice("Life force trade")]: Disarm Grab Disarm Grab. Second strike will deal 20 damage to the target and 5 damage to you. Third strike will deall 20 stamina and 5 burn damage to the target, and will make it unable to use ranged weapons for 2 second as well as a more long shove slowdown. Finishing the combo with a headwear on will just deal 25/25 brute/burn damage to the target, and if you don't wear a helmet, you will instantly grab the target by a neck, aswell as start to drain life from them.") + to_chat(usr, "[span_notice("Immolate")]: Disarm Disarm. Second strike will deal 25 burn damage to the target and 5 burn damage to you. Third strike will apply 5 fire stacks to EVERYONE in the range of 2 tiles. Finishing the combo will, if you don't wear any headwear, will deal 30 burn damage to anyone except you in the range of 2 tiles, or ignite them if they are close enough to you. You target will get additional 10 burn damage and get blurry vision.") \ No newline at end of file diff --git a/yogstation/code/datums/martial/garden_warfare.dm b/yogstation/code/datums/martial/garden_warfare.dm new file mode 100644 index 000000000000..ef1061d2796f --- /dev/null +++ b/yogstation/code/datums/martial/garden_warfare.dm @@ -0,0 +1,253 @@ +#define VINE_SNATCH_COMBO "DD" + +#define STRANGLE_COMBO "GGG" +#define PRE_STRANGLE_COMBO "GG" + +#define SPLINTER_COMBO "HHH" +#define PRE_SPLINTER_COMBO "HH" + +/datum/martial_art/gardern_warfare + name = "Garden Warfare" //I feel like that I deserve a bullet into my head for this names + id = MARTIALART_GARDENWARFARE + block_chance = 50 + help_verb = /mob/living/carbon/human/proc/gardern_warfare_help + var/datum/action/vine_snatch/vine_snatch = new /datum/action/vine_snatch() + var/current_combo + +/datum/martial_art/gardern_warfare/can_use(mob/living/carbon/human/H) + return ispodperson(H) + +/datum/martial_art/gardern_warfare/teach(mob/living/carbon/human/H,make_temporary=0) + if(..()) + vine_snatch.Grant(H) + H.dna.species.speedmod = 0 + +/datum/martial_art/gardern_warfare/on_remove(mob/living/carbon/human/H) + vine_snatch.Remove(H) + H.dna.species.speedmod = initial(H.dna.species.speedmod) + +/datum/martial_art/gardern_warfare/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!can_use(A)) + return FALSE + if(current_combo && current_combo != SPLINTER_COMBO) + current_combo = null + streak = "" + add_to_streak("H",D) + if(check_streak(A,D)) + return TRUE + return FALSE + +/datum/martial_art/gardern_warfare/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!(can_use(A))) + return FALSE + if(current_combo && current_combo != VINE_SNATCH_COMBO) + current_combo = null + streak = "" + add_to_streak("D",D) + if(check_streak(A,D)) + return TRUE + return FALSE + +/datum/martial_art/gardern_warfare/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(A.a_intent == INTENT_GRAB && A!=D && (can_use(A))) + if(current_combo && current_combo != STRANGLE_COMBO) + current_combo = null + streak = "" + add_to_streak("G",D) + if(check_streak(A,D)) + return TRUE + return FALSE + +/datum/martial_art/gardern_warfare/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!can_use(A)) + return + if(findtext(streak, VINE_SNATCH_COMBO)) + current_combo = VINE_SNATCH_COMBO + vine_mark(A,D) + return FALSE + if(findtext(streak, PRE_STRANGLE_COMBO)) + current_combo = STRANGLE_COMBO + strangle(A,D) + return FALSE ///Zamn + if(findtext(streak, PRE_SPLINTER_COMBO)) + current_combo = SPLINTER_COMBO + splinter_stab(A,D) + return TRUE + +/datum/martial_art/gardern_warfare/proc/vine_mark(mob/living/carbon/human/A, mob/living/carbon/human/D) + to_chat(A, span_notice("You mark [D] with a vine mark. Using vine snatch now will pull an item from their active hands to you, or knokdown them and pull them to you.")) + to_chat(D, span_danger("[A] marks you!")) + vine_snatch.marked_dude = D + vine_snatch.last_time_marked = world.time + streak = "" + +/datum/martial_art/gardern_warfare/proc/strangle(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(findtext(streak, STRANGLE_COMBO)) + streak = "" + ADD_TRAIT(D, TRAIT_MUTE, "martial") + block_chance = 25 + final_strangle(A,D) + block_chance = initial(block_chance) + REMOVE_TRAIT(D, TRAIT_MUTE, "martial") + streak = "" + else + D.visible_message(span_danger("[A] wraps a vine around [D]'s throat!"), \ + span_userdanger("[A] wraps a vine around your throat!")) + log_combat(A, D, "pre-strangles(Garden Warfare)") + + D.Stun(1 SECONDS) + D.adjustOxyLoss(10) + +/datum/martial_art/gardern_warfare/proc/splinter_stab(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(findtext(streak, SPLINTER_COMBO)) + A.do_attack_animation(D, ATTACK_EFFECT_SLASH) + playsound(get_turf(D), 'sound/weapons/slash.ogg', 50, TRUE, -1) + D.visible_message(span_danger("[A] impales [D]!"), \ + span_userdanger("[A] impales you!")) + log_combat(A, D, "impales(Garden Warfare)") + + var/selected_zone = A.zone_selected + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(selected_zone)) + var/armor_block = D.run_armor_check(affecting, MELEE, 30) + + D.apply_damage(20, BRUTE, selected_zone, armor_block, sharpness = SHARP_POINTY) + D.Stun(1 SECONDS) + + var/list/arms = list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM) + var/arm_zone = pick(arms) + arms -= arm_zone + var/obj/item/bodypart/affecting_arm = A.get_bodypart(ran_zone(arm_zone)) + if(!affecting_arm) + affecting_arm = A.get_bodypart(ran_zone(pick(arms))) + var/arm_armor_block = A.run_armor_check(affecting_arm, MELEE, 5) + + A.apply_damage(5, BRUTE, arm_zone, arm_armor_block) + + var/obj/item/splinter = new /obj/item/splinter(D) + D.embed_object(splinter, affecting, FALSE, FALSE, TRUE) + streak = "" + else + A.do_attack_animation(D, ATTACK_EFFECT_SLASH) + playsound(get_turf(D), 'sound/weapons/slash.ogg', 50, TRUE, -1) + D.visible_message(span_danger("[A] stabs [D]!"), \ + span_userdanger("[A] stabs you!")) + log_combat(A, D, "stabs(Garden Warfare)") + + var/selected_zone = A.zone_selected + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(selected_zone)) + var/armor_block = D.run_armor_check(affecting, MELEE, 30) + + D.apply_damage(15, BRUTE, selected_zone, armor_block, sharpness = SHARP_POINTY) + +/datum/martial_art/gardern_warfare/proc/final_strangle(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!can_strangle(A, D)) + return + if(!do_mob(A, D, 1 SECONDS)) + return + if(!can_strangle(A, D)) + return + D.adjustOxyLoss(10) + if(prob(35)) + to_chat(D, span_danger("You can't breath!")) + final_strangle(A,D) + +/datum/martial_art/gardern_warfare/proc/can_strangle(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!can_use(A)) + return FALSE + if(!A.pulling) + return FALSE + if(!(A.pulling == D)) + return FALSE + if(A.stat == DEAD || A.stat == UNCONSCIOUS) + return FALSE + if(D.stat == DEAD) + return FALSE + return TRUE + +/datum/action/vine_snatch + name = "Vine Snatch - using it while having a target, recently marked with a vine mark in the range of 2 tiles will pull an item in their active hands to you, or pull and knockdown them.." + icon_icon = 'icons/obj/changeling.dmi' + button_icon_state = "tentacle" + var/mob/living/carbon/human/marked_dude = null + var/last_time_marked = 0 + var/range = 2 + +/datum/action/vine_snatch/New() + . = ..() + last_time_marked = world.time + +/datum/action/vine_snatch/Trigger() + if(owner.incapacitated()) + to_chat(owner, span_warning("You can't use [name] while you're incapacitated.")) + return + if(!marked_dude) + to_chat(owner, span_warning("You can't use [name] while not having anyone marked.")) + return + if(world.time > last_time_marked + 3 SECONDS) + to_chat(owner, span_warning("Your mark has expired, you can't use [name].")) + return + if(get_dist(get_turf(owner),get_turf(marked_dude)) > range) + to_chat(owner, span_warning("Your target needs to be in a range of [range] titles, to be able to use [name].")) + return + to_chat(owner, span_notice("You throw a vine into [marked_dude]!")) + owner.Beam(marked_dude, "vine", time= 10, maxdistance = range) + var/obj/item/I = marked_dude.get_active_held_item() + if(I && !HAS_TRAIT(I, TRAIT_NODROP)) + marked_dude.visible_message(span_warning("[owner] hits [marked_dude] with a vine, pulling [I] out of their hands!"), \ + span_userdanger("[owner] hits you with a vine, pulling [I] out of your hands!")) + if(I && marked_dude.temporarilyRemoveItemFromInventory(I)) + I.forceMove(get_turf(owner)) + else + marked_dude.throw_at(get_step_towards(owner, marked_dude), 7, 2) + marked_dude.visible_message(span_warning("[owner] hits [marked_dude] with a vine, knocking them down and pulling them to themselfes!"), \ + span_userdanger("[owner] hits you with a vine, pulling you to themselfs!")) + marked_dude.Knockdown(3 SECONDS) + marked_dude = null + +/obj/item/splinter + name = "splinter" + desc = "It's sharp!" + throwforce = 3 + sharpness = SHARP_EDGED + embedding = list("embedded_pain_multiplier" = 3, "embed_chance" = 100, "embedded_fall_chance" = 0) + var/passive_damage = 3 + +/obj/item/splinter/on_embed_removal(mob/living/carbon/human/embedde) + qdel(src) + . = ..() + +/obj/item/splinter/embed_tick(mob/living/carbon/human/embedde, obj/item/bodypart/part) + part.receive_damage(passive_damage, wound_bonus=-30, sharpness = TRUE) + +/datum/martial_art/gardern_warfare/handle_counter(mob/living/carbon/human/user, mob/living/carbon/human/attacker) + if(!can_use(user)) + return + if(!user.in_throw_mode) + return + user.do_attack_animation(attacker, ATTACK_EFFECT_SLASH) + playsound(get_turf(attacker), 'sound/weapons/slash.ogg', 50, TRUE, -1) + attacker.visible_message(span_danger("[user] stabs [attacker]!"), \ + span_userdanger("[user] stabs you!")) + log_combat(user, attacker, "counterattacks(Garden Warfare)") + + var/selected_zone = user.zone_selected + var/obj/item/bodypart/affecting = attacker.get_bodypart(ran_zone(selected_zone)) + var/armor_block = attacker.run_armor_check(affecting, MELEE, 0) + + attacker.apply_damage(10, BRUTE, selected_zone, armor_block, sharpness = SHARP_EDGED) + + var/obj/item/splinter = new /obj/item/splinter(attacker) + attacker.embed_object(splinter, affecting, FALSE, FALSE, TRUE) + streak = "" + +/mob/living/carbon/human/proc/gardern_warfare_help() + set name = "Remember the basics" + set desc = "You try to remember some basic actions from the garden warfare art." + set category = "Garden Warfare" + to_chat(usr, "You try to remember some basic actions from the garden warfare art.") + + to_chat(usr, "[span_notice("Vine snatch")]: Disarm Disarm. Finishing this combo will mark the victim with a vine mark, allowing you to drag them or an item in their active hand by using ["Vine Snatch"] ability. The mark lasts only 3 seconds.") + to_chat(usr, "[span_notice("Strangle")]: Grab Grab Grab. The second grab will deal 10 oxyloss damage to the target, and finishing the combo will upgrade your grab, making it mute the target and deal 10 oxyloss damage per second.") + to_chat(usr, "[span_notice("Splinter stab")]: Harm harm Harm. The second attack will deal increased damage with 30 armor penetration, and finishing the combo will deal 20 damage with 30 armor penetration, while also embedding a splinter into the targets limb.") + + to_chat(usr, span_notice("Additionaly, you have a passive 50% block chance(25% if strangling someone), and having throw mode on will allow you to counterattack attackers, dealing them 10 damage and embedding a splinter into them.")) diff --git a/yogstation/code/datums/martial/stealth.dm b/yogstation/code/datums/martial/stealth.dm new file mode 100644 index 000000000000..162a1b9fce1f --- /dev/null +++ b/yogstation/code/datums/martial/stealth.dm @@ -0,0 +1,148 @@ +///hidden dagger +#define PRE_DAGGER_COMBO "HH" +#define DAGGER_COMBO "HHG" +///injection +#define PRE_INJECTION_COMBO "DH" +#define INJECTION_COMBO "DHD" +///fingergun +#define FINGERGUN_COMBO "HDD" + +/datum/martial_art/stealth + name = "Stealth" //Sorry for shitty name, I am bad at this + id = MARTIALART_PRETERNISSTEALTH + help_verb = /mob/living/carbon/human/proc/preternis_martial_help + +/datum/martial_art/stealth/can_use(mob/living/carbon/human/H) + return ispreternis(H) + +/datum/martial_art/stealth/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(A.a_intent == INTENT_GRAB && A!=D && (can_use(A))) // A!=D prevents grabbing yourself + add_to_streak("G",D) + if(check_streak(A,D)) //if a combo is made no grab upgrade is done + return TRUE + return FALSE + else + return FALSE + +/datum/martial_art/stealth/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!can_use(A)) + return FALSE + add_to_streak("H",D) + if(check_streak(A,D)) + return TRUE + return FALSE ///We need it work like a generic, non martial art attack + + +/datum/martial_art/stealth/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!(can_use(A))) + return FALSE + add_to_streak("D",D) + if(check_streak(A,D)) + return TRUE + return FALSE ///Same as with harm_act + +/datum/martial_art/stealth/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!can_use(A)) + return + if(findtext(streak, PRE_DAGGER_COMBO)) + return hidden_knife(A,D) + if(findtext(streak, PRE_INJECTION_COMBO)) + injection(A,D) + return TRUE + if(findtext(streak, FINGERGUN_COMBO)) + fingergun(A,D) + return FALSE + +/datum/martial_art/stealth/proc/hidden_knife(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(findtext(streak, DAGGER_COMBO)) + var/selected_zone = A.zone_selected + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(selected_zone)) + var/armor_block = D.run_armor_check(affecting, MELEE, armour_penetration = 40) + D.apply_damage(30, BRUTE, selected_zone, armor_block, sharpness = SHARP_EDGED) + to_chat(A, span_warning("You stab [D] with a hidden blade!")) + to_chat(D, span_userdanger("You are suddenly stabbed with a blade!")) + streak = "" + return TRUE + else + var/selected_zone = A.zone_selected + var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(selected_zone)) + var/armor_block = D.run_armor_check(affecting, MELEE, armour_penetration = 10) + D.apply_damage(20, STAMINA, affecting, armor_block) + D.apply_damage(5, BRUTE, affecting, armor_block) + return FALSE //Because it is a stealthy martial art, we need it to work like... a normal shove, so people nearby couldn't understand so easy that you know a martial art. + + +/datum/martial_art/stealth/proc/injection(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(findtext(streak, INJECTION_COMBO)) + D.reagents.add_reagent(/datum/reagent/toxin/sodium_thiopental, 8) + to_chat(A, span_warning("You inject sodium thiopental into [D]!")) + to_chat(D, span_notice("You feel a tiny prick.")) + streak = "" + else + D.reagents.add_reagent(/datum/reagent/toxin/cyanide, 5) + to_chat(A, span_warning("You inject cyanide into [D]!")) + to_chat(D, span_notice("You feel a tiny prick.")) + +/datum/martial_art/stealth/proc/fingergun(mob/living/carbon/human/A, mob/living/carbon/human/D) + var/obj/item/gun/ballistic/automatic/pistol/martial/gun = new /obj/item/gun/ballistic/automatic/pistol/martial (A) ///I don't check does the user have an item in a hand, because it is a martial art action, and to use it... you need to have a empty hand + gun.gun_owner = A + A.put_in_hands(gun) + to_chat(A, span_notice("You extract a hiden gun from your hand.")) + D.Paralyze(1 SECONDS) + streak = "" + +/obj/item/gun/ballistic/automatic/pistol/martial + desc = "A concelated version of a stechkin APS pistol, that comes with special Preternis upgrade modules." + can_suppress = TRUE + fire_sound_volume = 30 + lefthand_file = null ///We don't want it to be visible inhands + righthand_file = null + mag_type = /obj/item/ammo_box/magazine/m10mm/martial + var/mob/gun_owner + var/dying = FALSE + +/obj/item/gun/ballistic/automatic/pistol/martial/Initialize(mapload) + . = ..() + var/obj/item/suppressor/S = new(src) + install_suppressor(S) + ADD_TRAIT(src, TRAIT_NODROP, "martial") + +/obj/item/gun/ballistic/automatic/pistol/martial/eject_magazine(mob/user, display_message = TRUE, obj/item/ammo_box/magazine/tac_load = null) + return FALSE + +/obj/item/gun/ballistic/automatic/pistol/martial/insert_magazine(mob/user, obj/item/ammo_box/magazine/AM, display_message = TRUE) + return FALSE + +/obj/item/gun/ballistic/automatic/pistol/martial/attack_self(mob/living/user) + to_chat(user, span_notice("You decide that it isn't the best time to use [src]")) + qdel(src) + return + +/obj/item/gun/ballistic/automatic/pistol/martial/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) + if(!dying) + addtimer(CALLBACK(src, .proc/process_burst), 1 SECONDS) ///I, kinda, don't very understand what gun code does, but it seems to be OK. + dying = TRUE + . = ..() + +/obj/item/gun/ballistic/automatic/pistol/martial/proc/Die() + to_chat(gun_owner, span_warning("You hide [src].")) + qdel(src) + +/obj/item/ammo_box/magazine/m10mm/martial + max_ammo = 1 + +/mob/living/carbon/human/proc/preternis_martial_help() + set name = "Refresh Data" + set desc = "You try to remember some basic actions from your upgraded combat modules." + set category = "Combat Modules" + to_chat(usr, "You try to remember some basic actions from your upgraded combat modules.") + + to_chat(usr, "[span_notice("Hidden Blade")]: Harm Harm Grab. The second strike will deal 20 stamina and 5 brute damage, and finishing the combo will make you stab the victim with a hiden blade, dealing 30 brute damage.") + to_chat(usr, "[span_notice("Injection")]: Disarm Harm Disarm. The second and third attack will stealthy inject respectively 5 units of cyanide and 8 unites of sodium thiopental.") + to_chat(usr, "[span_notice("Finger gun")]: Harm Disarm Disarm. Finishing the combo will paralyze your target and place a stealthy version of a stechkin in your hand.") + +#undef PRE_DAGGER_COMBO +#undef DAGGER_COMBO +#undef PRE_INJECTION_COMBO +#undef INJECTION_COMBO +#undef FINGERGUN_COMBO \ No newline at end of file diff --git a/yogstation/code/modules/uplink/uplink_item.dm b/yogstation/code/modules/uplink/uplink_item.dm index 587aae6bcf5f..2b61fd4fbcb6 100644 --- a/yogstation/code/modules/uplink/uplink_item.dm +++ b/yogstation/code/modules/uplink/uplink_item.dm @@ -144,9 +144,31 @@ cost = 40 cant_discount = TRUE +/datum/uplink_item/race_restricted/garden_warfare + name = "Martial art scroll" + desc = "A special scroll with a martial art, that teaches phytosians of capabilities of their body." + cost = 13 + item = /obj/item/book/granter/martial/garden_warfare + restricted_species = list("pod") + +/datum/uplink_item/race_restricted/combat_modules + name = "Combat Modules Board" + desc = "An upgrade board, containing upgrades and programs for your melee attacks." + cost = 11 + item = /obj/item/book/granter/martial/preternis_stealth + restricted_species = list("preternis") + +/datum/uplink_item/race_restricted/explosive_fist_art + name = "Burned scroll" + desc = "An ancient scroll, containing a guide to an ancient plasmamen martial art." + cost = 14 + item = /obj/item/book/granter/martial/explosive_fist + restricted_species = list("plasmaman") + /datum/uplink_item/stealthy_weapons/camera_flash name = "Camera Flash" desc = "A camera with an upgraded flashbulb. Can be used much like a handheld flash except with a longer cooldown between uses, allowing the bulb to cool down; avoid burning out altogether." item = /obj/item/camera/tator cost = 4 - surplus = 15 \ No newline at end of file + surplus = 15 +