Skip to content

Commit

Permalink
Protolathe improvements (#19098)
Browse files Browse the repository at this point in the history
Various DMdocs to the protolathe code.
Increased power draw of the protolathe.
Made the protolathe realtime-constant.
Adding materials to the protolathe now uses TGUI input.
Removed a basically useless processing, uses a timer / queue handler
proc now.
DMDoc'd other parts of the code, including corrections between ticks and
deciseconds.
  • Loading branch information
FluffyGhoster authored May 9, 2024
1 parent a83008b commit 944bc50
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 86 deletions.
2 changes: 1 addition & 1 deletion code/__HELPERS/unsorted.dm
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
*
* Arguments:
* * user: The user to check for.
* * delay: The delay in ticks to wait before returning TRUE.
* * delay: The delay in deciseconds to wait before returning TRUE.
* * target: The target to check for. Optional.
* * do_flags: Flags that determine what the user and target can and cannot do, defined in [mobs.dm]. Defaults to DO_DEFAULT.
* * incapacitation_flags: Incapacitation flags that determines if the user can be incapacitated. Defaults to INCAPACITATION_DEFAULT.
Expand Down
27 changes: 18 additions & 9 deletions code/modules/research/designs/designs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,24 @@ other types of metals and chemistry for reagents).
// #TODO-MERGE: Go over this file and make sure everything's fine. We might have missing vars.

/datum/design //Datum for object designs, used in construction
var/name //Name of the created object. If null it will be 'guessed' from build_path if possible.
var/desc //Description of the created object. If null it will use group_desc and name where applicable.
var/item_name //An item name before it is modified by various name-modifying procs
var/list/req_tech = list() //IDs of that techs the object originated from and the minimum level requirements.
var/build_type //Flag as to what kind machine the design is built in. See defines.
var/list/materials = list() //List of materials. Format: "id" = amount.
var/list/chemicals = list() //List of chemicals.
var/build_path //The path of the object that gets created.
var/time = 10 //How many ticks it requires to build
///Name of the created object, if null it will be 'guessed' from build_path if possible
var/name
///Description of the created object. If null it will use group_desc and name where applicable
var/desc
///An item name before it is modified by various name-modifying procs
var/item_name
///IDs of that techs the object originated from and the minimum level requirements
var/list/req_tech = list()
///Flag as to what kind machine the design is built in, see defines in `code\__DEFINES\research.dm`
var/build_type
///List of materials, format: `"id" = amount`
var/list/materials = list()
///List of chemicals
var/list/chemicals = list()
//The path of the object that gets created
var/build_path
///How many deciseconds it requires to build
var/time = 10
var/p_category = "Misc"
var/category //Primarily used for Mech Fabricators, but can be used for anything.

Expand Down
220 changes: 144 additions & 76 deletions code/modules/research/protolathe.dm
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,29 @@
icon_state = "protolathe"
atom_flags = ATOM_FLAG_OPEN_CONTAINER

idle_power_usage = 30
active_power_usage = 5000
idle_power_usage = 30 WATTS
active_power_usage = 25 KILO WATTS

var/max_material_storage = 100000
var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, MATERIAL_GLASS = 0, MATERIAL_GOLD = 0, MATERIAL_SILVER = 0, MATERIAL_PHORON = 0, MATERIAL_URANIUM = 0, MATERIAL_DIAMOND = 0)

/**
* A `/list` of enqueued `/datum/design` to be printed, processed in the queue
*/
var/list/datum/design/queue = list()
var/progress = 0

/**
* How much efficient (or inefficient) the protolathe is at manufacturing things
*/
var/mat_efficiency = 1
var/speed = 1

/**
* The production speed of this specific protolathe, a factor
*/
var/production_speed = 1

///The timer id for the build callback, if we're building something
var/build_callback_timer

component_types = list(
/obj/item/circuitboard/protolathe,
Expand All @@ -23,33 +35,8 @@
/obj/item/reagent_containers/glass/beaker = 2
)

/obj/machinery/r_n_d/protolathe/process()
..()
if(stat)
update_icon()
return
if(queue.len == 0)
busy = 0
update_icon()
return
var/datum/design/D = queue[1]
if(canBuild(D))
busy = 1
progress += speed
if(progress >= D.time)
build(D)
progress = 0
removeFromQueue(1)
if(linked_console)
linked_console.updateUsrDialog()
update_icon()
else
if(busy)
visible_message("<span class='notice'>[icon2html(src, viewers(get_turf(src)))] [src] flashes: insufficient materials: [getLackingMaterials(D)].</span>")
busy = 0
update_icon()

