diff --git a/code/__DEFINES/wounds.dm b/code/__DEFINES/wounds.dm index 763bdb4405a..5d060da2443 100644 --- a/code/__DEFINES/wounds.dm +++ b/code/__DEFINES/wounds.dm @@ -8,6 +8,8 @@ #define WOUND_MINIMUM_DAMAGE 5 /// an attack must do this much damage after armor in order to be eliigible to dismember a suitably mushed bodypart #define DISMEMBER_MINIMUM_DAMAGE 10 +/// If an attack rolls this high with their wound (including mods), we try to outright dismember the limb. Note 250 is high enough that with a perfect max roll of 145 (see max cons'd damage), you'd need +100 in mods to do this +#define WOUND_DISMEMBER_OUTRIGHT_THRESH 250 /// set wound_bonus on an item or attack to this to disable checking wounding for the attack #define CANT_WOUND -100 diff --git a/code/datums/components/gunpoint.dm b/code/datums/components/gunpoint.dm index 512cc202870..4c55d00c734 100644 --- a/code/datums/components/gunpoint.dm +++ b/code/datums/components/gunpoint.dm @@ -1,8 +1,16 @@ +/// How many tiles around the target the shooter can roam without losing their shot #define GUNPOINT_SHOOTER_STRAY_RANGE 2 -#define GUNPOINT_DELAY_STAGE_2 25 -#define GUNPOINT_DELAY_STAGE_3 75 // cumulative with past stages, so 100 deciseconds +/// How long it takes from the gunpoint is initiated to reach stage 2 +#define GUNPOINT_DELAY_STAGE_2 2.5 SECONDS +/// How long it takes from stage 2 starting to move up to stage 3 +#define GUNPOINT_DELAY_STAGE_3 7.5 SECONDS +/// If the projectile doesn't have a wound_bonus of CANT_WOUND, we add (this * the stage mult) to their wound_bonus and bare_wound_bonus upon triggering +#define GUNPOINT_BASE_WOUND_BONUS 5 +/// How much the damage and wound bonus mod is multiplied when you're on stage 1 #define GUNPOINT_MULT_STAGE_1 1 +/// As above, for stage 2 #define GUNPOINT_MULT_STAGE_2 2 +/// As above, for stage 3 #define GUNPOINT_MULT_STAGE_3 2.5 @@ -64,27 +72,30 @@ UnregisterSignal(parent, COMSIG_MOB_ATTACK_HAND) UnregisterSignal(parent, list(COMSIG_LIVING_START_PULL, COMSIG_MOVABLE_BUMP)) +///If the shooter bumps the target, cancel the holdup to avoid cheesing and forcing the charged shot /datum/component/gunpoint/proc/check_bump(atom/B, atom/A) SIGNAL_HANDLER - var/mob/living/T = A - if(T && T == target) - var/mob/living/shooter = parent - shooter.visible_message("[shooter] bumps into [target] and fumbles [shooter.p_their()] aim!", \ - "You bump into [target] and fumble your aim!", target) - to_chat(target, "[shooter] bumps into you and fumbles [shooter.p_their()] aim!") - qdel(src) + if(A != target) + return + var/mob/living/shooter = parent + shooter.visible_message("[shooter] bumps into [target] and fumbles [shooter.p_their()] aim!", \ + "You bump into [target] and fumble your aim!", target) + to_chat(target, "[shooter] bumps into you and fumbles [shooter.p_their()] aim!") + qdel(src) +///If the shooter shoves or grabs the target, cancel the holdup to avoid cheesing and forcing the charged shot /datum/component/gunpoint/proc/check_shove(mob/living/carbon/shooter, mob/shooter_again, mob/living/T) SIGNAL_HANDLER - if(T == target && (shooter.a_intent == INTENT_DISARM || shooter.a_intent == INTENT_GRAB)) - shooter.visible_message("[shooter] bumps into [target] and fumbles [shooter.p_their()] aim!", \ - "You bump into [target] and fumble your aim!", target) - to_chat(target, "[shooter] bumps into you and fumbles [shooter.p_their()] aim!") - qdel(src) + if(T != target || shooter.a_intent == INTENT_DISARM || shooter.a_intent == INTENT_GRAB) + return + shooter.visible_message("[shooter] bumps into [target] and fumbles [shooter.p_their()] aim!", \ + "You bump into [target] and fumble your aim!", target) + to_chat(target, "[shooter] bumps into you and fumbles [shooter.p_their()] aim!") + qdel(src) -// if you're gonna try to break away from a holdup, better to do it right away +///Update the damage multiplier for whatever stage we're entering into /datum/component/gunpoint/proc/update_stage(new_stage) stage = new_stage if(stage == 2) @@ -97,22 +108,26 @@ to_chat(target, "[parent] has fully steadied [weapon] on you!") damage_mult = GUNPOINT_MULT_STAGE_3 +///Cancel the holdup if the shooter moves out of sight or out of range of the target /datum/component/gunpoint/proc/check_deescalate() SIGNAL_HANDLER if(!can_see(parent, target, GUNPOINT_SHOOTER_STRAY_RANGE - 1)) cancel() +///Bang bang, we're firing a charged shot off /datum/component/gunpoint/proc/trigger_reaction() SIGNAL_HANDLER_DOES_SLEEP + var/mob/living/shooter = parent + shooter.remove_status_effect(STATUS_EFFECT_HOLDUP) // try doing these before the trigger gets pulled since the target (or shooter even) may not exist after pulling the trigger, dig? + target.remove_status_effect(STATUS_EFFECT_HELDUP) SEND_SIGNAL(target, COMSIG_CLEAR_MOOD_EVENT, "gunpoint") + if(point_of_no_return) return point_of_no_return = TRUE - var/mob/living/shooter = parent - if(!weapon.can_shoot() || !weapon.can_trigger_gun(shooter) || (weapon.weapon_weight == WEAPON_HEAVY && shooter.get_inactive_held_item())) shooter.visible_message("[shooter] fumbles [weapon]!", \ "You fumble [weapon] and fail to fire at [target]!", target) @@ -122,13 +137,23 @@ if(weapon.chambered && weapon.chambered.BB) weapon.chambered.BB.damage *= damage_mult + if(weapon.chambered.BB.wound_bonus != CANT_WOUND) + weapon.chambered.BB.wound_bonus += damage_mult * GUNPOINT_BASE_WOUND_BONUS + weapon.chambered.BB.bare_wound_bonus += damage_mult * GUNPOINT_BASE_WOUND_BONUS if(weapon.check_botched(shooter)) return - weapon.process_fire(target, shooter) + var/fired = weapon.process_fire(target, shooter) + if(!fired && weapon.chambered?.BB) + weapon.chambered.BB.damage /= damage_mult + if(weapon.chambered.BB.wound_bonus != CANT_WOUND) + weapon.chambered.BB.wound_bonus -= damage_mult * GUNPOINT_BASE_WOUND_BONUS + weapon.chambered.BB.bare_wound_bonus -= damage_mult * GUNPOINT_BASE_WOUND_BONUS + qdel(src) +///Shooter canceled their shot, either by dropping/equipping their weapon, leaving sight/range, or clicking on the alert /datum/component/gunpoint/proc/cancel() SIGNAL_HANDLER @@ -139,10 +164,13 @@ SEND_SIGNAL(target, COMSIG_CLEAR_MOOD_EVENT, "gunpoint") qdel(src) +///If the shooter is hit by an attack, they have a 50% chance to flinch and fire. If it hit the arm holding the trigger, it's an 80% chance to fire instead /datum/component/gunpoint/proc/flinch(attacker, damage, damagetype, def_zone) SIGNAL_HANDLER_DOES_SLEEP var/mob/living/shooter = parent + if(attacker == shooter) + return // somehow this wasn't checked for months but no one tried punching themselves to initiate the shot, amazing var/flinch_chance = 50 var/gun_hand = LEFT_HANDS @@ -161,6 +189,7 @@ #undef GUNPOINT_SHOOTER_STRAY_RANGE #undef GUNPOINT_DELAY_STAGE_2 #undef GUNPOINT_DELAY_STAGE_3 +#undef GUNPOINT_BASE_WOUND_BONUS #undef GUNPOINT_MULT_STAGE_1 #undef GUNPOINT_MULT_STAGE_2 #undef GUNPOINT_MULT_STAGE_3 diff --git a/code/datums/components/pellet_cloud.dm b/code/datums/components/pellet_cloud.dm index 254aa94f86c..1c6e2fcf9a6 100644 --- a/code/datums/components/pellet_cloud.dm +++ b/code/datums/components/pellet_cloud.dm @@ -105,6 +105,11 @@ if(!zone_override) zone_override = shooter.zone_selected + // things like mouth executions and gunpoints can multiply the damage and wounds of projectiles, so this makes sure those effects are applied to each pellet instead of just one + var/original_damage = shell.BB.damage + var/original_wb = shell.BB.wound_bonus + var/original_bwb = shell.BB.bare_wound_bonus + for(var/i in 1 to num_pellets) shell.ready_proj(target, user, SUPPRESSED_VERY, zone_override, fired_from) if(distro) @@ -115,6 +120,9 @@ RegisterSignal(shell.BB, COMSIG_PROJECTILE_SELF_ON_HIT, .proc/pellet_hit) RegisterSignal(shell.BB, list(COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PARENT_QDELETING), .proc/pellet_range) + shell.BB.damage = original_damage + shell.BB.wound_bonus = original_wb + shell.BB.bare_wound_bonus = original_bwb pellets += shell.BB if(!shell.throw_proj(target, targloc, shooter, params, spread)) return diff --git a/code/datums/wounds/loss.dm b/code/datums/wounds/loss.dm index bc7e18a1831..1fd318cdd8b 100644 --- a/code/datums/wounds/loss.dm +++ b/code/datums/wounds/loss.dm @@ -5,28 +5,39 @@ sound_effect = 'sound/effects/dismember.ogg' severity = WOUND_SEVERITY_LOSS - threshold_minimum = 180 + threshold_minimum = WOUND_DISMEMBER_OUTRIGHT_THRESH // not actually used since dismembering is handled differently, but may as well assign it since we got it status_effect_type = null scar_keyword = "dismember" wound_flags = null /// Our special proc for our special dismembering, the wounding type only matters for what text we have -/datum/wound/loss/proc/apply_dismember(obj/item/bodypart/dismembered_part, wounding_type=WOUND_SLASH) +/datum/wound/loss/proc/apply_dismember(obj/item/bodypart/dismembered_part, wounding_type=WOUND_SLASH, outright = FALSE) if(!istype(dismembered_part) || !dismembered_part.owner || !(dismembered_part.body_zone in viable_zones) || isalien(dismembered_part.owner) || !dismembered_part.can_dismember()) qdel(src) return already_scarred = TRUE // so we don't scar a limb we don't have. If I add different levels of amputation desc, do it here - switch(wounding_type) - if(WOUND_BLUNT) - occur_text = "is shattered through the last bone holding it together, severing it completely!" - if(WOUND_SLASH) - occur_text = "is slashed through the last tissue holding it together, severing it completely!" - if(WOUND_PIERCE) - occur_text = "is pierced through the last tissue holding it together, severing it completely!" - if(WOUND_BURN) - occur_text = "is completely incinerated, falling to dust!" + if(outright) + switch(wounding_type) + if(WOUND_BLUNT) + occur_text = "is outright smashed to a gross pulp, severing it completely!" + if(WOUND_SLASH) + occur_text = "is outright slashed off, severing it completely!" + if(WOUND_PIERCE) + occur_text = "is outright blasted apart, severing it completely!" + if(WOUND_BURN) + occur_text = "is outright incinerated, falling to dust!" + else + switch(wounding_type) + if(WOUND_BLUNT) + occur_text = "is shattered through the last bone holding it together, severing it completely!" + if(WOUND_SLASH) + occur_text = "is slashed through the last tissue holding it together, severing it completely!" + if(WOUND_PIERCE) + occur_text = "is pierced through the last tissue holding it together, severing it completely!" + if(WOUND_BURN) + occur_text = "is completely incinerated, falling to dust!" victim = dismembered_part.owner diff --git a/code/datums/wounds/pierce.dm b/code/datums/wounds/pierce.dm index c307f9582de..baf7125aeb4 100644 --- a/code/datums/wounds/pierce.dm +++ b/code/datums/wounds/pierce.dm @@ -131,7 +131,7 @@ internal_bleeding_chance = 30 internal_bleeding_coefficient = 1.25 threshold_minimum = 30 - threshold_penalty = 15 + threshold_penalty = 20 status_effect_type = /datum/status_effect/wound/pierce/moderate scar_keyword = "piercemoderate" @@ -148,7 +148,7 @@ internal_bleeding_chance = 60 internal_bleeding_coefficient = 1.5 threshold_minimum = 50 - threshold_penalty = 25 + threshold_penalty = 35 status_effect_type = /datum/status_effect/wound/pierce/severe scar_keyword = "piercesevere" @@ -165,7 +165,7 @@ internal_bleeding_chance = 80 internal_bleeding_coefficient = 1.75 threshold_minimum = 100 - threshold_penalty = 40 + threshold_penalty = 50 status_effect_type = /datum/status_effect/wound/pierce/critical scar_keyword = "piercecritical" wound_flags = (FLESH_WOUND | ACCEPTS_GAUZE | MANGLES_FLESH) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 7e295e075ec..6fdfbaeac04 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -19,9 +19,9 @@ var/flags_ricochet = NONE ///When a projectile tries to ricochet off this atom, the projectile ricochet chance is multiplied by this - var/ricochet_chance_mod = 1 + var/receive_ricochet_chance_mod = 1 ///When a projectile ricochets off this atom, it deals the normal damage * this modifier to this atom - var/ricochet_damage_mod = 0.33 + var/receive_ricochet_damage_coeff = 0.33 ///Reagents holder var/datum/reagents/reagents = null diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index c9aafb38716..7bc293bf446 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -90,7 +90,7 @@ Class Procs: max_integrity = 200 layer = BELOW_OBJ_LAYER //keeps shit coming out of the machine from ending up underneath it. flags_ricochet = RICOCHET_HARD - ricochet_chance_mod = 0.3 + receive_ricochet_chance_mod = 0.3 anchored = TRUE interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 08aa0696e3f..b88aa3438e9 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -13,7 +13,7 @@ armor = list(MELEE = 30, BULLET = 30, LASER = 20, ENERGY = 20, BOMB = 10, BIO = 100, RAD = 100, FIRE = 80, ACID = 70) CanAtmosPass = ATMOS_PASS_DENSITY flags_1 = PREVENT_CLICK_UNDER_1 - ricochet_chance_mod = 0.8 + receive_ricochet_chance_mod = 0.8 damage_deflection = 10 interaction_flags_atom = INTERACT_ATOM_UI_INTERACT diff --git a/code/game/objects/items/sharpener.dm b/code/game/objects/items/sharpener.dm index a2a3e6a0305..eb3551bf643 100644 --- a/code/game/objects/items/sharpener.dm +++ b/code/game/objects/items/sharpener.dm @@ -37,9 +37,10 @@ return if(!(signal_out & COMPONENT_BLOCK_SHARPEN_APPLIED)) I.force = clamp(I.force + increment, 0, max) + I.wound_bonus = I.wound_bonus + increment user.visible_message("[user] sharpens [I] with [src]!", "You sharpen [I], making it much more deadly than before.") playsound(src, 'sound/items/unsheath.ogg', 25, TRUE) - I.sharpness = SHARP_POINTY + I.sharpness = SHARP_EDGED I.throwforce = clamp(I.throwforce + increment, 0, max) I.name = "[prefix] [I.name]" name = "worn out [name]" diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 4d1126983e8..4104a1424ba 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -350,8 +350,8 @@ /obj/handle_ricochet(obj/projectile/P) . = ..() - if(. && ricochet_damage_mod) - take_damage(P.damage * ricochet_damage_mod, P.damage_type, P.flag, 0, turn(P.dir, 180), P.armour_penetration) // pass along ricochet_damage_mod damage to the structure for the ricochet + if(. && receive_ricochet_damage_coeff) + take_damage(P.damage * receive_ricochet_damage_coeff, P.damage_type, P.flag, 0, turn(P.dir, 180), P.armour_penetration) // pass along receive_ricochet_damage_coeff damage to the structure for the ricochet /obj/update_overlays() . = ..() diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index cb2bfc273ba..8f83559622c 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -5,7 +5,7 @@ interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT layer = BELOW_OBJ_LAYER flags_ricochet = RICOCHET_HARD - ricochet_chance_mod = 0.5 + receive_ricochet_chance_mod = 0.6 var/climb_time = 20 var/climb_stun = 20 diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 9d5b3b76a52..092dcf2cbee 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -27,7 +27,7 @@ var/breaksound = "shatter" var/hitsound = 'sound/effects/Glasshit.ogg' flags_ricochet = RICOCHET_HARD - ricochet_chance_mod = 0.4 + receive_ricochet_chance_mod = 0.5 /obj/structure/window/examine(mob/user) @@ -371,7 +371,7 @@ state = RWINDOW_SECURE glass_type = /obj/item/stack/sheet/rglass rad_insulation = RAD_HEAVY_INSULATION - ricochet_chance_mod = 0.8 + receive_ricochet_chance_mod = 1.1 //this is shitcode but all of construction is shitcode and needs a refactor, it works for now //If you find this like 4 years later and construction still hasn't been refactored, I'm so sorry for this @@ -683,7 +683,7 @@ explosion_block = 3 glass_type = /obj/item/stack/sheet/titaniumglass glass_amount = 2 - ricochet_chance_mod = 0.9 + receive_ricochet_chance_mod = 1.2 /obj/structure/window/shuttle/narsie_act() add_atom_colour("#3C3434", FIXED_COLOUR_PRIORITY) diff --git a/code/modules/cargo/goodies.dm b/code/modules/cargo/goodies.dm index c071cbb4753..086de036429 100644 --- a/code/modules/cargo/goodies.dm +++ b/code/modules/cargo/goodies.dm @@ -49,7 +49,7 @@ /datum/supply_pack/goody/hell_single name = "Hellgun Single-Pack" desc = "Contains one hellgun, an old pattern of laser gun infamous for its ability to horribly disfigure targets with burns. Technically violates the Space Geneva Convention when used on humanoids." - cost = 2000 + cost = 2250 contains = list(/obj/item/gun/energy/laser/hellgun) /datum/supply_pack/goody/wt550_single diff --git a/code/modules/projectiles/ammunition/ballistic/lmg.dm b/code/modules/projectiles/ammunition/ballistic/lmg.dm index 724c1f6d2b0..b727e5dd727 100644 --- a/code/modules/projectiles/ammunition/ballistic/lmg.dm +++ b/code/modules/projectiles/ammunition/ballistic/lmg.dm @@ -26,3 +26,8 @@ name = "7.12x82mm match bullet casing" desc = "A 7.12x82mm bullet casing manufactured to unfailingly high standards, you could pull off some cool trickshots with this." projectile_type = /obj/projectile/bullet/mm712x82_match + +/obj/item/ammo_casing/mm712x82/bouncy + name = "7.12x82mm rubber bullet casing" + desc = "A 7.12x82mm rubber bullet casing manufactured to unfailingly disastrous standards, you could piss off a lot of people spraying this down a hallway." + projectile_type = /obj/projectile/bullet/mm712x82_bouncy diff --git a/code/modules/projectiles/boxes_magazines/_box_magazine.dm b/code/modules/projectiles/boxes_magazines/_box_magazine.dm index 643b9af9c91..86431ec909d 100644 --- a/code/modules/projectiles/boxes_magazines/_box_magazine.dm +++ b/code/modules/projectiles/boxes_magazines/_box_magazine.dm @@ -43,8 +43,27 @@ material_amount /= max_ammo LAZYSET(bullet_cost, material, material_amount) if(!start_empty) - for(var/i = 1, i <= max_ammo, i++) - stored_ammo += new ammo_type(src) + top_off(starting=TRUE) + update_icon() + +/** + * top_off is used to refill the magazine to max, in case you want to increase the size of a magazine with VV then refill it at once + * + * Arguments: + * * load_type - if you want to specify a specific ammo casing type to load, enter the path here, otherwise it'll use the basic [/obj/item/ammo_box/var/ammo_type]. Must be a compatible round + * * starting - Relevant for revolver cylinders, if FALSE then we mind the nulls that represent the empty cylinders (since those nulls don't exist yet if we haven't initialized when this is TRUE) + */ +/obj/item/ammo_box/proc/top_off(load_type, starting=FALSE) + if(!load_type) //this check comes first so not defining an argument means we just go with default ammo + load_type = ammo_type + + var/obj/item/ammo_casing/round_check = load_type + if(!starting && (caliber && initial(round_check.caliber) != caliber) || (!caliber && load_type != ammo_type)) + stack_trace("Tried loading unsupported ammocasing type [load_type] into ammo box [type].") + return + + for(var/i = max(1, stored_ammo.len), i <= max_ammo, i++) + stored_ammo += new round_check(src) update_icon() ///gets a round from the magazine, if keep is TRUE the round will stay in the gun diff --git a/code/modules/projectiles/boxes_magazines/external/lmg.dm b/code/modules/projectiles/boxes_magazines/external/lmg.dm index f1e3a5740a6..793c4cf14c9 100644 --- a/code/modules/projectiles/boxes_magazines/external/lmg.dm +++ b/code/modules/projectiles/boxes_magazines/external/lmg.dm @@ -21,6 +21,14 @@ name = "box magazine (Match 7.12x82mm)" ammo_type = /obj/item/ammo_casing/mm712x82/match +/obj/item/ammo_box/magazine/mm712x82/bouncy + name = "box magazine (Rubber 7.12x82mm)" + ammo_type = /obj/item/ammo_casing/mm712x82/bouncy + +/obj/item/ammo_box/magazine/mm712x82/bouncy/hicap + name = "hi-cap box magazine (Rubber 7.12x82mm)" + max_ammo = 150 + /obj/item/ammo_box/magazine/mm712x82/update_icon() ..() icon_state = "a762-[round(ammo_count(),10)]" diff --git a/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm b/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm index 9af0b0e0877..3ce4190109c 100644 --- a/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm +++ b/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm @@ -47,3 +47,15 @@ return TRUE return FALSE + +/obj/item/ammo_box/magazine/internal/cylinder/top_off(load_type, starting=FALSE) + if(starting) // nulls don't exist when we're starting off + return ..() + + if(!load_type) + load_type = ammo_type + + for(var/i = 1, i <= max_ammo, i++) + if(!give_round(new load_type(src))) + break + update_icon() diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 3f6ae6059c3..8641a7c305d 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -620,8 +620,14 @@ if(chambered && chambered.BB) chambered.BB.damage *= 5 + if(chambered.BB.wound_bonus != CANT_WOUND) + chambered.BB.wound_bonus += 5 // much more dramatic on multiple pellet'd projectiles really - process_fire(target, user, TRUE, params, BODY_ZONE_HEAD) + var/fired = process_fire(target, user, TRUE, params, BODY_ZONE_HEAD) + if(!fired && chambered?.BB) + chambered.BB.damage /= 5 + if(chambered.BB.wound_bonus != CANT_WOUND) + chambered.BB.wound_bonus -= 5 /obj/item/gun/proc/unlock() //used in summon guns and as a convience for admins if(pin) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 25e13c73db4..1d1c1ab9bb6 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -48,7 +48,7 @@ var/ricochets = 0 /// how many times we can ricochet max var/ricochets_max = 0 - /// 0-100, the base chance of ricocheting, before being modified by the atom we shoot and our chance decay + /// 0-100 (or more, I guess), the base chance of ricocheting, before being modified by the atom we shoot and our chance decay var/ricochet_chance = 0 /// 0-1 (or more, I guess) multiplier, the ricochet_chance is modified by multiplying this after each ricochet var/ricochet_decay_chance = 0.7 @@ -290,6 +290,9 @@ beam_segments[beam_index] = null /obj/projectile/Bump(atom/A) + if(!trajectory) + qdel(src) + return var/datum/point/pcache = trajectory.copy_to() var/turf/T = get_turf(A) if(ricochets < ricochets_max && check_ricochet_flag(A) && check_ricochet(A)) @@ -377,7 +380,7 @@ //Returns null if nothing at all was found. /obj/projectile/proc/check_ricochet(atom/A) - var/chance = ricochet_chance * A.ricochet_chance_mod + var/chance = ricochet_chance * A.receive_ricochet_chance_mod if(firer && HAS_TRAIT(firer, TRAIT_NICE_SHOT)) chance += NICE_SHOT_RICOCHET_BONUS if(prob(chance)) diff --git a/code/modules/projectiles/projectile/bullets/lmg.dm b/code/modules/projectiles/projectile/bullets/lmg.dm index e05240a7369..8691cdf9664 100644 --- a/code/modules/projectiles/projectile/bullets/lmg.dm +++ b/code/modules/projectiles/projectile/bullets/lmg.dm @@ -55,5 +55,14 @@ ricochets_max = 2 ricochet_chance = 60 ricochet_auto_aim_range = 4 - ricochet_incidence_leeway = 35 + ricochet_incidence_leeway = 55 wound_bonus = -50 + +/obj/projectile/bullet/mm712x82_bouncy + name = "7.12x82mm rubber bullet" + damage = 25 + ricochets_max = 40 + ricochet_chance = 500 // will bounce off anything and everything, whether they like it or not + ricochet_auto_aim_range = 4 + ricochet_incidence_leeway = 0 + ricochet_decay_chance = 0.9 diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 9e8406e0f85..e0ad366a4b3 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -367,6 +367,11 @@ injury_roll += check_woundings_mods(woundtype, damage, wound_bonus, bare_wound_bonus) var/list/wounds_checking = GLOB.global_wound_types[woundtype] + if(injury_roll > WOUND_DISMEMBER_OUTRIGHT_THRESH && (get_damage() / max_damage * 50)) + var/datum/wound/loss/dismembering = new + dismembering.apply_dismember(src, woundtype, outright=TRUE) + return + // quick re-check to see if bare_wound_bonus applies, for the benefit of log_wound(), see about getting the check from check_woundings_mods() somehow if(ishuman(owner)) var/mob/living/carbon/human/human_wearer = owner