Files
GhomandGitHub 6eb2d1674a Implementing materials checks for techweb designs. (#96257)
## About The Pull Request
In similar fashion to what was done with crafting recipes last year,
this year it's time for techweb designs and printed items to be audited.
This is mostly just about consistency, we've a lot of items that can be
printed by protolathes, autolathes, circuit printers and techfab etc.
However they almost all (except most stacks, mainly) have custom
materials that do not match in one way or another with the materials
used by the design, which is what this PR is for.

"But items printed from lathes etc. already get the mats used to make
them." Yes, they do, however that isn't the case for items of the same
type that were spawned in some other way (cargo shuttle, space/maints
loot, mapped, admins), this create a subtle discrepancy. It isn't a huge
deal (in spite of the size of this PR, ton of designs), but given that I
have done something similar with crafting recipes before, I may as well
give it a second arc of some sort and bring things to completion. And
fix a few possible oversights.

TL;DR consistency and stuff

## Why It's Good For The Game
Consistency, unit test checks to make it harder not to be consistent in
the future. Still has a few TODOs like:

- [x] Fixed newly printed, fully charged RCDs costing less than the RCD
cartridges required to fully charge one. EDIT: I had to tweak the newly
added RDD as well because it suffered from the same fundamental issue.
- [x] Fixed plates being made of iron and yet shattering like ceramic
ones. A new subtype for metallic ones has been made.

## Changelog

🆑
refactor: Refactored a few things with techweb designs (the ones for
autolathes, protolathes, circuit printers, mechfabs etc.) to make sure
that the materials of items that can be made from these designs more
closely match the materials used to make them.
fix: Lizard fries no longer need a plate to be made, like all other
treats that used to require plates in a distant past.
balance: Tweaked the materials cost of RCD, RDD and RCD cartridges.
image: Oven trays now have a more metallic hue.
balance: Plates printed printed from lathes won't shatter like ceramic
ones, in virtue of them being made out of iron instead.
/🆑
2026-07-06 00:13:02 -07:00

625 lines
21 KiB
Plaintext

#define ORE_BAG_BALOON_COOLDOWN (2 SECONDS)
/*
* These absorb the functionality of the plant bag, ore satchel, etc.
* They use the use_to_pickup, quick_gather, and quick_empty functions
* that were already defined in weapon/storage, but which had been
* re-implemented in other classes.
*
* Contains:
* Trash Bag
* Mining Satchel
* Plant Bag
* Sheet Snatcher
* Book Bag
* Biowaste Bag
*
* -Sayu
*/
// Generic non-item
/obj/item/storage/bag
abstract_type = /obj/item/storage/bag
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_BULKY
storage_type = /datum/storage/bag
/obj/item/storage/bag/trash
name = "trash bag"
desc = "It's the heavy-duty black polymer kind. Time to take out the trash!"
icon = 'icons/obj/service/janitor.dmi'
icon_state = "trashbag"
inhand_icon_state = "trashbag"
worn_icon_state = "trashbag"
lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
storage_type = /datum/storage/bag/trash
custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT)
///If true, can be inserted into the janitor cart
var/insertable = TRUE
/obj/item/storage/bag/trash/Initialize(mapload)
. = ..()
RegisterSignal(atom_storage, COMSIG_STORAGE_DUMP_POST_TRANSFER, PROC_REF(post_insertion))
/// If you dump a trash bag into something, anything that doesn't get inserted will spill out onto your feet
/obj/item/storage/bag/trash/proc/post_insertion(datum/storage/source, atom/dest_object, mob/user)
SIGNAL_HANDLER
// If there's no item in there, don't do anything
if(!(locate(/obj/item) in src))
return
// Otherwise, we're gonna dump into the dest object
var/turf/dump_onto = get_turf(dest_object)
user.visible_message(
span_notice("[user] dumps the contents of [src] all out on \the [dump_onto]"),
span_notice("The remaining trash in \the [src] falls out onto \the [dump_onto]"),
)
source.remove_all(dump_onto)
/obj/item/storage/bag/trash/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] puts [src] over [user.p_their()] head and starts chomping at the insides! Disgusting!"))
playsound(loc, 'sound/items/eatfood.ogg', 50, TRUE, -1)
return TOXLOSS
/obj/item/storage/bag/trash/update_icon_state()
switch(contents.len)
if(20 to INFINITY)
icon_state = "[initial(icon_state)]3"
if(11 to 20)
icon_state = "[initial(icon_state)]2"
if(1 to 11)
icon_state = "[initial(icon_state)]1"
else
icon_state = "[initial(icon_state)]"
return ..()
/obj/item/storage/bag/trash/filled/PopulateContents()
. = ..()
for(var/i in 1 to rand(1, 7))
new /obj/effect/spawner/random/trash/garbage(src)
update_icon_state()
/obj/item/storage/bag/trash/bluespace
name = "trash bag of holding"
desc = "The latest and greatest in custodial convenience, a trashbag that is capable of holding vast quantities of garbage."
icon_state = "bluetrashbag"
worn_icon_state = "bluetrashbag"
inhand_icon_state = "bluetrashbag"
item_flags = NO_MAT_REDEMPTION
storage_type = /datum/storage/bag/trash/bluespace
custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT * 0.75, /datum/material/plasma = SHEET_MATERIAL_AMOUNT * 0.75, /datum/material/uranium = SMALL_MATERIAL_AMOUNT * 2.5)
/obj/item/storage/bag/trash/bluespace/cyborg
insertable = FALSE
/obj/item/storage/bag/ore
name = "mining satchel"
desc = "This little bugger can be used to store and transport ores."
icon = 'icons/obj/mining.dmi'
icon_state = "satchel"
worn_icon_state = "satchel"
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKETS
w_class = WEIGHT_CLASS_NORMAL
storage_type = /datum/storage/bag/ore
/// If this is TRUE, the holder won't receive any messages when they fail to pick up ore through crossing it
var/spam_protection = FALSE
/// Mob we're currently tracking
var/mob/listening_to = null
/// Are we currently dropping off ores? Used to prevent the bag from instantly picking up ores after dropping them
var/dropping_ores = FALSE
/// Cooldown on balloon alerts when picking ore
COOLDOWN_DECLARE(ore_bag_balloon_cooldown)
/obj/item/storage/bag/ore/Destroy(force)
listening_to = null
return ..()
/obj/item/storage/bag/ore/equipped(mob/user)
. = ..()
if (listening_to)
return
RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_user_moved))
if (isturf(user.loc))
RegisterSignal(user.loc, COMSIG_ATOM_ENTERED, PROC_REF(on_obj_entered))
RegisterSignal(user.loc, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(on_atom_initialized_on))
listening_to = user
/obj/item/storage/bag/ore/dropped()
. = ..()
if(!listening_to)
return
UnregisterSignal(listening_to, COMSIG_MOVABLE_MOVED)
if (listening_to.loc)
UnregisterSignal(listening_to.loc, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON))
listening_to = null
// Ensure we don't suck up ores that we've just dropped off
/obj/item/storage/bag/ore/attack_self(mob/user, modifiers)
dropping_ores = TRUE
. = ..()
dropping_ores = FALSE
/obj/item/storage/bag/ore/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(istype(tool, /obj/item/boulder))
to_chat(user, span_warning("You can't fit [tool] into [src]. \
Perhaps you should break it down first, or find an ore box."))
return ITEM_INTERACT_BLOCKING
return NONE
/obj/item/storage/bag/ore/proc/on_user_moved(mob/living/user, atom/old_loc, dir, forced)
SIGNAL_HANDLER
if(old_loc)
UnregisterSignal(old_loc, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON))
var/turf/tile = get_turf(user)
if(!isturf(tile))
return
RegisterSignal(tile, COMSIG_ATOM_ENTERED, PROC_REF(on_obj_entered))
RegisterSignal(tile, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(on_atom_initialized_on))
INVOKE_ASYNC(src, PROC_REF(handle_move), user)
/obj/item/storage/bag/ore/proc/handle_move(mob/living/user)
if(user.stat != CONSCIOUS)
return
var/turf/tile = get_turf(user)
var/obj/structure/ore_box/box = null
if(istype(user.pulling, /obj/structure/ore_box))
box = user.pulling
var/show_message = FALSE
for(var/atom/thing as anything in tile)
if(!is_type_in_typecache(thing, atom_storage.can_hold))
continue
if(pickup_ore(thing, user, box))
show_message = TRUE
if (!show_message)
spam_protection = FALSE
return
playsound(user, SFX_RUSTLE, 50, TRUE)
if(!COOLDOWN_FINISHED(src, ore_bag_balloon_cooldown))
return
spam_protection = FALSE
COOLDOWN_START(src, ore_bag_balloon_cooldown, ORE_BAG_BALOON_COOLDOWN)
if (box)
balloon_alert(user, "scoops ore into box")
user.visible_message(
span_notice("[user] offloads the ores beneath [user.p_them()] into [box]."),
ignored_mobs = user
)
return
balloon_alert(user, "scoops ore into bag")
user.visible_message(
span_notice("[user] scoops up the ores beneath [user.p_them()]."),
ignored_mobs = user
)
/obj/item/storage/bag/ore/proc/pickup_ore(obj/item/ore, mob/user, obj/structure/ore_box/box)
if (!box && istype(user.pulling, /obj/structure/ore_box))
box = user.pulling
if (box)
user.transferItemToLoc(ore, box, animated = FALSE)
return TRUE
if (istype(ore, /obj/item/stack/ore))
var/obj/item/stack/ore/real_ore = ore
for(var/obj/item/stack/ore/stored_ore as anything in src)
if(!real_ore.can_merge(stored_ore))
continue
real_ore.merge(stored_ore)
if(QDELETED(real_ore))
return TRUE
if (atom_storage.attempt_insert(ore, user))
return TRUE
if (!spam_protection)
balloon_alert(user, "bag full!")
spam_protection = TRUE
return FALSE
/obj/item/storage/bag/ore/proc/on_obj_entered(atom/new_loc, atom/movable/arrived, atom/old_loc)
SIGNAL_HANDLER
if(is_type_in_list(arrived, atom_storage.can_hold) && !dropping_ores && old_loc != loc)
INVOKE_ASYNC(src, PROC_REF(pickup_ore), arrived, listening_to)
/obj/item/storage/bag/ore/proc/on_atom_initialized_on(atom/loc, atom/new_atom)
SIGNAL_HANDLER
if(is_type_in_list(new_atom, atom_storage.can_hold))
INVOKE_ASYNC(src, PROC_REF(pickup_ore), new_atom, listening_to)
/obj/item/storage/bag/ore/cyborg
name = "cyborg mining satchel"
/obj/item/storage/bag/ore/holding //miners, your messiah has arrived
name = "mining satchel of holding"
desc = "A revolution in convenience, this satchel allows for huge amounts of ore storage. It's been outfitted with anti-malfunction safety measures."
icon_state = "satchel_bspace"
storage_type = /datum/storage/bag/ore/holding
custom_materials = list(/datum/material/uranium = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/gold = SMALL_MATERIAL_AMOUNT * 2.5)
/obj/item/storage/bag/plants
name = "plant bag"
icon = 'icons/obj/service/hydroponics/equipment.dmi'
icon_state = "plantbag"
worn_icon_state = "plantbag"
resistance_flags = FLAMMABLE
storage_type = /datum/storage/bag/plants
/obj/item/storage/bag/plants/portaseeder
name = "portable seed extractor"
desc = "For the enterprising botanist on the go. Less efficient than the stationary model, it creates one seed per plant."
icon_state = "portaseeder"
custom_materials = list(/datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 4)
/obj/item/storage/bag/plants/portaseeder/Initialize(mapload)
. = ..()
register_context()
/obj/item/storage/bag/plants/portaseeder/add_context(
atom/source,
list/context,
obj/item/held_item,
mob/living/user
)
context[SCREENTIP_CONTEXT_CTRL_LMB] = "Make seeds"
return CONTEXTUAL_SCREENTIP_SET
/obj/item/storage/bag/plants/portaseeder/examine(mob/user)
. = ..()
. += span_notice("Ctrl-click to activate seed extraction.")
/obj/item/storage/bag/plants/portaseeder/item_ctrl_click(mob/user)
for(var/obj/item/plant in contents)
seedify(plant, 1)
return CLICK_ACTION_SUCCESS
/obj/item/storage/bag/plants/cyborg
name = "cyborg plant bag"
/obj/item/storage/bag/sheetsnatcher
name = "sheet snatcher"
desc = "A patented Nanotrasen storage system designed for any kind of mineral sheet."
icon = 'icons/obj/mining.dmi'
icon_state = "sheetsnatcher"
worn_icon_state = "satchel"
storage_type = /datum/storage/bag/sheet_snatcher
/obj/item/storage/bag/sheetsnatcher/borg
name = "sheet snatcher 9000"
desc = ""
storage_type = /datum/storage/bag/sheet_snatcher/borg
/obj/item/storage/bag/sheetsnatcher/debug
name = "sheet snatcher EXTREME EDITION"
desc = "A Nanotrasen storage system designed which has been given post-market alterations to hold any type of sheet. Comes pre-populated with "
color = "#ff3737" // I'm too lazy to make a unique sprite
w_class = WEIGHT_CLASS_TINY
storage_type = /datum/storage/bag/sheet_snatcher_debug
// Copy-pasted from the former /obj/item/storage/box/material, w/ small additions like rods, cardboard, plastic.
// "Only 20 uranium 'cause of radiation"
/obj/item/storage/bag/sheetsnatcher/debug/PopulateContents()
// amount should be null if it should spawn with the type's default amount
var/list/items_inside = list(
/obj/item/stack/sheet/iron/fifty = null,
/obj/item/stack/sheet/glass/fifty = null,
/obj/item/stack/sheet/rglass/fifty = null,
/obj/item/stack/sheet/plasmaglass/fifty = null,
/obj/item/stack/sheet/titaniumglass/fifty = null,
/obj/item/stack/sheet/plastitaniumglass/fifty = null,
/obj/item/stack/sheet/plasteel/fifty = null,
/obj/item/stack/sheet/mineral/titanium/fifty = null,
/obj/item/stack/sheet/mineral/gold = 50,
/obj/item/stack/sheet/mineral/silver = 50,
/obj/item/stack/sheet/mineral/plasma = 50,
/obj/item/stack/sheet/mineral/uranium = 20,
/obj/item/stack/sheet/mineral/diamond = 50,
/obj/item/stack/sheet/bluespace_crystal = 50,
/obj/item/stack/sheet/mineral/bananium = 50,
/obj/item/stack/sheet/mineral/wood/fifty = null,
/obj/item/stack/sheet/plastic/fifty = null,
/obj/item/stack/sheet/runed_metal/fifty = null,
/obj/item/stack/rods/fifty = null,
/obj/item/stack/sheet/mineral/plastitanium = 50,
/obj/item/stack/sheet/mineral/abductor = 50,
/obj/item/stack/sheet/mineral/sandbags/fifty = null,
/obj/item/stack/sheet/cardboard/fifty = null,
)
for(var/obj/item/stack/stack_type as anything in items_inside)
var/amt = items_inside[stack_type]
new stack_type(src, amt, FALSE)
/obj/item/storage/bag/books
name = "book bag"
desc = "A bag for books."
icon = 'icons/obj/service/library.dmi'
icon_state = "bookbag"
worn_icon_state = "bookbag"
resistance_flags = FLAMMABLE
storage_type = /datum/storage/bag/books
/obj/item/storage/bag/tray
name = "serving tray"
icon = 'icons/obj/food/containers.dmi'
icon_state = "tray"
worn_icon_state = "tray"
desc = "A metal tray to lay food on."
force = 5
throwforce = 10
throw_speed = 3
throw_range = 5
obj_flags = CONDUCTS_ELECTRICITY
slot_flags = ITEM_SLOT_BELT
custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT*1.5)
custom_price = PAYCHECK_CREW * 0.6
storage_type = /datum/storage/bag/tray
sound_vary = TRUE
pickup_sound = SFX_TRAY_PICKUP
drop_sound = SFX_TRAY_DROP
/obj/item/storage/bag/tray/attack(mob/living/M, mob/living/user)
. = ..()
// Drop all the things. All of them.
var/list/obj/item/oldContents = contents.Copy()
atom_storage.remove_all(user)
// Make each item scatter a bit
for(var/obj/item/tray_item in oldContents)
do_scatter(tray_item)
if(prob(50))
playsound(M, 'sound/items/trayhit/trayhit1.ogg', 50, TRUE)
else
playsound(M, 'sound/items/trayhit/trayhit2.ogg', 50, TRUE)
if(ishuman(M))
if(prob(10))
M.Paralyze(40)
update_appearance()
/obj/item/storage/bag/tray/proc/do_scatter(obj/item/tray_item)
var/delay = rand(2,4)
var/datum/move_loop/loop = GLOB.move_manager.move_rand(tray_item, list(NORTH,SOUTH,EAST,WEST), delay, timeout = rand(1, 2) * delay, flags = MOVEMENT_LOOP_START_FAST)
//This does mean scattering is tied to the tray. Not sure how better to handle it
RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(change_speed))
/obj/item/storage/bag/tray/proc/change_speed(datum/move_loop/source)
SIGNAL_HANDLER
var/new_delay = rand(2, 4)
var/count = source.lifetime / source.delay
source.lifetime = count * new_delay
source.delay = new_delay
/obj/item/storage/bag/tray/update_overlays()
. = ..()
for(var/obj/item/I in contents)
var/mutable_appearance/I_copy = new(I)
I_copy.plane = FLOAT_PLANE
I_copy.layer = FLOAT_LAYER
. += I_copy
/obj/item/storage/bag/tray/cyborg_unequip(mob/user)
. = ..()
atom_storage.remove_all(drop_location())
/obj/item/storage/bag/tray/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
update_appearance()
/obj/item/storage/bag/tray/Exited(atom/movable/gone, direction)
. = ..()
update_appearance()
/obj/item/storage/bag/tray/cafeteria
name = "cafeteria tray"
icon = 'icons/obj/food/containers.dmi'
icon_state = "foodtray"
desc = "A cheap metal tray to pile today's meal onto."
/obj/item/storage/bag/chemistry
name = "chemistry bag"
icon = 'icons/obj/medical/chemical.dmi'
icon_state = "bag"
worn_icon_state = "chembag"
desc = "A bag for storing pills, patches, and bottles."
resistance_flags = FLAMMABLE
storage_type = /datum/storage/bag/chemistry
/obj/item/storage/bag/money
name = "money bag"
desc = "A bag for storing your profits."
icon_state = "moneybag"
worn_icon_state = "moneybag"
force = 10
throwforce = 0
resistance_flags = FLAMMABLE
max_integrity = 100
w_class = WEIGHT_CLASS_BULKY
storage_type = /datum/storage/bag/money
/obj/item/storage/bag/money/Initialize(mapload)
. = ..()
if(prob(20))
icon_state = "moneybagalt"
/obj/item/storage/bag/money/vault/PopulateContents()
new /obj/item/coin/silver(src)
new /obj/item/coin/silver(src)
new /obj/item/coin/silver(src)
new /obj/item/coin/silver(src)
new /obj/item/coin/gold(src)
new /obj/item/coin/gold(src)
new /obj/item/coin/adamantine(src)
///Used in the dutchmen pirate shuttle.
/obj/item/storage/bag/money/dutchmen/PopulateContents()
for(var/iteration in 1 to 9)
new /obj/item/coin/silver/doubloon(src)
for(var/iteration in 1 to 9)
new /obj/item/coin/gold/doubloon(src)
new /obj/item/coin/adamantine/doubloon(src)
/obj/item/storage/bag/bio
name = "bio bag"
icon = 'icons/obj/medical/chemical.dmi'
icon_state = "biobag"
worn_icon_state = "biobag"
desc = "A bag for the safe transportation and disposal of biowaste and other virulent materials."
resistance_flags = FLAMMABLE
storage_type = /datum/storage/bag/bio
/obj/item/storage/bag/xeno
name = "science bag"
icon = 'icons/obj/medical/chemical.dmi'
icon_state = "xenobag"
worn_icon_state = "xenobag"
desc = "A bag for the storage and transport of anomalous materials."
resistance_flags = FLAMMABLE
storage_type = /datum/storage/bag/xeno
/obj/item/storage/bag/construction
name = "construction bag"
icon = 'icons/obj/tools.dmi'
icon_state = "construction_bag"
worn_icon_state = "construction_bag"
desc = "A bag for storing small construction components."
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKETS
resistance_flags = FLAMMABLE
storage_type = /datum/storage/bag/construction
/obj/item/storage/bag/harpoon_quiver
name = "harpoon quiver"
desc = "A quiver for holding harpoons."
icon = 'icons/obj/weapons/bows/quivers.dmi'
icon_state = "quiver"
inhand_icon_state = null
worn_icon_state = "harpoon_quiver"
storage_type = /datum/storage/bag/harpoon_quiver
/obj/item/storage/bag/harpoon_quiver/PopulateContents()
for(var/i in 1 to 40)
new /obj/item/ammo_casing/harpoon(src)
/obj/item/storage/bag/rebar_quiver
name = "rebar quiver"
icon = 'icons/obj/weapons/bows/quivers.dmi'
icon_state = "rebar_quiver"
worn_icon_state = "rebar_quiver"
inhand_icon_state = "rebar_quiver"
desc = "A oxygen tank cut in half, used for holding sharpened rods for the rebar crossbow."
slot_flags = ITEM_SLOT_BACK|ITEM_SLOT_SUITSTORE|ITEM_SLOT_NECK
resistance_flags = FLAMMABLE
storage_type = /datum/storage/bag/rebar_quiver
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 1.15, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 1.5)
actions_types = list(/datum/action/item_action/reload_rebar)
/obj/item/storage/bag/rebar_quiver/Initialize(mapload)
. = ..()
update_appearance(UPDATE_OVERLAYS)
/obj/item/storage/bag/rebar_quiver/ui_action_click(mob/user, actiontype)
if(istype(actiontype, /datum/action/item_action/reload_rebar))
reload_held_rebar(user)
/obj/item/storage/bag/rebar_quiver/proc/reload_held_rebar(mob/user)
if(!contents.len)
user.balloon_alert(user, "no bolts left!")
return
var/obj/held_item = user.get_active_held_item()
if(!held_item || !istype(held_item, /obj/item/gun/ballistic/rifle/rebarxbow))
user.balloon_alert(user, "no held crossbow!")
return
var/obj/item/gun/ballistic/rifle/rebarxbow/held_crossbow = held_item
if(held_crossbow.magazine.contents.len >= held_crossbow.magazine.max_ammo)
user.balloon_alert(user, "no more room!")
return
if(!do_after(user, held_crossbow.reload_time, user,timed_action_flags = held_crossbow.doafter_flags))
return
var/obj/item/ammo_casing/rebar/ammo_to_load = contents[1]
held_crossbow.item_interaction(user, ammo_to_load)
/obj/item/storage/bag/rebar_quiver/syndicate
icon_state = "syndie_quiver_0"
worn_icon_state = "syndie_quiver_0"
inhand_icon_state = "holyquiver"
desc = "A specialized quiver meant to hold any kind of bolts intended for use with the rebar crossbow. Allows moving while reloading.\
Clearly a better design than a cut up oxygen tank..."
slot_flags = ITEM_SLOT_NECK
w_class = WEIGHT_CLASS_NORMAL
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
actions_types = list(/datum/action/item_action/reload_rebar)
storage_type = /datum/storage/bag/rebar_quiver/syndicate
/obj/item/storage/bag/rebar_quiver/syndicate/PopulateContents()
for(var/to_fill in 1 to 20)
new /obj/item/ammo_casing/rebar/syndie(src)
/obj/item/storage/bag/rebar_quiver/syndicate/update_icon_state()
. = ..()
switch(contents.len)
if(0)
icon_state = "syndie_quiver_0"
if(1 to 7)
icon_state = "syndie_quiver_1"
if(8 to 13)
icon_state = "syndie_quiver_2"
if(14 to 20)
icon_state = "syndie_quiver_3"
/obj/item/storage/bag/quiver
name = "quiver"
desc = "Holds arrows for your bow. Good, because while pocketing arrows is possible, it surely can't be pleasant."
icon = 'icons/obj/weapons/bows/quivers.dmi'
icon_state = "quiver"
inhand_icon_state = null
worn_icon_state = "harpoon_quiver"
storage_type = /datum/storage/bag/quiver
/// type of arrow the quivel should hold
var/arrow_path = /obj/item/ammo_casing/arrow
/obj/item/storage/bag/quiver/lesser
storage_type = /datum/storage/bag/quiver/less
custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT)
/obj/item/storage/bag/quiver/full/PopulateContents()
. = ..()
for(var/i in 1 to 10)
new arrow_path(src)
/obj/item/storage/bag/quiver/holy
name = "divine quiver"
desc = "Holds arrows for your divine bow, where they wait to find their target."
icon_state = "holyquiver"
inhand_icon_state = "holyquiver"
worn_icon_state = "holyquiver"
arrow_path = /obj/item/ammo_casing/arrow/holy
/obj/item/storage/bag/quiver/holy/PopulateContents()
. = ..()
for(var/i in 1 to 10)
new arrow_path(src)
/obj/item/storage/bag/quiver/endless
name = "endless quiver"
desc = "Holds arrows for your bow. A deep digital void is contained within."
storage_type = /datum/storage/bag/quiver/endless
/obj/item/storage/bag/quiver/endless/PopulateContents()
. = ..()
new arrow_path(src)
#undef ORE_BAG_BALOON_COOLDOWN