#define AUTOLATHE_MAIN_MENU 1 #define AUTOLATHE_CATEGORY_MENU 2 #define AUTOLATHE_SEARCH_MENU 3 /obj/machinery/autolathe name = "autolathe" desc = "It produces items using metal and glass." icon_state = "autolathe" density = TRUE idle_power_consumption = 10 active_power_consumption = 100 var/list/queue = list() var/queue_max_len = 12 var/turf/BuildTurf anchored = TRUE var/list/L = list() var/list/LL = list() var/hacked = FALSE var/disabled = FALSE var/shocked = FALSE var/hack_wire var/disable_wire var/shock_wire var/busy = FALSE var/prod_coeff var/datum/wires/autolathe/wires = null var/list/being_built = list() var/datum/research/files var/list/imported = list() // /datum/design.id -> boolean var/list/datum/design/matching_designs var/temp_search var/selected_category var/list/recipiecache = list() var/list/categories = list("Tools", "Electronics", "Construction", "Communication", "Security", "Machinery", "Medical", "Miscellaneous", "Dinnerware", "Imported") var/board_type = /obj/item/circuitboard/autolathe var/disk_design_load_delay = 1.5 SECONDS /obj/machinery/autolathe/Initialize(mapload) . = ..() AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS), _show_on_examine = TRUE, _after_insert = CALLBACK(src, PROC_REF(AfterMaterialInsert))) initialize_parts() RefreshParts() RegisterSignal(src, COMSIG_TOOL_ATTACK, PROC_REF(on_tool_attack)) wires = new(src) files = new /datum/research/autolathe(src) matching_designs = list() /obj/machinery/autolathe/proc/initialize_parts() component_parts = list() component_parts += new board_type(null) component_parts += new /obj/item/stock_parts/matter_bin(null) component_parts += new /obj/item/stock_parts/matter_bin(null) component_parts += new /obj/item/stock_parts/matter_bin(null) component_parts += new /obj/item/stock_parts/manipulator(null) component_parts += new /obj/item/stack/sheet/glass(null) /obj/machinery/autolathe/loaded/Initialize(mapload) . = ..() var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) materials.insert_amount(MINERAL_MATERIAL_AMOUNT * 50, MAT_METAL) materials.insert_amount(MINERAL_MATERIAL_AMOUNT * 50, MAT_GLASS) /obj/machinery/autolathe/loaded/upgraded/initialize_parts() component_parts = list() component_parts += new board_type(null) component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null) component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null) component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null) component_parts += new /obj/item/stock_parts/manipulator/femto(null) component_parts += new /obj/item/stack/sheet/glass(null) /obj/machinery/autolathe/loaded/upgraded/gamma/Initialize(mapload) . = ..() files = new /datum/research/autolathe/gamma(src) adjust_hacked(TRUE) /obj/machinery/autolathe/Destroy() SStgui.close_uis(wires) QDEL_NULL(wires) var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) materials.retrieve_all() return ..() /obj/machinery/autolathe/proc/on_tool_attack(datum/source, atom/tool, mob/user) SIGNAL_HANDLER // COMSIG_TOOL_ATTACK var/obj/item/I = tool if(!istype(I)) return // Allows screwdrivers to be recycled on harm intent if(I.tool_behaviour == TOOL_SCREWDRIVER && user.a_intent == INTENT_HARM) return COMPONENT_CANCEL_TOOLACT /obj/machinery/autolathe/interact(mob/user) if(shocked && !(stat & NOPOWER)) if(shock(user, 50)) return if(panel_open) wires.Interact(user) else if(!disabled) ui_interact(user) /obj/machinery/autolathe/ui_state(mob/user) return GLOB.default_state /obj/machinery/autolathe/ui_interact(mob/user, datum/tgui/ui = null) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "Autolathe", name) ui.open() /obj/machinery/autolathe/ui_static_data(mob/user) var/list/data = list() data["categories"] = categories if(!length(recipiecache)) var/list/recipes = list() for(var/v in files.known_designs) var/datum/design/D = files.known_designs[v] var/list/cost_list = design_cost_data(D) var/list/matreq = list() for(var/list/x in cost_list) if(!x["amount"]) continue if(x["name"] == "metal") // Do not use MAT_METAL or MAT_GLASS here. matreq["metal"] = x["amount"] if(x["name"] == "glass") matreq["glass"] = x["amount"] var/obj/item/I = D.build_path var/maxmult = 1 if(ispath(D.build_path, /obj/item/stack)) maxmult = D.maxstack var/list/default_categories = D.category var/list/categories = istype(default_categories) ? default_categories.Copy() : list() if(imported[D.id]) categories |= "Imported" recipes.Add(list(list( "name" = D.name, "category" = categories, "uid" = D.UID(), "requirements" = matreq, "hacked" = ("hacked" in categories) ? TRUE : FALSE, "max_multiplier" = maxmult, "image" = "[icon2base64(icon(initial(I.icon), initial(I.icon_state), SOUTH, 1))]" ))) recipiecache = recipes data["recipes"] = recipiecache return data /obj/machinery/autolathe/ui_data(mob/user) var/list/data = list() var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) data["total_amount"] = materials.total_amount data["max_amount"] = materials.max_amount data["fill_percent"] = round((materials.total_amount / materials.max_amount) * 100) data["metal_amount"] = materials.amount(MAT_METAL) data["glass_amount"] = materials.amount(MAT_GLASS) data["busyname"] = FALSE data["busyamt"] = 1 if(length(being_built) > 0) var/datum/design/D = being_built[1] data["busyname"] = istype(D) && D.name ? D.name : FALSE data["busyamt"] = length(being_built) > 1 ? being_built[2] : 1 data["showhacked"] = hacked ? TRUE : FALSE data["buildQueue"] = queue data["buildQueueLen"] = length(queue) return data /obj/machinery/autolathe/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) if(..()) return FALSE add_fingerprint(usr) . = TRUE switch(action) if("clear_queue") queue = list() if("remove_from_queue") var/index = text2num(params["remove_from_queue"]) if(isnum(index) && ISINRANGE(index, 1, length(queue))) remove_from_queue(index) to_chat(usr, SPAN_NOTICE("Removed item from queue.")) if("make") BuildTurf = loc var/datum/design/design_last_ordered design_last_ordered = locateUID(params["make"]) if(!istype(design_last_ordered)) to_chat(usr, SPAN_WARNING("Invalid design")) return if(!(design_last_ordered.id in files.known_designs)) to_chat(usr, SPAN_WARNING("Invalid design (not in autolathe's known designs, report this error.)")) return var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) var/coeff = get_coeff(design_last_ordered) if(design_last_ordered.materials[MAT_METAL] / coeff > materials.amount(MAT_METAL)) to_chat(usr, SPAN_WARNING("Invalid design (not enough metal)")) return if(design_last_ordered.materials[MAT_GLASS] / coeff > materials.amount(MAT_GLASS)) to_chat(usr, SPAN_WARNING("Invalid design (not enough glass)")) return if(!hacked && ("hacked" in design_last_ordered.category)) to_chat(usr, SPAN_WARNING("Invalid design (lathe requires hacking)")) return //multiplier checks : only stacks can have one and its value is 1, 10 ,25 or max_multiplier var/multiplier = text2num(params["multiplier"]) var/max_multiplier = min(design_last_ordered.maxstack, design_last_ordered.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/design_last_ordered.materials[MAT_METAL]):INFINITY,design_last_ordered.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/design_last_ordered.materials[MAT_GLASS]):INFINITY) var/is_stack = ispath(design_last_ordered.build_path, /obj/item/stack) if(!is_stack && (multiplier > 1)) return if(!(multiplier in list(1, 10, 25, max_multiplier))) //"enough materials ?" is checked in the build proc message_admins("Player [key_name_admin(usr)] attempted to pass invalid multiplier [multiplier] to an autolathe in ui_act. Possible href exploit.") return if((length(queue) + 1) < queue_max_len) add_to_queue(design_last_ordered, multiplier) else to_chat(usr, SPAN_WARNING("The autolathe queue is full!")) if(!busy) busy = TRUE process_queue() busy = FALSE /obj/machinery/autolathe/ui_status(mob/user, datum/ui_state/state) . = disabled ? UI_DISABLED : UI_INTERACTIVE return min(..(), .) /obj/machinery/autolathe/proc/design_cost_data(datum/design/D) var/list/data = list() var/coeff = get_coeff(D) var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) var/has_metal = 1 if(D.materials[MAT_METAL] && (materials.amount(MAT_METAL) < (D.materials[MAT_METAL] / coeff))) has_metal = 0 var/has_glass = 1 if(D.materials[MAT_GLASS] && (materials.amount(MAT_GLASS) < (D.materials[MAT_GLASS] / coeff))) has_glass = 0 data[++data.len] = list("name" = "metal", "amount" = D.materials[MAT_METAL] / coeff, "is_red" = !has_metal) data[++data.len] = list("name" = "glass", "amount" = D.materials[MAT_GLASS] / coeff, "is_red" = !has_glass) return data /obj/machinery/autolathe/proc/queue_data(list/data) var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) var/temp_metal = materials.amount(MAT_METAL) var/temp_glass = materials.amount(MAT_GLASS) data["processing"] = length(being_built) ? get_processing_line() : null if(istype(queue) && length(queue)) var/list/data_queue = list() for(var/list/L in queue) var/datum/design/D = L[1] var/list/LL = get_design_cost_as_list(D, L[2]) data_queue[++data_queue.len] = list("name" = initial(D.name), "can_build" = can_build(D, L[2], temp_metal, temp_glass), "multiplier" = L[2]) temp_metal = max(temp_metal - LL[1], 1) temp_glass = max(temp_glass - LL[2], 1) data["queue"] = data_queue data["queue_len"] = length(data_queue) else data["queue"] = null return data /obj/machinery/autolathe/item_interaction(mob/living/user, obj/item/used, list/modifiers) if(busy) to_chat(user, SPAN_ALERT("The autolathe is busy. Please wait for completion of previous operation.")) return ITEM_INTERACT_COMPLETE if(stat) return ITEM_INTERACT_COMPLETE // Disks in general if(istype(used, /obj/item/disk)) if(istype(used, /obj/item/disk/design_disk)) var/obj/item/disk/design_disk/D = used if(D.blueprint) var/datum/design/design = D.blueprint // READ ONLY!! if(design.id in files.known_designs) to_chat(user, SPAN_WARNING("This design has already been loaded into the autolathe.")) return TRUE if(!files.CanAddDesign2Known(design)) to_chat(user, SPAN_WARNING("This design is not compatible with the autolathe.")) return TRUE user.visible_message( "[user] begins to load \the [used] in \the [src]...", "You begin to load a design from \the [used]...", "You hear the chatter of a floppy drive." ) playsound(get_turf(src), 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) busy = TRUE if(do_after(user, disk_design_load_delay, target = src)) imported[design.id] = TRUE files.AddDesign2Known(design) recipiecache = list() SStgui.close_uis(src) // forces all connected users to re-open the TGUI. Imported entries won't show otherwise due to static_data busy = FALSE else to_chat(user, SPAN_WARNING("That disk does not have a design on it!")) return ITEM_INTERACT_COMPLETE else // So that people who are bad at computers don't shred their disks to_chat(user, SPAN_WARNING("This is not the correct type of disk for the autolathe!")) return ITEM_INTERACT_COMPLETE return ..() /obj/machinery/autolathe/crowbar_act(mob/user, obj/item/I) if(!panel_open) return if(!I.use_tool(src, user, 0, volume = 0)) return . = TRUE if(busy) to_chat(user, SPAN_ALERT("The autolathe is busy. Please wait for completion of previous operation.")) return if(shocked && !(stat & NOPOWER)) if(shock(user, 50)) return if(panel_open) default_deconstruction_crowbar(user, I) /obj/machinery/autolathe/screwdriver_act(mob/user, obj/item/I) if(!I.use_tool(src, user, 0, volume = 0)) return . = TRUE if(busy) to_chat(user, SPAN_ALERT("The autolathe is busy. Please wait for completion of previous operation.")) return default_deconstruction_screwdriver(user, "[initial(icon_state)]_t", initial(icon_state), I) /obj/machinery/autolathe/wirecutter_act(mob/user, obj/item/I) if(!panel_open) return if(!I.use_tool(src, user, 0, volume = 0)) return . = TRUE if(busy) to_chat(user, SPAN_ALERT("The autolathe is busy. Please wait for completion of previous operation.")) return interact(user) /obj/machinery/autolathe/multitool_act(mob/user, obj/item/I) if(!panel_open) return if(!I.use_tool(src, user, 0, volume = 0)) return . = TRUE if(busy) to_chat(user, SPAN_ALERT("The autolathe is busy. Please wait for completion of previous operation.")) return interact(user) /obj/machinery/autolathe/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) switch(id_inserted) if(MAT_METAL) flick("autolathe_o", src)//plays metal insertion animation if(MAT_GLASS) flick("autolathe_r", src)//plays glass insertion animation use_power(min(1000, amount_inserted / 100)) SStgui.update_uis(src) /obj/machinery/autolathe/attack_ghost(mob/user) interact(user) /obj/machinery/autolathe/attack_hand(mob/user) if(..(user, 0)) return interact(user) /obj/machinery/autolathe/RefreshParts() var/tot_rating = 0 prod_coeff = 0 for(var/obj/item/stock_parts/matter_bin/MB in component_parts) tot_rating += MB.rating tot_rating *= 25000 var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) materials.max_amount = tot_rating * 3 for(var/obj/item/stock_parts/manipulator/M in component_parts) prod_coeff += M.rating - 1 recipiecache = list() SStgui.close_uis(src) // forces all connected users to re-open the TGUI. Imported entries won't show otherwise due to static_data /obj/machinery/autolathe/proc/get_coeff(datum/design/D) var/coeff = (ispath(D.build_path,/obj/item/stack) ? 1 : 2 ** prod_coeff)//stacks are unaffected by production coefficient return coeff /obj/machinery/autolathe/proc/build_item(datum/design/D, multiplier) desc = initial(desc)+"\nIt's building \a [initial(D.name)]." var/is_stack = ispath(D.build_path, /obj/item/stack) var/coeff = get_coeff(D) var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) var/metal_cost = D.materials[MAT_METAL] var/glass_cost = D.materials[MAT_GLASS] var/power = max(2000, (metal_cost+glass_cost)*multiplier/5) if(can_build(D, multiplier)) being_built = list(D, multiplier) use_power(power) icon_state = "autolathe" flick("autolathe_n",src) if(is_stack) var/list/materials_used = list(MAT_METAL=metal_cost*multiplier, MAT_GLASS=glass_cost*multiplier) materials.use_amount(materials_used) else var/list/materials_used = list(MAT_METAL=metal_cost/coeff, MAT_GLASS=glass_cost/coeff) materials.use_amount(materials_used) SStgui.update_uis(src) sleep(32/coeff) if(is_stack) var/obj/item/stack/S = new D.build_path(BuildTurf) S.amount = multiplier else var/obj/item/new_item = new D.build_path(BuildTurf) new_item.materials[MAT_METAL] /= coeff new_item.materials[MAT_GLASS] /= coeff new_item.scatter_atom() if(is_station_level(z)) SSblackbox.record_feedback("tally", "station_autolathe_production", 1, "[D.type]") SStgui.update_uis(src) desc = initial(desc) /obj/machinery/autolathe/proc/can_build(datum/design/D, multiplier = 1, custom_metal, custom_glass) if(length(D.make_reagents)) return 0 var/coeff = get_coeff(D) var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) var/metal_amount = materials.amount(MAT_METAL) if(custom_metal) metal_amount = custom_metal var/glass_amount = materials.amount(MAT_GLASS) if(custom_glass) glass_amount = custom_glass if(D.materials[MAT_METAL] && (metal_amount < (multiplier*D.materials[MAT_METAL] / coeff))) return 0 if(D.materials[MAT_GLASS] && (glass_amount < (multiplier*D.materials[MAT_GLASS] / coeff))) return 0 return 1 /obj/machinery/autolathe/proc/get_design_cost_as_list(datum/design/D, multiplier = 1) var/list/OutputList = list(0,0) var/coeff = get_coeff(D) if(D.materials[MAT_METAL]) OutputList[1] = (D.materials[MAT_METAL] / coeff)*multiplier if(D.materials[MAT_GLASS]) OutputList[2] = (D.materials[MAT_GLASS] / coeff)*multiplier return OutputList /obj/machinery/autolathe/proc/get_processing_line() var/datum/design/D = being_built[1] var/multiplier = being_built[2] var/is_stack = (multiplier>1) var/output = "PROCESSING: [initial(D.name)][is_stack?" (x[multiplier])":null]" return output /obj/machinery/autolathe/proc/add_to_queue(D, multiplier) if(!istype(queue)) queue = list() if(D) queue.Add(list(list(D,multiplier))) return length(queue) /obj/machinery/autolathe/proc/remove_from_queue(index) if(!isnum(index) || !istype(queue) || (index<1 || index>length(queue))) return 0 queue.Cut(index,++index) return 1 /obj/machinery/autolathe/proc/process_queue() var/datum/design/D = queue[1][1] var/multiplier = queue[1][2] if(!D) remove_from_queue(1) if(length(queue)) return process_queue() else return while(D) if((stat & (NOPOWER|BROKEN)) || disabled) being_built = new /list() return 0 if(!can_build(D, multiplier)) visible_message("[bicon(src)] \The [src] beeps, \"Not enough resources. Queue processing terminated.\"") queue = list() being_built = new /list() return 0 remove_from_queue(1) build_item(D,multiplier) D = listgetindex(listgetindex(queue, 1),1) multiplier = listgetindex(listgetindex(queue,1),2) being_built = new /list() /obj/machinery/autolathe/proc/adjust_hacked(hack) hacked = hack if(hack) for(var/datum/design/D in files.possible_designs) if((D.build_type & AUTOLATHE) && ("hacked" in D.category)) files.AddDesign2Known(D) else for(var/datum/design/D in files.known_designs) if("hacked" in D.category) files.known_designs -= D.id SStgui.close_uis(src) // forces all connected users to re-open the TGUI, thus adding/removing hacked entries from lists recipiecache = list() /obj/machinery/autolathe/proc/check_hacked_callback() if(!wires.is_cut(WIRE_AUTOLATHE_HACK)) adjust_hacked(FALSE) /obj/machinery/autolathe/proc/check_electrified_callback() if(!wires.is_cut(WIRE_ELECTRIFY)) shocked = FALSE /obj/machinery/autolathe/proc/check_disabled_callback() if(!wires.is_cut(WIRE_AUTOLATHE_DISABLE)) disabled = FALSE /obj/machinery/autolathe/get_internal_wires() return wires /obj/machinery/autolathe/syndicate name = "syndicate autolathe" board_type = /obj/item/circuitboard/autolathe/syndi /obj/machinery/autolathe/syndicate/Initialize(mapload) . = ..() files = new /datum/research/autolathe/syndicate(src) #undef AUTOLATHE_MAIN_MENU #undef AUTOLATHE_CATEGORY_MENU #undef AUTOLATHE_SEARCH_MENU