Files
CRUNCH da1ebd039d Adds Materials to Every Autolathe and Protolathe Item (except for ammo boxes, IK mags, and Dart Boxes) (#31445)
* Holy fuck there's so much shit I'm not even done

* oh god there's so many

* it is done

* Update autolathe_designs.dm

* Update circuitboard.dm
2026-01-30 21:25:40 +00:00

584 lines
20 KiB
Plaintext

/*
* 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.
*
* CONTENTS:
* Trash bag
* Mining satchel
* Plant bag
* Cash bag
* Book bag
* Serving tray
* Chemistry bag
* Bio bag
* Mail bag
* Construction bag
* Treasure bag
*/
// Generic non-item
/obj/item/storage/bag
allow_quick_gather = TRUE
allow_quick_empty = TRUE
display_contents_with_number = 1 // should work fine now
use_to_pickup = 1
slot_flags = ITEM_SLOT_BELT
////////////////////////////////////////
// MARK: Trash 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/janitor.dmi'
icon_state = "trashbag"
belt_icon = "trashbag"
lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
slot_flags = null
storage_slots = 30
max_combined_w_class = 30
can_hold = list() // any
cant_hold = list(/obj/item/disk/nuclear, /obj/item/grown/bananapeel/traitorpeel)
/obj/item/storage/bag/trash/proc/update_weight()
if(!length(contents))
w_class = WEIGHT_CLASS_SMALL
return
w_class = WEIGHT_CLASS_BULKY
/obj/item/storage/bag/trash/remove_from_storage(obj/item/I, atom/new_location)
. = ..()
update_weight()
/obj/item/storage/bag/trash/can_be_inserted(obj/item/I, stop_messages = FALSE)
if(isstorage(loc) && !istype(loc, /obj/item/storage/backpack/holding))
to_chat(usr, SPAN_WARNING("You can't seem to fit [I] into [src]."))
return FALSE
if(ishuman(loc)) // If the trashbag is on a humanoid, they can't store things in it while it's in their pockets
var/mob/living/carbon/human/H = loc
if(H.l_store == src || H.r_store == src)
to_chat(usr, SPAN_WARNING("You can't seem to fit [I] into [src]."))
return FALSE
. = ..()
/obj/item/storage/bag/trash/Initialize(mapload)
. = ..()
update_weight()
/obj/item/storage/bag/trash/handle_item_insertion(obj/item/I, mob/user, prevent_warning)
. = ..()
update_weight()
/obj/item/storage/bag/trash/suicide_act(mob/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(length(contents))
if(21 to INFINITY)
icon_state = "[initial(icon_state)]3"
if(11 to 20)
icon_state = "[initial(icon_state)]2"
if(1 to 10)
icon_state = "[initial(icon_state)]1"
else
icon_state = "[initial(icon_state)]"
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.update_inv_l_hand()
H.update_inv_r_hand()
/obj/item/storage/bag/trash/cyborg
/obj/item/storage/bag/trash/bluespace
name = "trash bag of holding"
desc = "An advanced trash bag that uses experimental bluespace technology to send stored trash to a specialized pocket dimension."
icon_state = "bluetrashbag"
belt_icon = "trashbag_blue"
origin_tech = "materials=4;bluespace=4;engineering=4;plasmatech=3"
max_combined_w_class = 60
storage_slots = 60
flags_2 = NO_MAT_REDEMPTION_2
/obj/item/storage/bag/trash/bluespace/cyborg
////////////////////////////////////////
// MARK: Plastic bag
////////////////////////////////////////
/obj/item/storage/bag/plasticbag
name = "plastic bag"
desc = "It's a very flimsy, very noisy alternative to a bag."
icon = 'icons/obj/trash.dmi'
icon_state = "plasticbag"
slot_flags = ITEM_SLOT_HEAD|ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_BULKY
display_contents_with_number = 0 //or else this will lead to stupid behavior.
can_hold = list() // any
cant_hold = list(/obj/item/disk/nuclear)
materials = list(MAT_PLASTIC = 6000)
/obj/item/storage/bag/plasticbag/mob_can_equip(mob/M, slot, disable_warning = FALSE)
if(slot == ITEM_SLOT_HEAD && length(contents))
to_chat(M, SPAN_WARNING("You need to empty the bag first!"))
return FALSE
return ..()
/obj/item/storage/bag/plasticbag/equipped(mob/user, slot)
if(slot==ITEM_SLOT_HEAD)
storage_slots = 0
START_PROCESSING(SSobj, src)
return
/obj/item/storage/bag/plasticbag/process()
if(is_equipped())
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
if(H.get_item_by_slot(ITEM_SLOT_HEAD) == src)
if(H.internal)
return
H.AdjustLoseBreath(2 SECONDS)
else
storage_slots = 7
STOP_PROCESSING(SSobj, src)
return
////////////////////////////////////////
// MARK: Mining satchel
////////////////////////////////////////
/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"
origin_tech = "engineering=2"
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BOTH_POCKETS
prefered_slot_flags = ITEM_SLOT_BOTH_POCKETS
storage_slots = 10
max_combined_w_class = 200 //Doesn't matter what this is, so long as it's more or equal to storage_slots * ore.w_class
max_w_class = WEIGHT_CLASS_NORMAL
can_hold = list(/obj/item/stack/ore)
/// The mob currently holding the ore bag, to track moving over ore to auto-pickup.
var/mob/listening_to
/obj/item/storage/bag/ore/Destroy()
. = ..()
listening_to = null
/obj/item/storage/bag/ore/equipped(mob/user, slot, initial)
. = ..()
if(listening_to == user)
return
begin_listening(src, user)
/obj/item/storage/bag/ore/proc/begin_listening(datum/source, mob/user) // Even though its unused, the datum/source argument is required to make the signals work.
SIGNAL_HANDLER // COMSIG_CYBORG_ITEM_ACTIVATED
if(listening_to)
UnregisterSignal(listening_to, COMSIG_MOVABLE_MOVED)
RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(pickup_ores))
listening_to = user
/obj/item/storage/bag/ore/proc/end_listening()
SIGNAL_HANDLER // COMSIG_CYBORG_ITEM_DEACTIVATED
if(listening_to)
UnregisterSignal(listening_to, COMSIG_MOVABLE_MOVED)
listening_to = null
/obj/item/storage/bag/ore/dropped()
. = ..()
end_listening()
/obj/item/storage/bag/ore/proc/pickup_ores(mob/living/user)
SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED
var/turf/simulated/floor/plating/asteroid/tile = get_turf(user)
if(!istype(tile))
return
tile.attempt_ore_pickup(src, user)
// Then, if the user is dragging an ore box, empty the satchel
// into the box.
if(istype(user.pulling, /obj/structure/ore_box))
var/obj/structure/ore_box/box = user.pulling
box.item_interaction(user, src)
/obj/item/storage/bag/ore/cyborg
name = "cyborg mining satchel"
flags = NODROP
/obj/item/storage/bag/ore/cyborg/Initialize(mapload)
. = ..()
RegisterSignal(src, COMSIG_CYBORG_ITEM_ACTIVATED, PROC_REF(begin_listening))
RegisterSignal(src, COMSIG_CYBORG_ITEM_DEACTIVATED, PROC_REF(end_listening))
/obj/item/storage/bag/ore/cyborg/Destroy()
UnregisterSignal(src, COMSIG_CYBORG_ITEM_ACTIVATED)
UnregisterSignal(src, COMSIG_CYBORG_ITEM_DEACTIVATED)
return ..()
/// miners, your messiah has arrived
/obj/item/storage/bag/ore/holding
name = "mining satchel of holding"
desc = "A revolution in convenience, this satchel allows for infinite ore storage. It's been outfitted with anti-malfunction safety measures."
storage_slots = INFINITY
max_combined_w_class = INFINITY
origin_tech = "bluespace=4;materials=3;engineering=3"
materials = list(MAT_GOLD = 250, MAT_URANIUM = 500)
icon_state = "satchel_bspace"
/obj/item/storage/bag/ore/cyborg/holding
name = "cyborg mining satchel of holding"
desc = "A revolution in convenience, this satchel allows for infinite ore storage. It's been outfitted with anti-malfunction safety measures."
icon_state = "satchel_bspace"
storage_slots = INFINITY
max_combined_w_class = INFINITY
////////////////////////////////////////
// MARK: Plant bag
////////////////////////////////////////
/obj/item/storage/bag/plants
name = "plant bag"
icon = 'icons/obj/hydroponics/equipment.dmi'
icon_state = "plantbag"
storage_slots = 40 //the number of plant pieces it can carry.
max_combined_w_class = 40 //Doesn't matter what this is, so long as it's more or equal to storage_slots * plants.w_class
max_w_class = WEIGHT_CLASS_NORMAL
prefered_slot_flags = ITEM_SLOT_BOTH_POCKETS
w_class = WEIGHT_CLASS_TINY
can_hold = list(
/obj/item/seeds,
/obj/item/unsorted_seeds,
/obj/item/food/grown,
/obj/item/grown,
/obj/item/food/grown/ash_flora,
/obj/item/food/honeycomb)
cant_hold = list(/obj/item/grown/bananapeel/traitorpeel)
resistance_flags = FLAMMABLE
/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"
origin_tech = "biotech=3;engineering=2"
materials = list(MAT_METAL = 1000, MAT_GLASS = 400)
/obj/item/storage/bag/plants/portaseeder/Initialize(mapload)
. = ..()
RegisterSignal(src, COMSIG_BIT_ATTACH, PROC_REF(add_bit))
RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(remove_bit))
/obj/item/storage/bag/plants/portaseeder/examine(mob/user)
. = ..()
if(Adjacent(user))
. += SPAN_NOTICE("You can <b>Ctrl-Shift-Click</b> to convert the plants inside to seeds.")
/obj/item/storage/bag/plants/portaseeder/proc/process_plants(mob/user)
if(!length(contents))
to_chat(user, SPAN_WARNING("[src] has no seeds inside!"))
return
var/had_anything = FALSE
var/seed_amount = 1
// Multiply seeds by productivity
seed_amount = clamp(seed_amount * bit_productivity_mod, 1, 4)
// Reduce with low efficiency
if(bit_efficiency_mod < 1)
seed_amount = max(1, seed_amount * bit_efficiency_mod)
for(var/obj/item/O in contents)
had_anything |= seedify(O, seed_amount)
hide_from_all()
if(had_anything)
to_chat(user, SPAN_NOTICE("[src] whirrs a bit as it converts the plants inside to seeds."))
else
to_chat(user, SPAN_WARNING("[src] whirrs a bit but stops. Doesn't seem like it could convert anything inside."))
playsound(user, "sound/machines/ding.ogg", 25)
/obj/item/storage/bag/plants/portaseeder/CtrlShiftClick(mob/user)
if(Adjacent(user) && ishuman(user) && !user.incapacitated(FALSE, TRUE))
process_plants(user)
/obj/item/storage/bag/plants/seed_sorting_tray
name = "seed sorting tray"
desc = "A simple wooden tray with compartments for manually sorting seeds. It's better than nothing, but a plant analyzer would be more effective."
icon_state = "seed_sorting_tray"
can_hold = list(
/obj/item/seeds,
/obj/item/unsorted_seeds)
/obj/item/storage/bag/plants/seed_sorting_tray/CtrlShiftClick(mob/user)
var/depth = 0
for(var/obj/item/unsorted_seeds/unsorted in src)
if(!do_after(user, 1 SECONDS, TRUE, src, must_be_held = TRUE))
break
depth = min(8, depth + 1)
unsorted.sort(depth)
/obj/item/storage/bag/plants/seed_sorting_tray/examine(mob/user)
. = ..()
if(Adjacent(user))
. += SPAN_NOTICE("You can <b>Ctrl-Shift-Click</b> to sort seeds inside.")
////////////////////////////////////////
// MARK: Cash bag
////////////////////////////////////////
/obj/item/storage/bag/cash
icon_state = "cashbag"
name = "Cash bag"
desc = "A bag for carrying lots of cash. It's got a big dollar sign printed on the front."
storage_slots = 50; //the number of cash pieces it can carry.
max_combined_w_class = 200 //Doesn't matter what this is, so long as it's more or equal to storage_slots * cash.w_class
max_w_class = WEIGHT_CLASS_NORMAL
w_class = WEIGHT_CLASS_TINY
can_hold = list(/obj/item/coin, /obj/item/stack/spacecash)
////////////////////////////////////////
// MARK: Book Bag
////////////////////////////////////////
/obj/item/storage/bag/books
name = "book bag"
desc = "A bag for books."
icon = 'icons/obj/library.dmi'
icon_state = "bookbag"
display_contents_with_number = 0 //This would look really stupid otherwise
max_combined_w_class = 21
max_w_class = WEIGHT_CLASS_NORMAL
w_class = WEIGHT_CLASS_BULKY //Bigger than a book because physics
can_hold = list(/obj/item/book, /obj/item/storage/bible, /obj/item/tome, /obj/item/spellbook)
resistance_flags = FLAMMABLE
////////////////////////////////////////
// MARK: Serving tray
////////////////////////////////////////
/obj/item/storage/bag/tray
name = "serving tray"
icon = 'icons/obj/food/containers.dmi'
icon_state = "tray"
desc = "A metal tray to lay food on."
storage_slots = 8
force = 5
throwforce = 10
throw_speed = 3
throw_range = 5
w_class = WEIGHT_CLASS_BULKY
flags = CONDUCT
slot_flags = null
materials = list(MAT_METAL = 3000)
can_hold = list(
/obj/item/food,
/obj/item/reagent_containers/drinks,
/obj/item/reagent_containers/condiment,
)
/obj/item/storage/bag/tray/attack__legacy__attackchain(mob/living/M, mob/living/user)
..()
// Drop all the things. All of them.
var/list/obj/item/oldContents = contents.Copy()
drop_inventory(user)
// Make each item scatter a bit
for(var/obj/item/I in oldContents)
do_scatter(I)
if(prob(50))
playsound(M, 'sound/items/trayhit1.ogg', 50, 1)
else
playsound(M, 'sound/items/trayhit2.ogg', 50, 1)
if(ishuman(M) && prob(10))
M.KnockDown(4 SECONDS)
/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, GLOB.cardinal, 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 // COMSIG_MOVELOOP_POSTPROCESS
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_icon_state()
return
/obj/item/storage/bag/tray/update_overlays()
. = ..()
for(var/obj/item/I in contents)
. += image(icon = I.icon, icon_state = I.icon_state, layer = -1)
/obj/item/storage/bag/tray/cyborg
/obj/item/storage/bag/tray/cyborg/afterattack__legacy__attackchain(atom/target, mob/user, proximity_flag)
// We cannot reach the target.
if(!proximity_flag)
return
// Tray is empty.
if(!length(contents))
return ..()
// Not a table or turf.
if(!isturf(target) && !istype(target, /obj/structure/table))
return ..()
var/found_table = istype(target, /obj/structure/table)
// We clicked a turf, search it for a table.
if(!found_table)
for(var/obj/structure/table/T in target)
found_table = TRUE
break
var/turf/dropspot
// We clicked the floor.
if(!found_table)
dropspot = user.loc
// We clicked the floor but there's a table on it.
else if(isturf(target))
dropspot = target
// We clicked a table directly.
else
dropspot = target.loc
var/list/obj/item/oldContents = contents.Copy()
drop_inventory(user)
for(var/obj/item/I in oldContents)
// Set the properties of the new item here, e.g., stack count, hover highlight, tooltip
I.forceMove(dropspot)
// If there is no table, dump the contents of the tray at our feet like we're doing the service equivilent of a micdrop.
if(!found_table && isturf(dropspot))
INVOKE_ASYNC(src, PROC_REF(do_scatter), I)
if(found_table)
user.visible_message(SPAN_NOTICE("[user] unloads [user.p_their()] serving tray."))
else
user.visible_message(SPAN_WARNING("[user] upends [user.p_their()] serving tray, sending everything on it crashing down to the floor!"))
update_icon(UPDATE_OVERLAYS)
return ..()
/obj/item/storage/bag/tray/cookies_tray
var/cookie = /obj/item/food/cookie
/obj/item/storage/bag/tray/cookies_tray/populate_contents() // By Azule Utama, thank you a lot!
for(var/i in 1 to 6)
var/obj/item/C = new cookie(src)
C.in_inventory = TRUE
update_icon(UPDATE_OVERLAYS)
/obj/item/storage/bag/tray/cookies_tray/sugarcookie
cookie = /obj/item/food/sugarcookie
////////////////////////////////////////
// MARK: Chemistry bag
////////////////////////////////////////
/obj/item/storage/bag/chemistry
name = "chemistry bag"
icon = 'icons/obj/chemical.dmi'
icon_state = "bag"
desc = "A bag for storing pills, patches, and bottles."
storage_slots = 50
max_combined_w_class = 200
prefered_slot_flags = ITEM_SLOT_BOTH_POCKETS
w_class = WEIGHT_CLASS_TINY
can_hold = list(/obj/item/reagent_containers/pill,
/obj/item/reagent_containers/patch,
/obj/item/reagent_containers/glass/beaker,
/obj/item/reagent_containers/glass/bottle)
resistance_flags = FLAMMABLE
////////////////////////////////////////
// MARK: Bio bag
////////////////////////////////////////
/obj/item/storage/bag/bio
name = "bio bag"
icon = 'icons/obj/chemical.dmi'
icon_state = "biobag"
desc = "A bag for the safe transportation and disposal of biowaste and other biological materials."
storage_slots = 25
max_combined_w_class = 200
prefered_slot_flags = ITEM_SLOT_BOTH_POCKETS
w_class = WEIGHT_CLASS_TINY
can_hold = list(/obj/item/slime_extract, /obj/item/food/monkeycube,
/obj/item/reagent_containers/syringe, /obj/item/reagent_containers/glass/beaker,
/obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/iv_bag,
/obj/item/reagent_containers/hypospray/autoinjector/epinephrine)
resistance_flags = FLAMMABLE
////////////////////////////////////////
// MARK: Mail bag
////////////////////////////////////////
/obj/item/storage/bag/mail
name = "mail bag"
desc = "A bag for envelopes, stamps, pens, and papers."
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "mailbag"
storage_slots = 14
max_combined_w_class = 28
prefered_slot_flags = ITEM_SLOT_BOTH_POCKETS
w_class = WEIGHT_CLASS_TINY
can_hold = list(/obj/item/envelope, /obj/item/stamp, /obj/item/pen, /obj/item/paper, /obj/item/mail_scanner)
resistance_flags = FLAMMABLE
////////////////////////////////////////
// MARK: Construction bag
////////////////////////////////////////
/obj/item/storage/bag/construction
name = "construction bag"
desc = "A bag for storing various small scale construction supplies, such as wiring and circuit boards."
icon = 'icons/obj/tools.dmi'
icon_state = "construction_bag"
storage_slots = 30
max_combined_w_class = 60
prefered_slot_flags = ITEM_SLOT_BOTH_POCKETS
w_class = WEIGHT_CLASS_TINY
can_hold = list(/obj/item/airlock_electronics, /obj/item/firelock_electronics, /obj/item/firealarm_electronics, /obj/item/apc_electronics, /obj/item/airalarm_electronics, /obj/item/camera_assembly, /obj/item/stock_parts/cell, /obj/item/circuitboard, /obj/item/stack/cable_coil)
resistance_flags = FLAMMABLE
////////////////////////////////////////
// MARK: Smith bag
////////////////////////////////////////
/obj/item/storage/bag/smith
name = "smith's bag"
desc = "A fireproof bag for storing modifications, casts, and modification components."
icon = 'icons/obj/tools.dmi'
icon_state = "smith_bag"
storage_slots = 30
max_combined_w_class = 60
prefered_slot_flags = ITEM_SLOT_BOTH_POCKETS
w_class = WEIGHT_CLASS_TINY
can_hold = list(/obj/item/smithed_item, /obj/item/smithing_cast)
resistance_flags = FIRE_PROOF
////////////////////////////////////////
// MARK: Treasure bag
////////////////////////////////////////
/obj/item/storage/bag/expedition
name = "treasure satchel"
desc = "A satchel for storing scavenged salvage. There be treasure."
icon = 'icons/obj/mining.dmi'
icon_state = "satchel_treasure"
origin_tech = "engineering=2"
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BOTH_POCKETS
prefered_slot_flags = ITEM_SLOT_BOTH_POCKETS
storage_slots = 15
max_combined_w_class = 60
max_w_class = WEIGHT_CLASS_NORMAL
can_hold = list(/obj/item/salvage)
/obj/item/storage/bag/expedition/robust
name = "robust treasure satchel"
desc = "We heard you liked booty so we put booty in your booty so ye could carry more booty."
storage_slots = 30
max_combined_w_class = 120
origin_tech = "materials=3;engineering=3"
icon_state = "satchel_bspace" // placeholder