adds some "factory" machines (#86063)

## About The Pull Request



https://github.com/user-attachments/assets/4ceb4c0f-d5ef-4fc0-8436-d7eec5b6f396



https://github.com/user-attachments/assets/56ddd387-7376-4c35-a067-1adccbddeecd



https://github.com/user-attachments/assets/dda6cc2b-614a-4adb-a8f4-2c03b51162e0



https://github.com/user-attachments/assets/fa7697fb-f484-48a0-bb85-ee0c2f4867e2



https://github.com/user-attachments/assets/02de4b24-2fa0-4a1e-b147-df9500109b3c



https://github.com/user-attachments/assets/b56c03ab-49c9-487f-a99f-fcba5ce038ac



https://github.com/user-attachments/assets/52bae5a4-68b0-4f25-99c1-1b677b99790a

i didnt feel like recording the lathe and crafter for a suitable file
size again but essentially the crafter crafts and the lathe lathes

all machines but the router and sorter are cable powered (suitable on
lavaland)
theyre researched roundstart

they can receive any resource that bumps into it if that resource is on
the conveyor

## Why It's Good For The Game
more fun engineering stuff
and perhaps mining given these are more efficient but require effort to
set up
https://hackmd.io/@jimmyl/S1dZRZosC
## Changelog
🆑
add: added the manufacturing
smelter,router,sorter,crafter,lathe,crusher,unloader
/🆑

---------

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
This commit is contained in:
jimmyl
2024-09-29 14:58:27 +02:00
committed by GitHub
parent 5c9bb9df50
commit 0771b1b3a7
22 changed files with 1395 additions and 10 deletions

View File

@@ -0,0 +1,4 @@
PROCESSING_SUBSYSTEM_DEF(manufacturing)
name = "Manufacturing Processing"
wait = 1 SECONDS
stat_tag = "MN"

View File

@@ -21,6 +21,8 @@
var/display_craftable_only = FALSE
var/display_compact = FALSE
var/forced_mode = FALSE
/// crafting flags we ignore when considering a recipe
var/ignored_flags = NONE
/* This is what procs do:
get_environment - gets a list of things accessable for crafting by user
@@ -205,16 +207,16 @@
if(!check_tools(crafter, recipe, contents))
return ", missing tool."
var/considered_flags = recipe.crafting_flags & ~(ignored_flags)
if((recipe.crafting_flags & CRAFT_ONE_PER_TURF) && (locate(recipe.result) in dest_turf))
if((considered_flags & CRAFT_ONE_PER_TURF) && (locate(recipe.result) in dest_turf))
return ", already one here!"
if(recipe.crafting_flags & CRAFT_CHECK_DIRECTION)
if(!valid_build_direction(dest_turf, crafter.dir, is_fulltile = (recipe.crafting_flags & CRAFT_IS_FULLTILE)))
if(considered_flags & CRAFT_CHECK_DIRECTION)
if(!valid_build_direction(dest_turf, crafter.dir, is_fulltile = (considered_flags & CRAFT_IS_FULLTILE)))
return ", won't fit here!"
if(recipe.crafting_flags & CRAFT_ON_SOLID_GROUND)
if(considered_flags & CRAFT_ON_SOLID_GROUND)
if(isclosedturf(dest_turf))
return ", cannot be made on a wall!"
@@ -222,7 +224,7 @@
if(!locate(/obj/structure/thermoplastic) in dest_turf) // for tram construction
return ", must be made on solid ground!"
if(recipe.crafting_flags & CRAFT_CHECK_DENSITY)
if(considered_flags & CRAFT_CHECK_DENSITY)
for(var/obj/object in dest_turf)
if(object.density && !(object.obj_flags & IGNORE_DENSITY) || object.obj_flags & BLOCKS_CONSTRUCTION)
return ", something is in the way!"
@@ -703,3 +705,20 @@
if(recipe == potential_recipe)
return TRUE
return FALSE
/datum/component/personal_crafting/machine
ignored_flags = CRAFT_CHECK_DENSITY
/datum/component/personal_crafting/machine/get_environment(atom/crafter, list/blacklist = null, radius_range = 1)
. = list()
for(var/atom/movable/content in crafter.contents)
if((content.flags_1 & HOLOGRAM_1) || (blacklist && (content.type in blacklist)))
continue
if(isitem(content))
var/obj/item/item = content
if(item.item_flags & ABSTRACT) //let's not tempt fate, shall we?
continue
. += content
/datum/component/personal_crafting/machine/check_tools(atom/source, datum/crafting_recipe/recipe, list/surroundings)
return TRUE

View File

@@ -74,3 +74,14 @@
)
category = CAT_STRUCTURE
crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_MUST_BE_LEARNED
/datum/crafting_recipe/manucrate
name = "Manufacturing Storage Unit"
result = /obj/machinery/power/manufacturing/storagebox
tool_behaviors = list(TOOL_SCREWDRIVER, TOOL_WELDER)
time = 6 SECONDS
reqs = list(
/obj/item/stack/sheet/iron = 10,
)
category = CAT_STRUCTURE
crafting_flags = CRAFT_CHECK_DENSITY

View File

@@ -1715,3 +1715,65 @@
req_components = list(
/datum/stock_part/servo = 1,
)
/obj/item/circuitboard/machine/manucrafter
name = /obj/machinery/power/manufacturing/crafter::name
greyscale_colors = CIRCUIT_COLOR_ENGINEERING
build_path = /obj/machinery/power/manufacturing/crafter
req_components = list(
/obj/item/stack/sheet/iron = 5,
/datum/stock_part/servo = 1,
)
/obj/item/circuitboard/machine/manulathe
name = /obj/machinery/power/manufacturing/lathe::name
greyscale_colors = CIRCUIT_COLOR_ENGINEERING
build_path = /obj/machinery/power/manufacturing/lathe
req_components = list(
/obj/item/stack/sheet/iron = 5,
/datum/stock_part/servo = 1,
)
/obj/item/circuitboard/machine/manucrusher
name = /obj/machinery/power/manufacturing/crusher::name
greyscale_colors = CIRCUIT_COLOR_ENGINEERING
build_path = /obj/machinery/power/manufacturing/crusher
req_components = list(
/obj/item/stack/sheet/iron = 5,
/datum/stock_part/servo = 1,
)
/obj/item/circuitboard/machine/manuunloader
name = /obj/machinery/power/manufacturing/unloader::name
greyscale_colors = CIRCUIT_COLOR_ENGINEERING
build_path = /obj/machinery/power/manufacturing/unloader
req_components = list(
/obj/item/stack/sheet/iron = 5,
/datum/stock_part/servo = 1,
)
/obj/item/circuitboard/machine/manusorter
name = /obj/machinery/power/manufacturing/sorter::name
greyscale_colors = CIRCUIT_COLOR_ENGINEERING
build_path = /obj/machinery/power/manufacturing/sorter
req_components = list(
/obj/item/stack/sheet/iron = 5,
/datum/stock_part/scanning_module = 1,
)
/obj/item/circuitboard/machine/manusmelter
name = /obj/machinery/power/manufacturing/smelter::name
greyscale_colors = CIRCUIT_COLOR_ENGINEERING
build_path = /obj/machinery/power/manufacturing/smelter
req_components = list(
/obj/item/stack/sheet/iron = 5,
/datum/stock_part/micro_laser = 1,
)
/obj/item/circuitboard/machine/manurouter
name = /obj/machinery/power/manufacturing/router::name
greyscale_colors = CIRCUIT_COLOR_ENGINEERING
build_path = /obj/machinery/power/manufacturing/router
req_components = list(
/obj/item/stack/sheet/iron = 5,
)

View File

@@ -270,7 +270,7 @@ GLOBAL_LIST_EMPTY(station_turfs)
* * type_list - are we checking for types of atoms to ignore and not physical atoms
*/
/turf/proc/is_blocked_turf(exclude_mobs = FALSE, source_atom = null, list/ignore_atoms, type_list = FALSE)
if(density)
if((!isnull(source_atom) && !CanPass(source_atom, get_dir(src, source_atom))) || density)
return TRUE
for(var/atom/movable/movable_content as anything in contents)

View File

@@ -0,0 +1,123 @@
#define MANUFACTURING_FAIL_FULL -1
#define MANUFACTURING_FAIL 0
#define MANUFACTURING_SUCCESS 1
#define POCKET_INPUT "Input"
#define POCKET_OUTPUT "Output"
#define MANUFACTURING_TURF_LAG_LIMIT 10 // max items on a turf before we consider it full
/obj/machinery/power/manufacturing
icon = 'icons/obj/machines/manufactorio.dmi'
name = "base manufacture receiving type"
desc = "this shouldnt exist"
density = TRUE
/// Do we add the simple_rotation component and a text that we are powered by cable? Also allows unwrenching
var/may_be_moved = TRUE
/// Allow taking in mobs from conveyors?
var/allow_mob_bump_intake = FALSE
/obj/machinery/power/manufacturing/Initialize(mapload)
. = ..()
if(may_be_moved)
AddComponent(/datum/component/simple_rotation)
if(anchored)
connect_to_network()
/obj/machinery/power/manufacturing/examine(mob/user)
. = ..()
if(may_be_moved)
. += "It receives power via cable, but certain buildings do not need power."
. += length(contents - circuit) ? "It contains:" : "Its empty."
for(var/atom/movable/thing as anything in contents - circuit)
var/text = thing.name
var/obj/item/stack/possible_stack = thing
if(istype(possible_stack))
text = "[possible_stack.amount] [text]"
. += text
/obj/machinery/power/manufacturing/Bumped(atom/movable/bumped_atom) //attempt to put in whatever is pushed into us via conveyor
. = ..()
if((!allow_mob_bump_intake && ismob(bumped_atom)) || !anchored) //only uncomment if youre brave
return
var/conveyor = locate(/obj/machinery/conveyor) in bumped_atom.loc
if(isnull(conveyor))
return
receive_resource(bumped_atom, bumped_atom.loc, get_dir(src, bumped_atom))
/obj/machinery/power/manufacturing/wrench_act(mob/living/user, obj/item/tool)
. = ..()
if(!may_be_moved)
return
default_unfasten_wrench(user, tool)
if(anchored)
connect_to_network()
else
disconnect_from_network()
return ITEM_INTERACT_SUCCESS
/obj/machinery/power/manufacturing/screwdriver_act(mob/living/user, obj/item/tool)
if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
return ITEM_INTERACT_SUCCESS
return ITEM_INTERACT_BLOCKING
/obj/machinery/power/manufacturing/crowbar_act(mob/living/user, obj/item/tool)
. = ITEM_INTERACT_BLOCKING
if(default_deconstruction_crowbar(tool))
return ITEM_INTERACT_SUCCESS
/obj/machinery/power/manufacturing/proc/generate_io_overlays(direction, color, offsets_override)
var/list/dir_offset
if(islist(offsets_override))
dir_offset = offsets_override
else
dir_offset = dir2offset(direction)
dir_offset[1] *= 32
dir_offset[2] *= 32
var/image/nonemissive = image(icon='icons/obj/doors/airlocks/station/overlays.dmi', icon_state="unres_[direction]")
nonemissive.pixel_x = dir_offset[1]
nonemissive.pixel_y = dir_offset[2]
nonemissive.color = color
var/mutable_appearance/emissive = emissive_appearance(nonemissive.icon, nonemissive.icon_state, offset_spokesman = src, alpha = nonemissive.alpha)
emissive.pixel_y = nonemissive.pixel_y
emissive.pixel_x = nonemissive.pixel_x
return list(nonemissive, emissive)
/// Returns whatever object it may output, or null if it cant do that
/obj/machinery/power/manufacturing/proc/request_resource()
/obj/machinery/power/manufacturing/proc/receive_resource(atom/movable/receiving, atom/from, receive_dir)
CRASH("Unimplemented!") //check can_receive_resource here
//use dir please
/obj/machinery/power/manufacturing/proc/send_resource(atom/movable/sending, atom/what_or_dir)
if(isobj(what_or_dir))
var/obj/machinery/power/manufacturing/target = what_or_dir
return target.receive_resource(sending, src, get_step(src, what_or_dir))
var/turf/next_turf = isturf(what_or_dir) ? what_or_dir : get_step(src, what_or_dir)
var/obj/machinery/power/manufacturing/manufactury = locate(/obj/machinery/power/manufacturing) in next_turf
if(!isnull(manufactury))
if(!manufactury.anchored)
return MANUFACTURING_FAIL
return manufactury.receive_resource(sending, src, isturf(what_or_dir) ? get_dir(src, what_or_dir) : what_or_dir)
if(next_turf.is_blocked_turf(exclude_mobs = TRUE, source_atom = sending))
return MANUFACTURING_FAIL
if(length(next_turf.contents) >= MANUFACTURING_TURF_LAG_LIMIT)
return MANUFACTURING_FAIL_FULL
if(isnull(sending))
return MANUFACTURING_SUCCESS // for the sake of being used as a check
if(isnull(sending.loc) || !sending.Move(next_turf, get_dir(src, next_turf)))
sending.forceMove(next_turf)
return MANUFACTURING_SUCCESS
/// Checks if this stack (if not a stack does not do anything) can merge WITHOUT creating two stacks in contents
/obj/machinery/power/manufacturing/proc/may_merge_in_contents(obj/item/stack/stack)
if(!istype(stack))
return
for(var/obj/item/stack/other in contents - circuit)
if(!other.can_merge(stack))
continue
if(other.amount + stack.amount <= other.max_amount)
return other

View File

@@ -0,0 +1,139 @@
/obj/machinery/power/manufacturing/crafter
name = "manufacturing assembling machine"
desc = "Assembles (crafts) the set recipe until it runs out of resources. Inputs irrelevant to the recipe are ignored."
icon_state = "crafter"
circuit = /obj/item/circuitboard/machine/manucrafter
/// power used per process() spent crafting
var/power_cost = 5 KILO WATTS
/// our output, if the way out was blocked is held here
var/atom/movable/withheld
/// current recipe
var/datum/crafting_recipe/recipe
/// crafting component
var/datum/component/personal_crafting/machine/craftsman
/// current timer for our crafting
var/craft_timer
/// do we use cooking recipes instead
var/cooking = FALSE
/obj/machinery/power/manufacturing/crafter/Initialize(mapload)
. = ..()
craftsman = AddComponent(/datum/component/personal_crafting/machine)
/obj/machinery/power/manufacturing/crafter/examine(mob/user)
. = ..()
. += span_notice("It is currently manufacturing <b>[isnull(recipe) ? "nothing. Use a multitool to set it" : recipe.name]</b>.")
if(isnull(recipe))
return
. += span_notice("It needs:")
for(var/valid_type in recipe.reqs)
// Check if they're datums, specifically reagents.
var/datum/reagent/reagent_ingredient = valid_type
if(istype(reagent_ingredient))
var/amount = recipe.reqs[reagent_ingredient]
. += "[amount] unit[amount > 1 ? "s" : ""] of [initial(reagent_ingredient.name)]"
var/atom/ingredient = valid_type
var/amount = recipe.reqs[ingredient]
. += "[amount > 1 ? ("[amount]" + " of") : "a"] [initial(ingredient.name)]"
/obj/machinery/power/manufacturing/crafter/update_overlays()
. = ..()
. += generate_io_overlays(dir, COLOR_ORANGE)
for(var/target_dir in GLOB.cardinals - dir)
. += generate_io_overlays(target_dir, COLOR_MODERATE_BLUE)
/obj/machinery/power/manufacturing/crafter/proc/valid_for_recipe(obj/item/checking)
. = FALSE
for(var/requirement_path in recipe.reqs)
if(!ispath(checking.type, requirement_path) || recipe.blacklist.Find(checking.type))
continue
return TRUE
/obj/machinery/power/manufacturing/crafter/proc/contains_type(path)
. = FALSE
for(var/content in contents - circuit)
if(!istype(content, path))
continue
return TRUE
/obj/machinery/power/manufacturing/crafter/receive_resource(obj/receiving, atom/from, receive_dir)
if(isnull(recipe) || !isitem(receiving) || surplus() < power_cost)
return MANUFACTURING_FAIL
if(receive_dir == dir || !valid_for_recipe(receiving))
return MANUFACTURING_FAIL
if(!may_merge_in_contents(receiving) && contains_type(receiving.type))
return MANUFACTURING_FAIL_FULL
receiving.Move(src, get_dir(receiving, src))
START_PROCESSING(SSmanufacturing, src)
return MANUFACTURING_SUCCESS
/obj/machinery/power/manufacturing/crafter/multitool_act(mob/living/user, obj/item/tool)
. = NONE
var/list/unavailable = list()
for(var/datum/crafting_recipe/potential_recipe as anything in cooking ? GLOB.cooking_recipes : GLOB.crafting_recipes)
if(craftsman.is_recipe_available(potential_recipe, user))
continue
var/obj/result = initial(potential_recipe.result)
if(istype(result) && initial(result.anchored))
continue
unavailable += potential_recipe
var/result = tgui_input_list(usr, "Recipe", "Select Recipe", (cooking ? GLOB.cooking_recipes : GLOB.crafting_recipes) - unavailable)
if(isnull(result) || result == recipe || !user.can_perform_action(src))
return ITEM_INTERACT_FAILURE
var/dump_target = get_step(src, get_dir(src, user))
for(var/atom/movable/thing as anything in contents - circuit)
thing.Move(dump_target)
recipe = result
return ITEM_INTERACT_SUCCESS
/obj/machinery/power/manufacturing/crafter/Exited(atom/movable/gone, direction)
. = ..()
if(gone == withheld)
withheld = null
/obj/machinery/power/manufacturing/crafter/atom_destruction(damage_flag)
. = ..()
withheld?.Move(drop_location(src))
/obj/machinery/power/manufacturing/crafter/Destroy()
. = ..()
recipe = null
craftsman = null
QDEL_NULL(withheld)
/obj/machinery/power/manufacturing/crafter/process(seconds_per_tick)
if(!isnull(withheld) && !send_resource(withheld, dir))
return
if(!isnull(craft_timer))
if(surplus() >= power_cost)
add_load()
else
deltimer(craft_timer)
craft_timer = null
say("Power failure!")
return
if(isnull(recipe) || !craftsman.check_contents(src, recipe, craftsman.get_surroundings(src)))
return
flick_overlay_view(mutable_appearance(icon, "crafter_printing"), recipe.time)
craft_timer = addtimer(CALLBACK(src, PROC_REF(craft), recipe), recipe.time, TIMER_STOPPABLE)
/obj/machinery/power/manufacturing/crafter/proc/craft(datum/crafting_recipe/recipe)
if(QDELETED(src))
return
craft_timer = null
var/atom/movable/result = craftsman.construct_item(src, recipe)
if(istype(result))
if(isitem(result))
result.pixel_x += rand(-4, 4)
result.pixel_y += rand(-4, 4)
result.Move(src)
send_resource(result, dir)
else
say(result)
/obj/machinery/power/manufacturing/crafter/cooker
name = "manufacturing cooking machine" // maybe this shouldnt be available dont wanna make chef useless, though otherwise it would need a sprite
desc = "Cooks the set recipe until it runs out of resources. Inputs irrelevant to the recipe are ignored."
cooking = TRUE

View File

@@ -0,0 +1,83 @@
/obj/machinery/power/manufacturing/crusher //todo make it work for other stuff
name = "manufacturing crusher"
desc = "Crushes any item put into it, boulders and such. Materials below a sheet are stored in the machine."
icon_state = "crusher"
circuit = /obj/item/circuitboard/machine/manucrusher
/// power used to crush
var/crush_cost = 3 KILO WATTS
/// how much can we hold
var/capacity = 5
/// withheld output because output is either blocked or full
var/atom/movable/withholding
/// list of held mats
var/list/obj/item/stack/held_mats = list()
/obj/machinery/power/manufacturing/crusher/update_overlays()
. = ..()
. += generate_io_overlays(dir, COLOR_ORANGE) // OUT - stuff in it
. += generate_io_overlays(REVERSE_DIR(dir), COLOR_MODERATE_BLUE) // IN - to crush
/obj/machinery/power/manufacturing/crusher/Destroy()
. = ..()
QDEL_NULL(withholding)
/obj/machinery/power/manufacturing/crusher/atom_destruction(damage_flag)
withholding?.Move(drop_location())
return ..()
/obj/machinery/power/manufacturing/crusher/receive_resource(obj/receiving, atom/from, receive_dir)
if(istype(receiving, /obj/item/stack/ore) || receiving.resistance_flags & INDESTRUCTIBLE || !isitem(receiving) || surplus() < crush_cost || receive_dir != REVERSE_DIR(dir))
return MANUFACTURING_FAIL
if(!may_merge_in_contents(receiving) && length(contents - circuit) >= capacity)
return MANUFACTURING_FAIL_FULL
receiving.Move(src, get_dir(receiving, src))
START_PROCESSING(SSmanufacturing, src)
return MANUFACTURING_SUCCESS
/obj/machinery/power/manufacturing/crusher/Exited(atom/movable/gone, direction)
. = ..()
if(gone == withholding)
withholding = null
/obj/machinery/power/manufacturing/crusher/process(seconds_per_tick) //noot functional
if(!isnull(withholding) && !send_resource(withholding, dir))
return
for(var/material in held_mats)
if(held_mats[material] >= 1)
var/new_amount = floor(held_mats[material])
held_mats[material] -= new_amount
if(held_mats[material] <= 0)
held_mats -= material
withholding = new material(null, new_amount)
return
var/list/poor_saps = contents - circuit
if(!length(poor_saps))
return PROCESS_KILL
if(surplus() < crush_cost)
return
var/obj/victim = poor_saps[length(poor_saps)]
if(istype(victim)) //todo handling for other things
if(!length(victim.custom_materials))
add_load(crush_cost)
victim.atom_destruction()
for(var/obj/object in victim.contents+victim)
for(var/datum/material/possible_mat as anything in object.custom_materials)
var/quantity = object.custom_materials[possible_mat]
object.set_custom_materials(object.custom_materials.Copy() - possible_mat, 1)
var/type_to_use = istype(victim, /obj/item/boulder) ? possible_mat.ore_type : possible_mat.sheet_type
if(quantity < SHEET_MATERIAL_AMOUNT)
if(!(type_to_use in held_mats))
held_mats[type_to_use] = quantity / SHEET_MATERIAL_AMOUNT
continue
held_mats[type_to_use] += quantity / SHEET_MATERIAL_AMOUNT
continue
var/obj/item/stack/sheet/new_item = new type_to_use(src, quantity / SHEET_MATERIAL_AMOUNT)
if(!send_resource(new_item, dir))
withholding = new_item
return
else if(isliving(victim))
var/mob/living/poor_sap = victim
poor_sap.adjustBruteLoss(95, TRUE)
if(!send_resource(poor_sap, dir))
withholding = poor_sap
return

View File

@@ -0,0 +1,18 @@
/obj/loop_spawner
name = "testing loop spawner"
icon = 'icons/obj/machines/mining_machines.dmi'
icon_state = "unloader"
anchored = TRUE
color = COLOR_PURPLE
/// directions we can output to right now
var/to_spawn = /obj/item/screwdriver
/// the subsystem to process us
var/subsystem_to_process_us = /datum/controller/subsystem/processing/obj
/obj/loop_spawner/Initialize(mapload)
. = ..()
var/datum/controller/subsystem/processing/subsystem = locate(subsystem_to_process_us) in Master.subsystems
START_PROCESSING(subsystem, src)
/obj/loop_spawner/process(seconds_per_tick)
new to_spawn(get_step(src, dir))

View File

@@ -0,0 +1,145 @@
/obj/machinery/power/manufacturing/lathe // this is a heavily gutted autolathe
name = "manufacturing lathe"
desc = "Lathes the set recipe until it runs out of resources. Only accepts sheets or other kinds of material stacks."
icon_state = "lathe"
circuit = /obj/item/circuitboard/machine/manulathe
/// power cost for lathing
var/power_cost = 5 KILO WATTS
/// design id we print
var/design_id
///The container to hold materials
var/datum/component/material_container/materials
//looping sound for printing items
var/datum/looping_sound/lathe_print/print_sound
///Designs related to the autolathe
var/datum/techweb/autounlocking/stored_research
/// timer id of printing
var/busy = FALSE
/// our output, if the way out was blocked is held here
var/atom/movable/withheld
/obj/machinery/power/manufacturing/lathe/Initialize(mapload)
. = ..()
print_sound = new(src, FALSE)
materials = AddComponent( \
/datum/component/material_container, \
SSmaterials.materials_by_category[MAT_CATEGORY_ITEM_MATERIAL], \
SHEET_MATERIAL_AMOUNT * MAX_STACK_SIZE * 2, \
MATCONTAINER_EXAMINE|MATCONTAINER_NO_INSERT, \
)
if(!GLOB.autounlock_techwebs[/datum/techweb/autounlocking/autolathe])
GLOB.autounlock_techwebs[/datum/techweb/autounlocking/autolathe] = new /datum/techweb/autounlocking/autolathe
stored_research = GLOB.autounlock_techwebs[/datum/techweb/autounlocking/autolathe]
/obj/machinery/power/manufacturing/lathe/examine(mob/user)
. = ..()
var/datum/design/design
if(!isnull(design_id))
design = SSresearch.techweb_design_by_id(design_id)
. += span_notice("It is set to print [!isnull(design) ? design.name : "nothing, set with a multitool"].")
if(isnull(design))
return
. += span_notice("It needs:")
for(var/valid_type in design.materials)
var/atom/ingredient = valid_type
var/amount = design.materials[ingredient] / SHEET_MATERIAL_AMOUNT
. += "[amount] sheets of [initial(ingredient.name)]"
/obj/machinery/power/manufacturing/lathe/update_overlays()
. = ..()
. += generate_io_overlays(dir, COLOR_ORANGE) // OUT - stuff in it
. += generate_io_overlays(REVERSE_DIR(dir), COLOR_MODERATE_BLUE) // IN - to crush
/obj/machinery/power/manufacturing/lathe/Destroy()
. = ..()
stored_research = null
QDEL_NULL(print_sound)
materials = null
QDEL_NULL(withheld)
/obj/machinery/power/manufacturing/lathe/atom_destruction(damage_flag)
withheld?.Move(drop_location())
return ..()
/obj/machinery/power/manufacturing/lathe/receive_resource(atom/movable/receiving, atom/from, receive_dir)
if(!isstack(receiving) || receiving.resistance_flags & INDESTRUCTIBLE || receive_dir != REVERSE_DIR(dir))
return MANUFACTURING_FAIL
materials.insert_item(receiving)
return MANUFACTURING_SUCCESS
/obj/machinery/power/manufacturing/lathe/multitool_act(mob/living/user, obj/item/tool)
. = ..()
var/list/name_to_id = list()
for(var/id in stored_research.researched_designs)
var/datum/design/design = SSresearch.techweb_design_by_id(id)
name_to_id[design.name] = id
var/result = tgui_input_list(user, "Select Design", "Select Design", sort_list(name_to_id))
if(isnull(result))
return ITEM_INTERACT_FAILURE
design_id = name_to_id[result]
return ITEM_INTERACT_SUCCESS
/obj/machinery/power/manufacturing/lathe/process()
if(!isnull(withheld) && !send_resource(withheld, dir))
return
var/datum/design/design = SSresearch.techweb_design_by_id(design_id)
if(isnull(design) || !(design.build_type & AUTOLATHE))
return
if(surplus() < power_cost)
finalize_build()
return
//check for materials required. For custom material items decode their required materials
var/list/materials_needed = list()
for(var/material in design.materials)
var/amount_needed = design.materials[material]
if(istext(material)) // category
for(var/datum/material/valid_candidate as anything in SSmaterials.materials_by_category[material])
if(materials.get_material_amount(valid_candidate) < amount_needed)
continue
material = valid_candidate
break
if(isnull(material))
return
materials_needed[material] = amount_needed
if(!materials.has_materials(materials_needed))
return
var/craft_time = (design.construction_time * design.lathe_time_factor) ** 0.8
flick_overlay_view(mutable_appearance(icon, "crafter_printing"), craft_time)
print_sound.start()
add_load(power_cost)
busy = addtimer(CALLBACK(src, PROC_REF(do_make_item), design, materials_needed), craft_time, TIMER_UNIQUE | TIMER_STOPPABLE | TIMER_DELETE_ME)
/obj/machinery/power/manufacturing/lathe/proc/do_make_item(datum/design/design, list/materials_needed)
finalize_build()
if(surplus() < power_cost)
return
var/is_stack = ispath(design.build_path, /obj/item/stack)
if(!materials.has_materials(materials_needed))
return
materials.use_materials(materials_needed)
var/atom/movable/created
if(is_stack)
var/obj/item/stack/stack_item = initial(design.build_path)
created = new stack_item(null, 1)
else
created = new design.build_path(null)
split_materials_uniformly(materials_needed, target_object = created)
if(isitem(created))
created.pixel_x = created.base_pixel_x + rand(-6, 6)
created.pixel_y = created.base_pixel_y + rand(-6, 6)
SSblackbox.record_feedback("nested tally", "lathe_printed_items", 1, list("[type]", "[created.type]"))
if(!send_resource(created, dir))
withheld = created
/obj/machinery/power/manufacturing/lathe/proc/finalize_build()
print_sound.stop()
deltimer(busy)
busy = null

View File

@@ -0,0 +1,66 @@
/obj/machinery/power/manufacturing/router // Basically a splitter
name = "manufacturing router"
desc = "Distributes input to 3 output directions equally. Stacks are split, and you may toggle outputs with a multitool. May not receive from other routers."
allow_mob_bump_intake = TRUE
icon_state = "splitter"
circuit = /obj/item/circuitboard/machine/manurouter
/// outputs disabled with a multitool
var/list/disabled_dirs = list()
/// directions we can output to right now
var/list/directions
/obj/machinery/power/manufacturing/router/Initialize(mapload)
. = ..()
directions = GLOB.cardinals.Copy()
/obj/machinery/power/manufacturing/router/multitool_act(mob/living/user, obj/item/tool)
. = ..()
var/to_toggle = get_dir(src, user)
if(!(to_toggle in GLOB.cardinals))
balloon_alert(user, "stand inline!")
return ITEM_INTERACT_FAILURE
if(to_toggle in disabled_dirs)
disabled_dirs -= to_toggle
else
disabled_dirs += to_toggle
update_appearance(UPDATE_OVERLAYS)
balloon_alert(user, "toggled output")
return ITEM_INTERACT_SUCCESS
/obj/machinery/power/manufacturing/router/update_overlays()
. = ..()
for(var/direction in GLOB.cardinals)
var/variant
if(disabled_dirs.Find(direction))
variant = "bl"
else
variant = (direction == dir) ? "in" : "out"
var/image/new_overlay = image(icon, "splitter_[variant]", layer = layer+0.001, dir = direction)
. += new_overlay
/obj/machinery/power/manufacturing/router/receive_resource(obj/receiving, atom/from, receive_dir)
if(istype(from, /obj/machinery/power/manufacturing/router))
return MANUFACTURING_FAIL
var/list/filtered = directions - receive_dir - disabled_dirs
if(!length(filtered))
directions = GLOB.cardinals.Copy()
for(var/target in filtered)
directions -= target
if(isstack(receiving))
receiving = handle_stack(receiving, receive_dir)
if(send_resource(receiving, target))
dir = receive_dir
update_appearance(UPDATE_OVERLAYS) // im sorry
return MANUFACTURING_SUCCESS
return MANUFACTURING_FAIL_FULL
/obj/machinery/power/manufacturing/router/proc/handle_stack(obj/item/stack/stack, direction)
. = stack
var/potential_output_count = length(GLOB.cardinals - direction - disabled_dirs)
if(potential_output_count <= 1)
return
var/split_amount = round(stack.amount / potential_output_count, 1)
if(stack.amount == potential_output_count)
return
var/atom/movable/new_stack = stack.split_stack(amount = min(stack.amount, split_amount))
return new_stack

View File

@@ -0,0 +1,59 @@
/obj/machinery/power/manufacturing/smelter
name = "manufacturing smelter"
desc = "Pretty much incinerates whatever is put into it. Refines ore (not boulders)."
icon_state = "smelter"
circuit = /obj/item/circuitboard/machine/manusmelter
/// power used to smelt
var/power_cost = 4 KILO WATTS
/// our output, if the way out was blocked is held here
var/atom/movable/withheld
/obj/machinery/power/manufacturing/smelter/update_overlays()
. = ..()
. += generate_io_overlays(dir, COLOR_ORANGE) // OUT - stuff in it
. += generate_io_overlays(REVERSE_DIR(dir), COLOR_MODERATE_BLUE) // IN - to crush
/obj/machinery/power/manufacturing/smelter/receive_resource(obj/receiving, atom/from, receive_dir)
if(!isitem(receiving) || surplus() < power_cost || receive_dir != REVERSE_DIR(dir))
return MANUFACTURING_FAIL
var/list/stacks = contents - circuit
if(!may_merge_in_contents(receiving) && length(stacks) >= 5)
return MANUFACTURING_FAIL_FULL
receiving.Move(src, get_dir(receiving, src))
START_PROCESSING(SSmanufacturing, src)
return MANUFACTURING_SUCCESS
/obj/machinery/power/manufacturing/smelter/Destroy()
. = ..()
QDEL_NULL(withheld)
/obj/machinery/power/manufacturing/smelter/atom_destruction(damage_flag)
withheld?.Move(drop_location())
return ..()
/obj/machinery/power/manufacturing/smelter/process(seconds_per_tick)
var/list/stacks = contents - circuit
if(!length(stacks))
return
var/list/stacks_preprocess = contents - circuit
var/obj/item/stack/ore/ore = stacks_preprocess[length(stacks_preprocess)]
if(isnull(ore))
return
if(isnull(withheld) && surplus() >= power_cost)
icon_state="smelter_on"
add_load(power_cost)
if(istype(ore))
var/obj/item/stack/new_stack = new ore.refined_type(null, min(5, ore.amount), FALSE)
new_stack.moveToNullspace()
ore.use(min(5, ore.amount))
ore = new_stack
else
ore.fire_act(1400)
withheld = ore
else if(surplus() < power_cost)
icon_state = "smelter"
if(send_resource(withheld, dir))
withheld = null // nullspace thumbs down
if(!length(contents - circuit))
return PROCESS_KILL //we finished

View File

@@ -0,0 +1,149 @@
/obj/machinery/power/manufacturing/sorter
icon_state = "router"
name = "conveyor sort-router"
desc = "Pushes things on it to its sides following set criteria, set via multitool."
layer = BELOW_OPEN_DOOR_LAYER
density = FALSE
interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND
circuit = /obj/item/circuitboard/machine/manurouter
/// for mappers; filter path = list(direction, value), otherwise a list of initialized filters
var/list/sort_filters = list()
/// dir to push to if there is no criteria
var/dir_if_not_met
/// timer id of the thing that makes stuff move
var/delay_timerid
/// max filters
var/max_filters = 10
/obj/machinery/power/manufacturing/sorter/Initialize(mapload)
. = ..()
if(isnull(dir_if_not_met))
dir_if_not_met = dir
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
for(var/i in 1 to length(sort_filters))
var/creating_type = sort_filters[i]
var/list/values = sort_filters[creating_type]
var/datum/sortrouter_filter/new_type = new creating_type(src)
new_type.dir_target = values[1]
new_type.value = values[2]
sort_filters[i] = new_type
START_PROCESSING(SSobj, src)
/obj/machinery/power/manufacturing/sorter/Destroy()
. = ..()
QDEL_LIST(sort_filters)
/obj/machinery/power/manufacturing/sorter/multitool_act(mob/living/user, obj/item/tool)
. = ..()
ui_interact(user)
/obj/machinery/power/manufacturing/sorter/receive_resource(atom/movable/receiving, atom/from, receive_dir)
if(length(loc.contents) >= MANUFACTURING_TURF_LAG_LIMIT)
return MANUFACTURING_FAIL_FULL
receiving.Move(loc)
return MANUFACTURING_SUCCESS
/obj/machinery/power/manufacturing/sorter/ui_data(mob/user)
. = list()
.["unmet_dir"] = dir_if_not_met
.["filters"] = list()
for(var/datum/sortrouter_filter/sorting as anything in sort_filters)
.["filters"] += list(list(
"name" = sorting.return_name(),
"ref" = REF(sorting),
"inverted" = sorting.inverted,
"dir" = sorting.dir_target,
))
/obj/machinery/power/manufacturing/sorter/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
switch(action)
if("del_filter")
var/datum/sortrouter_filter/filter = locate(params["ref"])
if(isnull(filter))
return
sort_filters -= filter
qdel(filter)
return TRUE
if("new_filter")
if(length(sort_filters) >= max_filters)
return
var/static/list/filter_by_name
if(!length(filter_by_name))
filter_by_name = list()
for(var/datum/sortrouter_filter/to_do as anything in subtypesof(/datum/sortrouter_filter))
filter_by_name[initial(to_do.name)] = to_do
filter_by_name = sort_list(filter_by_name)
var/target_type = tgui_input_list(usr, "Select a filter", "New Filter", filter_by_name)
if(isnull(target_type)|| !usr.can_perform_action(src, ALLOW_SILICON_REACH))
return
target_type = filter_by_name[target_type]
sort_filters += new target_type(src)
return TRUE
if("rotate")
var/datum/sortrouter_filter/filter = locate(params["ref"])
if(isnull(filter))
return
var/next_ind = GLOB.cardinals.Find(filter.dir_target) + 1
filter.dir_target = GLOB.cardinals[WRAP(next_ind, 1, 5)]
return TRUE
if("rotate_unmet")
var/next_ind = GLOB.cardinals.Find(dir_if_not_met) + 1
dir_if_not_met = GLOB.cardinals[WRAP(next_ind, 1, 5)]
return TRUE
if("edit")
var/datum/sortrouter_filter/filter = locate(params["ref"])
if(isnull(filter))
return
filter.edit(usr)
return TRUE
if("shift")
var/datum/sortrouter_filter/filter = locate(params["ref"])
if(isnull(filter))
return
var/next_ind = WRAP(sort_filters.Find(filter) + text2num(params["amount"]), 1, length(sort_filters)+1)
sort_filters -= filter
sort_filters.Insert(next_ind, filter)
return TRUE
/obj/machinery/power/manufacturing/sorter/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "ManufacturingSorter")
ui.open()
/obj/machinery/power/manufacturing/sorter/proc/send_nomobs(atom/movable/moving, dir)
var/mutable_appearance/operate = mutable_appearance(icon, "router_operate")
operate.dir = dir
flick_overlay_view(operate, 1 SECONDS)
return ismob(moving) ? moving.Move(get_step(src,dir), dir) : send_resource(moving, dir)
/obj/machinery/power/manufacturing/sorter/process()
if(delay_timerid || !length(loc?.contents - 1))
return
launch_everything()
/obj/machinery/power/manufacturing/sorter/proc/on_entered(datum/source, atom/movable/mover)
SIGNAL_HANDLER
if(mover == src || !istype(mover) || mover.anchored || delay_timerid)
return
delay_timerid = addtimer(CALLBACK(src, PROC_REF(launch_everything)), 0.2 SECONDS)
/obj/machinery/power/manufacturing/sorter/proc/launch_everything()
delay_timerid = null
var/turf/where_we_at = get_turf(src)
for(var/atom/movable/mover as anything in where_we_at.contents)
if(mover.anchored)
continue
for(var/datum/sortrouter_filter/sorting as anything in sort_filters)
if(sorting.meets_conditions(mover) == sorting.inverted)
continue
send_nomobs(mover, sorting.dir_target)
return
send_nomobs(mover, dir_if_not_met)

