Files
Bubberstation/code/modules/projectiles/guns/ballistic.dm
SkyratBot 73e54c3433 [MIRROR] Energy gunfire sound pitch now varies based on how much ammo is left. Ballistic guns now have bolt sounds for low ammo. [MDB IGNORE] (#14341)
* Energy gunfire sound pitch now varies based on how much ammo is left. Ballistic guns now have bolt sounds for low ammo. (#67549)

About The Pull Request
u0aw4DulWt.mp4
Why It's Good For The Game

Breaks up the monotony of firearm sounds, makes them actually useful in a gameplay context. Energy gun effects inspired by Cruelty Squad.
Changelog

cl
soundadd: Energy and Magic gunfire sound pitch now varies based on how much ammo is left.
soundadd: Ballistic gunfire now has a low-ammo click sound.
/cl

* Energy gunfire sound pitch now varies based on how much ammo is left. Ballistic guns now have bolt sounds for low ammo.

* mirror

* Update gun.dm

Co-authored-by: Iamgoofball <iamgoofball@gmail.com>
Co-authored-by: John Doe <gamingskeleton3@gmail.com>
Co-authored-by: Gandalf <9026500+Gandalf2k15@users.noreply.github.com>
2022-06-19 22:40:40 +01:00

702 lines
26 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
///Subtype for any kind of ballistic gun
///This has a shitload of vars on it, and I'm sorry for that, but it does make making new subtypes really easy
/obj/item/gun/ballistic
desc = "Now comes in flavors like GUN. Uses 10mm ammo, for some reason."
name = "projectile gun"
icon_state = "pistol"
w_class = WEIGHT_CLASS_NORMAL
///sound when inserting magazine
var/load_sound = 'sound/weapons/gun/general/magazine_insert_full.ogg'
///sound when inserting an empty magazine
var/load_empty_sound = 'sound/weapons/gun/general/magazine_insert_empty.ogg'
///volume of loading sound
var/load_sound_volume = 40
///whether loading sound should vary
var/load_sound_vary = TRUE
///sound of racking
var/rack_sound = 'sound/weapons/gun/general/bolt_rack.ogg'
///volume of racking
var/rack_sound_volume = 60
///whether racking sound should vary
var/rack_sound_vary = TRUE
///sound of when the bolt is locked back manually
var/lock_back_sound = 'sound/weapons/gun/general/slide_lock_1.ogg'
///volume of lock back
var/lock_back_sound_volume = 60
///whether lock back varies
var/lock_back_sound_vary = TRUE
///Sound of ejecting a magazine
var/eject_sound = 'sound/weapons/gun/general/magazine_remove_full.ogg'
///sound of ejecting an empty magazine
var/eject_empty_sound = 'sound/weapons/gun/general/magazine_remove_empty.ogg'
///volume of ejecting a magazine
var/eject_sound_volume = 40
///whether eject sound should vary
var/eject_sound_vary = TRUE
///sound of dropping the bolt or releasing a slide
var/bolt_drop_sound = 'sound/weapons/gun/general/bolt_drop.ogg'
///volume of bolt drop/slide release
var/bolt_drop_sound_volume = 60
///empty alarm sound (if enabled)
var/empty_alarm_sound = 'sound/weapons/gun/general/empty_alarm.ogg'
///empty alarm volume sound
var/empty_alarm_volume = 70
///whether empty alarm sound varies
var/empty_alarm_vary = TRUE
///Whether the gun will spawn loaded with a magazine
var/spawnwithmagazine = TRUE
///Compatible magazines with the gun
var/mag_type = /obj/item/ammo_box/magazine/m10mm //Removes the need for max_ammo and caliber info
///Whether the sprite has a visible magazine or not
var/mag_display = TRUE
///Whether the sprite has a visible ammo display or not
var/mag_display_ammo = FALSE
///Whether the sprite has a visible indicator for being empty or not.
var/empty_indicator = FALSE
///Whether the gun alarms when empty or not.
var/empty_alarm = FALSE
///Whether the gun supports multiple special mag types
var/special_mags = FALSE
///The bolt type of the gun, affects quite a bit of functionality, see combat.dm defines for bolt types: BOLT_TYPE_STANDARD; BOLT_TYPE_LOCKING; BOLT_TYPE_OPEN; BOLT_TYPE_NO_BOLT
var/bolt_type = BOLT_TYPE_STANDARD
///Used for locking bolt and open bolt guns. Set a bit differently for the two but prevents firing when true for both.
var/bolt_locked = FALSE
var/show_bolt_icon = TRUE ///Hides the bolt icon.
///Whether the gun has to be racked each shot or not.
var/semi_auto = TRUE
///Actual magazine currently contained within the gun
var/obj/item/ammo_box/magazine/magazine
///whether the gun ejects the chambered casing
var/casing_ejector = TRUE
///Whether the gun has an internal magazine or a detatchable one. Overridden by BOLT_TYPE_NO_BOLT.
var/internal_magazine = FALSE
///Phrasing of the bolt in examine and notification messages; ex: bolt, slide, etc.
var/bolt_wording = "bolt"
///Phrasing of the magazine in examine and notification messages; ex: magazine, box, etx
var/magazine_wording = "magazine"
///Phrasing of the cartridge in examine and notification messages; ex: bullet, shell, dart, etc.
var/cartridge_wording = "bullet"
///length between individual racks
var/rack_delay = 5
///time of the most recent rack, used for cooldown purposes
var/recent_rack = 0
///Whether the gun can be tacloaded by slapping a fresh magazine directly on it
var/tac_reloads = TRUE //Snowflake mechanic no more.
///Whether the gun can be sawn off by sawing tools
var/can_be_sawn_off = FALSE
var/flip_cooldown = 0
var/suppressor_x_offset ///pixel offset for the suppressor overlay on the x axis.
var/suppressor_y_offset ///pixel offset for the suppressor overlay on the y axis.
/// Check if you are able to see if a weapon has a bullet loaded in or not.
var/hidden_chambered = FALSE
///Gun internal magazine modification and misfiring
///Can we modify our ammo type in this gun's internal magazine?
var/can_modify_ammo = FALSE
///our initial ammo type. Should match initial caliber, but a bit of redundency doesn't hurt.
var/initial_caliber
///our alternative ammo type.
var/alternative_caliber
///our initial fire sound. same reasons for initial caliber
var/initial_fire_sound
///our alternative fire sound, in case we want our gun to be louder or quieter or whatever
var/alternative_fire_sound
///If only our alternative ammuntion misfires and not our main ammunition, we set this to TRUE
var/alternative_ammo_misfires = FALSE
/// Misfire Variables ///
/// Whether our ammo misfires now or when it's set by the wrench_act. TRUE means it misfires.
var/can_misfire = FALSE
///How likely is our gun to misfire?
var/misfire_probability = 0
///How much does shooting the gun increment the misfire probability?
var/misfire_percentage_increment = 0
///What is the cap on our misfire probability? Do not set this to 100.
var/misfire_probability_cap = 25
/obj/item/gun/ballistic/Initialize(mapload)
. = ..()
if (!spawnwithmagazine)
bolt_locked = TRUE
update_appearance()
return
if (!magazine)
magazine = new mag_type(src)
if(bolt_type == BOLT_TYPE_STANDARD)
chamber_round()
else
chamber_round(replace_new_round = TRUE)
update_appearance()
RegisterSignal(src, COMSIG_ITEM_RECHARGED, .proc/instant_reload)
/obj/item/gun/ballistic/add_weapon_description()
AddElement(/datum/element/weapon_description, attached_proc = .proc/add_notes_ballistic)
/obj/item/gun/ballistic/fire_sounds()
var/frequency_to_use = sin((90/magazine?.max_ammo) * get_ammo())
var/click_frequency_to_use = 1 - frequency_to_use * 0.75
var/play_click = round(sqrt(magazine?.max_ammo * 2)) > get_ammo()
if(suppressed)
playsound(src, suppressed_sound, suppressed_volume, vary_fire_sound, ignore_walls = FALSE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0)
if(play_click)
playsound(src, 'sound/weapons/gun/general/ballistic_click.ogg', suppressed_volume, vary_fire_sound, ignore_walls = FALSE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0, frequency = click_frequency_to_use)
else
playsound(src, fire_sound, fire_sound_volume, vary_fire_sound)
if(play_click)
playsound(src, 'sound/weapons/gun/general/ballistic_click.ogg', fire_sound_volume, vary_fire_sound, frequency = click_frequency_to_use)
/**
*
* Outputs type-specific weapon stats for ballistic weaponry based on its magazine and its caliber.
* It contains extra breaks for the sake of presentation
*
*/
/obj/item/gun/ballistic/proc/add_notes_ballistic()
if(magazine) // Make sure you have a magazine, to get the notes from
return "\n[magazine.add_notes_box()]"
else
return "\nThe warning attached to the magazine is missing..."
/obj/item/gun/ballistic/vv_edit_var(vname, vval)
. = ..()
if(vname in list(NAMEOF(src, suppressor_x_offset), NAMEOF(src, suppressor_y_offset), NAMEOF(src, internal_magazine), NAMEOF(src, magazine), NAMEOF(src, chambered), NAMEOF(src, empty_indicator), NAMEOF(src, sawn_off), NAMEOF(src, bolt_locked), NAMEOF(src, bolt_type)))
update_appearance()
/obj/item/gun/ballistic/update_icon_state()
if(current_skin)
icon_state = "[unique_reskin[current_skin]][sawn_off ? "_sawn" : ""]"
else
icon_state = "[base_icon_state || initial(icon_state)][sawn_off ? "_sawn" : ""]"
return ..()
/obj/item/gun/ballistic/update_overlays()
. = ..()
if(show_bolt_icon)
if (bolt_type == BOLT_TYPE_LOCKING)
. += "[icon_state]_bolt[bolt_locked ? "_locked" : ""]"
if (bolt_type == BOLT_TYPE_OPEN && bolt_locked)
. += "[icon_state]_bolt"
if(suppressed)
var/mutable_appearance/MA = mutable_appearance(icon, "[icon_state]_suppressor")
if(suppressor_x_offset)
MA.pixel_x = suppressor_x_offset
if(suppressor_y_offset)
MA.pixel_y = suppressor_y_offset
. += MA
if(!chambered && empty_indicator) //this is duplicated in c20's update_overlayss due to a layering issue with the select fire icon.
. += "[icon_state]_empty"
if(gun_flags & TOY_FIREARM_OVERLAY)
. += "[icon_state]_toy"
if(!magazine || internal_magazine || !mag_display)
return
if(special_mags)
. += "[icon_state]_mag_[initial(magazine.icon_state)]"
if(mag_display_ammo && !magazine.ammo_count())
. += "[icon_state]_mag_empty"
return
. += "[icon_state]_mag"
if(!mag_display_ammo)
return
var/capacity_number
switch(get_ammo() / magazine.max_ammo)
if(1 to INFINITY) //cause we can have one in the chamber.
capacity_number = 100
if(0.8 to 1)
capacity_number = 80
if(0.6 to 0.8)
capacity_number = 60
if(0.4 to 0.6)
capacity_number = 40
if(0.2 to 0.4)
capacity_number = 20
if(capacity_number)
. += "[icon_state]_mag_[capacity_number]"
/obj/item/gun/ballistic/handle_chamber(empty_chamber = TRUE, from_firing = TRUE, chamber_next_round = TRUE)
if(!semi_auto && from_firing)
return
var/obj/item/ammo_casing/casing = chambered //Find chambered round
if(istype(casing)) //there's a chambered round
if(QDELING(casing))
stack_trace("Trying to move a qdeleted casing of type [casing.type]!")
chambered = null
else if(casing_ejector || !from_firing)
casing.forceMove(drop_location()) //Eject casing onto ground.
casing.bounce_away(TRUE)
SEND_SIGNAL(casing, COMSIG_CASING_EJECTED)
chambered = null
else if(empty_chamber)
chambered = null
if (chamber_next_round && (magazine?.max_ammo > 1))
chamber_round()
///Used to chamber a new round and eject the old one
/obj/item/gun/ballistic/proc/chamber_round(keep_bullet = FALSE, spin_cylinder, replace_new_round)
if (chambered || !magazine)
return
if (magazine.ammo_count())
chambered = magazine.get_round(keep_bullet || bolt_type == BOLT_TYPE_NO_BOLT)
if (bolt_type != BOLT_TYPE_OPEN)
chambered.forceMove(src)
if(replace_new_round)
magazine.give_round(new chambered.type)
///updates a bunch of racking related stuff and also handles the sound effects and the like
/obj/item/gun/ballistic/proc/rack(mob/user = null)
if (bolt_type == BOLT_TYPE_NO_BOLT) //If there's no bolt, nothing to rack
return
if (bolt_type == BOLT_TYPE_OPEN)
if(!bolt_locked) //If it's an open bolt, racking again would do nothing
if (user)
to_chat(user, span_notice("[src]'s [bolt_wording] is already cocked!"))
return
bolt_locked = FALSE
if (user)
to_chat(user, span_notice("You rack the [bolt_wording] of [src]."))
process_chamber(!chambered, FALSE)
if (bolt_type == BOLT_TYPE_LOCKING && !chambered)
bolt_locked = TRUE
playsound(src, lock_back_sound, lock_back_sound_volume, lock_back_sound_vary)
else
playsound(src, rack_sound, rack_sound_volume, rack_sound_vary)
update_appearance()
///Drops the bolt from a locked position
/obj/item/gun/ballistic/proc/drop_bolt(mob/user = null)
playsound(src, bolt_drop_sound, bolt_drop_sound_volume, FALSE)
if (user)
to_chat(user, span_notice("You drop the [bolt_wording] of [src]."))
chamber_round()
bolt_locked = FALSE
update_appearance()
///Handles all the logic needed for magazine insertion
/obj/item/gun/ballistic/proc/insert_magazine(mob/user, obj/item/ammo_box/magazine/AM, display_message = TRUE)
if(!istype(AM, mag_type))
to_chat(user, span_warning("[AM] doesn't seem to fit into [src]..."))
return FALSE
if(user.transferItemToLoc(AM, src))
magazine = AM
if (display_message)
to_chat(user, span_notice("You load a new [magazine_wording] into [src]."))
playsound(src, load_empty_sound, load_sound_volume, load_sound_vary)
if (bolt_type == BOLT_TYPE_OPEN && !bolt_locked)
chamber_round(TRUE)
update_appearance()
return TRUE
else
to_chat(user, span_warning("You cannot seem to get [src] out of your hands!"))
return FALSE
///Handles all the logic of magazine ejection, if tac_load is set that magazine will be tacloaded in the place of the old eject
/obj/item/gun/ballistic/proc/eject_magazine(mob/user, display_message = TRUE, obj/item/ammo_box/magazine/tac_load = null)
if(bolt_type == BOLT_TYPE_OPEN)
chambered = null
if (magazine.ammo_count())
playsound(src, load_sound, load_sound_volume, load_sound_vary)
else
playsound(src, load_empty_sound, load_sound_volume, load_sound_vary)
magazine.forceMove(drop_location())
var/obj/item/ammo_box/magazine/old_mag = magazine
if (tac_load)
if (insert_magazine(user, tac_load, FALSE))
to_chat(user, span_notice("You perform a tactical reload on [src]."))
else
to_chat(user, span_warning("You dropped the old [magazine_wording], but the new one doesn't fit. How embarassing."))
magazine = null
else
magazine = null
user.put_in_hands(old_mag)
old_mag.update_appearance()
if (display_message)
to_chat(user, span_notice("You pull the [magazine_wording] out of [src]."))
update_appearance()
SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) //SKYRAT EDIT ADDITION
/obj/item/gun/ballistic/can_shoot()
return chambered
/* SKYRAT EDIT REMOVAL MOVED TO MODULAR BALLISTIC_MASTER.DM
/obj/item/gun/ballistic/attackby(obj/item/A, mob/user, params)
. = ..()
if (.)
return
if (!internal_magazine && istype(A, /obj/item/ammo_box/magazine))
var/obj/item/ammo_box/magazine/AM = A
if (!magazine)
insert_magazine(user, AM)
else
if (tac_reloads)
eject_magazine(user, FALSE, AM)
else
to_chat(user, span_notice("There's already a [magazine_wording] in [src]."))
return
if (istype(A, /obj/item/ammo_casing) || istype(A, /obj/item/ammo_box))
if (bolt_type == BOLT_TYPE_NO_BOLT || internal_magazine)
if (chambered && !chambered.loaded_projectile)
chambered.forceMove(drop_location())
chambered = null
var/num_loaded = magazine?.attackby(A, user, params, TRUE)
if (num_loaded)
to_chat(user, span_notice("You load [num_loaded] [cartridge_wording]\s into [src]."))
playsound(src, load_sound, load_sound_volume, load_sound_vary)
if (chambered == null && bolt_type == BOLT_TYPE_NO_BOLT)
chamber_round()
A.update_appearance()
update_appearance()
return
if(istype(A, /obj/item/suppressor))
var/obj/item/suppressor/S = A
if(!can_suppress)
to_chat(user, span_warning("You can't seem to figure out how to fit [S] on [src]!"))
return
if(!user.is_holding(src))
to_chat(user, span_warning("You need be holding [src] to fit [S] to it!"))
return
if(suppressed)
to_chat(user, span_warning("[src] already has a suppressor!"))
return
if(user.transferItemToLoc(A, src))
to_chat(user, span_notice("You screw [S] onto [src]."))
install_suppressor(A)
return
if (can_be_sawn_off)
if (sawoff(user, A))
return
if(can_misfire && istype(A, /obj/item/stack/sheet/cloth))
if(guncleaning(user, A))
return
return FALSE
*/ // SKYRAT EDIT END
/obj/item/gun/ballistic/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
if(magazine && chambered.loaded_projectile && can_misfire && misfire_probability > 0)
if(prob(misfire_probability))
if(blow_up(user))
to_chat(user, span_userdanger("[src] misfires!"))
if (sawn_off)
bonus_spread += SAWN_OFF_ACC_PENALTY
. = ..()
/obj/item/gun/ballistic/shoot_live_shot(mob/living/user, pointblank = 0, atom/pbtarget = null, message = 1)
if(can_misfire)
misfire_probability += misfire_percentage_increment
misfire_probability = clamp(misfire_probability, 0, misfire_probability_cap)
. = ..()
///Installs a new suppressor, assumes that the suppressor is already in the contents of src
/obj/item/gun/ballistic/proc/install_suppressor(obj/item/suppressor/S)
suppressed = S
w_class += S.w_class //so pistols do not fit in pockets when suppressed
update_appearance()
/obj/item/gun/ballistic/clear_suppressor()
if(!can_unsuppress)
return
if(isitem(suppressed))
var/obj/item/I = suppressed
w_class -= I.w_class
return ..()
/obj/item/gun/ballistic/AltClick(mob/user)
if (unique_reskin && !current_skin && user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY))
reskin_obj(user)
return
if(loc == user)
if(suppressed && can_unsuppress)
var/obj/item/suppressor/S = suppressed
if(!user.is_holding(src))
return ..()
to_chat(user, span_notice("You unscrew [S] from [src]."))
user.put_in_hands(S)
clear_suppressor()
///Prefire empty checks for the bolt drop
/obj/item/gun/ballistic/proc/prefire_empty_checks()
if (!chambered && !get_ammo())
if (bolt_type == BOLT_TYPE_OPEN && !bolt_locked)
bolt_locked = TRUE
playsound(src, bolt_drop_sound, bolt_drop_sound_volume)
update_appearance()
SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD)
///postfire empty checks for bolt locking and sound alarms
/obj/item/gun/ballistic/proc/postfire_empty_checks(last_shot_succeeded)
if (!chambered && !get_ammo())
if (empty_alarm && last_shot_succeeded)
playsound(src, empty_alarm_sound, empty_alarm_volume, empty_alarm_vary)
update_appearance()
if (last_shot_succeeded && bolt_type == BOLT_TYPE_LOCKING)
bolt_locked = TRUE
update_appearance()
/obj/item/gun/ballistic/afterattack()
prefire_empty_checks()
. = ..() //The gun actually firing
postfire_empty_checks(.)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/gun/ballistic/attack_hand(mob/user, list/modifiers)
if(!internal_magazine && loc == user && user.is_holding(src) && magazine)
eject_magazine(user)
return
return ..()
/obj/item/gun/ballistic/attack_self(mob/living/user)
if(HAS_TRAIT(user, TRAIT_GUNFLIP))
SpinAnimation(4,2)
if(flip_cooldown <= world.time)
if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(40))
to_chat(user, span_userdanger("While trying to flip [src] you pull the trigger and accidentaly shoot yourself!"))
var/flip_mistake = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_CHEST)
process_fire(user, user, FALSE, flip_mistake)
user.dropItemToGround(src, TRUE)
return
flip_cooldown = (world.time + 30)
user.visible_message(span_notice("[user] spins [src] around [user.p_their()] finger by the trigger. Thats pretty badass."))
playsound(src, 'sound/items/handling/ammobox_pickup.ogg', 20, FALSE)
return
if(!internal_magazine && magazine)
if(!magazine.ammo_count())
eject_magazine(user)
return
if(bolt_type == BOLT_TYPE_NO_BOLT)
chambered = null
var/num_unloaded = 0
for(var/obj/item/ammo_casing/CB in get_ammo_list(FALSE, TRUE))
CB.forceMove(drop_location())
CB.bounce_away(FALSE, NONE)
num_unloaded++
var/turf/T = get_turf(drop_location())
if(T && is_station_level(T.z))
SSblackbox.record_feedback("tally", "station_mess_created", 1, CB.name)
if (num_unloaded)
to_chat(user, span_notice("You unload [num_unloaded] [cartridge_wording]\s from [src]."))
playsound(user, eject_sound, eject_sound_volume, eject_sound_vary)
update_appearance()
else
to_chat(user, span_warning("[src] is empty!"))
return
if(bolt_type == BOLT_TYPE_LOCKING && bolt_locked)
drop_bolt(user)
return
if (recent_rack > world.time)
return
recent_rack = world.time + rack_delay
rack(user)
return
/obj/item/gun/ballistic/examine(mob/user)
. = ..()
var/count_chambered = !(bolt_type == BOLT_TYPE_NO_BOLT || bolt_type == BOLT_TYPE_OPEN)
. += "It has [get_ammo(count_chambered)] round\s remaining."
if (!chambered && !hidden_chambered)
. += "It does not seem to have a round chambered."
if (bolt_locked)
. += "The [bolt_wording] is locked back and needs to be released before firing or de-fouling."
if (suppressed)
. += "It has a suppressor attached that can be removed with <b>alt+click</b>."
if(can_misfire)
. += span_danger("You get the feeling this might explode if you fire it....")
if(misfire_probability > 0)
. += span_danger("Given the state of the gun, there is a [misfire_probability]% chance it'll misfire.")
///Gets the number of bullets in the gun
/obj/item/gun/ballistic/proc/get_ammo(countchambered = TRUE)
var/boolets = 0 //mature var names for mature people
if (chambered && countchambered)
boolets++
if (magazine)
boolets += magazine.ammo_count()
return boolets
///gets a list of every bullet in the gun
/obj/item/gun/ballistic/proc/get_ammo_list(countchambered = TRUE, drop_all = FALSE)
var/list/rounds = list()
if(chambered && countchambered)
rounds.Add(chambered)
if(drop_all)
chambered = null
if(magazine)
rounds.Add(magazine.ammo_list(drop_all))
return rounds
#define BRAINS_BLOWN_THROW_RANGE 3
#define BRAINS_BLOWN_THROW_SPEED 1
/obj/item/gun/ballistic/suicide_act(mob/user)
var/obj/item/organ/internal/brain/B = user.getorganslot(ORGAN_SLOT_BRAIN)
if (B && chambered && chambered.loaded_projectile && can_trigger_gun(user) && !chambered.loaded_projectile.nodamage)
user.visible_message(span_suicide("[user] is putting the barrel of [src] in [user.p_their()] mouth. It looks like [user.p_theyre()] trying to commit suicide!"))
sleep(25)
if(user.is_holding(src))
var/turf/T = get_turf(user)
process_fire(user, user, FALSE, null, BODY_ZONE_HEAD)
user.visible_message(span_suicide("[user] blows [user.p_their()] brain[user.p_s()] out with [src]!"))
var/turf/target = get_ranged_target_turf(user, turn(user.dir, 180), BRAINS_BLOWN_THROW_RANGE)
B.Remove(user)
B.forceMove(T)
var/datum/callback/gibspawner = CALLBACK(GLOBAL_PROC, /proc/spawn_atom_to_turf, /obj/effect/gibspawner/generic, B, 1, FALSE, user)
B.throw_at(target, BRAINS_BLOWN_THROW_RANGE, BRAINS_BLOWN_THROW_SPEED, callback=gibspawner)
return(BRUTELOSS)
else
user.visible_message(span_suicide("[user] panics and starts choking to death!"))
return(OXYLOSS)
else
user.visible_message(span_suicide("[user] is pretending to blow [user.p_their()] brain[user.p_s()] out with [src]! It looks like [user.p_theyre()] trying to commit suicide!</b>"))
playsound(src, dry_fire_sound, 30, TRUE)
return (OXYLOSS)
#undef BRAINS_BLOWN_THROW_SPEED
#undef BRAINS_BLOWN_THROW_RANGE
GLOBAL_LIST_INIT(gun_saw_types, typecacheof(list(
/obj/item/gun/energy/plasmacutter,
/obj/item/melee/energy,
/obj/item/dualsaber
)))
///Handles all the logic of sawing off guns,
/obj/item/gun/ballistic/proc/sawoff(mob/user, obj/item/saw)
if(!saw.get_sharpness() || (!is_type_in_typecache(saw, GLOB.gun_saw_types) && saw.tool_behaviour != TOOL_SAW)) //needs to be sharp. Otherwise turned off eswords can cut this.
return
if(sawn_off)
to_chat(user, span_warning("[src] is already shortened!"))
return
if(bayonet)
to_chat(user, span_warning("You cannot saw-off [src] with [bayonet] attached!"))
return
user.changeNext_move(CLICK_CD_MELEE)
user.visible_message(span_notice("[user] begins to shorten [src]."), span_notice("You begin to shorten [src]..."))
//if there's any live ammo inside the gun, makes it go off
if(blow_up(user))
user.visible_message(span_danger("[src] goes off!"), span_danger("[src] goes off in your face!"))
return
if(do_after(user, 30, target = src))
if(sawn_off)
return
user.visible_message(span_notice("[user] shortens [src]!"), span_notice("You shorten [src]."))
name = "sawn-off [src.name]"
desc = sawn_desc
w_class = WEIGHT_CLASS_NORMAL
//The file might not have a "gun" icon, let's prepare for this
lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
inhand_x_dimension = 32
inhand_y_dimension = 32
inhand_icon_state = "gun"
worn_icon_state = "gun"
slot_flags &= ~ITEM_SLOT_BACK //you can't sling it on your back
slot_flags |= ITEM_SLOT_BELT //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally)
recoil = SAWN_OFF_RECOIL
sawn_off = TRUE
update_appearance()
return TRUE
/obj/item/gun/ballistic/proc/guncleaning(mob/user, obj/item/A)
if(misfire_probability == 0)
to_chat(user, span_notice("[src] seems to be already clean of fouling."))
return
user.changeNext_move(CLICK_CD_MELEE)
user.visible_message(span_notice("[user] begins to cleaning [src]."), span_notice("You begin to clean the internals of [src]."))
if(do_after(user, 100, target = src))
var/original_misfire_value = initial(misfire_probability)
if(misfire_probability > original_misfire_value)
misfire_probability = original_misfire_value
user.visible_message(span_notice("[user] cleans [src] of any fouling."), span_notice("You clean [src], removing any fouling, preventing misfire."))
return TRUE
/obj/item/gun/ballistic/wrench_act(mob/living/user, obj/item/I)
if(!user.is_holding(src))
to_chat(user, span_notice("You need to hold [src] to modify it."))
return TRUE
if(!can_modify_ammo)
return
if(bolt_type == BOLT_TYPE_STANDARD)
if(get_ammo())
to_chat(user, span_notice("You can't get at the internals while the gun has a bullet in it!"))
return
else if(!bolt_locked)
to_chat(user, span_notice("You can't get at the internals while the bolt is down!"))
return
to_chat(user, span_notice("You begin to tinker with [src]..."))
I.play_tool_sound(src)
if(!I.use_tool(src, user, 3 SECONDS))
return TRUE
if(blow_up(user))
user.visible_message(span_danger("[src] goes off!"), span_danger("[src] goes off in your face!"))
return
if(magazine.caliber == initial_caliber)
magazine.caliber = alternative_caliber
if(alternative_ammo_misfires)
can_misfire = TRUE
fire_sound = alternative_fire_sound
to_chat(user, span_notice("You modify [src]. Now it will fire [alternative_caliber] rounds."))
else
magazine.caliber = initial_caliber
if(alternative_ammo_misfires)
can_misfire = FALSE
fire_sound = initial_fire_sound
to_chat(user, span_notice("You reset [src]. Now it will fire [initial_caliber] rounds."))
///used for sawing guns, causes the gun to fire without the input of the user
/obj/item/gun/ballistic/proc/blow_up(mob/user)
. = FALSE
for(var/obj/item/ammo_casing/AC in magazine.stored_ammo)
if(AC.loaded_projectile)
process_fire(user, user, FALSE)
. = TRUE
/obj/item/gun/ballistic/proc/instant_reload()
SIGNAL_HANDLER
if(magazine)
magazine.top_off()
else
if(!mag_type)
return
magazine = new mag_type(src)
chamber_round()
update_appearance()
/obj/item/suppressor
name = "suppressor"
desc = "A Scarborough Arms small-arms suppressor for maximum espionage." //SKYRAT EDIT
icon = 'icons/obj/guns/ballistic.dmi'
icon_state = "suppressor"
w_class = WEIGHT_CLASS_TINY
/obj/item/suppressor/specialoffer
name = "cheap suppressor"
desc = "A foreign knock-off suppressor, it feels flimsy, cheap, and brittle. Still fits most weapons."