diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm index b7e55cddb8..5e01c43e55 100644 --- a/code/game/objects/items/weapons/stunbaton.dm +++ b/code/game/objects/items/weapons/stunbaton.dm @@ -122,7 +122,7 @@ var/mob/living/carbon/human/H = target affecting = H.get_organ(hit_zone) - if(user.a_intent == I_HURT) + if(user.a_intent == I_HURT || user.a_intent == I_DISARM) . = ..() //whacking someone causes a much poorer electrical contact than deliberately prodding them. agony *= 0.5 diff --git a/code/modules/mob/holder.dm b/code/modules/mob/holder.dm index 121b999d21..d608bb0988 100644 --- a/code/modules/mob/holder.dm +++ b/code/modules/mob/holder.dm @@ -114,6 +114,11 @@ var/list/holder_mob_icon_cache = list() if(!holder_type || buckled || pinned.len) return + if(self_grab) + if(src.incapacitated()) return + else + if(grabber.incapacitated()) return + var/obj/item/weapon/holder/H = new holder_type(get_turf(src)) H.held_mob = src src.forceMove(H) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 6b9cbf0258..4eaf5aa8fe 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -194,7 +194,7 @@ status += "hurts when touched" if(org.status & ORGAN_DEAD) status += "is bruised and necrotic" - if(!org.is_usable()) + if(!org.is_usable() || org.is_dislocated()) status += "dangling uselessly" if(status.len) src.show_message("My [org.name] is [english_list(status)].",1) @@ -309,14 +309,19 @@ if(!item) return + var/throw_range = item.throw_range if (istype(item, /obj/item/weapon/grab)) var/obj/item/weapon/grab/G = item item = G.throw_held() //throw the person instead of the grab if(ismob(item)) + var/mob/M = item + + //limit throw range by relative mob size + throw_range = round(M.throw_range * min(src.mob_size/M.mob_size, 1)) + var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors var/turf/end_T = get_turf(target) if(start_T && end_T) - var/mob/M = item var/start_T_descriptor = "tile at [start_T.x], [start_T.y], [start_T.z] in area [get_area(start_T)]" var/end_T_descriptor = "tile at [end_T.x], [end_T.y], [end_T.z] in area [get_area(end_T)]" @@ -324,31 +329,28 @@ usr.attack_log += text("\[[time_stamp()]\] Has thrown [M.name] ([M.ckey]) from [start_T_descriptor] with the target [end_T_descriptor]") msg_admin_attack("[usr.name] ([usr.ckey]) has thrown [M.name] ([M.ckey]) from [start_T_descriptor] with the target [end_T_descriptor] (JMP)") - if(!item) return //Grab processing has a chance of returning null - - - src.remove_from_mob(item) - item.loc = src.loc + src.drop_from_inventory(item) + if(!item || !isturf(item.loc)) + return //actually throw it! - if (item) - src.visible_message("\red [src] has thrown [item].") + src.visible_message("[src] has thrown [item].") - if(!src.lastarea) - src.lastarea = get_area(src.loc) - if((istype(src.loc, /turf/space)) || (src.lastarea.has_gravity == 0)) - src.inertia_dir = get_dir(target, src) - step(src, inertia_dir) + if(!src.lastarea) + src.lastarea = get_area(src.loc) + if((istype(src.loc, /turf/space)) || (src.lastarea.has_gravity == 0)) + src.inertia_dir = get_dir(target, src) + step(src, inertia_dir) /* - if(istype(src.loc, /turf/space) || (src.flags & NOGRAV)) //they're in space, move em one space in the opposite direction - src.inertia_dir = get_dir(target, src) - step(src, inertia_dir) + if(istype(src.loc, /turf/space) || (src.flags & NOGRAV)) //they're in space, move em one space in the opposite direction + src.inertia_dir = get_dir(target, src) + step(src, inertia_dir) */ - item.throw_at(target, item.throw_range, item.throw_speed, src) + item.throw_at(target, throw_range, item.throw_speed, src) /mob/living/carbon/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) ..() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index d6a88f35ff..e884dbcd5d 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1324,8 +1324,8 @@ var/list/limbs = list() for(var/limb in organs_by_name) var/obj/item/organ/external/current_limb = organs_by_name[limb] - if(current_limb && current_limb.dislocated == 2) - limbs |= limb + if(current_limb && current_limb.dislocated > 0 && !current_limb.is_parent_dislocated()) //if the parent is also dislocated you will have to relocate that first + limbs |= current_limb var/choice = input(usr,"Which joint do you wish to relocate?") as null|anything in limbs if(!choice) diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index 7cc3156f18..d8d40ab660 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -326,7 +326,7 @@ if(!target_zone) return 0 var/obj/item/organ/external/organ = get_organ(check_zone(target_zone)) - if(!organ || organ.is_dislocated() || organ.dislocated == -1) + if(!organ || organ.dislocated > 0 || organ.dislocated == -1) //don't use is_dislocated() here, that checks parent return 0 user.visible_message("[user] begins to dislocate [src]'s [organ.joint]!") diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 614bbe1de8..6edefd2951 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -187,11 +187,13 @@ emp_act // Handle striking to cripple. if(user.a_intent == I_DISARM) - effective_force /= 2 - if(..(I, user, effective_force, blocked, hit_zone)) - attack_joint(affecting, I, blocked) - else + effective_force *= 0.66 //reduced effective force... + if(!..(I, user, effective_force, blocked, hit_zone)) return 0 + + //set the dislocate mult less than the effective force mult so that + //dislocating limbs on disarm is a bit easier than breaking limbs on harm + attack_joint(affecting, I, effective_force, 0.5, blocked) //...but can dislocate joints else if(!..()) return 0 @@ -242,10 +244,16 @@ emp_act return 1 -/mob/living/carbon/human/proc/attack_joint(var/obj/item/organ/external/organ, var/obj/item/W, var/blocked) +/mob/living/carbon/human/proc/attack_joint(var/obj/item/organ/external/organ, var/obj/item/W, var/effective_force, var/dislocate_mult, var/blocked) if(!organ || (organ.dislocated == 2) || (organ.dislocated == -1) || blocked >= 100) return 0 - if(prob(W.force * (100 - blocked)/100)) + + if(W.damtype != BRUTE) + return 0 + + //want the dislocation chance to be such that the limb is expected to dislocate after dealing a fraction of the damage needed to break the limb + var/dislocate_chance = effective_force/(dislocate_mult * organ.min_broken_damage * config.organ_health_multiplier)*100 + if(prob(dislocate_chance * (100 - blocked)/100)) visible_message("[src]'s [organ.joint] [pick("gives way","caves in","crumbles","collapses")]!") organ.dislocate(1) return 1 diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm index f65b9926c8..8db85ef5fe 100644 --- a/code/modules/mob/living/carbon/human/human_organs.dm +++ b/code/modules/mob/living/carbon/human/human_organs.dm @@ -68,7 +68,7 @@ /mob/living/carbon/human/proc/handle_stance() // Don't need to process any of this if they aren't standing anyways // unless their stance is damaged, and we want to check if they should stay down - if (!stance_damage && (lying || resting) && (life_tick % 4) == 0) + if (!stance_damage && (lying || resting) && (life_tick % 4) != 0) return stance_damage = 0 @@ -80,7 +80,7 @@ var/limb_pain for(var/limb_tag in list("l_leg","r_leg","l_foot","r_foot")) var/obj/item/organ/external/E = organs_by_name[limb_tag] - if(!E || (E.status & (ORGAN_MUTATED|ORGAN_DEAD)) || E.is_stump()) //should just be !E.is_usable() here but dislocation screws that up. + if(!E || !E.is_usable()) stance_damage += 2 // let it fail even if just foot&leg else if (E.is_malfunctioning()) //malfunctioning only happens intermittently so treat it as a missing limb when it procs @@ -93,7 +93,7 @@ spark_system.start() spawn(10) qdel(spark_system) - else if (E.is_broken() || !E.is_usable()) + else if (E.is_broken()) stance_damage += 1 else if (E.is_dislocated()) stance_damage += 0.5 diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 80798fa57b..8e892436e0 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -847,7 +847,10 @@ if(!isSynthetic() && (species.flags & IS_PLANT) && (!light_organ || light_organ.is_broken())) if(nutrition < 200) take_overall_damage(2,0) - traumatic_shock++ + + //traumatic_shock is updated every tick, incrementing that is pointless - shock_stage is the counter. + //Not that it matters much for diona, who have NO_PAIN. + shock_stage++ // TODO: stomach and bloodstream organ. if(!isSynthetic()) diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index 25a8149e48..def9ae59f2 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -190,6 +190,7 @@ /datum/species/proc/create_organs(var/mob/living/carbon/human/H) //Handles creation of mob organs. + H.mob_size = mob_size for(var/obj/item/organ/organ in H.contents) if((organ in H.organs) || (organ in H.internal_organs)) qdel(organ) @@ -249,7 +250,6 @@ H.mob_swap_flags = swap_flags H.mob_push_flags = push_flags H.pass_flags = pass_flags - H.mob_size = mob_size /datum/species/proc/handle_death(var/mob/living/carbon/human/H) //Handles any species-specific death events (such as dionaea nymph spawns). return diff --git a/code/modules/mob/living/carbon/shock.dm b/code/modules/mob/living/carbon/shock.dm index 92eccc66df..370d3f89d2 100644 --- a/code/modules/mob/living/carbon/shock.dm +++ b/code/modules/mob/living/carbon/shock.dm @@ -23,8 +23,10 @@ if(istype(src,/mob/living/carbon/human)) var/mob/living/carbon/human/M = src for(var/obj/item/organ/external/organ in M.organs) - if(organ && (organ.is_broken() || organ.open)) + if(organ.is_broken() || organ.open) src.traumatic_shock += 30 + else if(organ.is_dislocated()) + src.traumatic_shock += 15 if(src.traumatic_shock < 0) src.traumatic_shock = 0 diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 4d2e254d30..be397a6def 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -623,26 +623,9 @@ default behaviour is: /mob/living/proc/resist_grab() var/resisting = 0 - for(var/obj/O in requests) - requests.Remove(O) - qdel(O) - resisting++ for(var/obj/item/weapon/grab/G in grabbed_by) resisting++ - switch(G.state) - if(GRAB_PASSIVE) - qdel(G) - if(GRAB_AGGRESSIVE) - //Not standing up makes it much harder to break, so it is easier to cuff someone who is down without forcing them into unconsciousness. - //Otherwise, it's the same chance of breaking the grab as disarm. - if(incapacitated(INCAPACITATION_KNOCKDOWN)? prob(15) : prob(60)) - visible_message("[src] has broken free of [G.assailant]'s grip!") - qdel(G) - if(GRAB_NECK) - //If the you move when grabbing someone then it's easier for them to break free. Same if the affected mob is immune to stun. - if (((world.time - G.assailant.l_move_time < 30 || !stunned) && prob(15)) || prob(3)) - visible_message("[src] has broken free of [G.assailant]'s headlock!") - qdel(G) + G.handle_resist() if(resisting) visible_message("[src] resists!") diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index b67e58af3e..5b937a57e2 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -121,7 +121,6 @@ var/datum/hud/hud_used = null var/list/grabbed_by = list( ) - var/list/requests = list( ) var/list/mapobjs = list() diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index 47e0584740..e40b14dffd 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -3,10 +3,15 @@ ///Process_Grab() ///Called by client/Move() -///Checks to see if you are grabbing anything and if moving will affect your grab. +///Checks to see if you are grabbing or being grabbed by anything and if moving will affect your grab. /client/proc/Process_Grab() - if(istype(mob, /mob/living)) + //if we are being grabbed + if(isliving(mob)) var/mob/living/L = mob + if(!L.canmove && L.grabbed_by.len) + L.resist() //shortcut for resisting grabs + + //if we are grabbing someone for(var/obj/item/weapon/grab/G in list(L.l_hand, L.r_hand)) G.reset_kill_state() //no wandering across the station/asteroid while choking someone @@ -131,7 +136,7 @@ handle_eye_mouth_covering(affecting, assailant, assailant.zone_sel.selecting) if(force_down) - if(affecting.loc != assailant.loc) + if(affecting.loc != assailant.loc || size_difference(affecting, assailant) > 0) force_down = 0 else affecting.Weaken(2) @@ -232,7 +237,7 @@ if(state < GRAB_AGGRESSIVE) if(!allow_upgrade) return - if(!affecting.lying) + if(!affecting.lying || size_difference(affecting, assailant) > 0) assailant.visible_message("[assailant] has grabbed [affecting] aggressively (now hands)!") else assailant.visible_message("[assailant] pins [affecting] down to the ground (now hands)!") @@ -335,6 +340,40 @@ hud.icon_state = "kill" state = GRAB_NECK +/obj/item/weapon/grab/proc/handle_resist() + var/grab_name + var/break_strength = 1 + var/list/break_chance_table = list(100) + switch(state) + //if(GRAB_PASSIVE) + + if(GRAB_AGGRESSIVE) + grab_name = "grip" + //Being knocked down makes it harder to break a grab, so it is easier to cuff someone who is down without forcing them into unconsciousness. + if(!affecting.incapacitated(INCAPACITATION_KNOCKDOWN)) + break_strength++ + break_chance_table = list(15, 60, 100) + + if(GRAB_NECK) + grab_name = "headlock" + //If the you move when grabbing someone then it's easier for them to break free. Same if the affected mob is immune to stun. + if(world.time - assailant.l_move_time < 30 || !affecting.stunned) + break_strength++ + break_chance_table = list(3, 18, 45, 100) + + //It's easier to break out of a grab by a smaller mob + break_strength += max(size_difference(affecting, assailant), 0) + + var/break_chance = break_chance_table[Clamp(break_strength, 1, break_chance_table.len)] + if(prob(break_chance)) + if(grab_name) + affecting.visible_message("[affecting] has broken free of [assailant]'s [grab_name]!") + qdel(src) + +//returns the number of size categories between affecting and assailant, rounded. Positive means A is larger than B +/obj/item/weapon/grab/proc/size_difference(mob/A, mob/B) + return mob_size_difference(A.mob_size, B.mob_size) + /obj/item/weapon/grab var/destroying = 0 diff --git a/code/modules/mob/mob_grab_specials.dm b/code/modules/mob/mob_grab_specials.dm index 5158a7f3d0..e7b75a2e5d 100644 --- a/code/modules/mob/mob_grab_specials.dm +++ b/code/modules/mob/mob_grab_specials.dm @@ -51,10 +51,16 @@ return attacker.visible_message("[attacker] [pick("bent", "twisted")] [target]'s [organ.name] into a jointlock!") + + if(target.species.flags & NO_PAIN) + return + var/armor = target.run_armor_check(target, "melee") if(armor < 60) target << "You feel extreme pain!" - affecting.adjustHalLoss(Clamp(0, 60-affecting.halloss, 30)) //up to 60 halloss + + var/max_halloss = round(target.species.total_health * 0.8) //up to 80% of passing out + affecting.adjustHalLoss(Clamp(0, max_halloss - affecting.halloss, 30)) /obj/item/weapon/grab/proc/attack_eye(mob/living/carbon/human/target, mob/living/carbon/human/attacker) if(!istype(attacker)) @@ -125,6 +131,10 @@ return if(force_down) attacker << "You are already pinning [target] to the ground." + return + if(size_difference(affecting, assailant) > 0) + attacker << "You are too small to do that!" + return attacker.visible_message("[attacker] starts forcing [target] to the ground!") if(do_after(attacker, 20) && target) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 22da202ede..2f788862f8 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -5,6 +5,9 @@ return L.mob_size <= MOB_SMALL return 0 +//returns the number of size categories between two mob_sizes, rounded. Positive means A is larger than B +/proc/mob_size_difference(var/mob_size_A, var/mob_size_B) + return round(log(2, mob_size_A/mob_size_B), 1) /proc/istiny(A) if(A && istype(A, /mob/living)) diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 13eef2eaf4..d8533239e7 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -50,6 +50,7 @@ var/list/organ_cache = list() max_damage = min_broken_damage * 2 if(istype(holder)) src.owner = holder + src.w_class = max(src.w_class + mob_size_difference(holder.mob_size, MOB_MEDIUM), 1) //smaller mobs have smaller organs. species = all_species["Human"] if(holder.dna) dna = holder.dna.Clone() diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index a1a31fad7c..d3b309fd84 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -60,7 +60,7 @@ var/cannot_gib // Impossible to gib, distinct from amputation. var/joint = "joint" // Descriptive string used in dislocation. var/amputation_point // Descriptive string used in amputation. - var/dislocated = 0 // If you target a joint, you can dislocate the limb, causing temporary damage to the organ. + var/dislocated = 0 // If you target a joint, you can dislocate the limb, impairing it's usefulness and causing pain var/encased // Needs to be opened with a saw to access the organs. // Surgery vars. @@ -156,32 +156,38 @@ /obj/item/organ/external/proc/is_dislocated() if(dislocated > 0) return 1 - if(parent) - return parent.is_dislocated() + if(is_parent_dislocated()) + return 1//if any parent is dislocated, we are considered dislocated as well return 0 -/obj/item/organ/external/proc/dislocate(var/primary) - if(dislocated != -1) - if(primary) - dislocated = 2 - else - dislocated = 1 - owner.verbs |= /mob/living/carbon/human/proc/undislocate - if(children && children.len) - for(var/obj/item/organ/external/child in children) - child.dislocate() +/obj/item/organ/external/proc/is_parent_dislocated() + var/obj/item/organ/external/O = parent + while(O && O.dislocated != -1) + if(O.dislocated == 1) + return 1 + O = O.parent + return 0 + + +/obj/item/organ/external/proc/dislocate() + if(dislocated == -1) + return + + dislocated = 1 + if(owner) + owner.verbs |= /mob/living/carbon/human/proc/undislocate /obj/item/organ/external/proc/undislocate() - if(dislocated != -1) - dislocated = 0 - if(children && children.len) - for(var/obj/item/organ/external/child in children) - if(child.dislocated == 1) - child.undislocate() + if(dislocated == -1) + return + + dislocated = 0 if(owner) owner.shock_stage += 20 + + //check to see if we still need the verb for(var/obj/item/organ/external/limb in owner.organs) - if(limb.dislocated == 2) + if(limb.dislocated == 1) return owner.verbs -= /mob/living/carbon/human/proc/undislocate @@ -1085,7 +1091,7 @@ Note that amputating the affected organ does in fact remove the infection from t return 0 /obj/item/organ/external/proc/is_usable() - return !is_dislocated() && !(status & (ORGAN_MUTATED|ORGAN_DEAD)) + return !(status & (ORGAN_MUTATED|ORGAN_DEAD)) /obj/item/organ/external/proc/is_malfunctioning() return ((robotic >= ORGAN_ROBOT) && (brute_dam + burn_dam) >= 10 && prob(brute_dam + burn_dam)) diff --git a/code/modules/organs/subtypes/diona.dm b/code/modules/organs/subtypes/diona.dm index ab5c436758..0776e318b4 100644 --- a/code/modules/organs/subtypes/diona.dm +++ b/code/modules/organs/subtypes/diona.dm @@ -206,4 +206,5 @@ amputation_point = "branch" joint = "structural ligament" dislocated = -1 - vital = 0 \ No newline at end of file + vital = 0 + slot_flags = SLOT_BELT diff --git a/code/modules/organs/subtypes/standard.dm b/code/modules/organs/subtypes/standard.dm index 36fc7ba677..e33a319f69 100644 --- a/code/modules/organs/subtypes/standard.dm +++ b/code/modules/organs/subtypes/standard.dm @@ -2,6 +2,8 @@ ORGAN DEFINES ****************************************************/ +//Make sure that w_class is set as if the parent mob was medium sized! This is because w_class is adjusted automatically for mob_size in New() + /obj/item/organ/external/chest name = "upper body" organ_tag = BP_TORSO @@ -148,6 +150,7 @@ organ_tag = BP_HEAD icon_name = "head" name = "head" + slot_flags = SLOT_BELT max_damage = 75 min_broken_damage = 35 w_class = 3 diff --git a/html/changelogs/HarpyEagle-dislocation-cleanup.yml b/html/changelogs/HarpyEagle-dislocation-cleanup.yml new file mode 100644 index 0000000000..84ae3a66b6 --- /dev/null +++ b/html/changelogs/HarpyEagle-dislocation-cleanup.yml @@ -0,0 +1,7 @@ +author: HarpyEagle + +delete-after: True + +changes: + - bugfix: "Fixes disarm-attack dislocation chances being so low that you were very likely to break the targeted limb before it would dislocate." + - bugfix: "It is now possible to disarm-attack with stunbatons like with other melee weapons. Note that this means that using a stunbaton on disarm intent will hurt people." diff --git a/html/changelogs/HarpyEagle-mob-grab.yml b/html/changelogs/HarpyEagle-mob-grab.yml new file mode 100644 index 0000000000..04125ebdf9 --- /dev/null +++ b/html/changelogs/HarpyEagle-mob-grab.yml @@ -0,0 +1,9 @@ +author: HarpyEagle + +delete-after: True + +changes: + - rscadd: "Trying to move while being grabbed will now automatically resist." + - tweak: "Small mobs are no longer able to pin larger mobs." + - tweak: "Resisting a smaller mob's grab is more likely to be successful." + - bugfix: "Fixed being able to climb onto a larger mob while restrained, weakened, unconscious, or dead."