View File

@@ -0,0 +1,120 @@
/datum/sortrouter_filter
/// name of the filter shown in UI
var/name
/// if it meets criteria, item is pushed to this direction
var/dir_target = NORTH
/// value of our filter, checked by us
var/value = ""
/// is our output inverted? checked by sorter
var/inverted = FALSE
/// the sorter we belong to
var/obj/machinery/power/manufacturing/sorter/sorter
/datum/sortrouter_filter/New(sorter)
. = ..()
if(isnull(sorter))
return
src.sorter = sorter
/datum/sortrouter_filter/Destroy()
. = ..()
if(isnull(sorter))
return
sorter = null
/datum/sortrouter_filter
/datum/sortrouter_filter/proc/return_name()
return name
/datum/sortrouter_filter/proc/edit(mob/user)
to_chat(user, "This filter is not editable.")
/datum/sortrouter_filter/proc/meets_conditions(atom/checking)
/datum/sortrouter_filter/is_stack
name = "input is stack"
/datum/sortrouter_filter/is_stack/meets_conditions(atom/checking)
return isstack(checking)
/datum/sortrouter_filter/is_ore
name = "input is ore"
/datum/sortrouter_filter/is_ore/meets_conditions(atom/checking)
return istype(checking, /obj/item/stack/ore)
/datum/sortrouter_filter/is_mail
name = "input is mail"
/datum/sortrouter_filter/is_mail/meets_conditions(atom/checking)
return istype(checking, /obj/item/mail)
/datum/sortrouter_filter/is_tagged
name = "input is tagged X"
/datum/sortrouter_filter/is_tagged/edit(mob/user)
var/target = tgui_input_list(user, "Select a tag", "Tag", sort_list(GLOB.TAGGERLOCATIONS))
if(isnull(target) || !user.can_perform_action(sorter, ALLOW_SILICON_REACH))
return
value = GLOB.TAGGERLOCATIONS.Find(target)
/datum/sortrouter_filter/is_tagged/return_name()
return "input is tagged [value ? GLOB.TAGGERLOCATIONS[value] : ""]"
/datum/sortrouter_filter/is_tagged/meets_conditions(checking)
var/obj/item/delivery/mail_or_delivery = checking
var/sort_tag
if(istype(checking, /obj/item/delivery) || istype(checking, /obj/item/mail))
sort_tag = mail_or_delivery.sort_tag
return value == sort_tag
/datum/sortrouter_filter/name_contains
name = "input's name contains"
/datum/sortrouter_filter/name_contains/edit(mob/user)
var/target = tgui_input_text(user, "What should it contain?", "Name", value, 12)
if(isnull(target)|| !user.can_perform_action(sorter, ALLOW_SILICON_REACH))
return
value = target
/datum/sortrouter_filter/name_contains/return_name()
return "input's name contains [value]"
/datum/sortrouter_filter/name_contains/meets_conditions(atom/checking)
return findtext(LOWER_TEXT(checking.name), value)
/datum/sortrouter_filter/is_path_specific
name = "input is specific item"
/// are we currently listening for an item to set as our filter?
var/currently_listening = FALSE
/datum/sortrouter_filter/is_path_specific/edit(mob/user)
name = initial(name)
if(!currently_listening)
name = "awaiting item"
to_chat(user, "Hit the sorter with the item of choice to set the filter.")
sorter.balloon_alert(user, "awaiting item!")
currently_listening = TRUE
RegisterSignal(sorter, COMSIG_ATOM_ATTACKBY, PROC_REF(sorter_hit))
else
currently_listening = FALSE
UnregisterSignal(sorter, COMSIG_ATOM_ATTACKBY)
/datum/sortrouter_filter/is_path_specific/proc/sorter_hit(datum/source, obj/item/attacking_item, user, params)
currently_listening = FALSE
value = attacking_item.type
name = attacking_item.name
sorter.balloon_alert(user, "filter set")
UnregisterSignal(sorter, COMSIG_ATOM_ATTACKBY)
return COMPONENT_NO_AFTERATTACK
/datum/sortrouter_filter/is_path_specific/meets_conditions(atom/checking)
return checking.type == value
/datum/sortrouter_filter/is_path_specific/subtypes
name = "input is specific kind of item"
/datum/sortrouter_filter/is_path_specific/subtypes/meets_conditions(atom/checking)
return istype(checking.type, value)

