diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 049584c5d80..cdae41e26b6 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -618,3 +618,7 @@ /atom/movable/proc/decompile_act(obj/item/matter_decompiler/C, mob/user) // For drones to decompile mobs and objs. See drone for an example. return FALSE + +/// called when a mob gets shoved into an items turf. false means the mob will be shoved backwards normally, true means the mob will not be moved by the disarm proc. +/atom/movable/proc/shove_impact(mob/living/target, mob/living/attacker) + return FALSE diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 3207f1c8581..a861fec8d4a 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -230,6 +230,20 @@ else return ..() +/obj/structure/table/shove_impact(mob/living/target, mob/living/attacker) + if(locate(/obj/structure/table) in get_turf(target)) + return FALSE + if(flipped) + return FALSE + var/pass_flags_cache = target.pass_flags + target.pass_flags |= PASSTABLE + if(target.Move(loc)) + . = TRUE + target.Weaken(4 SECONDS) + add_attack_logs(attacker, target, "pushed onto [src]", ATKLOG_ALL) + else + . = FALSE + target.pass_flags = pass_flags_cache /obj/structure/table/screwdriver_act(mob/user, obj/item/I) if(flags & NODECONSTRUCT) @@ -441,6 +455,16 @@ L.Weaken(10 SECONDS) qdel(src) +/obj/structure/table/glass/shove_impact(mob/living/target, mob/living/attacker) + var/pass_flags_cache = target.pass_flags + target.pass_flags |= PASSTABLE + if(target.Move(loc)) // moving onto a table smashes it, stunning them + . = TRUE + add_attack_logs(attacker, target, "pushed onto [src]", ATKLOG_ALL) + else + . = FALSE + target.pass_flags = pass_flags_cache + /obj/structure/table/glass/deconstruct(disassembled = TRUE, wrench_disassembly = 0) if(!(flags & NODECONSTRUCT)) if(disassembled) diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm index 8221fd2d7ec..86de892a51e 100644 --- a/code/modules/mob/living/carbon/human/species/_species.dm +++ b/code/modules/mob/living/carbon/human/species/_species.dm @@ -513,6 +513,8 @@ SEND_SIGNAL(target, COMSIG_PARENT_ATTACKBY) /datum/species/proc/disarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style) + if(user == target) + return FALSE if(target.check_block()) target.visible_message("[target] blocks [user]'s disarm attempt!") return FALSE @@ -523,59 +525,73 @@ return FALSE if(attacker_style && attacker_style.disarm_act(user, target) == TRUE) return TRUE + user.do_attack_animation(target, ATTACK_EFFECT_DISARM) + if(target.move_resist > user.pull_force) + return FALSE + if(!(target.status_flags & CANPUSH)) + return FALSE + if(target.anchored) + return FALSE + if(target.buckled) + target.buckled.unbuckle_mob(target) + + var/shove_dir = get_dir(user.loc, target.loc) + var/turf/shove_to = get_step(target.loc, shove_dir) + playsound(shove_to, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + + if(shove_to == user.loc) + return FALSE + + //Directional checks to make sure that we're not shoving through a windoor or something like that + var/directional_blocked = FALSE + var/target_turf = get_turf(target) + if(shove_dir in list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)) // if we are moving diagonially, we need to check if there are dense walls either side of us + var/turf/T = get_step(target.loc, turn(shove_dir, 45)) // check to the left for a dense turf + if(T.density) + directional_blocked = TRUE + else + T = get_step(target.loc, turn(shove_dir, -45)) // check to the right for a dense turf + if(T.density) + directional_blocked = TRUE + + if(!directional_blocked) + for(var/obj/obj_content in target_turf) // check the tile we are on for border + if(obj_content.flags & ON_BORDER && obj_content.dir & shove_dir && obj_content.density) + directional_blocked = TRUE + break + if(!directional_blocked) + for(var/obj/obj_content in shove_to) // check tile we are moving to for borders + if(obj_content.flags & ON_BORDER && obj_content.dir & turn(shove_dir, 180) && obj_content.density) + directional_blocked = TRUE + break + + if(!directional_blocked) + for(var/atom/movable/AM in shove_to) + if(AM.shove_impact(target, user)) // check for special interactions EG. tabling someone + return TRUE + + var/moved = target.Move(shove_to, shove_dir) + if(!moved) //they got pushed into a dense object + add_attack_logs(user, target, "Disarmed into a dense object", ATKLOG_ALL) + target.visible_message("[user] slams [target] into an obstacle!", \ + "You get slammed into the obstacle by [user]!", \ + "You hear a loud thud.") + if(!HAS_TRAIT(target, TRAIT_FLOORED)) + target.KnockDown(3 SECONDS) + addtimer(CALLBACK(target, /mob/living.proc/SetKnockDown, 0), 3 SECONDS) // so you cannot chain stun someone + else if(!user.IsStunned()) + target.Stun(0.5 SECONDS) else - add_attack_logs(user, target, "Disarmed", ATKLOG_ALL) - user.do_attack_animation(target, ATTACK_EFFECT_DISARM) - if(target.w_uniform) - target.w_uniform.add_fingerprint(user) - var/obj/item/organ/external/affecting = target.get_organ(ran_zone(user.zone_selected)) - var/randn = rand(1, 100) - if(randn <= 25) - target.apply_effect(4 SECONDS, WEAKEN, target.run_armor_check(affecting, MELEE)) - playsound(target.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - target.visible_message("[user] has pushed [target]!") - add_attack_logs(user, target, "Pushed over", ATKLOG_ALL) - if(!iscarbon(user)) - target.LAssailant = null - else - target.LAssailant = user - return - - var/talked = 0 // BubbleWrap - - if(randn <= 60) - //BubbleWrap: Disarming breaks a pull - if(target.pulling) - target.visible_message("[user] has broken [target]'s grip on [target.pulling]!") - talked = 1 - target.stop_pulling() - - //BubbleWrap: Disarming also breaks a grab - this will also stop someone being choked, won't it? - if(istype(target.l_hand, /obj/item/grab)) - var/obj/item/grab/lgrab = target.l_hand - if(lgrab.affecting) - target.visible_message("[user] has broken [target]'s grip on [lgrab.affecting]!") - talked = 1 - spawn(1) - qdel(lgrab) - if(istype(target.r_hand, /obj/item/grab)) - var/obj/item/grab/rgrab = target.r_hand - if(rgrab.affecting) - target.visible_message("[user] has broken [target]'s grip on [rgrab.affecting]!") - talked = 1 - spawn(1) - qdel(rgrab) - //End BubbleWrap - - if(!talked) //BubbleWrap - if(target.drop_item()) - target.visible_message("[user] has disarmed [target]!") - playsound(target.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - return - - - playsound(target.loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - target.visible_message("[user] attempted to disarm [target]!") + if(target.IsSlowed() && target.get_active_hand()) + target.drop_item() + add_attack_logs(user, target, "Disarmed object out of hand", ATKLOG_ALL) + else + target.Slowed(2.5 SECONDS, 1) + var/obj/item/I = target.get_active_hand() + if(I) + to_chat(target, "Your grip on [I] loosens!") + add_attack_logs(user, target, "Disarmed, shoved back", ATKLOG_ALL) + target.stop_pulling() /datum/species/proc/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style) //Handles any species-specific attackhand events. if(!istype(M)) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index ffd95be3d76..ab88c0a05dd 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -374,3 +374,12 @@ /mob/living/proc/cult_self_harm(damage) return FALSE + +/mob/living/shove_impact(mob/living/target, mob/living/attacker) + if(IS_HORIZONTAL(src)) + return FALSE + add_attack_logs(attacker, target, "pushed into [src]", ATKLOG_ALL) + playsound(src, 'sound/weapons/punch1.ogg', 50, 1) + target.KnockDown(1 SECONDS) // knock them both down + KnockDown(1 SECONDS) + return TRUE