diff --git a/code/controllers/subsystem/research.dm b/code/controllers/subsystem/research.dm index 2a2c11c1ac1..ce4082d2d44 100644 --- a/code/controllers/subsystem/research.dm +++ b/code/controllers/subsystem/research.dm @@ -36,7 +36,7 @@ SUBSYSTEM_DEF(research) var/list/techweb_nodes_experimental = list() ///path = list(point type = value) var/list/techweb_point_items = list( - /obj/item/assembly/signaler/anomaly = list(TECHWEB_POINT_TYPE_GENERIC = 10000) + /obj/item/assembly/signaler/anomaly = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS) ) var/list/errored_datums = list() ///Associated list of all point types that techwebs will have and their respective 'abbreviated' name. @@ -104,6 +104,13 @@ SUBSYSTEM_DEF(research) techweb_list.last_income = world.time + if(techweb_list.research_queue_nodes.len) + techweb_list.research_node_id(techweb_list.research_queue_nodes[1]) // Attempt to research the first node in queue if possible + + for(var/datum/techweb_node/node as anything in techweb_list.research_queue_nodes) + if(node.is_free(techweb_list)) // Automatically research all free nodes in queue if any + techweb_list.research_node(node) + /datum/controller/subsystem/research/proc/autosort_categories() for(var/i in techweb_nodes) var/datum/techweb_node/I = techweb_nodes[i] diff --git a/code/modules/modular_computers/file_system/programs/techweb.dm b/code/modules/modular_computers/file_system/programs/techweb.dm index 014e6a56727..4e181370fe2 100644 --- a/code/modules/modular_computers/file_system/programs/techweb.dm +++ b/code/modules/modular_computers/file_system/programs/techweb.dm @@ -50,6 +50,7 @@ return data data += list( "nodes" = list(), + "queue_nodes" = stored_research.research_queue_nodes, "experiments" = list(), "researched_designs" = stored_research.researched_designs, "points" = stored_research.research_points, @@ -64,6 +65,10 @@ // Serialize all nodes to display for(var/tier in stored_research.tiers) var/datum/techweb_node/node = SSresearch.techweb_node_by_id(tier) + var/enqueued_by_user = FALSE + + if((tier in stored_research.research_queue_nodes) && stored_research.research_queue_nodes[tier] == user) + enqueued_by_user = TRUE // Ensure node is supposed to be visible if (stored_research.hidden_nodes[tier]) @@ -71,8 +76,11 @@ data["nodes"] += list(list( "id" = node.id, + "is_free" = node.is_free(stored_research), "can_unlock" = stored_research.can_unlock_node(node), - "tier" = stored_research.tiers[node.id] + "have_experiments_done" = stored_research.have_experiments_for_node(node), + "tier" = stored_research.tiers[node.id], + "enqueued_by_user" = enqueued_by_user )) // Get experiments and serialize them @@ -111,6 +119,12 @@ if ("researchNode") research_node(params["node_id"], usr) return TRUE + if ("enqueueNode") + enqueue_node(params["node_id"], usr) + return TRUE + if ("dequeueNode") + dequeue_node(params["node_id"], usr) + return TRUE /datum/computer_file/program/science/ui_static_data(mob/user) . = list( @@ -188,6 +202,20 @@ id_cache_seq += 1 return id_cache[id] +/datum/computer_file/program/science/proc/enqueue_node(id, mob/user) + if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id]) + computer.say("Node enqueue failed: Either no techweb is found, node is already researched or is not available!") + return FALSE + stored_research.enqueue_node(id, user) + return TRUE + +/datum/computer_file/program/science/proc/dequeue_node(id, mob/user) + if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id]) + computer.say("Node dequeue failed: Either no techweb is found, node is already researched or is not available!") + return FALSE + stored_research.dequeue_node(id, user) + return TRUE + /datum/computer_file/program/science/proc/research_node(id, mob/user) if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id]) computer.say("Node unlock failed: Either no techweb is found, node is already researched or is not available!") diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm index cc8e842f18e..91cf89582d3 100644 --- a/code/modules/research/rdconsole.dm +++ b/code/modules/research/rdconsole.dm @@ -96,6 +96,20 @@ Nothing else in the console has ID requirements. stored_research = tool.buffer return TRUE +/obj/machinery/computer/rdconsole/proc/enqueue_node(id, mob/user) + if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id]) + say("Node enqueue failed: Either no techweb is found, node is already researched or is not available!") + return FALSE + stored_research.enqueue_node(id, user) + return TRUE + +/obj/machinery/computer/rdconsole/proc/dequeue_node(id, mob/user) + if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id]) + say("Node dequeue failed: Either no techweb is found, node is already researched or is not available!") + return FALSE + stored_research.dequeue_node(id, user) + return TRUE + /obj/machinery/computer/rdconsole/proc/research_node(id, mob/user) if(!stored_research || !stored_research.available_nodes[id] || stored_research.researched_nodes[id]) say("Node unlock failed: Either no techweb is found, node is already researched or is not available!") @@ -171,6 +185,7 @@ Nothing else in the console has ID requirements. return data data += list( "nodes" = list(), + "queue_nodes" = stored_research.research_queue_nodes, "experiments" = list(), "researched_designs" = stored_research.researched_designs, "points" = stored_research.research_points, @@ -194,6 +209,10 @@ Nothing else in the console has ID requirements. // Serialize all nodes to display for(var/v in stored_research.tiers) var/datum/techweb_node/n = SSresearch.techweb_node_by_id(v) + var/enqueued_by_user = FALSE + + if((v in stored_research.research_queue_nodes) && stored_research.research_queue_nodes[v] == user) + enqueued_by_user = TRUE // Ensure node is supposed to be visible if (stored_research.hidden_nodes[v]) @@ -201,8 +220,11 @@ Nothing else in the console has ID requirements. data["nodes"] += list(list( "id" = n.id, + "is_free" = n.is_free(stored_research), "can_unlock" = stored_research.can_unlock_node(n), + "have_experiments_done" = stored_research.have_experiments_for_node(n), "tier" = stored_research.tiers[n.id], + "enqueued_by_user" = enqueued_by_user )) // Get experiments and serialize them @@ -322,6 +344,12 @@ Nothing else in the console has ID requirements. if ("researchNode") research_node(params["node_id"], usr) return TRUE + if ("enqueueNode") + enqueue_node(params["node_id"], usr) + return TRUE + if ("dequeueNode") + dequeue_node(params["node_id"], usr) + return TRUE if ("ejectDisk") eject_disk(params["type"]) return TRUE diff --git a/code/modules/research/techweb/_techweb.dm b/code/modules/research/techweb/_techweb.dm index 3c920f6b9a6..a85c4b82c17 100644 --- a/code/modules/research/techweb/_techweb.dm +++ b/code/modules/research/techweb/_techweb.dm @@ -70,6 +70,12 @@ * Filled with nulls on init, populated only on publication. */ var/list/published_papers + /** + * Assoc list of nodes queued for automatic research when there are enough points available + * research_queue_nodes[node_id] = user_enqueued + */ + var/list/research_queue_nodes = list() + /datum/techweb/New() SSresearch.techwebs += src @@ -325,6 +331,40 @@ /datum/techweb/proc/printout_points() return techweb_point_display_generic(research_points) +/datum/techweb/proc/enqueue_node(id, mob/user) + var/mob/living/carbon/human/human_user = user + var/is_rd = FALSE + if(human_user.wear_id) + var/list/access = human_user.wear_id.GetAccess() + if(ACCESS_RD in access) + is_rd = TRUE + + if(id in research_queue_nodes) + if(is_rd) + research_queue_nodes.Remove(id) + else + return FALSE + + for(var/node_id in research_queue_nodes) + if(research_queue_nodes[node_id] == user) + research_queue_nodes.Remove(node_id) + + if (is_rd) + research_queue_nodes.Insert(1, id) + research_queue_nodes[id] = user + + return TRUE + +/datum/techweb/proc/dequeue_node(id, mob/user) + if(!(id in research_queue_nodes)) + return FALSE + if(research_queue_nodes[id] != user) + return FALSE + + research_queue_nodes.Remove(id) + + return TRUE + /datum/techweb/proc/research_node_id(id, force, auto_update_points, get_that_dosh_id) return research_node(SSresearch.techweb_node_by_id(id), force, auto_update_points, get_that_dosh_id) @@ -377,6 +417,10 @@ if (MC_RUNNING()) log_research(log_message) + // Dequeue + if(node.id in research_queue_nodes) + research_queue_nodes.Remove(node.id) + return TRUE /datum/techweb/proc/unresearch_node_id(id) diff --git a/code/modules/research/techweb/_techweb_node.dm b/code/modules/research/techweb/_techweb_node.dm index 7714946a4d2..9b26ca860d8 100644 --- a/code/modules/research/techweb/_techweb_node.dm +++ b/code/modules/research/techweb/_techweb_node.dm @@ -95,6 +95,17 @@ return actual_costs +/datum/techweb_node/proc/is_free(datum/techweb/host) + var/list/costs = get_price(host) + var/total_points = 0 + + for(var/point_type in costs) + total_points += costs[point_type] + + if(total_points == 0) + return TRUE + return FALSE + /datum/techweb_node/proc/price_display(datum/techweb/TN) return techweb_point_display_generic(get_price(TN)) diff --git a/tgui/packages/tgui/interfaces/Techweb.jsx b/tgui/packages/tgui/interfaces/Techweb.jsx index dcafea78bf5..ca60c948db3 100644 --- a/tgui/packages/tgui/interfaces/Techweb.jsx +++ b/tgui/packages/tgui/interfaces/Techweb.jsx @@ -10,6 +10,7 @@ import { Flex, Icon, Input, + LabeledList, Modal, ProgressBar, Section, @@ -147,6 +148,8 @@ export const TechwebContent = (props) => { t_disk, d_disk, locked, + queue_nodes = [], + node_cache, } = data; const [techwebRoute, setTechwebRoute] = useLocalState('techwebRoute', null); const [lastPoints, setLastPoints] = useState({}); @@ -156,27 +159,35 @@ export const TechwebContent = (props) => { - - Available points: -
    - {Object.keys(points).map((k) => ( -
  • - {k}: {points[k]} - {!!points_last_tick[k] && ` (+${points_last_tick[k]}/sec)`} -
  • - ))} -
-
- - Security protocols: - - {sec_protocols ? 'Engaged' : 'Disengaged'} - - + + + + {sec_protocols ? 'Engaged' : 'Disengaged'} + + + {Object.keys(points).map((k) => ( + + {points[k]} + {!!points_last_tick[k] && ` (+${points_last_tick[k]}/sec)`} + + ))} + + {queue_nodes.length !== 0 + ? Object.keys(queue_nodes).map((node_id) => ( + + )) + : 'Empty'} + +
@@ -493,9 +504,17 @@ const TechNode = (props) => { points = [], nodes, point_types_abbreviations = [], + queue_nodes = [], } = data; const { node, nodetails, nocontrols } = props; - const { id, can_unlock, tier } = node; + const { + id, + can_unlock, + have_experiments_done, + tier, + enqueued_by_user, + is_free, + } = node; const { name, description, @@ -555,6 +574,40 @@ const TechNode = (props) => { buttons={ !nocontrols && ( <> + {tier > 0 && + (!!can_unlock && (is_free || queue_nodes.length === 0) ? ( + + ) : enqueued_by_user ? ( + + ) : id in queue_nodes && !enqueued_by_user ? ( + + ) : ( + + ))} {!nodetails && ( )} - {tier > 0 && ( - - )} ) }