View File

@@ -0,0 +1,46 @@
/obj/machinery/power/manufacturing/storagebox
name = "manufacturing storage unit"
desc = "Its basically a box. Receives resources (if anchored). Needs a machine to take stuff out of without dumping everything out."
icon_state = "box"
/// how much can we hold
var/max_stuff = 16
/obj/machinery/power/manufacturing/request_resource() //returns last inserted item
var/list/real_contents = contents - circuit
if(!length(real_contents))
return
return (real_contents)[length(real_contents)]
/obj/machinery/power/manufacturing/storagebox/receive_resource(atom/movable/receiving, atom/from, receive_dir)
if(iscloset(receiving) && length(receiving.contents))
return MANUFACTURING_FAIL
if(!may_merge_in_contents(receiving) && length(contents - circuit) >= max_stuff)
return MANUFACTURING_FAIL_FULL
receiving.Move(src,receive_dir)
return MANUFACTURING_SUCCESS
/obj/machinery/power/manufacturing/storagebox/container_resist_act(mob/living/user)
. = ..()
user.Move(drop_location())
/obj/machinery/power/manufacturing/storagebox/screwdriver_act(mob/living/user, obj/item/tool)
. = NONE
balloon_alert(user, "disassembling...")
if(!do_after(user, 5 SECONDS, src))
return ITEM_INTERACT_FAILURE
atom_destruction()
return ITEM_INTERACT_SUCCESS
/obj/machinery/power/manufacturing/storagebox/atom_destruction(damage_flag)
new /obj/item/stack/sheet/iron(drop_location(), 10)
dump_inventory_contents()
return ..()
/obj/machinery/power/manufacturing/storagebox/attack_hand(mob/living/user, list/modifiers)
. = ..()
if(user.combat_mode)
return
balloon_alert(user, "dumping..")
if(!do_after(user, 1.25 SECONDS, src))
return
dump_inventory_contents()