/obj/machinery/r_n_d/protolathe/proc/TotalMaterials() //returns the total of all the stored materials. Makes code neater.
///Returns the total of all the stored materials
/obj/machinery/r_n_d/protolathe/proc/TotalMaterials()
var/t = 0
for(var/f in materials)
t += materials[f]
Expand All @@ -75,7 +62,7 @@
for(var/obj/item/stock_parts/manipulator/M in component_parts)
T += M.rating
mat_efficiency = 1 - (T - 2) / 8
speed = T / 2
production_speed = T / 2

/obj/machinery/r_n_d/protolathe/dismantle()
for(var/obj/I in component_parts)
Expand All @@ -92,14 +79,14 @@
/obj/machinery/r_n_d/protolathe/update_icon()
if(panel_open)
icon_state = "protolathe_t"
else if(busy)
else if(build_callback_timer)
icon_state = "protolathe_n"
else
icon_state = "protolathe"

/obj/machinery/r_n_d/protolathe/attackby(obj/item/attacking_item, mob/user)
if(busy)
to_chat(user, "<span class='notice'>\The [src] is busy. Please wait for completion of previous operation.</span>")
if(build_callback_timer)
to_chat(user, SPAN_NOTICE("\The [src] is busy. Please wait for completion of previous operation."))
return 1
if(default_deconstruction_screwdriver(user, attacking_item))
if(linked_console)
Expand All @@ -113,98 +100,179 @@
if(attacking_item.is_open_container())
return 1
if(panel_open)
to_chat(user, "<span class='notice'>You can't load \the [src] while it's opened.</span>")
to_chat(user, SPAN_NOTICE("You can't load \the [src] while it's opened."))
return 1
if(!linked_console)
to_chat(user, "<span class='notice'>\The [src] must be linked to an R&D console first!</span>")
to_chat(user, SPAN_NOTICE("The [src] must be linked to an R&D console first!"))
return 1
if(!istype(attacking_item, /obj/item/stack/material))
to_chat(user, "<span class='notice'>You cannot insert this item into \the [src]!</span>")
to_chat(user, SPAN_NOTICE("You cannot insert this item into \the [src]!"))
return 1
if(stat)
return 1

if(TotalMaterials() + SHEET_MATERIAL_AMOUNT > max_material_storage)
to_chat(user, "<span class='notice'>\The [src]'s material bin is full. Please remove material before adding more.</span>")
to_chat(user, SPAN_NOTICE("The [src]'s material bin is full. Please remove material before adding more."))
return 1

var/obj/item/stack/material/stack = attacking_item
if(!stack.default_type)
to_chat(user, SPAN_WARNING("This stack cannot be used!"))
return
var/amount = round(input("How many sheets do you want to add?") as num)//No decimals
var/amount = tgui_input_number(user, "How many sheets do you want to add?", "Add sheets", 10,
max_value = min(stack.get_amount(), round((max_material_storage - TotalMaterials()) / SHEET_MATERIAL_AMOUNT)),
min_value = 1, round_value = TRUE)
if(!attacking_item)
return
if(!Adjacent(user))
to_chat(user, "<span class='notice'>\The [src] is too far away for you to insert this.</span>")
to_chat(user, SPAN_NOTICE("The [src] is too far away for you to insert this."))
return
if(amount <= 0)//No negative numbers
if(amount <= 0)//No negative numbers, no nulls
return
if(amount > stack.get_amount())
amount = stack.get_amount()
if(max_material_storage - TotalMaterials() < (amount * SHEET_MATERIAL_AMOUNT)) //Can't overfill
amount = min(stack.get_amount(), round((max_material_storage - TotalMaterials()) / SHEET_MATERIAL_AMOUNT))

AddOverlays("protolathe_[stack.default_type]")
CUT_OVERLAY_IN("protolathe_[stack.default_type]", 10)

busy = 1
//Use some power and add the materials
use_power_oneoff(max(1000, (SHEET_MATERIAL_AMOUNT * amount / 10)))
if(do_after(user, 16))
if(do_after(user, 1.6 SECONDS))
if(stack.use(amount))
to_chat(user, "<span class='notice'>You add [amount] sheets to \the [src].</span>")
to_chat(user, SPAN_NOTICE("You add [amount] sheets to \the [src]."))
materials[stack.default_type] += amount * SHEET_MATERIAL_AMOUNT
busy = 0

//In case there's things queued up, we run the queue handler
handle_queue()

//Give an update on the UIs
updateUsrDialog()
return
if(linked_console)
linked_console.updateUsrDialog()

/obj/machinery/r_n_d/protolathe/proc/addToQueue(var/datum/design/D)
queue += D
/**
* Adds a design to the queue
*
* * design_to_add: The design to add
*/
/obj/machinery/r_n_d/protolathe/proc/addToQueue(datum/design/design_to_add)
queue += design_to_add

