Files
Bubberstation/code/datums/elements/table_smash.dm
GhomandGitHub 54df8cab2d Moved a few monkeys features out of species code (+ updated a couple infusion organs) (#96735)
## About The Pull Request
Title. I'm moving a few monkey features away from the species and into
its bodyparts and organs. I plan on following up with another PR for the
various `ismonkey()` in the code, as these are necessary changes for a
niche thing I'm ultimately working on.

Also one carp organ and one stoat organ both had bits of code made
redundant by available traits. This takes care of them.
Also removed the passwindow_on/off and passtable_on/off procs. We can
just register the associated trait signals on init for living mobs (plus
they were being misused on non-mobs).

## Why It's Good For The Game
Less code associated directly to the species and more to its body parts
and organs, which is basically something we've been doing for a few
years ~~(also I need it for skeletonized monkeys)~~.

## Changelog
N/A
2026-07-11 21:51:39 +01:00

204 lines
8.5 KiB
Plaintext

/// Component which allows mobs to be smashed onto this surface like a wrestling move
/datum/element/table_smash
element_flags = ELEMENT_BESPOKE
argument_hash_start_idx = 2
/// If true, mobs will be placed gently on the table even if they're in an aggressive grab
var/gentle_push
/// Passed proc call path for what to do after smashing harmfully into our 'table'-like obj
var/after_smash_proccall
/datum/element/table_smash/Attach(datum/target, gentle_push = FALSE, after_smash_proccall)
. = ..()
if (!isobj(target))
return ELEMENT_INCOMPATIBLE
src.gentle_push = gentle_push
src.after_smash_proccall = after_smash_proccall
RegisterSignal(target, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_interaction))
RegisterSignal(target, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_item_interaction))
var/static/list/loc_connections = list(
COMSIG_LIVING_DISARM_COLLIDE = TYPE_PROC_REF(/obj/structure, on_disarm_shoved_into),
)
target.AddElement(/datum/element/connect_loc, loc_connections)
/datum/element/table_smash/Detach(datum/source, ...)
. = ..()
UnregisterSignal(source, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ITEM_INTERACTION))
/// Called when someone clicks on our surface
/datum/element/table_smash/proc/on_interaction(datum/source, mob/user)
SIGNAL_HANDLER
var/obj/source_obj = source
if (!source_obj.Adjacent(user) || !user.pulling)
return
if (!isliving(user.pulling))
if (!(user.pulling.pass_flags & PASSTABLE))
return
user.Move_Pulled(source_obj)
if (user.pulling.loc == source_obj.loc)
user.visible_message(span_notice("[user] places [user.pulling] onto [source_obj]."),
span_notice("You place [user.pulling] onto [source_obj]."))
user.stop_pulling()
return COMPONENT_CANCEL_ATTACK_CHAIN
var/mob/living/pushed_mob = user.pulling
if (pushed_mob.buckled)
if (pushed_mob.buckled == source_obj)
//Already buckled to the object, you probably meant to unbuckle them
return
to_chat(user, span_warning("[pushed_mob] is buckled to [pushed_mob.buckled]!"))
return COMPONENT_CANCEL_ATTACK_CHAIN
INVOKE_ASYNC(src, PROC_REF(perform_table_smash), source_obj, user)
return COMPONENT_CANCEL_ATTACK_CHAIN
/// We have a mob being pressed onto the table, but how strongly?
/datum/element/table_smash/proc/perform_table_smash(obj/structure/table/table, mob/living/user)
var/mob/living/pushed_mob = user.pulling
if (user.combat_mode)
switch(user.grab_state)
if (GRAB_PASSIVE)
to_chat(user, span_warning("You need a better grip to do that!"))
return
if (GRAB_AGGRESSIVE)
if (gentle_push)
tableplace(user, pushed_mob, table)
else
tablepush(user, pushed_mob, table)
if (GRAB_NECK to GRAB_KILL)
tablelimbsmash(user, pushed_mob, table)
else
pushed_mob.visible_message(span_notice("[user] begins to place [pushed_mob] onto [table]..."), \
span_userdanger("[user] begins to place [pushed_mob] onto [table]..."))
if (do_after(user, 3.5 SECONDS, target = pushed_mob))
tableplace(user, pushed_mob, table)
else
return
user.stop_pulling()
/// Called when someone clicks on our surface with an item
/datum/element/table_smash/proc/on_item_interaction(obj/structure/table/table, mob/living/user, obj/item/item, modifiers)
SIGNAL_HANDLER
if (!istype(item, /obj/item/riding_offhand))
return NONE
var/obj/item/riding_offhand/riding_item = item
var/mob/living/carried_mob = riding_item.rider
if (carried_mob == user) //Piggyback user.
return NONE
INVOKE_ASYNC(src, PROC_REF(riding_offhand_act), user, item, table)
return ITEM_INTERACT_BLOCKING
/// Called when someone clicks on our surface using a fireman's carry
/datum/element/table_smash/proc/riding_offhand_act(mob/living/user, obj/item/riding_offhand/riding_item, obj/structure/table/table)
var/mob/living/carried_mob = riding_item.rider
if (user.combat_mode)
user.unbuckle_mob(carried_mob)
tablelimbsmash(user, carried_mob)
return ITEM_INTERACT_SUCCESS
var/tableplace_delay = 3.5 SECONDS
var/skills_space = ""
if (HAS_TRAIT(user, TRAIT_QUICKER_CARRY))
tableplace_delay = 2 SECONDS
skills_space = " expertly"
else if (HAS_TRAIT(user, TRAIT_QUICK_CARRY))
tableplace_delay = 2.75 SECONDS
skills_space = " quickly"
var/obj/item/organ/cyberimp/chest/spine/potential_spine = user.get_organ_slot(ORGAN_SLOT_SPINE)
if (istype(potential_spine))
tableplace_delay *= potential_spine.athletics_boost_multiplier
carried_mob.visible_message(span_notice("[user] begins to[skills_space] place [carried_mob] onto [table]..."),
span_userdanger("[user] begins to[skills_space] place [carried_mob] onto [table]..."))
if (!do_after(user, tableplace_delay, target = carried_mob))
return ITEM_INTERACT_BLOCKING
user.unbuckle_mob(carried_mob)
tableplace(user, carried_mob, table)
return ITEM_INTERACT_SUCCESS
/// Gently place the mob onto the table
/datum/element/table_smash/proc/tableplace(mob/living/user, mob/living/pushed_mob, obj/structure/table/table)
pushed_mob.forceMove(table.loc)
pushed_mob.set_resting(TRUE, TRUE)
pushed_mob.visible_message(span_notice("[user] places [pushed_mob] onto [table]."), \
span_notice("[user] places [pushed_mob] onto [table]."))
log_combat(user, pushed_mob, "placed", null, "onto [table]")
/// Aggressively smash the mob onto the table
/datum/element/table_smash/proc/tablepush(mob/living/user, mob/living/pushed_mob, obj/structure/table/table)
if (HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, span_danger("Throwing [pushed_mob] onto the table might hurt them!"))
return
var/passtable_key = REF(user)
ADD_TRAIT(pushed_mob, TRAIT_PASSTABLE, passtable_key)
for (var/obj/obj in user.loc.contents)
if (!obj.CanAllowThrough(pushed_mob))
return
pushed_mob.Move(table.loc)
REMOVE_TRAIT(pushed_mob, TRAIT_PASSTABLE, passtable_key)
if (pushed_mob.loc != table.loc) //Something prevented the tabling
return
pushed_mob.Knockdown(3 SECONDS)
pushed_mob.apply_damage(10, BRUTE)
pushed_mob.apply_damage(40, STAMINA)
playsound(pushed_mob, 'sound/effects/tableslam.ogg', 90, TRUE)
pushed_mob.visible_message(span_danger("[user] slams [pushed_mob] onto \the [table]!"), \
span_userdanger("[user] slams you onto \the [table]!"))
log_combat(user, pushed_mob, "tabled", null, "onto [table]")
pushed_mob.add_mood_event("table", /datum/mood_event/table)
SEND_SIGNAL(user, COMSIG_LIVING_TABLE_SLAMMING, pushed_mob, table)
if(after_smash_proccall)
call(table, after_smash_proccall)(pushed_mob)
/// Even more aggressively smash a single part of a mob onto the table
/datum/element/table_smash/proc/tablelimbsmash(mob/living/user, mob/living/pushed_mob, obj/structure/table/table)
pushed_mob.Knockdown(3 SECONDS)
var/obj/item/bodypart/banged_limb = pushed_mob.get_bodypart(user.zone_selected) || pushed_mob.get_bodypart(BODY_ZONE_HEAD)
var/extra_wound = 0
if (HAS_TRAIT(user, TRAIT_HULK))
extra_wound = 20
pushed_mob.apply_damage(30, BRUTE, banged_limb, wound_bonus = extra_wound)
pushed_mob.apply_damage(60, STAMINA)
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 [table]!"),
span_userdanger("[user] smashes your [banged_limb.plaintext_zone] against \the [table]"))
log_combat(user, pushed_mob, "head slammed", null, "against [table]")
pushed_mob.add_mood_event("table", /datum/mood_event/table_limbsmash, banged_limb)
table.take_damage(50)
SEND_SIGNAL(user, COMSIG_LIVING_TABLE_LIMB_SLAMMING, pushed_mob, table)
if(after_smash_proccall)
call(table, after_smash_proccall)(pushed_mob)
/// Called when someone is shoved into our tile
/obj/structure/proc/on_disarm_shoved_into(datum/source, mob/living/shover, mob/living/target, shove_flags, obj/item/weapon)
SIGNAL_HANDLER
if((shove_flags & SHOVE_KNOCKDOWN_BLOCKED) || !(shove_flags & SHOVE_BLOCKED))
return
target.Knockdown(SHOVE_KNOCKDOWN_TABLE, daze_amount = 3 SECONDS)
target.visible_message(span_danger("[shover.name] shoves [target.name] onto \the [src]!"),
span_userdanger("You're shoved onto \the [src] by [shover.name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, shover)
to_chat(shover, span_danger("You shove [target.name] onto \the [src]!"))
target.throw_at(src, 1, 1, null, FALSE) //1 speed throws with no spin are basically just forcemoves with a hard collision check
log_combat(shover, target, "shoved", "onto [src] (table)[weapon ? " with [weapon]" : ""]")
after_smash(target)
return COMSIG_LIVING_SHOVE_HANDLED
/// Called after someone is harmfully smashed onto us
/obj/structure/proc/after_smash(mob/living/smashed_onto)
return // This is mostly for our children