Files
CHOMPStation2/code/modules/projectiles/guns/projectile.dm
mwerezak 630369b9dd More flash shell tweaks, autolathe recipes
Tweaks the flash shell some more.
Tweaks shotgun stun shells, adds spent icon.
Additional autolathe ammo recipes.
2015-02-11 22:48:21 -05:00

199 lines
7.3 KiB
Plaintext

#define HOLD_CASINGS 0 //do not do anything after firing. Manual action, like pump shotguns
#define EJECT_CASINGS 1 //drop spent casings on the ground after firing
#define CYCLE_CASINGS 2 //experimental: cycle casings, like a revolver. Also works for multibarrelled guns
/obj/item/weapon/gun/projectile
name = "gun"
desc = "A gun that fires bullets."
icon_state = "revolver"
origin_tech = "combat=2;materials=2"
w_class = 3
matter = list("metal" = 1000)
recoil = 1
var/caliber = "357" //determines which casings will fit
var/handle_casings = EJECT_CASINGS //determines how spent casings should be handled
var/load_method = SINGLE_CASING|SPEEDLOADER //1 = Single shells, 2 = box or quick loader, 3 = magazine
var/obj/item/ammo_casing/chambered = null
//For SINGLE_CASING or SPEEDLOADER guns
var/max_shells = 0 //the number of casings that will fit inside
var/ammo_type = null //the type of ammo that the gun comes preloaded with
var/list/loaded = list() //stored ammo
//For MAGAZINE guns
var/magazine_type = null //the type of magazine that the gun comes preloaded with
var/obj/item/ammo_magazine/ammo_magazine = null //stored magazine
var/auto_eject = 0 //if the magazine should automatically eject itself when empty.
var/auto_eject_sound = null
/obj/item/weapon/gun/projectile/New()
..()
if(ispath(ammo_type) && (load_method & (SINGLE_CASING|SPEEDLOADER)))
for(var/i in 1 to max_shells)
loaded += new ammo_type(src)
if(ispath(magazine_type) && (load_method & MAGAZINE))
ammo_magazine = new magazine_type(src)
update_icon()
/obj/item/weapon/gun/projectile/can_fire()
var/obj/item/ammo_casing/C
if(loaded.len)
C = loaded[1]
else if(ammo_magazine && ammo_magazine.stored_ammo.len)
C = ammo_magazine.stored_ammo[1]
return (C && C.BB)
/obj/item/weapon/gun/projectile/get_next_projectile()
//store the next ammo_casing in a var so that handle_post_fire() knows which one to eject
//also we might as well remove chambered here, so that we don't have to figure out where it came from later
if(loaded.len)
chambered = loaded[1] //load next casing.
if(handle_casings != HOLD_CASINGS)
loaded -= chambered
else if(ammo_magazine && ammo_magazine.stored_ammo.len)
chambered = ammo_magazine.stored_ammo[1]
if(handle_casings != HOLD_CASINGS)
ammo_magazine.stored_ammo -= chambered
return chambered.BB
/obj/item/weapon/gun/projectile/handle_post_fire()
..()
if(chambered)
chambered.expend()
switch(handle_casings)
if(EJECT_CASINGS) //eject casing onto ground.
chambered.loc = get_turf(src)
chambered = null
if(CYCLE_CASINGS) //cycle the casing back to the end.
if(ammo_magazine)
ammo_magazine.stored_ammo += chambered
else
loaded += chambered
chambered = null
//Attempts to load A into src, depending on the type of thing being loaded and the load_method
//Maybe this should be broken up into separate procs for each load method?
/obj/item/weapon/gun/projectile/proc/load_ammo(var/obj/item/A, mob/user)
if(istype(A, /obj/item/ammo_magazine))
var/obj/item/ammo_magazine/AM = A
if(!(load_method & AM.mag_type) || caliber != AM.caliber)
return //incompatible
switch(AM.mag_type)
if(MAGAZINE)
if(ammo_magazine)
user << "<span class='warning'>[src] already has a magazine loaded!</span>" //already a magazine here
return
user.remove_from_mob(AM)
AM.loc = src
ammo_magazine = AM
user.visible_message("[user] inserts [AM] into [src].", "<span class='notice'>You insert [AM] into [src]!</span>")
if(SPEEDLOADER)
if(loaded.len >= max_shells)
user << "<span class='warning'>[src] is full!</span>"
return
var/count = 0
for(var/obj/item/ammo_casing/C in AM.stored_ammo)
if(loaded.len >= max_shells)
break
if(C.caliber == caliber)
C.loc = src
loaded += C
AM.stored_ammo -= C //should probably go inside an ammo_magazine proc, but I guess less proc calls this way...
count++
if(count)
user.visible_message("[user] reloads [src].", "<span class='notice'>You load [count] round\s into [src]!</span>")
AM.update_icon()
else if(istype(A, /obj/item/ammo_casing))
var/obj/item/ammo_casing/C = A
if(!(load_method & SINGLE_CASING) || caliber != C.caliber)
return //incompatible
if(loaded.len >= max_shells)
user << "<span class='warning'>[src] is full!</span>"
return
user.remove_from_mob(C)
C.loc = src
loaded.Insert(1, C) //add to the head of the list
user.visible_message("[user] inserts \a [C] into [src].", "<span class='notice'>You insert \a [C] into [src]!</span>")
update_icon()
//attempts to unload src
/obj/item/weapon/gun/projectile/proc/unload_ammo(mob/user)
if(ammo_magazine)
user.put_in_hands(ammo_magazine)
user.visible_message("[user] removes [ammo_magazine] from [src].", "<span class='notice'>You remove [ammo_magazine] from [src]!</span>")
ammo_magazine.update_icon()
ammo_magazine = null
else if(loaded.len)
//presumably, if it can be speed-loaded, it can be speed-unloaded.
if(load_method & SPEEDLOADER)
var/count = 0
var/turf/T = get_turf(user)
if(T)
for(var/obj/item/ammo_casing/C in loaded)
C.loc = T
count++
loaded.Cut()
if(count)
user.visible_message("[user] unloads [src].", "<span class='notice'>You unload [count] round\s from [src]!</span>")
else if(load_method & SINGLE_CASING)
var/obj/item/ammo_casing/C = loaded[loaded.len]
loaded.len--
user.put_in_hands(C)
user.visible_message("[user] removes \a [C] from [src].", "<span class='notice'>You remove \a [C] from [src]!</span>")
else
user << "<span class='warning'>[src] is empty!</span>"
update_icon()
/obj/item/weapon/gun/projectile/attackby(var/obj/item/A as obj, mob/user as mob)
load_ammo(A, user)
/obj/item/weapon/gun/projectile/attack_self(mob/user as mob)
if (aim_targets) //TODO replace untargeting with a hotkey
return ..()
unload_ammo(user)
/obj/item/weapon/gun/projectile/attack_hand(mob/user as mob)
//allow guns with both SPEEDLOADER and SINGLE_CASING a way to remove casings without dumping everything on the floor
if((load_method & SINGLE_CASING) && loaded.len && (src in user))
var/obj/item/ammo_casing/C = loaded[loaded.len]
loaded.len--
user.put_in_hands(C)
user.visible_message("[user] removes \a [C] from [src].", "<span class='notice'>You remove \a [C] from [src]!</span>")
else
return ..()
/obj/item/weapon/gun/projectile/afterattack(atom/A, mob/living/user)
..()
if(auto_eject && ammo_magazine && ammo_magazine.stored_ammo && !ammo_magazine.stored_ammo.len)
ammo_magazine.loc = get_turf(src.loc)
user.visible_message(
"[ammo_magazine] falls out and clatters on the floor!",
"<span class='notice'>[ammo_magazine] falls out and clatters on the floor!</span>"
)
if(auto_eject_sound)
playsound(user, auto_eject_sound, 40, 1)
ammo_magazine = null
update_icon()
/obj/item/weapon/gun/projectile/examine(mob/user)
..(user)
user << "Has [getAmmo()] round\s remaining."
if(ammo_magazine)
user << "It has \a [ammo_magazine] loaded."
return
/obj/item/weapon/gun/projectile/proc/getAmmo()
var/bullets = 0
if(loaded)
bullets += loaded.len
if(ammo_magazine && ammo_magazine.stored_ammo)
bullets += ammo_magazine.stored_ammo.len
if(chambered)
bullets += 1
return bullets