diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm index 7aeffdbd2046..49907949dd9b 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm @@ -110,3 +110,6 @@ #define MOVABLE_SAY_QUOTE_MESSAGE 1 #define MOVABLE_SAY_QUOTE_MESSAGE_SPANS 2 #define MOVABLE_SAY_QUOTE_MESSAGE_MODS 3 + +/// from /datum/thrownthing/tick() +#define COMSIG_MOVABLE_THROW_TICK "movable_throw_tick" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm index 6f7be1b2a0a9..632acf561ab3 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm @@ -85,6 +85,8 @@ ///from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone, blocked, wound_bonus, bare_wound_bonus, sharpness, attack_direction) #define COMSIG_MOB_APPLY_DAMAGE "mob_apply_damage" + /// Cancels incoming damage. + #define COMPONENT_NO_APPLY_DAMAGE (1<<0) ///from adjust procs and bodypart heal_damage(): (amount, damtype) #define COMSIG_MOB_APPLY_HEALING "mob_apply_healing" ///from base of /mob/living/attack_alien(): (user) diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm index 198ceffff0f5..7435b2142e16 100644 --- a/code/controllers/subsystem/throwing.dm +++ b/code/controllers/subsystem/throwing.dm @@ -94,6 +94,8 @@ SUBSYSTEM_DEF(throwing) last_move = world.time + SEND_SIGNAL(AM, COMSIG_MOVABLE_THROW_TICK) + //calculate how many tiles to move, making up for any missed ticks. var/tilestomove = CEILING(min(((((world.time+world.tick_lag) - start_time + delayed_time) * speed) - (dist_travelled ? dist_travelled : -1)), speed*MAX_TICKS_TO_MAKE_UP) * (world.tick_lag * SSthrowing.wait), 1) while (tilestomove-- > 0) diff --git a/code/datums/martial.dm b/code/datums/martial.dm index 221559fad591..8f20d7be0cae 100644 --- a/code/datums/martial.dm +++ b/code/datums/martial.dm @@ -18,10 +18,6 @@ ///current thing being targetted for combos, switches if the user hits a different opponent var/current_target var/datum/martial_art/base // The permanent style. This will be null unless the martial art is temporary - ///chance to deflect bullets - var/deflection_chance = 0 - ///check for if deflected bullets should be destroyed (false) or redirected (true) - var/reroute_deflection = FALSE ///chance for the martial art to block a melee attack when throw is on var/block_chance = 0 ///used for CQC's restrain combo @@ -40,6 +36,8 @@ var/list/gun_exceptions = list() ///list of traits given to the martial art user var/list/martial_traits = list() + ///the mob that uses this martial art + var/mob/living/martial_owner /** * martial art specific disarm attacks @@ -156,7 +154,7 @@ * gives the user the martial art, if it's a temporary one it will only temporarily override an older martial art rather than replacing it * unless the current art won't allow a temporary override */ -/datum/martial_art/proc/teach(mob/living/carbon/human/H,make_temporary=0) +/datum/martial_art/proc/teach(mob/living/carbon/human/H, make_temporary=0) if(!istype(H) || !H.mind) return FALSE if(H.mind.martial_art) @@ -173,6 +171,7 @@ if(LAZYLEN(martial_traits)) H.add_traits(martial_traits, id) H.mind.martial_art = src + martial_owner = H if(no_guns) for(var/mob/living/simple_animal/hostile/guardian/guardian in H.hasparasites()) guardian.stats.ranged = FALSE @@ -201,6 +200,7 @@ if(!istype(H) || !H.mind || H.mind.martial_art != src) return on_remove(H) + martial_owner = null H.mind.martial_art = null if(base) base.teach(H) diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm index 7782c2d62280..fd4cf7237c3f 100644 --- a/code/datums/martial/sleeping_carp.dm +++ b/code/datums/martial/sleeping_carp.dm @@ -1,155 +1,299 @@ -#define WRIST_WRENCH_COMBO "DD" -#define BACK_KICK_COMBO "HG" -#define STOMACH_KNEE_COMBO "GH" -#define HEAD_KICK_COMBO "DHH" -#define ELBOW_DROP_COMBO "HDHDH" +/// The rate at which focus decays per second. +#define FOCUS_DECAY_RATE -5 +/// Delay before focus starts decaying over time. +#define FOCUS_DECAY_COOLDOWN 2 SECONDS /datum/martial_art/the_sleeping_carp name = "The Sleeping Carp" id = MARTIALART_SLEEPINGCARP - deflection_chance = 0 //no block unless throwmode is on - reroute_deflection = TRUE no_guns = TRUE allow_temp_override = FALSE help_verb = /mob/living/carbon/human/proc/sleeping_carp_help - var/old_grab_state = null + martial_traits = list(TRAIT_REDUCED_DAMAGE_SLOWDOWN, TRAIT_STRONG_GRABBER) + /// Focus is built up by attacking and depletes over time or when taking damage. + var/focus_level = 0 + /// Temporary immunity to focus decay over time. + var/focus_decay_immunity = 0 + /// Image overlay when building up focus. + var/image/focus_shield -/datum/martial_art/the_sleeping_carp/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(findtext(streak,WRIST_WRENCH_COMBO)) - streak = "" - wristWrench(A,D) - return TRUE - if(findtext(streak,BACK_KICK_COMBO)) - streak = "" - backKick(A,D) - return TRUE - if(findtext(streak,STOMACH_KNEE_COMBO)) - streak = "" - kneeStomach(A,D) - return TRUE - if(findtext(streak,HEAD_KICK_COMBO)) - streak = "" - headKick(A,D) - return TRUE - if(findtext(streak,ELBOW_DROP_COMBO)) - streak = "" - elbowDrop(A,D) +/datum/martial_art/the_sleeping_carp/teach(mob/living/carbon/human/user, make_temporary) + . = ..() + user.faction.Add("carp") // fish are friends, not food! + RegisterSignal(user, COMSIG_ATOM_BULLET_ACT, PROC_REF(on_bullet_act)) + RegisterSignal(user, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays)) + RegisterSignal(user, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(on_apply_damage)) + RegisterSignal(user, COMSIG_MOB_CLICKON, PROC_REF(on_click)) + START_PROCESSING(SSfastprocess, src) + focus_shield = new('icons/effects/effects.dmi', icon_state = "topaz-barrier") + user.update_appearance(UPDATE_OVERLAYS) + +/datum/martial_art/the_sleeping_carp/remove(mob/living/carbon/human/user) + STOP_PROCESSING(SSfastprocess, src) + UnregisterSignal(user, list(COMSIG_ATOM_BULLET_ACT, COMSIG_ATOM_UPDATE_OVERLAYS, COMSIG_MOB_APPLY_DAMAGE, COMSIG_MOB_CLICKON)) + adjust_focus(-focus_level) // remove space protection and update overlays + user.faction.Remove("carp") + QDEL_NULL(focus_shield) + return ..() + +/datum/martial_art/the_sleeping_carp/proc/on_click(mob/living/carbon/human/user, atom/target, params) + SIGNAL_HANDLER + if(user.pulling) + return NONE + if(world.time < user.next_move) + return NONE + if(user == target) + return NONE + if(!(user.mobility_flags & MOBILITY_USE)) + return NONE + if(user.incapacitated()) + return NONE + if(user.CanReach(target)) + return NONE + var/list/modifiers = params2list(params) + if(modifiers[RIGHT_CLICK]) + lunge(user, target) + return COMSIG_MOB_CANCEL_CLICKON + return NONE + +/datum/martial_art/the_sleeping_carp/proc/lunge(mob/living/carbon/human/user, atom/target) + playsound(user, 'sound/weapons/punchmiss.ogg', 60, 1, -1) + ADD_TRAIT(user, TRAIT_IMMOBILIZED, MARTIALART_SLEEPINGCARP) + user.AddComponent(/datum/component/after_image, 0.5 SECONDS, 0.5, TRUE) + user.changeNext_move(CLICK_CD_MELEE * 2) // this gets reduced on a successful hit + user.apply_status_effect(STATUS_EFFECT_DODGING) + user.throw_at(target, 7, 3, user, TRUE, callback = CALLBACK(src, PROC_REF(end_lunge), user)) + +/datum/martial_art/the_sleeping_carp/proc/end_lunge(mob/living/carbon/human/user) + user.remove_status_effect(STATUS_EFFECT_DODGING) + var/datum/component/after_image = user.GetComponent(/datum/component/after_image) + if(after_image) + qdel(after_image) + REMOVE_TRAIT(user, TRAIT_IMMOBILIZED, MARTIALART_SLEEPINGCARP) + +/datum/martial_art/the_sleeping_carp/handle_throw(atom/hit_atom, mob/living/carbon/human/user, datum/thrownthing/throwingdatum) + if(ishuman(hit_atom)) + back_kick(user, hit_atom) return TRUE return FALSE -/datum/martial_art/the_sleeping_carp/proc/wristWrench(mob/living/carbon/human/A, mob/living/carbon/human/D) +/datum/martial_art/the_sleeping_carp/process(delta_time) + if(focus_decay_immunity > 0) + focus_decay_immunity -= delta_time SECONDS + else if(focus_level > 0) + adjust_focus(FOCUS_DECAY_RATE * delta_time) + +/datum/martial_art/the_sleeping_carp/proc/adjust_focus(amount = 0) + if(amount > 0) // take some time before losing focus + focus_decay_immunity = FOCUS_DECAY_COOLDOWN + if(focus_level <= 0) + ADD_TRAIT(martial_owner, TRAIT_RESISTLOWPRESSURE, MARTIALART_SLEEPINGCARP) // go hang out with space carp! + ADD_TRAIT(martial_owner, TRAIT_RESISTCOLD, MARTIALART_SLEEPINGCARP) + else if(focus_level > 0 && focus_level + amount <= 0) + REMOVE_TRAIT(martial_owner, TRAIT_RESISTLOWPRESSURE, MARTIALART_SLEEPINGCARP) + REMOVE_TRAIT(martial_owner, TRAIT_RESISTCOLD, MARTIALART_SLEEPINGCARP) + focus_level = clamp(focus_level + amount, 0, 100) + martial_owner.update_appearance(UPDATE_OVERLAYS) + +/datum/martial_art/the_sleeping_carp/proc/on_update_overlays(atom/source, list/overlays) + focus_shield.alpha = (1 - (1 - focus_level/100)**2) * 255 + overlays += focus_shield + +/datum/martial_art/the_sleeping_carp/proc/on_apply_damage(mob/living/carbon/human/defender, damage = 0, damagetype = BRUTE, def_zone = null, blocked = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE, attack_direction = null) + if(focus_level <= 0) + return NONE + if(blocked >= 100) + return NONE + if(damage <= 0) + return NONE + if(defender.incapacitated()) + return NONE + var/effective_damage = damage * (damagetype == STAMINA ? 0.5 : 1) * (100 - blocked) / 100 + if(effective_damage > focus_level) + var/effective_block = 100 * focus_level / effective_damage + to_chat(defender, "Focus: [focus_level]") // remove this + adjust_focus(-focus_level) // this needs to be set to zero before calling apply_damage again or it causes an infinite loop + var/damage_taken = defender.apply_damage(damage, damagetype, def_zone, effective_block, wound_bonus, bare_wound_bonus, sharpness, attack_direction) + to_chat(defender, "Damage: [damage], Taken: [damage_taken], Armor: [blocked]%, Effective Block: [effective_block]%") // remove this + defender.visible_message(span_danger("[src] deflects some of the incoming damage!"), span_userdanger("You deflect some of the incoming damage!")) + else + adjust_focus(-effective_damage) + defender.visible_message(span_danger("[src] deflects the attack!"), span_userdanger("You deflect the attack!")) + return COMPONENT_NO_APPLY_DAMAGE + +/datum/martial_art/the_sleeping_carp/proc/on_bullet_act(mob/living/carbon/human/defender, obj/projectile/incoming, def_zone) + if(!(defender.mobility_flags & MOBILITY_USE)) + return NONE + if(defender.dna?.check_mutation(HULK)) + return NONE + var/effective_damage = incoming.damage + if(defender.status_flags & GODMODE) // you won't take damage anyway, deflect because it looks cool + effective_damage = 0 + else + if(incoming.damage_type == STAMINA) + effective_damage *= 0.5 + effective_damage *= (100 - defender.getarmor(def_zone, incoming.armor_flag)) / 100 + if(effective_damage > focus_level) // can't block the full damage, no reflection + return NONE + if(!incoming.nodamage) // only lose focus if it was actually going to do real damage + adjust_focus(-effective_damage) + defender.visible_message(span_danger("[defender] deflects the projectile!"), span_userdanger("You deflect the projectile!")) + playsound(defender, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1) + incoming.firer = defender + if(incoming.hitscan) + incoming.store_hitscan_collision(incoming.trajectory.copy_to()) + incoming.setAngle(rand(0, 360))//SHING + return BULLET_ACT_FORCE_PIERCE + +/datum/martial_art/the_sleeping_carp/proc/wrist_wrench(mob/living/carbon/human/A, mob/living/carbon/human/D) if(!D.stat && !D.IsStun() && !D.IsParalyzed()) log_combat(A, D, "wrist wrenched (Sleeping Carp)") + if(A.pulling == D) + A.stop_pulling() A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) D.visible_message(span_warning("[A] grabs [D]'s wrist and wrenches it sideways!"), \ span_userdanger("[A] grabs your wrist and violently wrenches it to the side!")) playsound(get_turf(A), 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + var/obj/item/bodypart/chosen_arm = D.get_active_hand() D.emote("scream") D.dropItemToGround(D.get_active_held_item()) - D.apply_damage(A.get_punchdamagehigh() / 2, BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM), wound_bonus = CANT_WOUND) //5 damage - D.Stun(60) + D.apply_damage(A.get_punchdamagehigh() / 2, BRUTE, chosen_arm.body_zone, wound_bonus = CANT_WOUND) //5 damage + D.Immobilize(1 SECONDS) + A.Immobilize(0.4 SECONDS) + adjust_focus(10) return TRUE + return basic_hit(A,D) +// it's called a back kick because your leg is moving opposite the direction your foot is pointing, hitting with the back of your heel +/datum/martial_art/the_sleeping_carp/proc/back_kick(mob/living/carbon/human/A, mob/living/carbon/human/D) + if((A.mobility_flags & MOBILITY_STAND) && (D.mobility_flags & MOBILITY_STAND)) + log_combat(A, D, "back kicked (Sleeping Carp)") + A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) + D.visible_message( + span_warning("[A] spins around and kicks [D] in the head!"), + span_userdanger("[A] spins around and kicks you in the jaw!"), + ) + var/turf/target_turf = get_edge_target_turf(D, get_dir(A, D)) + playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1) + D.throw_at(target_turf, 2, 2, A, TRUE) // throw them back a few tiles + D.apply_damage(A.get_punchdamagehigh() + 10, A.dna.species.attack_type, BODY_ZONE_HEAD, wound_bonus = CANT_WOUND) //20 damage + D.Knockdown(CLICK_CD_MELEE) // short knockdown + A.changeNext_move(CLICK_CD_MELEE * 1.5) // heavy attack, longer cooldown + A.emote("flip") + adjust_focus(20) + return TRUE return basic_hit(A,D) -/datum/martial_art/the_sleeping_carp/proc/backKick(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!D.stat && !D.IsParalyzed()) - if(A.dir == D.dir) - log_combat(A, D, "back-kicked (Sleeping Carp)") - A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) - D.visible_message(span_warning("[A] kicks [D] in the back!"), \ - span_userdanger("[A] kicks you in the back, making you stumble and fall!")) - step_to(D,get_step(D,D.dir),1) - D.Paralyze(80) - playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1) - return TRUE +/datum/martial_art/the_sleeping_carp/proc/suplex(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(A.pulling == D) + log_combat(A, D, "suplexed (Sleeping Carp)") + var/turf/target_turf = get_step(get_turf(A), turn(get_dir(A, D), 180)) + if(target_turf.density) + D.forceMove(get_turf(A)) else - log_combat(A, D, "missed a back-kick (Sleeping Carp) on") - D.visible_message(span_warning("[A] tries to kick [D] in the back, but misses!"), \ - span_userdanger("[A] tries to kick you in the back, but misses!")) + D.forceMove(target_turf) + D.Knockdown(1.5 SECONDS) + D.apply_damage(A.get_punchdamagehigh() + focus_level / 5, A.dna.species.attack_type, BODY_ZONE_HEAD, wound_bonus = CANT_WOUND) //10-30 damage + D.visible_message( + span_warning("[A] suplexes [D] into [target_turf]!"), + span_userdanger("[A] suplexes you into [target_turf]!"), + ) + playsound(target_turf, 'sound/effects/meteorimpact.ogg', 60, 1) + playsound(A, 'sound/effects/gravhit.ogg', 20, 1) + A.face_atom(D) + A.changeNext_move(CLICK_CD_MELEE) + adjust_focus(20) + return TRUE return basic_hit(A,D) -/datum/martial_art/the_sleeping_carp/proc/kneeStomach(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!D.stat && !D.IsParalyzed()) +/datum/martial_art/the_sleeping_carp/proc/stomach_knee(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!D.stat && !D.IsParalyzed() && A.pulling == D) + A.stop_pulling() log_combat(A, D, "stomach kneed (Sleeping Carp)") A.do_attack_animation(D, ATTACK_EFFECT_KICK) - D.visible_message(span_warning("[A] knees [D] in the stomach!"), \ - span_userdanger("[A] winds you with a knee in the stomach!")) - D.audible_message("[D] gags!") + D.visible_message( + span_warning("[A] knees [D] in the stomach!"), + span_userdanger("[A] winds you with a knee in the stomach!"), + ) + D.emote("gasp") D.losebreath += 3 - D.Stun(40) - playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1) - return TRUE - return basic_hit(A,D) - -/datum/martial_art/the_sleeping_carp/proc/headKick(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!D.stat && !D.IsParalyzed()) - log_combat(A, D, "head kicked (Sleeping Carp)") - A.do_attack_animation(D, ATTACK_EFFECT_KICK) - D.visible_message(span_warning("[A] kicks [D] in the head!"), \ - span_userdanger("[A] kicks you in the jaw!")) - D.apply_damage(A.get_punchdamagehigh() + 10, A.dna.species.attack_type, BODY_ZONE_HEAD, wound_bonus = CANT_WOUND) //20 damage - D.drop_all_held_items() + D.Immobilize(0.5 SECONDS) + D.apply_damage(30, OXY, BODY_ZONE_CHEST, wound_bonus = CANT_WOUND) playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1) - D.Stun(80) + adjust_focus(20) return TRUE return basic_hit(A,D) -/datum/martial_art/the_sleeping_carp/proc/elbowDrop(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!(D.mobility_flags & MOBILITY_STAND)) +/datum/martial_art/the_sleeping_carp/proc/elbow_drop(mob/living/carbon/human/A, mob/living/carbon/human/D) + if((A.mobility_flags & MOBILITY_STAND) && !(D.mobility_flags & MOBILITY_STAND)) + var/dunk_damage = A.get_punchdamagehigh() + focus_level // deal damage based on your focus level + punch damage, get dunked on + adjust_focus(-focus_level) // spend all your focus on one big hit, you get some back later log_combat(A, D, "elbow dropped (Sleeping Carp)") - var/dunk_damage = A.get_punchdamagehigh() * 3 + 20 //50 damage, get dunked on A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) - D.visible_message(span_warning("[A] elbow drops [D]!"), \ - span_userdanger("[A] piledrives you with their elbow!")) + D.visible_message( + span_warning("[A] elbow drops [D]!"), + span_userdanger("[A] piledrives you with [A.p_their()] elbow!"), + ) if(D.stat) D.death() //FINISH HIM! + adjust_focus(50) + else + adjust_focus(30) + A.emote("flip") + A.forceMove(get_turf(D)) + A.Immobilize(0.5 SECONDS) + A.changeNext_move(CLICK_CD_MELEE * 2) // take some time to recover D.apply_damage(dunk_damage, A.dna.species.attack_type, BODY_ZONE_CHEST, wound_bonus = CANT_WOUND) + playsound(get_turf(D), 'sound/effects/wounds/crack2.ogg', 75, 1, -1) // ouch, that's gotta hurt playsound(get_turf(D), 'sound/weapons/punch1.ogg', 75, 1, -1) return TRUE return basic_hit(A,D) /datum/martial_art/the_sleeping_carp/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(A!=D) // 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 - old_grab_state = A.grab_state - D.grabbedby(A, 1) - if(old_grab_state == GRAB_PASSIVE) - D.drop_all_held_items() - A.setGrabState(GRAB_AGGRESSIVE) //Instant agressive grab if on grab intent - log_combat(A, D, "grabbed", addition="aggressively") - D.visible_message(span_warning("[A] violently grabs [D]!"), \ - span_userdanger("[A] violently grabs you!")) + if(A.pulling == D && D.stat != DEAD) + wrist_wrench(A, D) return TRUE - else - return FALSE + return FALSE /datum/martial_art/the_sleeping_carp/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - add_to_streak("H",D) - if(check_streak(A,D)) - return TRUE + if(D.stat == DEAD) + return basic_hit(A, D) + if(!(D.mobility_flags & MOBILITY_STAND)) + return elbow_drop(A, D) + if(A.pulling == D) + return suplex(A, D) + return basic_hit(A, D) + +/datum/martial_art/the_sleeping_carp/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(D.stat == DEAD) + return FALSE + if(A.pulling == D) + return stomach_knee(A, D) + else + return back_kick(A, D) + +/datum/martial_art/the_sleeping_carp/basic_hit(mob/living/carbon/human/A, mob/living/carbon/human/D) A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) var/atk_verb = pick("punches", "kicks", "chops", "hits", "slams") - var/harm_damage = A.get_punchdamagehigh() + rand(0,5) //10-15 damage - D.visible_message(span_danger("[A] [atk_verb] [D]!"), \ - span_userdanger("[A] [atk_verb] you!")) - D.apply_damage(harm_damage, BRUTE, wound_bonus = CANT_WOUND) + var/harm_damage = A.get_punchdamagehigh() + focus_level / 20 //10-15 damage + D.visible_message( + span_danger("[A] [atk_verb] [D]!"), + span_userdanger("[A] [atk_verb] you!"), + ) + D.apply_damage(harm_damage, A.dna.species.attack_type, wound_bonus = CANT_WOUND) playsound(get_turf(D), 'sound/weapons/punch1.ogg', 25, 1, -1) - if(prob(D.getBruteLoss()) && (D.mobility_flags & MOBILITY_STAND)) - D.visible_message(span_warning("[D] stumbles and falls!"), span_userdanger("The blow sends you to the ground!")) - D.Paralyze(80) + if(D.getBruteLoss() > 90 && (D.mobility_flags & MOBILITY_STAND)) + var/datum/brain_trauma/mild/concussion/ouchie = new() + D.gain_trauma(ouchie, TRAUMA_RESILIENCE_BASIC) + D.visible_message(span_warning("[A] knocks [D] out cold with an uppercut!"), span_userdanger("[A] knocks you out cold!")) + D.Unconscious(2 SECONDS) // short knockout, enough time for an elbow drop + D.Knockdown(3 SECONDS) + A.changeNext_move(CLICK_CD_MELEE * 0.75) // basic hits are faster log_combat(A, D, "[atk_verb] (Sleeping Carp)") + if(D.stat != DEAD) + adjust_focus(10) return TRUE - -/datum/martial_art/the_sleeping_carp/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - add_to_streak("D",D) - if(check_streak(A,D)) - return TRUE - return ..() - /mob/living/carbon/human/proc/sleeping_carp_help() set name = "Recall Teachings" set desc = "Remember the martial techniques of the Sleeping Carp clan." @@ -157,13 +301,14 @@ to_chat(usr, "You retreat inward and recall the teachings of the Sleeping Carp...") - to_chat(usr, "[span_notice("Wrist Wrench")]: Disarm Disarm. Forces opponent to drop item in hand.") - to_chat(usr, "[span_notice("Back Kick")]: Harm Grab. Opponent must be facing away. Knocks down.") - to_chat(usr, "[span_notice("Stomach Knee")]: Grab Harm. Knocks the wind out of opponent and stuns.") - to_chat(usr, "[span_notice("Head Kick")]: Disarm Harm Harm. Decent damage, forces opponent to drop item in hand.") - to_chat(usr, "[span_notice("Elbow Drop")]: Harm Disarm Harm Disarm Harm. Opponent must be on the ground. Deals huge damage, instantly kills anyone in critical condition.") + to_chat(usr, "[span_notice("Focus")]: Landing hits builds up focus, which deflects incoming damage and increases the power of some attacks.") + to_chat(usr, "[span_notice("Wrist Wrench")]: Grab twice. Forces opponent to drop item in hand and immobilizes for a short time.") + to_chat(usr, "[span_notice("Flying Kick")]: Left click to perform a flying back kick, dealing heavy damage and sending you off balance for a moment.") + to_chat(usr, "[span_notice("Suplex")]: Left click while grabbing. Decent damage scaling with focus, knocks opponent onto the ground.") + to_chat(usr, "[span_notice("Stomach Knee")]: Right click while grabbing. Causes temporary suffocation.") + to_chat(usr, "[span_notice("Elbow Drop")]: Left click a downed opponent. Deals damage based on your focus, instantly kills anyone in critical condition.") - to_chat(usr, "You will only deflect projectiles while throwmode is enabled.") + to_chat(usr, "You will only deflect projectiles when you have enough focus to deflect all incoming damage.") /obj/item/melee/bostaff name = "bo staff" @@ -211,6 +356,9 @@ if(C.stat) to_chat(user, span_warning("It would be dishonorable to attack a foe while they cannot retaliate.")) return + var/datum/martial_art/the_sleeping_carp/carp = user.mind?.has_martialart(MARTIALART_SLEEPINGCARP) + if(carp) + carp.adjust_focus(15) // synergy! var/list/modifiers = params2list(params) if(HAS_TRAIT(src, TRAIT_WIELDED) && !(modifiers && modifiers[RIGHT_CLICK])) // right click to harm if(!ishuman(target)) diff --git a/code/datums/martial/ultra_violence.dm b/code/datums/martial/ultra_violence.dm index 4bfc6eb4874b..b216f9ca19ce 100644 --- a/code/datums/martial/ultra_violence.dm +++ b/code/datums/martial/ultra_violence.dm @@ -13,8 +13,6 @@ name = "Ultra Violence" id = MARTIALART_ULTRAVIOLENCE no_guns = TRUE - deflection_chance = 0 - reroute_deflection = TRUE help_verb = /mob/living/carbon/human/proc/ultra_violence_help gun_exceptions = list(/obj/item/gun/ballistic/revolver/ipcmartial) no_gun_message = "This gun is not compliant with Ultra Violence standards." diff --git a/code/modules/antagonists/eldritch_cult/transmutations/mind_transmutations.dm b/code/modules/antagonists/eldritch_cult/transmutations/mind_transmutations.dm index b407e15cc2f9..65c8482474ea 100644 --- a/code/modules/antagonists/eldritch_cult/transmutations/mind_transmutations.dm +++ b/code/modules/antagonists/eldritch_cult/transmutations/mind_transmutations.dm @@ -17,7 +17,26 @@ /datum/martial_art/Absolute_Spacial_Domination id = MARTIALART_SPACIALLDOMINANCE - deflection_chance = 100 + +/datum/martial_art/Absolute_Spacial_Domination/teach(mob/living/carbon/human/user, make_temporary) + . = ..() + RegisterSignal(user, COMSIG_ATOM_BULLET_ACT, PROC_REF(on_bullet_act)) + +/datum/martial_art/Absolute_Spacial_Domination/remove(mob/living/carbon/human/user) + UnregisterSignal(user, COMSIG_ATOM_BULLET_ACT) + return ..() + +/datum/martial_art/Absolute_Spacial_Domination/proc/on_bullet_act(mob/living/carbon/human/defender, obj/projectile/incoming, def_zone) + if(!(defender.mobility_flags & MOBILITY_USE)) + return NONE + if(defender.dna?.check_mutation(HULK)) + return NONE + defender.visible_message( + span_danger("[defender] deflects the projectile; [defender.p_they()] can't be hit with ranged weapons!"), + span_userdanger("You deflect the projectile!"), + ) + playsound(defender, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1) + return BULLET_ACT_BLOCK /datum/eldritch_transmutation/final/mind_final/on_finished_recipe(mob/living/user, list/atoms, loc) var/mob/living/carbon/human/H = user diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 62712be6e1e4..96daf8fafa13 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -71,10 +71,7 @@ I.pixel_x = initial(I.pixel_x) I.pixel_y = initial(I.pixel_y) I.transform = initial(I.transform) - //If() explanation: if we have a mind and a martial art that we can use, check if it has a block or deflect chance or it's sleeping carp - //Assuming any of that isnt true, then throw mode isnt helpful and it gets turned off. Otherwise, it stays on. - if(!(mind && mind.martial_art && mind.martial_art.can_use(src) && (mind.martial_art.deflection_chance || mind.martial_art.block_chance || mind.martial_art.id == "sleeping carp"))) - throw_mode_off() + throw_mode_off() return TRUE ..() diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index 2748e6211da2..a4e106049b92 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -2067,7 +2067,9 @@ GLOBAL_LIST_EMPTY(features_by_species) return TRUE /datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE, attack_direction = null) - SEND_SIGNAL(H, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness, attack_direction) // make sure putting wound_bonus here doesn't screw up other signals or uses for this signal) + if(SEND_SIGNAL(H, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone, blocked, wound_bonus, bare_wound_bonus, sharpness, attack_direction) & COMPONENT_NO_APPLY_DAMAGE) // make sure putting wound_bonus here doesn't screw up other signals or uses for this signal) + return FALSE + var/hit_percent = (100-(blocked+armor))/100 hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100 if(!damage || hit_percent <= 0) @@ -2117,7 +2119,7 @@ GLOBAL_LIST_EMPTY(features_by_species) if(H.buckled && istype(H.buckled, /obj/structure))//prevent buckling corpses to chairs to make indestructible projectile walls var/obj/structure/sitter = H.buckled sitter.take_damage(damage, damagetype) - return 1 + return damage * hit_percent /datum/species/proc/on_hit(obj/projectile/P, mob/living/carbon/human/H) // called when hit by a projectile diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 83496a66cc37..876399540bd0 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -48,28 +48,6 @@ if(spec_return) return spec_return - if(mind) - if(mind.martial_art && !incapacitated(FALSE, TRUE) && mind.martial_art.can_use(src) && (mind.martial_art.deflection_chance || ((mind.martial_art.id == "sleeping carp") && in_throw_mode))) //Some martial arts users can deflect projectiles! - if(prob(mind.martial_art.deflection_chance) || ((mind.martial_art.id == "sleeping carp") && in_throw_mode)) // special check if sleeping carp is our martial art and throwmode is on, deflect - if((mobility_flags & MOBILITY_USE) && dna && !dna.check_mutation(HULK)) //But only if they're otherwise able to use items, and hulks can't do it - if(!isturf(loc)) //if we're inside something and still got hit - P.force_hit = TRUE //The thing we're in passed the bullet to us. Pass it back, and tell it to take the damage. - loc.bullet_act(P) - return BULLET_ACT_HIT - if(mind.martial_art.deflection_chance >= 100) //if they can NEVER be hit, lets clue sec in ;) - visible_message(span_danger("[src] deflects the projectile; [p_they()] can't be hit with ranged weapons!"), span_userdanger("You deflect the projectile!")) - else - visible_message(span_danger("[src] deflects the projectile!"), span_userdanger("You deflect the projectile!")) - playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1) - if(!mind.martial_art.reroute_deflection) - return BULLET_ACT_BLOCK - else - P.firer = src - if(P.hitscan) - P.store_hitscan_collision(P.trajectory.copy_to()) - P.setAngle(rand(0, 360))//SHING - return BULLET_ACT_FORCE_PIERCE - if(!(P.original == src && P.firer == src)) //can't block or reflect when shooting yourself var/shield_check = check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration, P.damage_type) if(shield_check & SHIELD_DODGE) // skill issue, just dodge diff --git a/code/modules/mob/living/carbon/human/species_types/wy_synths.dm b/code/modules/mob/living/carbon/human/species_types/wy_synths.dm index 0dfdaad80e2f..af6cd3aadd05 100644 --- a/code/modules/mob/living/carbon/human/species_types/wy_synths.dm +++ b/code/modules/mob/living/carbon/human/species_types/wy_synths.dm @@ -182,6 +182,8 @@ /datum/species/wy_synth/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE, attack_direction = null) . = ..() + if(!.) + return var/hit_percent = (100-(blocked+armor))/100 if(damage * hit_percent * brutemod > 0) if(last_warned <= world.time) diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index c3ff97cb8bf6..2b10162730d2 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -9,10 +9,11 @@ standard 0 if fail */ /mob/living/proc/apply_damage(damage = 0, damagetype = BRUTE, def_zone = null, blocked = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE, attack_direction = null) - SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone) + if(SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness, attack_direction) & COMPONENT_NO_APPLY_DAMAGE) + return FALSE var/hit_percent = (100-blocked)/100 if(!damage || (hit_percent <= 0)) - return 0 + return FALSE switch(damagetype) if(BRUTE) adjustBruteLoss(damage * hit_percent) @@ -28,7 +29,7 @@ adjustStaminaLoss(damage * hit_percent) if(BRAIN) adjustOrganLoss(ORGAN_SLOT_BRAIN, damage * hit_percent) - return 1 + return damage * hit_percent /mob/living/proc/apply_damage_type(damage = 0, damagetype = BRUTE, required_status, forced) //like apply damage except it always uses the damage procs switch(damagetype) diff --git a/code/modules/mob/living/silicon/damage_procs.dm b/code/modules/mob/living/silicon/damage_procs.dm index c4f30635f746..756b7ccc70de 100644 --- a/code/modules/mob/living/silicon/damage_procs.dm +++ b/code/modules/mob/living/silicon/damage_procs.dm @@ -1,5 +1,7 @@ /mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE, attack_direction = null) + if(SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone) & COMPONENT_NO_APPLY_DAMAGE) + return FALSE var/hit_percent = (100-blocked)/100 if(!damage || (hit_percent <= 0)) return 0 @@ -8,7 +10,7 @@ adjustBruteLoss(damage * hit_percent) if(BURN) adjustFireLoss(damage * hit_percent) - return 1 + return damage * hit_percent /mob/living/silicon/apply_effect(effect = 0,effecttype = EFFECT_STUN, blocked = FALSE) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 4d26890bfc91..23dbfb9d95e0 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -121,15 +121,14 @@ Difficulty: Very Hard else alternating_dir_shots() -/mob/living/simple_animal/hostile/megafauna/colossus/proc/enrage(mob/living/L) - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.mind) - if(H.mind.martial_art && prob(H.mind.martial_art.deflection_chance)) - . = TRUE - if(H.mind) - if(H.dna.species == /datum/species/golem/sand) - . = TRUE +/mob/living/simple_animal/hostile/megafauna/colossus/proc/enrage(mob/living/carbon/target) + if(!iscarbon(target)) + return FALSE + if(target.dna?.species == /datum/species/golem/sand) + return TRUE + if(target.mind.has_martialart(MARTIALART_SPACIALLDOMINANCE)) + return TRUE + return FALSE /mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots() ranged_cooldown = world.time + 40 diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/stalwart.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/stalwart.dm index 2c87d5d02254..92bdd5963654 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/stalwart.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/stalwart.dm @@ -266,15 +266,14 @@ var/turf/E = get_step(src, d) shoot_projectile(E) -/mob/living/simple_animal/hostile/megafauna/stalwart/proc/enrage(mob/living/L) - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.mind) - if(H.mind.martial_art && prob(H.mind.martial_art.deflection_chance)) - . = TRUE - if(H.mind) - if(H.dna.species == /datum/species/golem/sand) - . = TRUE +/mob/living/simple_animal/hostile/megafauna/stalwart/proc/enrage(mob/living/carbon/target) + if(!iscarbon(target)) + return FALSE + if(target.dna?.species == /datum/species/golem/sand) + return TRUE + if(target.mind.has_martialart(MARTIALART_SPACIALLDOMINANCE)) + return TRUE + return FALSE /mob/living/simple_animal/hostile/megafauna/stalwart/death() . = ..() diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index c7fe80096652..0314317a6e6f 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