mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-01-28 10:12:01 +00:00
This PR is mostly about fixing body/cryo bag layering issues. Currently live and annoying to try and treat around:  Currently, bags are are below roller beds when first deployed. If buckled and unbuckled, they are then above, because of the + 0.1 on buckling. If buckled on a roller bed, dragged, and then unbuckled, they are then above player mobs, because of the `layer + 1` on roller bed `Move()` proc, which also doesn't get reset. I removed this +1 on Move() because it was older than the +0.1 and seems to serve the same purpose. The +0.1 did not have a method of reverting to the original buckled atom's layer, so I added a var and check to handle that. I also changed bullet casing's layer to `BELOW_TABLE_LAYER` with the intent of not having them on top of stasis bags and roller beds, because it causes issues as someone trying to treat wounded after a firefight. Changing bullet casing's layer does have a consequence of putting bullets underneath, for example, the soil in the public garden. Putting the soil on `BELOW_TABLE_LAYER` would fix this, but with the consequence of having the bushes be on top, making clicking the soil difficult for farming purposes. Adding bushes to that layer causes consequences with side window layering (like on deck three outside the public lounge.) For this PR I only adjusted the bullet casing layer. I'm sure it has other unintended layering interactions. Let me know if it would be better to leave them where they are or on some other layer. Current changes in PR: Bullets under soil/roller beds/stasis bags.  (Example of why making bullets on top of soil is difficult) Screenshot shows both bullets and the soil being `BELOW_TABLE_LAYER` with bushes on top, as a reference. PR Currently has bullets underneath soil. 
220 lines
7.3 KiB
Plaintext
220 lines
7.3 KiB
Plaintext
/obj/item/ammo_casing
|
|
name = "bullet casing"
|
|
desc = "A bullet casing."
|
|
icon = 'icons/obj/ammo.dmi'
|
|
icon_state = "s-casing"
|
|
randpixel = 10
|
|
obj_flags = OBJ_FLAG_CONDUCTABLE
|
|
slot_flags = SLOT_BELT | SLOT_EARS
|
|
throwforce = 1
|
|
layer = BELOW_TABLE_LAYER
|
|
w_class = WEIGHT_CLASS_TINY
|
|
|
|
var/leaves_residue = 1
|
|
var/caliber = "" //Which kind of guns it can be loaded into
|
|
var/max_stack = 5 // how many of us can fit in a pile
|
|
var/projectile_type //The bullet type to create when New() is called
|
|
var/obj/projectile/BB = null //The loaded bullet - make it so that the projectiles are created only when needed?
|
|
var/spent_icon = "s-casing-spent"
|
|
|
|
drop_sound = /singleton/sound_category/casing_drop_sound
|
|
pickup_sound = 'sound/items/pickup/ring.ogg'
|
|
var/reload_sound = 'sound/weapons/reload_bullet.ogg' //sound that plays when inserted into gun.
|
|
|
|
/obj/item/ammo_casing/Initialize()
|
|
. = ..()
|
|
if(ispath(projectile_type))
|
|
BB = new projectile_type(src)
|
|
else
|
|
expend() // allows spawning spent casings by nulling projectile_type
|
|
randpixel_xy()
|
|
transform = turn(transform,rand(0,360))
|
|
|
|
/obj/item/ammo_casing/Destroy()
|
|
QDEL_NULL(BB)
|
|
. = ..()
|
|
|
|
//removes the projectile from the ammo casing
|
|
/obj/item/ammo_casing/proc/expend()
|
|
. = BB
|
|
BB = null
|
|
set_dir(pick(GLOB.alldirs)) //spin spent casings
|
|
update_icon()
|
|
|
|
/obj/item/ammo_casing/attackby(obj/item/attacking_item, mob/user)
|
|
if(attacking_item.isscrewdriver())
|
|
if(!BB)
|
|
to_chat(user, SPAN_NOTICE("There is no bullet in the casing to inscribe anything into."))
|
|
return
|
|
|
|
var/tmp_label = ""
|
|
var/label_text = sanitizeSafe( tgui_input_text(user, "Inscribe some text into \the [initial(BB.name)]", "Inscription", tmp_label, MAX_NAME_LEN), MAX_NAME_LEN )
|
|
if(length(label_text) > 20)
|
|
to_chat(user, SPAN_WARNING("The inscription can be at most 20 characters long."))
|
|
else if(!label_text)
|
|
to_chat(user, SPAN_NOTICE("You scratch the inscription off of [initial(BB)]."))
|
|
BB.name = initial(BB.name)
|
|
else
|
|
to_chat(user, SPAN_NOTICE("You inscribe \"[label_text]\" into \the [initial(BB.name)]."))
|
|
BB.name = "[initial(BB.name)] (\"[label_text]\")"
|
|
else if(istype(attacking_item, /obj/item/ammo_casing))
|
|
if(attacking_item.type != src.type)
|
|
to_chat(user, SPAN_WARNING("Ammo of different types cannot stack!"))
|
|
return
|
|
if(max_stack == 1)
|
|
to_chat(user, SPAN_WARNING("\The [src] cannot be stacked!"))
|
|
return
|
|
if(!src.BB)
|
|
to_chat(user, SPAN_WARNING("That round is spent!"))
|
|
return
|
|
var/obj/item/ammo_casing/B = attacking_item
|
|
if(!B.BB)
|
|
to_chat(user, SPAN_WARNING("Your round is spent!"))
|
|
return
|
|
var/obj/item/ammo_pile/pile = new /obj/item/ammo_pile(get_turf(user), list(src, attacking_item))
|
|
user.put_in_hands(pile)
|
|
..()
|
|
|
|
/obj/item/ammo_casing/update_icon()
|
|
if(spent_icon && !BB)
|
|
icon_state = spent_icon
|
|
|
|
/obj/item/ammo_casing/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
|
|
. = ..()
|
|
if (!BB)
|
|
. += "This one is spent."
|
|
|
|
//Gun loading types
|
|
#define SINGLE_CASING 1 //The gun only accepts ammo_casings. ammo_magazines should never have this as their mag_type.
|
|
#define SPEEDLOADER 2 //Transfers casings from the mag to the gun when used.
|
|
#define MAGAZINE 4 //The magazine item itself goes inside the gun
|
|
|
|
//An item that holds casings and can be used to put them inside guns
|
|
/obj/item/ammo_magazine
|
|
name = "magazine"
|
|
desc = "A magazine for some kind of gun."
|
|
icon_state = "357"
|
|
icon = 'icons/obj/ammo.dmi'
|
|
obj_flags = OBJ_FLAG_CONDUCTABLE
|
|
slot_flags = SLOT_BELT
|
|
item_state = "box"
|
|
matter = list(DEFAULT_WALL_MATERIAL = 500)
|
|
throwforce = 5
|
|
w_class = WEIGHT_CLASS_SMALL
|
|
throw_speed = 4
|
|
throw_range = 10
|
|
|
|
var/list/stored_ammo = list()
|
|
var/mag_type = SPEEDLOADER //ammo_magazines can only be used with compatible guns. This is not a bitflag, the load_method var on guns is.
|
|
var/caliber = "357"
|
|
var/max_ammo = 7
|
|
|
|
/// Ammo type that is initially loaded
|
|
var/ammo_type = /obj/item/ammo_casing
|
|
var/initial_ammo = null
|
|
|
|
var/multiple_sprites = FALSE
|
|
//because BYOND doesn't support numbers as keys in associative lists
|
|
var/list/icon_keys = list() //keys
|
|
var/list/ammo_states = list() //values
|
|
|
|
/// sound item plays when it is inserted into a gun.
|
|
var/insert_sound = /singleton/sound_category/metal_slide_reload
|
|
/// sound item plays when it is ejected from a gun.
|
|
var/eject_sound = 'sound/weapons/magazine_eject.ogg'
|
|
|
|
/obj/item/ammo_magazine/Initialize()
|
|
. = ..()
|
|
if(multiple_sprites)
|
|
initialize_magazine_icondata(src)
|
|
|
|
if(isnull(initial_ammo))
|
|
initial_ammo = max_ammo
|
|
|
|
if(initial_ammo)
|
|
for(var/i in 1 to initial_ammo)
|
|
stored_ammo += new ammo_type(src)
|
|
update_icon()
|
|
|
|
/obj/item/ammo_magazine/Destroy()
|
|
QDEL_LIST(stored_ammo)
|
|
. = ..()
|
|
|
|
/obj/item/ammo_magazine/attackby(obj/item/attacking_item, mob/user)
|
|
if(istype(attacking_item, /obj/item/ammo_casing))
|
|
var/obj/item/ammo_casing/C = attacking_item
|
|
if(C.caliber != caliber)
|
|
to_chat(user, SPAN_WARNING("[C] does not fit into [src]."))
|
|
return
|
|
if(stored_ammo.len >= max_ammo)
|
|
to_chat(user, SPAN_WARNING("[src] is full!"))
|
|
return
|
|
user.remove_from_mob(C)
|
|
C.forceMove(src)
|
|
stored_ammo.Insert(1, C) //add to the head of the list
|
|
update_icon()
|
|
else if(istype(attacking_item, /obj/item/gun) && ishuman(user))
|
|
var/mob/living/carbon/human/H = user
|
|
if(H.check_weapon_affinity(attacking_item)) // if we have gun-kata, we can reload by attacking a magazine
|
|
attacking_item.attackby(src, user)
|
|
|
|
/obj/item/ammo_magazine/attack_self(mob/user)
|
|
if(!stored_ammo.len)
|
|
to_chat(user, SPAN_NOTICE("[src] is already empty!"))
|
|
return
|
|
to_chat(user, SPAN_NOTICE("You empty [src]."))
|
|
for(var/obj/item/ammo_casing/C in stored_ammo)
|
|
C.forceMove(user.loc)
|
|
playsound(C, /singleton/sound_category/casing_drop_sound, 50, FALSE)
|
|
C.set_dir(pick(GLOB.alldirs))
|
|
stored_ammo.Cut()
|
|
update_icon()
|
|
|
|
/obj/item/ammo_magazine/update_icon()
|
|
if(multiple_sprites)
|
|
//find the lowest key greater than or equal to stored_ammo.len
|
|
var/new_state = null
|
|
for(var/idx in 1 to icon_keys.len)
|
|
var/ammo_count = icon_keys[idx]
|
|
if (ammo_count >= stored_ammo.len)
|
|
new_state = ammo_states[idx]
|
|
break
|
|
icon_state = (new_state)? new_state : initial(icon_state)
|
|
if(!length(stored_ammo))
|
|
recyclable = TRUE
|
|
else
|
|
recyclable = FALSE
|
|
|
|
/obj/item/ammo_magazine/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
|
|
. = ..()
|
|
. += "There [(stored_ammo.len == 1)? "is" : "are"] [stored_ammo.len] round\s left!"
|
|
|
|
//magazine icon state caching (caching lists are in SSicon_cache)
|
|
|
|
/proc/initialize_magazine_icondata(var/obj/item/ammo_magazine/M)
|
|
var/list/magazine_icondata_keys = SSicon_cache.magazine_icondata_keys
|
|
var/list/magazine_icondata_states = SSicon_cache.magazine_icondata_states
|
|
|
|
var/typestr = "[M.type]"
|
|
if(!(typestr in magazine_icondata_keys) || !(typestr in magazine_icondata_states))
|
|
magazine_icondata_cache_add(M)
|
|
|
|
M.icon_keys = magazine_icondata_keys[typestr]
|
|
M.ammo_states = magazine_icondata_states[typestr]
|
|
|
|
/proc/magazine_icondata_cache_add(var/obj/item/ammo_magazine/M)
|
|
var/list/magazine_icondata_keys = SSicon_cache.magazine_icondata_keys
|
|
var/list/magazine_icondata_states = SSicon_cache.magazine_icondata_states
|
|
|
|
var/list/icon_keys = list()
|
|
var/list/ammo_states = list()
|
|
var/list/states = icon_states(M.icon)
|
|
for(var/i = 0, i <= M.max_ammo, i++)
|
|
var/ammo_state = "[M.icon_state]-[i]"
|
|
if(ammo_state in states)
|
|
icon_keys += i
|
|
ammo_states += ammo_state
|
|
|
|
magazine_icondata_keys["[M.type]"] = icon_keys
|
|
magazine_icondata_states["[M.type]"] = ammo_states
|