//Wake up, we have things to do
handle_queue()

/**
* Removes a design from the queue
*
* * index: The index of the design to remove
*/
/obj/machinery/r_n_d/protolathe/proc/removeFromQueue(var/index)
queue.Cut(index, index + 1)

/obj/machinery/r_n_d/protolathe/proc/canBuild(var/datum/design/D)
for(var/M in D.materials)
if(materials[M] < D.materials[M])
return 0
for(var/C in D.chemicals)
if(!reagents.has_reagent(C, D.chemicals[C]))
return 0
return 1
//Wake up, we have things to do
handle_queue()

/**
* Handle the construction queue
*/
/obj/machinery/r_n_d/protolathe/proc/handle_queue()

/obj/machinery/r_n_d/protolathe/proc/getLackingMaterials(var/datum/design/D)
//No work to do or already busy, stop
if(!length(queue) || build_callback_timer)
update_icon()
return

//If there's no power, there's no building
if(stat & NOPOWER)
queue = list()
return

//Get the first design in the queue
var/datum/design/D = queue[1]

//If we can build it, process the request
if(canBuild(D))
build_callback_timer = addtimer(CALLBACK(src, PROC_REF(build), D), (D.time / production_speed), TIMER_UNIQUE)
removeFromQueue(1)

else
visible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] \The [src] flashes: Insufficient materials: [getLackingMaterials(D)]."))

update_icon()


/**
* Checks if the protolathe can build the given design
*
* * design_to_check: The design to check
*
* Returns `TRUE` if the design can be built, `FALSE` otherwise
*/
/obj/machinery/r_n_d/protolathe/proc/canBuild(datum/design/design_to_check)
for(var/M in design_to_check.materials)
if(materials[M] < design_to_check.materials[M])
return FALSE

for(var/C in design_to_check.chemicals)
if(!reagents.has_reagent(C, design_to_check.chemicals[C]))
return FALSE

return TRUE

/**
* Get what materials (chemicals included) are lacking from being able to build the given design
*
* * design_to_check: The design to check
*
* Returns a string of the materials that are missing
*/
/obj/machinery/r_n_d/protolathe/proc/getLackingMaterials(var/datum/design/design_to_check)
var/ret = ""
for(var/M in D.materials)
if(materials[M] < D.materials[M])
for(var/M in design_to_check.materials)
if(materials[M] < design_to_check.materials[M])
if(ret != "")
ret += ", "
ret += "[D.materials[M] - materials[M]] [M]"
for(var/C in D.chemicals)
if(!reagents.has_reagent(C, D.chemicals[C]))
ret += "[design_to_check.materials[M] - materials[M]] [M]"
for(var/C in design_to_check.chemicals)
if(!reagents.has_reagent(C, design_to_check.chemicals[C]))
var/singleton/reagent/R = GET_SINGLETON(C)
if(ret != "")
ret += ", "
ret += "[R.name]"
return ret

/obj/machinery/r_n_d/protolathe/proc/build(var/datum/design/D)
/**
* Builds the given design, assuming all the necessary conditions are met
*
* * design_to_build: The design to build
*/
/obj/machinery/r_n_d/protolathe/proc/build(datum/design/design_to_build)
//Consume some power
var/power = active_power_usage
for(var/M in D.materials)
power += round(D.materials[M] / 5)
for(var/M in design_to_build.materials)
power += round(design_to_build.materials[M] / 2)
power = max(active_power_usage, power)
use_power_oneoff(power)
for(var/M in D.materials)
materials[M] = max(0, materials[M] - D.materials[M] * mat_efficiency)
for(var/C in D.chemicals)
reagents.remove_reagent(C, D.chemicals[C] * mat_efficiency)

//Consume the materials
for(var/M in design_to_build.materials)
materials[M] = max(0, materials[M] - design_to_build.materials[M] * mat_efficiency)
for(var/C in design_to_build.chemicals)
reagents.remove_reagent(C, design_to_build.chemicals[C] * mat_efficiency)

intent_message(MACHINE_SOUND)

if(D.build_path)
var/obj/new_item = D.Fabricate(src, src)
if(design_to_build.build_path)
var/obj/new_item = design_to_build.Fabricate(src, src)
new_item.forceMove(loc)
if(mat_efficiency != 1) // No matter out of nowhere
if(new_item.matter && new_item.matter.len > 0)
for(var/i in new_item.matter)
new_item.matter[i] = new_item.matter[i] * mat_efficiency

if(linked_console)
linked_console.updateUsrDialog()

//We finished building, clear the timer
build_callback_timer = null

//Do the queue handling for the next item, or to stop
handle_queue()
Loading

0 comments on commit 944bc50

Please sign in to comment.