diff --git a/code/__DEFINES/guns.dm b/code/__DEFINES/guns.dm index 01cfa1e5bd5c..b98da0bc1d18 100644 --- a/code/__DEFINES/guns.dm +++ b/code/__DEFINES/guns.dm @@ -69,6 +69,11 @@ #define MANUFACTURER_PGF "the Etherbor Industries emblem" #define MANUFACTURER_IMPORT "Lanchester Import Co." +// Misfire chances if the gun's safety is off +#define GUN_NO_SAFETY_MALFUNCTION_CHANCE_LOW 100 +#define GUN_NO_SAFETY_MALFUNCTION_CHANCE_MEDIUM 100 +#define GUN_NO_SAFETY_MALFUNCTION_CHANCE_HIGH 100 + ///////////////// // ATTACHMENTS // ///////////////// diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 02b96c1b81de..17e2208cdebe 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -49,6 +49,7 @@ . = ..() if(!.) return + owner.trip_with_gun("knockdown") ADD_TRAIT(owner, TRAIT_FLOORED, TRAIT_STATUS_EFFECT(id)) /datum/status_effect/incapacitating/knockdown/on_remove() @@ -79,6 +80,7 @@ . = ..() if(!.) return + owner.trip_with_gun("paralyze") ADD_TRAIT(owner, TRAIT_INCAPACITATED, TRAIT_STATUS_EFFECT(id)) ADD_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(id)) ADD_TRAIT(owner, TRAIT_FLOORED, TRAIT_STATUS_EFFECT(id)) diff --git a/code/game/objects/items/storage/storage.dm b/code/game/objects/items/storage/storage.dm index c2619eef4c14..f0e5665b446b 100644 --- a/code/game/objects/items/storage/storage.dm +++ b/code/game/objects/items/storage/storage.dm @@ -28,6 +28,12 @@ if(EXPLODE_LIGHT) SSexplosions.lowobj += A +/obj/item/storage/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) + . = ..() + for(var/obj/item/gun/at_risk in get_all_contents()) + if(at_risk.safety == FALSE && prob(GUN_NO_SAFETY_MALFUNCTION_CHANCE_HIGH)) + at_risk.discharge("is hits the ground hard") + /obj/item/storage/canStrip(mob/who) . = ..() if(!. && rummage_if_nodrop) diff --git a/code/modules/projectiles/ammunition/_firing.dm b/code/modules/projectiles/ammunition/_firing.dm index 93fa4208d571..bdc5254f13c9 100644 --- a/code/modules/projectiles/ammunition/_firing.dm +++ b/code/modules/projectiles/ammunition/_firing.dm @@ -1,7 +1,7 @@ -/obj/item/ammo_casing/proc/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread, atom/fired_from) +/obj/item/ammo_casing/proc/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread, atom/fired_from, misfire = FALSE) distro += variance var/targloc = get_turf(target) - ready_proj(target, user, quiet, zone_override, fired_from) + ready_proj(target, user, quiet, zone_override, fired_from, misfire) if(pellets == 1) if(distro) //We have to spread a pixel-precision bullet. throw_proj was called before so angles should exist by now... if(randomspread) @@ -15,19 +15,20 @@ return FALSE AddComponent(/datum/component/pellet_cloud, projectile_type, pellets) SEND_SIGNAL(src, COMSIG_PELLET_CLOUD_INIT, target, user, fired_from, randomspread, spread, zone_override, params, distro) + if(user) + if(click_cooldown_override) + user.changeNext_move(click_cooldown_override) - if(click_cooldown_override) - user.changeNext_move(click_cooldown_override) - - user.newtonian_move(get_dir(target, user)) + user.newtonian_move(get_dir(target, user)) update_appearance() return TRUE -/obj/item/ammo_casing/proc/ready_proj(atom/target, mob/living/user, quiet, zone_override = "", atom/fired_from) +/obj/item/ammo_casing/proc/ready_proj(atom/target, mob/living/user, quiet, zone_override = "", atom/fired_from, misfire = FALSE) if (!BB) return BB.original = target BB.firer = user + BB.misfire = misfire BB.fired_from = fired_from if (zone_override) BB.def_zone = zone_override @@ -45,7 +46,11 @@ qdel(reagents) /obj/item/ammo_casing/proc/throw_proj(atom/target, turf/targloc, mob/living/user, params, spread) - var/turf/curloc = get_turf(user) + var/turf/curloc + if(user) + curloc = get_turf(user) + else + curloc = get_turf(src) if (!istype(targloc) || !istype(curloc) || !BB) return FALSE @@ -60,7 +65,10 @@ if(target) //if the target is right on our location we'll skip the travelling code in the proj's fire() direct_target = target if(!direct_target) - BB.preparePixelProjectile(target, user, params, spread) + if(user) + BB.preparePixelProjectile(target, user, params, spread) + else + BB.preparePixelProjectile(target, curloc, params, spread) BB.fire(null, direct_target) BB = null return TRUE diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 023f6212e06f..6e32ef1136ed 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -726,6 +726,11 @@ if(zoomed) zoom(user, user.dir) +/obj/item/gun/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) + . = ..() + if(prob(GUN_NO_SAFETY_MALFUNCTION_CHANCE_HIGH)) + discharge("hits the ground hard") + /obj/item/gun/update_overlays() . = ..() if(ismob(loc) && has_safety) @@ -945,6 +950,49 @@ flash_loc.vis_contents -= muzzle_flash muzzle_flash.applied = FALSE +// for guns firing on their own without a user +/obj/item/gun/proc/discharge(cause, seek_chance = 10) + var/target + if(!safety) + // someone is very unlucky and about to be shot + if(prob(seek_chance)) + for(var/mob/living/target_mob in range(6, get_turf(src))) + if(!isInSight(src, target_mob)) + continue + target = target_mob + break + if(!target) + var/fire_dir = pick(GLOB.alldirs) + target = get_ranged_target_turf(get_turf(src),fire_dir,6) + if(!chambered || !chambered.BB) + visible_message(span_danger("\The [src] [cause ? "[cause], suddenly going off" : "suddenly goes off"] without its safteies on! Luckily it wasn't live.")) + playsound(src, dry_fire_sound, 30, TRUE) + else + visible_message(span_danger("\The [src] [cause ? "[cause], suddenly going off" : "suddenly goes off"] without its safeties on!")) + unsafe_shot(target) + +/obj/item/gun/proc/unsafe_shot(target) + if(chambered) + chambered.fire_casing(target,null, null, null, suppressed, ran_zone(BODY_ZONE_CHEST, 50), 0, src,TRUE) + playsound(src, fire_sound, 100, TRUE) + +/mob/living/proc/trip_with_gun(cause) + var/mob/living/carbon/human/human_holder + if(ishuman(src)) + human_holder = src + for(var/obj/item/gun/at_risk in get_all_contents()) + var/chance_to_fire = GUN_NO_SAFETY_MALFUNCTION_CHANCE_MEDIUM + var/did_fire = FALSE + if(human_holder) + // gun is less likely to go off in a holster + if(at_risk == human_holder.s_store) + chance_to_fire = GUN_NO_SAFETY_MALFUNCTION_CHANCE_LOW + if(at_risk.safety == FALSE && prob(chance_to_fire)) + if(at_risk.process_fire(src,src,FALSE, null, pick(BODY_ZONE_L_LEG,BODY_ZONE_R_LEG)) == TRUE) + log_combat(src,src,"misfired",at_risk,"caused by [cause]") + visible_message(span_danger("\The [at_risk.name]'s trigger gets caught as [src] falls, suddenly going off into [src]'s leg without its safties on!"), span_danger("\The [at_risk.name]'s trigger gets caught on something as you fall, suddenly going off into your leg without its safeties on!")) + emote("scream") + //I need to refactor this into an attachment /datum/action/toggle_scope_zoom name = "Toggle Scope" diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm index 1790ba25a858..5e0cde24147c 100644 --- a/code/modules/projectiles/guns/ballistic.dm +++ b/code/modules/projectiles/guns/ballistic.dm @@ -373,3 +373,8 @@ GLOBAL_LIST_INIT(gun_saw_types, typecacheof(list( if(AC.BB) process_fire(user, user, FALSE) . = TRUE + +/obj/item/gun/ballistic/unsafe_shot(target, empty_chamber = TRUE) + . = ..() + process_chamber(empty_chamber,TRUE) + diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index abe3e3fd7b43..aaf37d4c9556 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -39,6 +39,8 @@ /obj/item/gun/energy/emp_act(severity) . = ..() if(!(. & EMP_PROTECT_CONTENTS)) + if(prob(GUN_NO_SAFETY_MALFUNCTION_CHANCE_HIGH)) + discharge("malfunctions from the EMP") cell.use(round(cell.charge / severity)) chambered = null //we empty the chamber recharge_newshot() //and try to charge a new shot @@ -323,3 +325,7 @@ . += "\The [name] has [round(cell.charge/shot.e_cost)] shots remaining on [shot.select_name] mode." else . += span_notice("\The [name] doesn't seem to have a cell!") + +/obj/item/gun/energy/unsafe_shot(target) + . = ..() + process_chamber() diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 66adeb53ac59..533f88c98da6 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -24,6 +24,8 @@ resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF var/def_zone = "" //Aiming at var/atom/movable/firer = null//Who shot it + // if the projectile was the result of a misfire. For logging. + var/misfire = FALSE var/atom/fired_from = null // the atom that the projectile was fired from (gun, turret) var/suppressed = FALSE //Attack message var/yo = null @@ -280,7 +282,9 @@ for(var/datum/reagent/R in reagents.reagent_list) reagent_note += "[R.name] ([num2text(R.volume)])" - if(ismob(firer)) + if(misfire) + L.log_message("has been hit by a misfired [src] from \a [fired_from] last touched by [fired_from.fingerprintslast]", LOG_ATTACK, color = "orange") + else if(ismob(firer)) log_combat(firer, L, "shot", src, reagent_note) else L.log_message("has been shot by [firer] with [src]", LOG_ATTACK, color="orange") diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index f4d68520c879..6cb9f7304585 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -291,6 +291,7 @@ All ShuttleMove procs go here /************************************Item move procs************************************/ + /obj/item/storage/pod/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation) . = ..() // If the pod was launched, the storage will always open. The reserved_level check @@ -299,6 +300,11 @@ All ShuttleMove procs go here if (oldT && !is_reserved_level(oldT)) unlocked = TRUE +/obj/item/gun/lateShuttleMove(turf/oldT, list/movement_force, move_dir) + . = ..() + if(prob(GUN_NO_SAFETY_MALFUNCTION_CHANCE_MEDIUM)) + discharge("is thrown around by the force of the take off") + /************************************Mob move procs************************************/ /mob/onShuttleMove(turf/newT, turf/oldT, list/movement_force, move_dir, obj/docking_port/stationary/old_dock, obj/docking_port/mobile/moving_dock, list/obj/docking_port/mobile/towed_shuttles)