mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-29 16:18:01 +01:00
moves the material container files inside the datum/materials folder and the food folder inside module/food_and_drinks. (#96526)
## About The Pull Request Title. ## Why It's Good For The Game In the case of materials. the two folders had roughly the same names, and material_containers rely on materials to work anyway so they belong inside the materials folder, instead of a separate directory with the same name (minus an s). As for food, the food items have filepaths that differ too much from the corresponding recipe files, despite the two being closely connected as far as features and game mechanics go, and that makes moving back and forth between these files a pain in the ass with a standard file manager, workspace viewer etc. ## Changelog N/A
This commit is contained in:
@@ -0,0 +1,801 @@
|
||||
/*!
|
||||
This datum should be used for handling mineral contents of machines and whatever else is supposed to hold minerals and make use of them.
|
||||
|
||||
Variables:
|
||||
amount - raw amount of the mineral this container is holding, calculated by the defined value SHEET_MATERIAL_AMOUNT=SHEET_MATERIAL_AMOUNT.
|
||||
max_amount - max raw amount of mineral this container can hold.
|
||||
sheet_type - type of the mineral sheet the container handles, used for output.
|
||||
parent - object that this container is being used by, used for output.
|
||||
MAX_STACK_SIZE - size of a stack of mineral sheets. Constant.
|
||||
*/
|
||||
|
||||
//The full item was consumed
|
||||
#define MATERIAL_INSERT_ITEM_SUCCESS 1
|
||||
|
||||
/datum/material_container
|
||||
///Parent to which this container is attached to
|
||||
VAR_PRIVATE/atom/movable/parent
|
||||
/// The maximum amount of materials this material container can contain
|
||||
var/max_amount
|
||||
/// Map of material ref -> amount
|
||||
var/list/materials //Map of key = material ref | Value = amount
|
||||
/// The list of materials that this material container can accept
|
||||
var/list/allowed_materials
|
||||
/// The typecache of things that this material container can accept
|
||||
var/list/allowed_item_typecache
|
||||
/// Whether or not this material container allows specific amounts from sheets to be inserted
|
||||
var/precise_insertion = FALSE
|
||||
/// The material container flags. See __DEFINES/construction/materials.dm.
|
||||
var/mat_container_flags
|
||||
/// Signals that are registered with this contained
|
||||
var/list/registered_signals
|
||||
|
||||
/// Sets up the proper signals and fills the list of materials with the appropriate references.
|
||||
/datum/material_container/New(
|
||||
target,
|
||||
list/init_mats,
|
||||
max_amt = 0,
|
||||
_mat_container_flags = NONE,
|
||||
list/allowed_mats = init_mats,
|
||||
list/allowed_items,
|
||||
list/container_signals
|
||||
)
|
||||
|
||||
parent = target
|
||||
|
||||
materials = list()
|
||||
max_amount = max(0, max_amt)
|
||||
mat_container_flags = _mat_container_flags
|
||||
|
||||
allowed_materials = allowed_mats || list()
|
||||
if(allowed_items)
|
||||
if(ispath(allowed_items) && allowed_items == /obj/item/stack)
|
||||
allowed_item_typecache = GLOB.typecache_stack
|
||||
else
|
||||
allowed_item_typecache = typecacheof(allowed_items)
|
||||
|
||||
//Make the assoc list material reference -> amount
|
||||
for(var/mat in init_mats)
|
||||
var/mat_ref = SSmaterials.get_material(mat)
|
||||
if(isnull(mat_ref))
|
||||
continue
|
||||
var/mat_amt = init_mats[mat]
|
||||
if(isnull(mat_amt))
|
||||
mat_amt = 0
|
||||
materials[mat_ref] += mat_amt
|
||||
|
||||
//all user handled signals
|
||||
if(length(container_signals))
|
||||
for(var/signal in container_signals)
|
||||
parent.RegisterSignal(src, signal, container_signals[signal])
|
||||
|
||||
//can we insert into this container
|
||||
if(!(mat_container_flags & MATCONTAINER_NO_INSERT))
|
||||
//to insert stuff into the container
|
||||
RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_item_insert))
|
||||
RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION_SECONDARY, PROC_REF(on_secondary_insert))
|
||||
|
||||
//screen tips for inserting items
|
||||
parent.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1
|
||||
RegisterSignal(parent, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, PROC_REF(on_requesting_context_from_item))
|
||||
|
||||
//to see available materials
|
||||
if(mat_container_flags & MATCONTAINER_EXAMINE)
|
||||
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
|
||||
|
||||
//drop sheets when the object is deconstructed but not deleted
|
||||
RegisterSignal(parent, COMSIG_OBJ_DECONSTRUCT, PROC_REF(drop_sheets))
|
||||
|
||||
/datum/material_container/Destroy(force)
|
||||
materials = null
|
||||
allowed_materials = null
|
||||
return ..()
|
||||
|
||||
/datum/material_container/proc/drop_sheets()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
retrieve_all()
|
||||
|
||||
/datum/material_container/proc/on_examine(datum/source, mob/user, list/examine_texts)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
for(var/I in materials)
|
||||
var/datum/material/M = I
|
||||
var/amt = materials[I] / SHEET_MATERIAL_AMOUNT
|
||||
if(amt)
|
||||
examine_texts += span_notice("It has [amt] sheets of [LOWER_TEXT(M.name)] stored.")
|
||||
|
||||
/datum/material_container/vv_edit_var(var_name, var_value)
|
||||
var/old_flags = mat_container_flags
|
||||
. = ..()
|
||||
if(var_name == NAMEOF(src, mat_container_flags) && parent)
|
||||
if(!(old_flags & MATCONTAINER_EXAMINE) && mat_container_flags & MATCONTAINER_EXAMINE)
|
||||
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
|
||||
else if(old_flags & MATCONTAINER_EXAMINE && !(mat_container_flags & MATCONTAINER_EXAMINE))
|
||||
UnregisterSignal(parent, COMSIG_ATOM_EXAMINE)
|
||||
|
||||
if(old_flags & MATCONTAINER_NO_INSERT && !(mat_container_flags & MATCONTAINER_NO_INSERT))
|
||||
RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_item_insert))
|
||||
RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION_SECONDARY, PROC_REF(on_secondary_insert))
|
||||
else if(!(old_flags & MATCONTAINER_NO_INSERT) && mat_container_flags & MATCONTAINER_NO_INSERT)
|
||||
UnregisterSignal(parent, list(COMSIG_ATOM_ITEM_INTERACTION, COMSIG_ATOM_ITEM_INTERACTION_SECONDARY))
|
||||
|
||||
/**
|
||||
* 3 Types of Procs
|
||||
* Material Insertion : Insert materials into the container
|
||||
* Material Validation : Checks how much materials are available, Extracts materials from items if the container can hold them
|
||||
* Material Removal : Removes material from the container
|
||||
*
|
||||
* Each Proc further belongs to a specific category
|
||||
* LOW LEVEL: Procs that are used internally & should not be used anywhere else unless you know what your doing
|
||||
* MID LEVEL: Procs that can be used by machines(like recycler, stacking machines) to bypass majority of checks
|
||||
* HIGH LEVEL: Procs that can be used by anyone publicly and guarantees safety checks & limits
|
||||
*/
|
||||
|
||||
//================================Material Insertion procs==============================
|
||||
|
||||
//======================================LOW LEVEL=========================================
|
||||
/**
|
||||
* Inserts the relevant materials from an item into this material container.
|
||||
* This low level proc should not be used directly by anyone
|
||||
*
|
||||
* Arguments:
|
||||
* - [source][/obj/item]: The source of the materials we are inserting.
|
||||
* - multiplier: The multiplier for the materials extract from this item being inserted.
|
||||
* - context: the atom performing the operation, is used mostly for silo logging, the silo resends this signal on the context to give it a
|
||||
* chance to process the item
|
||||
* - user_data: in the form rendered by ID_DATA(user), for material logging (and if this component is connected to a silo, also for permission checking)
|
||||
*/
|
||||
/datum/material_container/proc/insert_item_materials(obj/item/source, multiplier = 1, atom/context = parent, alist/user_data)
|
||||
var/primary_mat
|
||||
var/max_mat_value = 0
|
||||
var/material_amount = 0
|
||||
|
||||
var/list/item_materials = source.get_material_composition(mat_container_flags)
|
||||
var/list/mats_consumed = list()
|
||||
for(var/MAT in item_materials)
|
||||
if(!can_hold_material(MAT))
|
||||
continue
|
||||
var/mat_amount = OPTIMAL_COST(item_materials[MAT] * multiplier)
|
||||
materials[MAT] += mat_amount
|
||||
if(item_materials[MAT] > max_mat_value)
|
||||
max_mat_value = item_materials[MAT]
|
||||
primary_mat = MAT
|
||||
mats_consumed[MAT] = mat_amount
|
||||
material_amount += mat_amount
|
||||
if(length(mats_consumed))
|
||||
SEND_SIGNAL(src, COMSIG_MATCONTAINER_ITEM_CONSUMED, source, primary_mat, mats_consumed, material_amount, context, user_data)
|
||||
|
||||
return primary_mat
|
||||
//===================================================================================
|
||||
|
||||
|
||||
//===============================MID LEVEL===================================================
|
||||
/**
|
||||
* For inserting an amount of material. Use this to add materials to the container directly
|
||||
*
|
||||
* Arguments:
|
||||
* - amt: amount of said material to insert
|
||||
* - mat: the material type to insert
|
||||
*/
|
||||
/datum/material_container/proc/insert_amount_mat(amt, datum/material/mat)
|
||||
if(amt <= 0)
|
||||
return 0
|
||||
amt = OPTIMAL_COST(amt)
|
||||
if(!has_space(amt))
|
||||
return 0
|
||||
|
||||
var/total_amount_saved = total_amount()
|
||||
if(mat)
|
||||
if(!istype(mat))
|
||||
mat = SSmaterials.get_material(mat)
|
||||
materials[mat] += amt
|
||||
else
|
||||
var/num_materials = length(materials)
|
||||
if(!num_materials)
|
||||
return 0
|
||||
|
||||
amt /= num_materials
|
||||
for(var/i in materials)
|
||||
materials[i] += amt
|
||||
return (total_amount() - total_amount_saved)
|
||||
|
||||
/**
|
||||
* Proc specifically for inserting items, use this when you want to insert any item into the container
|
||||
* this bypasses most of the material flag checks so much be used by machines like recycler, stacking machine etc that
|
||||
* does not care for such checks
|
||||
*
|
||||
* Arguments:
|
||||
* - [weapon][obj/item]: the item you are trying to insert
|
||||
* - multiplier: The multiplier for the materials being inserted
|
||||
* - context: the atom performing the operation, this is the last argument sent in COMSIG_MATCONTAINER_ITEM_CONSUMED and is used mostly for silo logging
|
||||
* * - delete_item: should we delete the item after its materials are consumed. does not apply to stacks if they were split due to lack of space
|
||||
* * - user_data - in the form rendered by ID_DATA(user), for material logging (and if this component is connected to a silo, also for permission checking)
|
||||
*/
|
||||
/datum/material_container/proc/insert_item(obj/item/weapon, multiplier = 1, atom/context = parent, delete_item = TRUE, alist/user_data)
|
||||
if(QDELETED(weapon))
|
||||
return MATERIAL_INSERT_ITEM_NO_MATS
|
||||
multiplier = CEILING(multiplier, 0.01)
|
||||
|
||||
var/obj/item/target = weapon
|
||||
|
||||
var/material_amount = OPTIMAL_COST(get_item_material_amount(target) * multiplier)
|
||||
if(!material_amount)
|
||||
return MATERIAL_INSERT_ITEM_NO_MATS
|
||||
var/obj/item/stack/item_stack
|
||||
if(isstack(weapon) && !has_space(material_amount)) //not enough space split and feed as many sheets possible
|
||||
item_stack = weapon
|
||||
var/space_left = max_amount - total_amount()
|
||||
if(!space_left)
|
||||
return MATERIAL_INSERT_ITEM_NO_SPACE
|
||||
var/material_per_sheet = material_amount / item_stack.amount
|
||||
var/sheets_to_insert = floor(space_left / material_per_sheet)
|
||||
if(!sheets_to_insert)
|
||||
return MATERIAL_INSERT_ITEM_NO_SPACE
|
||||
target = fast_split_stack(item_stack, sheets_to_insert)
|
||||
material_amount = get_item_material_amount(target) * multiplier
|
||||
material_amount = OPTIMAL_COST(material_amount)
|
||||
|
||||
//do the insert
|
||||
var/last_inserted_id = insert_item_materials(target, multiplier, context, user_data = user_data)
|
||||
if(!isnull(last_inserted_id))
|
||||
if(delete_item || target != weapon) //we could have split the stack ourselves
|
||||
qdel(target) //item gone
|
||||
return material_amount
|
||||
else if(!isnull(item_stack) && item_stack != target) //insertion failed, merge the split stack back into the original
|
||||
var/obj/item/stack/inserting_stack = target
|
||||
item_stack.add(inserting_stack.amount)
|
||||
qdel(inserting_stack)
|
||||
|
||||
return MATERIAL_INSERT_ITEM_FAILURE
|
||||
//============================================================================================
|
||||
|
||||
|
||||
//===================================HIGH LEVEL===================================================
|
||||
/**
|
||||
* inserts an item from the players hand into the container. Loops through all the contents inside recursively
|
||||
* Does all explicit checking for mat flags & callbacks to check if insertion is valid
|
||||
* This proc is what you should be using for almost all cases
|
||||
*
|
||||
* Arguments:
|
||||
* * held_item - the item to insert
|
||||
* * user - the mob inserting this item
|
||||
* * context - the atom performing the operation, this is the last argument sent in COMSIG_MATCONTAINER_ITEM_CONSUMED and is used mostly for silo logging
|
||||
*/
|
||||
/datum/material_container/proc/user_insert(obj/item/held_item, mob/living/user, atom/context = parent)
|
||||
set waitfor = FALSE
|
||||
. = 0
|
||||
|
||||
//All items that do not have any contents
|
||||
var/list/obj/item/items = list(held_item)
|
||||
//is this the first item we are ever processing
|
||||
var/first_checks = TRUE
|
||||
//list of items to delete
|
||||
var/list/obj/item/to_delete = list()
|
||||
//The status of the last insert attempt
|
||||
var/inserted = 0
|
||||
//All messages to be displayed to chat
|
||||
var/list/chat_msgs = list()
|
||||
//differs from held_item when using TK
|
||||
var/obj/item/active_held = user.get_active_held_item()
|
||||
//omni tools can act as any tool so get its real behaviour
|
||||
active_held = active_held.get_proxy_attacker_for(held_item)
|
||||
|
||||
while(items.len)
|
||||
//no point inserting more items
|
||||
if(inserted == MATERIAL_INSERT_ITEM_NO_SPACE)
|
||||
break
|
||||
|
||||
//Pop the 1st item out from the list
|
||||
var/obj/item/target_item = items[1]
|
||||
items -= target_item
|
||||
|
||||
//e.g. projectiles inside bullets are not objects
|
||||
if(!istype(target_item))
|
||||
continue
|
||||
//can't allow abstract, hologram items
|
||||
if((target_item.item_flags & ABSTRACT) || (target_item.flags_1 & HOLOGRAM_1))
|
||||
continue
|
||||
//user defined conditions
|
||||
if(SEND_SIGNAL(src, COMSIG_MATCONTAINER_PRE_USER_INSERT, target_item, user) & MATCONTAINER_BLOCK_INSERT)
|
||||
continue
|
||||
//item is either indestructible, not allowed for redemption or not in the allowed types
|
||||
if((target_item.resistance_flags & INDESTRUCTIBLE) || (target_item.item_flags & NO_MAT_REDEMPTION) || (allowed_item_typecache && !is_type_in_typecache(target_item, allowed_item_typecache)))
|
||||
if(!(mat_container_flags & MATCONTAINER_SILENT))
|
||||
var/list/status_data = chat_msgs["[MATERIAL_INSERT_ITEM_FAILURE]"] || list()
|
||||
var/list/item_data = status_data[target_item.name] || list()
|
||||
item_data["item"] = target_item
|
||||
item_data["count"] += 1
|
||||
status_data[target_item.name] = item_data
|
||||
chat_msgs["[MATERIAL_INSERT_ITEM_FAILURE]"] = status_data
|
||||
|
||||
if(target_item.resistance_flags & INDESTRUCTIBLE)
|
||||
if(target_item != active_held) //move it out of any storage medium its in so it doesn't get consumed with its parent, but only if that storage medium is not our hand
|
||||
target_item.forceMove(get_turf(context))
|
||||
continue
|
||||
|
||||
//storage items usually come here
|
||||
//this is so players can insert items from their bags into machines for convinience
|
||||
if(!target_item.atom_storage || !target_item.contents.len)
|
||||
continue
|
||||
//at this point we can check if we have enough for all items & other stuff
|
||||
if(first_checks)
|
||||
//duffle bags needs to be unzipped
|
||||
if(target_item.atom_storage?.locked)
|
||||
if(!(mat_container_flags & MATCONTAINER_SILENT))
|
||||
to_chat(user, span_warning("[target_item] has its storage locked"))
|
||||
return
|
||||
|
||||
//anything that isn't a stack cannot be split so find out if we have enough space, we don't want to consume half the contents of an object & leave it in a broken state
|
||||
//for duffle bags and other storage items we can check for space 1 item at a time
|
||||
if(!isstack(target_item) && !target_item.atom_storage)
|
||||
var/total_amount = 0
|
||||
for(var/obj/item/weapon as anything in target_item.get_all_contents_type(/obj/item))
|
||||
total_amount += get_item_material_amount(weapon)
|
||||
if(!has_space(total_amount))
|
||||
if(!(mat_container_flags & MATCONTAINER_SILENT))
|
||||
to_chat(user, span_warning("[parent] does not have enough space for [target_item]!"))
|
||||
return
|
||||
|
||||
first_checks = FALSE
|
||||
|
||||
//if stack, check if we want to read precise amount of sheets to insert
|
||||
var/obj/item/stack/item_stack = null
|
||||
if(isstack(target_item) && precise_insertion)
|
||||
var/atom/current_parent = parent
|
||||
item_stack = target_item
|
||||
var/requested_amount = tgui_input_number(user, "How much do you want to insert?", "Inserting [item_stack.singular_name]s", item_stack.amount, item_stack.amount)
|
||||
if(!requested_amount || QDELETED(target_item) || QDELETED(user) || QDELETED(src))
|
||||
continue
|
||||
if(parent != current_parent || user.get_active_held_item() != active_held)
|
||||
continue
|
||||
if(requested_amount != item_stack.amount) //only split if its not the whole amount
|
||||
target_item = fast_split_stack(item_stack, requested_amount) //split off the requested amount
|
||||
requested_amount = 0
|
||||
|
||||
//is this item a stack and was it split by the player?
|
||||
var/was_stack_split = !isnull(item_stack) && item_stack != target_item
|
||||
//if it was split then item_stack has the reference to the original stack/item
|
||||
var/obj/item/original_item = was_stack_split ? item_stack : target_item
|
||||
//if this item is not the one the player is holding then don't remove it from their hand
|
||||
if(original_item != active_held)
|
||||
original_item = null
|
||||
if(!isnull(original_item) && !user.temporarilyRemoveItemFromInventory(original_item)) //remove from hand(if split remove the original stack else the target)
|
||||
return
|
||||
|
||||
//insert the item
|
||||
var/item_name = target_item.name
|
||||
var/item_count = 1
|
||||
var/is_stack = FALSE
|
||||
if(isstack(target_item))
|
||||
var/obj/item/stack/the_stack = target_item
|
||||
item_name = the_stack.singular_name
|
||||
item_count = the_stack.amount
|
||||
is_stack = TRUE
|
||||
|
||||
//we typically don't want to consume bags, boxes but only their contents. so we skip processing
|
||||
inserted = !target_item.atom_storage ? insert_item(target_item, 1, context, is_stack, user_data = ID_DATA(user)) : 0
|
||||
if(inserted > 0)
|
||||
. += inserted
|
||||
inserted /= SHEET_MATERIAL_AMOUNT // display units inserted as sheets for improved readability
|
||||
|
||||
//collect all messages to print later
|
||||
var/list/status_data = chat_msgs["[MATERIAL_INSERT_ITEM_SUCCESS]"] || list()
|
||||
var/list/item_data = status_data[item_name] || list()
|
||||
item_data["item"] = target_item
|
||||
item_data["count"] += item_count
|
||||
item_data["amount"] += inserted
|
||||
status_data[item_name] = item_data
|
||||
chat_msgs["[MATERIAL_INSERT_ITEM_SUCCESS]"] = status_data
|
||||
|
||||
//delete the item or merge stacks if any left over
|
||||
if(is_stack)
|
||||
//player split it & machine further split that due to lack of space? merge with remaining stack
|
||||
if(!QDELETED(target_item) && was_stack_split)
|
||||
var/obj/item/stack/inserting_stack = target_item
|
||||
item_stack.add(inserting_stack.amount)
|
||||
qdel(inserting_stack)
|
||||
|
||||
//was this the original item in the players hand? put what's left back in the player's hand
|
||||
if(!QDELETED(original_item))
|
||||
user.put_in_active_hand(original_item)
|
||||
|
||||
//skip processing children & other stuff. irrelevant for stacks
|
||||
continue
|
||||
|
||||
//queue the object for deletion
|
||||
to_delete += target_item
|
||||
else
|
||||
//collect all messages to print later
|
||||
var/list/status_data = chat_msgs["[inserted]"] || list()
|
||||
var/list/item_data = status_data[item_name] || list()
|
||||
item_data["item"] = target_item
|
||||
item_data["count"] += item_count
|
||||
status_data[item_name] = item_data
|
||||
chat_msgs["[inserted]"] = status_data
|
||||
|
||||
//player split the stack by the requested amount but even that split amount could not be salvaged. merge it back with the original
|
||||
if(was_stack_split)
|
||||
var/obj/item/stack/inserting_stack = target_item
|
||||
item_stack.add(inserting_stack.amount)
|
||||
qdel(inserting_stack)
|
||||
|
||||
//was this the original item in the players hand? put it back because we coudn't salvage it
|
||||
if(!QDELETED(original_item))
|
||||
user.put_in_active_hand(original_item)
|
||||
|
||||
//we can stop here as remaining items will fail to insert as well
|
||||
if(inserted == MATERIAL_INSERT_ITEM_NO_SPACE)
|
||||
break
|
||||
|
||||
//we failed to process the item so don't bother going into its contents
|
||||
//but if we are dealing with storage items like bags, boxes etc then we make a exception
|
||||
if(!target_item.atom_storage)
|
||||
continue
|
||||
|
||||
//If any mats were consumed we can proceed to delete the parent
|
||||
//If it has children then we will process them first in the 2nd round
|
||||
//This is done so we don't delete the children when the parent is consumed
|
||||
//We only do this on the 1st iteration so we don't re-iterate through its children again
|
||||
if(target_item.contents.len)
|
||||
if(target_item.atom_storage?.locked) //can't access contents of locked storage(like duffle bags)
|
||||
continue
|
||||
//process children
|
||||
items += target_item.contents
|
||||
|
||||
//we now summarize the chat msgs collected
|
||||
if(!(mat_container_flags & MATCONTAINER_SILENT))
|
||||
for(var/status in chat_msgs)
|
||||
var/list/status_data = chat_msgs[status]
|
||||
|
||||
for(var/item_name in status_data)
|
||||
//read the params
|
||||
var/list/chat_data = status_data[item_name]
|
||||
var/obj/item/item = chat_data["item"]
|
||||
var/count = chat_data["count"]
|
||||
var/amount = chat_data["amount"]
|
||||
|
||||
//decode the message
|
||||
switch(text2num(status))
|
||||
if(MATERIAL_INSERT_ITEM_SUCCESS) //no problems full item was consumed
|
||||
if(isstack(item))
|
||||
var/obj/item/stack/stack = item
|
||||
var/sheets = min(count, amount) //minimum between sheets inserted vs sheets consumed(values differ for alloys)
|
||||
if (sheets > 1)
|
||||
to_chat(user, span_notice("[sheets] [stack.singular_name][plural_s(stack.singular_name)] were added to [parent]."))
|
||||
else
|
||||
to_chat(user, span_notice("The [stack.singular_name] was added to [parent]."))
|
||||
else
|
||||
if (count > 1)
|
||||
to_chat(user, span_notice("[count] [item.name][plural_s(item.name)], worth [amount] sheet\s, were added to [parent]."))
|
||||
else
|
||||
to_chat(user, span_notice("\The [item], worth [amount] sheet\s, [item.p_were()] added to [parent]."))
|
||||
if(MATERIAL_INSERT_ITEM_NO_SPACE) //no space
|
||||
to_chat(user, span_warning("[parent] has no space to accept \the [item]!"))
|
||||
if(MATERIAL_INSERT_ITEM_NO_MATS) //no materials inside these items
|
||||
if(isstack(item))
|
||||
var/obj/item/stack/stack = item
|
||||
to_chat(user, span_warning("The [stack.singular_name] has no materials that can be accepted by [parent]!"))
|
||||
else
|
||||
to_chat(user, span_warning("\The [item][count > 1 ? "[plural_s(item.name)] have" : " [item.p_have()]"] no materials that can be accepted by [parent]!"))
|
||||
if(MATERIAL_INSERT_ITEM_FAILURE) //could be because the material type was not accepted or other stuff
|
||||
if (isstack(item))
|
||||
var/obj/item/stack/stack = item
|
||||
to_chat(user, span_warning("The [stack.singular_name] was rejected by [parent]!"))
|
||||
else
|
||||
to_chat(user, span_warning("\The [item][count > 1 ? "[plural_s(item.name)] were" : " [item.p_were()]"] rejected by [parent]!"))
|
||||
|
||||
//finally delete the items
|
||||
for(var/obj/item/deleting as anything in to_delete)
|
||||
if(!QDELETED(deleting)) //deleting parents also delete their children so we check
|
||||
qdel(deleting)
|
||||
|
||||
/datum/material_container/proc/on_item_insert(datum/source, mob/living/user, obj/item/weapon)
|
||||
SIGNAL_HANDLER
|
||||
// Don't insert material items with left click
|
||||
if (isstack(weapon))
|
||||
return attempt_insert(user, weapon)
|
||||
|
||||
/datum/material_container/proc/on_secondary_insert(datum/source, mob/living/user, obj/item/weapon)
|
||||
SIGNAL_HANDLER
|
||||
return attempt_insert(user, weapon)
|
||||
|
||||
/// Proc that allows players to fill the parent with mats
|
||||
/datum/material_container/proc/attempt_insert(mob/living/user, obj/item/weapon)
|
||||
//Allows you to attack the machine with iron sheets for e.g.
|
||||
if(!(mat_container_flags & MATCONTAINER_ANY_INTENT) && user.combat_mode)
|
||||
return
|
||||
|
||||
if(ismachinery(parent))
|
||||
var/obj/machinery/machine = parent
|
||||
if(machine.machine_stat || machine.panel_open)
|
||||
return
|
||||
|
||||
user_insert(weapon, user)
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
//===============================================================================================
|
||||
|
||||
|
||||
//======================================Material Validation=======================================
|
||||
|
||||
//=========================================LOW LEVEL===================================
|
||||
/**
|
||||
* Proc that returns TRUE if the container has space
|
||||
*
|
||||
* Arguments:
|
||||
* * amt - can this container hold this much amount of materials
|
||||
*/
|
||||
/datum/material_container/proc/has_space(amt = 0)
|
||||
return (total_amount() + amt) <= max_amount
|
||||
|
||||
/**
|
||||
* The default check for whether we can add materials to this material container.
|
||||
*
|
||||
* Arguments:
|
||||
* - [mat][/atom/material]: The material we are checking for insertability.
|
||||
*/
|
||||
/datum/material_container/proc/can_hold_material(datum/material/mat)
|
||||
if(mat in allowed_materials)
|
||||
return TRUE
|
||||
if(istype(mat) && ((mat.id in allowed_materials) || (mat.type in allowed_materials)))
|
||||
allowed_materials += mat // This could get messy with passing lists by ref... but if you're doing that the list expansion is probably being taken care of elsewhere anyway...
|
||||
return TRUE
|
||||
if(SEND_SIGNAL(src, COMSIG_MATCONTAINER_MAT_CHECK, mat) & MATCONTAINER_ALLOW_MAT)
|
||||
allowed_materials += mat
|
||||
return TRUE
|
||||
return FALSE
|
||||
//========================================================================================
|
||||
|
||||
|
||||
//===================================MID LEVEL=============================================
|
||||
/**
|
||||
* Returns the amount of a specific material in this container.
|
||||
*
|
||||
* Arguments:
|
||||
* -[mat][datum/material] : the material type to check for 3 cases
|
||||
* a) If it's an path its ref is retrieved
|
||||
* b) If it's text then its an category material & there is no way to deal with it so return 0
|
||||
* c) If normal material proceeds as usual
|
||||
*/
|
||||
/datum/material_container/proc/get_material_amount(datum/material/mat)
|
||||
if(!istype(mat))
|
||||
mat = SSmaterials.get_material(mat)
|
||||
return materials[mat]
|
||||
|
||||
/**
|
||||
* Returns the amount of material relevant to this container;
|
||||
* if this container does not support glass, any glass in 'I' will not be taken into account
|
||||
*
|
||||
* Arguments:
|
||||
* - [I][obj/item]: the item whos materials must be retrieved
|
||||
*/
|
||||
/datum/material_container/proc/get_item_material_amount(obj/item/item)
|
||||
if(!istype(item) || !item.custom_materials)
|
||||
return 0
|
||||
var/material_amount = 0
|
||||
var/list/item_materials = item.get_material_composition(mat_container_flags)
|
||||
for(var/MAT in item_materials)
|
||||
if(!can_hold_material(MAT))
|
||||
continue
|
||||
material_amount += item_materials[MAT]
|
||||
return material_amount
|
||||
//================================================================================================
|
||||
|
||||
|
||||
//=========================================HIGH LEVEL==========================================
|
||||
/// returns the total amount of material in the container
|
||||
/datum/material_container/proc/total_amount()
|
||||
. = 0
|
||||
for(var/i in materials)
|
||||
. += get_material_amount(i)
|
||||
|
||||
/**
|
||||
* Returns TRUE if you have enough of the specified material.
|
||||
*
|
||||
* Arguments:
|
||||
* - [req_mat][datum/material]: the material to check for
|
||||
* - amount: how much material do we need
|
||||
*/
|
||||
/datum/material_container/proc/has_enough_of_material(datum/material/req_mat, amount = 1)
|
||||
return get_material_amount(req_mat) >= OPTIMAL_COST(amount)
|
||||
|
||||
|
||||
/**
|
||||
* Checks if its possible to afford a certain amount of materials. Takes a dictionary of materials.
|
||||
* coefficient can be thought of as the machines efficiency & multiplier as the print quantity
|
||||
*
|
||||
* Arguments:
|
||||
* - mats: list of materials(key=material, value= 1 unit of material) to check for
|
||||
* - coefficient: scaling applied to 1 unit of material in the mats list
|
||||
* - multiplier: how many units(after scaling) do we require
|
||||
*/
|
||||
/datum/material_container/proc/has_materials(list/mats, coefficient = 1, multiplier = 1)
|
||||
if(!length(mats))
|
||||
return FALSE
|
||||
|
||||
for(var/x in mats) //Loop through all required materials
|
||||
var/wanted = OPTIMAL_COST(mats[x] * coefficient) * multiplier
|
||||
if(!has_enough_of_material(x, wanted))//Not a category, so just check the normal way
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
//==========================================================================================================
|
||||
|
||||
|
||||
//================================================Material Usage============================================
|
||||
|
||||
//==================================================LOW LEVEL=======================================
|
||||
/**
|
||||
* Uses an amount of a specific material, effectively removing it.
|
||||
*
|
||||
* Arguments:
|
||||
* - amt: amount of said material to use
|
||||
* - [mat][datum/material]: type of mat to use
|
||||
*/
|
||||
/datum/material_container/proc/use_amount_mat(amt, datum/material/mat)
|
||||
//round amount
|
||||
amt = OPTIMAL_COST(amt)
|
||||
|
||||
//get ref if necessary
|
||||
if(!istype(mat))
|
||||
mat = SSmaterials.get_material(mat)
|
||||
|
||||
//check if sufficient is available
|
||||
if(materials[mat] < amt)
|
||||
return 0
|
||||
|
||||
//consume & return amount consumed
|
||||
materials[mat] -= amt
|
||||
return amt
|
||||
//==============================================================================================
|
||||
|
||||
//=========================================MID LEVEL==========================================
|
||||
/**
|
||||
* For consuming a dictionary of materials.
|
||||
*
|
||||
* Arguments:
|
||||
* - mats: map of materials to consume(key = material type, value = amount)
|
||||
* - coefficient: how much fraction of unit material in the mats list must be consumed. This is usually your machines efficiency
|
||||
* - multiplier: how many units of material in the mats list(after each unit is multiplied and rounded with coefficient) must be consumed, This is usually your print quantity
|
||||
*/
|
||||
/datum/material_container/proc/use_materials(list/mats, coefficient = 1, multiplier = 1)
|
||||
if(!mats || !length(mats))
|
||||
return FALSE
|
||||
|
||||
var/amount_removed = 0
|
||||
for(var/i in mats)
|
||||
amount_removed += use_amount_mat(OPTIMAL_COST(mats[i] * coefficient) * multiplier, i)
|
||||
|
||||
return amount_removed
|
||||
//============================================================================================
|
||||
|
||||
|
||||
//===========================================HIGH LEVEL=======================================
|
||||
|
||||
/**
|
||||
* For spawning stacks (mineral sheets or ore) at a specific location. Used by machines to output sheets.
|
||||
*
|
||||
* Arguments:
|
||||
* stack_amt: number of sheets to extract
|
||||
* [material][datum/material]: type of sheets present in this container to extract
|
||||
* [target][atom]: drop location
|
||||
* [atom][context]: context - the atom performing the operation, this is the last argument sent in COMSIG_MATCONTAINER_STACK_RETRIEVED and is used mostly for silo logging
|
||||
* user_data - in the form rendered by ID_DATA(user), for material logging (and if this component is connected to a silo, also for permission checking)
|
||||
*/
|
||||
/datum/material_container/proc/retrieve_stack(stack_amt, datum/material/material, atom/target = null, atom/context = parent, alist/user_data)
|
||||
//do we support sheets of this material
|
||||
if(!material.sheet_type)
|
||||
return 0 //Add greyscale sheet handling here later
|
||||
if(!can_hold_material(material))
|
||||
return 0
|
||||
|
||||
//requested amount greater than available amount or just an invalid value
|
||||
stack_amt = min(floor(materials[material] / SHEET_MATERIAL_AMOUNT), stack_amt)
|
||||
if(stack_amt <= 0)
|
||||
return 0
|
||||
//auto drop location
|
||||
if(!target)
|
||||
var/atom/parent_atom = parent
|
||||
target = parent_atom.drop_location()
|
||||
if(!target)
|
||||
return 0
|
||||
|
||||
//eject sheets based on available amount after each iteration
|
||||
var/count = 0
|
||||
while(stack_amt > 0)
|
||||
var/type_to_retrieve = material.sheet_type || material.ore_type
|
||||
//don't merge yet. we need to do stuff with it first
|
||||
var/obj/item/stack/new_stack = new type_to_retrieve(target, min(stack_amt, MAX_STACK_SIZE), FALSE)
|
||||
if(istype(new_stack, /obj/item/stack/sheet))
|
||||
var/obj/item/stack/sheet/new_sheets = new_stack
|
||||
new_sheets.manufactured = TRUE
|
||||
count += new_stack.amount
|
||||
//use material & deduct work needed
|
||||
use_amount_mat(new_stack.amount * SHEET_MATERIAL_AMOUNT, material)
|
||||
stack_amt -= new_stack.amount
|
||||
//send signal
|
||||
SEND_SIGNAL(src, COMSIG_MATCONTAINER_STACK_RETRIEVED, new_stack, context, user_data)
|
||||
//no point merging anything into an already full stack
|
||||
if(new_stack.amount == new_stack.max_amount)
|
||||
continue
|
||||
//now we can merge since we are done with it
|
||||
for(var/obj/item/stack/item_stack in target)
|
||||
if(item_stack == new_stack || item_stack.type != type_to_retrieve) //don't merge with self or different type
|
||||
continue
|
||||
//speed merge
|
||||
var/merge_amount = min(item_stack.amount, new_stack.max_amount - new_stack.get_amount())
|
||||
item_stack.use(merge_amount)
|
||||
new_stack.add(merge_amount)
|
||||
break
|
||||
return count
|
||||
|
||||
/**
|
||||
* Proc to get all the materials and dump them as sheets
|
||||
*
|
||||
* Arguments:
|
||||
* - target: drop location of the sheets
|
||||
* - context: the atom which is ejecting the sheets. Used mostly in silo logging
|
||||
*/
|
||||
/datum/material_container/proc/retrieve_all(target = null, atom/context = parent)
|
||||
var/result = 0
|
||||
for(var/MAT in materials)
|
||||
result += retrieve_stack(amount2sheet(materials[MAT]), MAT, target, context, user_data = ID_DATA(null))
|
||||
return result
|
||||
//============================================================================================
|
||||
|
||||
|
||||
/datum/material_container/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["SHEET_MATERIAL_AMOUNT"] = SHEET_MATERIAL_AMOUNT
|
||||
return data
|
||||
|
||||
/// List format is list(material_name = list(amount = ..., ref = ..., etc.))
|
||||
/datum/material_container/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
for(var/datum/material/material as anything in materials)
|
||||
var/amount = materials[material]
|
||||
|
||||
data += list(list(
|
||||
"name" = material.name,
|
||||
"ref" = REF(material),
|
||||
"amount" = amount,
|
||||
"color" = material.greyscale_color || material.color
|
||||
))
|
||||
|
||||
return data
|
||||
|
||||
/**
|
||||
* Adds context sensitivy directly to the material container file for screentips
|
||||
* Arguments:
|
||||
* * source - refers to item that will display its screentip
|
||||
* * context - refers to, in this case, an item in the users hand hovering over the material container, such as an autolathe
|
||||
* * held_item - refers to the item that has materials accepted by the material container
|
||||
* * user - refers to user who will see the screentip when the proper context and tool are there
|
||||
*/
|
||||
/datum/material_container/proc/on_requesting_context_from_item(datum/source, list/context, obj/item/held_item, mob/living/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(isnull(held_item))
|
||||
return NONE
|
||||
if(!(mat_container_flags & MATCONTAINER_ANY_INTENT) && user.combat_mode)
|
||||
return NONE
|
||||
if(held_item.item_flags & ABSTRACT)
|
||||
return NONE
|
||||
if((held_item.flags_1 & HOLOGRAM_1) || (held_item.item_flags & NO_MAT_REDEMPTION) || (allowed_item_typecache && !is_type_in_typecache(held_item, allowed_item_typecache)))
|
||||
return NONE
|
||||
var/list/item_materials = held_item.get_material_composition(mat_container_flags)
|
||||
if(!length(item_materials))
|
||||
return NONE
|
||||
for(var/material in item_materials)
|
||||
if(can_hold_material(material))
|
||||
continue
|
||||
return NONE
|
||||
|
||||
if (isstack(held_item))
|
||||
context[SCREENTIP_CONTEXT_LMB] = "Insert stack"
|
||||
context[SCREENTIP_CONTEXT_RMB] = "Insert"
|
||||
|
||||
return CONTEXTUAL_SCREENTIP_SET
|
||||
|
||||
#undef MATERIAL_INSERT_ITEM_SUCCESS
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
This component allows machines to connect remotely to a material container
|
||||
(namely an /obj/machinery/ore_silo) elsewhere. It offers optional graceful
|
||||
fallback to a local material storage in case remote storage is unavailable, and
|
||||
handles linking back and forth.
|
||||
*/
|
||||
|
||||
/datum/remote_materials
|
||||
// Three possible states:
|
||||
// 1. silo exists, materials is parented to silo
|
||||
// 2. silo is null, materials is parented to parent
|
||||
// 3. silo is null, materials is null
|
||||
|
||||
///Movable this connection is attached to
|
||||
var/atom/movable/parent
|
||||
///The silo machine this container is connected to
|
||||
var/obj/machinery/ore_silo/silo
|
||||
///Material container. the value is either the silo or local
|
||||
var/datum/material_container/mat_container
|
||||
///Should we create a local storage if we can't connect to silo
|
||||
var/allow_standalone
|
||||
///Local size of container when silo = null
|
||||
var/local_size = INFINITY
|
||||
///Flags used for the local material container(exceptions for item insert & intent flags)
|
||||
var/mat_container_flags = NONE
|
||||
///List of signals to hook onto the local container
|
||||
var/list/mat_container_signals
|
||||
///Callback for round start silo connections. Have to cancel this if the parent gets destroyed before round start
|
||||
VAR_PRIVATE/datum/callback/connection
|
||||
|
||||
/datum/remote_materials/New(
|
||||
parent,
|
||||
mapload,
|
||||
allow_standalone = TRUE,
|
||||
force_connect = FALSE,
|
||||
mat_container_flags = NONE,
|
||||
list/mat_container_signals = null,
|
||||
)
|
||||
src.parent = parent
|
||||
|
||||
src.allow_standalone = allow_standalone
|
||||
src.mat_container_flags = mat_container_flags
|
||||
src.mat_container_signals = mat_container_signals
|
||||
|
||||
RegisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), PROC_REF(OnMultitool))
|
||||
|
||||
var/turf/T = get_turf(parent)
|
||||
var/connect_to_silo = FALSE
|
||||
if(force_connect || (mapload && is_station_level(T.z)))
|
||||
connect_to_silo = TRUE
|
||||
|
||||
if(mapload) // wait for silo to initialize during mapload
|
||||
connection = CALLBACK(src, PROC_REF(_PrepareStorage), connect_to_silo)
|
||||
SSticker.OnRoundstart(connection)
|
||||
else //directly register in round
|
||||
_PrepareStorage(connect_to_silo)
|
||||
|
||||
/**
|
||||
* Internal proc. prepares local storage if onnect_to_silo = FALSE
|
||||
*
|
||||
* Arguments
|
||||
* connect_to_silo- if true connect to global silo. If not successfull then go to local storage
|
||||
* only if allow_standalone = TRUE, else you a null mat_container
|
||||
*/
|
||||
/datum/remote_materials/proc/_PrepareStorage(connect_to_silo)
|
||||
PRIVATE_PROC(TRUE)
|
||||
|
||||
connection = null
|
||||
if (connect_to_silo)
|
||||
silo = GLOB.ore_silo_default
|
||||
if (silo)
|
||||
silo.connect_receptacle(src, parent)
|
||||
if(!(mat_container_flags & MATCONTAINER_NO_INSERT))
|
||||
RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_item_insert))
|
||||
RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION_SECONDARY, PROC_REF(on_secondary_insert))
|
||||
|
||||
if (!mat_container && allow_standalone)
|
||||
_MakeLocal()
|
||||
|
||||
/datum/remote_materials/Destroy()
|
||||
if(connection)
|
||||
LAZYREMOVE(SSticker.round_start_events, connection)
|
||||
connection = null
|
||||
if(silo)
|
||||
allow_standalone = FALSE
|
||||
disconnect()
|
||||
QDEL_NULL(mat_container)
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/remote_materials/proc/_MakeLocal()
|
||||
PRIVATE_PROC(TRUE)
|
||||
|
||||
silo = null
|
||||
|
||||
mat_container = new ( \
|
||||
parent, \
|
||||
SSmaterials.get_materials_by_flag(MATERIAL_SILO_STORED), \
|
||||
local_size, \
|
||||
mat_container_flags, \
|
||||
container_signals = mat_container_signals, \
|
||||
allowed_items = /obj/item/stack \
|
||||
)
|
||||
|
||||
/// Adds/Removes this connection from the silo
|
||||
/datum/remote_materials/proc/toggle_holding()
|
||||
if(isnull(silo))
|
||||
return
|
||||
|
||||
if(!silo.holds[src])
|
||||
silo.holds[src] = TRUE
|
||||
else
|
||||
silo.holds -= src
|
||||
|
||||
/**
|
||||
* Sets the storage size for local materials when not linked with silo
|
||||
* Arguments
|
||||
*
|
||||
* * size - the new size for local storage. measured in SHEET_MATERIAL_SIZE units
|
||||
*/
|
||||
/datum/remote_materials/proc/set_local_size(size)
|
||||
local_size = size
|
||||
if (!silo && mat_container)
|
||||
mat_container.max_amount = size
|
||||
|
||||
///Disconnects this component from the silo
|
||||
/datum/remote_materials/proc/disconnect()
|
||||
if(isnull(silo))
|
||||
return
|
||||
|
||||
UnregisterSignal(parent, list(COMSIG_ATOM_ITEM_INTERACTION, COMSIG_ATOM_ITEM_INTERACTION_SECONDARY))
|
||||
silo.disconnect_receptacle(src, parent)
|
||||
|
||||
if (allow_standalone)
|
||||
_MakeLocal()
|
||||
|
||||
/datum/remote_materials/proc/OnMultitool(datum/source, mob/user, obj/item/multitool/M)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
. = NONE
|
||||
if (!QDELETED(M.buffer) && istype(M.buffer, /obj/machinery/ore_silo))
|
||||
if (silo == M.buffer)
|
||||
to_chat(user, span_warning("[parent] is already connected to [silo]!"))
|
||||
return ITEM_INTERACT_BLOCKING
|
||||
if(!check_z_level(M.buffer))
|
||||
to_chat(user, span_warning("[parent] is too far away to get a connection signal!"))
|
||||
return ITEM_INTERACT_BLOCKING
|
||||
|
||||
var/obj/machinery/ore_silo/new_silo = M.buffer
|
||||
var/datum/material_container/new_container = new_silo.materials
|
||||
if (silo)
|
||||
silo.disconnect_receptacle(src, parent)
|
||||
else if (mat_container)
|
||||
//transfer all mats to silo. whatever cannot be transfered is dumped out as sheets
|
||||
if(mat_container.total_amount())
|
||||
for(var/datum/material/mat as anything in mat_container.materials)
|
||||
var/mat_amount = mat_container.materials[mat]
|
||||
if(!mat_amount || !new_container.has_space(mat_amount) || !new_container.can_hold_material(mat))
|
||||
continue
|
||||
new_container.materials[mat] += mat_amount
|
||||
mat_container.materials[mat] = 0
|
||||
qdel(mat_container)
|
||||
new_silo.connect_receptacle(src, parent)
|
||||
if(!(mat_container_flags & MATCONTAINER_NO_INSERT))
|
||||
RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_item_insert))
|
||||
RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION_SECONDARY, PROC_REF(on_secondary_insert))
|
||||
to_chat(user, span_notice("You connect [parent] to [silo] from the multitool's buffer."))
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
|
||||
/datum/remote_materials/proc/on_item_insert(datum/source, mob/living/user, obj/item/target)
|
||||
SIGNAL_HANDLER
|
||||
// Only insert stacks with left click
|
||||
if(isstack(target))
|
||||
return attempt_insert(user, target)
|
||||
|
||||
/datum/remote_materials/proc/on_secondary_insert(datum/source, mob/living/user, obj/item/target)
|
||||
SIGNAL_HANDLER
|
||||
return attempt_insert(user, target)
|
||||
|
||||
/// Insert mats into silo
|
||||
/datum/remote_materials/proc/attempt_insert(mob/living/user, obj/item/target)
|
||||
if(!(mat_container_flags & MATCONTAINER_ANY_INTENT) && user.combat_mode)
|
||||
return
|
||||
|
||||
if(silo)
|
||||
mat_container.user_insert(target, user, parent)
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
|
||||
/**
|
||||
* Checks if the param silo is in the same level as this components parent i.e. connected machine, rcd, etc
|
||||
*
|
||||
* Arguments
|
||||
* silo_to_check- Is this components parent in the same Z level as this param silo. If null
|
||||
* then check this components connected silo
|
||||
*
|
||||
* Returns true if both are on the station or same z level
|
||||
*/
|
||||
/datum/remote_materials/proc/check_z_level(obj/silo_to_check = silo)
|
||||
if(isnull(silo_to_check))
|
||||
return FALSE
|
||||
|
||||
return is_valid_z_level(get_turf(silo_to_check), get_turf(parent))
|
||||
|
||||
/// returns TRUE if this connection put on hold by the silo
|
||||
/datum/remote_materials/proc/on_hold()
|
||||
return check_z_level() ? silo.holds[src] : FALSE
|
||||
|
||||
/**
|
||||
* Check if this connection can use any materials from the silo
|
||||
* Returns true only if
|
||||
* - The parent is of type movable atom
|
||||
* - A mat container is actually present
|
||||
* - The silo in not on hold
|
||||
* Arguments
|
||||
* * check_hold - should we check if the silo is on hold
|
||||
* * user_data - in the form rendered by ID_DATA(user); used as a reference for silo bans and access checks
|
||||
*/
|
||||
/datum/remote_materials/proc/can_use_resource(check_hold = TRUE, alist/user_data)
|
||||
var/atom/movable/movable_parent = parent
|
||||
if (!istype(movable_parent))
|
||||
return FALSE
|
||||
if (!mat_container) //no silolink & local storage not supported
|
||||
movable_parent.say("No access to material storage, please contact the quartermaster.")
|
||||
return FALSE
|
||||
if(check_hold && on_hold()) //silo on hold
|
||||
movable_parent.say("Mineral access is on hold, please contact the quartermaster.")
|
||||
return FALSE
|
||||
if(SEND_SIGNAL(movable_parent, COMSIG_ORE_SILO_PERMISSION_CHECKED, user_data, movable_parent) & COMPONENT_ORE_SILO_DENY)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Use materials from either the silo(if connected) or from the local storage. If silo then this action
|
||||
* is logged else not e.g. action="build" & name="matter bin" means you are trying to build a matter bin
|
||||
*
|
||||
* Arguments
|
||||
* [mats][list]- list of materials to use
|
||||
* coefficient- each mat unit is scaled by this value then rounded. This value if usually your machine efficiency e.g. upgraded protolathe has reduced costs
|
||||
* multiplier- each mat unit is scaled by this value then rounded after it is scaled by coefficient. This value is your print quatity e.g. printing multiple items
|
||||
* action- For logging only. e.g. build, create, i.e. the action you are trying to perform
|
||||
* name- For logging only. the design you are trying to build e.g. matter bin, etc.
|
||||
* user_data - in the form rendered by ID_DATA(user), for material logging and (if this component is connected to a silo), permission checking
|
||||
*/
|
||||
/datum/remote_materials/proc/use_materials(list/mats, coefficient = 1, multiplier = 1, action = "processed", name = "design", alist/user_data)
|
||||
if(!can_use_resource(user_data = user_data))
|
||||
return 0
|
||||
|
||||
var/amount_consumed = mat_container.use_materials(mats, coefficient, multiplier)
|
||||
|
||||
if (silo)//log only if silo is linked
|
||||
var/list/scaled_mats = list()
|
||||
for(var/i in mats)
|
||||
scaled_mats[i] = OPTIMAL_COST(OPTIMAL_COST(mats[i] * coefficient) * multiplier)
|
||||
silo.silo_log(parent, action, -multiplier, name, scaled_mats, user_data)
|
||||
|
||||
return amount_consumed
|
||||
|
||||
/**
|
||||
* Ejects the given material ref and logs it
|
||||
*
|
||||
* Arguments
|
||||
* [material_ref][datum/material]- The material type you are trying to eject
|
||||
* eject_amount- how many sheets to eject
|
||||
* [drop_target][atom]- optional where to drop the sheets. null means it is dropped at this components parent location
|
||||
* user_data - in the form rendered by ID_DATA(user), for material logging and (if this component is connected to a silo), permission checking
|
||||
*/
|
||||
/datum/remote_materials/proc/eject_sheets(datum/material/material_ref, eject_amount, atom/drop_target = null, alist/user_data)
|
||||
if(!can_use_resource(user_data = user_data))
|
||||
return 0
|
||||
|
||||
var/atom/movable/movable_parent = parent
|
||||
if(isnull(drop_target))
|
||||
drop_target = movable_parent.drop_location()
|
||||
|
||||
return mat_container.retrieve_stack(eject_amount, material_ref, target = drop_target, context = parent, user_data = user_data)
|
||||
|
||||
/**
|
||||
* Insert an item into the mat container, helper proc to insert items with the correct context
|
||||
*
|
||||
* Arguments
|
||||
* * obj/item/weapon - the item you are trying to insert
|
||||
* * multiplier - the multiplier applied on the materials consumed
|
||||
* * user_data - an alist in the form rendered by ID_DATA(user), for logging who/where/when the item was inserted
|
||||
*/
|
||||
/datum/remote_materials/proc/insert_item(obj/item/weapon, multiplier = 1, alist/user_data)
|
||||
// Inserting materials automatically shouldn't be permission-restricted
|
||||
if(!islist(user_data))
|
||||
user_data = ID_DATA(null)
|
||||
user_data[SILICON_OVERRIDE] = SILICON_OVERRIDE
|
||||
if(!can_use_resource(FALSE, user_data))
|
||||
return MATERIAL_INSERT_ITEM_FAILURE
|
||||
|
||||
return mat_container.insert_item(weapon, multiplier, parent, user_data = user_data)
|
||||
Reference in New Issue
Block a user