View File

@@ -0,0 +1,78 @@
/obj/machinery/power/manufacturing/unloader
name = "manufacturing crate unloader"
desc = "Unloads crates (and ore boxes) passed into it, ejecting the empty crate to the side and its contents forwards. Use a multitool to flip the crate output."
icon = 'icons/obj/machines/mining_machines.dmi'
icon_state = "unloader-corner"
circuit = /obj/item/circuitboard/machine/manuunloader
/// power used per attempt to unload a crate
var/power_to_unload_crate = 2 KILO WATTS
/// whether the side we output unloaded crates is flipped
var/flip_side = FALSE
/obj/machinery/power/manufacturing/unloader/update_overlays()
. = ..()
. += generate_io_overlays(dir, COLOR_ORANGE) // OUT - stuff in it
. += generate_io_overlays(REVERSE_DIR(dir), COLOR_MODERATE_BLUE) // IN - crate
. += generate_io_overlays(turn(dir, flip_side ? 90 : -90), COLOR_ORANGE) // OUT -- empty crate
/obj/machinery/power/manufacturing/unloader/request_resource() //returns held crate if someone wants to do that for some reason
var/list/real_contents = contents - circuit
if(!length(real_contents))
return
return (real_contents)[1]
/obj/machinery/power/manufacturing/unloader/multitool_act(mob/living/user, obj/item/tool)
. = ..()
balloon_alert(user, "flipped")
flip_side = !flip_side
update_appearance()
/obj/machinery/power/manufacturing/unloader/receive_resource(obj/receiving, atom/from, receive_dir)
if(surplus() < power_to_unload_crate || receive_dir != REVERSE_DIR(dir))
return MANUFACTURING_FAIL
var/list/real_contents = contents - circuit
if(length(real_contents))
return MANUFACTURING_FAIL_FULL
var/obj/structure/closet/as_closet = receiving
var/obj/structure/ore_box/as_orebox = receiving
if(istype(as_closet))
if(!as_closet.can_open())
return MANUFACTURING_FAIL
else if(!istype(as_orebox))
return MANUFACTURING_FAIL
receiving.Move(src, get_dir(receiving, src))
START_PROCESSING(SSfastprocess, src)
return MANUFACTURING_SUCCESS
/obj/machinery/power/manufacturing/unloader/process(seconds_per_tick)
var/list/real_contents = contents - circuit
if(!length(real_contents))
return PROCESS_KILL
if(surplus() < power_to_unload_crate)
return
add_load(power_to_unload_crate)
var/obj/structure/closet/closet = real_contents[1]
if(istype(closet))
return unload_crate(closet)
else
return unload_orebox(closet)
/obj/machinery/power/manufacturing/unloader/proc/unload_crate(obj/structure/closet/closet)
if (!closet.contents_initialized)
closet.contents_initialized = TRUE
closet.PopulateContents()
SEND_SIGNAL(closet, COMSIG_CLOSET_CONTENTS_INITIALIZED)
for(var/atom/thing as anything in closet.contents)
if(ismob(thing))
continue
send_resource(thing, dir)
if(!length(closet.contents) && send_resource(closet, turn(dir, flip_side ? 90 : -90)))
closet.open(force = TRUE)
return PROCESS_KILL
/obj/machinery/power/manufacturing/unloader/proc/unload_orebox(obj/structure/ore_box/box)
for(var/atom/thing as anything in box.contents)
send_resource(thing, dir)
if(!length(box.contents) && send_resource(box, turn(dir, flip_side ? 90 : -90)))
return PROCESS_KILL

