Files
GhomandGitHub a28575aa82 [NO GBP] Fixing more issues with sand (you can make sand walls again, also ghost glass sheets?) (#93215)
## About The Pull Request
Apparently wall construction code is snowflaked and indented as fuck
(and the same goes for door assemblies). I'm not bothering refactoring
everything with them, only to reduce the indentation, changing a couple
vars and overall making it easier to work with them later. This includes
wall construction not being hardcoded to sheets but include the
possibility to use other kind of stacks as well (if you don't count the
snowflake interaction with iron rods). In layman's terms, this means you
can make walls made out of sand (distinct from sandstone) again.

Also I've done some small changes to the materials storage, so that it
can eject ores too if the material doesn't have a sheet type.

Also, I've been told there may be issues with broken, uninteractable
(probably not properly initialized) glass sheets beside the ORM. I'm not
100% sure about the deets, but it may have something to do with spawning
the glass on the same turf the ORM is listening to, when smelting sand,
causing some race conditions, so let's spawn it in nullspace

## Why It's Good For The Game
While I'm sure there may be more elegant solutions (just take a look at
the wall and door construction code, they both use text2path oh god!),
I'm just here to make things a lil' cleaner and be done with issues with
the fact that sand is made of sand.

## Changelog

🆑
fix: You can once again make sand walls.
fix: Deconstructing an autolathe with sand in it should now drop sand.
/🆑
2025-10-02 19:12:11 +03:00

409 lines
14 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
/// List of components that need to be packed along with the circuitboard
var/list/obj/item/flatpacked_components = list()
/// 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)
QDEL_LIST(flatpacked_components)
. = ..()
/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"
if(!isnull(held_item) && (held_item.type in inserted_board.flatpack_components))
context[SCREENTIP_CONTEXT_LMB] = "Insert flatpack component"
return CONTEXTUAL_SCREENTIP_SET
. = 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")]")
if(length(inserted_board.flatpack_components))
var/list/obj/item/to_insert
for(var/obj/item/component as anything in inserted_board.flatpack_components)
var/inserted = get_flatpack_component_count(component)
var/required = inserted_board.req_components[component]
if(inserted == required)
continue
LAZYADDASSOC(to_insert, get_flatpack_component_name(component), "[inserted]/[required]")
if(length(to_insert))
. += span_warning("The following components must be inserted by hand before packaging")
for(var/component_name in to_insert)
. += span_warning("[component_name]:[to_insert[component_name]]")
/obj/machinery/flatpacker/update_overlays()
. = ..()
if(!QDELETED(inserted_board))
. += mutable_appearance(icon, "[base_icon_state]_c")
/**
* Returns the name of this component. Vending canistors & maybe other types in the future require special parsing
*
* Arguments
* * obj/item/component - the component typepath we are trying to get the name
*/
/obj/machinery/flatpacker/proc/get_flatpack_component_name(obj/item/component)
PRIVATE_PROC(TRUE)
if(ispath(component, /obj/item/vending_refill))
var/obj/item/vending_refill/canister = component
return "\improper [canister::machine_name] restocking unit"
return component::name
/**
* Returns count of inserted flatpack component parts
*
* Arguments
* * obj/item/type - the component type we are trying to count
*/
/obj/machinery/flatpacker/proc/get_flatpack_component_count(obj/item/type)
PRIVATE_PROC(TRUE)
. = 0
for(var/obj/item/test as anything in flatpacked_components)
if(test.type == type)
. += 1
/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)
if(gone in flatpacked_components)
flatpacked_components -= gone
/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
* * count - the number of parts to compute the cost of
*/
/obj/machinery/flatpacker/proc/analyze_cost(part_type, costs, count)
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] * count)
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 in inserted_board.req_components)
//these don't count to the final cost as they have to inserted manually
if(type in inserted_board.flatpack_components)
continue
needed_mats = analyze_cost(type, needed_mats, inserted_board.req_components[type])
// 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
else if(!QDELETED(inserted_board) && (attacking_item.type in inserted_board.flatpack_components))
if(get_flatpack_component_count(attacking_item.type) == inserted_board.req_components[attacking_item.type])
balloon_alert(user, "max count reached!")
return ITEM_INTERACT_BLOCKING
if(!user.transferItemToLoc(attacking_item, src))
to_chat(user, span_warning("[attacking_item] is stuck in hand!"))
return ITEM_INTERACT_BLOCKING
LAZYADD(flatpacked_components, attacking_item)
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_batched/sheetmaterials),
get_asset_datum(/datum/asset/spritesheet_batched/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 = ""
if(print_tier > max_part_tier)
disableReason = "This design is too advanced for this machine. "
else if(!materials.has_materials(needed_mats, creation_efficiency))
disableReason = "Not enough materials. "
else
for(var/obj/item/component as anything in inserted_board.flatpack_components)
var/diff = inserted_board.req_components[component] - get_flatpack_component_count(component)
if(diff)
disableReason = "Please insert [diff] [get_flatpack_component_name(component)]"
break
design = list(
"name" = initial(build.name),
"requiredMaterials" = cost_mats,
"icon" = icon2base64(icon(initial(build.icon), initial(build.icon_state), frame = 1)),
"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
for(var/obj/item/component as anything in inserted_board.flatpack_components)
if(inserted_board.req_components[component] != get_flatpack_component_count(component))
say("Not enough [get_flatpack_component_name(component)].")
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_stack(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)
var/obj/item/flatpack/box = new (drop_location(), board)
for(var/obj/item/component as anything in flatpacked_components)
component.forceMove(box)
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)
var/drop = drop_location()
for(var/obj/item/component as anything in flatpacked_components)
component.forceMove(drop)
return CLICK_ACTION_SUCCESS
#undef CREATE_AND_INCREMENT