Ported the Microlathe from Bay, and began autolathe refactor: Take II (#21668)

Continued from #19839

> Part 1 of a refactor to try and make our various fabricators use the
same system and basic code. This adds the microlathe as a proof of
concept to the whole refactor, ported from Baystation. Currently not
mapped anywhere.

> This also ports over Nebula's autolathe working sound, however it is
adapted to fit our code. As such, I went to the original sound and
re-cut it, now including start and stop sounds.

Plan is overall to make it much easier to add new types of fabricators. 

### Asset Licenses
The following assets that **have not** been created by myself are
included in this PR:

| Path | Original Author | License |
| --- | --- | --- |
| icons/obj/machinery/fabricators/microlathe.dmi |
[BloodyMan](https://github.com/BloodyMan) (Baystation 12) | CC-BY-SA |
| sound/machines/fabricators/autolathe/ |
[pencilina](https://freesound.org/people/pencilina/) | CC-0 |

---------

Co-authored-by: Cody Brittain <cbrittain10@live.com>
This commit is contained in:
Cody Brittain
2026-01-14 19:29:32 +00:00
committed by GitHub
co-authored by Cody Brittain
parent 5e69ccbd6a
commit ccbfbdc50a
146 changed files with 1406 additions and 955 deletions
+287
View File
@@ -0,0 +1,287 @@
ABSTRACT_TYPE(/obj/machinery/fabricator)
density = TRUE
anchored = TRUE
use_power = POWER_USE_IDLE
idle_power_usage = 10
active_power_usage = 2000
clicksound = /singleton/sound_category/keyboard_sound
clickvol = 30
manufacturer = "hephaestus"
/// What location to print to. Only used for mounted autolathes
var/atom/print_loc
/// Class of fabricator. Determines recipes loaded. See entries in [__fabricator_defines.dm]
var/fabricator_class = FABRICATOR_CLASS_GENERAL
/// List of stored materials.
var/list/stored_material = list()
/// List of material capacities. Should not be modified directly, use [var/list/base_storage_capacity] instead.
var/list/storage_capacity = list()
/// Base storage capacity, which is modified by default by the amount and tier of matter bins.
var/list/base_storage_capacity = list(
DEFAULT_WALL_MATERIAL = 25000,
MATERIAL_ALUMINIUM = 25000,
MATERIAL_GLASS = 12500,
MATERIAL_PLASTIC = 12500,
MATERIAL_PHORON = 12500
)
/// Current category to show for this fabricator
var/show_category = "All"
/// Status bitflags for the fabricator
var/fab_status_flags = 0
/// Current queue for this fabricator
var/list/print_queue = list()
/// What is currently printing in this fabricator
var/datum/fabricator_build_order/currently_printing
/// Global list of the substances currently stored in the fabricator
var/static/list/stored_substances_to_names = list()
/// How efficiently this fabricator uses materials. Modified by default by the amount and tier of micro lasers
var/mat_efficiency = 1
/// What to multiply build times by. Modified by default by the amount and tier of manipulators
var/build_time_multiplier = 1
/// Snowflake for mounted autolathes
var/does_flick = TRUE
/// The fabricator's wires
var/datum/wires/fabricator/wires
component_types = list(
/obj/item/circuitboard/autolathe,
/obj/item/stock_parts/matter_bin = 3,
/obj/item/stock_parts/micro_laser,
/obj/item/stock_parts/manipulator,
/obj/item/stock_parts/console_screen
)
///The sound this fabricator will emit while running
var/fabricating_sound_loop = /datum/looping_sound/fabricator
///The looping sound used while the fabricator is running
VAR_PRIVATE/datum/looping_sound/fabricator_looping_sound
/obj/machinery/fabricator/upgrade_hints(mob/user, distance, is_adjacent)
. += ..()
. += "- Upgraded <b>matter bins</b> will increase material storage capacity."
. += SPAN_NOTICE(" - The current storage limit per material type is <b>[storage_capacity[DEFAULT_WALL_MATERIAL] / 2000]</b> sheets")
. += "- Upgraded <b>micro lasers</b> will improve material use efficiency."
. += SPAN_NOTICE(" - The current material cost reduction is <b>[round((1 - mat_efficiency) * 100)]%</b>")
. += "- Upgraded <b>manipulators</b> will increase the fabrication speed."
. += SPAN_NOTICE(" - The current build speed increase is <b>[round(build_time_multiplier * 100)]%</b>")
/obj/machinery/fabricator/Initialize(mapload)
wires = new(src)
print_loc = src
stored_material = list()
for(var/mat in base_storage_capacity)
stored_material[mat] = 0
// Update global type to string cache.
if(!stored_substances_to_names[mat])
if(ispath(mat, /material))
var/material/mat_instance = mat
mat_instance = SSmaterials.get_material_by_name(initial(mat_instance.name))
if(istype(mat_instance))
stored_substances_to_names[mat] = mat_instance.display_name
else if(ispath(mat, /singleton/reagent))
var/singleton/reagent/reg = mat
stored_substances_to_names[mat] = initial(reg.name)
update_icon()
. = ..()
/obj/machinery/fabricator/Destroy()
print_loc = null
QDEL_NULL(currently_printing)
QDEL_NULL(wires)
QDEL_LIST(print_queue)
QDEL_NULL(fabricator_looping_sound)
return ..()
/obj/machinery/fabricator/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Autolathe", capitalize_first_letters(name))
ui.open()
/obj/machinery/fabricator/ui_data(mob/user)
. = ..()
var/list/data = list()
data["manufacturer"] = manufacturer
data["disabled"] = (fab_status_flags & FAB_DISABLED)
data["material_efficiency"] = mat_efficiency
data["materials"] = list()
data["categories"] = SSfabrication.get_categories(fabricator_class)|"All"
data["build_time"] = currently_printing?.remaining_time
data["show_category"] = show_category
for(var/material in stored_material)
data["materials"] += list(list("material" = material, "stored" = stored_material[material], "max_capacity" = storage_capacity[material]))
data["recipes"] = list()
for(var/recipe in SSfabrication.get_recipes(fabricator_class))
var/singleton/fabricator_recipe/R = recipe
if(R.hack_only && !(fab_status_flags & FAB_HACKED))
continue
var/list/recipe_data = list()
recipe_data["name"] = R.name
recipe_data["recipe"] = R.type
recipe_data["security_level"] = R.security_level ? capitalize(num2seclevel(R.security_level)) : "None"
recipe_data["hack_only"] = R.hack_only
recipe_data["enabled"] = can_print_item(R)
var/list/resources = list()
for(var/resource in R.resources)
resources += "[R.resources[resource] * mat_efficiency] [resource]"
recipe_data["sheets"] = stored_material[resource]/round(R.resources[resource]*mat_efficiency)
recipe_data["can_make"] = !isnull(stored_material[resource]) && stored_material[resource] < round(R.resources[resource]*mat_efficiency)
recipe_data["category"] = R.category
recipe_data["resources"] = english_list(resources)
recipe_data["build_time"] = (R.build_time / 10)
recipe_data["max_sheets"] = null
if(R.is_stack)
var/obj/item/stack/R_stack = R.path
recipe_data["max_sheets"] = initial(R_stack.max_amount)
data["recipes"] += list(recipe_data)
data["currently_printing"] = null
if(currently_printing)
data["currently_printing"] = REF("[currently_printing]")
data["queue"] = list()
for(var/datum/fabricator_build_order/AR in print_queue)
data["queue"] += list(
list(
"ref" = REF(AR),
"order" = AR.target_recipe.name,
"path" = AR.target_recipe.type,
"multiplier" = AR.multiplier,
"build_time" = AR.target_recipe.build_time,
"progress" = AR.target_recipe.build_time - AR.remaining_time,
"remaining_time" = AR.remaining_time
)
)
return data
/obj/machinery/fabricator/attackby(obj/item/attacking_item, mob/user)
if(fab_status_flags & FAB_BUSY)
to_chat(user, SPAN_NOTICE("\The [src] is busy. Please wait for the completion of previous operation."))
return TRUE
if(default_deconstruction_screwdriver(user, attacking_item))
SStgui.update_uis(src)
return TRUE
if(default_deconstruction_crowbar(user, attacking_item))
return TRUE
if(default_part_replacement(user, attacking_item))
return TRUE
if(stat)
return TRUE
if(panel_open)
//Don't eat multitools or wirecutters used on an open lathe.
if(attacking_item.ismultitool() || attacking_item.iswirecutter())
if(panel_open)
wires.interact(user)
else
to_chat(user, SPAN_WARNING("\The [src]'s wires aren't exposed."))
return TRUE
if(attacking_item.loc != user && !istype(attacking_item, /obj/item/stack))
return FALSE
if(is_robot_module(attacking_item))
return FALSE
load_lathe(attacking_item, user)
return TRUE
/obj/machinery/fabricator/attack_hand(mob/user)
user.set_machine(src)
ui_interact(user)
///
/obj/machinery/fabricator/proc/is_functioning()
. = use_power != POWER_USE_OFF && !(fab_status_flags & FAB_DISABLED)
/obj/machinery/fabricator/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
usr.set_machine(src)
add_fingerprint(usr)
playsound(src, /singleton/sound_category/keyboard_sound, 50)
if(action == "make")
START_PROCESSING_MACHINE(src, MACHINERY_PROCESS_SELF)
var/multiplier = text2num(params["multiplier"])
var/singleton/fabricator_recipe/R = GET_SINGLETON(text2path(params["recipe"]))
if(!istype(R))
CRASH("Unknown recipe given! [R], param is [params["recipe"]].")
intent_message(MACHINE_SOUND)
try_queue_build(R, multiplier)
. = TRUE
if(action == "remove")
var/datum/fabricator_build_order/order = locate(params["ref"])
try_cancel_build(order)
. = TRUE
/obj/machinery/fabricator/process(seconds_per_tick)
..()
if(use_power == POWER_USE_ACTIVE && (fab_status_flags & FAB_BUSY))
update_current_build(seconds_per_tick)
/obj/machinery/fabricator/update_icon()
if(!does_flick)
return
ClearOverlays()
if(panel_open)
AddOverlays("[icon_state]_panel")
if(currently_printing)
AddOverlays(emissive_appearance(icon, "[icon_state]_lights_working"))
AddOverlays("[icon_state]_lights_working")
AddOverlays("[icon_state]_process")
else if(powered())
AddOverlays(emissive_appearance(icon, "[icon_state]_lights"))
AddOverlays("[icon_state]_lights")
/obj/machinery/fabricator/proc/remove_mat_overlay(mat_overlay)
CutOverlays(mat_overlay)
update_icon()
/obj/machinery/fabricator/RefreshParts()
..()
var/mb_rating = 0
var/man_rating = 0
var/las_rating = 0
for(var/obj/item/stock_parts/matter_bin/MB in component_parts)
mb_rating += MB.rating
for(var/obj/item/stock_parts/manipulator/M in component_parts)
man_rating += M.rating
for(var/obj/item/stock_parts/micro_laser/L in component_parts)
las_rating += L.rating
for(var/mat in base_storage_capacity)
storage_capacity[mat] = mb_rating * base_storage_capacity[mat]
mat_efficiency = 1.1 - (las_rating * 0.1) // Normally, price is 1.25 the amount of material, so this shouldn't go higher than 0.8. Maximum rating of parts is 3
build_time_multiplier = initial(build_time_multiplier) * man_rating
/obj/machinery/fabricator/dismantle()
for(var/mat in stored_material)
var/material/M = SSmaterials.get_material_by_name(mat)
if(!istype(M))
continue
var/obj/item/stack/material/S = new M.stack_type(get_turf(src))
if(stored_material[mat] > S.perunit)
S.amount = round(stored_material[mat] / S.perunit)
else
qdel(S)
..()
return TRUE
@@ -0,0 +1,19 @@
/// Queue items are needed so that the queue knows exactly what it's doing.
/datum/fabricator_build_order
/// The recipe singleton. We need to know exactly what we're making.
var/singleton/fabricator_recipe/target_recipe
/// Multiplier, used to know how many sheets we are printing. Note that this is specifically for sheets.
var/multiplier = 1
/// The materials used for this order.
var/list/earmarked_materials = list()
/// Remaining time for this order.
var/remaining_time = 0
/datum/fabricator_build_order/New(singleton/fabricator_recipe/_target_recipe, _multiplier = 1)
..()
target_recipe = _target_recipe
multiplier = _multiplier
/datum/fabricator_build_order/Destroy()
target_recipe = null
. = ..()
@@ -0,0 +1,21 @@
/singleton/fabricator_recipe
/// Name to show in the fabricator for this recipe
var/name = "object"
/// Path of the object to print
var/path
/// If true, the fabricator needs to be hacked before it can print this design
var/hack_only
/// If set, the ship needs to be at this alert level before fabricators on it can print this design. Ignored if hacked
var/security_level
/// What category will the recipe appear in?
var/category
/// What resources the recipe needs. Defaults to the amount of materials inside the object, multiplied by 1.25
var/list/resources
/// Whether to treat the object as a stack or not. Will show multipliers for building
var/is_stack
/// What types of fabricators will this recipe appear in?
var/list/fabricator_types = list(
FABRICATOR_CLASS_GENERAL
)
/// Build time for the recipe. Defaults to 5 SECONDS
var/build_time = 5 SECONDS
@@ -0,0 +1,145 @@
ABSTRACT_TYPE(/singleton/fabricator_recipe/ammunition)
name = "Abstract Ammunition"
category = "Ammunition"
/singleton/fabricator_recipe/ammunition/New()
..()
if(ispath(path, /obj/item/ammo_pile))
var/obj/item/ammo_pile/pile = path
var/ammo_type = initial(pile.ammo_type)
var/obj/item/ammo_casing/ammo = new ammo_type
var/list/ammo_matter = ammo.matter.Copy()
resources = ammo_matter
for(var/material in resources)
resources[material] = resources[material] * ammo.max_stack
qdel(ammo)
/singleton/fabricator_recipe/ammunition/syringegun_ammo
name = "syringe gun cartridge"
path = /obj/item/syringe_cartridge
/singleton/fabricator_recipe/ammunition/shotgun
name = "shells (blank, shotgun)"
path = /obj/item/ammo_pile/shotgun_blanks
/singleton/fabricator_recipe/ammunition/shotgun/beanbag
name = "shells (beanbag, shotgun)"
path = /obj/item/ammo_pile/shotgun_beanbag
/singleton/fabricator_recipe/ammunition/shotgun/flash
name = "shells (flash, shotgun)"
path = /obj/item/ammo_pile/shotgun_flash
/singleton/fabricator_recipe/ammunition/shotgun/stun
name = "shells (stun cartridge, shotgun)"
path = /obj/item/ammo_casing/shotgun/stunshell
/singleton/fabricator_recipe/ammunition/shotgun/slug
name = "shells (slug, shotgun)"
path = /obj/item/ammo_pile/slug
security_level = SEC_LEVEL_RED
/singleton/fabricator_recipe/ammunition/shotgun/pellet
name = "shells (buckshot, shotgun)"
path = /obj/item/ammo_pile/shotgun_pellet
security_level = SEC_LEVEL_RED
/singleton/fabricator_recipe/ammunition/magazine_revolver_1
name = "speed loader (.357)"
path = /obj/item/ammo_magazine/a357
hack_only = TRUE
/singleton/fabricator_recipe/ammunition/detective_revolver_rubber
name = "speed loader (.38, rubber)"
path = /obj/item/ammo_magazine/c38/rubber
hack_only = TRUE
/singleton/fabricator_recipe/ammunition/detective_revolver_lethal
name = "speed loader (.38)"
path = /obj/item/ammo_magazine/c38
hack_only = TRUE
/singleton/fabricator_recipe/ammunition/magazine_fourty_five
name = "magazine (.45, pistol)"
path = /obj/item/ammo_magazine/c45m
security_level = SEC_LEVEL_RED
/singleton/fabricator_recipe/ammunition/magazine_rubber
name = "magazine (.45, rubber, pistol)"
path = /obj/item/ammo_magazine/c45m/rubber
/singleton/fabricator_recipe/ammunition/magazine_flash
name = "magazine (.45, flash, pistol)"
path = /obj/item/ammo_magazine/c45m/flash
/singleton/fabricator_recipe/ammunition/magazine_fourty_five/extended
name = "magazine (.45, extended, pistol)"
path = /obj/item/ammo_magazine/c45m/stendo
security_level = SEC_LEVEL_RED
/singleton/fabricator_recipe/ammunition/submachine_mag
name = "magazine (.45, submachine gun)"
path = /obj/item/ammo_magazine/submachinemag
hack_only = TRUE
/singleton/fabricator_recipe/ammunition/uzi_mag
name = "magazine (.45, machine pistol)"
path = /obj/item/ammo_magazine/c45uzi
security_level = SEC_LEVEL_RED
/singleton/fabricator_recipe/ammunition/magazine_stetchkin
name = "magazine (9mm)"
path = /obj/item/ammo_magazine/mc9mm
security_level = SEC_LEVEL_RED
/singleton/fabricator_recipe/ammunition/magazine_stetchkin_flash
name = "magazine (9mm, flash)"
path = /obj/item/ammo_magazine/mc9mm/flash
/singleton/fabricator_recipe/ammunition/magazine_smg
name = "magazine (9mm, top mounted, machine pistol)"
path = /obj/item/ammo_magazine/mc9mmt
security_level = SEC_LEVEL_RED
/singleton/fabricator_recipe/ammunition/magazine_smg_rubber
name = "magazine (9mm rubber, top mounted, machine pistol)"
path = /obj/item/ammo_magazine/mc9mmt/rubber
/singleton/fabricator_recipe/ammunition/magazine_c20r
name = "magazine (10mm)"
path = /obj/item/ammo_magazine/a10mm
hack_only = TRUE
/singleton/fabricator_recipe/ammunition/magazine_carbine
name = "magazine (5.56mm, rifle)"
path = /obj/item/ammo_magazine/a556
security_level = SEC_LEVEL_RED
/singleton/fabricator_recipe/ammunition/magazine_carbinepolymer
name = "magazine (5.56mm polymer, rifle)"
path = /obj/item/ammo_magazine/a556/polymer
security_level = SEC_LEVEL_RED
/singleton/fabricator_recipe/ammunition/magazine_smallcarbine
name = "magazine (5.56mm, carbine)"
path = /obj/item/ammo_magazine/a556/carbine
security_level = SEC_LEVEL_RED
/singleton/fabricator_recipe/ammunition/magazine_smallcarbinepolymer
name = "magazine (5.56mm polymer, carbine)"
path = /obj/item/ammo_magazine/a556/carbine/polymer
security_level = SEC_LEVEL_RED
/singleton/fabricator_recipe/ammunition/magazine_arifle
name = "magazine (7.62mm)"
path = /obj/item/ammo_magazine/c762
hack_only = TRUE
/singleton/fabricator_recipe/ammunition/clip_boltaction
name = "clip (7.62mm)"
path = /obj/item/ammo_magazine/boltaction
hack_only = TRUE
@@ -0,0 +1,40 @@
ABSTRACT_TYPE(/singleton/fabricator_recipe/armaments)
name = "Abstract Armaments"
category = "Armaments"
/singleton/fabricator_recipe/armaments/grenade
name = "grenade casing"
path = /obj/item/grenade/chem_grenade
/singleton/fabricator_recipe/armaments/grenade/large
name = "large grenade casing"
path = /obj/item/grenade/chem_grenade/large
/singleton/fabricator_recipe/armaments/handcuffs
name = "handcuffs"
path = /obj/item/handcuffs
/singleton/fabricator_recipe/armaments/flamethrower
name = "flamethrower"
path = /obj/item/flamethrower/full
security_level = SEC_LEVEL_RED
/singleton/fabricator_recipe/armaments/tacknife
name = "tactical knife"
path = /obj/item/material/knife/tacknife
security_level = SEC_LEVEL_RED
/singleton/fabricator_recipe/armaments/brassknuckles
name = "brass knuckles"
path = /obj/item/clothing/gloves/brassknuckles
hack_only = TRUE
/singleton/fabricator_recipe/armaments/electropack
name = "electropack"
path = /obj/item/device/radio/electropack
hack_only = TRUE
/singleton/fabricator_recipe/armaments/trap
name = "mechanical trap"
path = /obj/item/trap
hack_only = TRUE
@@ -0,0 +1,53 @@
ABSTRACT_TYPE(/singleton/fabricator_recipe/components)
name = "Abstract Components"
category = "Devices and Components"
/singleton/fabricator_recipe/components/consolescreen
name = "console screen"
path = /obj/item/stock_parts/console_screen
/singleton/fabricator_recipe/components/igniter
name = "igniter"
path = /obj/item/device/assembly/igniter
/singleton/fabricator_recipe/components/signaler
name = "signaler"
path = /obj/item/device/assembly/signaler
/singleton/fabricator_recipe/components/sensor_infra
name = "infrared sensor"
path = /obj/item/device/assembly/infra
/singleton/fabricator_recipe/components/timer
name = "timer"
path = /obj/item/device/assembly/timer
/singleton/fabricator_recipe/components/sensor_prox
name = "proximity sensor"
path = /obj/item/device/assembly/prox_sensor
/singleton/fabricator_recipe/components/cable_coil
name = "cable coil"
path = /obj/item/stack/cable_coil
is_stack = TRUE
// Basic Stock Parts for Engineering
/singleton/fabricator_recipe/components/micromanip
name = "micro-manipulator"
path = /obj/item/stock_parts/manipulator
/singleton/fabricator_recipe/components/matterbin
name = "matter bin"
path = /obj/item/stock_parts/matter_bin
/singleton/fabricator_recipe/components/capacitor
name = "capacitor"
path = /obj/item/stock_parts/capacitor
/singleton/fabricator_recipe/components/scanningmod
name = "scanning module"
path = /obj/item/stock_parts/scanning_module
/singleton/fabricator_recipe/components/microlaser
name = "micro-laser"
path = /obj/item/stock_parts/micro_laser
@@ -0,0 +1,53 @@
ABSTRACT_TYPE(/singleton/fabricator_recipe/engineering)
name = "Abstract Engineering"
category = "Engineering"
/singleton/fabricator_recipe/engineering/airlockmodule
name = "airlock electronics"
path = /obj/item/airlock_electronics
/singleton/fabricator_recipe/engineering/airalarm
name = "air alarm electronics"
path = /obj/item/airalarm_electronics
/singleton/fabricator_recipe/engineering/firealarm
name = "fire alarm electronics"
path = /obj/item/firealarm_electronics
/singleton/fabricator_recipe/engineering/powermodule
name = "power control module"
path = /obj/item/module/power_control
/singleton/fabricator_recipe/engineering/stockparts_box
name = "stock parts box"
path = /obj/item/storage/bag/stockparts_box
/singleton/fabricator_recipe/engineering/camera_assembly
name = "camera assembly"
path = /obj/item/camera_assembly
/singleton/fabricator_recipe/engineering/suit_cooling
name = "portable suit cooling unit"
path = /obj/item/device/suit_cooling_unit/no_cell
/singleton/fabricator_recipe/engineering/emergency_cell
name = "miniature cell"
path = /obj/item/cell/device/emergency_light/empty
/singleton/fabricator_recipe/engineering/debugger
name = "debugger"
path = /obj/item/device/debugger
/singleton/fabricator_recipe/engineering/floor_light
name = "floor light"
path = /obj/machinery/floor_light
/singleton/fabricator_recipe/engineering/tile_circuit_blue
name = "circuit tile, blue"
path = /obj/item/stack/tile/circuit_blue
is_stack = TRUE
/singleton/fabricator_recipe/engineering/tile_circuit_green
name = "circuit tile, green"
path = /obj/item/stack/tile/circuit_green
is_stack = TRUE
@@ -0,0 +1,51 @@
ABSTRACT_TYPE(/singleton/fabricator_recipe/general)
name = "Abstract General"
category = "General"
/singleton/fabricator_recipe/general/bucket
name = "bucket"
path = /obj/item/reagent_containers/glass/bucket
/singleton/fabricator_recipe/general/flashlight
name = "flashlight"
path = /obj/item/device/flashlight/empty
/singleton/fabricator_recipe/general/extinguisher
name = "extinguisher"
path = /obj/item/extinguisher
/singleton/fabricator_recipe/general/radio_headset
name = "radio headset"
path = /obj/item/device/radio/headset
/singleton/fabricator_recipe/general/radio_bounced
name = "shortwave radio"
path = /obj/item/device/radio/off
/singleton/fabricator_recipe/general/weldermask
name = "welding mask"
path = /obj/item/clothing/head/welding
/singleton/fabricator_recipe/general/taperecorder
name = "tape recorder"
path = /obj/item/device/taperecorder
/singleton/fabricator_recipe/general/tube
name = "light tube"
path = /obj/item/light/tube
/singleton/fabricator_recipe/general/bulb
name = "light bulb"
path = /obj/item/light/bulb
/singleton/fabricator_recipe/general/labeler
name = "hand labeler"
path = /obj/item/device/hand_labeler
/singleton/fabricator_recipe/general/destTagger
name = "destination tagger"
path = /obj/item/device/destTagger
/singleton/fabricator_recipe/general/cratescanner
name = "crate contents scanner"
path = /obj/item/device/cratescanner
@@ -0,0 +1,28 @@
ABSTRACT_TYPE(/singleton/fabricator_recipe/materials)
name = "Abstract Materials"
category = "Materials"
is_stack = TRUE
/singleton/fabricator_recipe/materials/metal
name = "steel sheets"
path = /obj/item/stack/material/steel
/singleton/fabricator_recipe/materials/aluminium
name = "aluminium sheets"
path = /obj/item/stack/material/aluminium
/singleton/fabricator_recipe/materials/glass
name = "glass sheets"
path = /obj/item/stack/material/glass
/singleton/fabricator_recipe/materials/rglass
name = "reinforced glass sheets"
path = /obj/item/stack/material/glass/reinforced
/singleton/fabricator_recipe/materials/rods
name = "metal rods"
path = /obj/item/stack/rods
/singleton/fabricator_recipe/materials/barbed_wire
name = "barbed wire"
path = /obj/item/stack/barbed_wire
@@ -0,0 +1,55 @@
ABSTRACT_TYPE(/singleton/fabricator_recipe/medical)
name = "Abstract Medical"
category = "Medical"
/singleton/fabricator_recipe/medical/scalpel
name = "scalpel"
path = /obj/item/surgery/scalpel
/singleton/fabricator_recipe/medical/circularsaw
name = "circular saw"
path = /obj/item/surgery/circular_saw
/singleton/fabricator_recipe/medical/surgicaldrill
name = "surgical drill"
path = /obj/item/surgery/surgicaldrill
/singleton/fabricator_recipe/medical/retractor
name = "retractor"
path = /obj/item/surgery/retractor
/singleton/fabricator_recipe/medical/cautery
name = "cautery"
path = /obj/item/surgery/cautery
/singleton/fabricator_recipe/medical/hemostat
name = "hemostat"
path = /obj/item/surgery/hemostat
/singleton/fabricator_recipe/medical/beaker
name = "glass beaker"
path = /obj/item/reagent_containers/glass/beaker
/singleton/fabricator_recipe/medical/beaker_large
name = "large glass beaker"
path = /obj/item/reagent_containers/glass/beaker/large
/singleton/fabricator_recipe/medical/vial
name = "glass vial"
path = /obj/item/reagent_containers/glass/beaker/vial
/singleton/fabricator_recipe/medical/autoinjector
name = "autoinjector"
path = /obj/item/reagent_containers/hypospray/autoinjector
/singleton/fabricator_recipe/medical/autoinhaler
name = "autoinhaler"
path = /obj/item/reagent_containers/inhaler
/singleton/fabricator_recipe/medical/syringe
name = "syringe"
path = /obj/item/reagent_containers/syringe
/singleton/fabricator_recipe/medical/syringe/large
name = "large syringe"
path = /obj/item/reagent_containers/syringe/large
@@ -0,0 +1,47 @@
ABSTRACT_TYPE(/singleton/fabricator_recipe/tools)
name = "Abstract Tools"
category = "Tools"
/singleton/fabricator_recipe/tools/crowbar
name = "crowbar"
path = /obj/item/crowbar
/singleton/fabricator_recipe/tools/multitool
name = "multitool"
path = /obj/item/device/multitool
/singleton/fabricator_recipe/tools/geiger
name = "geiger counter"
path = /obj/item/device/geiger
/singleton/fabricator_recipe/tools/t_scanner
name = "T-ray scanner"
path = /obj/item/device/t_scanner
/singleton/fabricator_recipe/tools/weldertool
name = "welding tool"
path = /obj/item/weldingtool
/singleton/fabricator_recipe/tools/welder_industrial
name = "industrial welding tool"
path = /obj/item/weldingtool/largetank
/singleton/fabricator_recipe/tools/screwdriver
name = "screwdriver"
path = /obj/item/screwdriver
/singleton/fabricator_recipe/tools/wirecutters
name = "wirecutters"
path = /obj/item/wirecutters
/singleton/fabricator_recipe/tools/wrench
name = "wrench"
path = /obj/item/wrench
/singleton/fabricator_recipe/tools/hatchet
name = "hatchet"
path = /obj/item/material/hatchet
/singleton/fabricator_recipe/tools/minihoe
name = "mini hoe"
path = /obj/item/material/minihoe
@@ -0,0 +1,76 @@
ABSTRACT_TYPE(/singleton/fabricator_recipe/cutlery)
name = "Abstract Cutlery"
category = "Cutlery"
fabricator_types = list(FABRICATOR_CLASS_MICRO)
/singleton/fabricator_recipe/cutlery/fork_aluminum
name = "fork, aluminium"
path = /obj/item/material/kitchen/utensil/fork
category = "Cutlery"
fabricator_types = list(FABRICATOR_CLASS_MICRO)
/singleton/fabricator_recipe/cutlery/spoon_aluminum
name = "spoon, aluminium"
path = /obj/item/material/kitchen/utensil/spoon
/singleton/fabricator_recipe/cutlery/spork_aluminum
name = "spork, aluminium"
path = /obj/item/material/kitchen/utensil/spork
/singleton/fabricator_recipe/cutlery/knife_aluminum
name = "table knife, aluminium"
path = /obj/item/material/kitchen/utensil/knife
/singleton/fabricator_recipe/cutlery/chopsticks_aluminum
name = "chopsticks, aluminium"
path = /obj/item/material/kitchen/utensil/fork/chopsticks
/singleton/fabricator_recipe/cutlery/fork_plastic
name = "fork, plastic"
path = /obj/item/material/kitchen/utensil/fork/plastic
build_time = 2 SECONDS
/singleton/fabricator_recipe/cutlery/spoon_plastic
name = "spoon, plastic"
path = /obj/item/material/kitchen/utensil/spoon/plastic
build_time = 2 SECONDS
/singleton/fabricator_recipe/cutlery/spork_plastic
name = "spork, plastic"
path = /obj/item/material/kitchen/utensil/spork/plastic
build_time = 2 SECONDS
/singleton/fabricator_recipe/cutlery/knife_plastic
name = "table knife, plastic"
path = /obj/item/material/kitchen/utensil/knife/plastic
build_time = 2 SECONDS
/singleton/fabricator_recipe/cutlery/chopsticks_plastic
name = "chopsticks, plastic"
path = /obj/item/material/kitchen/utensil/fork/chopsticks/plastic
build_time = 2 SECONDS
/singleton/fabricator_recipe/cutlery/fork_bamboo
name = "fork, bamboo"
path = /obj/item/material/kitchen/utensil/fork/bamboo
build_time = 4 SECONDS
/singleton/fabricator_recipe/cutlery/spoon_bamboo
name = "spoon, bamboo"
path = /obj/item/material/kitchen/utensil/spoon/bamboo
build_time = 4 SECONDS
/singleton/fabricator_recipe/cutlery/spork_bamboo
name = "spork, bamboo"
path = /obj/item/material/kitchen/utensil/spork/bamboo
build_time = 4 SECONDS
/singleton/fabricator_recipe/cutlery/knife_bamboo
name = "table knife, bamboo"
path = /obj/item/material/kitchen/utensil/knife/bamboo
build_time = 4 SECONDS
/singleton/fabricator_recipe/cutlery/chopsticks_bamboo
name = "chopsticks, bamboo"
path = /obj/item/material/kitchen/utensil/fork/chopsticks/bamboo
build_time = 4 SECONDS
@@ -0,0 +1,20 @@
ABSTRACT_TYPE(/singleton/fabricator_recipe/dinnerware)
name = "Abstract Dinnerware"
category = "Dinnerware"
fabricator_types = list(FABRICATOR_CLASS_MICRO, FABRICATOR_CLASS_GENERAL)
/singleton/fabricator_recipe/dinnerware/jar
name = "jar"
path = /obj/item/glass_jar
/singleton/fabricator_recipe/dinnerware/bowl
name = "bowl"
path = /obj/item/reagent_containers/cooking_container/board/bowl
/singleton/fabricator_recipe/dinnerware/bottle
name = "bottle"
path = /obj/item/reagent_containers/food/drinks/bottle
/singleton/fabricator_recipe/dinnerware/ashtray_glass
name = "glass ashtray"
path = /obj/item/material/ashtray/glass
@@ -0,0 +1,49 @@
ABSTRACT_TYPE(/singleton/fabricator_recipe/drinking_glass)
name = "Abstract Drinking Glasses"
category = "Drinking Glasses"
fabricator_types = list(FABRICATOR_CLASS_MICRO, FABRICATOR_CLASS_GENERAL)
build_time = 3 SECONDS
/singleton/fabricator_recipe/drinking_glass/regular_glass
name = "drinking glass"
path = /obj/item/reagent_containers/food/drinks/drinkingglass
/singleton/fabricator_recipe/drinking_glass/half_pint_glass
name = "half pint glass"
path = /obj/item/reagent_containers/food/drinks/drinkingglass/newglass/square
/singleton/fabricator_recipe/drinking_glass/rocks_glass
name = "rocks glass"
path = /obj/item/reagent_containers/food/drinks/drinkingglass/newglass/rocks
/singleton/fabricator_recipe/drinking_glass/sherry_glass
name = "sherry glass"
path = /obj/item/reagent_containers/food/drinks/drinkingglass/newglass/shake
/singleton/fabricator_recipe/drinking_glass/cocktail_glass
name = "cocktail glass"
path = /obj/item/reagent_containers/food/drinks/drinkingglass/newglass/cocktail
/singleton/fabricator_recipe/drinking_glass/shot_glass
name = "shot glass"
path = /obj/item/reagent_containers/food/drinks/drinkingglass/newglass/shot
/singleton/fabricator_recipe/drinking_glass/pint_glass
name = "pint glass"
path = /obj/item/reagent_containers/food/drinks/drinkingglass/newglass/pint
/singleton/fabricator_recipe/drinking_glass/mug_glass
name = "mug glass"
path = /obj/item/reagent_containers/food/drinks/drinkingglass/newglass/mug
/singleton/fabricator_recipe/drinking_glass/flute_glass
name = "flute glass"
path = /obj/item/reagent_containers/food/drinks/drinkingglass/newglass/flute
/singleton/fabricator_recipe/drinking_glass/cognac_glass
name = "cognac glass"
path = /obj/item/reagent_containers/food/drinks/drinkingglass/newglass/cognac
/singleton/fabricator_recipe/drinking_glass/goblet_glass
name = "goblet glass"
path = /obj/item/reagent_containers/food/drinks/drinkingglass/newglass/goblet
@@ -0,0 +1,108 @@
///Processes the current build, incrementing its remaining time and handling removing it from the print queue
/obj/machinery/fabricator/proc/update_current_build(spend_time)
if(!istype(currently_printing) || !is_functioning())
return
// Decrement our current build timer.
currently_printing.remaining_time -= max(1, max(1, spend_time SECONDS * build_time_multiplier))
if(currently_printing.remaining_time > 0)
return
// Print the item.
var/obj/item/I = new currently_printing.target_recipe.path(get_turf(print_loc))
I.Created()
if(currently_printing.multiplier > 1 && istype(I, /obj/item/stack))
var/obj/item/stack/S = I
S.amount = currently_printing.multiplier
print_queue -= currently_printing
QDEL_NULL(currently_printing)
get_next_build()
update_icon()
/obj/machinery/fabricator/proc/start_building()
if(!(fab_status_flags & FAB_BUSY) && is_functioning())
//Start the fabricator's looping sound
if (fabricator_looping_sound == null)
fabricator_looping_sound = new fabricating_sound_loop(src)
fabricator_looping_sound.start()
fab_status_flags |= FAB_BUSY
update_use_power(POWER_USE_ACTIVE)
update_icon()
/obj/machinery/fabricator/proc/stop_building()
fabricator_looping_sound.stop()
QDEL_NULL(fabricator_looping_sound)
if(fab_status_flags & FAB_BUSY)
fab_status_flags &= ~FAB_BUSY
update_use_power(POWER_USE_IDLE)
update_icon()
STOP_PROCESSING_MACHINE(src, MACHINERY_PROCESS_SELF)
/obj/machinery/fabricator/proc/get_next_build()
currently_printing = null
if(length(print_queue))
currently_printing = print_queue[1]
start_building()
else
stop_building()
updateUsrDialog()
///Tries to build the next item in the fabricator's queue
/obj/machinery/fabricator/proc/try_queue_build(singleton/fabricator_recipe/recipe, multiplier)
// Do some basic sanity checking.
if(!is_functioning() || !istype(recipe) || !(recipe in SSfabrication.get_recipes(fabricator_class)) || !can_print_item(recipe))
return
multiplier = sanitize_integer(multiplier, 1, 100, 1)
if(!ispath(recipe.path, /obj/item/stack) && multiplier > 1)
multiplier = 1
// Check if sufficient resources exist.
for(var/material in recipe.resources)
if(stored_material[material] < round(recipe.resources[material] * mat_efficiency) * multiplier)
return
// Generate and track a new order.
var/datum/fabricator_build_order/order = new
order.remaining_time = recipe.build_time * multiplier
order.target_recipe = recipe
order.multiplier = multiplier
print_queue += order
// Remove/earmark resources.
for(var/material in recipe.resources)
var/removed_mat = round(recipe.resources[material] * mat_efficiency) * multiplier
stored_material[material] = max(0, stored_material[material] - removed_mat)
order.earmarked_materials[material] = removed_mat
if(!currently_printing)
get_next_build()
else
start_building()
///Tries to cancel the build order
/obj/machinery/fabricator/proc/try_cancel_build(datum/fabricator_build_order/order)
if(istype(order) && currently_printing != order && is_functioning())
if(order in print_queue)
// Refund some mats.
for(var/mat in order.earmarked_materials)
stored_material[mat] = min(stored_material[mat] + (order.earmarked_materials[mat] * 0.9), storage_capacity[mat])
print_queue -= order
qdel(order)
return TRUE
return FALSE
///Determines whether the recipe is valid to print in this fabricator. Checks for hacked status and ship security levels.
/obj/machinery/fabricator/proc/can_print_item(singleton/fabricator_recipe/recipe)
var/ship_security_level = seclevel2num(get_security_level())
var/is_on_ship = is_station_level(z) // since ship security levels are global FOR NOW, we'll ignore the alert check for offship fabricators
if(!(fab_status_flags & FAB_HACKED))
if(recipe.hack_only)
return FALSE
else if(is_on_ship && ship_security_level < recipe.security_level)
return FALSE
return TRUE
@@ -0,0 +1,74 @@
#define NO_SPACE "No Space"
#define FILL_COMPLETELY "Fill Completely"
#define FILL_INCOMPLETELY "Fill Incompletely"
///Loads the lathe with materials
/obj/machinery/fabricator/proc/load_lathe(obj/item/loading_item, mob/user)
//Resources are being loaded.
var/obj/item/eating = loading_item
if(!eating.matter || !eating.recyclable)
to_chat(user, SPAN_WARNING("\The [eating] cannot be recycled by \the [src]."))
return
var/list/fill_status = list() // Used to determine message in cases of multiple materials.
var/total_used = 0 // Amount of material used.
var/mass_per_sheet = 0 // Amount of material constituting one sheet.
var/is_stack = FALSE //Affects the fill message
for(var/material in eating.matter)
if(isnull(stored_material[material]) || isnull(storage_capacity[material]))
continue
if(stored_material[material] >= storage_capacity[material])
LAZYADD(fill_status[NO_SPACE], material)
continue
var/total_material = eating.matter[material]
//If it's a stack, we eat multiple sheets.
if(istype(eating, /obj/item/stack))
var/obj/item/stack/stack = eating
is_stack = TRUE
total_material *= stack.get_amount()
if(stored_material[material] + total_material > storage_capacity[material])
total_material = storage_capacity[material] - stored_material[material]
LAZYADD(fill_status[FILL_COMPLETELY], material)
else
LAZYADD(fill_status[FILL_INCOMPLETELY], material)
stored_material[material] += total_material
total_used += total_material
mass_per_sheet += eating.matter[material]
if(fill_status[NO_SPACE])
to_chat(user, SPAN_WARNING("\The [src] is full of [english_list(fill_status[NO_SPACE])]. Please remove some material in order to insert more."))
return
else if(fill_status[FILL_COMPLETELY])
to_chat(user, SPAN_NOTICE("You fill \the [src] to capacity with [english_list(fill_status[FILL_COMPLETELY])][is_stack ? "." : " from \the [eating]."]"))
else if(fill_status[FILL_INCOMPLETELY])
to_chat(user, SPAN_NOTICE("You fill \the [src] with [english_list(fill_status[FILL_INCOMPLETELY])][is_stack ? "." : " from \the [eating]."]"))
// Plays metal insertion animation.
if(istype(eating, /obj/item/stack/material) && does_flick)
var/obj/item/stack/material/sheet = eating
var/image/adding_mat_overlay = overlay_image(icon, "[icon_state]_mat")
adding_mat_overlay.color = sheet.material.icon_colour
AddOverlays(adding_mat_overlay)
CUT_OVERLAY_IN(adding_mat_overlay, 1 SECOND)
// Play the lights animation (even if what we inserted wasn't a stack)
if(powered() && does_flick)
flick_overlay_view(mutable_appearance(icon, "[icon_state]_progress"), 1 SECONDS)
if(istype(eating, /obj/item/stack))
var/obj/item/stack/stack = eating
var/amount_needed = total_used / mass_per_sheet
stack.use(min(stack.get_amount(), (round(amount_needed) == amount_needed)? amount_needed : round(amount_needed) + 1)) // Prevent maths imprecision from leading to infinite resources
else
user.remove_from_mob(loading_item)
qdel(loading_item)
#undef NO_SPACE
#undef FILL_COMPLETELY
#undef FILL_INCOMPLETELY
@@ -0,0 +1,17 @@
/obj/machinery/fabricator/autolathe
name = "autolathe"
desc = "A large device loaded with various item schematics. It produces common day to day items from a variety of materials."
icon = 'icons/obj/machinery/fabricators/autolathe.dmi'
icon_state = "autolathe"
/obj/machinery/fabricator/autolathe/mounted
name = "\improper mounted autolathe"
density = FALSE
anchored = FALSE
idle_power_usage = 0
active_power_usage = 0
interact_offline = TRUE
does_flick = FALSE
/obj/machinery/fabricator/mounted/ui_state(mob/user)
return GLOB.heavy_vehicle_state
@@ -0,0 +1,5 @@
/obj/machinery/fabricator/autolathe/hacked
desc = "An atypical autolathe. It has an unusual icon in the interface, and appears to have far more options than a normal autolathe."
name = "jailbroken autolathe"
fab_status_flags = FAB_HACKED
manufacturer = "hammertail"
@@ -0,0 +1,42 @@
/obj/machinery/fabricator/microlathe
name = "microlathe"
desc = "It produces small items from common resources."
icon = 'icons/obj/machinery/fabricators/microlathe.dmi'
icon_state = "minilathe"
idle_power_usage = 5
active_power_usage = 1000
fabricator_class = FABRICATOR_CLASS_MICRO
base_storage_capacity = list(
MATERIAL_ALUMINIUM = 5000,
MATERIAL_GLASS = 5000,
MATERIAL_PLASTIC = 5000,
MATERIAL_BAMBOO = 5000
)
manufacturer = "idris"
component_types = list(
/obj/item/circuitboard/microlathe,
/obj/item/stock_parts/matter_bin = 3,
/obj/item/stock_parts/micro_laser,
/obj/item/stock_parts/manipulator,
/obj/item/stock_parts/console_screen
)
fabricating_sound_loop = /datum/looping_sound/fabricator/minilathe
//Subtype for mapping, starts preloaded and set to print glasses
/obj/machinery/fabricator/microlathe/bartender
show_category = "Drinking Glasses"
/obj/machinery/fabricator/microlathe/bartender/Initialize(mapload)
. = ..()
stored_material[MATERIAL_GLASS] = storage_capacity[MATERIAL_GLASS]
//Subtype for mapping, starts preloaded and set to print cutlery
/obj/machinery/fabricator/microlathe/cafe
show_category = "Cutlery"
/obj/machinery/fabricator/microlathe/cafe/Initialize(mapload)
. = ..()
stored_material[MATERIAL_PLASTIC] = storage_capacity[MATERIAL_PLASTIC]
stored_material[MATERIAL_BAMBOO] = storage_capacity[MATERIAL_BAMBOO]