Files
Bubberstation/code/datums/martial/sleeping_carp.dm
T
MrMelbertandRoxy bc2215667f Re-refactors batons / Refactors attack chain force modifiers (#90809)
Melee attack chain now has a list passed along with it,
`attack_modifiers`, which you can stick force modifiers to change the
resulting attack

This is basically a soft implementation of damage packets until a more
definitive pr, but one that only applies to item attack chain, and not
unarmed attacks.

This change was done to facilitate a baton refactor - batons no longer
hack together their own attack chain, and are now integrated straight
into the real attack chain. This refactor itself was done because batons
don't send any attack signals, which has been annoying in the past (for
swing combat).

🆑 Melbert
refactor: Batons have been refactored again. Baton stuns now properly
count as an attack, when before it was a nothing. Report any oddities,
particularly in regards to harmbatonning vs normal batonning.
refactor: The method of adjusting item damage mid-attack has been
refactored - some affected items include the Nullblade and knives.
Report any strange happenings with damage numbers.
refactor: A few objects have been moved to the new interaction chain -
records consoles, mawed crucible, alien weeds and space vines, hedges,
restaurant portals, and some mobs - to name a few.
fix: Spears only deal bonus damage against secure lockers, not all
closet types (including crates)
/🆑
2025-05-22 21:30:07 -04:00

343 lines
16 KiB
Plaintext

