mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2025-12-24 00:51:26 +00:00
The Leyon Pistol has been changed to be closer to the original vision of it. It is loaded via a special 10mm speedloader, however loading it takes some time. Each gun (when ordered via cargo) will ship with a speedloader. The speedloaders itself can not be ordered, but 10mm ammunition boxes can which can be used to restock the speedloaders or load the gun directly. Reduces the price for the gun at the merchants from 1500 to 500 credits And makes additional ammo boxes purchaseable Also changes the path name of the 10mm ammo/mags/... to be consistent with the standard pathing for ammo Adds additional messages if you can not load a gun with a single cartridge (currently it fails silently)
260 lines
9.1 KiB
Plaintext
260 lines
9.1 KiB
Plaintext
#define HOLD_CASINGS 0 //do not do anything after firing. Manual action, like pump shotguns, or guns that want to define custom behaviour
|
|
#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 = list(TECH_COMBAT = 2, TECH_MATERIAL = 2)
|
|
w_class = 3
|
|
matter = list(DEFAULT_WALL_MATERIAL = 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/list/allowed_magazines //determines list of which magazines will fit in the gun
|
|
var/auto_eject = 0 //if the magazine should automatically eject itself when empty.
|
|
var/auto_eject_sound = null
|
|
|
|
var/is_jammed = 0 //Whether this gun is jammed
|
|
var/jam_chance = 0 //Chance it jams on fire
|
|
|
|
//TODO generalize ammo icon states for guns
|
|
//var/magazine_states = 0
|
|
//var/list/icon_keys = list() //keys
|
|
//var/list/ammo_states = list() //values
|
|
|
|
/obj/item/weapon/gun/projectile/Initialize()
|
|
. = ..()
|
|
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/consume_next_projectile()
|
|
if(is_jammed)
|
|
return 0
|
|
//get the next casing
|
|
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
|
|
|
|
if (chambered)
|
|
return chambered.BB
|
|
return null
|
|
|
|
/obj/item/weapon/gun/projectile/handle_post_fire()
|
|
..()
|
|
if(chambered)
|
|
chambered.expend()
|
|
process_chambered()
|
|
|
|
/obj/item/weapon/gun/projectile/handle_click_empty()
|
|
..()
|
|
process_chambered()
|
|
|
|
/obj/item/weapon/gun/projectile/special_check(var/mob/user)
|
|
if(!..())
|
|
return 0
|
|
if(!is_jammed && jam_chance)
|
|
if(prob(jam_chance))
|
|
user << "<span class='danger'>\The [src] jams!</span>"
|
|
is_jammed = 1
|
|
return 1
|
|
|
|
/obj/item/weapon/gun/projectile/proc/process_chambered()
|
|
if (!chambered) return
|
|
|
|
// Aurora forensics port, gunpowder residue.
|
|
if(chambered.leaves_residue)
|
|
var/mob/living/carbon/human/H = loc
|
|
if(istype(H))
|
|
if(!H.gloves)
|
|
H.gunshot_residue = chambered.caliber
|
|
else
|
|
var/obj/item/clothing/G = H.gloves
|
|
G.gunshot_residue = chambered.caliber
|
|
|
|
switch(handle_casings)
|
|
if(EJECT_CASINGS) //eject casing onto ground.
|
|
chambered.forceMove(get_turf(src))
|
|
if(CYCLE_CASINGS) //cycle the casing back to the end.
|
|
if(ammo_magazine)
|
|
ammo_magazine.stored_ammo += chambered
|
|
else
|
|
loaded += chambered
|
|
|
|
if(handle_casings != HOLD_CASINGS)
|
|
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 || (allowed_magazines && !is_type_in_list(A, allowed_magazines)))
|
|
to_chat(user,"<span class='warning'>[AM] won't load into [src]!</span>")
|
|
return
|
|
switch(AM.mag_type)
|
|
if(MAGAZINE)
|
|
if(ammo_magazine)
|
|
to_chat(user,"<span class='warning'>[src] already has a magazine loaded.</span>") //already a magazine here
|
|
return
|
|
user.remove_from_mob(AM)
|
|
AM.forceMove(src)
|
|
ammo_magazine = AM
|
|
user.visible_message("[user] inserts [AM] into [src].", "<span class='notice'>You insert [AM] into [src].</span>")
|
|
playsound(src.loc, 'sound/weapons/flipblade.ogg', 50, 1)
|
|
if(SPEEDLOADER)
|
|
if(loaded.len >= max_shells)
|
|
to_chat(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.forceMove(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>")
|
|
playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1)
|
|
AM.update_icon()
|
|
else if(istype(A, /obj/item/ammo_casing))
|
|
var/obj/item/ammo_casing/C = A
|
|
if(!(load_method & SINGLE_CASING))
|
|
to_chat(user,"<span class='warning'>[src] can not be loaded with single casings.</span>")
|
|
return //incompatible
|
|
if(caliber != C.caliber)
|
|
to_chat(user,"<span class='warning'>\The [C] does not fit.</span>")
|
|
return //incompatible
|
|
if(loaded.len >= max_shells)
|
|
to_chat(user,"<span class='warning'>[src] is full.</span>")
|
|
return
|
|
|
|
user.remove_from_mob(C)
|
|
C.forceMove(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>")
|
|
playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1)
|
|
|
|
update_icon()
|
|
|
|
//attempts to unload src. If allow_dump is set to 0, the speedloader unloading method will be disabled
|
|
/obj/item/weapon/gun/projectile/proc/unload_ammo(mob/user, var/allow_dump=1)
|
|
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>")
|
|
playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1)
|
|
ammo_magazine.update_icon()
|
|
ammo_magazine = null
|
|
else if(loaded.len)
|
|
//presumably, if it can be speed-loaded, it can be speed-unloaded.
|
|
if(allow_dump && (load_method & SPEEDLOADER))
|
|
var/count = 0
|
|
var/turf/T = get_turf(user)
|
|
if(T)
|
|
for(var/obj/item/ammo_casing/C in loaded)
|
|
C.forceMove(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(is_jammed)
|
|
user << "<span class='notice'>\The [user] unjams \the [src]!</span>"
|
|
if(do_after(user, 5))
|
|
playsound(src.loc, 'sound/weapons/empty.ogg', 100, 1)
|
|
is_jammed = 0
|
|
else if(firemodes.len > 1)
|
|
..()
|
|
else
|
|
unload_ammo(user)
|
|
|
|
/obj/item/weapon/gun/projectile/attack_hand(mob/user as mob)
|
|
if(user.get_inactive_hand() == src)
|
|
unload_ammo(user, allow_dump=0)
|
|
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.forceMove(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.update_icon()
|
|
ammo_magazine = null
|
|
update_icon() //make sure to do this after unsetting ammo_magazine
|
|
|
|
/obj/item/weapon/gun/projectile/examine(mob/user)
|
|
..(user)
|
|
if(is_jammed)
|
|
user << "<span class='warning'>It looks jammed.</span>"
|
|
if(ammo_magazine)
|
|
user << "It has \a [ammo_magazine] loaded."
|
|
user << "Has [getAmmo()] round\s remaining."
|
|
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
|
|
|
|
/* Unneeded -- so far.
|
|
//in case the weapon has firemodes and can't unload using attack_hand()
|
|
/obj/item/weapon/gun/projectile/verb/unload_gun()
|
|
set name = "Unload Ammo"
|
|
set category = "Object"
|
|
set src in usr
|
|
|
|
if(usr.stat || usr.restrained()) return
|
|
|
|
unload_ammo(usr)
|
|
*/
|