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