mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 10:21:11 +00:00
* look at the branch name of the last pr and this one * fix and add * added one * cabinet * more * flavor text and KEEPER AI module bugfix but why? * \the * real flavor text hours also secrets and an email account * ace coder over here * revert fun removal and fix things * cig * let's see if this resolves inti's remaining change * oh hey pomf copy-pasted from mecha.dm * this didn't commit wtf * "whyyyyyyy", said the bauds. and nothing got done that day * there was even another src.name * exxion * revert fun description removal * whoops didn't commit vox change * spooky
267 lines
8.6 KiB
Plaintext
267 lines
8.6 KiB
Plaintext
#define SPEEDLOADER 0 //the gun takes bullets directly
|
|
#define FROM_BOX 1
|
|
#define MAGAZINE 2 //the gun takes a magazine into gun storage
|
|
|
|
/obj/item/weapon/gun/projectile
|
|
desc = "A classic revolver. Uses .357 ammo."
|
|
name = "revolver"
|
|
icon_state = "revolver"
|
|
caliber = list("357" = 1)
|
|
origin_tech = Tc_COMBAT + "=2;" + Tc_MATERIALS + "=2"
|
|
w_class = W_CLASS_MEDIUM
|
|
starting_materials = list(MAT_IRON = 1000)
|
|
w_type = RECYK_METAL
|
|
recoil = 1
|
|
var/ammo_type = "/obj/item/ammo_casing/a357"
|
|
var/list/loaded = list()
|
|
var/max_shells = 7 //only used by guns with no magazine
|
|
var/load_method = SPEEDLOADER //0 = Single shells or quick loader, 1 = box, 2 = magazine
|
|
var/obj/item/ammo_storage/magazine/stored_magazine = null
|
|
var/obj/item/ammo_casing/chambered = null
|
|
var/mag_type = ""
|
|
var/mag_drop_sound ='sound/weapons/magdrop_1.ogg'
|
|
|
|
var/gun_flags = EMPTYCASINGS //Yay, flags
|
|
|
|
/obj/item/weapon/gun/projectile/isHandgun() //fffuuuuuuck non-abstract base types
|
|
return TRUE
|
|
|
|
/obj/item/weapon/gun/projectile/New()
|
|
..()
|
|
if(mag_type && load_method == 2)
|
|
stored_magazine = new mag_type(src)
|
|
chamber_round()
|
|
else
|
|
for(var/i = 1, i <= max_shells, i++)
|
|
if(ammo_type)
|
|
loaded += new ammo_type(src)
|
|
update_icon()
|
|
return
|
|
|
|
//loads the argument magazine into the gun
|
|
/obj/item/weapon/gun/projectile/proc/LoadMag(var/obj/item/ammo_storage/magazine/AM, var/mob/user)
|
|
if(istype(AM, text2path(mag_type)) && !stored_magazine)
|
|
if(user)
|
|
if(user.drop_item(AM, src))
|
|
to_chat(usr, "<span class='notice'>You load the magazine into \the [src].</span>")
|
|
else
|
|
return
|
|
|
|
stored_magazine = AM
|
|
chamber_round()
|
|
AM.update_icon()
|
|
update_icon()
|
|
|
|
if(user)
|
|
user.update_inv_hands()
|
|
return 1
|
|
return 0
|
|
|
|
/obj/item/weapon/gun/projectile/proc/RemoveMag(var/mob/user)
|
|
if(stored_magazine)
|
|
if(jammed)
|
|
to_chat(usr, "<span class='notice'>You begin unjamming \the [name]...</span>")
|
|
if(do_after(usr,src,50))
|
|
jammed = 0
|
|
in_chamber = null
|
|
var/dropped_bullets
|
|
var/to_drop = rand(stored_magazine.max_ammo/4, stored_magazine.max_ammo/3)
|
|
for(var/i = 1; i<=min(to_drop, stored_magazine.stored_ammo.len); i++)
|
|
var/obj/item/ammo_casing/AC = stored_magazine.stored_ammo[1]
|
|
stored_magazine.stored_ammo -= AC
|
|
AC.forceMove(get_turf(user))
|
|
dropped_bullets++
|
|
stored_magazine.update_icon()
|
|
to_chat(usr, "<span class='notice'>You unjam the [name], and spill [dropped_bullets] bullet\s in the process.</span>")
|
|
chamber_round()
|
|
update_icon()
|
|
return 0
|
|
return 0
|
|
stored_magazine.forceMove(get_turf(src.loc))
|
|
if(user)
|
|
user.put_in_hands(stored_magazine)
|
|
to_chat(usr, "<span class='notice'>You pull the magazine out of \the [src]!</span>")
|
|
stored_magazine.update_icon()
|
|
stored_magazine = null
|
|
update_icon()
|
|
if(user)
|
|
user.update_inv_hands()
|
|
return 1
|
|
return 0
|
|
|
|
/obj/item/weapon/gun/projectile/verb/force_removeMag()
|
|
set name = "Remove Ammo / Magazine"
|
|
set category = "Object"
|
|
set src in range(0)
|
|
if(usr.incapacitated())
|
|
to_chat(usr, "<span class='rose'>You can't do this!</span>")
|
|
return
|
|
if(stored_magazine)
|
|
RemoveMag(usr)
|
|
else
|
|
to_chat(usr, "<span class='rose'>There is no magazine to remove!</span>")
|
|
|
|
|
|
/obj/item/weapon/gun/projectile/proc/chamber_round() //Only used by guns with magazine
|
|
if(chambered || !stored_magazine)
|
|
return 0
|
|
else
|
|
var/obj/item/ammo_casing/round = stored_magazine.get_round()
|
|
if(istype(round))
|
|
chambered = round
|
|
chambered.forceMove(src)
|
|
return 1
|
|
return 0
|
|
|
|
/obj/item/weapon/gun/projectile/proc/getAC()
|
|
var/obj/item/ammo_casing/AC = null
|
|
if(mag_type && load_method == 2)
|
|
AC = chambered
|
|
else if(getAmmo())
|
|
AC = loaded[1] //load next casing.
|
|
return AC
|
|
|
|
/obj/item/weapon/gun/projectile/process_chambered()
|
|
var/obj/item/ammo_casing/AC = getAC()
|
|
if(in_chamber)
|
|
return 1 //{R}
|
|
if(isnull(AC) || !istype(AC))
|
|
return
|
|
if(mag_type && load_method == 2)
|
|
chambered = null //Remove casing from chamber.
|
|
chamber_round()
|
|
else
|
|
loaded -= AC //Remove casing from loaded list.
|
|
if(gun_flags &EMPTYCASINGS)
|
|
AC.forceMove(get_turf(src)) //Eject casing onto ground.
|
|
if(AC.BB)
|
|
in_chamber = AC.BB //Load projectile into chamber.
|
|
AC.BB.forceMove(src) //Set projectile loc to gun.
|
|
AC.BB = null //Empty casings
|
|
AC.update_icon()
|
|
return 1
|
|
return 0
|
|
|
|
/obj/item/weapon/gun/projectile/attackby(var/obj/item/A as obj, mob/user as mob)
|
|
if(istype(A, /obj/item/gun_part/silencer) && src.gun_flags &SILENCECOMP)
|
|
if(!user.is_holding_item(src)) //if we're not in his hands
|
|
to_chat(user, "<span class='notice'>You'll need [src] in your hands to do that.</span>")
|
|
return
|
|
|
|
if(user.drop_item(A, src)) //put the silencer into the gun
|
|
to_chat(user, "<span class='notice'>You screw [A] onto [src].</span>")
|
|
silenced = A //dodgy?
|
|
w_class = W_CLASS_MEDIUM
|
|
update_icon()
|
|
return 1
|
|
|
|
var/num_loaded = 0
|
|
if(istype(A, /obj/item/ammo_storage/magazine))
|
|
var/obj/item/ammo_storage/magazine/AM = A
|
|
if(load_method == MAGAZINE)
|
|
if(!stored_magazine)
|
|
LoadMag(AM, user)
|
|
else
|
|
to_chat(user, "<span class='rose'>There is already a magazine loaded in \the [src]!</span>")
|
|
else
|
|
to_chat(user, "<span class='rose'>You can't load \the [src] with a magazine, dummy!</span>")
|
|
if(istype(A, /obj/item/ammo_storage) && load_method != MAGAZINE)
|
|
var/obj/item/ammo_storage/AS = A
|
|
var/success_load = AS.LoadInto(AS, src)
|
|
if(success_load)
|
|
to_chat(user, "<span class='notice'>You successfully fill the [src] with [success_load] shell\s from the [AS].</span>")
|
|
if(istype(A, /obj/item/ammo_casing))
|
|
var/obj/item/ammo_casing/AC = A
|
|
//message_admins("Loading the [src], with [AC], [AC.caliber] and [caliber.len]") //Enable this for testing
|
|
if(AC.BB && caliber[AC.caliber]) // a used bullet can't be fired twice
|
|
if(load_method == MAGAZINE && !chambered)
|
|
if(user.drop_item(AC, src))
|
|
chambered = AC
|
|
num_loaded++
|
|
playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 25, 1)
|
|
else if(getAmmo() < max_shells && load_method != MAGAZINE)
|
|
if(user.drop_item(AC, src))
|
|
loaded += AC
|
|
num_loaded++
|
|
playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 25, 1)
|
|
|
|
if(num_loaded)
|
|
to_chat(user, "<span class='notice'>You load [num_loaded] shell\s into \the [src]!</span>")
|
|
A.update_icon()
|
|
update_icon()
|
|
return
|
|
|
|
/obj/item/weapon/gun/projectile/attack_self(mob/user as mob)
|
|
if (target)
|
|
return ..()
|
|
if (loaded.len || stored_magazine)
|
|
if (load_method == SPEEDLOADER)
|
|
var/obj/item/ammo_casing/AC = loaded[1]
|
|
loaded -= AC
|
|
AC.forceMove(get_turf(src)) //Eject casing onto ground.
|
|
to_chat(user, "<span class='notice'>You unload \the [AC] from \the [src]!</span>")
|
|
update_icon()
|
|
return
|
|
if (load_method == MAGAZINE && stored_magazine)
|
|
RemoveMag(user)
|
|
else if(loc == user)
|
|
if(chambered) // So it processing unloading of a bullet first
|
|
var/obj/item/ammo_casing/AC = chambered
|
|
AC.forceMove(get_turf(src)) //Eject casing onto ground.
|
|
chambered = null
|
|
to_chat(user, "<span class='notice'>You unload \the [AC] from \the [src]!</span>")
|
|
update_icon()
|
|
return
|
|
if(silenced)
|
|
if(!user.is_holding_item(src))
|
|
..()
|
|
return
|
|
to_chat(user, "<span class='notice'>You unscrew [silenced] from [src].</span>")
|
|
user.put_in_hands(silenced)
|
|
silenced = 0
|
|
w_class = W_CLASS_SMALL
|
|
update_icon()
|
|
return
|
|
else
|
|
to_chat(user, "<span class='warning'>Nothing loaded in \the [src]!</span>")
|
|
|
|
/obj/item/weapon/gun/projectile/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag, struggle = 0)
|
|
..()
|
|
if(!chambered && stored_magazine && !stored_magazine.ammo_count() && gun_flags &AUTOMAGDROP) //auto_mag_drop decides whether or not the mag is dropped once it empties
|
|
RemoveMag(user)
|
|
if(mag_drop_sound)
|
|
playsound(user, mag_drop_sound, 40, 1)
|
|
return
|
|
|
|
/obj/item/weapon/gun/projectile/examine(mob/user)
|
|
..()
|
|
if(conventional_firearm)
|
|
to_chat(user, "<span class='info'>Has [getAmmo()] round\s remaining.</span>")
|
|
// if(in_chamber && !loaded.len)
|
|
// to_chat(usr, "However, it has a chambered round.")
|
|
// if(in_chamber && loaded.len)
|
|
// to_chat(usr, "It also has a chambered round." {R})
|
|
if(istype(silenced, /obj/item/gun_part/silencer))
|
|
to_chat(user, "<span class='warning'>It has a supressor attached to the barrel.</span>")
|
|
|
|
/obj/item/weapon/gun/projectile/proc/getAmmo()
|
|
var/bullets = 0
|
|
if(mag_type && load_method == 2)
|
|
if(stored_magazine)
|
|
bullets += stored_magazine.ammo_count()
|
|
if(chambered)
|
|
bullets++
|
|
else
|
|
for(var/obj/item/ammo_casing/AC in loaded)
|
|
if(istype(AC))
|
|
bullets += 1
|
|
return bullets
|
|
|
|
/obj/item/weapon/gun/projectile/failure_check(var/mob/living/carbon/human/M)
|
|
if(load_method == MAGAZINE && prob(3))
|
|
jammed = 1
|
|
M.visible_message("*click click*", "<span class='danger'>*click*</span>")
|
|
playsound(M, empty_sound, 100, 1)
|
|
return 0
|
|
return ..()
|