mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-27 17:41:50 +00:00
Adds a mail sorting unit! The unit accepts mail, stores it and sorts it per department. It can also search for individual envelopes, change it's output tile and be VERY loud. At the meantime, this PR also adds two flatpack subtypes - flatpacker subtype and mail sorter subtype. The first one is intended to be used by mappers to have a flatpacker be accessible roundstart, and the second one is now sold at cargo wardrobes to make the mail sorter also available roundstart.
486 lines
16 KiB
Plaintext
486 lines
16 KiB
Plaintext
///Incremets an an value assosiated by an key in the list creating that value if nessassary
|
|
#define CREATE_AND_INCREMENT(L, I, increment) if(!(I in L)) { L[I] = 0; } L[I] += increment;
|
|
|
|
/obj/machinery/flatpacker
|
|
name = "flatpacker"
|
|
desc = "It produces items using iron, glass, plastic and maybe some more."
|
|
icon = 'icons/obj/machines/lathes.dmi'
|
|
base_icon_state = "flatpacker"
|
|
icon_state = "flatpacker"
|
|
density = TRUE
|
|
active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION
|
|
circuit = /obj/item/circuitboard/machine/flatpacker
|
|
|
|
/// Are we busy printing?
|
|
var/busy = FALSE
|
|
/// Coefficient applied to consumed materials. Lower values result in lower material consumption.
|
|
var/creation_efficiency = 2
|
|
///The container to hold materials
|
|
var/datum/component/material_container/materials
|
|
/// The inserted board
|
|
var/obj/item/circuitboard/machine/inserted_board
|
|
/// Materials needed to print this board
|
|
var/list/needed_mats = list()
|
|
/// The highest tier of this board
|
|
var/print_tier = 1
|
|
/// Our max print tier
|
|
var/max_part_tier = 1
|
|
/// time needed to produce a flatpacked machine
|
|
var/flatpack_time = 4.5 SECONDS
|
|
|
|
/obj/machinery/flatpacker/Initialize(mapload)
|
|
register_context()
|
|
|
|
materials = AddComponent( \
|
|
/datum/component/material_container, \
|
|
SSmaterials.materials_by_category[MAT_CATEGORY_SILO], \
|
|
0, \
|
|
MATCONTAINER_EXAMINE, \
|
|
container_signals = list(COMSIG_MATCONTAINER_ITEM_CONSUMED = TYPE_PROC_REF(/obj/machinery/flatpacker, AfterMaterialInsert)) \
|
|
)
|
|
|
|
return ..()
|
|
|
|
/obj/machinery/flatpacker/Destroy()
|
|
materials = null
|
|
QDEL_NULL(inserted_board)
|
|
. = ..()
|
|
|
|
/obj/machinery/flatpacker/add_context(atom/source, list/context, obj/item/held_item, mob/user)
|
|
. = NONE
|
|
if(!QDELETED(inserted_board))
|
|
context[SCREENTIP_CONTEXT_CTRL_LMB] = "Eject board"
|
|
. = CONTEXTUAL_SCREENTIP_SET
|
|
|
|
if(!isnull(held_item))
|
|
if(istype(held_item, /obj/item/circuitboard/machine))
|
|
context[SCREENTIP_CONTEXT_LMB] = "Insert board"
|
|
return CONTEXTUAL_SCREENTIP_SET
|
|
else if(held_item.tool_behaviour == TOOL_SCREWDRIVER)
|
|
context[SCREENTIP_CONTEXT_LMB] = "[panel_open ? "Close" : "Open"] panel"
|
|
return CONTEXTUAL_SCREENTIP_SET
|
|
else if(held_item.tool_behaviour == TOOL_CROWBAR && panel_open)
|
|
context[SCREENTIP_CONTEXT_LMB] = "Deconstruct"
|
|
return CONTEXTUAL_SCREENTIP_SET
|
|
|
|
/obj/machinery/flatpacker/examine(mob/user)
|
|
. += ..()
|
|
if(!in_range(user, src) && !isobserver(user))
|
|
return
|
|
|
|
. += span_notice("The status display reads:")
|
|
. += span_notice("Capable of packing up to <b>Tier [max_part_tier]</b>.")
|
|
. += span_notice("Storing up to <b>[materials.max_amount]</b> material units.")
|
|
. += span_notice("Material consumption at <b>[creation_efficiency * 100]%</b>")
|
|
|
|
. += span_notice("Its maintainence panel can be [EXAMINE_HINT("screwed")] [panel_open ? "close" : "open"]")
|
|
if(panel_open)
|
|
. += span_notice("It can be [EXAMINE_HINT("pried")] apart")
|
|
if(!QDELETED(inserted_board))
|
|
. += span_notice("The board can be ejected via [EXAMINE_HINT("Ctrl Click")]")
|
|
|
|
/obj/machinery/flatpacker/update_overlays()
|
|
. = ..()
|
|
|
|
if(!QDELETED(inserted_board))
|
|
. += mutable_appearance(icon, "[base_icon_state]_c")
|
|
|
|
/obj/machinery/flatpacker/Exited(atom/movable/gone, direction)
|
|
. = ..()
|
|
if(gone == inserted_board)
|
|
inserted_board = null
|
|
needed_mats.Cut()
|
|
print_tier = 1
|
|
update_appearance(UPDATE_OVERLAYS)
|
|
|
|
/obj/machinery/flatpacker/RefreshParts()
|
|
. = ..()
|
|
|
|
var/mat_capacity = 0
|
|
for(var/datum/stock_part/matter_bin/new_matter_bin in component_parts)
|
|
mat_capacity += new_matter_bin.tier * 25 * SHEET_MATERIAL_AMOUNT
|
|
materials.max_amount = mat_capacity
|
|
|
|
var/datum/stock_part/servo/servo = locate() in component_parts
|
|
max_part_tier = servo.tier
|
|
flatpack_time = initial(flatpack_time) - servo.tier / 2 // T4 = 2 seconds off
|
|
var/efficiency = initial(creation_efficiency)
|
|
for(var/datum/stock_part/micro_laser/laser in component_parts)
|
|
efficiency -= laser.tier * 0.2
|
|
creation_efficiency = max(1.2, efficiency)
|
|
|
|
/obj/machinery/flatpacker/proc/AfterMaterialInsert(container, obj/item/item_inserted, last_inserted_id, mats_consumed, amount_inserted, atom/context)
|
|
SIGNAL_HANDLER
|
|
|
|
//we use initial(active_power_usage) because higher tier parts will have higher active usage but we have no benefit from it
|
|
if(directly_use_energy(ROUND_UP((amount_inserted / (MAX_STACK_SIZE * SHEET_MATERIAL_AMOUNT)) * 0.4 * initial(active_power_usage))))
|
|
flick_overlay_view(mutable_appearance('icons/obj/machines/lathes.dmi', "flatpacker_bar"), 1 SECONDS)
|
|
|
|
var/datum/material/highest_mat_ref
|
|
var/highest_mat = 0
|
|
for(var/datum/material/mat as anything in mats_consumed)
|
|
var/present_mat = mats_consumed[mat]
|
|
if(present_mat > highest_mat)
|
|
highest_mat = present_mat
|
|
highest_mat_ref = mat
|
|
|
|
flick_overlay_view(material_insertion_animation(highest_mat_ref), 1 SECONDS)
|
|
|
|
/**
|
|
* Attempts to find the total material cost of a typepath (including our creation efficiency), modifying a list
|
|
* The list is modified as an assoc list: Material datum typepath = Cost
|
|
* If the type is found on a techweb, uses material costs from there
|
|
* Otherwise, the typepath is created in nullspace and fetches materials from the initialized one, then deleted.
|
|
*
|
|
* Args:
|
|
* part_type - Typepath of the item we are trying to find the costs of
|
|
* costs - Assoc list we modify and return
|
|
*/
|
|
/obj/machinery/flatpacker/proc/analyze_cost(part_type, costs)
|
|
PRIVATE_PROC(TRUE)
|
|
|
|
var/comp_type = part_type
|
|
if(ispath(part_type, /datum/stock_part))
|
|
var/datum/stock_part/as_part = part_type
|
|
comp_type = initial(as_part.physical_object_type)
|
|
if(as_part.tier > print_tier)
|
|
print_tier = as_part.tier
|
|
|
|
var/list/mat_list
|
|
var/obj/item/null_comp
|
|
if(!isnull(SSresearch.item_to_design[comp_type]))
|
|
mat_list = SSresearch.item_to_design[comp_type][1].materials
|
|
else
|
|
var/datum/stock_part/part = GLOB.stock_part_datums_per_object[comp_type]
|
|
if(part)
|
|
mat_list = part.physical_object_reference.custom_materials
|
|
else
|
|
null_comp = new comp_type
|
|
mat_list = null_comp.custom_materials
|
|
|
|
for(var/atom/mat as anything in mat_list)
|
|
CREATE_AND_INCREMENT(costs, mat.type, mat_list[mat] * inserted_board.req_components[part_type])
|
|
|
|
if(null_comp)
|
|
qdel(null_comp)
|
|
return costs
|
|
|
|
/obj/machinery/flatpacker/base_item_interaction(mob/living/user, obj/item/attacking_item, list/modifiers)
|
|
if(attacking_item.flags_1 & HOLOGRAM_1 || attacking_item.item_flags & ABSTRACT)
|
|
return ITEM_INTERACT_SKIP_TO_ATTACK
|
|
|
|
if(istype(attacking_item, /obj/item/circuitboard/machine))
|
|
if(busy)
|
|
balloon_alert(user, "busy!")
|
|
return ITEM_INTERACT_BLOCKING
|
|
if (!user.transferItemToLoc(attacking_item, src))
|
|
return ITEM_INTERACT_BLOCKING
|
|
|
|
// If insertion was successful and there's already a diskette in the console, eject the old one.
|
|
if(inserted_board)
|
|
inserted_board.forceMove(drop_location())
|
|
inserted_board = attacking_item
|
|
|
|
//compute the needed mats from its stock parts
|
|
for(var/type as anything in inserted_board.req_components)
|
|
needed_mats = analyze_cost(type, needed_mats)
|
|
|
|
// 5 sheets of iron and 5 of cable coil
|
|
CREATE_AND_INCREMENT(needed_mats, /datum/material/iron, (SHEET_MATERIAL_AMOUNT * 5 + (SHEET_MATERIAL_AMOUNT / 20)))
|
|
CREATE_AND_INCREMENT(needed_mats, /datum/material/glass, (SHEET_MATERIAL_AMOUNT / 20))
|
|
|
|
update_appearance(UPDATE_OVERLAYS)
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
return ..()
|
|
|
|
/obj/machinery/flatpacker/screwdriver_act(mob/living/user, obj/item/tool)
|
|
. = ITEM_INTERACT_BLOCKING
|
|
if(default_deconstruction_screwdriver(user, "[base_icon_state]_o", base_icon_state, tool))
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
/obj/machinery/flatpacker/crowbar_act(mob/living/user, obj/item/tool)
|
|
. = ITEM_INTERACT_BLOCKING
|
|
if(default_deconstruction_crowbar(tool))
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
/obj/machinery/flatpacker/ui_interact(mob/user, datum/tgui/ui)
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if(!ui)
|
|
ui = new(user, src, "Flatpacker")
|
|
ui.open()
|
|
|
|
/obj/machinery/flatpacker/ui_assets(mob/user)
|
|
return list(
|
|
get_asset_datum(/datum/asset/spritesheet/sheetmaterials),
|
|
get_asset_datum(/datum/asset/spritesheet/research_designs),
|
|
)
|
|
|
|
/obj/machinery/flatpacker/ui_static_data(mob/user)
|
|
return materials.ui_static_data()
|
|
|
|
/obj/machinery/flatpacker/ui_data(mob/user)
|
|
. = list()
|
|
|
|
.["materials"] = materials.ui_data()
|
|
.["busy"] = busy
|
|
|
|
var/list/design
|
|
if(!QDELETED(inserted_board))
|
|
var/list/cost_mats = list()
|
|
for(var/datum/material/mat_type as anything in needed_mats)
|
|
var/list/new_entry = list()
|
|
new_entry["name"] = initial(mat_type.name)
|
|
new_entry["amount"] = OPTIMAL_COST(needed_mats[mat_type] * creation_efficiency)
|
|
cost_mats += list(new_entry)
|
|
|
|
var/atom/build = initial(inserted_board.build_path)
|
|
|
|
var/disableReason = ""
|
|
var/has_materials = materials.has_materials(needed_mats, creation_efficiency)
|
|
if(!has_materials)
|
|
disableReason += "Not enough materials. "
|
|
if(print_tier > max_part_tier)
|
|
disableReason += "This design is too advanced for this machine. "
|
|
design = list(
|
|
"name" = initial(build.name),
|
|
"requiredMaterials" = cost_mats,
|
|
"icon" = icon2base64(icon(initial(build.icon), initial(build.icon_state), frame = 1)),
|
|
"canPrint" = has_materials && print_tier <= max_part_tier,
|
|
"disableReason" = disableReason
|
|
)
|
|
.["design"] = design
|
|
|
|
/obj/machinery/flatpacker/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
|
|
switch(action)
|
|
if("build")
|
|
if(busy)
|
|
return
|
|
|
|
if(QDELETED(inserted_board))
|
|
return
|
|
if(print_tier > max_part_tier)
|
|
say("Design too complex.")
|
|
return
|
|
if(!materials.has_materials(needed_mats, creation_efficiency))
|
|
say("Not enough materials to begin production.")
|
|
return
|
|
playsound(src, 'sound/items/tools/rped.ogg', 50, TRUE)
|
|
|
|
busy = TRUE
|
|
flick_overlay_view(mutable_appearance('icons/obj/machines/lathes.dmi', "flatpacker_bar"), flatpack_time)
|
|
addtimer(CALLBACK(src, PROC_REF(finish_build), inserted_board), flatpack_time)
|
|
return TRUE
|
|
|
|
if("ejectBoard")
|
|
try_put_in_hand(inserted_board, ui.user)
|
|
return TRUE
|
|
|
|
if("eject")
|
|
var/datum/material/material = locate(params["ref"])
|
|
if(!istype(material))
|
|
return
|
|
|
|
var/amount = params["amount"]
|
|
if(isnull(amount))
|
|
return
|
|
|
|
amount = text2num(amount)
|
|
if(isnull(amount))
|
|
return
|
|
|
|
//we use initial(active_power_usage) because higher tier parts will have higher active usage but we have no benefit from it
|
|
if(!directly_use_energy(ROUND_UP((amount / MAX_STACK_SIZE) * 0.4 * initial(active_power_usage))))
|
|
say("No power to dispense sheets")
|
|
return
|
|
|
|
materials.retrieve_sheets(amount, material)
|
|
return TRUE
|
|
|
|
/**
|
|
* Turns the supplied board into a flatpack, and sets the machine as not busy
|
|
* Arguments
|
|
*
|
|
* * board - the board to put inside the flatpack
|
|
*/
|
|
/obj/machinery/flatpacker/proc/finish_build(board)
|
|
PRIVATE_PROC(TRUE)
|
|
|
|
busy = FALSE
|
|
|
|
materials.use_materials(needed_mats, creation_efficiency)
|
|
new /obj/item/flatpack(drop_location(), board)
|
|
|
|
SStgui.update_uis(src)
|
|
|
|
/obj/machinery/flatpacker/click_ctrl(mob/user)
|
|
if(QDELETED(inserted_board) || busy)
|
|
return CLICK_ACTION_BLOCKING
|
|
|
|
try_put_in_hand(inserted_board, user)
|
|
|
|
return CLICK_ACTION_SUCCESS
|
|
|
|
#undef CREATE_AND_INCREMENT
|
|
|
|
/obj/item/flatpack
|
|
name = "flatpack"
|
|
desc = "A box containing a compactly packed machine. Use multitool to deploy."
|
|
icon = 'icons/obj/devices/circuitry_n_data.dmi'
|
|
icon_state = "flatpack"
|
|
density = TRUE
|
|
w_class = WEIGHT_CLASS_HUGE //cart time
|
|
throw_range = 2
|
|
item_flags = SLOWS_WHILE_IN_HAND | IMMUTABLE_SLOW
|
|
slowdown = 2.5
|
|
drag_slowdown = 3.5 //use the cart stupid
|
|
|
|
/// The board we deploy
|
|
var/obj/item/circuitboard/machine/board
|
|
|
|
/obj/item/flatpack/Initialize(mapload, obj/item/circuitboard/machine/new_board)
|
|
if(isnull(board) && isnull(new_board))
|
|
return INITIALIZE_HINT_QDEL //how
|
|
|
|
. = ..()
|
|
|
|
var/static/list/tool_behaviors = list(
|
|
TOOL_MULTITOOL = list(
|
|
SCREENTIP_CONTEXT_LMB = "Deploy",
|
|
),
|
|
)
|
|
AddElement(/datum/element/contextual_screentip_tools, tool_behaviors)
|
|
|
|
board = !isnull(new_board) ? new_board : new board(src) // i got board
|
|
if(board.loc != src)
|
|
board.forceMove(src)
|
|
var/obj/machinery/build = initial(board.build_path)
|
|
name += " ([initial(build.name)])"
|
|
|
|
/obj/item/flatpack/Destroy()
|
|
QDEL_NULL(board)
|
|
. = ..()
|
|
|
|
/obj/item/flatpack/examine(mob/user)
|
|
. = ..()
|
|
if(!in_range(user, src) && !isobserver(user))
|
|
return
|
|
|
|
if(loc == user)
|
|
. += span_warning("You can't deploy while holding it in your hand.")
|
|
else if(isturf(loc))
|
|
var/turf/location = loc
|
|
if(!isopenturf(location))
|
|
. += span_warning("Can't deploy in this location")
|
|
else if(location.is_blocked_turf(source_atom = src))
|
|
. += span_warning("No space for deployment")
|
|
|
|
/obj/item/flatpack/multitool_act(mob/living/user, obj/item/tool)
|
|
. = NONE
|
|
|
|
if(isnull(board))
|
|
return ITEM_INTERACT_BLOCKING
|
|
if(loc == user)
|
|
balloon_alert(user, "can't deploy in hand")
|
|
return ITEM_INTERACT_BLOCKING
|
|
else if(isturf(loc))
|
|
var/turf/location = loc
|
|
if(!isopenturf(location))
|
|
balloon_alert(user, "can't deploy here")
|
|
return ITEM_INTERACT_BLOCKING
|
|
else if(location.is_blocked_turf(source_atom = src))
|
|
balloon_alert(user, "no space for deployment")
|
|
return ITEM_INTERACT_BLOCKING
|
|
balloon_alert_to_viewers("deploying!")
|
|
if(!do_after(user, 1 SECONDS, target = src))
|
|
return ITEM_INTERACT_BLOCKING
|
|
|
|
new /obj/effect/temp_visual/mook_dust(loc)
|
|
var/obj/machinery/new_machine = new board.build_path(loc)
|
|
loc.visible_message(span_warning("[src] deploys!"))
|
|
playsound(src, 'sound/machines/terminal/terminal_eject.ogg', 70, TRUE)
|
|
new_machine.on_construction(user)
|
|
qdel(src)
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
///Maximum number of flatpacks in a cart
|
|
#define MAX_FLAT_PACKS 3
|
|
|
|
/obj/structure/flatpack_cart
|
|
name = "flatpack cart"
|
|
desc = "A cart specifically made to hold flatpacks from a flatpacker, evenly distributing weight. Convenient!"
|
|
icon = 'icons/obj/structures.dmi'
|
|
icon_state = "flatcart"
|
|
density = TRUE
|
|
opacity = FALSE
|
|
|
|
/obj/structure/flatpack_cart/Initialize(mapload)
|
|
. = ..()
|
|
|
|
register_context()
|
|
|
|
AddElement(/datum/element/noisy_movement, volume = 45) // i hate noise
|
|
|
|
/obj/structure/flatpack_cart/atom_deconstruct(disassembled)
|
|
for(var/atom/movable/content as anything in contents)
|
|
content.forceMove(drop_location())
|
|
|
|
/obj/structure/flatpack_cart/add_context(atom/source, list/context, obj/item/held_item, mob/user)
|
|
. = NONE
|
|
if(isnull(held_item))
|
|
return
|
|
|
|
if(istype(held_item, /obj/item/flatpack))
|
|
context[SCREENTIP_CONTEXT_LMB] = "Load pack"
|
|
return CONTEXTUAL_SCREENTIP_SET
|
|
|
|
/obj/structure/flatpack_cart/examine(mob/user)
|
|
. = ..()
|
|
if(!in_range(user, src) && !isobserver(user))
|
|
return
|
|
|
|
. += "From bottom to top, this cart contains:"
|
|
for(var/obj/item/flatpack as anything in contents)
|
|
. += flatpack.name
|
|
|
|
/obj/structure/flatpack_cart/update_overlays()
|
|
. = ..()
|
|
|
|
var/offset = 0
|
|
for(var/item in contents)
|
|
var/mutable_appearance/flatpack_overlay = mutable_appearance(icon, "flatcart_flat", layer = layer + (offset * 0.01))
|
|
flatpack_overlay.pixel_y = offset
|
|
offset += 4
|
|
. += flatpack_overlay
|
|
|
|
/obj/structure/flatpack_cart/attack_hand(mob/user, list/modifiers)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
user.put_in_hands(contents[length(contents)]) //topmost box
|
|
update_appearance(UPDATE_OVERLAYS)
|
|
|
|
/obj/structure/flatpack_cart/item_interaction(mob/living/user, obj/item/attacking_item, params)
|
|
if(!istype(attacking_item, /obj/item/flatpack) || user.combat_mode || attacking_item.flags_1 & HOLOGRAM_1 || attacking_item.item_flags & ABSTRACT)
|
|
return ITEM_INTERACT_SKIP_TO_ATTACK
|
|
|
|
if (length(contents) >= MAX_FLAT_PACKS)
|
|
balloon_alert(user, "full!")
|
|
return ITEM_INTERACT_BLOCKING
|
|
if (!user.transferItemToLoc(attacking_item, src))
|
|
return ITEM_INTERACT_BLOCKING
|
|
update_appearance(UPDATE_OVERLAYS)
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
#undef MAX_FLAT_PACKS
|
|
|
|
/obj/item/flatpack/flatpacker // a roundstart flatpacker is NICE you can gahdamn tell the time and everythin'
|
|
board = /obj/item/circuitboard/machine/flatpacker
|
|
|
|
/obj/item/flatpack/mailsorter // to have a roundstart mail sorter at cargo
|
|
board = /obj/item/circuitboard/machine/mailsorter
|