From a585b40a1c7f9905e2d70e2a8bf9219fde05e91d Mon Sep 17 00:00:00 2001
From: SkyratBot <59378654+SkyratBot@users.noreply.github.com>
Date: Mon, 15 Jul 2024 06:35:42 +0200
Subject: [PATCH] [MIRROR] Research queue (#28846)
* Research queue (#84731)
## About The Pull Request
Added an ability to queue up to one node per player in a techweb for an
automatic research.
You can queue up a node only when all requirements are met, but there
are not enough points.
People can't research when there is something in the queue, only add
things to the queue. So a 40 points node can't be researched if someone
queued up a 200 points node ahead of it.
When a node is enqueued by RD, it is placed in front of the queue.
The research button is available when the queue is empty.
TODO:
- [x] Bypass queue when the node cost is zero
## Why It's Good For The Game
No need to stay at the console to wait for the points. No "Research"
button spamming.
## Changelog
:cl:
qol: Research nodes can be queued, one per player. RDs can place their
node at the beginning of the queue.
/:cl:
* Research queue
---------
Co-authored-by: Andrew
---
code/controllers/subsystem/research.dm | 9 +-
.../file_system/programs/techweb.dm | 30 ++++-
code/modules/research/rdconsole.dm | 28 +++++
code/modules/research/techweb/_techweb.dm | 44 ++++++++
.../modules/research/techweb/_techweb_node.dm | 11 ++
tgui/packages/tgui/interfaces/Techweb.jsx | 106 +++++++++++++-----
6 files changed, 195 insertions(+), 33 deletions(-)
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:
-