View File

@@ -34,15 +34,18 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
var/flipped = FALSE
/// Are we currently conveying items?
var/conveying = FALSE
//Direction -> if we have a conveyor belt in that direction
///Direction -> if we have a conveyor belt in that direction
var/list/neighbors
/// are we operating in wire power mode
var/wire_mode = FALSE
/// weakref to attached cable if wire mode
var/datum/weakref/attached_wire_ref
/obj/machinery/conveyor/Initialize(mapload, new_dir, new_id)
. = ..()
AddElement(/datum/element/footstep_override, priority = STEP_SOUND_CONVEYOR_PRIORITY)
AddElement(/datum/element/give_turf_traits, string_list(list(TRAIT_TURF_IGNORE_SLOWDOWN)))
register_context()
if(new_dir)
setDir(new_dir)
if(new_id)
@@ -58,6 +61,9 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
AddElement(/datum/element/connect_loc, loc_connections)
update_move_direction()
LAZYADD(GLOB.conveyors_by_id[id], src)
if(wire_mode)
update_cable()
START_PROCESSING(SSmachines, src)
/obj/machinery/conveyor/examine(mob/user)
. = ..()
@@ -66,6 +72,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
. += "\nLeft-click with a <b>wrench</b> to rotate."
. += "Left-click with a <b>screwdriver</b> to invert its direction."
. += "Right-click with a <b>screwdriver</b> to flip its belt around."
. += "Left-click with a <b>multitool</b> to toggle whether this conveyor receives power via cable. Toggling connects and disconnects."
. += "Using another <b>conveyor belt assembly</b> on this will place a <b>new conveyor belt<b> in the direction this one is pointing."
/obj/machinery/conveyor/add_context(atom/source, list/context, obj/item/held_item, mob/user)
@@ -80,6 +87,9 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
context[SCREENTIP_CONTEXT_LMB] = "Invert conveyor belt"
context[SCREENTIP_CONTEXT_RMB] = "Flip conveyor belt"
return CONTEXTUAL_SCREENTIP_SET
if(held_item?.tool_behaviour == TOOL_MULTITOOL)
context[SCREENTIP_CONTEXT_LMB] = "Toggle conveyor belt wire mode"
return CONTEXTUAL_SCREENTIP_SET
/obj/machinery/conveyor/centcom_auto
id = "round_end_belt"
@@ -118,6 +128,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
/obj/machinery/conveyor/Destroy()
set_operating(FALSE)
LAZYREMOVE(GLOB.conveyors_by_id[id], src)
attached_wire_ref = null
return ..()
/obj/machinery/conveyor/vv_edit_var(var_name, var_value)
@@ -295,7 +306,16 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
inverted = !inverted
update_move_direction()
to_chat(user, span_notice("You set [src]'s direction [inverted ? "backwards" : "back to default"]."))
else if(attacking_item.tool_behaviour == TOOL_MULTITOOL)
attacking_item.play_tool_sound(src)
wire_mode = !wire_mode
update_cable()
power_change()
if(wire_mode)
START_PROCESSING(SSmachines, src)
else
STOP_PROCESSING(SSmachines, src)
to_chat(user, span_notice("You set [src]'s wire mode [wire_mode ? "on" : "off"]."))
else if(istype(attacking_item, /obj/item/stack/conveyor))
// We should place a new conveyor belt machine on the output turf the conveyor is pointing to.
var/turf/target_turf = get_step(get_turf(src), forwards)
@@ -334,10 +354,51 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
return
user.Move_Pulled(src)
/obj/machinery/conveyor/powered(chan = power_channel, ignore_use_power = FALSE)
if(!wire_mode)
return ..()
var/datum/powernet/powernet = get_powernet()
if(!isnull(powernet))
return clamp(powernet.avail-powernet.load, 0, powernet.avail) >= active_power_usage
return ..()
/obj/machinery/conveyor/power_change()
. = ..()
update()
/obj/machinery/conveyor/process()
if(!wire_mode)
return PROCESS_KILL
if(isnull(attached_wire_ref))
update_cable()
return
var/datum/powernet/powernet = get_powernet()
if(isnull(powernet))
return
if(powered())
powernet.load += active_power_usage
else
power_change()
/obj/machinery/conveyor/proc/update_cable()
if(!wire_mode)
attached_wire_ref = null
return
var/turf/our_turf = get_turf(src)
attached_wire_ref = WEAKREF(locate(/obj/structure/cable) in our_turf)
if(attached_wire_ref)
return power_change()
/obj/machinery/conveyor/proc/get_powernet()
if(!wire_mode)
return
var/obj/structure/cable/cable = attached_wire_ref.resolve()
if(isnull(cable))
attached_wire_ref = null
return
return cable.powernet
// Conveyor switch
/obj/machinery/conveyor_switch
name = "conveyor switch"