#define STRONG_PUNCH_COMBO "HH"
#define LAUNCH_KICK_COMBO "HD"
#define DROP_KICK_COMBO "DD"
/datum/martial_art/the_sleeping_carp
name = "The Sleeping Carp"
id = MARTIALART_SLEEPINGCARP
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_TOSS_GUN_HARD, TRAIT_HARDLY_WOUNDED, TRAIT_NODISMEMBER, TRAIT_HEAVY_SLEEPER)
/datum/martial_art/the_sleeping_carp/activate_style(mob/living/new_holder)
. = ..()
new_holder.add_traits(scarp_traits, SLEEPING_CARP_TRAIT)
RegisterSignal(new_holder, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby))
RegisterSignal(new_holder, COMSIG_ATOM_PRE_BULLET_ACT, PROC_REF(hit_by_projectile))
new_holder.faction |= FACTION_CARP //:D
new_holder.grant_language(/datum/language/carptongue, ALL, type)
/datum/martial_art/the_sleeping_carp/deactivate_style(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 //:(
remove_from.remove_language(/datum/language/carptongue, ALL, type)
return ..()
/datum/martial_art/the_sleeping_carp/proc/check_streak(mob/living/attacker, mob/living/defender)
if(findtext(streak,STRONG_PUNCH_COMBO))
reset_streak()
return strongPunch(attacker, defender)
if(findtext(streak,LAUNCH_KICK_COMBO))
reset_streak()
return launchKick(attacker, defender)
if(findtext(streak,DROP_KICK_COMBO))
reset_streak()
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
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!"),
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/items/weapons/punch1.ogg', 25, TRUE, -1)
log_combat(attacker, defender, "strong punched (Sleeping Carp)")
defender.apply_damage(20, attacker.get_attack_type(), affecting)
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(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 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(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!"), \
span_userdanger("You are kicked in the head by [attacker], sending you crashing to the floor!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker)
else
defender.drop_all_held_items()
defender.visible_message(span_warning("[attacker] kicks [defender] in the head!"), \
span_userdanger("You are kicked in the head by [attacker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacker)
defender.apply_damage(40, STAMINA)
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 TRUE
/datum/martial_art/the_sleeping_carp/grab_act(mob/living/attacker, mob/living/defender)
if(!can_deflect(attacker)) //allows for deniability
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 MARTIAL_ATTACK_SUCCESS
var/grab_log_description = "grabbed"
attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
playsound(defender, 'sound/items/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_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 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 \
&& attacker.zone_selected == BODY_ZONE_HEAD \
&& attacker.pulling == defender \
&& defender.stat != DEAD \
)
var/obj/item/bodypart/head = defender.get_bodypart(BODY_ZONE_HEAD)
if(!isnull(head))
playsound(defender, 'sound/effects/wounds/crack1.ogg', 100)
defender.visible_message(
span_danger("[attacker] snaps the neck of [defender]!"),
span_userdanger("Your neck is snapped by [attacker]!"),
span_hear("You hear a sickening snap!"),
ignored_mobs = attacker
)
to_chat(attacker, span_danger("In a swift motion, you snap the neck of [defender]!"))
log_combat(attacker, defender, "snapped neck")
defender.apply_damage(100, BRUTE, BODY_ZONE_HEAD, wound_bonus=CANT_WOUND)
if(!HAS_TRAIT(defender, TRAIT_NODEATH))
defender.death()
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)
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(final_damage, attacker.get_attack_type(), affecting, wound_bonus = CANT_WOUND)
playsound(defender, 'sound/items/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 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 MARTIAL_ATTACK_SUCCESS
attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
playsound(defender, 'sound/items/weapons/punch1.ogg', 25, TRUE, -1)
defender.apply_damage(20, STAMINA)
log_combat(attacker, defender, "disarmed (Sleeping Carp)")
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)
return FALSE
if(INCAPACITATED_IGNORING(carp_user, INCAPABLE_GRAB)) //NO STUN
return FALSE
if(!(carp_user.mobility_flags & MOBILITY_USE)) //NO UNABLE TO USE
return FALSE
if(HAS_TRAIT(carp_user, TRAIT_HULK)) //NO HULK
return FALSE
if(!isturf(carp_user.loc)) //NO MOTHERFLIPPIN MECHS!
return FALSE
return TRUE
/datum/martial_art/the_sleeping_carp/proc/hit_by_projectile(mob/living/carp_user, obj/projectile/hitting_projectile, def_zone)
SIGNAL_HANDLER
if(!can_deflect(carp_user))
return NONE
carp_user.visible_message(
span_danger("[carp_user] effortlessly swats [hitting_projectile] aside! [carp_user.p_They()] can block bullets with [carp_user.p_their()] bare hands!"),
span_userdanger("You deflect [hitting_projectile]!"),
)
playsound(carp_user, SFX_BULLET_MISS, 75, TRUE)
hitting_projectile.firer = carp_user
hitting_projectile.set_angle(rand(0, 360))//SHING
return COMPONENT_BULLET_PIERCED
///Signal from getting attacked with an item, for a special interaction with touch spells
/datum/martial_art/the_sleeping_carp/proc/on_attackby(mob/living/carp_user, obj/item/attack_weapon, mob/attacker, list/modifiers)
SIGNAL_HANDLER
if(!istype(attack_weapon, /obj/item/melee/touch_attack))
return
if(!can_deflect(carp_user))
return
var/obj/item/melee/touch_attack/touch_weapon = attack_weapon
carp_user.visible_message(
span_danger("[carp_user] carefully dodges [attacker]'s [touch_weapon]!"),
span_userdanger("You take great care to remain untouched by [attacker]'s [touch_weapon]!"),
)
return COMPONENT_NO_AFTERATTACK
/// Verb added to humans who learn the art of the sleeping carp.
/mob/living/proc/sleeping_carp_help()
set name = "Recall Teachings"
set desc = "Remember the martial techniques of the Sleeping Carp clan."
set category = "Sleeping Carp"
to_chat(usr, "<b><i>You retreat inward and recall the teachings of the Sleeping Carp...</i></b>\n\
[span_notice("Gnashing Teeth")]: Punch Punch. Deal additional damage every second (consecutive) punch! Very good chance to wound!\n\
[span_notice("Crashing Wave Kick")]: Punch Shove. Launch your opponent away from you with incredible force!\n\
[span_notice("Keelhaul")]: Shove Shove. Nonlethally kick an opponent to the floor, knocking them down, discombobulating them and dealing substantial stamina damage. If they're already prone, disarm them as well.\n\
[span_notice("Grabs and Shoves")]: While in combat mode, your typical grab and shove do decent stamina damage. If you grab someone who has substantial amounts of stamina damage, you knock them out!\n\
<span class='notice'>While in combat mode (and not stunned, not a hulk, and not in a mech), you can reflect all projectiles that come your way, sending them back at the people who fired them! \n\
Also, you are more resilient against suffering wounds in combat, and your limbs cannot be dismembered. This grants you extra staying power during extended combat, especially against slashing and other bleeding weapons. \n\
You are not invincible, however- while you may not suffer debilitating wounds often, you must still watch your health and should have appropriate medical supplies for use during downtime. \n\
In addition, your training has imbued you with a loathing of guns, and you can no longer use them.</span>")
/obj/item/staff/bostaff
name = "bo staff"
desc = "A long, tall staff made of polished wood. Traditionally used in ancient old-Earth martial arts. Can be wielded to both kill and incapacitate."
force = 10
w_class = WEIGHT_CLASS_BULKY
slot_flags = ITEM_SLOT_BACK
throwforce = 20
throw_speed = 2
attack_verb_continuous = list("smashes", "slams", "whacks", "thwacks")
attack_verb_simple = list("smash", "slam", "whack", "thwack")
icon = 'icons/obj/weapons/staff.dmi'
icon_state = "bostaff0"
base_icon_state = "bostaff"
icon_angle = -135
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
block_chance = 50
/obj/item/staff/bostaff/Initialize(mapload)
. = ..()
AddComponent(/datum/component/two_handed, \
force_unwielded = 10, \
force_wielded = 24, \
)
/obj/item/staff/bostaff/update_icon_state()
icon_state = inhand_icon_state = "[base_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]"
return ..()
/obj/item/staff/bostaff/attack(mob/target, mob/living/user, list/modifiers, list/attack_modifiers)
add_fingerprint(user)
if((HAS_TRAIT(user, TRAIT_CLUMSY)) && prob(50))
to_chat(user, span_warning("You club yourself over the head with [src]."))
user.Paralyze(6 SECONDS)
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.apply_damage(2*force, BRUTE, BODY_ZONE_HEAD)
else
user.take_bodypart_damage(2*force)
return
if(iscyborg(target))
return ..()
if(!isliving(target))
return ..()
var/mob/living/carbon/C = target
if(C.stat)
to_chat(user, span_warning("It would be dishonorable to attack a foe while they cannot retaliate."))
return
if(LAZYACCESS(modifiers, RIGHT_CLICK))
if(!HAS_TRAIT(src, TRAIT_WIELDED))
return ..()
if(!ishuman(target))
return ..()
var/mob/living/carbon/human/H = target
var/list/fluffmessages = list("club", "smack", "broadside", "beat", "slam")
H.visible_message(span_warning("[user] [pick(fluffmessages)]s [H] with [src]!"), \
span_userdanger("[user] [pick(fluffmessages)]s you with [src]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), null, user)
to_chat(user, span_danger("You [pick(fluffmessages)] [H] with [src]!"))
playsound(get_turf(user), 'sound/effects/woodhit.ogg', 75, TRUE, -1)
H.adjustStaminaLoss(rand(13,20))
if(prob(10))
H.visible_message(span_warning("[H] collapses!"), \
span_userdanger("Your legs give out!"))
H.Paralyze(8 SECONDS)
if(H.staminaloss && !H.IsSleeping())
var/total_health = (H.health - H.staminaloss)
if(total_health <= HEALTH_THRESHOLD_CRIT && !H.stat)
H.visible_message(span_warning("[user] delivers a heavy hit to [H]'s head, knocking [H.p_them()] out cold!"), \
span_userdanger("You're knocked unconscious by [user]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), null, user)
to_chat(user, span_danger("You deliver a heavy hit to [H]'s head, knocking [H.p_them()] out cold!"))
H.SetSleeping(60 SECONDS)
H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 15, 150)
else
return ..()
/obj/item/staff/bostaff/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
if(!HAS_TRAIT(src, TRAIT_WIELDED))
return ..()
return FALSE
/obj/item/clothing/gloves/the_sleeping_carp
name = "carp gloves"
desc = "These gloves are capable of making people use The Sleeping Carp."
icon_state = "black"
greyscale_colors = COLOR_BLACK
cold_protection = HANDS
min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
heat_protection = HANDS
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
resistance_flags = NONE
/obj/item/clothing/gloves/the_sleeping_carp/Initialize(mapload)
. = ..()
AddComponent(/datum/component/martial_art_giver, /datum/martial_art/the_sleeping_carp)
#undef STRONG_PUNCH_COMBO
#undef LAUNCH_KICK_COMBO
#undef DROP_KICK_COMBO