General maintenance for Lathes (#81244)

## About The Pull Request
1. **Qol Stuff**
- Screentips & examines for screwdriver, crowbar acts, multiool &
wirecutter Also for Alt click
- Techfabs can now also use the Mouse drag functionality to set drop
target for items
- Lathe printing animation now plays on loop instead of just flicking
once till printing is finished for more visual feedback

2. **Code Improvements**
- Merged `start_making()` with `do_make_item()`. That proc was like only
3 lines long and used only in 1 place so let's just move that code to
`ui_act()`
- Merged `user_print_item_id()` with `ui_act()`. Again was used only in
1 place so let's just move that code in to save some proc overhead
- Sets `processing_flags` for autolathe to `NONE` cause we don't use
`process()`
    - Autodocs vars such as `hacked` , `shocked` etc & procs
- `maxmult` is now computed client side saving backend bandwidth,
`construction_time` is removed from lathes which did not use it
- Removed all usages of lathe taxes and their related vars, removed
engineering lathe no tax from ice moon, replaced with normal engineering
lathe

3. **Fixes**
- Lathe sheet insertion animations are now linked & work again for all
material types inserted via remote silo/local storage,
silver/titanium/plastic all play the same animation(that is
`protolathe_shiny` overlay). Other materials have their own respective
overlays
- Fixes #81243. Calling `update_static_data_for_all_viewers()` is too
expensive for the UI. We should instead use `SStgui.update_uis(src)`
which will report the `busy` status to the UI more immediatly
- Fixes #81236. Some problems with the params passed to the timer
callback. It should now print the correct number of requested items
- Fixes #81192. `design.materials` would runtime for custom material
items as they were list of texts not materials. We have to pass our
manually parsed list of materials for an specific item to ensure they
are set & used correctly. Same fixes apply for techfabs as well


## Changelog
🆑
qol: adds screentips & examines for screwdriver & crowbar acts & alt
click.
qol: techfabs can now use the mouse drop functionality to set drop
target.
qol: lathe printing animation plays on loop while printing rather than
flicking once for more visual feedback
fix: lathe sheet insertion animations are now linked & work again for
all material types inserted via remote silo/local storage
fix: printing custom materials items from autolathe works again.
fix: printing multiple items from lathes will actually print that
correct quantity of items requested.
fix: printing items the 2nd time around from lathes won't cause the UI
to reload each time.
code: autodoc for some vars & procs, merges procs.
refactor: Optimized code for autolathe & techfabs in general. Report
bugs on github
/🆑

---------

Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
This commit is contained in:
SyncIt21
2024-02-08 13:34:03 +01:00
committed by GitHub
co-authored by MrMelbert Ghom
parent 59796f050d
commit e4b23f2b4b
18 changed files with 626 additions and 413 deletions
+217 -163
View File
@@ -7,24 +7,24 @@
active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * 0.5
circuit = /obj/item/circuitboard/machine/autolathe
layer = BELOW_OBJ_LAYER
processing_flags = NONE
///Is the autolathe hacked via wiring
var/hacked = FALSE
///Is the autolathe disabled via wiring
var/disabled = FALSE
///Did we recently shock a mob who medled with the wiring
var/shocked = FALSE
///Are we currently printing something
var/busy = FALSE
/// Coefficient applied to consumed materials. Lower values result in lower material consumption.
///Coefficient applied to consumed materials. Lower values result in lower material consumption.
var/creation_efficiency = 1.6
var/datum/design/being_built
///Designs related to the autolathe
var/datum/techweb/autounlocking/stored_research
///Designs imported from technology disks that we can print.
var/list/imported_designs = list()
///The container to hold materials
var/datum/component/material_container/materials
///direction we output onto (if 0, on top of us)
var/drop_direction = 0
@@ -43,17 +43,69 @@
GLOB.autounlock_techwebs[/datum/techweb/autounlocking/autolathe] = new /datum/techweb/autounlocking/autolathe
stored_research = GLOB.autounlock_techwebs[/datum/techweb/autounlocking/autolathe]
register_context()
/obj/machinery/autolathe/Destroy()
materials = null
QDEL_NULL(wires)
return ..()
/obj/machinery/autolathe/examine(mob/user)
. = ..()
if(!in_range(user, src) && !isobserver(user))
return
. += span_notice("Material usage cost at <b>[creation_efficiency * 100]%</b>")
if(drop_direction)
. += span_notice("Currently configured to drop printed objects <b>[dir2text(drop_direction)]</b>.")
. += span_notice("[EXAMINE_HINT("Alt-click")] to reset.")
else
. += span_notice("[EXAMINE_HINT("Drag")] towards a direction (while next to it) to change drop direction.")
. += span_notice("Its maintainence panel can be [EXAMINE_HINT("screwed")] [panel_open ? "closed" : "open"].")
if(panel_open)
. += span_notice("The machine can be [EXAMINE_HINT("pried")] apart.")
/obj/machinery/autolathe/add_context(atom/source, list/context, obj/item/held_item, mob/user)
if(drop_direction)
context[SCREENTIP_CONTEXT_ALT_LMB] = "Reset Drop"
return CONTEXTUAL_SCREENTIP_SET
if(isnull(held_item))
return NONE
if(held_item.tool_behaviour == TOOL_SCREWDRIVER)
context[SCREENTIP_CONTEXT_RMB] = "[panel_open ? "Close" : "Open"] Panel"
return CONTEXTUAL_SCREENTIP_SET
if(panel_open && held_item.tool_behaviour == TOOL_CROWBAR)
context[SCREENTIP_CONTEXT_LMB] = "Deconstruct"
return CONTEXTUAL_SCREENTIP_SET
/obj/machinery/autolathe/crowbar_act(mob/living/user, obj/item/tool)
. = ITEM_INTERACT_BLOCKING
if(default_deconstruction_crowbar(tool))
return ITEM_INTERACT_SUCCESS
/obj/machinery/autolathe/screwdriver_act_secondary(mob/living/user, obj/item/tool)
. = ITEM_INTERACT_BLOCKING
if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", tool))
return ITEM_INTERACT_SUCCESS
/obj/machinery/autolathe/proc/AfterMaterialInsert(container, obj/item/item_inserted, last_inserted_id, mats_consumed, amount_inserted, atom/context)
SIGNAL_HANDLER
flick("autolathe_[item_inserted.has_material_type(/datum/material/glass) ? "r" : "o"]", src)
directly_use_power(round((amount_inserted / SHEET_MATERIAL_AMOUNT) * active_power_usage * 0.0025))
/obj/machinery/autolathe/ui_interact(mob/user, datum/tgui/ui)
if(!is_operational)
return
if(shocked && !(machine_stat & NOPOWER))
shock(user, 50)
return
ui = SStgui.try_update_ui(user, src, ui)
@@ -61,23 +113,75 @@
ui = new(user, src, "Autolathe")
ui.open()
/**
* Converts all the designs supported by this autolathe into UI data
* Arguments
*
* * list/designs - the list of techweb designs we are trying to send to the UI
*/
/obj/machinery/autolathe/proc/handle_designs(list/designs)
PRIVATE_PROC(TRUE)
var/list/output = list()
var/datum/asset/spritesheet/research_designs/spritesheet = get_asset_datum(/datum/asset/spritesheet/research_designs)
var/size32x32 = "[spritesheet.name]32x32"
for(var/design_id in designs)
var/datum/design/design = SSresearch.techweb_design_by_id(design_id)
if(design.make_reagent)
continue
//compute cost & maximum number of printable items
var/coeff = (ispath(design.build_path, /obj/item/stack) ? 1 : creation_efficiency)
var/list/cost = list()
var/customMaterials = FALSE
for(var/i in design.materials)
var/datum/material/mat = i
var/design_cost = OPTIMAL_COST(design.materials[i] * coeff)
if(istype(mat))
cost[mat.name] = design_cost
customMaterials = FALSE
else
cost[i] = design_cost
customMaterials = TRUE
//create & send ui data
var/icon_size = spritesheet.icon_size_id(design.id)
var/list/design_data = list(
"name" = design.name,
"desc" = design.get_description(),
"cost" = cost,
"id" = design.id,
"categories" = design.category,
"icon" = "[icon_size == size32x32 ? "" : "[icon_size] "][design.id]",
"customMaterials" = customMaterials
)
output += list(design_data)
return output
/obj/machinery/autolathe/ui_static_data(mob/user)
var/list/data = materials.ui_static_data()
var/max_available = materials.total_amount()
for(var/datum/material/container_mat as anything in materials.materials)
var/available = materials.materials[container_mat]
if(available)
max_available = max(max_available, available)
data["designs"] = handle_designs(stored_research.researched_designs, max_available)
data["designs"] = handle_designs(stored_research.researched_designs)
if(imported_designs.len)
data["designs"] += handle_designs(imported_designs, max_available)
data["designs"] += handle_designs(imported_designs)
if(hacked)
data["designs"] += handle_designs(stored_research.hacked_designs, max_available)
data["designs"] += handle_designs(stored_research.hacked_designs)
return data
/obj/machinery/autolathe/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet/sheetmaterials),
get_asset_datum(/datum/asset/spritesheet/research_designs),
)
/obj/machinery/autolathe/ui_data(mob/user)
var/list/data = list()
@@ -89,107 +193,51 @@
return data
/obj/machinery/autolathe/proc/handle_designs(list/designs, max_available)
var/list/output = list()
var/datum/asset/spritesheet/research_designs/spritesheet = get_asset_datum(/datum/asset/spritesheet/research_designs)
var/size32x32 = "[spritesheet.name]32x32"
var/max_multiplier = INFINITY
for(var/design_id in designs)
var/datum/design/design = SSresearch.techweb_design_by_id(design_id)
if(design.make_reagent)
continue
//compute cost & maximum number of printable items
max_multiplier = INFINITY
var/coeff = (ispath(design.build_path, /obj/item/stack) ? 1 : creation_efficiency)
var/list/cost = list()
for(var/i in design.materials)
var/datum/material/mat = i
var/design_cost = OPTIMAL_COST(design.materials[i] * coeff)
if(istype(mat))
cost[mat.name] = design_cost
else
cost[i] = design_cost
var/mat_available
if(istype(mat)) //regular mat
mat_available = materials.get_material_amount(mat)
else //category mat means we can make it from any mat, use largest available mat
mat_available = max_available
max_multiplier = min(max_multiplier, 50, round(mat_available / design_cost))
//create & send ui data
var/icon_size = spritesheet.icon_size_id(design.id)
var/list/design_data = list(
"name" = design.name,
"desc" = design.get_description(),
"cost" = cost,
"id" = design.id,
"categories" = design.category,
"icon" = "[icon_size == size32x32 ? "" : "[icon_size] "][design.id]",
"constructionTime" = -1,
"maxmult" = max_multiplier
)
output += list(design_data)
return output
/obj/machinery/autolathe/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet/sheetmaterials),
get_asset_datum(/datum/asset/spritesheet/research_designs),
)
/obj/machinery/autolathe/ui_act(action, list/params, datum/tgui/ui)
. = ..()
if(.)
return
//sanity checks to start printing
if(action != "make")
stack_trace("unknown autolathe ui_act: [action]")
return
if(disabled)
say("Unable to print, voltage mismatch in internal wiring.")
return
if(busy)
balloon_alert(ui.user, "busy!")
return
var/turf/target_location = get_step(src, drop_direction)
if(isclosedturf(target_location))
say("Output path is obstructed by a large object.")
say("currently printing.")
return
//validate design
var/design_id = params["id"]
if(!design_id)
return
var/valid_design = stored_research.researched_designs[design_id]
valid_design ||= stored_research.hacked_designs[design_id]
valid_design ||= imported_designs[design_id]
if(!valid_design)
return
var/datum/design/design = SSresearch.techweb_design_by_id(design_id)
if(isnull(design))
stack_trace("got passed an invalid design id: [design_id] and somehow made it past all checks")
return
if(!(design.build_type & AUTOLATHE))
say("This fabricator does not have the necessary keys to decrypt this design.")
return
var/build_count = text2num(params["multiplier"])
if(!build_count)
//validate print quantity
var/build_count = params["multiplier"]
if(isnull(build_count))
return
build_count = text2num(build_count)
if(isnull(build_count))
return
build_count = clamp(build_count, 1, 50)
//check for materials required. For custom material items decode their required materials
var/list/materials_needed = list()
for(var/datum/material/material as anything in design.materials)
for(var/material in design.materials)
var/amount_needed = design.materials[material]
if(istext(material)) // category
var/list/choices = list()
@@ -215,64 +263,70 @@
return
materials_needed[material] = amount_needed
//checks for available materials
var/material_cost_coefficient = ispath(design.build_path, /obj/item/stack) ? 1 : creation_efficiency
if(!materials.has_materials(materials_needed, material_cost_coefficient, build_count))
say("Not enough materials to begin production.")
return
//use power
var/total_charge = 0
//compute power & time to print 1 item
var/charge_per_item = 0
for(var/material in design.materials)
total_charge += round(design.materials[material] * material_cost_coefficient * build_count)
var/charge_per_item = total_charge / build_count
var/total_time = (design.construction_time * design.lathe_time_factor * build_count) ** 0.8
var/time_per_item = total_time / build_count
start_making(design, build_count, time_per_item, material_cost_coefficient, charge_per_item)
return TRUE
/// Begins the act of making the given design the given number of items
/// Does not check or use materials/power/etc
/obj/machinery/autolathe/proc/start_making(datum/design/design, build_count, build_time_per_item, material_cost_coefficient, charge_per_item)
PROTECTED_PROC(TRUE)
charge_per_item += design.materials[material]
charge_per_item = min(active_power_usage, round(charge_per_item * material_cost_coefficient))
var/build_time_per_item = (design.construction_time * design.lathe_time_factor) ** 0.8
//do the printing sequentially
busy = TRUE
icon_state = "autolathe_n"
update_static_data_for_all_viewers()
SStgui.update_uis(src)
var/turf/target_location
if(drop_direction)
target_location = get_step(src, drop_direction)
if(isclosedturf(target_location))
target_location = get_turf(src)
else
target_location = get_turf(src)
addtimer(CALLBACK(src, PROC_REF(do_make_item), design, build_count, build_time_per_item, material_cost_coefficient, charge_per_item, materials_needed, target_location), build_time_per_item)
addtimer(CALLBACK(src, PROC_REF(do_make_item), design, material_cost_coefficient, build_time_per_item, charge_per_item, build_count), build_time_per_item)
return TRUE
/// Callback for start_making, actually makes the item
/// Called using timers started by start_making
/obj/machinery/autolathe/proc/do_make_item(datum/design/design, material_cost_coefficient, time_per_item, charge_per_item, items_remaining)
/**
* Callback for start_making, actually makes the item
* Arguments
*
* * datum/design/design - the design we are trying to print
* * items_remaining - the number of designs left out to print
* * build_time_per_item - the time taken to print 1 item
* * material_cost_coefficient - the cost efficiency to print 1 design
* * charge_per_item - the amount of power to print 1 item
* * list/materials_needed - the list of materials to print 1 item
* * turf/target - the location to drop the printed item on
*/
/obj/machinery/autolathe/proc/do_make_item(datum/design/design, items_remaining, build_time_per_item, material_cost_coefficient, charge_per_item, list/materials_needed, turf/target)
PROTECTED_PROC(TRUE)
if(!items_remaining) // how
if(items_remaining <= 0) // how
finalize_build()
return
if(!directly_use_power(charge_per_item))
if(!is_operational || !directly_use_power(charge_per_item))
say("Unable to continue production, power failure.")
finalize_build()
return
var/list/design_materials = design.materials
var/is_stack = ispath(design.build_path, /obj/item/stack)
if(!materials.has_materials(design_materials, material_cost_coefficient, is_stack ? items_remaining : 1))
if(!materials.has_materials(materials_needed, material_cost_coefficient, is_stack ? items_remaining : 1))
say("Unable to continue production, missing materials.")
return
materials.use_materials(design_materials, material_cost_coefficient, is_stack ? items_remaining : 1)
var/turf/target = get_step(src, drop_direction)
if(isclosedturf(target))
target = get_turf(src)
materials.use_materials(materials_needed, material_cost_coefficient, is_stack ? items_remaining : 1)
var/atom/movable/created
if(is_stack)
created = new design.build_path(target, items_remaining)
else
created = new design.build_path(target)
split_materials_uniformly(design_materials, material_cost_coefficient, created)
split_materials_uniformly(materials_needed, material_cost_coefficient, created)
created.pixel_x = created.base_pixel_x + rand(-6, 6)
created.pixel_y = created.base_pixel_y + rand(-6, 6)
@@ -283,26 +337,44 @@
else
items_remaining -= 1
if(!items_remaining)
if(items_remaining <= 0)
finalize_build()
return
addtimer(CALLBACK(src, PROC_REF(do_make_item), design, material_cost_coefficient, time_per_item, items_remaining), time_per_item)
addtimer(CALLBACK(src, PROC_REF(do_make_item), design, items_remaining, build_time_per_item, material_cost_coefficient, charge_per_item, materials_needed, target), build_time_per_item)
/// Resets the icon state and busy flag
/// Called at the end of do_make_item's timer loop
/**
* Resets the icon state and busy flag
* Called at the end of do_make_item's timer loop
*/
/obj/machinery/autolathe/proc/finalize_build()
PROTECTED_PROC(TRUE)
icon_state = initial(icon_state)
busy = FALSE
update_static_data_for_all_viewers()
SStgui.update_uis(src)
/obj/machinery/autolathe/crowbar_act(mob/living/user, obj/item/tool)
if(default_deconstruction_crowbar(tool))
return ITEM_INTERACT_SUCCESS
/obj/machinery/autolathe/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params)
. = ..()
if((!issilicon(usr) && !isAdminGhostAI(usr)) && !Adjacent(usr))
return
if(busy)
balloon_alert(usr, "printing started!")
return
var/direction = get_dir(src, over_location)
if(!direction)
return
drop_direction = direction
balloon_alert(usr, "dropping [dir2text(drop_direction)]")
/obj/machinery/autolathe/screwdriver_act_secondary(mob/living/user, obj/item/tool)
if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", tool))
return ITEM_INTERACT_SUCCESS
/obj/machinery/autolathe/AltClick(mob/user)
. = ..()
if(!drop_direction || !can_interact(user))
return
if(busy)
balloon_alert(user, "busy printing!")
return
balloon_alert(user, "drop direction reset")
drop_direction = 0
/obj/machinery/autolathe/attackby(obj/item/attacking_item, mob/living/user, params)
if(user.combat_mode) //so we can hit the machine
@@ -346,25 +418,6 @@
return ..()
/obj/machinery/autolathe/proc/AfterMaterialInsert(container, obj/item/item_inserted, last_inserted_id, mats_consumed, amount_inserted, atom/context)
SIGNAL_HANDLER
flick("autolathe_[item_inserted.has_material_type(/datum/material/glass) ? "r" : "o"]", src)
use_power(min(active_power_usage * 0.25, amount_inserted / SHEET_MATERIAL_AMOUNT))
update_static_data_for_all_viewers()
/obj/machinery/autolathe/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params)
. = ..()
if((!issilicon(usr) && !isAdminGhostAI(usr)) && !Adjacent(usr))
return
var/direction = get_dir(src, over_location)
if(!direction)
return
drop_direction = direction
balloon_alert(usr, "dropping [dir2text(drop_direction)]")
/obj/machinery/autolathe/RefreshParts()
. = ..()
var/mat_capacity = 0
@@ -377,24 +430,12 @@
efficiency -= new_servo.tier * 0.2
creation_efficiency = max(1,efficiency) // creation_efficiency goes 1.6 -> 1.4 -> 1.2 -> 1 per level of servo efficiency
/obj/machinery/autolathe/examine(mob/user)
. += ..()
if(in_range(user, src) || isobserver(user))
. += span_notice("The status display reads: Storing up to <b>[materials.max_amount]</b> material units.<br>Material consumption at <b>[creation_efficiency*100]%</b>.")
if(drop_direction)
. += span_notice("Currently configured to drop printed objects <b>[dir2text(drop_direction)]</b>.")
. += span_notice("<b>Alt-click</b> to reset.")
else
. += span_notice("<b>Drag towards a direction</b> (while next to it) to change drop direction.")
/obj/machinery/autolathe/AltClick(mob/user)
. = ..()
if(!can_interact(user))
return
if(drop_direction)
balloon_alert(user, "drop direction reset")
drop_direction = 0
/**
* Cut a wire in the autolathe
* Arguments
*
* * wire - the wire we are trying to cut
*/
/obj/machinery/autolathe/proc/reset(wire)
switch(wire)
if(WIRE_HACK)
@@ -407,6 +448,13 @@
if(!wires.is_cut(wire))
disabled = FALSE
/**
* Shock a mob who is trying to interact with the autolathe
* Arguments
*
* * mob/user - the mob we are trying to shock
* * prb - the probability of getting shocked
*/
/obj/machinery/autolathe/proc/shock(mob/user, prb)
if(machine_stat & (BROKEN|NOPOWER)) // unpowered, no shock
return FALSE
@@ -417,6 +465,12 @@
s.start()
return electrocute_mob(user, get_area(src), src, 0.7, TRUE)
/**
* Is the autolathe hacked. Allowing us to acess hidden designs
* Arguments
*
* state - TRUE/FALSE for is the autolathe hacked
*/
/obj/machinery/autolathe/proc/adjust_hacked(state)
hacked = state
update_static_data_for_all_viewers()
@@ -309,9 +309,6 @@
greyscale_colors = CIRCUIT_COLOR_ENGINEERING
build_path = /obj/machinery/rnd/production/protolathe/department/engineering
/obj/item/circuitboard/machine/protolathe/department/engineering/no_tax
build_path = /obj/machinery/rnd/production/protolathe/department/engineering/no_tax
/obj/item/circuitboard/machine/rtg
name = "RTG"
greyscale_colors = CIRCUIT_COLOR_ENGINEERING