diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 7e63ccb3..a3543eaf 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -252,6 +252,11 @@
SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone)
. = P.on_hit(src, 0, def_zone)
+//used on altdisarm() for special interactions between the shoved victim (target) and the src, with user being the one shoving the target on it.
+// IMPORTANT: if you wish to add a new own shove_act() to a certain object, remember to add SHOVABLE_ONTO to its obj_flags bitfied var first.
+/atom/proc/shove_act(mob/living/target, mob/living/user)
+ return FALSE
+
/atom/proc/in_contents_of(container)//can take class or object instance as argument
if(ispath(container))
if(istype(src.loc, container))
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index a2216580..9c08d100 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -21,6 +21,7 @@
anchored = TRUE
layer = TABLE_LAYER
climbable = TRUE
+ obj_flags = CAN_BE_HIT|SHOVABLE_ONTO
pass_flags = LETPASSTHROW //You can throw objects over this, despite it's density.")
var/frame = /obj/structure/table_frame
var/framestack = /obj/item/stack/rods
@@ -136,6 +137,15 @@
var/mob/living/carbon/human/H = pushed_mob
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "table", /datum/mood_event/table)
+/obj/structure/table/shove_act(mob/living/target, mob/living/user)
+ if(!target.resting)
+ target.Knockdown(SHOVE_KNOCKDOWN_TABLE)
+ user.visible_message("[user.name] shoves [target.name] onto \the [src]!",
+ "You shove [target.name] onto \the [src]!", null, COMBAT_MESSAGE_RANGE)
+ target.forceMove(src.loc)
+ log_combat(user, target, "shoved", "onto [src] (table)")
+ return TRUE
+
/obj/structure/table/attackby(obj/item/I, mob/user, params)
if(!(flags_1 & NODECONSTRUCT_1))
if(istype(I, /obj/item/screwdriver) && deconstruction_ready)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 6933872f..180ef378 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -457,6 +457,19 @@
if(ismob(A) || .)
A.ratvar_act()
+//called on /datum/species/proc/altdisarm()
+/turf/shove_act(mob/living/target, mob/living/user, pre_act = FALSE)
+ var/list/possibilities
+ for(var/obj/O in contents)
+ if(CHECK_BITFIELD(O.obj_flags, SHOVABLE_ONTO))
+ LAZYADD(possibilities, O)
+ else if(!O.CanPass(target, src))
+ return FALSE
+ if(possibilities)
+ var/obj/O = pick(possibilities)
+ return O.shove_act(target, user)
+ return FALSE
+
/turf/proc/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
underlay_appearance.icon = icon
underlay_appearance.icon_state = icon_state
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 39868088..ccd5ac5d 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1780,6 +1780,130 @@ GLOBAL_LIST_EMPTY(roundstart_races)
H.forcesay(GLOB.hit_appends) //forcesay checks stat already.
return TRUE
+/datum/species/proc/alt_spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style)
+ if(!istype(M))
+ return TRUE
+ CHECK_DNA_AND_SPECIES(M)
+ CHECK_DNA_AND_SPECIES(H)
+
+ if(!istype(M)) //sanity check for drones.
+ return TRUE
+ if(M.mind)
+ attacker_style = M.mind.martial_art
+ if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(M, 0, M.name, attack_type = UNARMED_ATTACK))
+ log_combat(M, H, "attempted to touch")
+ H.visible_message("[M] attempted to touch [H]!")
+ return TRUE
+ switch(M.a_intent)
+ if(INTENT_HELP)
+ if(M == H)
+ althelp(M, H, attacker_style)
+ return TRUE
+ return FALSE
+ if(INTENT_DISARM)
+ altdisarm(M, H, attacker_style)
+ return TRUE
+ return FALSE
+
+/datum/species/proc/althelp(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
+ if(user == target && istype(user))
+ if(user.getStaminaLoss() >= STAMINA_SOFTCRIT)
+ to_chat(user, "You're too exhausted for that.")
+ return
+ if(!user.resting)
+ to_chat(user, "You can only force yourself up if you're on the ground.")
+ return
+ user.visible_message("[user] forces [p_them()]self up to [p_their()] feet!", "You force yourself up to your feet!")
+ user.resting = 0
+ user.update_canmove()
+ user.adjustStaminaLossBuffered(user.stambuffer) //Rewards good stamina management by making it easier to instantly get up from resting
+ playsound(user, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
+
+/datum/species/proc/altdisarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
+ if(user.getStaminaLoss() >= STAMINA_SOFTCRIT)
+ to_chat(user, "You're too exhausted.")
+ return FALSE
+ if(target.check_block())
+ target.visible_message("[target] blocks [user]'s shoving attempt!")
+ return FALSE
+ if(attacker_style && attacker_style.disarm_act(user,target))
+ return TRUE
+ if(user.resting)
+ return FALSE
+ else
+ if(user == target)
+ return
+ user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
+ user.adjustStaminaLossBuffered(4)
+ playsound(target, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+
+ if(target.w_uniform)
+ target.w_uniform.add_fingerprint(user)
+ SEND_SIGNAL(target, COMSIG_HUMAN_DISARM_HIT, user, user.zone_selected)
+
+ if(!target.resting)
+ target.adjustStaminaLoss(5)
+
+ if(target.is_shove_knockdown_blocked())
+ return
+
+ var/turf/target_oldturf = target.loc
+ var/shove_dir = get_dir(user.loc, target_oldturf)
+ var/turf/target_shove_turf = get_step(target.loc, shove_dir)
+ var/mob/living/carbon/human/target_collateral_human
+ var/shove_blocked = FALSE //Used to check if a shove is blocked so that if it is knockdown logic can be applied
+
+ //Thank you based whoneedsspace
+ target_collateral_human = locate(/mob/living/carbon/human) in target_shove_turf.contents
+ if(target_collateral_human && target_collateral_human.resting)
+ shove_blocked = TRUE
+ else
+ target_collateral_human = null
+ target.Move(target_shove_turf, shove_dir)
+ if(get_turf(target) == target_oldturf)
+ shove_blocked = TRUE
+
+ if(shove_blocked && !target.buckled)
+ var/directional_blocked = !target.Adjacent(target_shove_turf)
+ var/targetatrest = target.resting
+ if((directional_blocked || (!target_collateral_human && !target_shove_turf.shove_act(target, user))) && !targetatrest)
+ target.Knockdown(SHOVE_KNOCKDOWN_SOLID)
+ user.visible_message("[user.name] shoves [target.name], knocking them down!",
+ "You shove [target.name], knocking them down!", null, COMBAT_MESSAGE_RANGE)
+ log_combat(user, target, "shoved", "knocking them down")
+ else if(target_collateral_human && !targetatrest)
+ target.Knockdown(SHOVE_KNOCKDOWN_HUMAN)
+ target_collateral_human.Knockdown(SHOVE_KNOCKDOWN_COLLATERAL)
+ user.visible_message("[user.name] shoves [target.name] into [target_collateral_human.name]!",
+ "You shove [target.name] into [target_collateral_human.name]!", null, COMBAT_MESSAGE_RANGE)
+ log_combat(user, target, "shoved", "into [target_collateral_human.name]")
+
+ else
+ user.visible_message("[user.name] shoves [target.name]!",
+ "You shove [target.name]!", null, COMBAT_MESSAGE_RANGE)
+ var/target_held_item = target.get_active_held_item()
+ var/knocked_item = FALSE
+ if(!is_type_in_typecache(target_held_item, GLOB.shove_disarming_types))
+ target_held_item = null
+ if(!target.has_movespeed_modifier(SHOVE_SLOWDOWN_ID))
+ target.add_movespeed_modifier(SHOVE_SLOWDOWN_ID, multiplicative_slowdown = SHOVE_SLOWDOWN_STRENGTH)
+ if(target_held_item)
+ target.visible_message("[target.name]'s grip on \the [target_held_item] loosens!",
+ "Your grip on \the [target_held_item] loosens!", null, COMBAT_MESSAGE_RANGE)
+ addtimer(CALLBACK(target, /mob/living/carbon/human/proc/clear_shove_slowdown), SHOVE_SLOWDOWN_LENGTH)
+ else if(target_held_item)
+ target.dropItemToGround(target_held_item)
+ knocked_item = TRUE
+ target.visible_message("[target.name] drops \the [target_held_item]!!",
+ "You drop \the [target_held_item]!!", null, COMBAT_MESSAGE_RANGE)
+ var/append_message = ""
+ if(target_held_item)
+ if(knocked_item)
+ append_message = "causing them to drop [target_held_item]"
+ else
+ append_message = "loosening their grip on [target_held_item]"
+ log_combat(user, target, "shoved", append_message)
+
/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H)
var/hit_percent = (100-(blocked+armor))/100
hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100
diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm
index 8c90fe20..04f3452f 100644
--- a/code/modules/recycling/disposal/bin.dm
+++ b/code/modules/recycling/disposal/bin.dm
@@ -119,14 +119,7 @@
stuff_mob_in(target, user)
/obj/machinery/disposal/proc/stuff_mob_in(mob/living/target, mob/living/user)
- if(!iscarbon(user) && !user.ventcrawler) //only carbon and ventcrawlers can climb into disposal by themselves.
- return
- if(!isturf(user.loc)) //No magically doing it from inside closets
- return
- if(target.buckled || target.has_buckled_mobs())
- return
- if(target.mob_size > MOB_SIZE_HUMAN)
- to_chat(user, "[target] doesn't fit inside [src]!")
+ if(!can_stuff_mob_in(target, user))
return
add_fingerprint(user)
if(user == target)
@@ -145,6 +138,19 @@
target.LAssailant = user
update_icon()
+/obj/machinery/disposal/proc/can_stuff_mob_in(mob/living/target, mob/living/user, pushing = FALSE)
+ if(!pushing && !iscarbon(user) && !user.ventcrawler) //only carbon and ventcrawlers can climb into disposal by themselves.
+ return FALSE
+ if(!isturf(user.loc)) //No magically doing it from inside closets
+ return FALSE
+ if(target.buckled || target.has_buckled_mobs())
+ return FALSE
+ if(target.mob_size > MOB_SIZE_HUMAN)
+ if(!pushing)
+ to_chat(user, "[target] doesn't fit inside [src]!")
+ return FALSE
+ return TRUE
+
/obj/machinery/disposal/relaymove(mob/user)
attempt_escape(user)
@@ -273,6 +279,7 @@
desc = "A pneumatic waste disposal unit."
icon_state = "disposal"
var/datum/oracle_ui/themed/nano/ui
+ obj_flags = CAN_BE_HIT | USES_TGUI | SHOVABLE_ONTO
/obj/machinery/disposal/bin/Initialize(mapload, obj/structure/disposalconstruct/make_from)
. = ..()
@@ -313,7 +320,7 @@
if(Adjacent(user))
return TRUE
return ..()
-
+
/obj/machinery/disposal/bin/oui_data(mob/user)
var/list/data = list()
@@ -368,6 +375,17 @@
else
return ..()
+/obj/machinery/disposal/bin/shove_act(mob/living/target, mob/living/user)
+ if(can_stuff_mob_in(target, user, TRUE))
+ target.Knockdown(SHOVE_KNOCKDOWN_SOLID)
+ target.forceMove(src)
+ user.visible_message("[user.name] shoves [target.name] into \the [src]!",
+ "You shove [target.name] into \the [src]!", null, COMBAT_MESSAGE_RANGE)
+ log_combat(user, target, "shoved", "into [src] (disposal bin)")
+ return TRUE
+ return FALSE
+
+
/obj/machinery/disposal/bin/flush()
..()
full_pressure = FALSE
diff --git a/modular_citadel/code/modules/mob/living/carbon/human/species.dm b/modular_citadel/code/modules/mob/living/carbon/human/species.dm
index 7e888ddd..52471504 100644
--- a/modular_citadel/code/modules/mob/living/carbon/human/species.dm
+++ b/modular_citadel/code/modules/mob/living/carbon/human/species.dm
@@ -1,162 +1,6 @@
/datum/species
var/should_draw_citadel = FALSE
-/datum/species/proc/alt_spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style)
- if(!istype(M))
- return TRUE
- CHECK_DNA_AND_SPECIES(M)
- CHECK_DNA_AND_SPECIES(H)
-
- if(!istype(M)) //sanity check for drones.
- return TRUE
- if(M.mind)
- attacker_style = M.mind.martial_art
- if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(M, 0, M.name, attack_type = UNARMED_ATTACK))
- log_combat(M, H, "attempted to touch")
- H.visible_message("[M] attempted to touch [H]!")
- return TRUE
- switch(M.a_intent)
- if(INTENT_HELP)
- if(M == H)
- althelp(M, H, attacker_style)
- return TRUE
- return FALSE
- if(INTENT_DISARM)
- altdisarm(M, H, attacker_style)
- return TRUE
- return FALSE
-
-/datum/species/proc/althelp(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
- if(user == target && istype(user))
- if(user.getStaminaLoss() >= STAMINA_SOFTCRIT)
- to_chat(user, "You're too exhausted for that.")
- return
- if(!user.resting)
- to_chat(user, "You can only force yourself up if you're on the ground.")
- return
- user.visible_message("[user] forces [p_them()]self up to [p_their()] feet!", "You force yourself up to your feet!")
- user.resting = 0
- user.update_canmove()
- user.adjustStaminaLossBuffered(user.stambuffer) //Rewards good stamina management by making it easier to instantly get up from resting
- playsound(user, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
-
-/datum/species/proc/altdisarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
- if(user.getStaminaLoss() >= STAMINA_SOFTCRIT)
- to_chat(user, "You're too exhausted.")
- return FALSE
- if(target.check_block())
- target.visible_message("[target] blocks [user]'s shoving attempt!")
- return FALSE
- if(attacker_style && attacker_style.disarm_act(user,target))
- return TRUE
- if(user.resting)
- return FALSE
- else
- if(user == target)
- return
- user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
- user.adjustStaminaLossBuffered(4)
- playsound(target, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
-
- if(target.w_uniform)
- target.w_uniform.add_fingerprint(user)
- SEND_SIGNAL(target, COMSIG_HUMAN_DISARM_HIT, user, user.zone_selected)
-
- if(!target.resting)
- target.adjustStaminaLoss(5)
-
-
- var/turf/target_oldturf = target.loc
- var/shove_dir = get_dir(user.loc, target_oldturf)
- var/turf/target_shove_turf = get_step(target.loc, shove_dir)
- var/mob/living/carbon/human/target_collateral_human
- var/obj/structure/table/target_table
- var/shove_blocked = FALSE //Used to check if a shove is blocked so that if it is knockdown logic can be applied
-
- //Thank you based whoneedsspace
- target_collateral_human = locate(/mob/living/carbon/human) in target_shove_turf.contents
- if(target_collateral_human)
- shove_blocked = TRUE
- else
- target.Move(target_shove_turf, shove_dir)
- if(get_turf(target) == target_oldturf)
- if(target_shove_turf.density)
- shove_blocked = TRUE
- else
- var/thoushallnotpass = FALSE
- for(var/obj/O in target_shove_turf)
- if(istype(O, /obj/structure/table))
- target_table = O
- else if(!O.CanPass(src, target_shove_turf))
- shove_blocked = TRUE
- thoushallnotpass = TRUE
- if(thoushallnotpass)
- target_table = null
-
- if(target.is_shove_knockdown_blocked())
- return
-
- if(shove_blocked || target_table)
- var/directional_blocked = FALSE
- if(shove_dir in GLOB.cardinals) //Directional checks to make sure that we're not shoving through a windoor or something like that
- var/target_turf = get_turf(target)
- for(var/obj/O in target_turf)
- if(O.flags_1 & ON_BORDER_1 && O.dir == shove_dir && O.density)
- directional_blocked = TRUE
- break
- if(target_turf != target_shove_turf) //Make sure that we don't run the exact same check twice on the same tile
- for(var/obj/O in target_shove_turf)
- if(O.flags_1 & ON_BORDER_1 && O.dir == turn(shove_dir, 180) && O.density)
- directional_blocked = TRUE
- break
- var/targetatrest = target.resting
- if(((!target_table && !target_collateral_human) || directional_blocked) && !targetatrest)
- target.Knockdown(SHOVE_KNOCKDOWN_SOLID)
- user.visible_message("[user.name] shoves [target.name], knocking them down!",
- "You shove [target.name], knocking them down!", null, COMBAT_MESSAGE_RANGE)
- log_combat(user, target, "shoved", "knocking them down")
- else if(target_table)
- if(!targetatrest)
- target.Knockdown(SHOVE_KNOCKDOWN_TABLE)
- user.visible_message("[user.name] shoves [target.name] onto \the [target_table]!",
- "You shove [target.name] onto \the [target_table]!", null, COMBAT_MESSAGE_RANGE)
- target.forceMove(target_shove_turf)
- log_combat(user, target, "shoved", "onto [target_table]")
- else if(target_collateral_human && !targetatrest)
- target.Knockdown(SHOVE_KNOCKDOWN_HUMAN)
- if(!target_collateral_human.resting)
- target_collateral_human.Knockdown(SHOVE_KNOCKDOWN_COLLATERAL)
- user.visible_message("[user.name] shoves [target.name] into [target_collateral_human.name]!",
- "You shove [target.name] into [target_collateral_human.name]!", null, COMBAT_MESSAGE_RANGE)
- log_combat(user, target, "shoved", "into [target_collateral_human.name]")
-
- else
- user.visible_message("[user.name] shoves [target.name]!",
- "You shove [target.name]!", null, COMBAT_MESSAGE_RANGE)
- var/target_held_item = target.get_active_held_item()
- var/knocked_item = FALSE
- if(!is_type_in_typecache(target_held_item, GLOB.shove_disarming_types))
- target_held_item = null
- if(!target.has_movespeed_modifier(SHOVE_SLOWDOWN_ID))
- target.add_movespeed_modifier(SHOVE_SLOWDOWN_ID, multiplicative_slowdown = SHOVE_SLOWDOWN_STRENGTH)
- if(target_held_item)
- target.visible_message("[target.name]'s grip on \the [target_held_item] loosens!",
- "Your grip on \the [target_held_item] loosens!", null, COMBAT_MESSAGE_RANGE)
- addtimer(CALLBACK(target, /mob/living/carbon/human/proc/clear_shove_slowdown), SHOVE_SLOWDOWN_LENGTH)
- else if(target_held_item)
- target.dropItemToGround(target_held_item)
- knocked_item = TRUE
- target.visible_message("[target.name] drops \the [target_held_item]!!",
- "You drop \the [target_held_item]!!", null, COMBAT_MESSAGE_RANGE)
- var/append_message = ""
- if(target_held_item)
- if(knocked_item)
- append_message = "causing them to drop [target_held_item]"
- else
- append_message = "loosening their grip on [target_held_item]"
- log_combat(user, target, "shoved", append_message)
-
-
////////////////////
/////BODYPARTS/////
////////////////////