diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index 19c87f3b4e0..33b7853cfd0 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -338,13 +338,13 @@ GLOBAL_LIST_INIT(arm_zones, list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
#define COMPONENT_AUTOFIRE_SHOT_SUCCESS (1<<0)
/// Martial arts attack requested but is not available, allow a check for a regular attack.
-#define MARTIAL_ATTACK_INVALID -1
+#define MARTIAL_ATTACK_INVALID NONE
/// Martial arts attack happened but failed, do not allow a check for a regular attack.
-#define MARTIAL_ATTACK_FAIL FALSE
+#define MARTIAL_ATTACK_FAIL COMPONENT_SKIP_ATTACK
/// Martial arts attack happened and succeeded, do not allow a check for a regular attack.
-#define MARTIAL_ATTACK_SUCCESS TRUE
+#define MARTIAL_ATTACK_SUCCESS COMPONENT_CANCEL_ATTACK_CHAIN
/// IF an object is weak against armor, this is the value that any present armor is multiplied by
#define ARMOR_WEAKENED_MULTIPLIER 2
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
index 7ac77efcaa7..2b113f853da 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
@@ -249,5 +249,9 @@
/// Sent from a mob to their loc when starting to remove cuffs on itself
#define COMSIG_MOB_REMOVING_CUFFS "living_removing_cuffs"
-/// Sent as a reply to above from any atom that wishs to stop self-cuff removal
-#define COMSIG_MOB_BLOCK_CUFF_REMOVAL (1<<0)
+ /// Sent as a reply to above from any atom that wishs to stop self-cuff removal
+ #define COMSIG_MOB_BLOCK_CUFF_REMOVAL (1<<0)
+
+/// Sent to a mob grabbing another mob: (mob/living/grabbing)
+#define COMSIG_LIVING_GRAB "living_grab"
+ // Return COMPONENT_CANCEL_ATTACK_CHAIN / COMPONENT_SKIP_ATTACK_CHAIN to stop the grab
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
index 2023493ac95..6b63c1aaa9f 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
@@ -70,6 +70,8 @@
///from mind/transfer_to. Sent to the receiving mob.
#define COMSIG_MOB_MIND_TRANSFERRED_INTO "mob_mind_transferred_into"
+///from mind/transfer_from. Sent to the mob the mind is being transferred out of.
+#define COMSIG_MOB_MIND_TRANSFERRED_OUT_OF "mob_mind_transferred_out_of"
/// From /mob/proc/ghostize() Called when a mob sucessfully ghosts
#define COMSIG_MOB_GHOSTIZED "mob_ghostized"
diff --git a/code/__DEFINES/melee.dm b/code/__DEFINES/melee.dm
index df7320f5085..64fdfd1889e 100644
--- a/code/__DEFINES/melee.dm
+++ b/code/__DEFINES/melee.dm
@@ -2,7 +2,6 @@
#define MARTIALART_BOXING "boxing"
#define MARTIALART_CQC "CQC"
-#define MARTIALART_HUGS_OF_THE_GONDOLA "hugs of the gondola"
#define MARTIALART_KRAVMAGA "krav maga"
#define MARTIALART_MUSHPUNCH "mushroom punch"
#define MARTIALART_PLASMAFIST "plasma fist"
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index c907f45466d..3cb12ad8188 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -358,46 +358,26 @@
/atom/proc/CtrlClick(mob/user)
SEND_SIGNAL(src, COMSIG_CLICK_CTRL, user)
SEND_SIGNAL(user, COMSIG_MOB_CTRL_CLICKED, src)
+
var/mob/living/ML = user
if(istype(ML))
ML.pulled(src)
if(!can_interact(user))
return FALSE
-/mob/living/CtrlClick(mob/user)
+/mob/living/CtrlClick(mob/living/user)
if(!isliving(user) || !user.CanReach(src) || user.incapacitated())
return ..()
if(world.time < user.next_move)
return FALSE
- var/mob/living/user_living = user
- if(user_living.apply_martial_art(src, null, is_grab=TRUE) == MARTIAL_ATTACK_SUCCESS)
- user_living.changeNext_move(CLICK_CD_MELEE)
+ if(user.grab(src))
+ user.changeNext_move(CLICK_CD_MELEE)
return TRUE
return ..()
-
-/mob/living/carbon/human/CtrlClick(mob/user)
- if(!iscarbon(user) || !user.CanReach(src) || user.incapacitated())
- return ..()
-
- if(world.time < user.next_move)
- return FALSE
-
- if (ishuman(user))
- var/mob/living/carbon/human/human_user = user
- if(human_user.dna.species.grab(human_user, src, human_user.mind.martial_art))
- human_user.changeNext_move(CLICK_CD_MELEE)
- return TRUE
- else if(isalien(user))
- var/mob/living/carbon/alien/adult/alien_boy = user
- if(alien_boy.grab(src))
- alien_boy.changeNext_move(CLICK_CD_MELEE)
- return TRUE
- return ..()
-
/mob/proc/CtrlMiddleClickOn(atom/A)
if(check_rights_for(client, R_ADMIN))
client.toggle_tag_datum(A)
diff --git a/code/datums/brain_damage/special.dm b/code/datums/brain_damage/special.dm
index d9348f98f4b..651881292e9 100644
--- a/code/datums/brain_damage/special.dm
+++ b/code/datums/brain_damage/special.dm
@@ -245,14 +245,15 @@
/datum/brain_trauma/special/psychotic_brawling/on_gain()
..()
- psychotic_brawling = new(null)
+ psychotic_brawling = new()
+ psychotic_brawling.allow_temp_override = FALSE
if(!psychotic_brawling.teach(owner, TRUE))
to_chat(owner, span_notice("But your martial knowledge keeps you grounded."))
qdel(src)
/datum/brain_trauma/special/psychotic_brawling/on_lose()
..()
- psychotic_brawling.remove(owner)
+ psychotic_brawling.fully_remove(owner)
QDEL_NULL(psychotic_brawling)
/datum/brain_trauma/special/psychotic_brawling/bath_salts
diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm
index 6ccb6aa7fdc..2a23deec611 100644
--- a/code/datums/components/tackle.dm
+++ b/code/datums/components/tackle.dm
@@ -234,7 +234,7 @@
target.Knockdown(3 SECONDS)
target.adjust_staggered_up_to(STAGGERED_SLOWDOWN_LENGTH * 2, 10 SECONDS)
if(ishuman(target) && ishuman(user))
- INVOKE_ASYNC(human_sacker.dna.species, TYPE_PROC_REF(/datum/species, grab), human_sacker, human_target)
+ INVOKE_ASYNC(human_sacker, TYPE_PROC_REF(/mob/living, grab), human_sacker, human_target)
human_sacker.setGrabState(GRAB_PASSIVE)
if(50 to INFINITY) // absolutely BODIED
@@ -260,7 +260,7 @@
target.Knockdown(3 SECONDS)
target.adjust_staggered_up_to(STAGGERED_SLOWDOWN_LENGTH * 3, 10 SECONDS)
if(ishuman(target) && ishuman(user))
- INVOKE_ASYNC(human_sacker.dna.species, TYPE_PROC_REF(/datum/species, grab), human_sacker, human_target)
+ INVOKE_ASYNC(human_sacker, TYPE_PROC_REF(/mob/living, grab), human_sacker, human_target)
human_sacker.setGrabState(GRAB_AGGRESSIVE)
/**
diff --git a/code/datums/martial/_martial.dm b/code/datums/martial/_martial.dm
index 2af604cd8fe..e5dc7860b0b 100644
--- a/code/datums/martial/_martial.dm
+++ b/code/datums/martial/_martial.dm
@@ -1,20 +1,41 @@
/datum/martial_art
+ /// Player readable name of the martial art
var/name = "Martial Art"
- var/id = "" //ID, used by mind/has_martialart
+ /// ID of the martial art
+ var/id = ""
+ /// The streak of attacks the user has performed
var/streak = ""
+ /// The maximum length of streaks allowed
var/max_streak_length = 6
- var/current_target
- var/datum/martial_art/base // The permanent style. This will be null unless the martial art is temporary
+
+ /// The current mob associated with this martial art datum. Do not set directly.
+ VAR_PRIVATE/mob/living/holder
+ /// Weakref to the last mob we attacked, for determining when to reset streaks
+ VAR_PRIVATE/datum/weakref/current_target
+ /// Used for temporary martial arts.
+ /// This is a reference to the last martial art that was replaced by this one.
+ VAR_PRIVATE/datum/martial_art/base
+
+ /// Path to verb to display help text for this martial art.
var/help_verb
- var/allow_temp_override = TRUE //if this martial art can be overridden by temporary martial arts
- var/smashes_tables = FALSE //If the martial art smashes tables when performing table slams and head smashes
- var/datum/weakref/holder //owner of the martial art
- var/display_combos = FALSE //shows combo meter if true
- var/combo_timer = 6 SECONDS // period of time after which the combo streak is reset.
+ /// If TRUE, this martial art can be overridden and stored (via base) by other martial arts if deemed "temporary" via teach().
+ var/allow_temp_override = TRUE
+ /// If TRUE, this martial art smashes tables when performing table slams and head smashes
+ var/smashes_tables = FALSE
+ /// If TRUE, a combo meter will be displayed on the HUD for the current streak
+ var/display_combos = FALSE
+ /// The length of time until streaks are auto-reset.
+ var/combo_timer = 6 SECONDS
+ /// Timer ID for the combo reset timer.
var/timerid
- /// If set to true this style allows you to punch people despite being a pacifist (for instance Boxing, which does no damage)
+ /// If TRUE, this style allows you to punch people despite being a pacifist (IE: Boxing, which does no damage)
var/pacifist_style = FALSE
+/datum/martial_art/Destroy()
+ if(!isnull(holder))
+ remove(holder)
+ return ..()
+
/datum/martial_art/serialize_list(list/options, list/semvers)
. = ..()
@@ -25,78 +46,317 @@
SET_SERIALIZATION_SEMVER(semvers, "1.0.0")
return .
+/// Signal proc for [COMSIG_LIVING_UNARMED_ATTACK] to hook into the appropriate proc
+/datum/martial_art/proc/unarmed_strike(mob/living/source, atom/attack_target, proximity, modifiers)
+ SIGNAL_HANDLER
+
+ if(!proximity || !isliving(attack_target))
+ return NONE
+
+ if(HAS_TRAIT(attack_target, TRAIT_MARTIAL_ARTS_IMMUNE))
+ return NONE
+
+ if(!can_use(source))
+ return NONE
+
+ if(LAZYACCESS(modifiers, RIGHT_CLICK))
+ return disarm_act(source, attack_target)
+
+ if(source.combat_mode)
+ if(HAS_TRAIT(source, TRAIT_PACIFISM) && !pacifist_style)
+ return NONE
+
+ return harm_act(source, attack_target)
+
+ return help_act(source, attack_target)
+
+/// Signal proc for [COMSIG_LIVING_GRAB] to hook into the grab
+/datum/martial_art/proc/attempt_grab(mob/living/source, mob/living/grabbing)
+ SIGNAL_HANDLER
+
+ if(HAS_TRAIT(grabbing, TRAIT_MARTIAL_ARTS_IMMUNE))
+ return NONE
+
+ if(!source.can_unarmed_attack()) // For parity with unarmed attacks
+ return NONE
+
+ if(!can_use(source))
+ return NONE
+
+ return grab_act(source, grabbing)
+
+/**
+ * Called when help-intenting on someone
+ *
+ * What is checked going into this:
+ * Adjacency, [TRAIT_MARTIAL_ARTS_IMMUNE], attacker incapacitated, can_unarmed_attack, can_use
+ *
+ * What is NOT:
+ * check_block
+ *
+ * Arguments
+ * * mob/living/attacker - The mob attacking
+ * * mob/living/defender - The mob being attacked
+ *
+ * Returns
+ * * MARTIAL_ATTACK_INVALID - The attack is not valid, do normal unarmed attack
+ * * MARTIAL_ATTACK_FAIL - The attack is valid, but failed. No followup attack is made.
+ * * MARTIAL_ATTACK_SUCCESS - The attack is valid, and succeeded. No followup attack is made.
+ */
/datum/martial_art/proc/help_act(mob/living/attacker, mob/living/defender)
+ SHOULD_CALL_PARENT(FALSE)
+ PROTECTED_PROC(TRUE)
return MARTIAL_ATTACK_INVALID
+/**
+ * Called when disarm-intenting on someone
+ *
+ * What is checked going into this:
+ * Adjacency, [TRAIT_MARTIAL_ARTS_IMMUNE], attacker incapacitated, can_unarmed_attack, can_use
+ *
+ * What is NOT:
+ * check_block
+ *
+ * Arguments
+ * * mob/living/attacker - The mob attacking
+ * * mob/living/defender - The mob being attacked
+ *
+ * Returns
+ * * MARTIAL_ATTACK_INVALID - The attack is not valid, do normal unarmed attack
+ * * MARTIAL_ATTACK_FAIL - The attack is valid, but failed. No followup attack is made.
+ * * MARTIAL_ATTACK_SUCCESS - The attack is valid, and succeeded. No followup attack is made.
+ */
/datum/martial_art/proc/disarm_act(mob/living/attacker, mob/living/defender)
+ SHOULD_CALL_PARENT(FALSE)
+ PROTECTED_PROC(TRUE)
return MARTIAL_ATTACK_INVALID
+/**
+ * Called when harm-intenting on someone
+ *
+ * What is checked going into this:
+ * Adjacency, [TRAIT_MARTIAL_ARTS_IMMUNE], attacker incapacitated, can_unarmed_attack, can_use
+ *
+ * What is NOT:
+ * check_block
+ *
+ * Arguments
+ * * mob/living/attacker - The mob attacking
+ * * mob/living/defender - The mob being attacked
+ *
+ * Returns
+ * * MARTIAL_ATTACK_INVALID - The attack is not valid, do normal unarmed attack
+ * * MARTIAL_ATTACK_FAIL - The attack is valid, but failed. No followup attack is made.
+ * * MARTIAL_ATTACK_SUCCESS - The attack is valid, and succeeded. No followup attack is made.
+ */
/datum/martial_art/proc/harm_act(mob/living/attacker, mob/living/defender)
+ SHOULD_CALL_PARENT(FALSE)
+ PROTECTED_PROC(TRUE)
return MARTIAL_ATTACK_INVALID
+/**
+ * Called when grabbing someone
+ *
+ * What is checked going into this:
+ * Adjacency, [TRAIT_MARTIAL_ARTS_IMMUNE], attacker incapacitated, can_unarmed_attack, can_use
+ *
+ * What is NOT:
+ * check_block
+ *
+ * Arguments
+ * * mob/living/attacker - The mob attacking
+ * * mob/living/defender - The mob being attacked
+ *
+ * Returns
+ * * MARTIAL_ATTACK_INVALID - The attack is not valid, do normal unarmed attack
+ * * MARTIAL_ATTACK_FAIL - The attack is valid, but failed. No followup attack is made.
+ * * MARTIAL_ATTACK_SUCCESS - The attack is valid, and succeeded. No followup attack is made.
+ */
/datum/martial_art/proc/grab_act(mob/living/attacker, mob/living/defender)
+ SHOULD_CALL_PARENT(FALSE)
+ PROTECTED_PROC(TRUE)
return MARTIAL_ATTACK_INVALID
-/datum/martial_art/proc/can_use(mob/living/L)
+/**
+ * Checks if the passed mob can use this martial art.
+ *
+ * Arguments
+ * * mob/living/martial_artist - The mob to check
+ *
+ * Returns
+ * * TRUE - The mob can use this martial art
+ * * FALSE - The mob cannot use this martial art
+ */
+/datum/martial_art/proc/can_use(mob/living/martial_artist)
return TRUE
+/**
+ * Adds the passed element to the current streak, resetting it if the target is not the same as the last target.
+ *
+ * Arguments
+ * * element - The element to add to the streak. This is some one letter string.
+ * * mob/living/defender - The mob being attacked
+ */
/datum/martial_art/proc/add_to_streak(element, mob/living/defender)
- if(defender != current_target)
+ if(!IS_WEAKREF_OF(defender, current_target))
reset_streak(defender)
- streak = streak+element
+ streak += element
if(length(streak) > max_streak_length)
streak = copytext(streak, 1 + length(streak[1]))
- if (display_combos)
- var/mob/living/holder_living = holder.resolve()
+ if(display_combos)
timerid = addtimer(CALLBACK(src, PROC_REF(reset_streak), null, FALSE), combo_timer, TIMER_UNIQUE | TIMER_STOPPABLE)
- holder_living?.hud_used?.combo_display.update_icon_state(streak, combo_timer - 2 SECONDS)
+ holder.hud_used?.combo_display.update_icon_state(streak, combo_timer - 2 SECONDS)
+/**
+ * Resets the current streak.
+ *
+ * Arguments
+ * * mob/living/new_target - (Optional) The mob being attacked while the reset is occuring.
+ * * update_icon - If TRUE, the combo display will be updated.
+ */
/datum/martial_art/proc/reset_streak(mob/living/new_target, update_icon = TRUE)
if(timerid)
deltimer(timerid)
- current_target = new_target
+ current_target = WEAKREF(new_target)
streak = ""
- if(update_icon)
- var/mob/living/holder_living = holder?.resolve()
- holder_living?.hud_used?.combo_display.update_icon_state(streak)
+ if(display_combos && update_icon)
+ holder.hud_used?.combo_display.update_icon_state(streak)
-/datum/martial_art/proc/teach(mob/living/holder_living, make_temporary=FALSE)
- if(!istype(holder_living) || !holder_living.mind)
+/**
+ * Teaches the passed mob this martial art.
+ *
+ * Arguments
+ * * mob/living/new_holder - The mob to teach this martial art to.
+ * * make_temporary - If FALSE, this martial art will completely replace any existing martial arts.
+ * If TRUE, any existing martial art will be stored in the base variable, and will be restored when this martial art is removed.
+ * This can only occur if allow_temp_override is TRUE.
+ *
+ * Returns
+ * * TRUE - The martial art was successfully taught.
+ * * FALSE - The mob failed to learn the martial art, for whatever reason.
+ */
+/datum/martial_art/proc/teach(mob/living/new_holder, make_temporary = FALSE)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(!istype(new_holder) || isnull(new_holder.mind))
return FALSE
- if(holder_living.mind.martial_art)
- if(make_temporary)
- if(!holder_living.mind.martial_art.allow_temp_override)
- return FALSE
- store(holder_living.mind.martial_art, holder_living)
- else
- holder_living.mind.martial_art.on_remove(holder_living)
- else if(make_temporary)
- base = holder_living.mind.default_martial_art
- if(help_verb)
- add_verb(holder_living, help_verb)
- holder_living.mind.martial_art = src
- holder = WEAKREF(holder_living)
+
+ var/datum/martial_art/existing_martial = new_holder.mind.martial_art
+ if(!isnull(existing_martial))
+ if(make_temporary && !existing_martial.allow_temp_override)
+ return FALSE
+
+ if(!isnull(existing_martial.base))
+ store_martial_art(existing_martial.base)
+ existing_martial.unstore_martial_art()
+ else if(make_temporary)
+ store_martial_art(existing_martial)
+
+ // Nulls out any existing martial art, it'll get GC'd if nothing owns it
+ existing_martial.remove(new_holder)
+
+ new_holder.mind.martial_art = src
+ holder = new_holder
+ on_teach(new_holder)
return TRUE
-/datum/martial_art/proc/store(datum/martial_art/old, mob/living/holder_living)
- old.on_remove(holder_living)
- if (old.base) //Checks if old is temporary, if so it will not be stored.
- base = old.base
- else //Otherwise, old is stored.
- base = old
+/// Stores the passed martial art in the base var.
+/datum/martial_art/proc/store_martial_art(datum/martial_art/martial)
+ if(!isnull(base))
+ UnregisterSignal(base, COMSIG_QDELETING)
-/datum/martial_art/proc/remove(mob/living/holder_living)
- if(!istype(holder_living) || !holder_living.mind || holder_living.mind.martial_art != src)
- return
- on_remove(holder_living)
- if(base)
- base.teach(holder_living)
- else
- var/datum/martial_art/default = holder_living.mind.default_martial_art
- default.teach(holder_living)
+ base = martial
+ RegisterSignal(base, COMSIG_QDELETING, PROC_REF(base_deleted))
+
+/// Unstores the base var.
+/datum/martial_art/proc/unstore_martial_art()
+ UnregisterSignal(base, COMSIG_QDELETING)
+ base = null
+
+/datum/martial_art/proc/base_deleted(datum/source)
+ SIGNAL_HANDLER
+ base = null
+
+/**
+ * Removes this martial art from the passed mob AND their mind.
+ *
+ * Arguments
+ * * mob/living/old_holder - The mob to remove this martial art from.
+ */
+/datum/martial_art/proc/remove(mob/living/old_holder)
+ SHOULD_CALL_PARENT(TRUE)
+
+ ASSERT(old_holder == holder)
+ ASSERT(old_holder.mind.martial_art == src)
+
+ on_remove(old_holder)
+ old_holder.mind.martial_art = null
+ if(!isnull(base))
+ base.teach(old_holder)
+ unstore_martial_art()
holder = null
-/datum/martial_art/proc/on_remove(mob/living/holder_living)
+/**
+ * A helper proc to remove the martial art from the passed mob fully, e
+ * ven if stored in another martial art's base.
+ *
+ * Arguments
+ * * mob/living/maybe_holder - The mob to check.
+ *
+ * Returns
+ * * TRUE - If the martial art was removed in some way
+ * * FALSE - If nothing happened
+ */
+/datum/martial_art/proc/fully_remove(mob/living/maybe_holder)
+ var/datum/martial_art/holder_art = maybe_holder.mind?.martial_art
+ if(isnull(holder_art))
+ return FALSE
+
+ if(holder_art == src)
+ remove(maybe_holder)
+ return TRUE
+
+ if(holder_art.base == src)
+ holder_art.unstore_martial_art()
+ return TRUE
+
+ return FALSE
+
+/**
+ * Called when this martial art is added to a mob.
+ */
+/datum/martial_art/proc/on_teach(mob/living/new_holder)
+ if(help_verb)
+ add_verb(new_holder, help_verb)
+ RegisterSignal(new_holder, COMSIG_QDELETING, PROC_REF(holder_deleted))
+ RegisterSignal(new_holder, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(unarmed_strike))
+ RegisterSignal(new_holder, COMSIG_LIVING_GRAB, PROC_REF(attempt_grab))
+ RegisterSignal(new_holder, COMSIG_MOB_MIND_TRANSFERRED_OUT_OF, PROC_REF(transfer_martial_arts))
+
+/**
+ * Called when this martial art is removed from a mob.
+ */
+/datum/martial_art/proc/on_remove(mob/living/remove_from)
if(help_verb)
- remove_verb(holder_living, help_verb)
- return
+ remove_verb(remove_from, help_verb)
+ UnregisterSignal(remove_from, list(COMSIG_QDELETING, COMSIG_LIVING_UNARMED_ATTACK, COMSIG_LIVING_GRAB, COMSIG_MOB_MIND_TRANSFERRED_OUT_OF))
+
+/datum/martial_art/proc/holder_deleted(datum/source)
+ SIGNAL_HANDLER
+ holder = null
+
+/// Signal proc for [COMSIG_MOB_MIND_TRANSFERRED_OUT_OF] to pass martial arts between bodies on mind transfer
+/// By this point the martial art's holder is the old body, but the mind that owns it is in the new body
+/datum/martial_art/proc/transfer_martial_arts(mob/living/old_body, mob/living/new_body)
+ SIGNAL_HANDLER
+
+ // This has some notable issues in that martial arts granted by items like Krav Maga
+ // will follow the body swap, the easiest fix would be to move martial arts off of the mind
+
+ if(!isnull(base)) // If we're home to a temporary one just don't touch it, give the base to the new body and leave it at that
+ base.teach(new_body)
+ unstore_martial_art()
+ return
+
+ on_remove(old_body) // on_remove rather than remove, because by this point the mind is already in the new body, which remove handles.
+ teach(new_body)
diff --git a/code/datums/martial/boxing.dm b/code/datums/martial/boxing.dm
index da30104c9af..8ef30db63aa 100644
--- a/code/datums/martial/boxing.dm
+++ b/code/datums/martial/boxing.dm
@@ -3,69 +3,78 @@
id = MARTIALART_BOXING
pacifist_style = TRUE
-/datum/martial_art/boxing/disarm_act(mob/living/attacker, mob/living/defender)
- to_chat(attacker, span_warning("Can't disarm while boxing!"))
- return TRUE
-
-/datum/martial_art/boxing/grab_act(mob/living/attacker, mob/living/defender)
- to_chat(attacker, span_warning("Can't grab while boxing!"))
- return TRUE
+/datum/martial_art/boxing/teach(mob/living/new_holder, make_temporary)
+ if(!ishuman(new_holder))
+ return FALSE
+ return ..()
-/datum/martial_art/boxing/harm_act(mob/living/attacker, mob/living/defender)
+/datum/martial_art/boxing/disarm_act(mob/living/carbon/human/attacker, mob/living/defender)
+ attacker.balloon_alert(attacker, "can't disarm while boxing!")
+ return MARTIAL_ATTACK_FAIL
- var/mob/living/carbon/human/attacker_human = attacker
- var/obj/item/bodypart/arm/active_arm = attacker_human.get_active_hand()
+/datum/martial_art/boxing/grab_act(mob/living/carbon/human/attacker, mob/living/defender)
+ attacker.balloon_alert(attacker, "can't grab while boxing!")
+ return MARTIAL_ATTACK_FAIL
+/datum/martial_art/boxing/harm_act(mob/living/carbon/human/attacker, mob/living/defender)
+ var/obj/item/bodypart/arm/active_arm = attacker.get_active_hand()
attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
- var/atk_verb = pick("left hook","right hook","straight punch")
-
var/damage = rand(5, 8) + active_arm.unarmed_damage_low
- if(!damage)
- playsound(defender.loc, active_arm.unarmed_miss_sound, 25, TRUE, -1)
- defender.visible_message(span_warning("[attacker]'s [atk_verb] misses [defender]!"), \
- span_danger("You avoid [attacker]'s [atk_verb]!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, attacker)
+ var/atk_verb = pick("left hook", "right hook", "straight punch")
+ if(damage <= 0)
+ playsound(defender, active_arm.unarmed_miss_sound, 25, TRUE, -1)
+ defender.visible_message(
+ span_warning("[attacker]'s [atk_verb] misses [defender]!"),
+ span_danger("You avoid [attacker]'s [atk_verb]!"),
+ span_hear("You hear a swoosh!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
to_chat(attacker, span_warning("Your [atk_verb] misses [defender]!"))
log_combat(attacker, defender, "attempted to hit", atk_verb)
- return FALSE
+ return MARTIAL_ATTACK_FAIL
+ if(defender.check_block(attacker, damage, "[attacker]'s [atk_verb]", UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
var/obj/item/bodypart/affecting = defender.get_bodypart(defender.get_random_valid_zone(attacker.zone_selected))
var/armor_block = defender.run_armor_check(affecting, MELEE)
- // NOVA EDIT CHANGE
- var/sound/attack_sound
- if(!active_arm.unarmed_attack_sound)
- attack_sound = get_sfx("punch")
- else
- attack_sound = active_arm.unarmed_attack_sound
- playsound(defender.loc, attack_sound, 25, TRUE, -1)
- //NOVA EDIT END
-
- defender.visible_message(span_danger("[attacker] [atk_verb]ed [defender]!"), \
- span_userdanger("You're [atk_verb]ed by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker)
+ playsound(defender, active_arm.unarmed_attack_sound, 25, TRUE, -1)
+ defender.visible_message(
+ span_danger("[attacker] [atk_verb]ed [defender]!"),
+ span_userdanger("You're [atk_verb]ed by [attacker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
to_chat(attacker, span_danger("You [atk_verb]ed [defender]!"))
-
defender.apply_damage(damage, STAMINA, affecting, armor_block)
log_combat(attacker, defender, "punched (boxing) ")
if(defender.getStaminaLoss() > 50 && istype(defender.mind?.martial_art, /datum/martial_art/boxing))
- var/knockout_prob = defender.getStaminaLoss() + rand(-15,15)
- if((defender.stat != DEAD) && prob(knockout_prob))
- defender.visible_message(span_danger("[attacker] knocks [defender] out with a haymaker!"), \
- span_userdanger("You're knocked unconscious by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker)
+ var/knockout_prob = defender.getStaminaLoss() + rand(-15, 15)
+ if(defender.stat != DEAD && prob(knockout_prob))
+ defender.visible_message(
+ span_danger("[attacker] knocks [defender] out with a haymaker!"),
+ span_userdanger("You're knocked unconscious by [attacker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
to_chat(attacker, span_danger("You knock [defender] out with a haymaker!"))
- defender.apply_effect(20 SECONDS,EFFECT_KNOCKDOWN, armor_block)
+ defender.apply_effect(20 SECONDS, EFFECT_KNOCKDOWN, armor_block)
defender.SetSleeping(10 SECONDS)
log_combat(attacker, defender, "knocked out (boxing) ")
- return TRUE
+ return MARTIAL_ATTACK_SUCCESS
-/datum/martial_art/boxing/can_use(mob/living/owner)
- if(!ishuman(owner))
+/datum/martial_art/boxing/can_use(mob/living/martial_artist)
+ if(!ishuman(martial_artist))
return FALSE
return ..()
/obj/item/clothing/gloves/boxing
- var/datum/martial_art/boxing/style = new
+ var/datum/martial_art/boxing/style
/obj/item/clothing/gloves/boxing/Initialize(mapload)
. = ..()
@@ -76,18 +85,18 @@
slapcraft_recipes = slapcraft_recipe_list,\
)
+ style = new()
+ style.allow_temp_override = FALSE
+
+/obj/item/clothing/gloves/boxing/Destroy()
+ QDEL_NULL(style)
+ return ..()
+
/obj/item/clothing/gloves/boxing/equipped(mob/user, slot)
- ..()
- // boxing requires human
- if(!ishuman(user))
- return
+ . = ..()
if(slot & ITEM_SLOT_GLOVES)
- var/mob/living/student = user
- style.teach(student, 1)
+ style.teach(user, TRUE)
/obj/item/clothing/gloves/boxing/dropped(mob/user)
- ..()
- if(!ishuman(user))
- return
- var/mob/living/owner = user
- style.remove(owner)
+ . = ..()
+ style.fully_remove(user)
diff --git a/code/datums/martial/cqc.dm b/code/datums/martial/cqc.dm
index ab92e854e87..8e33ac5a851 100644
--- a/code/datums/martial/cqc.dm
+++ b/code/datums/martial/cqc.dm
@@ -10,19 +10,19 @@
help_verb = /mob/living/proc/CQC_help
smashes_tables = TRUE
display_combos = TRUE
- var/old_grab_state = null
- var/mob/restraining_mob
+ /// Weakref to a mob we're currently restraining (with grab-grab combo)
+ VAR_PRIVATE/datum/weakref/restraining_mob
/// Probability of successfully blocking attacks while on throw mode
var/block_chance = 75
-/datum/martial_art/cqc/teach(mob/living/cqc_user, make_temporary)
+/datum/martial_art/cqc/on_teach(mob/living/new_holder)
. = ..()
- RegisterSignal(cqc_user, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby))
- RegisterSignal(cqc_user, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(check_block))
+ RegisterSignal(new_holder, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby))
+ RegisterSignal(new_holder, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(check_block))
-/datum/martial_art/cqc/on_remove(mob/living/cqc_user)
- UnregisterSignal(cqc_user, list(COMSIG_ATOM_ATTACKBY, COMSIG_LIVING_CHECK_BLOCK))
- . = ..()
+/datum/martial_art/cqc/on_remove(mob/living/remove_from)
+ UnregisterSignal(remove_from, list(COMSIG_ATOM_ATTACKBY, COMSIG_LIVING_CHECK_BLOCK))
+ return ..()
///Signal from getting attacked with an item, for a special interaction with touch spells
/datum/martial_art/cqc/proc/on_attackby(mob/living/cqc_user, obj/item/attack_weapon, mob/attacker, params)
@@ -69,13 +69,11 @@
/datum/martial_art/cqc/reset_streak(mob/living/new_target)
- if(new_target && new_target != restraining_mob)
+ if(!IS_WEAKREF_OF(new_target, restraining_mob))
restraining_mob = null
return ..()
/datum/martial_art/cqc/proc/check_streak(mob/living/attacker, mob/living/defender)
- if(!can_use(attacker))
- return FALSE
if(findtext(streak, SLAM_COMBO))
reset_streak()
return Slam(attacker, defender)
@@ -94,122 +92,152 @@
return FALSE
/datum/martial_art/cqc/proc/Slam(mob/living/attacker, mob/living/defender)
- if(!can_use(attacker))
+ if(defender.body_position != STANDING_UP)
return FALSE
- if(defender.body_position == STANDING_UP)
- defender.visible_message(span_danger("[attacker] slams [defender] into the ground!"), \
- span_userdanger("You're slammed into the ground by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), null, attacker)
- to_chat(attacker, span_danger("You slam [defender] into the ground!"))
- playsound(get_turf(attacker), 'sound/weapons/slam.ogg', 50, TRUE, -1)
- defender.apply_damage(10, BRUTE)
- defender.Paralyze(12 SECONDS)
- log_combat(attacker, defender, "slammed (CQC)")
- return TRUE
+
+ attacker.do_attack_animation(defender)
+ defender.visible_message(
+ span_danger("[attacker] slams [defender] into the ground!"),
+ span_userdanger("You're slammed into the ground by [attacker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ null,
+ attacker,
+ )
+ to_chat(attacker, span_danger("You slam [defender] into the ground!"))
+ playsound(attacker, 'sound/weapons/slam.ogg', 50, TRUE, -1)
+ defender.apply_damage(10, BRUTE)
+ defender.Paralyze(12 SECONDS)
+ log_combat(attacker, defender, "slammed (CQC)")
+ return TRUE
/datum/martial_art/cqc/proc/Kick(mob/living/attacker, mob/living/defender)
- if(!can_use(attacker) || defender.stat != CONSCIOUS)
+ if(defender.stat != CONSCIOUS)
return FALSE
+ attacker.do_attack_animation(defender)
if(defender.body_position == LYING_DOWN && !defender.IsUnconscious() && defender.getStaminaLoss() >= 100)
log_combat(attacker, defender, "knocked out (Head kick)(CQC)")
- defender.visible_message(span_danger("[attacker] kicks [defender]'s head, knocking [defender.p_them()] out!"), \
- span_userdanger("You're knocked unconscious by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), null, attacker)
+ defender.visible_message(
+ span_danger("[attacker] kicks [defender]'s head, knocking [defender.p_them()] out!"),
+ span_userdanger("You're knocked unconscious by [attacker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ null,
+ attacker,
+ )
to_chat(attacker, span_danger("You kick [defender]'s head, knocking [defender.p_them()] out!"))
- playsound(get_turf(attacker), 'sound/weapons/genhit1.ogg', 50, TRUE, -1)
+ playsound(attacker, 'sound/weapons/genhit1.ogg', 50, TRUE, -1)
var/helmet_protection = defender.run_armor_check(BODY_ZONE_HEAD, MELEE)
defender.apply_effect(20 SECONDS, EFFECT_KNOCKDOWN, helmet_protection)
defender.apply_effect(10 SECONDS, EFFECT_UNCONSCIOUS, helmet_protection)
defender.adjustOrganLoss(ORGAN_SLOT_BRAIN, 15, 150)
+
else
- defender.visible_message(span_danger("[attacker] kicks [defender] back!"), \
- span_userdanger("You're kicked back by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker)
+ defender.visible_message(
+ span_danger("[attacker] kicks [defender] back!"),
+ span_userdanger("You're kicked back by [attacker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
to_chat(attacker, span_danger("You kick [defender] back!"))
- playsound(get_turf(attacker), 'sound/weapons/cqchit1.ogg', 50, TRUE, -1)
+ playsound(attacker, 'sound/weapons/cqchit1.ogg', 50, TRUE, -1)
var/atom/throw_target = get_edge_target_turf(defender, attacker.dir)
defender.throw_at(throw_target, 1, 14, attacker)
defender.apply_damage(10, attacker.get_attack_type())
if(defender.body_position == LYING_DOWN && !defender.IsUnconscious())
defender.adjustStaminaLoss(45)
log_combat(attacker, defender, "kicked (CQC)")
- . = TRUE
+
+ return TRUE
/datum/martial_art/cqc/proc/Pressure(mob/living/attacker, mob/living/defender)
- if(!can_use(attacker))
- return FALSE
+ attacker.do_attack_animation(defender)
log_combat(attacker, defender, "pressured (CQC)")
- defender.visible_message(span_danger("[attacker] punches [defender]'s neck!"), \
- span_userdanger("Your neck is punched by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker)
+ defender.visible_message(
+ span_danger("[attacker] punches [defender]'s neck!"),
+ span_userdanger("Your neck is punched by [attacker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
to_chat(attacker, span_danger("You punch [defender]'s neck!"))
defender.adjustStaminaLoss(60)
- playsound(get_turf(attacker), 'sound/weapons/cqchit1.ogg', 50, TRUE, -1)
+ playsound(attacker, 'sound/weapons/cqchit1.ogg', 50, TRUE, -1)
return TRUE
/datum/martial_art/cqc/proc/Restrain(mob/living/attacker, mob/living/defender)
- if(restraining_mob)
- return
- if(!can_use(attacker))
+ if(restraining_mob?.resolve())
+ return FALSE
+ if(defender.stat != CONSCIOUS)
return FALSE
- if(!defender.stat)
- log_combat(attacker, defender, "restrained (CQC)")
- defender.visible_message(span_warning("[attacker] locks [defender] into a restraining position!"), \
- span_userdanger("You're locked into a restraining position by [attacker]!"), span_hear("You hear shuffling and a muffled groan!"), null, attacker)
- to_chat(attacker, span_danger("You lock [defender] into a restraining position!"))
- defender.adjustStaminaLoss(20)
- defender.Stun(10 SECONDS)
- restraining_mob = defender
- addtimer(VARSET_CALLBACK(src, restraining_mob, null), 50, TIMER_UNIQUE)
- return TRUE
+
+ log_combat(attacker, defender, "restrained (CQC)")
+ defender.visible_message(
+ span_warning("[attacker] locks [defender] into a restraining position!"),
+ span_userdanger("You're locked into a restraining position by [attacker]!"),
+ span_hear("You hear shuffling and a muffled groan!"),
+ null,
+ attacker,
+ )
+ to_chat(attacker, span_danger("You lock [defender] into a restraining position!"))
+ defender.adjustStaminaLoss(20)
+ defender.Stun(10 SECONDS)
+ restraining_mob = WEAKREF(defender)
+ addtimer(VARSET_CALLBACK(src, restraining_mob, null), 5 SECONDS, TIMER_UNIQUE)
+ return TRUE
/datum/martial_art/cqc/proc/Consecutive(mob/living/attacker, mob/living/defender)
- if(!can_use(attacker))
+ if(defender.stat != CONSCIOUS)
return FALSE
- if(!defender.stat)
- log_combat(attacker, defender, "consecutive CQC'd (CQC)")
- defender.visible_message(span_danger("[attacker] strikes [defender]'s abdomen, neck and back consecutively"), \
- span_userdanger("Your abdomen, neck and back are struck consecutively by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker)
- to_chat(attacker, span_danger("You strike [defender]'s abdomen, neck and back consecutively!"))
- playsound(get_turf(defender), 'sound/weapons/cqchit2.ogg', 50, TRUE, -1)
- var/obj/item/held_item = defender.get_active_held_item()
- if(held_item && defender.temporarilyRemoveItemFromInventory(held_item))
- attacker.put_in_hands(held_item)
- defender.adjustStaminaLoss(50)
- defender.apply_damage(25, attacker.get_attack_type())
- return TRUE
-/datum/martial_art/cqc/grab_act(mob/living/attacker, mob/living/defender)
- if(attacker != defender && can_use(attacker)) // attacker != defender prevents grabbing yourself
- add_to_streak("G", defender)
- if(check_streak(attacker, defender)) //if a combo is made no grab upgrade is done
- return TRUE
- old_grab_state = attacker.grab_state
- defender.grabbedby(attacker, 1)
- if(old_grab_state == GRAB_PASSIVE)
- defender.drop_all_held_items()
- attacker.setGrabState(GRAB_AGGRESSIVE) //Instant aggressive grab if on grab intent
- log_combat(attacker, defender, "grabbed", addition="aggressively")
- defender.visible_message(span_warning("[attacker] violently grabs [defender]!"), \
- span_userdanger("You're grabbed violently by [attacker]!"), span_hear("You hear sounds of aggressive fondling!"), COMBAT_MESSAGE_RANGE, attacker)
- to_chat(attacker, span_danger("You violently grab [defender]!"))
- return TRUE
- else
- return FALSE
+ attacker.do_attack_animation(defender)
+ log_combat(attacker, defender, "consecutive CQC'd (CQC)")
+ defender.visible_message(
+ span_danger("[attacker] strikes [defender]'s abdomen, neck and back consecutively"), \
+ span_userdanger("Your abdomen, neck and back are struck consecutively by [attacker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
+ to_chat(attacker, span_danger("You strike [defender]'s abdomen, neck and back consecutively!"))
+ playsound(defender, 'sound/weapons/cqchit2.ogg', 50, TRUE, -1)
+ var/obj/item/held_item = defender.get_active_held_item()
+ if(held_item && defender.temporarilyRemoveItemFromInventory(held_item))
+ attacker.put_in_hands(held_item)
+ defender.adjustStaminaLoss(50)
+ defender.apply_damage(25, attacker.get_attack_type())
+ return TRUE
-/datum/martial_art/cqc/harm_act(mob/living/attacker, mob/living/defender)
- if(!can_use(attacker))
+/datum/martial_art/cqc/grab_act(mob/living/attacker, mob/living/defender)
+ if(attacker == defender)
+ return MARTIAL_ATTACK_INVALID
+ if(defender.check_block(attacker, 0, attacker.name, UNARMED_ATTACK))
return MARTIAL_ATTACK_FAIL
- if(attacker.resting && defender.stat != DEAD && defender.body_position == STANDING_UP)
- defender.visible_message(span_danger("[attacker] leg sweeps [defender]!"), \
- span_userdanger("Your legs are sweeped by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), null, attacker)
- to_chat(attacker, span_danger("You leg sweep [defender]!"))
- playsound(get_turf(attacker), 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
- attacker.do_attack_animation(defender)
- defender.apply_damage(10, BRUTE)
- defender.Knockdown(5 SECONDS)
- log_combat(attacker, defender, "sweeped (CQC)")
- reset_streak()
+ add_to_streak("G", defender)
+ if(check_streak(attacker, defender)) //if a combo is made no grab upgrade is done
return MARTIAL_ATTACK_SUCCESS
+ if(attacker.body_position == LYING_DOWN)
+ return MARTIAL_ATTACK_INVALID
+
+ var/old_grab_state = attacker.grab_state
+ defender.grabbedby(attacker, TRUE)
+ if(old_grab_state == GRAB_PASSIVE)
+ defender.drop_all_held_items()
+ attacker.setGrabState(GRAB_AGGRESSIVE) //Instant aggressive grab if on grab intent
+ log_combat(attacker, defender, "grabbed", addition="aggressively")
+ defender.visible_message(
+ span_warning("[attacker] violently grabs [defender]!"),
+ span_userdanger("You're grabbed violently by [attacker]!"),
+ span_hear("You hear sounds of aggressive fondling!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
+ to_chat(attacker, span_danger("You violently grab [defender]!"))
+ return MARTIAL_ATTACK_SUCCESS
+
+/datum/martial_art/cqc/harm_act(mob/living/attacker, mob/living/defender)
if(attacker.grab_state == GRAB_KILL \
&& attacker.zone_selected == BODY_ZONE_HEAD \
&& attacker.pulling == defender \
@@ -232,63 +260,107 @@
defender.investigate_log("has had [defender.p_their()] neck snapped by [attacker].", INVESTIGATE_DEATHS)
return MARTIAL_ATTACK_SUCCESS
+ if(defender.check_block(attacker, 10, attacker.name, UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
+
+ if(attacker.resting && defender.stat != DEAD && defender.body_position == STANDING_UP)
+ defender.visible_message(
+ span_danger("[attacker] leg sweeps [defender]!"),
+ span_userdanger("Your legs are sweeped by [attacker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ null,
+ attacker,
+ )
+ to_chat(attacker, span_danger("You leg sweep [defender]!"))
+ playsound(attacker, 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
+ attacker.do_attack_animation(defender)
+ defender.apply_damage(10, BRUTE)
+ defender.Knockdown(5 SECONDS)
+ log_combat(attacker, defender, "sweeped (CQC)")
+ reset_streak()
+ return MARTIAL_ATTACK_SUCCESS
+
add_to_streak("H", defender)
if(check_streak(attacker, defender))
return MARTIAL_ATTACK_SUCCESS
- log_combat(attacker, defender, "attacked (CQC)")
attacker.do_attack_animation(defender)
var/picked_hit_type = pick("CQC", "Big Boss")
var/bonus_damage = 13
if(defender.body_position == LYING_DOWN)
bonus_damage += 5
- picked_hit_type = "stomp"
+ picked_hit_type = pick("kick", "stomp")
defender.apply_damage(bonus_damage, BRUTE)
- if(picked_hit_type == "kick" || picked_hit_type == "stomp")
- playsound(get_turf(defender), 'sound/weapons/cqchit2.ogg', 50, TRUE, -1)
- else
- playsound(get_turf(defender), 'sound/weapons/cqchit1.ogg', 50, TRUE, -1)
- defender.visible_message(span_danger("[attacker] [picked_hit_type]ed [defender]!"), \
- span_userdanger("You're [picked_hit_type]ed by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker)
- to_chat(attacker, span_danger("You [picked_hit_type] [defender]!"))
- log_combat(attacker, defender, "[picked_hit_type]s (CQC)")
+ playsound(defender, (picked_hit_type == "kick" || picked_hit_type == "stomp") ? 'sound/weapons/cqchit2.ogg' : 'sound/weapons/cqchit1.ogg', 50, TRUE, -1)
+
+ defender.visible_message(
+ span_danger("[attacker] [picked_hit_type]ed [defender]!"),
+ span_userdanger("You're [picked_hit_type]ed by [attacker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
+ to_chat(attacker, span_danger("You [picked_hit_type] [defender]!"))
+ log_combat(attacker, defender, "attacked ([picked_hit_type]'d)(CQC)")
return MARTIAL_ATTACK_SUCCESS
/datum/martial_art/cqc/disarm_act(mob/living/attacker, mob/living/defender)
- if(!can_use(attacker))
- return FALSE
+ if(defender.check_block(attacker, 0, attacker.name, UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
+
add_to_streak("D", defender)
- var/obj/item/held_item = null
if(check_streak(attacker, defender))
- return TRUE
- log_combat(attacker, defender, "disarmed (CQC)", "[held_item ? " grabbing \the [held_item]" : ""]")
- if(restraining_mob && attacker.pulling == restraining_mob)
- log_combat(attacker, defender, "knocked out (Chokehold)(CQC)")
- defender.visible_message(span_danger("[attacker] puts [defender] into a chokehold!"), \
- span_userdanger("You're put into a chokehold by [attacker]!"), span_hear("You hear shuffling and a muffled groan!"), null, attacker)
+ return MARTIAL_ATTACK_SUCCESS
+
+ if(IS_WEAKREF_OF(attacker.pulling, restraining_mob))
+ log_combat(attacker, defender, "disarmed (CQC)", addition = "knocked out (CQC Chokehold)")
+ defender.visible_message(
+ span_danger("[attacker] puts [defender] into a chokehold!"),
+ span_userdanger("You're put into a chokehold by [attacker]!"),
+ span_hear("You hear shuffling and a muffled groan!"),
+ null,
+ attacker,
+ )
to_chat(attacker, span_danger("You put [defender] into a chokehold!"))
defender.SetSleeping(40 SECONDS)
restraining_mob = null
if(attacker.grab_state < GRAB_NECK && !HAS_TRAIT(attacker, TRAIT_PACIFISM))
attacker.setGrabState(GRAB_NECK)
- return TRUE
- if(prob(65))
- if(!defender.stat || !defender.IsParalyzed() || !restraining_mob)
- held_item = defender.get_active_held_item()
- defender.visible_message(span_danger("[attacker] strikes [defender]'s jaw with their hand!"), \
- span_userdanger("Your jaw is struck by [attacker], you feel disoriented!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker)
- to_chat(attacker, span_danger("You strike [defender]'s jaw, leaving [defender.p_them()] disoriented!"))
- playsound(get_turf(defender), 'sound/weapons/cqchit1.ogg', 50, TRUE, -1)
- if(held_item && defender.temporarilyRemoveItemFromInventory(held_item))
- attacker.put_in_hands(held_item)
- defender.set_jitter_if_lower(4 SECONDS)
- defender.apply_damage(5, attacker.get_attack_type())
- else
- defender.visible_message(span_danger("[attacker] fails to disarm [defender]!"), \
- span_userdanger("You're nearly disarmed by [attacker]!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, attacker)
- to_chat(attacker, span_warning("You fail to disarm [defender]!"))
- playsound(defender, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
- return FALSE
+ return MARTIAL_ATTACK_SUCCESS
+
+ attacker.do_attack_animation(defender, ATTACK_EFFECT_DISARM)
+ if(prob(65) && (defender.stat == CONSCIOUS || !defender.IsParalyzed() || !restraining_mob?.resolve()))
+ var/obj/item/disarmed_item = defender.get_active_held_item()
+ if(disarmed_item && defender.temporarilyRemoveItemFromInventory(disarmed_item))
+ attacker.put_in_hands(disarmed_item)
+ else
+ disarmed_item = null
+
+ defender.visible_message(
+ span_danger("[attacker] strikes [defender]'s jaw with their hand[disarmed_item ? ", disarming [defender.p_them()] of [disarmed_item]" : ""]!"),
+ span_userdanger("[attacker] strikes your jaw,[disarmed_item ? " disarming you of [disarmed_item] and" : ""] leaving you disoriented!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
+ to_chat(attacker, span_danger("You strike [defender]'s jaw,[disarmed_item ? " disarming [defender.p_them()] of [disarmed_item] and" : ""] leaving [defender.p_them()] disoriented!"))
+ playsound(defender, 'sound/weapons/cqchit1.ogg', 50, TRUE, -1)
+ defender.set_jitter_if_lower(4 SECONDS)
+ defender.apply_damage(5, attacker.get_attack_type())
+ log_combat(attacker, defender, "disarmed (CQC)", addition = disarmed_item ? "(disarmed of [disarmed_item])" : null)
+ return MARTIAL_ATTACK_SUCCESS
+
+ defender.visible_message(
+ span_danger("[attacker] fails to disarm [defender]!"), \
+ span_userdanger("You're nearly disarmed by [attacker]!"),
+ span_hear("You hear a swoosh!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
+ to_chat(attacker, span_warning("You fail to disarm [defender]!"))
+ playsound(defender, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
+ log_combat(attacker, defender, "failed to disarm (CQC)")
+ return MARTIAL_ATTACK_FAIL
/mob/living/proc/CQC_help()
@@ -330,8 +402,8 @@
kitchen_areas |= path
/// Limits where the chef's CQC can be used to only whitelisted areas.
-/datum/martial_art/cqc/under_siege/can_use(mob/living/owner)
- if(!is_type_in_list(get_area(owner), kitchen_areas))
+/datum/martial_art/cqc/under_siege/can_use(mob/living/martial_artist)
+ if(!is_type_in_list(get_area(martial_artist), kitchen_areas))
return FALSE
return ..()
diff --git a/code/datums/martial/hugs_of_the_gondola.dm b/code/datums/martial/hugs_of_the_gondola.dm
deleted file mode 100644
index a1fae03a35a..00000000000
--- a/code/datums/martial/hugs_of_the_gondola.dm
+++ /dev/null
@@ -1,20 +0,0 @@
-/// Gondola love, makes hugs inject pax if the arms are exposed
-/datum/martial_art/hugs_of_the_gondola
- name = "Hugs of the Gondola"
- id = MARTIALART_HUGS_OF_THE_GONDOLA
-
-/datum/martial_art/hugs_of_the_gondola/help_act(mob/living/attacker, mob/living/defender)
- if(ishuman(defender) && ishuman(attacker))
- var/mob/living/carbon/human/human_attacker = attacker
- var/mob/living/carbon/human/human_defender = defender
- var/list/covered_body_zones = human_attacker.get_covered_body_zones()
- var/pax_injected = 4
- if(BODY_ZONE_L_ARM in covered_body_zones)
- pax_injected -= 2
- if(BODY_ZONE_R_ARM in covered_body_zones)
- pax_injected -= 2
- if(pax_injected)
- human_defender.reagents.add_reagent(/datum/reagent/pax, pax_injected)
- to_chat(defender, span_warning("You feel a tiny prick!"))
- //this is so it hugs/shakes up as usual
- return MARTIAL_ATTACK_INVALID
diff --git a/code/datums/martial/krav_maga.dm b/code/datums/martial/krav_maga.dm
index fcc432d41f8..d75437be2b7 100644
--- a/code/datums/martial/krav_maga.dm
+++ b/code/datums/martial/krav_maga.dm
@@ -1,18 +1,31 @@
/datum/martial_art/krav_maga
name = "Krav Maga"
id = MARTIALART_KRAVMAGA
- var/datum/action/neck_chop/neckchop = new/datum/action/neck_chop()
- var/datum/action/leg_sweep/legsweep = new/datum/action/leg_sweep()
- var/datum/action/lung_punch/lungpunch = new/datum/action/lung_punch()
+ VAR_PRIVATE/datum/action/neck_chop/neckchop
+ VAR_PRIVATE/datum/action/leg_sweep/legsweep
+ VAR_PRIVATE/datum/action/lung_punch/lungpunch
+
+/datum/martial_art/krav_maga/New()
+ . = ..()
+ neckchop = new(src)
+ legsweep = new(src)
+ lungpunch = new(src)
+
+/datum/martial_art/krav_maga/Destroy()
+ neckchop = null
+ legsweep = null
+ lungpunch = null
+ return ..()
/datum/action/neck_chop
name = "Neck Chop - Injures the neck, stopping the victim from speaking for a while."
button_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "neckchop"
+ check_flags = AB_CHECK_INCAPACITATED|AB_CHECK_HANDS_BLOCKED|AB_CHECK_CONSCIOUS
/datum/action/neck_chop/Trigger(trigger_flags)
- if(owner.incapacitated())
- to_chat(owner, span_warning("You can't use [name] while you're incapacitated."))
+ . = ..()
+ if(!.)
return
if (owner.mind.martial_art.streak == "neck_chop")
owner.visible_message(span_danger("[owner] assumes a neutral stance."), "Your next attack is cleared.")
@@ -25,10 +38,11 @@
name = "Leg Sweep - Trips the victim, knocking them down for a brief moment."
button_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "legsweep"
+ check_flags = AB_CHECK_INCAPACITATED|AB_CHECK_HANDS_BLOCKED|AB_CHECK_CONSCIOUS
/datum/action/leg_sweep/Trigger(trigger_flags)
- if(owner.incapacitated())
- to_chat(owner, span_warning("You can't use [name] while you're incapacitated."))
+ . = ..()
+ if(!.)
return
if (owner.mind.martial_art.streak == "leg_sweep")
owner.visible_message(span_danger("[owner] assumes a neutral stance."), "Your next attack is cleared.")
@@ -41,10 +55,11 @@
name = "Lung Punch - Delivers a strong punch just above the victim's abdomen, constraining the lungs. The victim will be unable to breathe for a short time."
button_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "lungpunch"
+ check_flags = AB_CHECK_INCAPACITATED|AB_CHECK_HANDS_BLOCKED|AB_CHECK_CONSCIOUS
/datum/action/lung_punch/Trigger(trigger_flags)
- if(owner.incapacitated())
- to_chat(owner, span_warning("You can't use [name] while you're incapacitated."))
+ . = ..()
+ if(!.)
return
if (owner.mind.martial_art.streak == "quick_choke")
owner.visible_message(span_danger("[owner] assumes a neutral stance."), "Your next attack is cleared.")
@@ -53,19 +68,20 @@
owner.visible_message(span_danger("[owner] assumes the Lung Punch stance!"), "Your next attack will be a Lung Punch.")
owner.mind.martial_art.streak = "quick_choke"//internal name for lung punch
-/datum/martial_art/krav_maga/teach(mob/living/owner, make_temporary=FALSE)
- if(..())
- to_chat(owner, span_userdanger("You know the arts of [name]!"))
- to_chat(owner, span_danger("Place your cursor over a move at the top of the screen to see what it does."))
- neckchop.Grant(owner)
- legsweep.Grant(owner)
- lungpunch.Grant(owner)
+/datum/martial_art/krav_maga/on_teach(mob/living/new_holder)
+ . = ..()
+ to_chat(new_holder, span_userdanger("You know the arts of [name]!"))
+ to_chat(new_holder, span_danger("Place your cursor over a move at the top of the screen to see what it does."))
+ neckchop.Grant(new_holder)
+ legsweep.Grant(new_holder)
+ lungpunch.Grant(new_holder)
-/datum/martial_art/krav_maga/on_remove(mob/living/owner)
- to_chat(owner, span_userdanger("You suddenly forget the arts of [name]..."))
- neckchop.Remove(owner)
- legsweep.Remove(owner)
- lungpunch.Remove(owner)
+/datum/martial_art/krav_maga/on_remove(mob/living/remove_from)
+ to_chat(remove_from, span_userdanger("You suddenly forget the arts of [name]..."))
+ neckchop?.Remove(remove_from)
+ legsweep?.Remove(remove_from)
+ lungpunch?.Remove(remove_from)
+ return ..()
/datum/martial_art/krav_maga/proc/check_streak(mob/living/attacker, mob/living/defender)
switch(streak)
@@ -84,80 +100,138 @@
return FALSE
/datum/martial_art/krav_maga/proc/leg_sweep(mob/living/attacker, mob/living/defender)
- if(defender.stat || defender.IsParalyzed())
- return FALSE
- defender.visible_message(span_warning("[attacker] leg sweeps [defender]!"), \
- span_userdanger("Your legs are sweeped by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), null, attacker)
+ if(defender.stat != CONSCIOUS || defender.IsParalyzed())
+ return MARTIAL_ATTACK_INVALID
+ defender.visible_message(
+ span_warning("[attacker] leg sweeps [defender]!"),
+ span_userdanger("Your legs are sweeped by [attacker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ null,
+ attacker,
+ )
to_chat(attacker, span_danger("You leg sweep [defender]!"))
- playsound(get_turf(attacker), 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
+ playsound(attacker, 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
defender.apply_damage(5, BRUTE, BODY_ZONE_CHEST)
defender.Knockdown(6 SECONDS)
log_combat(attacker, defender, "leg sweeped")
- return TRUE
+ return MARTIAL_ATTACK_SUCCESS
/datum/martial_art/krav_maga/proc/quick_choke(mob/living/attacker, mob/living/defender)//is actually lung punch
- defender.visible_message(span_warning("[attacker] pounds [defender] on the chest!"), \
- span_userdanger("Your chest is slammed by [attacker]! You can't breathe!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker)
+ attacker.do_attack_animation(defender)
+ defender.visible_message(
+ span_warning("[attacker] pounds [defender] on the chest!"),
+ span_userdanger("Your chest is slammed by [attacker]! You can't breathe!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
to_chat(attacker, span_danger("You pound [defender] on the chest!"))
- playsound(get_turf(attacker), 'sound/effects/hit_punch.ogg', 50, TRUE, -1)
+ playsound(attacker, 'sound/effects/hit_punch.ogg', 50, TRUE, -1)
if(defender.losebreath <= 10)
defender.losebreath = clamp(defender.losebreath + 5, 0, 10)
defender.adjustOxyLoss(10)
log_combat(attacker, defender, "quickchoked")
- return TRUE
+ return MARTIAL_ATTACK_SUCCESS
/datum/martial_art/krav_maga/proc/neck_chop(mob/living/attacker, mob/living/defender)
- defender.visible_message(span_warning("[attacker] karate chops [defender]'s neck!"), \
- span_userdanger("Your neck is karate chopped by [attacker], rendering you unable to speak!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker)
+ attacker.do_attack_animation(defender)
+ defender.visible_message(
+ span_warning("[attacker] karate chops [defender]'s neck!"),
+ span_userdanger("Your neck is karate chopped by [attacker], rendering you unable to speak!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
to_chat(attacker, span_danger("You karate chop [defender]'s neck, rendering [defender.p_them()] unable to speak!"))
- playsound(get_turf(attacker), 'sound/effects/hit_punch.ogg', 50, TRUE, -1)
+ playsound(attacker, 'sound/effects/hit_punch.ogg', 50, TRUE, -1)
defender.apply_damage(10, attacker.get_attack_type(), BODY_ZONE_HEAD)
defender.adjust_silence_up_to(20 SECONDS, 20 SECONDS)
log_combat(attacker, defender, "neck chopped")
- return TRUE
+ return MARTIAL_ATTACK_SUCCESS
/datum/martial_art/krav_maga/harm_act(mob/living/attacker, mob/living/defender)
- if(check_streak(attacker, defender))
- return TRUE
- log_combat(attacker, defender, "punched")
- var/obj/item/bodypart/affecting = defender.get_bodypart(defender.get_random_valid_zone(attacker.zone_selected))
var/picked_hit_type = pick("punch", "kick")
var/bonus_damage = 0
if(defender.body_position == LYING_DOWN)
bonus_damage += 5
picked_hit_type = "stomp"
+
+ if(defender.check_block(attacker, 10 + bonus_damage, "[attacker]'s [picked_hit_type]", UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
+ if(check_streak(attacker, defender))
+ return MARTIAL_ATTACK_SUCCESS
+
+ log_combat(attacker, defender, "[picked_hit_type]ed")
+ var/obj/item/bodypart/affecting = defender.get_bodypart(defender.get_random_valid_zone(attacker.zone_selected))
defender.apply_damage(10 + bonus_damage, attacker.get_attack_type(), affecting)
if(picked_hit_type == "kick" || picked_hit_type == "stomp")
attacker.do_attack_animation(defender, ATTACK_EFFECT_KICK)
- playsound(get_turf(defender), 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
+ playsound(defender, 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
else
attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
- playsound(get_turf(defender), 'sound/effects/hit_punch.ogg', 50, TRUE, -1)
- defender.visible_message(span_danger("[attacker] [picked_hit_type]s [defender]!"), \
- span_userdanger("You're [picked_hit_type]ed by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker)
+ playsound(defender, 'sound/effects/hit_punch.ogg', 50, TRUE, -1)
+ defender.visible_message(
+ span_danger("[attacker] [picked_hit_type]s [defender]!"),
+ span_userdanger("You're [picked_hit_type]ed by [attacker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
to_chat(attacker, span_danger("You [picked_hit_type] [defender]!"))
log_combat(attacker, defender, "[picked_hit_type] with [name]")
- return TRUE
+ return MARTIAL_ATTACK_SUCCESS
/datum/martial_art/krav_maga/disarm_act(mob/living/attacker, mob/living/defender)
+ if(defender.check_block(attacker, 0, attacker.name, UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
if(check_streak(attacker, defender))
- return TRUE
- var/obj/item/stuff_in_hand = null
- stuff_in_hand = defender.get_active_held_item()
- if(prob(60) && stuff_in_hand)
- if(defender.temporarilyRemoveItemFromInventory(stuff_in_hand))
- attacker.put_in_hands(stuff_in_hand)
- defender.visible_message("[attacker] disarms [defender]!", \
- "You're disarmed by [attacker]!", "You hear aggressive shuffling!", COMBAT_MESSAGE_RANGE, attacker)
- to_chat(attacker, "You disarm [defender]!")
- playsound(defender, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
- log_combat(attacker, defender, "shoved (Krav Maga)", "[stuff_in_hand ? " removing \the [stuff_in_hand]" : ""]")
- return FALSE
+ return MARTIAL_ATTACK_SUCCESS
+ attacker.do_attack_animation(defender, ATTACK_EFFECT_DISARM)
+ var/obj/item/stuff_in_hand = defender.get_active_held_item()
+ if(prob(60) && stuff_in_hand && defender.temporarilyRemoveItemFromInventory(stuff_in_hand))
+ attacker.put_in_hands(stuff_in_hand)
+ defender.visible_message(
+ span_danger("[attacker] disarms [defender]!"),
+ span_userdanger("You're disarmed by [attacker]!"),
+ span_hear("You hear aggressive shuffling!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
+ to_chat(attacker, span_danger("You disarm [defender]!"))
+ playsound(defender, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+ log_combat(attacker, defender, "disarmed (Krav Maga)", addition = "(disarmed of [stuff_in_hand])")
+ return MARTIAL_ATTACK_SUCCESS
+
+ defender.visible_message(
+ span_danger("[attacker] fails to disarm [defender]!"), \
+ span_userdanger("You're nearly disarmed by [attacker]!"),
+ span_hear("You hear a swoosh!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
+ to_chat(attacker, span_warning("You fail to disarm [defender]!"))
+ playsound(defender, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
+ log_combat(attacker, defender, "failed to disarm (Krav Maga)")
+ return MARTIAL_ATTACK_FAIL
//Krav Maga Gloves
/obj/item/clothing/gloves/krav_maga
+<<<<<<< HEAD
var/datum/martial_art/krav_maga/style = new
+=======
+ var/datum/martial_art/krav_maga/style
+ clothing_traits = list(TRAIT_FAST_CUFFING)
+>>>>>>> e13267b45 ([MIRROR] Kicks Martial Arts out of the attack chain (yippee), makes it use signals, plus a large clean up of existing martial arts (#734))
+
+/obj/item/clothing/gloves/krav_maga/Initialize(mapload)
+ . = ..()
+ style = new()
+ style.allow_temp_override = FALSE
+
+/obj/item/clothing/gloves/krav_maga/Destroy()
+ QDEL_NULL(style)
+ return ..()
/obj/item/clothing/gloves/krav_maga/equipped(mob/user, slot)
. = ..()
@@ -166,8 +240,7 @@
/obj/item/clothing/gloves/krav_maga/dropped(mob/user)
. = ..()
- if(user.get_item_by_slot(ITEM_SLOT_GLOVES) == src)
- style.remove(user)
+ style.fully_remove(user)
/obj/item/clothing/gloves/krav_maga/sec//more obviously named, given to sec
name = "krav maga gloves"
diff --git a/code/datums/martial/mushpunch.dm b/code/datums/martial/mushpunch.dm
index eea4439691f..e571f46be35 100644
--- a/code/datums/martial/mushpunch.dm
+++ b/code/datums/martial/mushpunch.dm
@@ -2,39 +2,56 @@
name = "Mushroom Punch"
id = MARTIALART_MUSHPUNCH
-/datum/martial_art/mushpunch/harm_act(mob/living/A, mob/living/D)
- var/atk_verb
- to_chat(A, span_spiderbroodmother("You begin to wind up an attack..."))
- if(!do_after(A, 2.5 SECONDS, target = D))
- to_chat(A, span_spiderbroodmother("Your attack was interrupted!"))
- return TRUE //martial art code was a mistake
- A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
- atk_verb = pick("punch", "smash", "crack")
- D.visible_message(span_danger("[A] [atk_verb]ed [D] with such inhuman strength that it sends [D.p_them()] flying backwards!"), \
- span_userdanger("You're [atk_verb]ed by [A] with such inhuman strength that it sends you flying backwards!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), null, A)
- to_chat(A, span_danger("You [atk_verb] [D] with such inhuman strength that it sends [D.p_them()] flying backwards!"))
- D.apply_damage(rand(15,30), A.get_attack_type())
- playsound(D, 'sound/effects/meteorimpact.ogg', 25, TRUE, -1)
- var/throwtarget = get_edge_target_turf(A, get_dir(A, get_step_away(D, A)))
- D.throw_at(throwtarget, 4, 2, A)//So stuff gets tossed around at the same time.
- D.Paralyze(2 SECONDS)
- if(atk_verb)
- log_combat(A, D, "[atk_verb] (Mushroom Punch)")
- return TRUE
+/datum/martial_art/mushpunch/harm_act(mob/living/attacker, mob/living/defender)
+ INVOKE_ASYNC(src, PROC_REF(charge_up_attack), attacker, defender)
+ return MARTIAL_ATTACK_SUCCESS
+
+/datum/martial_art/mushpunch/proc/charge_up_attack(mob/living/attacker, mob/living/defender)
+
+ to_chat(attacker, span_spiderbroodmother("You begin to wind up an attack..."))
+ if(!do_after(attacker, 2.5 SECONDS, defender))
+ to_chat(attacker, span_spiderbroodmother("Your attack was interrupted!"))
+ return
+
+ var/final_damage = rand(15, 30)
+ var/atk_verb = pick("punch", "smash", "crack")
+ if(defender.check_block(attacker, final_damage, "[attacker]'s [atk_verb]", UNARMED_ATTACK))
+ return
+
+ attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
+ defender.visible_message(
+ span_danger("[attacker] [atk_verb]ed [defender] with such inhuman strength that it sends [defender.p_them()] flying backwards!"), \
+ span_userdanger("You're [atk_verb]ed by [attacker] with such inhuman strength that it sends you flying backwards!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ null,
+ attacker,
+ )
+ to_chat(attacker, span_danger("You [atk_verb] [defender] with such inhuman strength that it sends [defender.p_them()] flying backwards!"))
+ defender.apply_damage(final_damage, attacker.get_attack_type())
+ playsound(defender, 'sound/effects/meteorimpact.ogg', 25, TRUE, -1)
+ var/throwtarget = get_edge_target_turf(attacker, get_dir(attacker, get_step_away(defender, attacker)))
+ defender.throw_at(throwtarget, 4, 2, attacker)//So stuff gets tossed around at the same time.
+ defender.Paralyze(2 SECONDS)
+ log_combat(attacker, defender, "[atk_verb] (Mushroom Punch)")
/obj/item/mushpunch
name = "odd mushroom"
- desc = "Sapienza Ophioglossoides:An odd mushroom from the flesh of a mushroom person. It has apparently retained some innate power of its owner, as it quivers with barely-contained POWER!"
+ desc = "Sapienza Ophioglossoides:An odd mushroom from the flesh of a mushroom person. \
+ It has apparently retained some innate power of its owner, as it quivers with barely-contained POWER!"
icon = 'icons/obj/service/hydroponics/seeds.dmi'
icon_state = "mycelium-angel"
/obj/item/mushpunch/attack_self(mob/living/user)
- if(!istype(user) || !user)
+ if(!istype(user))
return
- var/message = span_spiderbroodmother("You devour [src], and a confluence of skill and power from the mushroom enhances your punches! You do need a short moment to charge these powerful punches.")
- to_chat(user, message)
- var/datum/martial_art/mushpunch/mush = new(null)
+ to_chat(user, span_spiderbroodmother("You devour [src], \
+ and a confluence of skill and power from the mushroom enhances your punches! \
+ You do need a short moment to charge these powerful punches."))
+ var/datum/martial_art/mushpunch/mush = new()
mush.teach(user)
+ visible_message(
+ span_warning("[user] devours [src]."),
+ span_notice("You devour [src]."),
+ )
+
qdel(src)
- visible_message(span_warning("[user] devours [src]."), \
- span_notice("You devour [src]."))
diff --git a/code/datums/martial/plasma_fist.dm b/code/datums/martial/plasma_fist.dm
index ce2b2e6e2d3..1e278414936 100644
--- a/code/datums/martial/plasma_fist.dm
+++ b/code/datums/martial/plasma_fist.dm
@@ -10,60 +10,71 @@
var/plasma_power = 1 //starts at a 1, 2, 4 explosion.
var/plasma_increment = 1 //how much explosion power gets added per kill (1 = 1, 2, 4. 2 = 2, 4, 8 and so on)
var/plasma_cap = 12 //max size explosion level
+ var/datum/action/cooldown/spell/aoe/repulse/tornado_spell
display_combos = TRUE
+/datum/martial_art/plasma_fist/New()
+ . = ..()
+ tornado_spell = new(src)
+
+/datum/martial_art/plasma_fist/Destroy()
+ tornado_spell = null
+ return ..()
+
/datum/martial_art/plasma_fist/proc/check_streak(mob/living/attacker, mob/living/defender)
if(findtext(streak,TORNADO_COMBO))
if(attacker == defender)//helps using apotheosis
return FALSE
reset_streak()
- Tornado(attacker, defender)
- return TRUE
+ return Tornado(attacker, defender)
if(findtext(streak,THROWBACK_COMBO))
if(attacker == defender)//helps using apotheosis
return FALSE
reset_streak()
- Throwback(attacker, defender)
- return TRUE
+ return Throwback(attacker, defender)
if(findtext(streak,PLASMA_COMBO))
reset_streak()
if(attacker == defender && !nobomb)
- Apotheosis(attacker, defender)
- else
- Plasma(attacker, defender)
- return TRUE
+ return Apotheosis(attacker, defender)
+ return Plasma(attacker, defender)
return FALSE
/datum/martial_art/plasma_fist/proc/Tornado(mob/living/attacker, mob/living/defender)
attacker.say("TORNADO SWEEP!", forced="plasma fist")
- dance_rotate(attacker, CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), attacker.loc, 'sound/weapons/punch1.ogg', 15, TRUE, -1))
-
- var/datum/action/cooldown/spell/aoe/repulse/tornado_spell = new(src)
+ dance_rotate(attacker, CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), attacker, 'sound/weapons/punch1.ogg', 15, TRUE, -1))
tornado_spell.cast(attacker)
- qdel(tornado_spell)
-
- log_combat(attacker, defender, "tornado sweeped(Plasma Fist)")
- return
+ log_combat(attacker, defender, "tornado sweeped (Plasma Fist)")
+ return TRUE
/datum/martial_art/plasma_fist/proc/Throwback(mob/living/attacker, mob/living/defender)
- defender.visible_message(span_danger("[attacker] hits [defender] with Plasma Punch!"), \
- span_userdanger("You're hit with a Plasma Punch by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), null, attacker)
+ defender.visible_message(
+ span_danger("[attacker] hits [defender] with Plasma Punch!"),
+ span_userdanger("You're hit with a Plasma Punch by [attacker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ null,
+ attacker,
+ )
to_chat(attacker, span_danger("You hit [defender] with Plasma Punch!"))
- playsound(defender.loc, 'sound/weapons/punch1.ogg', 50, TRUE, -1)
+ playsound(defender, 'sound/weapons/punch1.ogg', 50, TRUE, -1)
var/atom/throw_target = get_edge_target_turf(defender, get_dir(defender, get_step_away(defender, attacker)))
defender.throw_at(throw_target, 200, 4,attacker)
attacker.say("HYAH!", forced="plasma fist")
log_combat(attacker, defender, "threw back (Plasma Fist)")
- return
+ return TRUE
/datum/martial_art/plasma_fist/proc/Plasma(mob/living/attacker, mob/living/defender)
- var/hasclient = defender.client ? TRUE : FALSE
+ var/hasclient = !!defender.client
attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
- playsound(defender.loc, 'sound/weapons/punch1.ogg', 50, TRUE, -1)
+ playsound(defender, 'sound/weapons/punch1.ogg', 50, TRUE, -1)
attacker.say("PLASMA FIST!", forced="plasma fist")
- defender.visible_message(span_danger("[attacker] hits [defender] with THE PLASMA FIST TECHNIQUE!"), \
- span_userdanger("You're suddenly hit with THE PLASMA FIST TECHNIQUE by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), null, attacker)
+ defender.visible_message(
+ span_danger("[attacker] hits [defender] with THE PLASMA FIST TECHNIQUE!"),
+ span_userdanger("You're suddenly hit with THE PLASMA FIST TECHNIQUE by [attacker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ null,
+ attacker,
+ )
to_chat(attacker, span_danger("You hit [defender] with THE PLASMA FIST TECHNIQUE!"))
log_combat(attacker, defender, "gibbed (Plasma Fist)")
var/turf/Dturf = get_turf(defender)
@@ -71,13 +82,15 @@
defender.gib(DROP_ALL_REMAINS)
if(nobomb)
return
+
if(!hasclient)
to_chat(attacker, span_warning("Taking this plasma energy for your [span_notice("Apotheosis")] would bring dishonor to the clan!"))
new /obj/effect/temp_visual/plasma_soul(Dturf)//doesn't beam to you, so it just hangs around and poofs.
- return
+
else if(plasma_power >= plasma_cap)
to_chat(attacker, span_warning("You cannot power up your [span_notice("Apotheosis")] any more!"))
new /obj/effect/temp_visual/plasma_soul(Dturf)//doesn't beam to you, so it just hangs around and poofs.
+
else
plasma_power += plasma_increment
to_chat(attacker, span_nicegreen("Power increasing! Your [span_notice("Apotheosis")] is now at power level [plasma_power]!"))
@@ -87,6 +100,7 @@
flash_color(attacker, flash_color = "#9C00FF", flash_time = 3 SECONDS)
animate(attacker, color = oldcolor, time = 3 SECONDS)
+ return TRUE
/datum/martial_art/plasma_fist/proc/Apotheosis(mob/living/user, mob/living/target)
user.say("APOTHEOSIS!!", forced="plasma fist")
@@ -109,12 +123,13 @@
to_chat(user, span_userdanger("The explosion knocks your soul out of your body!"))
user.ghostize(FALSE) //prevents... horrible memes just believe me
- user.apply_damage(rand(50,70), BRUTE)
+ user.apply_damage(rand(50, 70), BRUTE, wound_bonus = CANT_WOUND)
addtimer(CALLBACK(src, PROC_REF(Apotheosis_end), user), 6 SECONDS)
playsound(boomspot, 'sound/weapons/punch1.ogg', 50, TRUE, -1)
explosion(user, devastation_range = plasma_power, heavy_impact_range = plasma_power*2, light_impact_range = plasma_power*4, ignorecap = TRUE, explosion_cause = src)
plasma_power = 1 //just in case there is any clever way to cause it to happen again
+ return TRUE
/datum/martial_art/plasma_fist/proc/Apotheosis_end(mob/living/dying)
dying.remove_traits(list(TRAIT_FORCED_STANDING, TRAIT_BOMBIMMUNE), type)
@@ -124,24 +139,29 @@
dying.death()
/datum/martial_art/plasma_fist/harm_act(mob/living/attacker, mob/living/defender)
+ if(defender.check_block(attacker, 10, attacker.name, UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
+
add_to_streak("H", defender)
- if(check_streak(attacker, defender))
- return TRUE
- return FALSE
+ return check_streak(attacker, defender) ? MARTIAL_ATTACK_SUCCESS : MARTIAL_ATTACK_INVALID
/datum/martial_art/plasma_fist/disarm_act(mob/living/attacker, mob/living/defender)
+ if(defender.check_block(attacker, 0, attacker.name, UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
add_to_streak("D", defender)
if(check_streak(attacker, defender))
- return TRUE
+ return MARTIAL_ATTACK_SUCCESS
if(attacker == defender)//there is no disarming yourself, so we need to let plasma fist user know
to_chat(attacker, span_notice("You have added a disarm to your streak."))
- return FALSE
+ return MARTIAL_ATTACK_FAIL
+ return MARTIAL_ATTACK_INVALID
/datum/martial_art/plasma_fist/grab_act(mob/living/attacker, mob/living/defender)
+ if(defender.check_block(attacker, 0, "[attacker]'s grab", UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
+
add_to_streak("G", defender)
- if(check_streak(attacker, defender))
- return TRUE
- return FALSE
+ return check_streak(attacker, defender) ? MARTIAL_ATTACK_SUCCESS : MARTIAL_ATTACK_INVALID
/mob/living/proc/plasma_fist_help()
set name = "Recall Teachings"
diff --git a/code/datums/martial/psychotic_brawl.dm b/code/datums/martial/psychotic_brawl.dm
index 671867f4252..9ba78f9ef45 100644
--- a/code/datums/martial/psychotic_brawl.dm
+++ b/code/datums/martial/psychotic_brawl.dm
@@ -24,6 +24,11 @@
attacker.Stun(2 SECONDS)
atk_verb = "cried looking at"
if(3)
+ if(defender.check_block(attacker, 0, "[attacker]'s grab", UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
+ if(attacker.body_position == LYING_DOWN)
+ return MARTIAL_ATTACK_INVALID
+
if(attacker.grab_state >= GRAB_AGGRESSIVE)
defender.grabbedby(attacker, 1)
else
@@ -33,22 +38,37 @@
defender.stop_pulling()
if(grab_attack)
log_combat(attacker, defender, "grabbed", addition="aggressively")
- defender.visible_message(span_warning("[attacker] violently grabs [defender]!"), \
- span_userdanger("You're violently grabbed by [attacker]!"), span_hear("You hear sounds of aggressive fondling!"), null, attacker)
+ defender.visible_message(
+ span_warning("[attacker] violently grabs [defender]!"),
+ span_userdanger("You're violently grabbed by [attacker]!"),
+ span_hear("You hear sounds of aggressive fondling!"),
+ null,
+ attacker,
+ )
to_chat(attacker, span_danger("You violently grab [defender]!"))
attacker.setGrabState(GRAB_AGGRESSIVE) //Instant aggressive grab
else
log_combat(attacker, defender, "grabbed", addition="passively")
attacker.setGrabState(GRAB_PASSIVE)
if(4)
- attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
atk_verb = "headbutt"
- defender.visible_message(span_danger("[attacker] [atk_verb]s [defender]!"), \
- span_userdanger("You're [atk_verb]ed by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), null, attacker)
+ var/defender_damage = rand(5, 10)
+ if(defender.check_block(attacker, defender_damage, "[attacker]'s [atk_verb]", UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
+
+ attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
+ attacker.emote("flip")
+ defender.visible_message(
+ span_danger("[attacker] [atk_verb]s [defender]!"),
+ span_userdanger("You're [atk_verb]ed by [attacker]!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ null,
+ attacker,
+ )
to_chat(attacker, span_danger("You [atk_verb] [defender]!"))
- playsound(get_turf(defender), 'sound/weapons/punch1.ogg', 40, TRUE, -1)
- defender.apply_damage(rand(5,10), attacker.get_attack_type(), BODY_ZONE_HEAD)
- attacker.apply_damage(rand(5,10), attacker.get_attack_type(), BODY_ZONE_HEAD)
+ playsound(defender, 'sound/weapons/punch1.ogg', 40, TRUE, -1)
+ defender.apply_damage(defender_damage, attacker.get_attack_type(), BODY_ZONE_HEAD)
+ attacker.apply_damage(rand(5, 10), attacker.get_attack_type(), BODY_ZONE_HEAD)
if(iscarbon(defender))
var/mob/living/carbon/carbon_defender = defender
if(!istype(carbon_defender.head, /obj/item/clothing/head/helmet/) && !istype(carbon_defender.head, /obj/item/clothing/head/utility/hardhat))
@@ -56,19 +76,29 @@
attacker.Stun(rand(1 SECONDS, 4.5 SECONDS))
defender.Stun(rand(0.5 SECONDS, 3 SECONDS))
if(5,6)
- attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
atk_verb = pick("kick", "hit", "slam")
- defender.visible_message(span_danger("[attacker] [atk_verb]s [defender] with such inhuman strength that it sends [defender.p_them()] flying backwards!"), \
- span_userdanger("You're [atk_verb]ed by [attacker] with such inhuman strength that it sends you flying backwards!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), null, attacker)
+ if(defender.check_block(attacker, 0, "[attacker]'s [atk_verb]", UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
+
+ attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
+ defender.visible_message(
+ span_danger("[attacker] [atk_verb]s [defender] with such inhuman strength that it sends [defender.p_them()] flying backwards!"),
+ span_userdanger("You're [atk_verb]ed by [attacker] with such inhuman strength that it sends you flying backwards!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ null,
+ attacker,
+ )
to_chat(attacker, span_danger("You [atk_verb] [defender] with such inhuman strength that it sends [defender.p_them()] flying backwards!"))
- defender.apply_damage(rand(15,30), attacker.get_attack_type())
- playsound(get_turf(defender), 'sound/effects/meteorimpact.ogg', 25, TRUE, -1)
+ defender.apply_damage(rand(15, 30), attacker.get_attack_type())
+ playsound(defender, 'sound/effects/meteorimpact.ogg', 25, TRUE, -1)
var/throwtarget = get_edge_target_turf(attacker, get_dir(attacker, get_step_away(defender, attacker)))
defender.throw_at(throwtarget, 4, 2, attacker)//So stuff gets tossed around at the same time.
defender.Paralyze(6 SECONDS)
if(7,8)
- return FALSE //Resume default behaviour
+ return MARTIAL_ATTACK_INVALID //Resume default behaviour
if(atk_verb)
log_combat(attacker, defender, "[atk_verb] (Psychotic Brawling)")
- return TRUE
+ return MARTIAL_ATTACK_SUCCESS
+
+ return MARTIAL_ATTACK_FAIL
diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm
index 1275bd2da74..a52d2724ca2 100644
--- a/code/datums/martial/sleeping_carp.dm
+++ b/code/datums/martial/sleeping_carp.dm
@@ -8,69 +8,77 @@
allow_temp_override = FALSE
help_verb = /mob/living/proc/sleeping_carp_help
display_combos = TRUE
+ /// List of traits applied to users of this martial art.
var/list/scarp_traits = list(TRAIT_NOGUNS, TRAIT_HARDLY_WOUNDED, TRAIT_NODISMEMBER, TRAIT_HEAVY_SLEEPER)
-/datum/martial_art/the_sleeping_carp/teach(mob/living/target, make_temporary = FALSE)
- . = ..()
- if(!.)
- return
- target.add_traits(scarp_traits, SLEEPING_CARP_TRAIT)
- RegisterSignal(target, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby))
- RegisterSignal(target, COMSIG_ATOM_PRE_BULLET_ACT, PROC_REF(hit_by_projectile))
- target.faction |= FACTION_CARP //:D
-
-/datum/martial_art/the_sleeping_carp/on_remove(mob/living/target)
- target.remove_traits(scarp_traits, SLEEPING_CARP_TRAIT)
- UnregisterSignal(target, COMSIG_ATOM_ATTACKBY)
- UnregisterSignal(target, COMSIG_ATOM_PRE_BULLET_ACT)
- target.faction -= FACTION_CARP //:(
+/datum/martial_art/the_sleeping_carp/on_teach(mob/living/new_holder)
. = ..()
+ new_holder.add_traits(scarp_traits, SLEEPING_CARP_TRAIT)
+ RegisterSignal(new_holder, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby))
+ RegisterSignal(new_holder, COMSIG_ATOM_PRE_BULLET_ACT, PROC_REF(hit_by_projectile))
+ new_holder.faction |= FACTION_CARP //:D
+
+/datum/martial_art/the_sleeping_carp/on_remove(mob/living/remove_from)
+ remove_from.remove_traits(scarp_traits, SLEEPING_CARP_TRAIT)
+ UnregisterSignal(remove_from, list(COMSIG_ATOM_ATTACKBY, COMSIG_ATOM_PRE_BULLET_ACT))
+ remove_from.faction -= FACTION_CARP //:(
+ return ..()
/datum/martial_art/the_sleeping_carp/proc/check_streak(mob/living/attacker, mob/living/defender)
if(findtext(streak,STRONG_PUNCH_COMBO))
reset_streak()
- strongPunch(attacker, defender)
- return TRUE
+ return strongPunch(attacker, defender)
+
if(findtext(streak,LAUNCH_KICK_COMBO))
reset_streak()
- launchKick(attacker, defender)
- return TRUE
+ return launchKick(attacker, defender)
+
if(findtext(streak,DROP_KICK_COMBO))
reset_streak()
- dropKick(attacker, defender)
- return TRUE
+ return dropKick(attacker, defender)
+
return FALSE
///Gnashing Teeth: Harm Harm, consistent 20 force punch on every second harm punch
/datum/martial_art/the_sleeping_carp/proc/strongPunch(mob/living/attacker, mob/living/defender)
- ///this var is so that the strong punch is always aiming for the body part the user is targeting and not trying to apply to the chest before deviating
+ // this var is so that the strong punch is always aiming for the body part the user is targeting and not trying to apply to the chest before deviating
var/obj/item/bodypart/affecting = defender.get_bodypart(defender.get_random_valid_zone(attacker.zone_selected))
attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
var/atk_verb = pick("precisely kick", "brutally chop", "cleanly hit", "viciously slam")
- defender.visible_message(span_danger("[attacker] [atk_verb]s [defender]!"), \
- span_userdanger("[attacker] [atk_verb]s you!"), null, null, attacker)
+ defender.visible_message(
+ span_danger("[attacker] [atk_verb]s [defender]!"),
+ span_userdanger("[attacker] [atk_verb]s you!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ null,
+ attacker,
+ )
to_chat(attacker, span_danger("You [atk_verb] [defender]!"))
playsound(defender, 'sound/weapons/punch1.ogg', 25, TRUE, -1)
log_combat(attacker, defender, "strong punched (Sleeping Carp)")
defender.apply_damage(20, attacker.get_attack_type(), affecting)
- return
+ return TRUE
///Crashing Wave Kick: Harm Disarm combo, throws people seven tiles backwards
/datum/martial_art/the_sleeping_carp/proc/launchKick(mob/living/attacker, mob/living/defender)
attacker.do_attack_animation(defender, ATTACK_EFFECT_KICK)
- defender.visible_message(span_warning("[attacker] kicks [defender] square in the chest, sending them flying!"), \
- span_userdanger("You are kicked square in the chest by [attacker], sending you flying!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker)
- playsound(get_turf(attacker), 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
+ defender.visible_message(
+ span_warning("[attacker] kicks [defender] square in the chest, sending them flying!"),
+ span_userdanger("You are kicked square in the chest by [attacker], sending you flying!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
+ playsound(attacker, 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
var/atom/throw_target = get_edge_target_turf(defender, attacker.dir)
defender.throw_at(throw_target, 7, 4, attacker)
defender.apply_damage(15, attacker.get_attack_type(), BODY_ZONE_CHEST, wound_bonus = CANT_WOUND)
log_combat(attacker, defender, "launchkicked (Sleeping Carp)")
- return
+ return TRUE
///Keelhaul: Disarm Disarm combo, knocks people down and deals substantial stamina damage, and also discombobulates them. Knocks objects out of their hands if they're already on the ground.
/datum/martial_art/the_sleeping_carp/proc/dropKick(mob/living/attacker, mob/living/defender)
attacker.do_attack_animation(defender, ATTACK_EFFECT_KICK)
- playsound(get_turf(attacker), 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
+ playsound(attacker, 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
if(defender.body_position == STANDING_UP)
defender.Knockdown(4 SECONDS)
defender.visible_message(span_warning("[attacker] kicks [defender] in the head, sending them face first into the floor!"), \
@@ -83,28 +91,32 @@
defender.adjust_dizzy_up_to(10 SECONDS, 10 SECONDS)
defender.adjust_temp_blindness_up_to(2 SECONDS, 10 SECONDS)
log_combat(attacker, defender, "dropkicked (Sleeping Carp)")
- return
+ return TRUE
/datum/martial_art/the_sleeping_carp/grab_act(mob/living/attacker, mob/living/defender)
if(!can_deflect(attacker)) //allows for deniability
- return ..()
+ return MARTIAL_ATTACK_INVALID
+
+ if(defender.check_block(attacker, 0, "[attacker]'s grab", UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
add_to_streak("G", defender)
if(check_streak(attacker, defender))
- return TRUE
+ return MARTIAL_ATTACK_SUCCESS
+
var/grab_log_description = "grabbed"
attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
playsound(defender, 'sound/weapons/punch1.ogg', 25, TRUE, -1)
if(defender.stat != DEAD && !defender.IsUnconscious() && defender.getStaminaLoss() >= 80) //We put our target to sleep.
defender.visible_message(
- span_danger("[attacker] carefully pinch a nerve in [defender]'s neck, knocking them out cold"),
+ span_danger("[attacker] carefully pinch a nerve in [defender]'s neck, knocking them out cold!"),
span_userdanger("[attacker] pinches something in your neck, and you fall unconscious!"),
)
grab_log_description = "grabbed and nerve pinched"
defender.Unconscious(10 SECONDS)
defender.apply_damage(20, STAMINA)
log_combat(attacker, defender, "[grab_log_description] (Sleeping Carp)")
- return ..()
+ return MARTIAL_ATTACK_INVALID // normal grab
/datum/martial_art/the_sleeping_carp/harm_act(mob/living/attacker, mob/living/defender)
if(attacker.grab_state == GRAB_KILL \
@@ -129,36 +141,45 @@
defender.investigate_log("has had [defender.p_their()] neck snapped by [attacker].", INVESTIGATE_DEATHS)
return MARTIAL_ATTACK_SUCCESS
+ var/atk_verb = pick("kick", "chop", "hit", "slam")
+ var/final_damage = rand(10, 15)
+ if(defender.check_block(attacker, final_damage, "[attacker]'s [atk_verb]", UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
+
add_to_streak("H", defender)
if(check_streak(attacker, defender))
return MARTIAL_ATTACK_SUCCESS
var/obj/item/bodypart/affecting = defender.get_bodypart(defender.get_random_valid_zone(attacker.zone_selected))
attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
- var/atk_verb = pick("kick", "chop", "hit", "slam")
- defender.visible_message(span_danger("[attacker] [atk_verb]s [defender]!"), \
- span_userdanger("[attacker] [atk_verb]s you!"), null, null, attacker)
+ defender.visible_message(
+ span_danger("[attacker] [atk_verb]s [defender]!"),
+ span_userdanger("[attacker] [atk_verb]s you!"),
+ span_hear("You hear a sickening sound of flesh hitting flesh!"),
+ null,
+ attacker,
+ )
to_chat(attacker, span_danger("You [atk_verb] [defender]!"))
-
- defender.apply_damage(rand(10,15), attacker.get_attack_type(), affecting, wound_bonus = CANT_WOUND)
+ defender.apply_damage(final_damage, attacker.get_attack_type(), affecting, wound_bonus = CANT_WOUND)
playsound(defender, 'sound/weapons/punch1.ogg', 25, TRUE, -1)
log_combat(attacker, defender, "punched (Sleeping Carp)")
return MARTIAL_ATTACK_SUCCESS
/datum/martial_art/the_sleeping_carp/disarm_act(mob/living/attacker, mob/living/defender)
if(!can_deflect(attacker)) //allows for deniability
- return ..()
+ return MARTIAL_ATTACK_INVALID
+ if(defender.check_block(attacker, 0, attacker.name, UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
add_to_streak("D", defender)
if(check_streak(attacker, defender))
- return TRUE
+ return MARTIAL_ATTACK_SUCCESS
attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
playsound(defender, 'sound/weapons/punch1.ogg', 25, TRUE, -1)
defender.apply_damage(20, STAMINA)
log_combat(attacker, defender, "disarmed (Sleeping Carp)")
-
- return ..()
+ return MARTIAL_ATTACK_INVALID // normal disarm
/datum/martial_art/the_sleeping_carp/proc/can_deflect(mob/living/carp_user)
if(!can_use(carp_user) || !carp_user.throw_mode) //FF EDIT, old: if(!can_use(carp_user) || !carp_user.throw_mode)
diff --git a/code/datums/martial/wrestling.dm b/code/datums/martial/wrestling.dm
index a28c2e75646..23591852ec9 100644
--- a/code/datums/martial/wrestling.dm
+++ b/code/datums/martial/wrestling.dm
@@ -18,13 +18,32 @@ If you make a derivative work from this code, you must include this notification
/datum/martial_art/wrestling
name = "Wrestling"
id = MARTIALART_WRESTLING
- var/datum/action/slam/slam = new/datum/action/slam()
- var/datum/action/throw_wrassle/throw_wrassle = new/datum/action/throw_wrassle()
- var/datum/action/kick/kick = new/datum/action/kick()
- var/datum/action/strike/strike = new/datum/action/strike()
- var/datum/action/drop/drop = new/datum/action/drop()
+ VAR_PRIVATE/datum/action/slam/slam
+ VAR_PRIVATE/datum/action/throw_wrassle/throw_wrassle
+ VAR_PRIVATE/datum/action/kick/kick
+ VAR_PRIVATE/datum/action/strike/strike
+ VAR_PRIVATE/datum/action/drop/drop
+
+/datum/martial_art/wrestling/New()
+ . = ..()
+ slam = new(src)
+ throw_wrassle = new(src)
+ kick = new(src)
+ strike = new(src)
+ drop = new(src)
+
+/datum/martial_art/wrestling/Destroy()
+ slam = null
+ throw_wrassle = null
+ kick = null
+ strike = null
+ drop = null
+ return ..()
/datum/martial_art/wrestling/proc/check_streak(mob/living/attacker, mob/living/defender)
+ if(defender.check_block(attacker, 10, "[attacker]'s [streak]", UNARMED_ATTACK))
+ return FALSE
+
switch(streak)
if("drop")
streak = ""
@@ -51,10 +70,11 @@ If you make a derivative work from this code, you must include this notification
/datum/action/slam
name = "Slam (Cinch) - Slam a grappled opponent into the floor."
button_icon_state = "wrassle_slam"
+ check_flags = AB_CHECK_INCAPACITATED|AB_CHECK_HANDS_BLOCKED|AB_CHECK_CONSCIOUS
/datum/action/slam/Trigger(trigger_flags)
- if(owner.incapacitated())
- to_chat(owner, span_warning("You can't WRESTLE while you're OUT FOR THE COUNT."))
+ . = ..()
+ if(!.)
return
owner.visible_message(span_danger("[owner] prepares to BODY SLAM!"), "Your next attack will be a BODY SLAM.")
owner.mind.martial_art.streak = "slam"
@@ -62,10 +82,11 @@ If you make a derivative work from this code, you must include this notification
/datum/action/throw_wrassle
name = "Throw (Cinch) - Spin a cinched opponent around and throw them."
button_icon_state = "wrassle_throw"
+ check_flags = AB_CHECK_INCAPACITATED|AB_CHECK_HANDS_BLOCKED|AB_CHECK_CONSCIOUS
/datum/action/throw_wrassle/Trigger(trigger_flags)
- if(owner.incapacitated())
- to_chat(owner, span_warning("You can't WRESTLE while you're OUT FOR THE COUNT."))
+ . = ..()
+ if(!.)
return
owner.visible_message(span_danger("[owner] prepares to THROW!"), "Your next attack will be a THROW.")
owner.mind.martial_art.streak = "throw"
@@ -73,10 +94,11 @@ If you make a derivative work from this code, you must include this notification
/datum/action/kick
name = "Kick - A powerful kick, sends people flying away from you. Also useful for escaping from bad situations."
button_icon_state = "wrassle_kick"
+ check_flags = AB_CHECK_INCAPACITATED|AB_CHECK_CONSCIOUS // This is supposed to be usable while cuffed but it probably isn't
/datum/action/kick/Trigger(trigger_flags)
- if(owner.incapacitated())
- to_chat(owner, span_warning("You can't WRESTLE while you're OUT FOR THE COUNT."))
+ . = ..()
+ if(!.)
return
owner.visible_message(span_danger("[owner] prepares to KICK!"), "Your next attack will be a KICK.")
owner.mind.martial_art.streak = "kick"
@@ -84,10 +106,11 @@ If you make a derivative work from this code, you must include this notification
/datum/action/strike
name = "Strike - Hit a neaby opponent with a quick attack."
button_icon_state = "wrassle_strike"
+ check_flags = AB_CHECK_INCAPACITATED|AB_CHECK_HANDS_BLOCKED|AB_CHECK_CONSCIOUS
/datum/action/strike/Trigger(trigger_flags)
- if(owner.incapacitated())
- to_chat(owner, span_warning("You can't WRESTLE while you're OUT FOR THE COUNT."))
+ . = ..()
+ if(!.)
return
owner.visible_message(span_danger("[owner] prepares to STRIKE!"), "Your next attack will be a STRIKE.")
owner.mind.martial_art.streak = "strike"
@@ -95,37 +118,36 @@ If you make a derivative work from this code, you must include this notification
/datum/action/drop
name = "Drop - Smash down onto an opponent."
button_icon_state = "wrassle_drop"
+ check_flags = AB_CHECK_INCAPACITATED|AB_CHECK_HANDS_BLOCKED
/datum/action/drop/Trigger(trigger_flags)
- if(owner.incapacitated())
- to_chat(owner, span_warning("You can't WRESTLE while you're OUT FOR THE COUNT."))
+ . = ..()
+ if(!.)
return
owner.visible_message(span_danger("[owner] prepares to LEG DROP!"), "Your next attack will be a LEG DROP.")
owner.mind.martial_art.streak = "drop"
-/datum/martial_art/wrestling/teach(mob/living/owner, make_temporary=FALSE)
- if(..())
- to_chat(owner, span_userdanger("SNAP INTO A THIN TIM!"))
- to_chat(owner, span_danger("Place your cursor over a move at the top of the screen to see what it does."))
- drop.Grant(owner)
- kick.Grant(owner)
- slam.Grant(owner)
- throw_wrassle.Grant(owner)
- strike.Grant(owner)
-
-/datum/martial_art/wrestling/on_remove(mob/living/owner)
- to_chat(owner, span_userdanger("You no longer feel that the tower of power is too sweet to be sour..."))
- drop.Remove(owner)
- kick.Remove(owner)
- slam.Remove(owner)
- throw_wrassle.Remove(owner)
- strike.Remove(owner)
+/datum/martial_art/wrestling/on_teach(mob/living/new_holder)
+ . = ..()
+ to_chat(new_holder, span_userdanger("SNAP INTO A THIN TIM!"))
+ to_chat(new_holder, span_danger("Place your cursor over a move at the top of the screen to see what it does."))
+ drop.Grant(new_holder)
+ kick.Grant(new_holder)
+ slam.Grant(new_holder)
+ throw_wrassle.Grant(new_holder)
+ strike.Grant(new_holder)
+
+/datum/martial_art/wrestling/on_remove(mob/living/remove_from)
+ to_chat(remove_from, span_userdanger("You no longer feel that the tower of power is too sweet to be sour..."))
+ drop?.Remove(remove_from)
+ kick?.Remove(remove_from)
+ slam?.Remove(remove_from)
+ throw_wrassle?.Remove(remove_from)
+ strike?.Remove(remove_from)
+ return ..()
/datum/martial_art/wrestling/harm_act(mob/living/attacker, mob/living/defender)
- if(check_streak(attacker, defender))
- return 1
- log_combat(attacker, defender, "punched with wrestling")
- ..()
+ return check_streak(attacker, defender) ? MARTIAL_ATTACK_SUCCESS : MARTIAL_ATTACK_INVALID
/datum/martial_art/wrestling/proc/throw_wrassle(mob/living/attacker, mob/living/defender)
if(!defender)
@@ -444,36 +466,46 @@ If you make a derivative work from this code, you must include this notification
return
/datum/martial_art/wrestling/disarm_act(mob/living/attacker, mob/living/defender)
- if(check_streak(attacker, defender))
- return 1
- log_combat(attacker, defender, "wrestling-disarmed")
- ..()
+ return check_streak(attacker, defender) ? MARTIAL_ATTACK_SUCCESS : MARTIAL_ATTACK_INVALID
/datum/martial_art/wrestling/grab_act(mob/living/attacker, mob/living/defender)
if(check_streak(attacker, defender))
- return 1
+ return MARTIAL_ATTACK_SUCCESS
+ if(defender.check_block(attacker, 0, "[attacker]'s grab", UNARMED_ATTACK))
+ return MARTIAL_ATTACK_FAIL
if(attacker.pulling == defender)
- return 1
+ return MARTIAL_ATTACK_FAIL
attacker.start_pulling(defender)
- defender.visible_message(span_danger("[attacker] gets [defender] in a cinch!"), \
- span_userdanger("You're put into a cinch by [attacker]!"), span_hear("You hear aggressive shuffling!"), COMBAT_MESSAGE_RANGE, attacker)
+ defender.visible_message(
+ span_danger("[attacker] gets [defender] in a cinch!"),
+ span_userdanger("You're put into a cinch by [attacker]!"),
+ span_hear("You hear aggressive shuffling!"),
+ COMBAT_MESSAGE_RANGE,
+ attacker,
+ )
to_chat(attacker, span_danger("You get [defender] in a cinch!"))
defender.Stun(rand(6 SECONDS, 10 SECONDS))
log_combat(attacker, defender, "cinched")
- return 1
+ return MARTIAL_ATTACK_SUCCESS
/obj/item/storage/belt/champion/wrestling
name = "Wrestling Belt"
- var/datum/martial_art/wrestling/style = new
+ var/datum/martial_art/wrestling/style
+
+/obj/item/storage/belt/champion/wrestling/Initialize(mapload)
+ . = ..()
+ style = new()
+ style.allow_temp_override = FALSE
+
+/obj/item/storage/belt/champion/wrestling/Destroy()
+ QDEL_NULL(style)
+ return ..()
/obj/item/storage/belt/champion/wrestling/equipped(mob/user, slot)
. = ..()
if(slot & ITEM_SLOT_BELT)
style.teach(user, TRUE)
- return
/obj/item/storage/belt/champion/wrestling/dropped(mob/user)
. = ..()
- if(user.get_item_by_slot(ITEM_SLOT_BELT) == src)
- style.remove(user)
- return
+ style.fully_remove(user)
diff --git a/code/datums/mind/_mind.dm b/code/datums/mind/_mind.dm
index e33065651a3..23921bd84af 100644
--- a/code/datums/mind/_mind.dm
+++ b/code/datums/mind/_mind.dm
@@ -53,7 +53,6 @@
/// Martial art on this mind
var/datum/martial_art/martial_art
- var/static/default_martial_art = new/datum/martial_art
/// List of antag datums on this mind
var/list/antag_datums
/// this mind's ANTAG_HUD should have this icon_state
@@ -107,7 +106,6 @@
/datum/mind/New(_key)
key = _key
- martial_art = default_martial_art
init_known_skills()
set_assigned_role(SSjob.GetJobType(/datum/job/unassigned)) // Unassigned by default.
@@ -181,9 +179,9 @@
new_character.mind.set_current(null)
var/mob/living/old_current = current
- if(current)
+ if(old_current)
//transfer anyone observing the old character to the new one
- current.transfer_observers_to(new_character)
+ old_current.transfer_observers_to(new_character)
// Offload all mind languages from the old holder to a temp one
var/datum/language_holder/empty/temp_holder = new()
@@ -205,7 +203,7 @@
if(iscarbon(new_character))
var/mob/living/carbon/carbon_character = new_character
carbon_character.last_mind = src
- transfer_martial_arts(new_character)
+
RegisterSignal(new_character, COMSIG_LIVING_DEATH, PROC_REF(set_death_time))
if(active || force_key_move)
new_character.key = key //now transfer the key to link the client to our new body
@@ -214,7 +212,9 @@
new_character.client.init_verbs() // re-initialize character specific verbs
SEND_SIGNAL(src, COMSIG_MIND_TRANSFERRED, old_current)
- SEND_SIGNAL(current, COMSIG_MOB_MIND_TRANSFERRED_INTO)
+ SEND_SIGNAL(current, COMSIG_MOB_MIND_TRANSFERRED_INTO, old_current)
+ if(!isnull(old_current))
+ SEND_SIGNAL(old_current, COMSIG_MOB_MIND_TRANSFERRED_OUT_OF, current)
//I cannot trust you fucks to do this properly
/datum/mind/proc/set_original_character(new_original_character)
@@ -517,19 +517,6 @@
usr = current
traitor_panel()
-/datum/mind/proc/transfer_martial_arts(mob/living/new_character)
- if(!ishuman(new_character))
- return
- if(martial_art)
- if(martial_art.base) //Is the martial art temporary?
- martial_art.remove(new_character)
- else
- martial_art.teach(new_character)
-
-/datum/mind/proc/has_martialart(string)
- if(martial_art && martial_art.id == string)
- return martial_art
- return FALSE
/datum/mind/proc/get_ghost(even_if_they_cant_reenter, ghosts_with_clients)
for(var/mob/dead/observer/G in (ghosts_with_clients ? GLOB.player_list : GLOB.dead_mob_list))
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 39892126c13..990f9812126 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -69,7 +69,7 @@
var/movement_type = GROUND
var/atom/movable/pulling
- var/grab_state = 0
+ var/grab_state = GRAB_PASSIVE
/// The strongest grab we can acomplish
var/max_grab = GRAB_KILL
var/throwforce = 0
diff --git a/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm b/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm
index 2a5c776709c..70dd58f9f27 100644
--- a/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm
@@ -79,15 +79,12 @@ Fluoride Stare: After someone says 5 words, blah blah blah...
icon_state = "liver"
greyscale_config = /datum/greyscale_config/mutant_organ
greyscale_colors = GONDOLA_COLORS
- /// instance of the martial art granted on insertion
- var/datum/martial_art/hugs_of_the_gondola/pax_hugs
/obj/item/organ/internal/liver/gondola/Initialize(mapload)
. = ..()
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/gondola)
AddElement(/datum/element/noticable_organ, "left arm has small needles breaching the skin all over it.", BODY_ZONE_L_ARM)
AddElement(/datum/element/noticable_organ, "right arm has small needles breaching the skin all over it.", BODY_ZONE_R_ARM)
- pax_hugs = new
/obj/item/organ/internal/liver/gondola/Insert(mob/living/carbon/liver_owner, special, movement_flags)
. = ..()
@@ -100,14 +97,13 @@ Fluoride Stare: After someone says 5 words, blah blah blah...
else
to_chat(liver_owner, span_warning("You feel like something would be happening to your arms right now... if you still had them."))
to_chat(liver_owner, span_notice("Hugging a target will pacify them, but you won't be able to carry much of anything anymore."))
- pax_hugs.teach(liver_owner)
RegisterSignal(liver_owner, COMSIG_HUMAN_EQUIPPING_ITEM, PROC_REF(on_owner_equipping_item))
RegisterSignal(liver_owner, COMSIG_LIVING_TRY_PULL, PROC_REF(on_owner_try_pull))
+ RegisterSignal(liver_owner, COMSIG_CARBON_HELPED, PROC_REF(on_hug))
/obj/item/organ/internal/liver/gondola/Remove(mob/living/carbon/liver_owner, special, movement_flags)
. = ..()
- pax_hugs.remove(liver_owner)
- UnregisterSignal(liver_owner, list(COMSIG_HUMAN_EQUIPPING_ITEM, COMSIG_LIVING_TRY_PULL))
+ UnregisterSignal(liver_owner, list(COMSIG_HUMAN_EQUIPPING_ITEM, COMSIG_LIVING_TRY_PULL, COMSIG_CARBON_HELPED))
/// signal sent when prompting if an item can be equipped
/obj/item/organ/internal/liver/gondola/proc/on_owner_equipping_item(mob/living/carbon/human/owner, obj/item/equip_target, slot)
@@ -130,6 +126,18 @@ Fluoride Stare: After someone says 5 words, blah blah blah...
item_target.balloon_alert(owner, "too weak to pull this!")
return COMSIG_LIVING_CANCEL_PULL
+/obj/item/organ/internal/liver/gondola/proc/on_hug(mob/living/carbon/human/source, mob/living/carbon/hugged)
+ SIGNAL_HANDLER
+
+ var/list/covered_body_zones = source.get_covered_body_zones()
+ var/pax_injected = 4
+ if(BODY_ZONE_L_ARM in covered_body_zones)
+ pax_injected -= 2
+ if(BODY_ZONE_R_ARM in covered_body_zones)
+ pax_injected -= 2
+ if(pax_injected > 0 && hugged.reagents?.add_reagent(/datum/reagent/pax, pax_injected))
+ to_chat(hugged, span_warning("You feel a tiny prick!"))
+
#undef GONDOLA_ORGAN_COLOR
#undef GONDOLA_SCLERA_COLOR
#undef GONDOLA_PUPIL_COLOR
diff --git a/code/game/objects/items/granters/martial_arts/_martial_arts.dm b/code/game/objects/items/granters/martial_arts/_martial_arts.dm
index 7646faec2c6..b7d186c99c0 100644
--- a/code/game/objects/items/granters/martial_arts/_martial_arts.dm
+++ b/code/game/objects/items/granters/martial_arts/_martial_arts.dm
@@ -9,7 +9,7 @@
/obj/item/book/granter/martial/can_learn(mob/user)
if(!martial)
CRASH("Someone attempted to learn [type], which did not have a martial arts set.")
- if(user.mind.has_martialart(initial(martial.id)))
+ if(istype(user.mind?.martial_art, martial))
to_chat(user, span_warning("You already know [martial_name]!"))
return FALSE
return TRUE
@@ -19,7 +19,12 @@
return TRUE
/obj/item/book/granter/martial/on_reading_finished(mob/user)
- to_chat(user, "[greet]")
var/datum/martial_art/martial_to_learn = new martial()
- martial_to_learn.teach(user)
+ if(!martial_to_learn.teach(user))
+ to_chat(user, span_warning("You attempt to learn [martial_name] from [src], \
+ but your current knowledge of martial arts conflicts with the new style, so it just doesn't stick with you."))
+ uses += 1 // Return the use
+ return
+
+ to_chat(user, "[greet]")
user.log_message("learned the martial art [martial_name] ([martial_to_learn])", LOG_ATTACK, color = "orange")
diff --git a/code/game/objects/items/granters/martial_arts/cqc.dm b/code/game/objects/items/granters/martial_arts/cqc.dm
index 697541e0216..b2191997586 100644
--- a/code/game/objects/items/granters/martial_arts/cqc.dm
+++ b/code/game/objects/items/granters/martial_arts/cqc.dm
@@ -10,7 +10,7 @@
"Lock... Kick...",
"Strike their abdomen, neck and back for critical damage...",
"Slam... Lock...",
- "I could probably combine this with some other martial arts!",
+ "I could probably combine this with some other martial arts! ...Wait, that's illegal.",
"Words that kill...",
"The last and final moment is yours...",
)
diff --git a/code/game/objects/items/implants/implant_krav_maga.dm b/code/game/objects/items/implants/implant_krav_maga.dm
index fa3ccd1ccb2..4d921269beb 100644
--- a/code/game/objects/items/implants/implant_krav_maga.dm
+++ b/code/game/objects/items/implants/implant_krav_maga.dm
@@ -3,7 +3,7 @@
desc = "Teaches you the arts of Krav Maga in 5 short instructional videos beamed directly into your eyeballs."
icon = 'icons/obj/scrolls.dmi'
icon_state ="scroll2"
- var/datum/martial_art/krav_maga/style = new
+ var/datum/martial_art/krav_maga/style
/obj/item/implant/krav_maga/get_data()
var/dat = {"Implant Specifications:
@@ -13,17 +13,23 @@
Function: Teaches even the clumsiest host the arts of Krav Maga."}
return dat
+/obj/item/implant/krav_maga/Initialize(mapload)
+ . = ..()
+ style = new()
+ style.allow_temp_override = FALSE
+
+/obj/item/implant/krav_maga/Destroy()
+ QDEL_NULL(style)
+ return ..()
+
/obj/item/implant/krav_maga/activate()
. = ..()
- var/mob/living/carbon/human/H = imp_in
- if(!ishuman(H))
+ if(isnull(imp_in.mind))
return
- if(!H.mind)
+ if(style.fully_remove(imp_in))
return
- if(H.mind.has_martialart(MARTIALART_KRAVMAGA))
- style.remove(H)
- else
- style.teach(H,1)
+
+ style.teach(imp_in, TRUE)
/obj/item/implanter/krav_maga
name = "implanter (krav maga)"
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 5e39bbdd6d8..423adb71044 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -181,7 +181,7 @@
pushed_mob.Knockdown(30)
pushed_mob.apply_damage(10, BRUTE)
pushed_mob.apply_damage(40, STAMINA)
- if(user.mind?.martial_art.smashes_tables && user.mind?.martial_art.can_use(user))
+ if(user.mind?.martial_art?.smashes_tables && user.mind?.martial_art.can_use(user))
deconstruct(FALSE)
playsound(pushed_mob, 'sound/effects/tableslam.ogg', 90, TRUE)
pushed_mob.visible_message(span_danger("[user] slams [pushed_mob] onto \the [src]!"), \
@@ -198,7 +198,7 @@
banged_limb?.receive_damage(30, wound_bonus = extra_wound)
pushed_mob.apply_damage(60, STAMINA)
take_damage(50)
- if(user.mind?.martial_art.smashes_tables && user.mind?.martial_art.can_use(user))
+ if(user.mind?.martial_art?.smashes_tables && user.mind?.martial_art.can_use(user))
deconstruct(FALSE)
playsound(pushed_mob, 'sound/effects/bang.ogg', 90, TRUE)
pushed_mob.visible_message(span_danger("[user] smashes [pushed_mob]'s [banged_limb.plaintext_zone] against \the [src]!"),
diff --git a/code/modules/library/skill_learning/job_skillchips/chef.dm b/code/modules/library/skill_learning/job_skillchips/chef.dm
index 589978452e1..75bc494543c 100644
--- a/code/modules/library/skill_learning/job_skillchips/chef.dm
+++ b/code/modules/library/skill_learning/job_skillchips/chef.dm
@@ -19,5 +19,5 @@
style.teach(user, make_temporary = TRUE)
/obj/item/skillchip/job/chef/on_deactivate(mob/living/carbon/user, silent = FALSE)
- style.remove(user)
+ style.fully_remove(user)
return ..()
diff --git a/code/modules/mob/living/carbon/alien/adult/adult.dm b/code/modules/mob/living/carbon/alien/adult/adult.dm
index 92f3febef2c..2cab03d670e 100644
--- a/code/modules/mob/living/carbon/alien/adult/adult.dm
+++ b/code/modules/mob/living/carbon/alien/adult/adult.dm
@@ -74,12 +74,6 @@ GLOBAL_LIST_INIT(strippable_alien_humanoid_items, create_strippable_list(list(
name = "[name] ([numba])"
real_name = name
-/mob/living/carbon/alien/adult/proc/grab(mob/living/carbon/human/target)
- if(target.check_block(src, 0, "[target]'s grab"))
- return FALSE
- target.grabbedby(src)
- return TRUE
-
/mob/living/carbon/alien/adult/setGrabState(newstate)
if(newstate == grab_state)
return
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 4ec78f728aa..31530240bd1 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -60,7 +60,7 @@
take_bodypart_damage(5 + 5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5)
else if(!iscarbon(hit_atom) && extra_speed)
take_bodypart_damage(5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5)
- visible_message(span_danger("[src] crashes into [hit_atom][extra_speed ? " really hard" : ""]"),\
+ visible_message(span_danger("[src] crashes into [hit_atom][extra_speed ? " really hard" : ""]!"),\
span_userdanger("You violently crash into [hit_atom][extra_speed ? " extra hard" : ""]!"))
log_combat(hit_atom, src, "crashes ")
oof_noise = TRUE
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index aa778643491..d0717972d59 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -612,7 +612,7 @@
if (!IS_ORGANIC_LIMB(limb))
. += (limb.brute_dam * limb.body_damage_coeff) + (limb.burn_dam * limb.body_damage_coeff)
-/mob/living/carbon/grabbedby(mob/living/carbon/user, supress_message = FALSE, grabbed_part) // NOVA EDIT CHANGE - ORIGINAL: /mob/living/carbon/grabbedby(mob/living/carbon/user, supress_message = FALSE)
+/mob/living/carbon/grabbedby(mob/living/user, supress_message = FALSE, grabbed_part) // NOVA EDIT CHANGE - ORIGINAL: /mob/living/carbon/grabbedby(mob/living/user, supress_message = FALSE)
if(user != src)
return ..()
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index 0de67ef055d..e7561bf4a60 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -1132,9 +1132,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(SEND_SIGNAL(target, COMSIG_CARBON_PRE_HELP, user, attacker_style) & COMPONENT_BLOCK_HELP_ACT)
return TRUE
- if(attacker_style?.help_act(user, target) == MARTIAL_ATTACK_SUCCESS)
- return TRUE
-
if(target.body_position == STANDING_UP || (target.appears_alive() && target.stat != SOFT_CRIT && target.stat != HARD_CRIT))
target.help_shake_act(user)
if(target != user)
@@ -1143,115 +1140,104 @@ GLOBAL_LIST_EMPTY(features_by_species)
user.do_cpr(target)
-/datum/species/proc/grab(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
- if(attacker_style?.grab_act(user, target) == MARTIAL_ATTACK_SUCCESS)
- return TRUE
- target.grabbedby(user)
- return TRUE
-
///This proc handles punching damage. IMPORTANT: Our owner is the TARGET and not the USER in this proc. For whatever reason...
/datum/species/proc/harm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
if(HAS_TRAIT(user, TRAIT_PACIFISM) && !attacker_style?.pacifist_style)
to_chat(user, span_warning("You don't want to harm [target]!"))
return FALSE
- if(attacker_style?.harm_act(user,target) == MARTIAL_ATTACK_SUCCESS)
- return TRUE
- else
- var/obj/item/organ/internal/brain/brain = user.get_organ_slot(ORGAN_SLOT_BRAIN)
- var/obj/item/bodypart/attacking_bodypart
- if(brain)
- attacking_bodypart = brain.get_attacking_limb(target)
- if(!attacking_bodypart)
- attacking_bodypart = user.get_active_hand()
- var/atk_verb = attacking_bodypart.unarmed_attack_verb
- var/atk_effect = attacking_bodypart.unarmed_attack_effect
-
- if(atk_effect == ATTACK_EFFECT_BITE)
- if(user.is_mouth_covered(ITEM_SLOT_MASK))
- to_chat(user, span_warning("You can't [atk_verb] with your mouth covered!"))
- return FALSE
- user.do_attack_animation(target, atk_effect)
+ var/obj/item/organ/internal/brain/brain = user.get_organ_slot(ORGAN_SLOT_BRAIN)
+ var/obj/item/bodypart/attacking_bodypart
+ if(brain)
+ attacking_bodypart = brain.get_attacking_limb(target)
+ if(!attacking_bodypart)
+ attacking_bodypart = user.get_active_hand()
+ var/atk_verb = attacking_bodypart.unarmed_attack_verb
+ var/atk_effect = attacking_bodypart.unarmed_attack_effect
+
+ if(atk_effect == ATTACK_EFFECT_BITE)
+ if(user.is_mouth_covered(ITEM_SLOT_MASK))
+ to_chat(user, span_warning("You can't [atk_verb] with your mouth covered!"))
+ return FALSE
+ user.do_attack_animation(target, atk_effect)
- //has our target been shoved recently? If so, they're staggered and we get an easy hit.
- var/staggered = FALSE
+ //has our target been shoved recently? If so, they're staggered and we get an easy hit.
+ var/staggered = FALSE
- //Someone in a grapple is much more vulnerable to being harmed by punches.
- var/grappled = FALSE
+ //Someone in a grapple is much more vulnerable to being harmed by punches.
+ var/grappled = FALSE
- if(target.get_timed_status_effect_duration(/datum/status_effect/staggered))
- staggered = TRUE
+ if(target.get_timed_status_effect_duration(/datum/status_effect/staggered))
+ staggered = TRUE
- if(target.pulledby && target.pulledby.grab_state >= GRAB_AGGRESSIVE)
- grappled = TRUE
+ if(target.pulledby && target.pulledby.grab_state >= GRAB_AGGRESSIVE)
+ grappled = TRUE
- var/damage = rand(attacking_bodypart.unarmed_damage_low, attacking_bodypart.unarmed_damage_high)
- var/limb_accuracy = attacking_bodypart.unarmed_effectiveness
+ var/damage = rand(attacking_bodypart.unarmed_damage_low, attacking_bodypart.unarmed_damage_high)
+ var/limb_accuracy = attacking_bodypart.unarmed_effectiveness
- var/obj/item/bodypart/affecting = target.get_bodypart(target.get_random_valid_zone(user.zone_selected))
+ var/obj/item/bodypart/affecting = target.get_bodypart(target.get_random_valid_zone(user.zone_selected))
- var/miss_chance = 100//calculate the odds that a punch misses entirely. considers stamina and brute damage of the puncher. punches miss by default to prevent weird cases
- if(attacking_bodypart.unarmed_damage_low)
- if((target.body_position == LYING_DOWN) || HAS_TRAIT(user, TRAIT_PERFECT_ATTACKER) || staggered) //kicks and attacks against staggered targets never miss (provided your species deals more than 0 damage)
- miss_chance = 0
- else
- miss_chance = clamp(UNARMED_MISS_CHANCE_BASE - limb_accuracy + user.getStaminaLoss() + (user.getBruteLoss()*0.5), 0, UNARMED_MISS_CHANCE_MAX) //Limb miss chance + various damage. capped at 80 so there is at least a chance to land a hit.
-
- if(!damage || !affecting || prob(miss_chance))//future-proofing for species that have 0 damage/weird cases where no zone is targeted
- playsound(target.loc, attacking_bodypart.unarmed_miss_sound, 25, TRUE, -1)
- target.visible_message(span_danger("[user]'s [atk_verb] misses [target]!"), \
- span_danger("You avoid [user]'s [atk_verb]!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, user)
- to_chat(user, span_warning("Your [atk_verb] misses [target]!"))
- log_combat(user, target, "attempted to punch")
- return FALSE
+ var/miss_chance = 100//calculate the odds that a punch misses entirely. considers stamina and brute damage of the puncher. punches miss by default to prevent weird cases
+ if(attacking_bodypart.unarmed_damage_low)
+ if((target.body_position == LYING_DOWN) || HAS_TRAIT(user, TRAIT_PERFECT_ATTACKER) || staggered) //kicks and attacks against staggered targets never miss (provided your species deals more than 0 damage)
+ miss_chance = 0
+ else
+ miss_chance = clamp(UNARMED_MISS_CHANCE_BASE - limb_accuracy + user.getStaminaLoss() + (user.getBruteLoss()*0.5), 0, UNARMED_MISS_CHANCE_MAX) //Limb miss chance + various damage. capped at 80 so there is at least a chance to land a hit.
+
+ if(!damage || !affecting || prob(miss_chance))//future-proofing for species that have 0 damage/weird cases where no zone is targeted
+ playsound(target.loc, attacking_bodypart.unarmed_miss_sound, 25, TRUE, -1)
+ target.visible_message(span_danger("[user]'s [atk_verb] misses [target]!"), \
+ span_danger("You avoid [user]'s [atk_verb]!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, user)
+ to_chat(user, span_warning("Your [atk_verb] misses [target]!"))
+ log_combat(user, target, "attempted to punch")
+ return FALSE
- var/armor_block = target.run_armor_check(affecting, MELEE)
-
- playsound(target.loc, attacking_bodypart.unarmed_attack_sound || get_sfx("punch"), 25, TRUE, -1) // NOVA EDIT - ORIGINAL: playsound(target.loc, attacking_bodypart.unarmed_attack_sound, 25, TRUE, -1)
-
- if(grappled && attacking_bodypart.grappled_attack_verb)
- atk_verb = attacking_bodypart.grappled_attack_verb
- target.visible_message(span_danger("[user] [atk_verb]ed [target]!"), \
- span_userdanger("You're [atk_verb]ed by [user]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, user)
- to_chat(user, span_danger("You [atk_verb] [target]!"))
-
- target.lastattacker = user.real_name
- target.lastattackerckey = user.ckey
-
- if(user.limb_destroyer)
- target.dismembering_strike(user, affecting.body_zone)
-
- var/attack_direction = get_dir(user, target)
- var/attack_type = attacking_bodypart.attack_type
- var/unarmed_sharpness = attacking_bodypart.unarmed_sharpness //NOVA EDIT ADDITION - If unarmed damage sharpness needs to be taken into account.
- if(atk_effect == ATTACK_EFFECT_KICK || grappled) //kicks and punches when grappling bypass armor slightly.
- if(damage >= 9)
- target.force_say()
- log_combat(user, target, grappled ? "grapple punched" : "kicked")
- target.apply_damage(damage, attack_type, affecting, armor_block - limb_accuracy, attack_direction = attack_direction)
- target.apply_damage(damage*1.5, STAMINA, affecting, armor_block - limb_accuracy)
- else // Normal attacks do not gain the benefit of armor penetration.
- target.apply_damage(damage, attack_type, affecting, armor_block, attack_direction = attack_direction, sharpness = unarmed_sharpness) //NOVA EDIT - Applies sharpness if it does - ORIGINAL: target.apply_damage(damage, attack_type, affecting, armor_block, attack_direction = attack_direction)
- target.apply_damage(damage*1.5, STAMINA, affecting, armor_block)
- if(damage >= 9)
- target.force_say()
- log_combat(user, target, "punched")
-
- //If we rolled a punch high enough to hit our stun threshold, or our target is staggered and they have at least 40 damage+stamina loss, we knock them down
- if((target.stat != DEAD) && prob(limb_accuracy) || (target.stat != DEAD) && staggered && (target.getStaminaLoss() + user.getBruteLoss()) >= 40)
- target.visible_message(span_danger("[user] knocks [target] down!"), \
- span_userdanger("You're knocked down by [user]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, user)
- to_chat(user, span_danger("You knock [target] down!"))
- /* NOVA EDIT REMOVAL - Less combat lethality and hard stungs
- var/knockdown_duration = 4 SECONDS + (target.getStaminaLoss() + (target.getBruteLoss()*0.5))*0.8 //50 total damage = 4 second base stun + 4 second stun modifier = 8 second knockdown duration
- target.apply_effect(knockdown_duration, EFFECT_KNOCKDOWN, armor_block)
- */ // SKYRAT REMOVAL END
- target.StaminaKnockdown(20) //NOVA EDIT ADDITION
- log_combat(user, target, "got a stun punch with their previous punch")
+ var/armor_block = target.run_armor_check(affecting, MELEE)
+
+ playsound(target.loc, attacking_bodypart.unarmed_attack_sound, 25, TRUE, -1)
+
+ if(grappled && attacking_bodypart.grappled_attack_verb)
+ atk_verb = attacking_bodypart.grappled_attack_verb
+ target.visible_message(span_danger("[user] [atk_verb]ed [target]!"), \
+ span_userdanger("You're [atk_verb]ed by [user]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, user)
+ to_chat(user, span_danger("You [atk_verb] [target]!"))
+
+ target.lastattacker = user.real_name
+ target.lastattackerckey = user.ckey
+
+ if(user.limb_destroyer)
+ target.dismembering_strike(user, affecting.body_zone)
+
+ var/attack_direction = get_dir(user, target)
+ var/attack_type = attacking_bodypart.attack_type
+ var/unarmed_sharpness = attacking_bodypart.unarmed_sharpness //NOVA EDIT ADDITION - If unarmed damage sharpness needs to be taken into account.
+ if(atk_effect == ATTACK_EFFECT_KICK || grappled) //kicks and punches when grappling bypass armor slightly.
+ if(damage >= 9)
+ target.force_say()
+ log_combat(user, target, grappled ? "grapple punched" : "kicked")
+ target.apply_damage(damage, attack_type, affecting, armor_block - limb_accuracy, attack_direction = attack_direction)
+ target.apply_damage(damage*1.5, STAMINA, affecting, armor_block - limb_accuracy)
+ else // Normal attacks do not gain the benefit of armor penetration.
+ target.apply_damage(damage, attack_type, affecting, armor_block, attack_direction = attack_direction, sharpness = unarmed_sharpness) //NOVA EDIT - Applies sharpness if it does - ORIGINAL: target.apply_damage(damage, attack_type, affecting, armor_block, attack_direction = attack_direction)
+ target.apply_damage(damage*1.5, STAMINA, affecting, armor_block)
+ if(damage >= 9)
+ target.force_say()
+ log_combat(user, target, "punched")
+
+ //If we rolled a punch high enough to hit our stun threshold, or our target is staggered and they have at least 40 damage+stamina loss, we knock them down
+ if((target.stat != DEAD) && prob(limb_accuracy) || (target.stat != DEAD) && staggered && (target.getStaminaLoss() + user.getBruteLoss()) >= 40)
+ target.visible_message(span_danger("[user] knocks [target] down!"), \
+ span_userdanger("You're knocked down by [user]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, user)
+ to_chat(user, span_danger("You knock [target] down!"))
+ /* NOVA EDIT REMOVAL - Less combat lethality and hard stuns
+ var/knockdown_duration = 4 SECONDS + (target.getStaminaLoss() + (target.getBruteLoss()*0.5))*0.8 //50 total damage = 4 second base stun + 4 second stun modifier = 8 second knockdown duration
+ target.apply_effect(knockdown_duration, EFFECT_KNOCKDOWN, armor_block)
+ NOVA EDIT REMOVAL END */
+ target.StaminaKnockdown(20) //NOVA EDIT ADDITION
+ log_combat(user, target, "got a stun punch with their previous punch")
/datum/species/proc/disarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
- if(attacker_style?.disarm_act(user,target) == MARTIAL_ATTACK_SUCCESS)
- return TRUE
if(user.body_position != STANDING_UP)
return FALSE
if(user == target)
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 6afdcebd955..b0e4c79b86d 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -109,7 +109,7 @@
return FALSE
-/mob/living/carbon/human/grippedby(mob/living/user, instant = FALSE)
+/mob/living/carbon/human/grippedby(mob/living/carbon/user, instant = FALSE)
if(w_uniform)
w_uniform.add_fingerprint(user)
..()
@@ -158,10 +158,6 @@
var/dam_zone = pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
var/obj/item/bodypart/affecting = get_bodypart(get_random_valid_zone(dam_zone))
- var/martial_result = user.apply_martial_art(src, modifiers)
- if (martial_result != MARTIAL_ATTACK_INVALID)
- return martial_result
-
if(LAZYACCESS(modifiers, RIGHT_CLICK)) //Always drop item in hand, if no item, get stunned instead.
var/obj/item/I = get_active_held_item()
if(I && !(I.item_flags & ABSTRACT) && dropItemToGround(I))
diff --git a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm
index 59099cec175..a16d9e92637 100644
--- a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm
@@ -45,12 +45,13 @@
if(!H.dna.mutant_bodyparts["caps"] || H.dna.mutant_bodyparts["caps"][MUTANT_INDEX_NAME] != "None") // NOVA EDIT - Customization - ORIGINAL: if(!H.dna.features["caps"])
H.dna.mutant_bodyparts["caps"] = list(MUTANT_INDEX_NAME = "Round", MUTANT_INDEX_COLOR_LIST = list(H.hair_color)) // NOVA EDIT - Customization - ORIGINAL: H.dna.features["caps"] = "Round"
handle_mutant_bodyparts(H)
- mush = new(null)
- mush.teach(H)
+ mush = new()
+ mush.teach(C)
+ mush.allow_temp_override = FALSE
/datum/species/mush/on_species_loss(mob/living/carbon/C)
. = ..()
- mush.remove(C)
+ mush.fully_remove(C)
QDEL_NULL(mush)
/datum/species/mush/handle_chemical(datum/reagent/chem, mob/living/carbon/human/affected, seconds_per_tick, times_fired)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index a05a750d743..a9747c9d34a 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -2470,31 +2470,6 @@ GLOBAL_LIST_EMPTY(fire_appearances)
/mob/living/proc/get_attack_type()
return BRUTE
-
-/**
- * Apply a martial art move from src to target.
- *
- * This is used to process martial art attacks against nonhumans.
- * It is also used to process martial art attacks by nonhumans, even against humans
- * Human vs human attacks are handled in species code right now.
- */
-/mob/living/proc/apply_martial_art(mob/living/target, modifiers, is_grab = FALSE)
- if(HAS_TRAIT(target, TRAIT_MARTIAL_ARTS_IMMUNE))
- return MARTIAL_ATTACK_INVALID
- var/datum/martial_art/style = mind?.martial_art
- if (!style)
- return MARTIAL_ATTACK_INVALID
- // will return boolean below since it's not invalid
- if (is_grab)
- return style.grab_act(src, target)
- if (LAZYACCESS(modifiers, RIGHT_CLICK))
- return style.disarm_act(src, target)
- if(combat_mode)
- if (HAS_TRAIT(src, TRAIT_PACIFISM))
- return FALSE
- return style.harm_act(src, target)
- return style.help_act(src, target)
-
/**
* Returns an assoc list of assignments and minutes for updating a client's exp time in the databse.
*
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index e110ce6b11f..55cb51c97dd 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -227,17 +227,34 @@
adjust_fire_stacks(3)
ignite_mob()
-/mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = FALSE, grabbed_part) // NOVA EDIT CHANGE - ORIGINAL: /mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = FALSE)
+/**
+ * Called when a mob is grabbing another mob.
+ */
+/mob/living/proc/grab(mob/living/target)
+ if(!istype(target))
+ return FALSE
+ if(SEND_SIGNAL(src, COMSIG_LIVING_GRAB, target) & (COMPONENT_CANCEL_ATTACK_CHAIN|COMPONENT_SKIP_ATTACK))
+ return FALSE
+ if(target.check_block(src, 0, "[src]'s grab"))
+ return FALSE
+ target.grabbedby(src)
+ return TRUE
+
+/**
+ * Called when this mob is grabbed by another mob.
+ */
+/mob/living/proc/grabbedby(mob/living/user, supress_message = FALSE, grabbed_part) // NOVA EDIT CHANGE - ORIGINAL: /mob/living/proc/grabbedby(mob/living/user, supress_message = FALSE)
if(user == src || anchored || !isturf(user.loc))
return FALSE
if(!user.pulling || user.pulling != src)
user.start_pulling(src, supress_message = supress_message)
return
-
+ // This line arbitrarily prevents any non-carbon from upgrading grabs
+ if(!iscarbon(user))
+ return
if(!(status_flags & CANPUSH) || HAS_TRAIT(src, TRAIT_PUSHIMMUNE))
to_chat(user, span_warning("[src] can't be grabbed more aggressively!"))
return FALSE
-
if(user.grab_state >= GRAB_AGGRESSIVE && HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, span_warning("You don't want to risk hurting [src]!"))
return FALSE
@@ -371,17 +388,9 @@
if(operations.next_step(user, modifiers))
return TRUE
- var/martial_result = user.apply_martial_art(src, modifiers)
- if (martial_result != MARTIAL_ATTACK_INVALID)
- return martial_result
-
return FALSE
/mob/living/attack_paw(mob/living/carbon/human/user, list/modifiers)
- var/martial_result = user.apply_martial_art(src, modifiers)
- if (martial_result != MARTIAL_ATTACK_INVALID)
- return martial_result
-
if(LAZYACCESS(modifiers, RIGHT_CLICK))
user.disarm(src)
return TRUE
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index bb5f9169f67..e84758d9f58 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -1,5 +1,5 @@
-/mob/living/silicon/grippedby(mob/living/user, instant = FALSE)
+/mob/living/silicon/grippedby(mob/living/carbon/user, instant = FALSE)
return //can't upgrade a simple pull into a more aggressive grab.
/mob/living/silicon/get_ear_protection()//no ears
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 d794e493910..e256e4f2921 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -155,8 +155,8 @@
return FALSE
if(isgolem(victim) && victim.has_status_effect(/datum/status_effect/golem/gold))
return TRUE
- var/mob/living/carbon/human/human_victim = victim
- return human_victim.mind && istype(human_victim.mind.martial_art, /datum/martial_art/the_sleeping_carp)
+
+ return istype(victim.mind?.martial_art, /datum/martial_art/the_sleeping_carp)
/obj/effect/temp_visual/at_shield
name = "anti-toolbox field"
diff --git a/code/modules/unit_tests/spell_mindswap.dm b/code/modules/unit_tests/spell_mindswap.dm
index 133f9662d91..f598bcc7263 100644
--- a/code/modules/unit_tests/spell_mindswap.dm
+++ b/code/modules/unit_tests/spell_mindswap.dm
@@ -11,6 +11,10 @@
var/mob/living/carbon/human/swapper = allocate(/mob/living/carbon/human/consistent)
var/mob/living/carbon/human/to_swap = allocate(/mob/living/carbon/human/consistent)
+ swapper.real_name = "The Mindswapper"
+ swapper.name = swapper.real_name
+ to_swap.real_name = "The Guy Who Gets Mindswapped"
+ to_swap.name = to_swap.real_name
swapper.forceMove(run_loc_floor_bottom_left)
to_swap.forceMove(locate(run_loc_floor_bottom_left.x + 1, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z))
diff --git a/tgstation.dme b/tgstation.dme
index 980cca03970..c8e370dfcc5 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1666,7 +1666,6 @@
#include "code\datums\martial\_martial.dm"
#include "code\datums\martial\boxing.dm"
#include "code\datums\martial\cqc.dm"
-#include "code\datums\martial\hugs_of_the_gondola.dm"
#include "code\datums\martial\krav_maga.dm"
#include "code\datums\martial\mushpunch.dm"
#include "code\datums\martial\plasma_fist.dm"