Kicks Martial Arts out of the attack chain (yippee), makes it use signals, plus a large clean up of existing martial arts (#81097)

## About The Pull Request

- Kicks Martial Arts out of the attack chain. 
- All Martial Arts attacks are now handled via unarmed attack or grab
signals
- This means all martial arts are now technically on the living level,
allowing any mob that can unarmed attack to martial arts. Sort of. YMMV.

- All martial arts block checking is now handled by the arts themselves,
meaning you can selectively decide for a martial arts strike to not be
blocked. Maybe good for the future.

- A comprehensive cleanup of all existing martial arts. Improving var
names, code, adding some missing animation calls, etc.

Fixes #74829

## Why It's Good For The Game

Untangles the mess that is martial arts, making it a lot easier to work
with the attack chain and making it overall a ton more consistent.

## Changelog

🆑 Melbert
refactor: Big martial arts refactor, they should now overall act a ton
more consistent. Also technically any mob can do martial arts. Let me
know if something is funky.
/🆑

---------

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
This commit is contained in:
MrMelbert
2024-02-01 14:18:46 +00:00
committed by GitHub
co-authored by Ghom
parent 656ec2ffdf
commit e21dc5fec7
37 changed files with 1159 additions and 688 deletions
+3 -3
View File
@@ -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
@@ -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
@@ -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"
-1
View File
@@ -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"
+4 -24
View File
@@ -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)
+3 -2
View File
@@ -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
+2 -2
View File
@@ -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)
/**
+312 -52
View File
@@ -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)
remove_verb(holder_living, help_verb)
return
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(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)
+58 -42
View File
@@ -3,62 +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/teach(mob/living/new_holder, make_temporary)
if(!ishuman(new_holder))
return FALSE
return ..()
/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/disarm_act(mob/living/carbon/human/attacker, mob/living/defender)
attacker.balloon_alert(attacker, "can't disarm while boxing!")
return MARTIAL_ATTACK_FAIL
/datum/martial_art/boxing/harm_act(mob/living/attacker, mob/living/defender)
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)
playsound(defender.loc, 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)
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)
. = ..()
@@ -69,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)
+200 -128
View File
@@ -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)
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
if(defender.stat != CONSCIOUS)
return FALSE
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
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/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
/datum/martial_art/cqc/harm_act(mob/living/attacker, mob/living/defender)
if(!can_use(attacker))
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 ..()
@@ -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
+127 -59
View File
@@ -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."), "<b><i>Your next attack is cleared.</i></b>")
@@ -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."), "<b><i>Your next attack is cleared.</i></b>")
@@ -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."), "<b><i>Your next attack is cleared.</i></b>")
@@ -53,19 +68,20 @@
owner.visible_message(span_danger("[owner] assumes the Lung Punch stance!"), "<b><i>Your next attack will be a Lung Punch.</i></b>")
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,81 +100,134 @@
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("<span class='danger'>[attacker] disarms [defender]!</span>", \
"<span class='userdanger'>You're disarmed by [attacker]!</span>", "<span class='hear'>You hear aggressive shuffling!</span>", COMBAT_MESSAGE_RANGE, attacker)
to_chat(attacker, "<span class='danger'>You disarm [defender]!</span>")
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
var/datum/martial_art/krav_maga/style = new
clothing_traits = list(TRAIT_FAST_CUFFING)
var/datum/martial_art/krav_maga/style
/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)
. = ..()
@@ -167,8 +236,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"
+43 -26
View File
@@ -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("<b>Your attack was interrupted!</b>"))
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("<b>Your attack was interrupted!</b>"))
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 = "<I>Sapienza Ophioglossoides</I>: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 = "<I>Sapienza Ophioglossoides</I>: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]."))
+53 -33
View File
@@ -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>[span_notice("Apotheosis")]<span class='warning'> 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>[span_notice("Apotheosis")]<span class='warning'> 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>[span_notice("Apotheosis")]<span class='nicegreen'> 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")
@@ -108,12 +122,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)
@@ -123,24 +138,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"
+45 -15
View File
@@ -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
+63 -42
View File
@@ -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)
/datum/martial_art/the_sleeping_carp/on_teach(mob/living/new_holder)
. = ..()
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
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/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_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.combat_mode)
+81 -49
View File
@@ -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!"), "<b><i>Your next attack will be a BODY SLAM.</i></b>")
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!"), "<b><i>Your next attack will be a THROW.</i></b>")
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!"), "<b><i>Your next attack will be a KICK.</i></b>")
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!"), "<b><i>Your next attack will be a STRIKE.</i></b>")
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!"), "<b><i>Your next attack will be a LEG DROP.</i></b>")
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_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/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_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)
+6 -19
View File
@@ -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)
@@ -510,19 +510,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))
+1 -1
View File
@@ -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
@@ -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
@@ -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")
@@ -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...",
)
@@ -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 = {"<b>Implant Specifications:</b><BR>
@@ -13,17 +13,23 @@
<b>Function:</b> 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)"
+2 -2
View File
@@ -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]!"),
@@ -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 ..()
@@ -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
+1 -1
View File
@@ -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
@@ -566,7 +566,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)
/mob/living/carbon/grabbedby(mob/living/user, supress_message = FALSE)
if(user != src)
return ..()
@@ -1105,9 +1105,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)
@@ -1116,111 +1113,100 @@ 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
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
//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.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/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")
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/armor_block = target.run_armor_check(affecting, MELEE)
//has our target been shoved recently? If so, they're staggered and we get an easy hit.
var/staggered = FALSE
playsound(target.loc, attacking_bodypart.unarmed_attack_sound, 25, TRUE, -1)
//Someone in a grapple is much more vulnerable to being harmed by punches.
var/grappled = FALSE
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]!"))
if(target.get_timed_status_effect_duration(/datum/status_effect/staggered))
staggered = TRUE
target.lastattacker = user.real_name
target.lastattackerckey = user.ckey
if(target.pulledby && target.pulledby.grab_state >= GRAB_AGGRESSIVE)
grappled = TRUE
if(user.limb_destroyer)
target.dismembering_strike(user, affecting.body_zone)
var/damage = rand(attacking_bodypart.unarmed_damage_low, attacking_bodypart.unarmed_damage_high)
var/limb_accuracy = attacking_bodypart.unarmed_effectiveness
var/attack_direction = get_dir(user, target)
var/attack_type = attacking_bodypart.attack_type
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)
target.apply_damage(damage*1.5, STAMINA, affecting, armor_block)
if(damage >= 9)
target.force_say()
log_combat(user, target, "punched")
var/obj/item/bodypart/affecting = target.get_bodypart(target.get_random_valid_zone(user.zone_selected))
//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!"))
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)
log_combat(user, target, "got a stun punch with their previous punch")
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, 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
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)
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!"))
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)
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)
@@ -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))
@@ -42,12 +42,13 @@
/datum/species/mush/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
if(ishuman(C))
mush = new(null)
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)
@@ -87,4 +88,3 @@
return FALSE
return TRUE
-25
View File
@@ -2392,31 +2392,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.
*
+20 -11
View File
@@ -215,17 +215,34 @@
adjust_fire_stacks(3)
ignite_mob()
/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)
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
@@ -359,17 +376,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
@@ -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
@@ -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"
@@ -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))
-1
View File
@@ -1565,7 +1565,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"