View File

@@ -1257,3 +1257,73 @@
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE | DEPARTMENT_BITFLAG_ENGINEERING | DEPARTMENT_BITFLAG_CARGO | DEPARTMENT_BITFLAG_SERVICE
/datum/design/board/manulathe
name = /obj/machinery/power/manufacturing/lathe::name
desc = "The circuit board for this machine."
id = "manulathe"
build_path = /obj/machinery/power/manufacturing/lathe
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING
)
departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING | DEPARTMENT_BITFLAG_CARGO
/datum/design/board/manucrafter
name = /obj/machinery/power/manufacturing/crafter::name
desc = "The circuit board for this machine."
id = "manucrafter"
build_path = /obj/machinery/power/manufacturing/crafter
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING
)
departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING | DEPARTMENT_BITFLAG_CARGO
/datum/design/board/manucrusher
name = /obj/machinery/power/manufacturing/crusher::name
desc = "The circuit board for this machine."
id = "manucrusher"
build_path = /obj/machinery/power/manufacturing/crusher
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING
)
departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING | DEPARTMENT_BITFLAG_CARGO
/datum/design/board/manurouter
name = /obj/machinery/power/manufacturing/router::name
desc = "The circuit board for this machine."
id = "manurouter"
build_path = /obj/machinery/power/manufacturing/router
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING
)
departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING | DEPARTMENT_BITFLAG_CARGO
/datum/design/board/manusorter
name = /obj/machinery/power/manufacturing/sorter::name
desc = "The circuit board for this machine."
id = "manusorter"
build_path = /obj/machinery/power/manufacturing/sorter
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING
)
departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING | DEPARTMENT_BITFLAG_CARGO
/datum/design/board/manuunloader
name = /obj/machinery/power/manufacturing/unloader::name
desc = "The circuit board for this machine."
id = "manuunloader"
build_path = /obj/machinery/power/manufacturing/unloader
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING
)
departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING | DEPARTMENT_BITFLAG_CARGO
/datum/design/board/manusmelter
name = /obj/machinery/power/manufacturing/smelter::name
desc = "The circuit board for this machine."
id = "manusmelter"
build_path = /obj/machinery/power/manufacturing/smelter
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING
)
departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING | DEPARTMENT_BITFLAG_CARGO

