Files
VOREStation/code/modules/projectiles/ammunition.dm
T
Cameron Lennox d5849910e5 Begin clickcode attack_self fix (#18797)
* Begin clickcode attack_self fix

Begins the work to make everything call back to parent for attack_self so that signals are sacred.

* Makes MORE things call the attack_self() parent

Yes, I could make special_handling a var on obj/item HOWEVER i want it to be specific so it can be tracked down later and ONLY the objects that use it can be refactored instead of sitting there literally forever and it just becoming 'a thing'.

* Finishes making the rest of attack_self call parent.

As mentioned, things such as 'specialty_goggles' 'special_handling' and the such are only there to help with attack_self until the attack_self is recoded for those items.

* begone foul demon

* some more cleanup

* These

* GOD this was annoying

* yeh

* Fix this

* fLARES

* Thesee too

* toys!

* Even more!

* More fixes

* Even more

* rest of em

* these too

* Update syndie.dm

* hardref clear

* Update code/game/gamemodes/nuclear/pinpointer.dm

* Update code/game/objects/effects/mines.dm

* Update code/game/objects/items/blueprints_vr.dm

* Update code/game/objects/items/blueprints_vr.dm

* Update code/game/objects/items/contraband_vr.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/gunbox.dm

* Update code/game/objects/items/gunbox.dm

* Update code/game/objects/items/gunbox_vr.dm

* Update code/game/objects/items/gunbox_vr.dm

* Update code/game/objects/items/weapons/gift_wrappaper.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/gunbox.dm

* these too

* Update maintpanel_stack.dm

* angry warning

* Fixes packaged snacks.

Fixes improper var default.

* Special handling for these

* proper poly types

* Fixes magclaws

Makes the 'features' it had just part  of base magboots that can be adjusted via varswap.

* Fixes jackets

Fixes https://github.com/VOREStation/VOREStation/issues/18941

* Small bugfix

Makes p_Theyre properly capitialize
Makes examine show proper wording

* Update gift_wrappaper.dm
2025-12-29 13:21:10 -05:00

287 lines
9.0 KiB
Plaintext

/obj/item/ammo_casing
name = "bullet casing"
desc = "A bullet casing."
icon = 'icons/obj/ammo.dmi'
icon_state = "s-casing"
randpixel = 10
slot_flags = SLOT_BELT | SLOT_EARS
throwforce = 1
w_class = ITEMSIZE_TINY
preserve_item = 1
drop_sound = 'sound/items/drop/ring.ogg'
pickup_sound = 'sound/items/pickup/ring.ogg'
var/leaves_residue = 1
var/caliber = "" //Which kind of guns it can be loaded into
var/projectile_type //The bullet type to create when New() is called
var/obj/item/projectile/BB = null //The loaded bullet - make it so that the projectiles are created only when needed?
var/caseless = null //Caseless ammo deletes its self once the projectile is fired.
/obj/item/ammo_casing/Initialize(mapload)
. = ..()
if(ispath(projectile_type))
BB = new projectile_type(src)
randpixel_xy()
/obj/item/ammo_casing/Destroy()
QDEL_NULL(BB)
return ..()
//removes the projectile from the ammo casing
/obj/item/ammo_casing/proc/expend()
. = BB
BB = null
set_dir(pick(GLOB.cardinal)) //spin spent casings
update_icon()
/obj/item/ammo_casing/attackby(obj/item/I as obj, mob/user as mob)
if(I.has_tool_quality(TOOL_SCREWDRIVER))
if(!BB)
to_chat(user, span_blue("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, encode = FALSE), MAX_NAME_LEN)
if(length(label_text) > 20)
to_chat(user, span_red("The inscription can be at most 20 characters long."))
else if(!label_text)
to_chat(user, span_blue("You scratch the inscription off of [initial(BB)]."))
BB.name = initial(BB.name)
else
to_chat(user, span_blue("You inscribe \"[label_text]\" into \the [initial(BB.name)]."))
BB.name = "[initial(BB.name)] (\"[label_text]\")"
else if(istype(I, /obj/item/ammo_magazine) && isturf(loc)) // Mass magazine reloading.
var/obj/item/ammo_magazine/box = I
if (!box.can_remove_ammo || box.reloading)
return ..()
box.reloading = TRUE
var/boolets = 0
var/turf/floor = loc
for(var/obj/item/ammo_casing/bullet in floor)
if(box.stored_ammo.len >= box.max_ammo)
break
if(box.caliber == bullet.caliber && bullet.BB)
if (boolets < 1)
to_chat(user, span_notice("You start collecting shells.")) // Say it here so it doesn't get said if we don't find anything useful.
if(do_after(user, 5, target = box))
if(box.stored_ammo.len >= box.max_ammo) // Double check because these can change during the wait.
break
if(bullet.loc != floor)
continue
bullet.forceMove(box)
box.stored_ammo.Add(bullet)
box.update_icon()
boolets++
else
break
if(boolets > 0)
to_chat(user, span_notice("You collect [boolets] shell\s. [box] now contains [box.stored_ammo.len] shell\s."))
else
to_chat(user, span_warning("You fail to collect anything!"))
box.reloading = FALSE
else
return ..()
/obj/item/ammo_casing/update_icon()
if(!BB)
icon_state = "[initial(icon_state)]-spent"
/obj/item/ammo_casing/examine(mob/user)
. = ..()
if (!BB)
. += "This one is spent."
//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'
slot_flags = SLOT_BELT
item_state = "syringe_kit"
matter = list(MAT_STEEL = 500)
throwforce = 5
w_class = ITEMSIZE_SMALL
throw_speed = 4
throw_range = 10
preserve_item = 1
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
var/ammo_type = /obj/item/ammo_casing //ammo type that is initially loaded
var/initial_ammo = null
var/can_remove_ammo = TRUE // Can this thing have bullets removed one-by-one? As of first implementation, only affects smart magazines
var/reloading = FALSE // Is this magazine being reloaded, currently? - Currently only useful for automatic pickups, ignored by manual reloading.
var/multiple_sprites = 0
//because BYOND doesn't support numbers as keys in associative lists
var/list/icon_keys = list() //keys
var/list/ammo_states = list() //values
/obj/item/ammo_magazine/Initialize(mapload)
. = ..()
pixel_x = rand(-5, 5)
pixel_y = rand(-5, 5)
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/attackby(obj/item/W as obj, mob/user as mob)
if(istype(W, /obj/item/ammo_casing))
var/obj/item/ammo_casing/C = W
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.Add(C)
update_icon()
if(istype(W, /obj/item/ammo_magazine/clip))
var/obj/item/ammo_magazine/clip/L = W
if(L.caliber != caliber)
to_chat(user, span_warning("The ammo in [L] does not fit into [src]."))
return
if(!L.stored_ammo.len)
to_chat(user, span_warning("There's no more ammo [L]!"))
return
if(stored_ammo.len >= max_ammo)
to_chat(user, span_warning("[src] is full!"))
return
var/obj/item/ammo_casing/AC = L.stored_ammo[1] //select the next casing.
L.stored_ammo -= AC //Remove this casing from loaded list of the clip.
AC.forceMove(src)
stored_ammo.Insert(1, AC) //add it to the head of our magazine's list
L.update_icon()
playsound(src, 'sound/weapons/flipblade.ogg', 50, 1)
update_icon()
// This dumps all the bullets right on the floor
/obj/item/ammo_magazine/attack_self(mob/user)
. = ..(user)
if(.)
return TRUE
if(can_remove_ammo)
if(!stored_ammo.len)
to_chat(user, span_notice("[src] is already empty!"))
return
to_chat(user, span_notice("You empty [src]."))
playsound(src, "casing_sound", 50, 1)
spawn(7)
playsound(src, "casing_sound", 50, 1)
spawn(10)
playsound(src, "casing_sound", 50, 1)
for(var/obj/item/ammo_casing/C in stored_ammo)
C.loc = user.loc
C.set_dir(pick(GLOB.cardinal))
stored_ammo.Cut()
update_icon()
else
to_chat(user, span_notice("\The [src] is not designed to be unloaded."))
return
// This puts one bullet from the magazine into your hand
/obj/item/ammo_magazine/attack_hand(mob/user)
if(can_remove_ammo) // For Smart Magazines
if(user.get_inactive_hand() == src)
if(stored_ammo.len)
var/obj/item/ammo_casing/C = stored_ammo[stored_ammo.len]
stored_ammo-=C
user.put_in_hands(C)
user.visible_message("\The [user] removes \a [C] from [src].", span_notice("You remove \a [C] from [src]."))
update_icon()
return
..()
/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)
/obj/item/ammo_magazine/examine(mob/user)
. = ..()
. += "There [(stored_ammo.len == 1)? "is" : "are"] [stored_ammo.len] round\s left!"
//magazine icon state caching
GLOBAL_LIST_EMPTY(magazine_icondata_keys)
GLOBAL_LIST_EMPTY(magazine_icondata_states)
/proc/initialize_magazine_icondata(var/obj/item/ammo_magazine/M)
var/typestr = M.type
if(!(typestr in GLOB.magazine_icondata_keys) || !(typestr in GLOB.magazine_icondata_states))
magazine_icondata_cache_add(M)
M.icon_keys = GLOB.magazine_icondata_keys[typestr]
M.ammo_states = GLOB.magazine_icondata_states[typestr]
/proc/magazine_icondata_cache_add(var/obj/item/ammo_magazine/M)
var/list/icon_keys = list()
var/list/ammo_states = list()
var/list/states = icon_states_fast(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
GLOB.magazine_icondata_keys[M.type] = icon_keys
GLOB.magazine_icondata_states[M.type] = ammo_states
/*
* Ammo Boxes
*/
/obj/item/ammo_magazine/ammo_box
name = "ammo box"
desc = "A box that holds some kind of ammo."
icon = 'icons/obj/ammo_boxes.dmi'
icon_state = "pistol"
slot_flags = null //You can't fit a box on your belt
item_state = "paper"
matter = null
throwforce = 3
throw_speed = 5
throw_range = 12
preserve_item = 1
caliber = ".357"
drop_sound = 'sound/items/drop/matchbox.ogg'
pickup_sound = 'sound/items/pickup/matchbox.ogg'
/obj/item/ammo_magazine/ammo_box/click_alt(mob/user)
if(can_remove_ammo)
if(isliving(user) && Adjacent(user))
if(stored_ammo.len)
var/obj/item/ammo_casing/C = stored_ammo[stored_ammo.len]
stored_ammo-=C
user.put_in_hands(C)
user.visible_message("\The [user] removes \a [C] from [src].", span_notice("You remove \a [C] from [src]."))
update_icon()
return
..()
/obj/item/ammo_magazine/ammo_box/examine(mob/user)
. = ..()
. += to_chat(user, span_notice("Alt-click to extract contents."))