View File

@@ -148,6 +148,13 @@
"light_tube",
"crossing_signal",
"guideway_sensor",
"manuunloader",
"manusmelter",
"manucrusher",
"manucrafter",
"manulathe",
"manusorter",
"manurouter",
)
/datum/techweb_node/energy_manipulation

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@@ -758,6 +758,7 @@
#include "code\controllers\subsystem\processing\fishing.dm"
#include "code\controllers\subsystem\processing\greyscale.dm"
#include "code\controllers\subsystem\processing\instruments.dm"
#include "code\controllers\subsystem\processing\manufacturing.dm"
#include "code\controllers\subsystem\processing\obj.dm"
#include "code\controllers\subsystem\processing\plumbing.dm"
#include "code\controllers\subsystem\processing\processing.dm"
@@ -4576,6 +4577,17 @@
#include "code\modules\mafia\roles\town\town_killing.dm"
#include "code\modules\mafia\roles\town\town_protective.dm"
#include "code\modules\mafia\roles\town\town_support.dm"
#include "code\modules\manufactorio\_manufacturing.dm"
#include "code\modules\manufactorio\machines\crafter.dm"
#include "code\modules\manufactorio\machines\crusher.dm"
#include "code\modules\manufactorio\machines\debug.dm"
#include "code\modules\manufactorio\machines\lathe.dm"
#include "code\modules\manufactorio\machines\router.dm"
#include "code\modules\manufactorio\machines\smelter.dm"
#include "code\modules\manufactorio\machines\sorter.dm"
#include "code\modules\manufactorio\machines\sorter_filters.dm"
#include "code\modules\manufactorio\machines\storagebox.dm"
#include "code\modules\manufactorio\machines\unloader.dm"
#include "code\modules\mapfluff\centcom\nuke_ops.dm"
#include "code\modules\mapfluff\ruins\generic.dm"
#include "code\modules\mapfluff\ruins\lavaland_ruin_code.dm"

View File

@@ -0,0 +1,113 @@
import { BooleanLike } from 'common/react';
import { useBackend } from '../backend';
import { Box, Button, Icon, LabeledList, Section, Stack } from '../components';
import { Window } from '../layouts';
type Data = {
filters: Filter[];
unmet_dir: number;
};
function dir2icon(dir) {
switch (dir) {
case 1:
return 'arrow-up';
case 2:
return 'arrow-down';
case 4:
return 'arrow-right';
case 8:
return 'arrow-left';
default:
return 'arrow-up';
}
}
type Filter = {
name: string;
ref: string;
inverted: BooleanLike;
dir: number;
};
export function ManufacturingSorter(props) {
const { act, data } = useBackend<Data>();
const { filters, unmet_dir } = data;
return (
<Window width={450} height={350} title="Manufacturing Sorter">
<Window.Content>
<Stack vertical fill>
<Stack.Item height="90%">
<Section
title="Filters"
height="100%"
overflowY="auto"
buttons={
<Button
color="green"
icon="plus"
onClick={() => act('new_filter')}
>
New filter
</Button>
}
>
<LabeledList>
{filters.map((filter, i) => (
<LabeledList.Item
label={i + 1 + '. ' + filter.name}
key={filter.ref}
>
<Button
icon={dir2icon(filter.dir)}
onClick={() => act('rotate', { ref: filter.ref })}
>
Output
</Button>
<Button onClick={() => act('edit', { ref: filter.ref })}>
<Icon ml="0.2rem" name="pencil" />
</Button>
<Button
color="red"
onClick={() => act('del_filter', { ref: filter.ref })}
>
<Icon ml="0.2rem" name="trash" />
</Button>
<Button
onClick={() =>
act('shift', { ref: filter.ref, amount: -1 })
}
>
<Icon ml="0.2rem" name="arrow-up" />
</Button>
<Button
onClick={() =>
act('shift', { ref: filter.ref, amount: 1 })
}
>
<Icon ml="0.2rem" name="arrow-down" />
</Button>
</LabeledList.Item>
))}
</LabeledList>
</Section>
</Stack.Item>
<Stack.Item>
<Stack>
<Stack.Item>
<Box>If no criteria is met, outputting to:</Box>
</Stack.Item>
<Stack.Item>
<Button onClick={() => act('rotate_unmet')}>
<Icon ml="0.2rem" name={dir2icon(unmet_dir)} />
</Button>
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Window.Content>
</Window>
);
}